From c365ecea546f9e28a715491348aaad8d98b81065 Mon Sep 17 00:00:00 2001 From: Avinash Lingaloo Date: Mon, 13 Jun 2022 16:07:03 +0400 Subject: [PATCH] MM-39711 - Gekidou - Clock functionality [1] (#6362) --- app/constants/screens.ts | 2 + app/hooks/android_back_handler.tsx | 26 +++ app/hooks/navigation_button_pressed.tsx | 24 +++ app/screens/index.tsx | 9 +- app/screens/settings/display/display.tsx | 8 +- .../settings/display_clock/display_clock.tsx | 153 ++++++++++++++++++ app/screens/settings/display_clock/index.tsx | 29 ++++ .../settings/display_theme/custom_theme.tsx | 2 +- assets/base/i18n/en.json | 7 +- 9 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 app/hooks/android_back_handler.tsx create mode 100644 app/hooks/navigation_button_pressed.tsx create mode 100644 app/screens/settings/display_clock/display_clock.tsx create mode 100644 app/screens/settings/display_clock/index.tsx diff --git a/app/constants/screens.ts b/app/constants/screens.ts index 46c6d4188..7b3c963e0 100644 --- a/app/constants/screens.ts +++ b/app/constants/screens.ts @@ -43,6 +43,7 @@ export const SELECT_TEAM = 'SelectTeam'; export const SERVER = 'Server'; export const SETTINGS = 'Settings'; export const SETTINGS_DISPLAY = 'SettingsDisplay'; +export const SETTINGS_DISPLAY_CLOCK = 'SettingsDisplayClock'; export const SETTINGS_DISPLAY_THEME = 'SettingsDisplayTheme'; export const SETTINGS_NOTIFICATION = 'SettingsNotification'; export const SETTINGS_NOTIFICATION_AUTO_RESPONDER = 'SettingsNotificationAutoResponder'; @@ -98,6 +99,7 @@ export default { SERVER, SETTINGS, SETTINGS_DISPLAY, + SETTINGS_DISPLAY_CLOCK, SETTINGS_DISPLAY_THEME, SETTINGS_NOTIFICATION, SETTINGS_NOTIFICATION_AUTO_RESPONDER, diff --git a/app/hooks/android_back_handler.tsx b/app/hooks/android_back_handler.tsx new file mode 100644 index 000000000..47d7548e5 --- /dev/null +++ b/app/hooks/android_back_handler.tsx @@ -0,0 +1,26 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {EffectCallback, useEffect} from 'react'; +import {BackHandler} from 'react-native'; + +import EphemeralStore from '@store/ephemeral_store'; + +const useAndroidHardwareBackHandler = (componentId: string, callback: EffectCallback) => { + useEffect(() => { + const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { + if (EphemeralStore.getNavigationTopComponentId() === componentId) { + callback(); + return true; + } + + return false; + }); + + return () => { + backHandler.remove(); + }; + }, [componentId]); +}; + +export default useAndroidHardwareBackHandler; diff --git a/app/hooks/navigation_button_pressed.tsx b/app/hooks/navigation_button_pressed.tsx new file mode 100644 index 000000000..d586cdf85 --- /dev/null +++ b/app/hooks/navigation_button_pressed.tsx @@ -0,0 +1,24 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {DependencyList, EffectCallback, useEffect} from 'react'; +import {Navigation} from 'react-native-navigation'; + +const useNavButtonPressed = (navButtonId: string, componentId: string, callback: EffectCallback, deps?: DependencyList) => { + useEffect(() => { + const unsubscribe = Navigation.events().registerComponentListener({ + navigationButtonPressed: ({buttonId}: { buttonId: string }) => { + if (buttonId === navButtonId) { + callback(); + } + }, + }, componentId); + + return () => { + unsubscribe.remove(); + }; + }, deps); +}; + +export default useNavButtonPressed; + diff --git a/app/screens/index.tsx b/app/screens/index.tsx index cbb27f30d..7456c9fcc 100644 --- a/app/screens/index.tsx +++ b/app/screens/index.tsx @@ -154,21 +154,24 @@ Navigation.setLazyComponentRegistrator((screenName) => { case Screens.SETTINGS_DISPLAY: screen = withServerDatabase(require('@screens/settings/display').default); break; + case Screens.SETTINGS_DISPLAY_CLOCK: + screen = withServerDatabase(require('@screens/settings/display_clock').default); + break; case Screens.SETTINGS_DISPLAY_THEME: screen = withServerDatabase(require('@screens/settings/display_theme').default); break; case Screens.SETTINGS_NOTIFICATION: screen = withServerDatabase(require('@screens/settings/notifications').default); break; + case Screens.SETTINGS_NOTIFICATION_AUTO_RESPONDER: + screen = withServerDatabase(require('@screens/settings/notification_auto_responder').default); + break; case Screens.SETTINGS_NOTIFICATION_MENTION: screen = withServerDatabase(require('@screens/settings/notification_mention').default); break; case Screens.SETTINGS_NOTIFICATION_PUSH: screen = withServerDatabase(require('@screens/settings/notification_push').default); break; - case Screens.SETTINGS_NOTIFICATION_AUTO_RESPONDER: - screen = withServerDatabase(require('@screens/settings/notification_auto_responder').default); - break; case Screens.SNACK_BAR: { const snackBarScreen = withServerDatabase(require('@screens/snack_bar').default); Navigation.registerComponent(Screens.SNACK_BAR, () => diff --git a/app/screens/settings/display/display.tsx b/app/screens/settings/display/display.tsx index 91976f132..b2efa1d92 100644 --- a/app/screens/settings/display/display.tsx +++ b/app/screens/settings/display/display.tsx @@ -57,6 +57,12 @@ const Display = ({isTimezoneEnabled, isThemeSwitchingEnabled}: DisplayProps) => goToScreen(screen, title); }); + const goToClockDisplaySettings = preventDoubleTap(() => { + const screen = Screens.SETTINGS_DISPLAY_CLOCK; + const title = intl.formatMessage({id: 'display_settings.clockDisplay', defaultMessage: 'Clock Display'}); + goToScreen(screen, title); + }); + return ( )} {isTimezoneEnabled && ( { + return { + container: { + flex: 1, + backgroundColor: theme.centerChannelBg, + }, + wrapper: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.06), + flex: 1, + paddingTop: 35, + }, + divider: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), + height: 1, + }, + containerStyle: { + paddingHorizontal: 8, + }, + }; +}); + +const SAVE_CLOCK_BUTTON_ID = 'settings_display.clock.save.button'; + +type DisplayClockProps = { + componentId: string; + currentUserId: string; + hasMilitaryTimeFormat: boolean; +} +const DisplayClock = ({componentId, currentUserId, hasMilitaryTimeFormat}: DisplayClockProps) => { + const theme = useTheme(); + const [isMilitaryTimeFormat, setIsMilitaryTimeFormat] = useState(hasMilitaryTimeFormat); + const serverUrl = useServerUrl(); + const intl = useIntl(); + + const styles = getStyleSheet(theme); + + const saveButton = useMemo(() => { + return { + id: SAVE_CLOCK_BUTTON_ID, + enabled: false, + showAsAction: 'always' as const, + testID: 'settings_display.save.button', + color: theme.sidebarHeaderTextColor, + text: intl.formatMessage({id: 'settings.display.militaryClock.save', defaultMessage: 'Save'}), + }; + }, [theme.sidebarHeaderTextColor]); + + const onSelectClockPreference = useCallback((clockType: keyof typeof CLOCK_TYPE) => { + setIsMilitaryTimeFormat(clockType === CLOCK_TYPE.MILITARY); + }, []); + + const close = () => popTopScreen(componentId); + + const saveClockDisplayPreference = () => { + const timePreference: PreferenceType = { + category: Preferences.CATEGORY_DISPLAY_SETTINGS, + name: 'use_military_time', + user_id: currentUserId, + value: `${isMilitaryTimeFormat}`, + }; + + savePreference(serverUrl, [timePreference]); + close(); + }; + + useEffect(() => { + const buttons = { + rightButtons: [{ + ...saveButton, + enabled: hasMilitaryTimeFormat !== isMilitaryTimeFormat, + }], + }; + setButtons(componentId, buttons); + }, [componentId, saveButton, isMilitaryTimeFormat]); + + useEffect(() => { + setButtons(componentId, { + rightButtons: [saveButton], + }); + }, []); + + useAndroidHardwareBackHandler(componentId, close); + useNavButtonPressed(SAVE_CLOCK_BUTTON_ID, componentId, saveClockDisplayPreference, [isMilitaryTimeFormat]); + + return ( + + + + + + + + + + + ); +}; + +export default DisplayClock; diff --git a/app/screens/settings/display_clock/index.tsx b/app/screens/settings/display_clock/index.tsx new file mode 100644 index 000000000..dd388f712 --- /dev/null +++ b/app/screens/settings/display_clock/index.tsx @@ -0,0 +1,29 @@ +// 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 {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {Preferences} from '@constants'; +import {getPreferenceAsBool} from '@helpers/api/preference'; +import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; +import {observeCurrentUserId} from '@queries/servers/system'; +import {WithDatabaseArgs} from '@typings/database/database'; + +import DisplayClock from './display_clock'; + +const enhanced = withObservables([], ({database}: WithDatabaseArgs) => { + return { + currentUserId: observeCurrentUserId(database), + hasMilitaryTimeFormat: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS). + observeWithColumns(['value']).pipe( + switchMap( + (preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)), + ), + ), + }; +}); + +export default withDatabase(enhanced(DisplayClock)); diff --git a/app/screens/settings/display_theme/custom_theme.tsx b/app/screens/settings/display_theme/custom_theme.tsx index 95c19b53b..b6d2f783d 100644 --- a/app/screens/settings/display_theme/custom_theme.tsx +++ b/app/screens/settings/display_theme/custom_theme.tsx @@ -41,7 +41,7 @@ const CustomTheme = ({customTheme, setTheme}: CustomThemeProps) => { action={setTheme} type='select' value={customTheme.type} - label={intl.formatMessage({id: 'user.settings.display.custom_theme', defaultMessage: 'Custom Theme'})} + label={intl.formatMessage({id: 'settings_display.custom_theme', defaultMessage: 'Custom Theme'})} selected={theme.type?.toLowerCase() === customTheme.type?.toLowerCase()} /> diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 0c8bfb526..c905af742 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -227,6 +227,7 @@ "custom_status.suggestions.working_from_home": "Working from home", "date_separator.today": "Today", "date_separator.yesterday": "Yesterday", + "display_settings.clockDisplay": "Clock Display", "display_settings.theme": "Theme", "download.error": "Unable to download the file. Try again later", "edit_post.editPost": "Edit the post...", @@ -660,8 +661,13 @@ "servers.login": "Log in", "servers.logout": "Log out", "servers.remove": "Remove", + "settings_display.clock.military": "24-hour clock (example: 16:00)", + "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.about": "About {appTitle}", "settings.display": "Display", + "settings.display.militaryClock.save": "Save", "settings.notifications": "Notifications", "snack.bar.favorited.channel": "This channel was favorited", "snack.bar.link.copied": "Link copied to clipboard", @@ -712,7 +718,6 @@ "unreads.empty.title": "No more unreads", "user.edit_profile.email.auth_service": "Login occurs through {service}. Email cannot be updated. Email address used for notifications is {email}.", "user.edit_profile.email.web_client": "Email must be updated using a web client or desktop application.", - "user.settings.display.custom_theme": "Custom Theme", "user.settings.general.email": "Email", "user.settings.general.field_handled_externally": "Some fields below are handled through your login provider. If you want to change them, you’ll need to do so through your login provider.", "user.settings.general.firstName": "First Name",