diff --git a/webapp/channels/src/actions/views/rhs.ts b/webapp/channels/src/actions/views/rhs.ts index 67fba87ea8..908d08a2be 100644 --- a/webapp/channels/src/actions/views/rhs.ts +++ b/webapp/channels/src/actions/views/rhs.ts @@ -14,13 +14,12 @@ import { searchFilesWithParams, } from 'mattermost-redux/actions/search'; import * as PostActions from 'mattermost-redux/actions/posts'; -import {getCurrentUserId, getCurrentUserMentionKeys} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserMentionKeys} from 'mattermost-redux/selectors/entities/users'; import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getCurrentChannelId, getCurrentChannelNameForSearchShortcut, getChannel as getChannelSelector} from 'mattermost-redux/selectors/entities/channels'; import {getPost} from 'mattermost-redux/selectors/entities/posts'; -import {makeGetUserTimezone} from 'mattermost-redux/selectors/entities/timezone'; -import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {Action, ActionResult, DispatchFunc, GenericAction, GetStateFunc} from 'mattermost-redux/types/actions'; import {Post} from '@mattermost/types/posts'; @@ -174,9 +173,7 @@ export function performSearch(terms: string, isMentionSearch?: boolean) { } // timezone offset in seconds - const userId = getCurrentUserId(getState()); - const userTimezone = makeGetUserTimezone()(getState(), userId); - const userCurrentTimezone = getUserCurrentTimezone(userTimezone); + const userCurrentTimezone = getCurrentTimezone(getState()); const timezoneOffset = ((userCurrentTimezone && (userCurrentTimezone.length > 0)) ? getUtcOffsetForTimeZone(userCurrentTimezone) : getBrowserUtcOffset()) * 60; const messagesPromise = dispatch(searchPostsWithParams(isMentionSearch ? '' : teamId, {terms, is_or_search: Boolean(isMentionSearch), include_deleted_channels: viewArchivedChannels, time_zone_offset: timezoneOffset, page: 0, per_page: 20})); const filesPromise = dispatch(searchFilesWithParams(teamId, {terms: termsWithExtensionsFilters, is_or_search: Boolean(isMentionSearch), include_deleted_channels: viewArchivedChannels, time_zone_offset: timezoneOffset, page: 0, per_page: 20})); diff --git a/webapp/channels/src/components/activity_and_insights/insights/top_channels/top_channels.tsx b/webapp/channels/src/components/activity_and_insights/insights/top_channels/top_channels.tsx index 86bb49fbc6..8d2dbb23e1 100644 --- a/webapp/channels/src/components/activity_and_insights/insights/top_channels/top_channels.tsx +++ b/webapp/channels/src/components/activity_and_insights/insights/top_channels/top_channels.tsx @@ -11,14 +11,13 @@ import {TopChannel, TopChannelGraphData} from '@mattermost/types/insights'; import {CircleSkeletonLoader, RectangleSkeletonLoader} from '@mattermost/components'; import {getCurrentRelativeTeamUrl, getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {getMyTopChannels, getTopChannelsForTeam} from 'mattermost-redux/actions/insights'; import Constants, {InsightsScopes} from 'utils/constants'; import {trackEvent} from 'actions/telemetry_actions'; -import {getCurrentUserTimezone} from 'selectors/general'; - import OverlayTrigger from 'components/overlay_trigger'; import Tooltip from 'components/tooltip'; @@ -38,7 +37,7 @@ const TopChannels = (props: WidgetHocProps) => { const currentTeamId = useSelector(getCurrentTeamId); const currentTeamUrl = useSelector(getCurrentRelativeTeamUrl); - const timeZone = useSelector(getCurrentUserTimezone); + const timeZone = useSelector(getCurrentTimezone); const getTopTeamChannels = useCallback(async () => { if (props.filterType === InsightsScopes.TEAM) { diff --git a/webapp/channels/src/components/custom_status/custom_status_emoji.test.tsx b/webapp/channels/src/components/custom_status/custom_status_emoji.test.tsx index 64c32488d1..7111f0c0fe 100644 --- a/webapp/channels/src/components/custom_status/custom_status_emoji.test.tsx +++ b/webapp/channels/src/components/custom_status/custom_status_emoji.test.tsx @@ -12,8 +12,8 @@ import mockStore from 'tests/test_store'; import CustomStatusEmoji from './custom_status_emoji'; +jest.mock('mattermost-redux/selectors/entities/timezone'); jest.mock('selectors/views/custom_status'); -jest.mock('selectors/general'); describe('components/custom_status/custom_status_emoji', () => { const store = mockStore({}); diff --git a/webapp/channels/src/components/custom_status/custom_status_emoji.tsx b/webapp/channels/src/components/custom_status/custom_status_emoji.tsx index 43ad92d45a..f4359fae1f 100644 --- a/webapp/channels/src/components/custom_status/custom_status_emoji.tsx +++ b/webapp/channels/src/components/custom_status/custom_status_emoji.tsx @@ -6,8 +6,9 @@ import {useSelector} from 'react-redux'; import {CustomStatusDuration} from '@mattermost/types/users'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; + import {GlobalState} from 'types/store'; -import {getCurrentUserTimezone} from 'selectors/general'; import {makeGetCustomStatus, isCustomStatusEnabled, isCustomStatusExpired} from 'selectors/views/custom_status'; import Constants from 'utils/constants'; @@ -41,7 +42,7 @@ function CustomStatusEmoji({ const getCustomStatus = useMemo(makeGetCustomStatus, []); const customStatus = useSelector((state: GlobalState) => getCustomStatus(state, userID)); - const timezone = useSelector(getCurrentUserTimezone); + const timezone = useSelector(getCurrentTimezone); const customStatusExpired = useSelector((state: GlobalState) => isCustomStatusExpired(state, customStatus)); const customStatusEnabled = useSelector(isCustomStatusEnabled); diff --git a/webapp/channels/src/components/custom_status/custom_status_modal.tsx b/webapp/channels/src/components/custom_status/custom_status_modal.tsx index 35844581f4..28f2efd9aa 100644 --- a/webapp/channels/src/components/custom_status/custom_status_modal.tsx +++ b/webapp/channels/src/components/custom_status/custom_status_modal.tsx @@ -11,6 +11,7 @@ import {useRouteMatch} from 'react-router-dom'; import {setCustomStatus, unsetCustomStatus, removeRecentCustomStatus} from 'mattermost-redux/actions/users'; import {setCustomStatusInitialisationState} from 'mattermost-redux/actions/preferences'; import {Preferences} from 'mattermost-redux/constants'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {loadCustomEmojisIfNeeded} from 'actions/emoji_actions'; import {closeModal} from 'actions/views/modals'; @@ -20,7 +21,6 @@ import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay'; import RenderEmoji from 'components/emoji/render_emoji'; import QuickInput, {MaxLengthInput} from 'components/quick_input'; import {makeGetCustomStatus, getRecentCustomStatuses, showStatusDropdownPulsatingDot, isCustomStatusExpired} from 'selectors/views/custom_status'; -import {getCurrentUserTimezone} from 'selectors/general'; import {GlobalState} from 'types/store'; import {getCurrentMomentForTimezone} from 'utils/timezone'; import {A11yCustomEventTypes, A11yFocusEventDetail, Constants, ModalIdentifiers} from 'utils/constants'; @@ -115,7 +115,7 @@ const CustomStatusModal: React.FC = (props: Props) => { const [duration, setDuration] = useState(initialDuration === undefined ? defaultDuration : initialDuration); const isStatusSet = Boolean(emoji || text); const firstTimeModalOpened = useSelector(showStatusDropdownPulsatingDot); - const timezone = useSelector(getCurrentUserTimezone); + const timezone = useSelector(getCurrentTimezone); const inCustomEmojiPath = useRouteMatch('/:team/emoji'); const currentTime = getCurrentMomentForTimezone(timezone); diff --git a/webapp/channels/src/components/dot_menu/index.ts b/webapp/channels/src/components/dot_menu/index.ts index e9d765ba59..1aea3d476c 100644 --- a/webapp/channels/src/components/dot_menu/index.ts +++ b/webapp/channels/src/components/dot_menu/index.ts @@ -12,7 +12,7 @@ import {getCurrentTeamId, getCurrentTeam, getTeam} from 'mattermost-redux/select import {makeGetThreadOrSynthetic} from 'mattermost-redux/selectors/entities/threads'; import {getPost} from 'mattermost-redux/selectors/entities/posts'; import {getBool, isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences'; -import {getCurrentUserTimezone} from 'selectors/general'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {isSystemMessage} from 'mattermost-redux/utils/post_utils'; import {GenericAction} from 'mattermost-redux/types/actions'; import {setThreadFollow} from 'mattermost-redux/actions/threads'; @@ -126,7 +126,7 @@ function makeMapStateToProps() { isCollapsedThreadsEnabled: collapsedThreads, threadReplyCount, isMobileView: getIsMobileView(state), - timezone: getCurrentUserTimezone(state), + timezone: getCurrentTimezone(state), isMilitaryTime, }; }; diff --git a/webapp/channels/src/components/post_markdown/index.ts b/webapp/channels/src/components/post_markdown/index.ts index 9de0edbf37..750d91d793 100644 --- a/webapp/channels/src/components/post_markdown/index.ts +++ b/webapp/channels/src/components/post_markdown/index.ts @@ -19,12 +19,11 @@ import {getBool} from 'mattermost-redux/selectors/entities/preferences'; import {Preferences} from 'utils/constants'; import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {Channel} from '@mattermost/types/channels'; import {Post} from '@mattermost/types/posts'; -import {getCurrentUserTimezone} from '../../selectors/general'; - import PostMarkdown from './post_markdown'; export function makeGetMentionKeysForPost(): ( @@ -75,7 +74,7 @@ function makeMapStateToProps() { isUserCanManageMembers: channel && canManageMembers(state, channel), mentionKeys: getMentionKeysForPost(state, ownProps.post, channel), isMilitaryTime: getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false), - timezone: getCurrentUserTimezone(state), + timezone: getCurrentTimezone(state), }; }; } diff --git a/webapp/channels/src/components/post_reminder_custom_time_picker_modal/index.ts b/webapp/channels/src/components/post_reminder_custom_time_picker_modal/index.ts index 32fd6c0fbf..2235ab865c 100644 --- a/webapp/channels/src/components/post_reminder_custom_time_picker_modal/index.ts +++ b/webapp/channels/src/components/post_reminder_custom_time_picker_modal/index.ts @@ -10,17 +10,16 @@ import {Preferences} from 'mattermost-redux/constants'; import {addPostReminder} from 'mattermost-redux/actions/posts'; import {getBool} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {GlobalState} from 'types/store'; import {makeAsyncComponent} from 'components/async_load'; -import {getCurrentUserTimezone} from 'selectors/general'; - const PostReminderCustomTimePicker = makeAsyncComponent('PostReminderCustomTimePicker', React.lazy(() => import('./post_reminder_custom_time_picker_modal'))); function mapStateToProps(state: GlobalState) { - const timezone = getCurrentUserTimezone(state); + const timezone = getCurrentTimezone(state); const userId = getCurrentUserId(state); const isMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false); diff --git a/webapp/channels/src/components/post_view/post_edited_indicator/index.ts b/webapp/channels/src/components/post_view/post_edited_indicator/index.ts index e18489f17a..a5613ece53 100644 --- a/webapp/channels/src/components/post_view/post_edited_indicator/index.ts +++ b/webapp/channels/src/components/post_view/post_edited_indicator/index.ts @@ -6,18 +6,16 @@ import {connect} from 'react-redux'; import {bindActionCreators, Dispatch} from 'redux'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/common'; -import {makeGetUserTimezone} from 'mattermost-redux/selectors/entities/timezone'; -import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; import {getBool} from 'mattermost-redux/selectors/entities/preferences'; import {getPost} from 'mattermost-redux/selectors/entities/posts'; import {getPostEditHistory} from 'mattermost-redux/actions/posts'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; import {getChannel} from 'mattermost-redux/selectors/entities/channels'; +import {getCurrentTimezone, isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; import {Preferences} from 'utils/constants'; import {isPostOwner, canEditPost} from 'utils/post_utils'; -import {areTimezonesEnabledAndSupported} from '../../../selectors/general'; import {GlobalState} from '../../../types/store'; import {Props as TimestampProps} from '../../timestamp/timestamp'; @@ -49,27 +47,23 @@ type DispatchProps = { export type Props = OwnProps & StateProps & DispatchProps; -function makeMapStateToProps() { - const getUserTimezone = makeGetUserTimezone(); +function mapStateToProps(state: GlobalState, ownProps: OwnProps): StateProps { + const currentUserId = getCurrentUserId(state); + const post = ownProps.postId ? getPost(state, ownProps.postId) : undefined; + const license = getLicense(state); + const config = getConfig(state); + const channel = getChannel(state, post?.channel_id || ''); - return (state: GlobalState, ownProps: OwnProps): StateProps => { - const currentUserId = getCurrentUserId(state); - const post = ownProps.postId ? getPost(state, ownProps.postId) : undefined; - const license = getLicense(state); - const config = getConfig(state); - const channel = getChannel(state, post?.channel_id || ''); + let timeZone: TimestampProps['timeZone']; - let timeZone: TimestampProps['timeZone']; + if (isTimezoneEnabled(state)) { + timeZone = getCurrentTimezone(state); + } + const postOwner = post ? isPostOwner(state, post) : undefined; - if (areTimezonesEnabledAndSupported(state)) { - timeZone = getUserCurrentTimezone(getUserTimezone(state, currentUserId)) ?? undefined; - } - const postOwner = post ? isPostOwner(state, post) : undefined; - - const isMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false); - const canEdit = post ? canEditPost(state, post, license, config, channel, currentUserId) : false; - return {isMilitaryTime, timeZone, postOwner, post, canEdit}; - }; + const isMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false); + const canEdit = post ? canEditPost(state, post, license, config, channel, currentUserId) : false; + return {isMilitaryTime, timeZone, postOwner, post, canEdit}; } function mapDispatchToProps(dispatch: Dispatch) { @@ -81,4 +75,4 @@ function mapDispatchToProps(dispatch: Dispatch) { }; } -export default connect(makeMapStateToProps, mapDispatchToProps)(PostEditedIndicator); +export default connect(mapStateToProps, mapDispatchToProps)(PostEditedIndicator); diff --git a/webapp/channels/src/components/profile_popover/index.ts b/webapp/channels/src/components/profile_popover/index.ts index dc6ab6a9db..0af4f863f1 100644 --- a/webapp/channels/src/components/profile_popover/index.ts +++ b/webapp/channels/src/components/profile_popover/index.ts @@ -19,12 +19,12 @@ import { import {getCallsConfig, getCalls} from 'mattermost-redux/selectors/entities/common'; import {Action} from 'mattermost-redux/types/actions'; import {getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentTimezone, isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; import {openDirectChannelToUserId} from 'actions/channel_actions'; import {getMembershipForEntities} from 'actions/views/profile_popover'; import {closeModal, openModal} from 'actions/views/modals'; -import {areTimezonesEnabledAndSupported, getCurrentUserTimezone} from 'selectors/general'; import {getRhsState, getSelectedPost} from 'selectors/rhs'; import {getIsMobileView} from 'selectors/views/browser'; import {isAnyModalOpen} from 'selectors/views/modals'; @@ -98,7 +98,7 @@ function makeMapStateToProps() { return { currentTeamId: team.id, currentUserId, - enableTimezone: areTimezonesEnabledAndSupported(state), + enableTimezone: isTimezoneEnabled(state), isTeamAdmin, isChannelAdmin, isInCurrentTeam: Boolean(teamMember) && teamMember?.delete_at === 0, @@ -111,7 +111,7 @@ function makeMapStateToProps() { isCustomStatusEnabled: isCustomStatusEnabled(state), isCustomStatusExpired: isCustomStatusExpired(state, customStatus), channelId, - currentUserTimezone: getCurrentUserTimezone(state), + currentUserTimezone: getCurrentTimezone(state), lastActivityTimestamp, enableLastActiveTime, timestampUnits, diff --git a/webapp/channels/src/components/profile_popover/profile_popover.tsx b/webapp/channels/src/components/profile_popover/profile_popover.tsx index da883d7c42..ba8419766a 100644 --- a/webapp/channels/src/components/profile_popover/profile_popover.tsx +++ b/webapp/channels/src/components/profile_popover/profile_popover.tsx @@ -40,7 +40,7 @@ import Tooltip from 'components/tooltip'; import ProfilePopoverCallButton from 'components/profile_popover_call_button'; import {ServerError} from '@mattermost/types/errors'; -import {UserCustomStatus, UserProfile, UserTimezone, CustomStatusDuration} from '@mattermost/types/users'; +import {UserCustomStatus, UserProfile, CustomStatusDuration} from '@mattermost/types/users'; import './profile_popover.scss'; import BotTag from '../widgets/tag/bot_tag'; @@ -587,7 +587,7 @@ class ProfilePopover extends React.PureComponent { - const currentUserId = getCurrentUserId(state); - const userTimezone = getUserTimezone(state, currentUserId); - const locale = getCurrentLocale(state); + const enableTimezone = isTimezoneEnabled(state); - const enableTimezone = areTimezonesEnabledAndSupported(state); + let currentDate; + if (enableTimezone) { + currentDate = getCurrentDateForTimezone(timezone); + } - let currentDate; - if (enableTimezone) { - if (userTimezone.useAutomaticTimezone) { - currentDate = getCurrentDateForTimezone(userTimezone.automaticTimezone); - } else { - currentDate = getCurrentDateForTimezone(userTimezone.manualTimezone); - } - } - - return { - currentDate, - locale, - }; + return { + currentDate, + locale, }; } -export default connect(makeMapStateToProps)(SearchDateSuggestion); +export default connect(mapStateToProps)(SearchDateSuggestion); diff --git a/webapp/channels/src/components/timestamp/index.test.tsx b/webapp/channels/src/components/timestamp/index.test.tsx index 182c7e4b8a..ecbaa75f5b 100644 --- a/webapp/channels/src/components/timestamp/index.test.tsx +++ b/webapp/channels/src/components/timestamp/index.test.tsx @@ -1,13 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import mergeObjects from 'packages/mattermost-redux/test/merge_objects'; + import {GlobalState} from 'types/store'; -import {UserTimezone} from '@mattermost/types/users'; -import {PreferenceType} from '@mattermost/types/preferences'; import * as Timestamp from './timestamp'; -import {makeMapStateToProps} from './index'; +import {mapStateToProps} from './index'; const supportsHourCycleOg = Timestamp.supportsHourCycle; Object.defineProperty(Timestamp, 'supportsHourCycle', {get: () => supportsHourCycleOg}); @@ -39,57 +39,89 @@ describe('mapStateToProps', () => { describe('hourCycle', () => { test('hourCycle should be h12 when military time is false and the prop was not set', () => { - const props = makeMapStateToProps()(initialState, {}); + const props = mapStateToProps(initialState, {}); expect(props.hourCycle).toBe('h12'); }); test('hourCycle should be h23 when military time is true and the prop was not set', () => { - const testState = {...initialState}; - testState.entities.preferences.myPreferences['display_settings--use_military_time'] = { - category: 'display_settings', - name: 'use_military_time', - user_id: currentUserId, - value: 'true', - } as PreferenceType; + const testState = mergeObjects(initialState, { + entities: { + preferences: { + myPreferences: { + 'display_settings--use_military_time': { + category: 'display_settings', + name: 'use_military_time', + user_id: currentUserId, + value: 'true', + }, + }, + }, + }, + }); - const props = makeMapStateToProps()(testState, {}); + const props = mapStateToProps(testState, {}); expect(props.hourCycle).toBe('h23'); }); test('hourCycle should have the value of prop.hourCycle when given', () => { - const testState = {...initialState}; - testState.entities.preferences.myPreferences['display_settings--use_military_time'] = { - category: 'display_settings', - name: 'use_military_time', - user_id: currentUserId, - value: 'true', - } as PreferenceType; + const testState = mergeObjects(initialState, { + entities: { + preferences: { + myPreferences: { + 'display_settings--use_military_time': { + category: 'display_settings', + name: 'use_military_time', + user_id: currentUserId, + value: 'true', + }, + }, + }, + }, + }); - const props = makeMapStateToProps()(testState, {hourCycle: 'h24'}); + const props = mapStateToProps(testState, {hourCycle: 'h24'}); expect(props.hourCycle).toBe('h24'); }); }); describe('timeZone', () => { test('timeZone should be the user TZ when the prop was not set', () => { - const testState = {...initialState}; - testState.entities.users.profiles[currentUserId].timezone = { - useAutomaticTimezone: false, - manualTimezone: 'Europe/Paris', - } as UserTimezone; + const testState = mergeObjects(initialState, { + entities: { + users: { + profiles: { + [currentUserId]: { + timezone: { + useAutomaticTimezone: false, + manualTimezone: 'Europe/Paris', + }, + }, + }, + }, + }, + }); - const props = makeMapStateToProps()(testState, {}); + const props = mapStateToProps(testState, {}); expect(props.timeZone).toBe('Europe/Paris'); }); test('timeZone should be the value of prop.timeZone when given', () => { - const testState = {...initialState}; - testState.entities.users.profiles[currentUserId].timezone = { - useAutomaticTimezone: false, - manualTimezone: 'Europe/Paris', - } as UserTimezone; + const testState = mergeObjects(initialState, { + entities: { + users: { + profiles: { + [currentUserId]: { + timezone: { + useAutomaticTimezone: false, + manualTimezone: 'Europe/Paris', + }, + }, + }, + }, + }, + }); - const props = makeMapStateToProps()(testState, {timeZone: 'America/Phoenix'}); + const props = mapStateToProps(testState, {timeZone: 'America/Phoenix'}); expect(props.timeZone).toBe('America/Phoenix'); }); @@ -97,51 +129,72 @@ describe('mapStateToProps', () => { const testState = {...initialState}; testState.entities.general.config.ExperimentalTimezone = 'false'; - const props = makeMapStateToProps()(testState, {timeZone: 'America/Chicago'}); + const props = mapStateToProps(testState, {timeZone: 'America/Chicago'}); expect(props.timeZone).toBe('America/Chicago'); }); }); describe('hour12, hourCycle unsupported', () => { test('hour12 should be false when using military time', () => { - const testState = {...initialState}; - testState.entities.preferences.myPreferences['display_settings--use_military_time'] = { - category: 'display_settings', - name: 'use_military_time', - user_id: currentUserId, - value: 'true', - } as PreferenceType; + const testState = mergeObjects(initialState, { + entities: { + preferences: { + myPreferences: { + 'display_settings--use_military_time': { + category: 'display_settings', + name: 'use_military_time', + user_id: currentUserId, + value: 'true', + }, + }, + }, + }, + }); supportsHourCycleSpy.mockReturnValueOnce(false); - const props = makeMapStateToProps()(testState, {}); + const props = mapStateToProps(testState, {}); expect(props.hour12).toBe(false); }); test('hour12 should be true when not using military time', () => { - const testState = {...initialState}; - testState.entities.preferences.myPreferences['display_settings--use_military_time'] = { - category: 'display_settings', - name: 'use_military_time', - user_id: currentUserId, - value: 'false', - } as PreferenceType; + const testState = mergeObjects(initialState, { + entities: { + preferences: { + myPreferences: { + 'display_settings--use_military_time': { + category: 'display_settings', + name: 'use_military_time', + user_id: currentUserId, + value: 'false', + }, + }, + }, + }, + }); supportsHourCycleSpy.mockReturnValueOnce(false); - const props = makeMapStateToProps()(testState, {}); + const props = mapStateToProps(testState, {}); expect(props.hour12).toBe(true); }); test('hour12 should equal props.hour12 when defined', () => { - const testState = {...initialState}; - testState.entities.preferences.myPreferences['display_settings--use_military_time'] = { - category: 'display_settings', - name: 'use_military_time', - user_id: currentUserId, - value: 'false', - } as PreferenceType; + const testState = mergeObjects(initialState, { + entities: { + preferences: { + myPreferences: { + 'display_settings--use_military_time': { + category: 'display_settings', + name: 'use_military_time', + user_id: currentUserId, + value: 'false8', + }, + }, + }, + }, + }); supportsHourCycleSpy.mockReturnValueOnce(false); - const props = makeMapStateToProps()(testState, {hour12: false}); + const props = mapStateToProps(testState, {hour12: false}); expect(props.hour12).toBe(false); }); }); diff --git a/webapp/channels/src/components/timestamp/index.ts b/webapp/channels/src/components/timestamp/index.ts index b3803ec0c1..a81d8fa389 100644 --- a/webapp/channels/src/components/timestamp/index.ts +++ b/webapp/channels/src/components/timestamp/index.ts @@ -3,14 +3,11 @@ import {connect} from 'react-redux'; -import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; -import {makeGetUserTimezone} from 'mattermost-redux/selectors/entities/timezone'; +import {getCurrentTimezoneFull, isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; import {getBool} from 'mattermost-redux/selectors/entities/preferences'; import {UserTimezone} from '@mattermost/types/users'; -import {areTimezonesEnabledAndSupported} from 'selectors/general'; - import {GlobalState} from 'types/store'; import {Preferences} from 'utils/constants'; @@ -24,33 +21,27 @@ type Props = { hourCycle?: TimestampProps['hourCycle']; } -export function makeMapStateToProps() { - const getUserTimezone = makeGetUserTimezone(); +export function mapStateToProps(state: GlobalState, ownProps: Props) { + let timeZone: TimestampProps['timeZone']; + let hourCycle: TimestampProps['hourCycle']; + let hour12: TimestampProps['hour12']; - return (state: GlobalState, ownProps: Props) => { - const currentUserId = getCurrentUserId(state); + if (isTimezoneEnabled(state)) { + timeZone = getUserCurrentTimezone(ownProps.userTimezone ?? getCurrentTimezoneFull(state)) ?? undefined; + } - let timeZone: TimestampProps['timeZone']; - let hourCycle: TimestampProps['hourCycle']; - let hour12: TimestampProps['hour12']; + const useMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false); - if (areTimezonesEnabledAndSupported(state)) { - timeZone = getUserCurrentTimezone(ownProps.userTimezone ?? getUserTimezone(state, currentUserId)) ?? undefined; - } + if (supportsHourCycle) { + hourCycle = ownProps.hourCycle || (useMilitaryTime ? 'h23' : 'h12'); + } else { + hour12 = ownProps.hour12 ?? (!useMilitaryTime); + } - const useMilitaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false); - - if (supportsHourCycle) { - hourCycle = ownProps.hourCycle || (useMilitaryTime ? 'h23' : 'h12'); - } else { - hour12 = ownProps.hour12 ?? (!useMilitaryTime); - } - - return {timeZone: ownProps.timeZone || timeZone, hourCycle, hour12}; - }; + return {timeZone: ownProps.timeZone || timeZone, hourCycle, hour12}; } -export default connect(makeMapStateToProps)(Timestamp); +export default connect(mapStateToProps)(Timestamp); export {default as SemanticTime} from './semantic_time'; import * as RelativeRanges from './relative_ranges'; diff --git a/webapp/channels/src/components/user_settings/display/index.ts b/webapp/channels/src/components/user_settings/display/index.ts index 725568b55e..fec3c71690 100644 --- a/webapp/channels/src/components/user_settings/display/index.ts +++ b/webapp/channels/src/components/user_settings/display/index.ts @@ -15,7 +15,7 @@ import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users'; import {get, isCollapsedThreadsAllowed, getCollapsedThreadsPreference} from 'mattermost-redux/selectors/entities/preferences'; -import {getTimezoneLabel, makeGetUserTimezone} from 'mattermost-redux/selectors/entities/timezone'; +import {getCurrentTimezoneFull, getCurrentTimezoneLabel} from 'mattermost-redux/selectors/entities/timezone'; import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; import {CollapsedThreads} from '@mattermost/types/config'; @@ -34,15 +34,13 @@ type Actions = { } export function makeMapStateToProps() { - const getUserTimezone = makeGetUserTimezone(); - return (state: GlobalState) => { const config = getConfig(state); const currentUserId = getCurrentUserId(state); - const userTimezone = getUserTimezone(state, currentUserId); + const userTimezone = getCurrentTimezoneFull(state); const automaticTimezoneNotSet = userTimezone && userTimezone.useAutomaticTimezone && !userTimezone.automaticTimezone; const shouldAutoUpdateTimezone = !userTimezone || automaticTimezoneNotSet; - const timezoneLabel = getTimezoneLabel(state, currentUserId); + const timezoneLabel = getCurrentTimezoneLabel(state); const allowCustomThemes = config.AllowCustomThemes === 'true'; const enableLinkPreviews = config.EnableLinkPreviews === 'true'; const defaultClientLocale = config.DefaultClientLocale as string; diff --git a/webapp/channels/src/components/user_settings/display/manage_timezones/index.ts b/webapp/channels/src/components/user_settings/display/manage_timezones/index.ts index 0730f27d06..46daa4d0f2 100644 --- a/webapp/channels/src/components/user_settings/display/manage_timezones/index.ts +++ b/webapp/channels/src/components/user_settings/display/manage_timezones/index.ts @@ -10,9 +10,7 @@ import {updateMe} from 'mattermost-redux/actions/users'; import {ActionFunc, ActionResult} from 'mattermost-redux/types/actions'; import {UserProfile} from '@mattermost/types/users'; import {GlobalState} from '@mattermost/types/store'; -import {getTimezoneLabel} from 'mattermost-redux/selectors/entities/timezone'; - -import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentTimezoneLabel} from 'mattermost-redux/selectors/entities/timezone'; import ManageTimezones from './manage_timezones'; @@ -27,8 +25,7 @@ function mapDispatchToProps(dispatch: Dispatch) { }, dispatch)}; } function mapStateToProps(state: GlobalState) { - const currentUserId = getCurrentUserId(state); - const timezoneLabel = getTimezoneLabel(state, currentUserId); + const timezoneLabel = getCurrentTimezoneLabel(state); return { timezones, timezoneLabel, diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/timezone.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/timezone.ts index 655088a0fa..823a519af1 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/timezone.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/timezone.ts @@ -2,15 +2,16 @@ // See LICENSE.txt for license information. import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; -import {makeGetUserTimezone} from 'mattermost-redux/selectors/entities/timezone'; +import {getCurrentTimezoneFull} from 'mattermost-redux/selectors/entities/timezone'; import {DispatchFunc, GetStateFunc} from 'mattermost-redux/types/actions'; import {updateMe} from './users'; + export function autoUpdateTimezone(deviceTimezone: string) { return async (dispatch: DispatchFunc, getState: GetStateFunc) => { const currentUser = getCurrentUser(getState()); - const currentTimezone = makeGetUserTimezone()(getState(), currentUser.id); + const currentTimezone = getCurrentTimezoneFull(getState()); const newTimezoneExists = currentTimezone.automaticTimezone !== deviceTimezone; if (currentTimezone.useAutomaticTimezone && newTimezoneExists) { diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/timezone.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/timezone.ts index 51505d5de5..5ff93935cd 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/timezone.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/timezone.ts @@ -1,17 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import timezones, {Timezone} from 'timezones.json'; - -import {getUser} from 'mattermost-redux/selectors/entities/users'; +import timezones from 'timezones.json'; import {GlobalState} from '@mattermost/types/store'; -import {UserProfile, UserTimezone} from '@mattermost/types/users'; +import {UserProfile} from '@mattermost/types/users'; import {createSelector} from 'reselect'; -import {getUserCurrentTimezone, getTimezoneLabel as getTimezoneLabelUtil} from 'mattermost-redux/utils/timezone_utils'; +import {getTimezoneLabel, getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils'; -export function getTimezoneForUserProfile(profile: UserProfile) { +import {getCurrentUser} from './common'; + +function getTimezoneForUserProfile(profile: UserProfile) { if (profile && profile.timezone) { return { ...profile.timezone, @@ -31,23 +31,30 @@ export function isTimezoneEnabled(state: GlobalState) { return config.ExperimentalTimezone === 'true'; } -export const makeGetUserTimezone = () => createSelector( - 'makeGetUserTimezone', - (state: GlobalState, userId: string) => getUser(state, userId), - (user: UserProfile) => { - return getTimezoneForUserProfile(user); +export const getCurrentTimezoneFull = createSelector( + 'getCurrentTimezoneFull', + getCurrentUser, + (currentUser) => { + return getTimezoneForUserProfile(currentUser); }, ); -export const getTimezoneLabel: (state: GlobalState, userId: UserProfile['id']) => string = createSelector( - 'getTimezoneLabel', - () => timezones, - makeGetUserTimezone(), - (timezones: Timezone[], timezoneObject: UserTimezone) => { - const timezone = getUserCurrentTimezone(timezoneObject); +export const getCurrentTimezone = createSelector( + 'getCurrentTimezone', + getCurrentTimezoneFull, + (timezoneFull) => { + return getUserCurrentTimezone(timezoneFull); + }, +); + +export const getCurrentTimezoneLabel = createSelector( + 'getCurrentTimezoneLabel', + getCurrentTimezone, + (timezone) => { if (!timezone) { return ''; } - return getTimezoneLabelUtil(timezones, timezone); + + return getTimezoneLabel(timezones, timezone); }, ); diff --git a/webapp/channels/src/packages/mattermost-redux/src/utils/timezone_utils.ts b/webapp/channels/src/packages/mattermost-redux/src/utils/timezone_utils.ts index b50c3d230d..afcb08cfe5 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/utils/timezone_utils.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/utils/timezone_utils.ts @@ -5,9 +5,9 @@ import {Timezone} from 'timezones.json'; import {UserTimezone} from '@mattermost/types/users'; -export function getUserCurrentTimezone(userTimezone?: UserTimezone): string | undefined | null { +export function getUserCurrentTimezone(userTimezone?: UserTimezone): string { if (!userTimezone) { - return null; + return 'UTC'; } const { useAutomaticTimezone, diff --git a/webapp/channels/src/selectors/general.ts b/webapp/channels/src/selectors/general.ts index 1d7f84d00e..e38fea69c8 100644 --- a/webapp/channels/src/selectors/general.ts +++ b/webapp/channels/src/selectors/general.ts @@ -1,13 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {createSelector} from 'reselect'; - -import {getCurrentUser} from 'mattermost-redux/selectors/entities/common'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; -import {getTimezoneForUserProfile} from 'mattermost-redux/selectors/entities/timezone'; - -import * as UserAgent from 'utils/user_agent'; import type {GlobalState} from 'types/store'; @@ -17,15 +11,6 @@ declare global { } } -export function areTimezonesEnabledAndSupported(state: GlobalState) { - if (UserAgent.isInternetExplorer()) { - return false; - } - - const config = getConfig(state); - return config.ExperimentalTimezone === 'true'; -} - export function getBasePath(state: GlobalState) { const config = getConfig(state) || {}; @@ -36,21 +21,6 @@ export function getBasePath(state: GlobalState) { return window.basename || '/'; } -export const getCurrentUserTimezone = createSelector( - 'getCurrentUserTimezone', - getCurrentUser, - areTimezonesEnabledAndSupported, - (user, enabledTimezone) => { - let timezone; - if (enabledTimezone) { - const userTimezone = getTimezoneForUserProfile(user); - timezone = userTimezone.useAutomaticTimezone ? userTimezone.automaticTimezone : userTimezone.manualTimezone; - } - - return timezone; - }, -); - export function getConnectionId(state: GlobalState) { return state.websocket.connectionId; } diff --git a/webapp/channels/src/selectors/views/custom_status.ts b/webapp/channels/src/selectors/views/custom_status.ts index 6d7742ccb4..cd536331a7 100644 --- a/webapp/channels/src/selectors/views/custom_status.ts +++ b/webapp/channels/src/selectors/views/custom_status.ts @@ -5,6 +5,7 @@ import moment from 'moment-timezone'; import {createSelector} from 'reselect'; +import {getCurrentTimezone} from 'mattermost-redux/selectors/entities/timezone'; import {getCurrentUser, getUser} from 'mattermost-redux/selectors/entities/users'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; @@ -15,7 +16,6 @@ import {CustomStatusDuration, UserCustomStatus} from '@mattermost/types/users'; import {isDateWithinDaysRange, TimeInformation} from 'utils/utils'; import {GlobalState} from 'types/store'; -import {getCurrentUserTimezone} from 'selectors/general'; import {getCurrentMomentForTimezone} from 'utils/timezone'; export function makeGetCustomStatus(): (state: GlobalState, userID?: string) => UserCustomStatus { @@ -39,7 +39,7 @@ export function isCustomStatusExpired(state: GlobalState, customStatus?: UserCus } const expiryTime = moment(customStatus.expires_at); - const timezone = getCurrentUserTimezone(state); + const timezone = getCurrentTimezone(state); const currentTime = getCurrentMomentForTimezone(timezone); return currentTime.isSameOrAfter(expiryTime); }