From fe3f0cc8927c9bd592c7a3a0739860c367801796 Mon Sep 17 00:00:00 2001 From: Avinash Lingaloo Date: Fri, 24 Jun 2022 14:52:05 +0400 Subject: [PATCH] MM-39711 - Gekidou Settings Timezone (#6384) --- app/actions/remote/retry.ts | 2 +- app/actions/remote/user.ts | 12 ++ app/components/search/index.tsx | 2 +- app/constants/screens.ts | 5 +- .../transformers/group.test.ts | 4 +- app/queries/servers/post.ts | 2 +- app/queries/servers/preference.ts | 2 +- app/screens/edit_profile/index.ts | 3 +- .../categories_list/categories/body/index.ts | 2 +- app/screens/in_app_notification/icon.tsx | 2 +- app/screens/index.tsx | 6 + app/screens/permalink/index.ts | 3 +- .../reactions/reactors_list/reactor/index.ts | 2 +- app/screens/settings/display/display.tsx | 16 +- app/screens/settings/display/index.tsx | 3 +- .../settings/display_clock/display_clock.tsx | 3 +- app/screens/settings/display_clock/index.tsx | 3 +- app/screens/settings/display_theme/index.ts | 3 +- .../display_timezone/display_timezone.tsx | 163 ++++++++++++++++++ .../settings/display_timezone/index.tsx | 20 +++ .../display_timezone_select/index.tsx | 143 +++++++++++++++ .../display_timezone_select/timezone_row.tsx | 69 ++++++++ .../notification_auto_responder/index.ts | 3 +- .../settings/notification_mention/index.tsx | 3 +- .../notification_mention/mention_settings.tsx | 3 +- .../settings/notification_push/index.tsx | 3 +- app/screens/settings/notifications/index.tsx | 3 +- app/utils/user/index.ts | 11 ++ assets/base/i18n/en.json | 4 + 29 files changed, 470 insertions(+), 30 deletions(-) create mode 100644 app/screens/settings/display_timezone/display_timezone.tsx create mode 100644 app/screens/settings/display_timezone/index.tsx create mode 100644 app/screens/settings/display_timezone_select/index.tsx create mode 100644 app/screens/settings/display_timezone_select/timezone_row.tsx diff --git a/app/actions/remote/retry.ts b/app/actions/remote/retry.ts index e5f006c08..783a5e4ce 100644 --- a/app/actions/remote/retry.ts +++ b/app/actions/remote/retry.ts @@ -12,7 +12,6 @@ import {prepareMyPreferences, queryPreferencesByCategoryAndName} from '@queries/ import {prepareCommonSystemValues, getCommonSystemValues} from '@queries/servers/system'; import {prepareMyTeams} from '@queries/servers/team'; import {getCurrentUser} from '@queries/servers/user'; -import TeamModel from '@typings/database/models/servers/team'; import {isDMorGM, selectDefaultChannelForTeam} from '@utils/channel'; import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, MyChannelsRequest} from './channel'; @@ -23,6 +22,7 @@ import {ConfigAndLicenseRequest, fetchConfigAndLicense} from './systems'; import {fetchMyTeams, MyTeamsRequest} from './team'; import type {Model} from '@nozbe/watermelondb'; +import type TeamModel from '@typings/database/models/servers/team'; export async function retryInitialTeamAndChannel(serverUrl: string) { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; diff --git a/app/actions/remote/user.ts b/app/actions/remote/user.ts index 528ef3dfc..430c7df78 100644 --- a/app/actions/remote/user.ts +++ b/app/actions/remote/user.ts @@ -867,3 +867,15 @@ export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: s return {error}; } }; + +export const getAllSupportedTimezones = async (serverUrl: string) => { + try { + const client = NetworkManager.getClient(serverUrl); + const allTzs = await client.getTimezones(); + return allTzs; + } catch (error) { + // eslint-disable-next-line no-console + console.log('FAILED TO GET ALL TIMEZONES', error); + return []; + } +}; diff --git a/app/components/search/index.tsx b/app/components/search/index.tsx index 57343823f..29e2c47b5 100644 --- a/app/components/search/index.tsx +++ b/app/components/search/index.tsx @@ -30,8 +30,8 @@ export type SearchProps = TextInputProps & { containerStyle?: StyleProp; inputContainerStyle?: StyleProp; inputStyle?: StyleProp; - loadingProps?: ActivityIndicatorProps; leftIconContainerStyle?: StyleProp; + loadingProps?: ActivityIndicatorProps; onCancel?(): void; onClear?(): void; rightIconContainerStyle?: StyleProp; diff --git a/app/constants/screens.ts b/app/constants/screens.ts index 7b3c963e0..84f655f12 100644 --- a/app/constants/screens.ts +++ b/app/constants/screens.ts @@ -8,7 +8,6 @@ export const BOTTOM_SHEET = 'BottomSheet'; export const BROWSE_CHANNELS = 'BrowseChannels'; export const CHANNEL = 'Channel'; export const CHANNEL_ADD_PEOPLE = 'ChannelAddPeople'; -export const CHANNEL_EDIT = 'ChannelEdit'; export const CHANNEL_INFO = 'ChannelInfo'; export const CHANNEL_MENTION = 'ChannelMention'; export const CODE = 'Code'; @@ -45,6 +44,8 @@ export const SETTINGS = 'Settings'; export const SETTINGS_DISPLAY = 'SettingsDisplay'; export const SETTINGS_DISPLAY_CLOCK = 'SettingsDisplayClock'; export const SETTINGS_DISPLAY_THEME = 'SettingsDisplayTheme'; +export const SETTINGS_DISPLAY_TIMEZONE = 'SettingsDisplayTimezone'; +export const SETTINGS_DISPLAY_TIMEZONE_SELECT = 'SettingsDisplayTimezoneSelect'; export const SETTINGS_NOTIFICATION = 'SettingsNotification'; export const SETTINGS_NOTIFICATION_AUTO_RESPONDER = 'SettingsNotificationAutoResponder'; export const SETTINGS_NOTIFICATION_MENTION = 'SettingsNotificationMention'; @@ -101,6 +102,8 @@ export default { SETTINGS_DISPLAY, SETTINGS_DISPLAY_CLOCK, SETTINGS_DISPLAY_THEME, + SETTINGS_DISPLAY_TIMEZONE, + SETTINGS_DISPLAY_TIMEZONE_SELECT, SETTINGS_NOTIFICATION, SETTINGS_NOTIFICATION_AUTO_RESPONDER, SETTINGS_NOTIFICATION_MENTION, diff --git a/app/database/operator/server_data_operator/transformers/group.test.ts b/app/database/operator/server_data_operator/transformers/group.test.ts index 807954215..50fe504b6 100644 --- a/app/database/operator/server_data_operator/transformers/group.test.ts +++ b/app/database/operator/server_data_operator/transformers/group.test.ts @@ -1,9 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import { - transformGroupRecord, -} from '@database/operator/server_data_operator/transformers/group'; +import {transformGroupRecord} from '@database/operator/server_data_operator/transformers/group'; import {createTestConnection} from '@database/operator/utils/create_test_connection'; import {OperationType} from '@typings/database/enums'; diff --git a/app/queries/servers/post.ts b/app/queries/servers/post.ts index a2c205412..176cb999e 100644 --- a/app/queries/servers/post.ts +++ b/app/queries/servers/post.ts @@ -7,10 +7,10 @@ import {switchMap} from 'rxjs/operators'; import {Preferences} from '@constants'; import {MM_TABLES} from '@constants/database'; -import PostModel from '@typings/database/models/servers/post'; import {queryPreferencesByCategoryAndName} from './preference'; +import type PostModel from '@typings/database/models/servers/post'; import type PostInChannelModel from '@typings/database/models/servers/posts_in_channel'; import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread'; diff --git a/app/queries/servers/preference.ts b/app/queries/servers/preference.ts index 733113479..5d58ea3e1 100644 --- a/app/queries/servers/preference.ts +++ b/app/queries/servers/preference.ts @@ -6,11 +6,11 @@ import {Database, Model, Q} from '@nozbe/watermelondb'; import {Preferences} from '@constants'; import {MM_TABLES} from '@constants/database'; import {getPreferenceValue} from '@helpers/api/preference'; -import {ServerDatabase} from '@typings/database/database'; import {getCurrentTeamId} from './system'; import type ServerDataOperator from '@database/operator/server_data_operator'; +import type {ServerDatabase} from '@typings/database/database'; import type PreferenceModel from '@typings/database/models/servers/preference'; const {SERVER: {PREFERENCE}} = MM_TABLES; diff --git a/app/screens/edit_profile/index.ts b/app/screens/edit_profile/index.ts index 271595a12..f16f08a79 100644 --- a/app/screens/edit_profile/index.ts +++ b/app/screens/edit_profile/index.ts @@ -8,10 +8,11 @@ import {switchMap} from 'rxjs/operators'; import {observeConfig} from '@queries/servers/system'; import {observeCurrentUser} from '@queries/servers/user'; -import {WithDatabaseArgs} from '@typings/database/database'; import EditProfile from './edit_profile'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const config = observeConfig(database); diff --git a/app/screens/home/channel_list/categories_list/categories/body/index.ts b/app/screens/home/channel_list/categories_list/categories/body/index.ts index 12463b1ac..c178e6b70 100644 --- a/app/screens/home/channel_list/categories_list/categories/body/index.ts +++ b/app/screens/home/channel_list/categories_list/categories/body/index.ts @@ -14,11 +14,11 @@ import {observeChannelsByCategoryChannelSortOrder, observeChannelsByLastPostAtIn import {observeNotifyPropsByChannels, queryChannelsByNames} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {observeCurrentChannelId, observeCurrentUserId, observeLastUnreadChannelId} from '@queries/servers/system'; -import {WithDatabaseArgs} from '@typings/database/database'; import {getDirectChannelName} from '@utils/channel'; import CategoryBody from './category_body'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type CategoryModel from '@typings/database/models/servers/category'; import type ChannelModel from '@typings/database/models/servers/channel'; import type PreferenceModel from '@typings/database/models/servers/preference'; diff --git a/app/screens/in_app_notification/icon.tsx b/app/screens/in_app_notification/icon.tsx index ceac5a03e..f20893dd2 100644 --- a/app/screens/in_app_notification/icon.tsx +++ b/app/screens/in_app_notification/icon.tsx @@ -12,9 +12,9 @@ import CompassIcon from '@components/compass_icon'; import NetworkManager from '@managers/network_manager'; import {observeConfig} from '@queries/servers/system'; import {observeUser} from '@queries/servers/user'; -import {WithDatabaseArgs} from '@typings/database/database'; import type {Client} from '@client/rest'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type UserModel from '@typings/database/models/servers/user'; interface NotificationIconProps { diff --git a/app/screens/index.tsx b/app/screens/index.tsx index 7456c9fcc..f31bd722a 100644 --- a/app/screens/index.tsx +++ b/app/screens/index.tsx @@ -160,6 +160,12 @@ Navigation.setLazyComponentRegistrator((screenName) => { case Screens.SETTINGS_DISPLAY_THEME: screen = withServerDatabase(require('@screens/settings/display_theme').default); break; + case Screens.SETTINGS_DISPLAY_TIMEZONE: + screen = withServerDatabase(require('@screens/settings/display_timezone').default); + break; + case Screens.SETTINGS_DISPLAY_TIMEZONE_SELECT: + screen = withServerDatabase(require('@screens/settings/display_timezone_select').default); + break; case Screens.SETTINGS_NOTIFICATION: screen = withServerDatabase(require('@screens/settings/notifications').default); break; diff --git a/app/screens/permalink/index.ts b/app/screens/permalink/index.ts index f12601914..ad8e9f7bf 100644 --- a/app/screens/permalink/index.ts +++ b/app/screens/permalink/index.ts @@ -10,11 +10,12 @@ import {observePost} from '@queries/servers/post'; import {observeCurrentTeamId, observeCurrentUserId} from '@queries/servers/system'; import {queryMyTeamsByIds, queryTeamByName} from '@queries/servers/team'; import {observeIsCRTEnabled} from '@queries/servers/thread'; -import {WithDatabaseArgs} from '@typings/database/database'; import PostModel from '@typings/database/models/servers/post'; import Permalink from './permalink'; +import type {WithDatabaseArgs} from '@typings/database/database'; + type OwnProps = { postId: PostModel['id']; teamName?: string; diff --git a/app/screens/reactions/reactors_list/reactor/index.ts b/app/screens/reactions/reactors_list/reactor/index.ts index b036baa1d..ee37dac20 100644 --- a/app/screens/reactions/reactors_list/reactor/index.ts +++ b/app/screens/reactions/reactors_list/reactor/index.ts @@ -8,10 +8,10 @@ import {switchMap} from 'rxjs/operators'; import {observePost} from '@queries/servers/post'; import {observeUser} from '@queries/servers/user'; -import {WithDatabaseArgs} from '@typings/database/database'; import Reactor from './reactor'; +import type {WithDatabaseArgs} from '@typings/database/database'; import type ReactionModel from '@typings/database/models/servers/reaction'; const enhance = withObservables(['reaction'], ({database, reaction}: {reaction: ReactionModel} & WithDatabaseArgs) => ({ diff --git a/app/screens/settings/display/display.tsx b/app/screens/settings/display/display.tsx index b2efa1d92..8d53fe596 100644 --- a/app/screens/settings/display/display.tsx +++ b/app/screens/settings/display/display.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {useIntl} from 'react-intl'; -import {Alert, Platform, ScrollView, View} from 'react-native'; +import {Platform, ScrollView, View} from 'react-native'; import {SafeAreaView} from 'react-native-safe-area-context'; import {Screens} from '@constants'; @@ -44,11 +44,6 @@ const Display = ({isTimezoneEnabled, isThemeSwitchingEnabled}: DisplayProps) => const theme = useTheme(); const styles = getStyleSheet(theme); const intl = useIntl(); - const onPressHandler = () => { - return Alert.alert( - 'The functionality you are trying to use has not yet been implemented.', - ); - }; const goToThemeSettings = preventDoubleTap(() => { const screen = Screens.SETTINGS_DISPLAY_THEME; @@ -63,6 +58,13 @@ const Display = ({isTimezoneEnabled, isThemeSwitchingEnabled}: DisplayProps) => goToScreen(screen, title); }); + const goToTimezoneSettings = preventDoubleTap(() => { + const screen = Screens.SETTINGS_DISPLAY_TIMEZONE; + const title = intl.formatMessage({id: 'display_settings.timezone', defaultMessage: 'Timezone'}); + + goToScreen(screen, title); + }); + return ( {isTimezoneEnabled && ( )} diff --git a/app/screens/settings/display/index.tsx b/app/screens/settings/display/index.tsx index 867a54c12..aab0bd82d 100644 --- a/app/screens/settings/display/index.tsx +++ b/app/screens/settings/display/index.tsx @@ -7,10 +7,11 @@ import {combineLatest, of as of$} from 'rxjs'; import {switchMap} from 'rxjs/operators'; import {observeAllowedThemesKeys, observeConfigBooleanValue} from '@queries/servers/system'; -import {WithDatabaseArgs} from '@typings/database/database'; import DisplaySettings from './display'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const isTimezoneEnabled = observeConfigBooleanValue(database, 'ExperimentalTimezone'); diff --git a/app/screens/settings/display_clock/display_clock.tsx b/app/screens/settings/display_clock/display_clock.tsx index 65dfd8bf7..19b1d00a6 100644 --- a/app/screens/settings/display_clock/display_clock.tsx +++ b/app/screens/settings/display_clock/display_clock.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {useIntl} from 'react-intl'; -import {StatusBar, View} from 'react-native'; +import {View} from 'react-native'; import {Edge, SafeAreaView} from 'react-native-safe-area-context'; import {savePreference} from '@actions/remote/preference'; @@ -119,7 +119,6 @@ const DisplayClock = ({componentId, currentUserId, hasMilitaryTimeFormat}: Displ style={styles.container} testID='settings_display.screen' > - { return { currentUserId: observeCurrentUserId(database), diff --git a/app/screens/settings/display_theme/index.ts b/app/screens/settings/display_theme/index.ts index 374998d86..6774c6dd3 100644 --- a/app/screens/settings/display_theme/index.ts +++ b/app/screens/settings/display_theme/index.ts @@ -9,10 +9,11 @@ import { observeCurrentTeamId, observeCurrentUserId, } from '@queries/servers/system'; -import {WithDatabaseArgs} from '@typings/database/database'; import DisplayTheme from './display_theme'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { const currentTeamId = observeCurrentTeamId(database); const currentUserId = observeCurrentUserId(database); diff --git a/app/screens/settings/display_timezone/display_timezone.tsx b/app/screens/settings/display_timezone/display_timezone.tsx new file mode 100644 index 000000000..318ce8a88 --- /dev/null +++ b/app/screens/settings/display_timezone/display_timezone.tsx @@ -0,0 +1,163 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import {useIntl} from 'react-intl'; +import {View} from 'react-native'; +import {Edge, SafeAreaView} from 'react-native-safe-area-context'; + +import {updateMe} from '@actions/remote/user'; +import OptionItem from '@components/option_item'; +import {Screens} from '@constants'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; +import useNavButtonPressed from '@hooks/navigation_button_pressed'; +import {goToScreen, popTopScreen, setButtons} from '@screens/navigation'; +import {preventDoubleTap} from '@utils/tap'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; +import {getDeviceTimezone} from '@utils/timezone'; +import {getTimezoneRegion, getUserTimezoneProps} from '@utils/user'; + +import type UserModel from '@typings/database/models/servers/user'; + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flex: 1, + }, + wrapper: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.06), + flex: 1, + paddingTop: 35, + }, + divider: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), + height: 1, + }, + separator: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), + height: 1, + marginLeft: 15, + }, + content: { + paddingHorizontal: 8, + }, + }; +}); + +const edges: Edge[] = ['left', 'right']; +const SAVE_TIMEZONE_BUTTON_ID = 'save_timezone'; + +type DisplayTimezoneProps = { + currentUser: UserModel; + componentId: string; +} +const DisplayTimezone = ({currentUser, componentId}: DisplayTimezoneProps) => { + const intl = useIntl(); + const serverUrl = useServerUrl(); + const timezone = useMemo(() => getUserTimezoneProps(currentUser), [currentUser.timezone]); + const [userTimezone, setUserTimezone] = useState(timezone); + const theme = useTheme(); + const styles = getStyleSheet(theme); + + const updateAutomaticTimezone = (useAutomaticTimezone: boolean) => { + const automaticTimezone = getDeviceTimezone(); + setUserTimezone((prev) => ({ + ...prev, + useAutomaticTimezone, + automaticTimezone, + })); + }; + + const updateManualTimezone = (mtz: string) => { + setUserTimezone({ + useAutomaticTimezone: false, + automaticTimezone: '', + manualTimezone: mtz, + }); + }; + + const goToSelectTimezone = preventDoubleTap(() => { + const screen = Screens.SETTINGS_DISPLAY_TIMEZONE_SELECT; + const title = intl.formatMessage({id: 'settings_display.timezone.select', defaultMessage: 'Select Timezone'}); + const passProps = { + selectedTimezone: timezone.manualTimezone, + onBack: updateManualTimezone, + }; + + goToScreen(screen, title, passProps); + }); + + const close = () => popTopScreen(componentId); + + const saveTimezone = useCallback(() => { + const timeZone = { + useAutomaticTimezone: userTimezone.useAutomaticTimezone.toString(), + automaticTimezone: userTimezone.automaticTimezone, + manualTimezone: userTimezone.manualTimezone, + }; + + updateMe(serverUrl, {timezone: timeZone}); + close(); + }, [userTimezone, currentUser.timezone, serverUrl]); + + const saveButton = useMemo(() => { + return { + id: SAVE_TIMEZONE_BUTTON_ID, + enabled: false, + showAsAction: 'always' as const, + testID: 'notification_settings.auto_res.save.button', + color: theme.sidebarHeaderTextColor, + text: intl.formatMessage({id: 'settings.save', defaultMessage: 'Save'}), + }; + }, [theme.sidebarHeaderTextColor]); + + useEffect(() => { + const buttons = { + rightButtons: [{ + ...saveButton, + enabled: timezone.useAutomaticTimezone !== userTimezone.useAutomaticTimezone, + }], + }; + setButtons(componentId, buttons); + }, [componentId, currentUser.timezone]); + + useNavButtonPressed(SAVE_TIMEZONE_BUTTON_ID, componentId, saveTimezone, [saveTimezone]); + + useAndroidHardwareBackHandler(componentId, close); + + return ( + + + + + {!userTimezone.useAutomaticTimezone && ( + + + + + )} + + + + ); +}; + +export default DisplayTimezone; diff --git a/app/screens/settings/display_timezone/index.tsx b/app/screens/settings/display_timezone/index.tsx new file mode 100644 index 000000000..4289737ee --- /dev/null +++ b/app/screens/settings/display_timezone/index.tsx @@ -0,0 +1,20 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import withObservables from '@nozbe/with-observables'; + +import {observeCurrentUser} from '@queries/servers/user'; + +import DisplayTimezone from './display_timezone'; + +import type {WithDatabaseArgs} from '@typings/database/database'; + +const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { + return { + currentUser: observeCurrentUser(database), + }; +}); + +export default withDatabase(enhanced(DisplayTimezone)); + diff --git a/app/screens/settings/display_timezone_select/index.tsx b/app/screens/settings/display_timezone_select/index.tsx new file mode 100644 index 000000000..d1e7c560c --- /dev/null +++ b/app/screens/settings/display_timezone_select/index.tsx @@ -0,0 +1,143 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useCallback, useEffect, useState} from 'react'; +import {useIntl} from 'react-intl'; +import {FlatList, View} from 'react-native'; +import {Edge, SafeAreaView} from 'react-native-safe-area-context'; + +import {getAllSupportedTimezones} from '@actions/remote/user'; +import Search from '@components/search'; +import {List} from '@constants'; +import {useServerUrl} from '@context/server'; +import {useTheme} from '@context/theme'; +import {popTopScreen} from '@screens/navigation'; +import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; +import {getTimezoneRegion} from '@utils/user'; + +import TimezoneRow from './timezone_row'; + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flex: 1, + backgroundColor: theme.centerChannelBg, + }, + searchBarInput: { + color: theme.centerChannelColor, + ...typography('Body', 100, 'Regular'), + }, + searchBar: { + height: 38, + marginVertical: 5, + }, + }; +}); + +const EDGES: Edge[] = ['left', 'right']; +const EMPTY_TIMEZONES: string[] = []; +const ITEM_HEIGHT = 45; +const VIEWABILITY_CONFIG = List.VISIBILITY_CONFIG_DEFAULTS; + +const keyExtractor = (item: string) => item; +const getItemLayout = (_data: string[], index: number) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, +}); + +type SelectTimezonesProps = { + selectedTimezone: string; + onBack: (tz: string) => void; +} +const SelectTimezones = ({selectedTimezone, onBack}: SelectTimezonesProps) => { + const intl = useIntl(); + const serverUrl = useServerUrl(); + const theme = useTheme(); + const styles = getStyleSheet(theme); + + const [timezones, setTimezones] = useState(EMPTY_TIMEZONES); + const [initialScrollIndex, setInitialScrollIndex] = useState(0); + const [value, setValue] = useState(''); + + const filteredTimezones = (timezonePrefix: string) => { + if (timezonePrefix.length === 0) { + return timezones; + } + + const lowerCasePrefix = timezonePrefix.toLowerCase(); + + return timezones.filter((t) => ( + getTimezoneRegion(t).toLowerCase().indexOf(lowerCasePrefix) >= 0 || + t.toLowerCase().indexOf(lowerCasePrefix) >= 0 + )); + }; + + const onPressTimezone = useCallback((tzne: string) => { + onBack(tzne); + popTopScreen(); + }, [onBack]); + + const renderItem = ({item: timezone}: {item: string}) => { + return ( + + ); + }; + + useEffect(() => { + // let's get all supported timezones + const getSupportedTimezones = async () => { + const allTzs = await getAllSupportedTimezones(serverUrl); + if (allTzs.length > 0) { + setTimezones(allTzs); + const timezoneIndex = allTzs.findIndex((timezone) => timezone === selectedTimezone); + if (timezoneIndex > 0) { + setInitialScrollIndex(timezoneIndex); + } + } + }; + getSupportedTimezones(); + }, []); + + return ( + + + + + + + ); +}; + +export default SelectTimezones; diff --git a/app/screens/settings/display_timezone_select/timezone_row.tsx b/app/screens/settings/display_timezone_select/timezone_row.tsx new file mode 100644 index 000000000..bee0b6408 --- /dev/null +++ b/app/screens/settings/display_timezone_select/timezone_row.tsx @@ -0,0 +1,69 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {TouchableOpacity, View, Text} from 'react-native'; + +import CompassIcon from '@components/compass_icon'; +import {useTheme} from '@context/theme'; +import {makeStyleSheetFromTheme} from '@utils/theme'; +import {typography} from '@utils/typography'; + +const ITEM_HEIGHT = 45; + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + itemContainer: { + flexDirection: 'row', + alignItems: 'center', + width: '100%', + paddingHorizontal: 15, + height: ITEM_HEIGHT, + }, + item: { + alignItems: 'center', + flex: 1, + flexDirection: 'row', + }, + itemText: { + color: theme.centerChannelColor, + ...typography('Body', 100, 'Regular'), + }, + }; +}); +type TimezoneRowProps = { + onPressTimezone: (timezone: string) => void; + selectedTimezone: string; + timezone: string; +} +const TimezoneRow = ({onPressTimezone, selectedTimezone, timezone}: TimezoneRowProps) => { + const theme = useTheme(); + const styles = getStyleSheet(theme); + + const onTimezoneSelect = () => { + onPressTimezone(timezone); + }; + + return ( + + + + {timezone} + + + {timezone === selectedTimezone && ( + + )} + + ); +}; + +export default TimezoneRow; diff --git a/app/screens/settings/notification_auto_responder/index.ts b/app/screens/settings/notification_auto_responder/index.ts index a1d967146..945764c9e 100644 --- a/app/screens/settings/notification_auto_responder/index.ts +++ b/app/screens/settings/notification_auto_responder/index.ts @@ -5,10 +5,11 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; import {observeCurrentUser} from '@queries/servers/user'; -import {WithDatabaseArgs} from '@typings/database/database'; import NotificationAutoResponder from './notification_auto_responder'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { return { currentUser: observeCurrentUser(database), diff --git a/app/screens/settings/notification_mention/index.tsx b/app/screens/settings/notification_mention/index.tsx index 07e15a990..53299cd66 100644 --- a/app/screens/settings/notification_mention/index.tsx +++ b/app/screens/settings/notification_mention/index.tsx @@ -6,10 +6,11 @@ import withObservables from '@nozbe/with-observables'; import {observeIsCRTEnabled} from '@queries/servers/thread'; import {observeCurrentUser} from '@queries/servers/user'; -import {WithDatabaseArgs} from '@typings/database/database'; import Mentions from './notification_mention'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { return { isCRTEnabled: observeIsCRTEnabled(database), diff --git a/app/screens/settings/notification_mention/mention_settings.tsx b/app/screens/settings/notification_mention/mention_settings.tsx index d2dfa8f3b..a822a9daf 100644 --- a/app/screens/settings/notification_mention/mention_settings.tsx +++ b/app/screens/settings/notification_mention/mention_settings.tsx @@ -15,11 +15,12 @@ import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import useNavButtonPressed from '@hooks/navigation_button_pressed'; import {t} from '@i18n'; import {popTopScreen, setButtons} from '@screens/navigation'; -import UserModel from '@typings/database/models/servers/user'; import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; import {getNotificationProps} from '@utils/user'; +import type UserModel from '@typings/database/models/servers/user'; + const mentionHeaderText = { id: t('notification_settings.mentions.wordsTrigger'), defaultMessage: 'Words that trigger mentions', diff --git a/app/screens/settings/notification_push/index.tsx b/app/screens/settings/notification_push/index.tsx index 1d2d4fbda..0db827959 100644 --- a/app/screens/settings/notification_push/index.tsx +++ b/app/screens/settings/notification_push/index.tsx @@ -7,10 +7,11 @@ import withObservables from '@nozbe/with-observables'; import {observeConfigBooleanValue} from '@queries/servers/system'; import {observeIsCRTEnabled} from '@queries/servers/thread'; import {observeCurrentUser} from '@queries/servers/user'; -import {WithDatabaseArgs} from '@typings/database/database'; import NotificationPush from './notification_push'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { return { currentUser: observeCurrentUser(database), diff --git a/app/screens/settings/notifications/index.tsx b/app/screens/settings/notifications/index.tsx index e2bd616f5..23f108992 100644 --- a/app/screens/settings/notifications/index.tsx +++ b/app/screens/settings/notifications/index.tsx @@ -6,10 +6,11 @@ import withObservables from '@nozbe/with-observables'; import {observeConfigBooleanValue} from '@queries/servers/system'; import {observeIsCRTEnabled} from '@queries/servers/thread'; -import {WithDatabaseArgs} from '@typings/database/database'; import NotificationSettings from './notifications'; +import type {WithDatabaseArgs} from '@typings/database/database'; + const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { return { isCRTEnabled: observeIsCRTEnabled(database), diff --git a/app/utils/user/index.ts b/app/utils/user/index.ts index 44194b545..9b2ca6fc2 100644 --- a/app/utils/user/index.ts +++ b/app/utils/user/index.ts @@ -138,6 +138,17 @@ export const getTimezone = (timezone: UserTimezone | null) => { return timezone.manualTimezone; }; +export const getTimezoneRegion = (timezone: string): string => { + if (timezone) { + const split = timezone.split('/'); + if (split.length > 1) { + return split.pop()!.replace(/_/g, ' '); + } + } + + return timezone; +}; + export const getUserCustomStatus = (user?: UserModel | UserProfile): UserCustomStatus | undefined => { try { if (typeof user?.props?.customStatus === 'string') { diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index b74e49830..8859e3632 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -229,6 +229,7 @@ "date_separator.yesterday": "Yesterday", "display_settings.clockDisplay": "Clock Display", "display_settings.theme": "Theme", + "display_settings.timezone": "Timezone", "download.error": "Unable to download the file. Try again later", "edit_post.editPost": "Edit the post...", "edit_post.save": "Save", @@ -691,6 +692,9 @@ "settings_display.clock.normal": "12-hour clock (example: 4:00 PM)", "settings_display.clock.preferTime": "Select how you prefer time displayed.", "settings_display.custom_theme": "Custom Theme", + "settings_display.timezone.automatically": "Set automatically", + "settings_display.timezone.manual": "Change timezone", + "settings_display.timezone.select": "Select Timezone", "settings.about": "About {appTitle}", "settings.display": "Display", "settings.display.militaryClock.save": "Save",