Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,36 @@ describe('CoreMenuOptions Component', () => {
// Check the trailing element is NOT rendered in the component as this is a bot
expect(screen.queryByText(/John Doe/)).toBeNull();
});

it('should format teammate time according to user locale', () => {
setMockDate(2); // Tuesday

const stateWithFrenchLocale = {
...initialState,
entities: {
...initialState.entities,
users: {
...initialState.entities.users,
profiles: {
currentUserId: {
locale: 'fr',
},
},
},
},
};

mockedUseTimePostBoxIndicator.mockReturnValue({
...defaultUseTimePostBoxIndicatorReturnValue,
isDM: true,
isSelfDM: false,
isBot: false,
});

renderComponent(stateWithFrenchLocale);

// Verify French format (no AM/PM)
const timeTexts = screen.getAllByText(/\d{2}:\d{2}(?!\s*[AP]M)/);
expect(timeTexts.length).toBeGreaterThan(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import {DateTime} from 'luxon';
import React, {memo, useCallback} from 'react';
import {FormattedMessage} from 'react-intl';
import {useSelector} from 'react-redux';

import {getCurrentLocale} from 'selectors/i18n';

import useTimePostBoxIndicator from 'components/advanced_text_editor/use_post_box_indicator';
import * as Menu from 'components/menu';
Expand All @@ -17,10 +20,23 @@ type Props = {
channelId: string;
}

function getScheduledTimeInTeammateTimezone(userCurrentTimestamp: number, teammateTimezoneString: string): string {
/**
* Formats a timestamp in the teammate's timezone using the current user's locale.
* @param userCurrentTimestamp - Timestamp in milliseconds (UTC)
* @param teammateTimezoneString - IANA timezone string (e.g., "America/New_York")
* @param userLocale - User's locale code (e.g., "fr", "en", "de")
* @returns Formatted time string respecting the user's locale
* @example
* // US locale: "8:00 AM"
* // French locale: "08:00"
* getScheduledTimeInTeammateTimezone(1635768000000, 'Europe/Paris', 'fr')
*/
function getScheduledTimeInTeammateTimezone(userCurrentTimestamp: number, teammateTimezoneString: string, userLocale: string): string {
const scheduledTimeUTC = DateTime.fromMillis(userCurrentTimestamp, {zone: 'utc'});
const teammateScheduledTime = scheduledTimeUTC.setZone(teammateTimezoneString);
const formattedTime = teammateScheduledTime.toFormat('h:mm a');
const formattedTime = teammateScheduledTime.
setLocale(userLocale).
toLocaleString(DateTime.TIME_SIMPLE);
return formattedTime;
}

Expand All @@ -41,6 +57,7 @@ function CoreMenuOptions({handleOnSelect, channelId}: Props) {
isBot,
} = useTimePostBoxIndicator(channelId);

const locale = useSelector(getCurrentLocale);
const now = DateTime.now().setZone(userCurrentTimezone);
const tomorrow9amTime = DateTime.now().
setZone(userCurrentTimezone).
Expand All @@ -66,7 +83,7 @@ function CoreMenuOptions({handleOnSelect, channelId}: Props) {

if (isDM && !isBot && !isSelfDM) {
const teammateTimezoneString = teammateTimezone.useAutomaticTimezone ? teammateTimezone.automaticTimezone : teammateTimezone.manualTimezone || 'UTC';
const scheduledTimeInTeammateTimezone = getScheduledTimeInTeammateTimezone(tomorrow9amTime, teammateTimezoneString);
const scheduledTimeInTeammateTimezone = getScheduledTimeInTeammateTimezone(tomorrow9amTime, teammateTimezoneString, locale);
const teammateTimeDisplay = (
<FormattedMessage
id='create_post_button.option.schedule_message.options.teammate_user_hour'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ body {
--gradient-center-color: #FFF;
--gradient-edge-color: rgba(255, 255, 255, 0);
position: absolute;
z-index: 100;
z-index: 1000;
top: 0px;
bottom: 0px;
display: flex;
Expand Down Expand Up @@ -68,9 +68,9 @@ body {
.LoadingAnimation {
--fade-duration: 150ms;
--colour: #1E325C;
--animation-initial-delay: 750ms;
--animation-start-duration: 750ms;
--animation-end-duration: 600ms;
--animation-initial-delay: 50ms;
--animation-start-duration: 400ms;
--animation-end-duration: 500ms;
--animation-spinner-speed: 500ms;
--animation-spinner-mask-stroke-length: 169.6;
--ease-in-cubic: cubic-bezier(0.32, 0, 0.67, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const ANIMATION_CLASS_FOR_MATTERMOST_LOGO_HIDE = 'LoadingAnimation__compass-shri
const ANIMATION_CLASS_FOR_COMPLETE_LOADER_HIDE = 'LoadingAnimation__shrink';

const DESTROY_DELAY_AFTER_ANIMATION_END = 1000;
const MINIMUM_LOADING_TIME = 2000; // Minimum time to show the loading screen (in ms)
const MINIMUM_LOADING_TIME = 1000; // Minimum time to show the loading screen (in ms)

const LOADING_CLASS_FOR_SCREEN = 'LoadingScreen';
const LOADING_COMPLETE_CLASS_FOR_SCREEN = 'LoadingScreen LoadingScreen--loaded';
Expand Down Expand Up @@ -123,6 +123,16 @@ export class InitialLoadingScreenClass {
return;
}

// Measure the actual load time before any artificial delays
measureAndReport({
name: Measure.SplashScreen,
startMark: 0,
canFail: false,
labels: {
page_type: pageType,
},
});

// Calculate how long the loading screen has been visible
const elapsedTime = this.startTime ? Date.now() - this.startTime : 0;
const remainingTime = Math.max(0, MINIMUM_LOADING_TIME - elapsedTime);
Expand All @@ -137,15 +147,6 @@ export class InitialLoadingScreenClass {

this.loadingScreenElement.className = LOADING_COMPLETE_CLASS_FOR_SCREEN;
this.loadingAnimationElement.className = LOADING_COMPLETE_CLASS_FOR_ANIMATION;

measureAndReport({
name: Measure.SplashScreen,
startMark: 0,
canFail: false,
labels: {
page_type: pageType,
},
});
}, remainingTime);
}
}
Expand Down
Loading