diff --git a/app/hooks/android_back_handler.tsx b/app/hooks/android_back_handler.ts
similarity index 100%
rename from app/hooks/android_back_handler.tsx
rename to app/hooks/android_back_handler.ts
diff --git a/app/hooks/navigate_back.ts b/app/hooks/navigate_back.ts
new file mode 100644
index 0000000000..8b595cfba7
--- /dev/null
+++ b/app/hooks/navigate_back.ts
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {EffectCallback, useEffect} from 'react';
+import {Navigation} from 'react-native-navigation';
+
+const BACK_BUTTON = 'RNN.back';
+
+const useBackNavigation = (callback: EffectCallback) => {
+ useEffect(() => {
+ const backListener = Navigation.events().registerNavigationButtonPressedListener(({buttonId}) => {
+ if (buttonId === BACK_BUTTON) {
+ callback();
+ }
+ });
+
+ return () => backListener.remove();
+ }, [callback]);
+};
+
+export default useBackNavigation;
diff --git a/app/hooks/navigation_button_pressed.tsx b/app/hooks/navigation_button_pressed.ts
similarity index 100%
rename from app/hooks/navigation_button_pressed.tsx
rename to app/hooks/navigation_button_pressed.ts
diff --git a/app/screens/settings/config.ts b/app/screens/settings/config.ts
index 6b39e0ac2e..4617dcebbf 100644
--- a/app/screens/settings/config.ts
+++ b/app/screens/settings/config.ts
@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import {t} from '@i18n';
+import {goToScreen} from '@screens/navigation';
import {typography} from '@utils/typography';
import type {IntlShape} from 'react-intl';
@@ -16,6 +17,18 @@ export const getSaveButton = (buttonId: string, intl: IntlShape, color: string)
...typography('Body', 100, 'SemiBold'),
});
+export const gotoSettingsScreen = (screen: string, title: string) => {
+ const passProps = {};
+ const options = {
+ topBar: {
+ backButton: {
+ popStackOnPress: false,
+ },
+ },
+ };
+ return goToScreen(screen, title, passProps, options);
+};
+
type SettingConfigDetails = {
defaultMessage?: string;
i18nId?: string;
diff --git a/app/screens/settings/display/display.tsx b/app/screens/settings/display/display.tsx
index 4dd0151cb0..b6b5d9a45a 100644
--- a/app/screens/settings/display/display.tsx
+++ b/app/screens/settings/display/display.tsx
@@ -8,6 +8,7 @@ import {Screens} from '@constants';
import {useTheme} from '@context/theme';
import {t} from '@i18n';
import {goToScreen} from '@screens/navigation';
+import {gotoSettingsScreen} from '@screens/settings/config';
import {preventDoubleTap} from '@utils/tap';
import {getUserTimezoneProps} from '@utils/user';
@@ -52,21 +53,19 @@ const Display = ({currentUser, hasMilitaryTimeFormat, isThemeSwitchingEnabled, i
const goToThemeSettings = preventDoubleTap(() => {
const screen = Screens.SETTINGS_DISPLAY_THEME;
const title = intl.formatMessage({id: 'display_settings.theme', defaultMessage: 'Theme'});
-
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);
+ gotoSettingsScreen(screen, title);
});
const goToTimezoneSettings = preventDoubleTap(() => {
const screen = Screens.SETTINGS_DISPLAY_TIMEZONE;
const title = intl.formatMessage({id: 'display_settings.timezone', defaultMessage: 'Timezone'});
-
- goToScreen(screen, title);
+ gotoSettingsScreen(screen, title);
});
return (
diff --git a/app/screens/settings/display_clock/display_clock.tsx b/app/screens/settings/display_clock/display_clock.tsx
index 11f8e0266c..f03bdf306e 100644
--- a/app/screens/settings/display_clock/display_clock.tsx
+++ b/app/screens/settings/display_clock/display_clock.tsx
@@ -1,18 +1,16 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useState} from 'react';
import {useIntl} from 'react-intl';
import {savePreference} from '@actions/remote/preference';
import {Preferences} 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 {popTopScreen, setButtons} from '@screens/navigation';
+import useBackNavigation from '@hooks/navigate_back';
+import {popTopScreen} from '@screens/navigation';
-import {getSaveButton} from '../config';
import SettingBlock from '../setting_block';
import SettingContainer from '../setting_container';
import SettingOption from '../setting_option';
@@ -23,51 +21,40 @@ const CLOCK_TYPE = {
MILITARY: 'MILITARY',
} as const;
-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 saveButton = useMemo(() => getSaveButton(SAVE_CLOCK_BUTTON_ID, intl, theme.sidebarHeaderTextColor), [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}`,
- };
+ const saveClockDisplayPreference = useCallback(() => {
+ if (hasMilitaryTimeFormat !== isMilitaryTimeFormat) {
+ const timePreference: PreferenceType = {
+ category: Preferences.CATEGORY_DISPLAY_SETTINGS,
+ name: 'use_military_time',
+ user_id: currentUserId,
+ value: `${isMilitaryTimeFormat}`,
+ };
+
+ savePreference(serverUrl, [timePreference]);
+ }
- savePreference(serverUrl, [timePreference]);
close();
- };
+ }, [hasMilitaryTimeFormat, isMilitaryTimeFormat, serverUrl]);
- useEffect(() => {
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled: hasMilitaryTimeFormat !== isMilitaryTimeFormat,
- }],
- };
- setButtons(componentId, buttons);
- }, [componentId, saveButton, isMilitaryTimeFormat]);
+ useBackNavigation(saveClockDisplayPreference);
- useAndroidHardwareBackHandler(componentId, close);
- useNavButtonPressed(SAVE_CLOCK_BUTTON_ID, componentId, saveClockDisplayPreference, [isMilitaryTimeFormat]);
+ useAndroidHardwareBackHandler(componentId, saveClockDisplayPreference);
return (
diff --git a/app/screens/settings/display_theme/display_theme.tsx b/app/screens/settings/display_theme/display_theme.tsx
index 906c533c0f..dda3867868 100644
--- a/app/screens/settings/display_theme/display_theme.tsx
+++ b/app/screens/settings/display_theme/display_theme.tsx
@@ -1,25 +1,20 @@
// 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 React, {useCallback, useMemo} from 'react';
import {savePreference} from '@actions/remote/preference';
import {Preferences} 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 {popTopScreen, setButtons} from '@screens/navigation';
-import {getSaveButton} from '@screens/settings/config';
+import {popTopScreen} from '@screens/navigation';
import SettingContainer from '../setting_container';
import CustomTheme from './custom_theme';
import {ThemeTiles} from './theme_tiles';
-const SAVE_DISPLAY_THEME_BTN_ID = 'SAVE_DISPLAY_THEME_BTN_ID';
-
type DisplayThemeProps = {
allowedThemeKeys: string[];
componentId: string;
@@ -29,55 +24,41 @@ type DisplayThemeProps = {
const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUserId}: DisplayThemeProps) => {
const serverUrl = useServerUrl();
const theme = useTheme();
- const intl = useIntl();
- const initialTheme = useMemo(() => theme.type, []); // dependency array should remain empty
-
- const [displayTheme, setDisplayTheme] = useState(initialTheme);
-
- const saveButton = useMemo(() => getSaveButton(SAVE_DISPLAY_THEME_BTN_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
+ const initialTheme = useMemo(() => theme.type, [/* dependency array should remain empty */]);
const close = () => popTopScreen(componentId);
- const updateTheme = useCallback(() => {
- const selectedTheme = allowedThemeKeys.find((tk) => tk === displayTheme);
- if (!selectedTheme) {
+ const setThemePreference = useCallback((newTheme?: string) => {
+ const allowedTheme = allowedThemeKeys.find((tk) => tk === newTheme);
+ const differentTheme = initialTheme?.toLowerCase() !== newTheme?.toLowerCase();
+
+ if (!allowedTheme || !differentTheme) {
+ close();
return;
}
+
const pref: PreferenceType = {
category: Preferences.CATEGORY_THEME,
name: currentTeamId,
user_id: currentUserId,
- value: JSON.stringify(Preferences.THEMES[selectedTheme]),
+ value: JSON.stringify(Preferences.THEMES[allowedTheme]),
};
savePreference(serverUrl, [pref]);
- close();
- }, [serverUrl, allowedThemeKeys, currentTeamId, displayTheme]);
+ }, [allowedThemeKeys, currentTeamId, initialTheme, serverUrl]);
- useEffect(() => {
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled: initialTheme?.toLowerCase() !== displayTheme?.toLowerCase(),
- }],
- };
- setButtons(componentId, buttons);
- }, [componentId, saveButton, displayTheme, initialTheme]);
-
- useNavButtonPressed(SAVE_DISPLAY_THEME_BTN_ID, componentId, updateTheme, [updateTheme]);
-
- useAndroidHardwareBackHandler(componentId, close);
+ useAndroidHardwareBackHandler(componentId, setThemePreference);
return (
{theme.type === 'custom' && (
)}
diff --git a/app/screens/settings/display_timezone/display_timezone.tsx b/app/screens/settings/display_timezone/display_timezone.tsx
index 220253c108..4cfb776d4a 100644
--- a/app/screens/settings/display_timezone/display_timezone.tsx
+++ b/app/screens/settings/display_timezone/display_timezone.tsx
@@ -1,29 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useMemo, useState} from 'react';
import {useIntl} from 'react-intl';
import {updateMe} from '@actions/remote/user';
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 useBackNavigation from '@hooks/navigate_back';
+import {goToScreen, popTopScreen} from '@screens/navigation';
import {preventDoubleTap} from '@utils/tap';
import {getDeviceTimezone} from '@utils/timezone';
import {getTimezoneRegion, getUserTimezoneProps} from '@utils/user';
-import {getSaveButton} from '../config';
import SettingContainer from '../setting_container';
import SettingOption from '../setting_option';
import SettingSeparator from '../settings_separator';
import type UserModel from '@typings/database/models/servers/user';
-const SAVE_TIMEZONE_BUTTON_ID = 'save_timezone';
-
type DisplayTimezoneProps = {
currentUser: UserModel;
componentId: string;
@@ -31,9 +27,8 @@ type DisplayTimezoneProps = {
const DisplayTimezone = ({currentUser, componentId}: DisplayTimezoneProps) => {
const intl = useIntl();
const serverUrl = useServerUrl();
- const initialTimezone = useMemo(() => getUserTimezoneProps(currentUser), []); // deps array should remain empty
+ const initialTimezone = useMemo(() => getUserTimezoneProps(currentUser), [/* dependency array should remain empty */]);
const [userTimezone, setUserTimezone] = useState(initialTimezone);
- const theme = useTheme();
const updateAutomaticTimezone = (useAutomaticTimezone: boolean) => {
const automaticTimezone = getDeviceTimezone();
@@ -67,36 +62,27 @@ const DisplayTimezone = ({currentUser, componentId}: DisplayTimezoneProps) => {
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(() => getSaveButton(SAVE_TIMEZONE_BUTTON_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
-
- useEffect(() => {
- const enabled =
+ const canSave =
initialTimezone.useAutomaticTimezone !== userTimezone.useAutomaticTimezone ||
initialTimezone.automaticTimezone !== userTimezone.automaticTimezone ||
initialTimezone.manualTimezone !== userTimezone.manualTimezone;
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled,
- }],
- };
- setButtons(componentId, buttons);
- }, [componentId, currentUser.timezone]);
+ if (canSave) {
+ const timeZone = {
+ useAutomaticTimezone: userTimezone.useAutomaticTimezone.toString(),
+ automaticTimezone: userTimezone.automaticTimezone,
+ manualTimezone: userTimezone.manualTimezone,
+ };
- useNavButtonPressed(SAVE_TIMEZONE_BUTTON_ID, componentId, saveTimezone, [saveTimezone]);
+ updateMe(serverUrl, {timezone: timeZone});
+ }
- useAndroidHardwareBackHandler(componentId, close);
+ close();
+ }, [userTimezone, currentUser.timezone, serverUrl]);
+
+ useBackNavigation(saveTimezone);
+
+ useAndroidHardwareBackHandler(componentId, saveTimezone);
const toggleDesc = useMemo(() => {
if (userTimezone.useAutomaticTimezone) {
@@ -116,17 +102,13 @@ const DisplayTimezone = ({currentUser, componentId}: DisplayTimezoneProps) => {
/>
{!userTimezone.useAutomaticTimezone && (
- <>
- {/* */}
-
- >
+
)}
- {/* */}
);
};
diff --git a/app/screens/settings/display_timezone_select/index.tsx b/app/screens/settings/display_timezone_select/index.tsx
index 993733b3f2..30ec34401e 100644
--- a/app/screens/settings/display_timezone_select/index.tsx
+++ b/app/screens/settings/display_timezone_select/index.tsx
@@ -11,9 +11,7 @@ import Search from '@components/search';
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 {popTopScreen, setButtons} from '@screens/navigation';
-import {getSaveButton} from '@screens/settings/config';
+import {popTopScreen} from '@screens/navigation';
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {getTimezoneRegion} from '@utils/user';
@@ -48,7 +46,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
const EDGES: Edge[] = ['left', 'right'];
const EMPTY_TIMEZONES: string[] = [];
const ITEM_HEIGHT = 48;
-const SAVE_DISPLAY_TZ_BTN_ID = 'SAVE_DISPLAY_TZ_BTN_ID';
const keyExtractor = (item: string) => item;
const getItemLayout = (_data: string[], index: number) => ({
length: ITEM_HEIGHT,
@@ -66,7 +63,7 @@ const SelectTimezones = ({componentId, onBack, currentTimezone}: SelectTimezones
const serverUrl = useServerUrl();
const theme = useTheme();
const styles = getStyleSheet(theme);
- const initialTimezones = useMemo(() => currentTimezone, []);
+
const cancelButtonProps = useMemo(() => ({
buttonTextStyle: {
color: changeOpacity(theme.centerChannelColor, 0.64),
@@ -80,7 +77,6 @@ const SelectTimezones = ({componentId, onBack, currentTimezone}: SelectTimezones
const [timezones, setTimezones] = useState(EMPTY_TIMEZONES);
const [initialScrollIndex, setInitialScrollIndex] = useState();
const [searchRegion, setSearchRegion] = useState(undefined);
- const [manualTimezone, setManualTimezone] = useState(currentTimezone);
const filteredTimezones = useCallback(() => {
if (!searchRegion) {
@@ -103,26 +99,24 @@ const SelectTimezones = ({componentId, onBack, currentTimezone}: SelectTimezones
));
}, [searchRegion, timezones, initialScrollIndex]);
- const onPressTimezone = useCallback((tz: string) => {
- setManualTimezone(tz);
+ const close = (newTimezone?: string) => {
+ onBack(newTimezone || currentTimezone);
+ popTopScreen(componentId);
+ };
+
+ const onPressTimezone = useCallback((selectedTimezone: string) => {
+ close(selectedTimezone);
}, []);
const renderItem = useCallback(({item: timezone}: {item: string}) => {
return (
);
- }, [manualTimezone, onPressTimezone]);
-
- const saveButton = useMemo(() => getSaveButton(SAVE_DISPLAY_TZ_BTN_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
-
- const close = () => {
- onBack(manualTimezone);
- popTopScreen(componentId);
- };
+ }, [currentTimezone, onPressTimezone]);
useEffect(() => {
// let's get all supported timezones
@@ -139,18 +133,6 @@ const SelectTimezones = ({componentId, onBack, currentTimezone}: SelectTimezones
getSupportedTimezones();
}, []);
- useEffect(() => {
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled: initialTimezones !== manualTimezone,
- }],
- };
- setButtons(componentId, buttons);
- }, [componentId, saveButton, initialTimezones, manualTimezone]);
-
- useNavButtonPressed(SAVE_DISPLAY_TZ_BTN_ID, componentId, close, [manualTimezone]);
-
useAndroidHardwareBackHandler(componentId, close);
return (
@@ -176,7 +158,6 @@ const SelectTimezones = ({componentId, onBack, currentTimezone}: SelectTimezones
{
return {
@@ -66,46 +64,37 @@ const NotificationAutoResponder = ({currentUser, componentId}: NotificationAutoR
const theme = useTheme();
const serverUrl = useServerUrl();
const intl = useIntl();
- const notifyProps = useMemo(() => getNotificationProps(currentUser), []); // dependency array should remain empty
+ const notifyProps = useMemo(() => getNotificationProps(currentUser), [/* dependency array should remain empty */]);
- const initialAutoResponderActive = useMemo(() => Boolean(currentUser.status === General.OUT_OF_OFFICE && notifyProps.auto_responder_active === 'true'), []); // dependency array should remain empty
+ const initialAutoResponderActive = useMemo(() => Boolean(currentUser.status === General.OUT_OF_OFFICE && notifyProps.auto_responder_active === 'true'), [/* dependency array should remain empty */]);
const [autoResponderActive, setAutoResponderActive] = useState(initialAutoResponderActive);
- const initialOOOMsg = useMemo(() => notifyProps.auto_responder_message || intl.formatMessage(OOO), []); // dependency array should remain empty
+ const initialOOOMsg = useMemo(() => notifyProps.auto_responder_message || intl.formatMessage(OOO), [/* dependency array should remain empty */]);
const [autoResponderMessage, setAutoResponderMessage] = useState(initialOOOMsg);
const styles = getStyleSheet(theme);
const close = () => popTopScreen(componentId);
- const saveButton = useMemo(() => getSaveButton(SAVE_OOO_BUTTON_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
-
const saveAutoResponder = useCallback(() => {
- updateMe(serverUrl, {
- notify_props: {
- ...notifyProps,
- auto_responder_active: `${autoResponderActive}`,
- auto_responder_message: autoResponderMessage,
- },
- });
- fetchStatusInBatch(serverUrl, currentUser.id);
+ const canSaveSetting = initialAutoResponderActive !== autoResponderActive || initialOOOMsg !== autoResponderMessage;
+
+ if (canSaveSetting) {
+ updateMe(serverUrl, {
+ notify_props: {
+ ...notifyProps,
+ auto_responder_active: `${autoResponderActive}`,
+ auto_responder_message: autoResponderMessage,
+ },
+ });
+ fetchStatusInBatch(serverUrl, currentUser.id);
+ }
close();
}, [serverUrl, autoResponderActive, autoResponderMessage, notifyProps, currentUser.id]);
- useEffect(() => {
- const enabled = initialAutoResponderActive !== autoResponderActive || initialOOOMsg !== autoResponderMessage;
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled,
- }],
- };
- setButtons(componentId, buttons);
- }, [autoResponderActive, autoResponderMessage, componentId, currentUser.status, notifyProps.auto_responder_message]);
+ useBackNavigation(saveAutoResponder);
- useNavButtonPressed(SAVE_OOO_BUTTON_ID, componentId, saveAutoResponder, [saveAutoResponder]);
-
- useAndroidHardwareBackHandler(componentId, close);
+ useAndroidHardwareBackHandler(componentId, saveAutoResponder);
return (
diff --git a/app/screens/settings/notification_email/notification_email.tsx b/app/screens/settings/notification_email/notification_email.tsx
index 8b9fd583ec..aec81b66d4 100644
--- a/app/screens/settings/notification_email/notification_email.tsx
+++ b/app/screens/settings/notification_email/notification_email.tsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {useCallback, useMemo, useState} from 'react';
import {useIntl} from 'react-intl';
import {Text} from 'react-native';
@@ -11,14 +11,13 @@ import {Preferences} 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 useBackNavigation from '@hooks/navigate_back';
import {t} from '@i18n';
-import {popTopScreen, setButtons} from '@screens/navigation';
+import {popTopScreen} from '@screens/navigation';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {getEmailInterval, getNotificationProps} from '@utils/user';
-import {getSaveButton} from '../config';
import SettingBlock from '../setting_block';
import SettingContainer from '../setting_container';
import SettingOption from '../setting_option';
@@ -31,6 +30,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
disabled: {
color: changeOpacity(theme.centerChannelColor, 0.64),
...typography('Body', 75, 'Regular'),
+ marginHorizontal: 20,
},
};
});
@@ -53,8 +53,6 @@ const emailFooterCRTText = {
defaultMessage: "When enabled, any reply to a thread you're following will send an email notification",
};
-const SAVE_EMAIL_BUTTON_ID = 'settings_notification.email.save.button';
-
type NotificationEmailProps = {
componentId: string;
currentUser: UserModel;
@@ -66,12 +64,11 @@ type NotificationEmailProps = {
const NotificationEmail = ({componentId, currentUser, emailInterval, enableEmailBatching, isCRTEnabled, sendEmailNotifications}: NotificationEmailProps) => {
const notifyProps = useMemo(() => getNotificationProps(currentUser), [currentUser.notifyProps]);
- const initialInterval = useMemo(() => getEmailInterval(
- sendEmailNotifications && notifyProps?.email === 'true',
- enableEmailBatching,
- parseInt(emailInterval, 10),
- ).toString(), []); // dependency array should remain empty
- const initialEmailThreads = useMemo(() => Boolean(notifyProps?.email_threads === 'all'), []); // dependency array should remain empty
+ const initialInterval = useMemo(
+ () => getEmailInterval(sendEmailNotifications && notifyProps?.email === 'true', enableEmailBatching, parseInt(emailInterval, 10)).toString(),
+ [/* dependency array should remain empty */],
+ );
+ const initialEmailThreads = useMemo(() => Boolean(notifyProps?.email_threads === 'all'), [/* dependency array should remain empty */]);
const [notifyInterval, setNotifyInterval] = useState(initialInterval);
const [emailThreads, setEmailThreads] = useState(initialEmailThreads);
@@ -81,47 +78,39 @@ const NotificationEmail = ({componentId, currentUser, emailInterval, enableEmail
const theme = useTheme();
const styles = getStyleSheet(theme);
- const saveButton = useMemo(() => getSaveButton(SAVE_EMAIL_BUTTON_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
-
const close = () => popTopScreen(componentId);
- const saveEmail = useCallback(async () => {
- const promises = [];
- const updatePromise = updateMe(serverUrl, {
- notify_props: {
- ...notifyProps,
- email: `${sendEmailNotifications && notifyInterval !== Preferences.INTERVAL_NEVER.toString()}`,
- email_threads: emailThreads ? 'all' : 'mention',
- },
- });
- promises.push(updatePromise);
+ const saveEmail = useCallback(() => {
+ const canSaveSetting = notifyInterval !== initialInterval || emailThreads !== initialEmailThreads;
+ if (canSaveSetting) {
+ const promises = [];
+ const updatePromise = updateMe(serverUrl, {
+ notify_props: {
+ ...notifyProps,
+ email: `${sendEmailNotifications && notifyInterval !== Preferences.INTERVAL_NEVER.toString()}`,
+ ...(isCRTEnabled && {email_threads: emailThreads ? 'all' : 'mention'}),
+ },
+ });
+ promises.push(updatePromise);
- if (notifyInterval !== initialInterval) {
- const emailIntervalPreference = {
- category: Preferences.CATEGORY_NOTIFICATIONS,
- name: Preferences.EMAIL_INTERVAL,
- user_id: currentUser.id,
- value: notifyInterval,
- };
- const savePrefPromise = savePreference(serverUrl, [emailIntervalPreference]);
- promises.push(savePrefPromise);
+ if (notifyInterval !== initialInterval) {
+ const emailIntervalPreference = {
+ category: Preferences.CATEGORY_NOTIFICATIONS,
+ name: Preferences.EMAIL_INTERVAL,
+ user_id: currentUser.id,
+ value: notifyInterval,
+ };
+ const savePrefPromise = savePreference(serverUrl, [emailIntervalPreference]);
+ promises.push(savePrefPromise);
+ }
+ Promise.all(promises);
}
- await Promise.all(promises);
close();
}, [notifyProps, notifyInterval, emailThreads, serverUrl, currentUser.id, sendEmailNotifications]);
- useEffect(() => {
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled: notifyInterval !== initialInterval || emailThreads !== initialEmailThreads,
- }],
- };
- setButtons(componentId, buttons);
- }, [componentId, saveButton, notifyInterval, emailThreads]);
+ useAndroidHardwareBackHandler(componentId, saveEmail);
- useAndroidHardwareBackHandler(componentId, close);
- useNavButtonPressed(SAVE_EMAIL_BUTTON_ID, componentId, saveEmail, [saveEmail]);
+ useBackNavigation(saveEmail);
return (
@@ -182,7 +171,7 @@ const NotificationEmail = ({componentId, currentUser, emailInterval, enableEmail
}
- {isCRTEnabled && notifyProps.email === 'true' && (
+ {isCRTEnabled && notifyInterval !== Preferences.INTERVAL_NEVER.toString() && (
{
return {
input: {
@@ -63,8 +61,11 @@ const getMentionProps = (currentUser: UserModel) => {
}
return {
- mentionKeys: mKeys.join(','),
+ mentionKeywords: mKeys.join(','),
usernameMention: usernameMentionIndex > -1,
+ channel: notifyProps.channel === 'true',
+ first_name: notifyProps.first_name === 'true',
+ comments: notifyProps.comments,
notifyProps,
};
};
@@ -72,134 +73,156 @@ const getMentionProps = (currentUser: UserModel) => {
type MentionSectionProps = {
componentId: string;
currentUser: UserModel;
+ isCRTEnabled: boolean;
}
-const MentionSettings = ({componentId, currentUser}: MentionSectionProps) => {
+const MentionSettings = ({componentId, currentUser, isCRTEnabled}: MentionSectionProps) => {
const serverUrl = useServerUrl();
- const mentionProps = useMemo(() => getMentionProps(currentUser), [currentUser.notifyProps]);
+ const mentionProps = useMemo(() => getMentionProps(currentUser), []);
+ const notifyProps = mentionProps.notifyProps;
- const notifyProps = currentUser.notifyProps || mentionProps.notifyProps;
- const [tglFirstName, setTglFirstName] = useState(notifyProps.first_name === 'true');
- const [tglChannel, setTglChannel] = useState(notifyProps.channel === 'true');
-
- const [tglUserName, setTglUserName] = useState(mentionProps.usernameMention);
- const [mentionKeys, setMentionKeys] = useState(mentionProps.mentionKeys);
+ const [mentionKeywords, setMentionKeywords] = useState(mentionProps.mentionKeywords);
+ const [channelMentionOn, setChannelMentionOn] = useState(mentionProps.channel);
+ const [firstNameMentionOn, setFirstNameMentionOn] = useState(mentionProps.first_name);
+ const [usernameMentionOn, setUsernameMentionOn] = useState(mentionProps.usernameMention);
+ const [replyNotificationType, setReplyNotificationType] = useState(mentionProps.comments);
const theme = useTheme();
const styles = getStyleSheet(theme);
const intl = useIntl();
- const saveButton = useMemo(() => getSaveButton(SAVE_MENTION_BUTTON_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
-
const close = () => popTopScreen(componentId);
+ const canSaveSettings = useCallback(() => {
+ const channelChanged = channelMentionOn !== mentionProps.channel;
+ const replyChanged = replyNotificationType !== mentionProps.comments;
+ const fNameChanged = firstNameMentionOn !== mentionProps.first_name;
+ const mnKeysChanged = mentionProps.mentionKeywords !== mentionKeywords;
+ const userNameChanged = usernameMentionOn !== mentionProps.usernameMention;
+
+ return fNameChanged || userNameChanged || channelChanged || mnKeysChanged || replyChanged;
+ }, [firstNameMentionOn, channelMentionOn, usernameMentionOn, mentionKeywords, notifyProps, replyNotificationType]);
+
const saveMention = useCallback(() => {
- const notify_props: UserNotifyProps = {
- ...notifyProps,
- first_name: `${tglFirstName}`,
- channel: `${tglChannel}`,
- mention_keys: mentionKeys};
- updateMe(serverUrl, {notify_props});
+ const canSave = canSaveSettings();
+
+ if (canSave) {
+ const mention_keys = [];
+ if (mentionKeywords.length > 0) {
+ mentionKeywords.split(',').forEach((m) => mention_keys.push(m.replace(/\s/g, '')));
+ }
+
+ if (usernameMentionOn) {
+ mention_keys.push(`${currentUser.username}`);
+ }
+ const notify_props: UserNotifyProps = {
+ ...notifyProps,
+ first_name: `${firstNameMentionOn}`,
+ channel: `${channelMentionOn}`,
+ mention_keys: mention_keys.join(','),
+ comments: replyNotificationType,
+ };
+ updateMe(serverUrl, {notify_props});
+ }
+
close();
- }, [serverUrl, notifyProps, tglFirstName, tglChannel, mentionKeys]);
+ }, [
+ canSaveSettings,
+ channelMentionOn,
+ firstNameMentionOn,
+ mentionKeywords,
+ notifyProps,
+ replyNotificationType,
+ serverUrl,
+ ]);
const onToggleFirstName = useCallback(() => {
- setTglFirstName((prev) => !prev);
+ setFirstNameMentionOn((prev) => !prev);
}, []);
const onToggleUserName = useCallback(() => {
- setTglUserName((prev) => !prev);
+ setUsernameMentionOn((prev) => !prev);
}, []);
const onToggleChannel = useCallback(() => {
- setTglChannel((prev) => !prev);
+ setChannelMentionOn((prev) => !prev);
}, []);
const onChangeText = useCallback((text: string) => {
- setMentionKeys(text);
+ setMentionKeywords(text);
}, []);
- useEffect(() => {
- const fNameChanged = tglFirstName !== Boolean(notifyProps.first_name);
- const channelChanged = tglChannel !== Boolean(notifyProps.channel);
+ useBackNavigation(saveMention);
- const usnChanged = tglUserName !== mentionProps.usernameMention;
- const kwsChanged = mentionProps.mentionKeys !== mentionKeys;
-
- const enabled = fNameChanged || usnChanged || channelChanged || kwsChanged;
-
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled,
- }],
- };
- setButtons(componentId, buttons);
- }, [componentId, saveButton, tglFirstName, tglChannel, tglUserName, mentionKeys, notifyProps]);
-
- useNavButtonPressed(SAVE_MENTION_BUTTON_ID, componentId, saveMention, [saveMention]);
-
- useAndroidHardwareBackHandler(componentId, close);
+ useAndroidHardwareBackHandler(componentId, saveMention);
return (
-
- {Boolean(currentUser?.firstName) && (
- <>
+ <>
+
+ {Boolean(currentUser?.firstName) && (
+ <>
+
+
+ >
+ )
+ }
+ {Boolean(currentUser?.username) && (
-
- >
- )
- }
- {Boolean(currentUser?.username) && (
+ )}
+
+
+
+
+ {intl.formatMessage({id: 'notification_settings.mentions.keywordsLabel', defaultMessage: 'Keywords are not case-sensitive. Separate keywords with commas.'})}
+
+
+ {!isCRTEnabled && (
+
)}
-
-
-
-
-
- {intl.formatMessage({id: 'notification_settings.mentions.keywordsLabel', defaultMessage: 'Keywords are not case-sensitive. Separate keywords with commas.'})}
-
-
+ >
);
};
diff --git a/app/screens/settings/notification_mention/notification_mention.tsx b/app/screens/settings/notification_mention/notification_mention.tsx
index 32094eb892..d50cb24c88 100644
--- a/app/screens/settings/notification_mention/notification_mention.tsx
+++ b/app/screens/settings/notification_mention/notification_mention.tsx
@@ -6,7 +6,6 @@ import React from 'react';
import SettingContainer from '../setting_container';
import MentionSettings from './mention_settings';
-import ReplySettings from './reply_settings';
import type UserModel from '@typings/database/models/servers/user';
@@ -21,8 +20,8 @@ const NotificationMention = ({componentId, currentUser, isCRTEnabled}: Notificat
- {!isCRTEnabled && }
);
};
diff --git a/app/screens/settings/notification_mention/reply_settings.tsx b/app/screens/settings/notification_mention/reply_settings.tsx
index fd826bc7a4..1e295c611a 100644
--- a/app/screens/settings/notification_mention/reply_settings.tsx
+++ b/app/screens/settings/notification_mention/reply_settings.tsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useState} from 'react';
+import React, {Dispatch, SetStateAction} from 'react';
import {useIntl} from 'react-intl';
import {t} from '@i18n';
@@ -14,9 +14,11 @@ const replyHeaderText = {
id: t('notification_settings.mention.reply'),
defaultMessage: 'Send reply notifications for',
};
-
-const ReplySettings = () => {
- const [replyNotificationType, setReplyNotificationType] = useState('any');
+type ReplySettingsProps = {
+ replyNotificationType: string;
+ setReplyNotificationType: Dispatch>;
+}
+const ReplySettings = ({replyNotificationType, setReplyNotificationType}: ReplySettingsProps) => {
const intl = useIntl();
return (
diff --git a/app/screens/settings/notification_push/notification_push.tsx b/app/screens/settings/notification_push/notification_push.tsx
index 679ca7e590..7398c8634c 100644
--- a/app/screens/settings/notification_push/notification_push.tsx
+++ b/app/screens/settings/notification_push/notification_push.tsx
@@ -1,20 +1,17 @@
// 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 React, {useCallback, useMemo, useState} from 'react';
import {Platform} from 'react-native';
import {updateMe} from '@actions/remote/user';
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 {popTopScreen, setButtons} from '@screens/navigation';
+import useBackNavigation from '@hooks/navigate_back';
+import {popTopScreen} from '@screens/navigation';
import SettingSeparator from '@screens/settings/settings_separator';
import {getNotificationProps} from '@utils/user';
-import {getSaveButton} from '../config';
import SettingContainer from '../setting_container';
import MobileSendPush from './push_send';
@@ -23,8 +20,6 @@ import MobilePushThread from './push_thread';
import type UserModel from '@typings/database/models/servers/user';
-const SAVE_NOTIF_BUTTON_ID = 'SAVE_NOTIF_BUTTON_ID';
-
type NotificationMobileProps = {
componentId: string;
currentUser: UserModel;
@@ -36,52 +31,40 @@ const NotificationPush = ({componentId, currentUser, isCRTEnabled, sendPushNotif
const notifyProps = useMemo(() => getNotificationProps(currentUser), [currentUser.notifyProps]);
- const [pushSend, setPushSend] = useState(notifyProps.push);
- const [pushStatus, setPushStatus] = useState(notifyProps.push_status);
- const [pushThread, setPushThreadPref] = useState(notifyProps?.push_threads || 'all');
-
- const intl = useIntl();
- const theme = useTheme();
+ const [pushSend, setPushSend] = useState(notifyProps.push);
+ const [pushStatus, setPushStatus] = useState(notifyProps.push_status);
+ const [pushThread, setPushThreadPref] = useState(notifyProps?.push_threads || 'all');
const onMobilePushThreadChanged = useCallback(() => {
setPushThreadPref(pushThread === 'all' ? 'mention' : 'all');
}, [pushThread]);
- const saveButton = useMemo(() => getSaveButton(SAVE_NOTIF_BUTTON_ID, intl, theme.sidebarHeaderTextColor), [theme.sidebarHeaderTextColor]);
+ const close = () => popTopScreen(componentId);
- const close = useCallback(() => popTopScreen(componentId), [componentId]);
-
- const saveNotificationSettings = useCallback(() => {
- const notify_props = {...notifyProps, push: pushSend, push_status: pushStatus, push_threads: pushThread};
- updateMe(serverUrl, {notify_props} as unknown as UserNotifyProps);
- close();
- }, [serverUrl, notifyProps, pushSend, pushStatus, pushThread, close]);
-
- useEffect(() => {
+ const canSaveSettings = useCallback(() => {
const p = pushSend !== notifyProps.push;
const pT = pushThread !== notifyProps.push_threads;
const pS = pushStatus !== notifyProps.push_status;
+ return p || pT || pS;
+ }, [notifyProps, pushSend, pushStatus, pushThread]);
- const enabled = p || pT || pS;
+ const saveNotificationSettings = useCallback(() => {
+ const canSave = canSaveSettings();
+ if (canSave) {
+ const notify_props: UserNotifyProps = {
+ ...notifyProps,
+ push: pushSend,
+ push_status: pushStatus,
+ push_threads: pushThread,
+ };
+ updateMe(serverUrl, {notify_props});
+ }
+ close();
+ }, [canSaveSettings, close, notifyProps, pushSend, pushStatus, pushThread, serverUrl]);
- const buttons = {
- rightButtons: [{
- ...saveButton,
- enabled,
- }],
- };
- setButtons(componentId, buttons);
- }, [
- componentId,
- notifyProps,
- pushSend,
- pushStatus,
- pushThread,
- ]);
+ useBackNavigation(saveNotificationSettings);
- useNavButtonPressed(SAVE_NOTIF_BUTTON_ID, componentId, saveNotificationSettings, [saveNotificationSettings]);
-
- useAndroidHardwareBackHandler(componentId, close);
+ useAndroidHardwareBackHandler(componentId, saveNotificationSettings);
return (
diff --git a/app/screens/settings/notification_push/push_send.tsx b/app/screens/settings/notification_push/push_send.tsx
index 5eedc91f9a..ad8085e49b 100644
--- a/app/screens/settings/notification_push/push_send.tsx
+++ b/app/screens/settings/notification_push/push_send.tsx
@@ -30,9 +30,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
});
type MobileSendPushProps = {
- pushStatus: PushStatus;
+ pushStatus: UserNotifyPropsPushStatus;
sendPushNotifications: boolean;
- setMobilePushPref: (status: PushStatus) => void;
+ setMobilePushPref: (status: UserNotifyPropsPushStatus) => void;
}
const MobileSendPush = ({sendPushNotifications, pushStatus, setMobilePushPref}: MobileSendPushProps) => {
const theme = useTheme();
diff --git a/app/screens/settings/notification_push/push_status.tsx b/app/screens/settings/notification_push/push_status.tsx
index 4559b8b047..a114bc582b 100644
--- a/app/screens/settings/notification_push/push_status.tsx
+++ b/app/screens/settings/notification_push/push_status.tsx
@@ -16,8 +16,8 @@ const headerText = {
};
type MobilePushStatusProps = {
- pushStatus: PushStatus;
- setMobilePushStatus: (status: PushStatus) => void;
+ pushStatus: UserNotifyPropsPushStatus;
+ setMobilePushStatus: (status: UserNotifyPropsPushStatus) => void;
}
const MobilePushStatus = ({pushStatus, setMobilePushStatus}: MobilePushStatusProps) => {
const intl = useIntl();
diff --git a/app/screens/settings/notification_push/push_thread.tsx b/app/screens/settings/notification_push/push_thread.tsx
index b3f0c88be9..f884855a76 100644
--- a/app/screens/settings/notification_push/push_thread.tsx
+++ b/app/screens/settings/notification_push/push_thread.tsx
@@ -17,7 +17,7 @@ const headerText = {
type MobilePushThreadProps = {
onMobilePushThreadChanged: (status: string) => void;
- pushThread: PushStatus;
+ pushThread: UserNotifyPropsPushThreads;
}
const MobilePushThread = ({pushThread, onMobilePushThreadChanged}: MobilePushThreadProps) => {
diff --git a/app/screens/settings/notifications/notifications.tsx b/app/screens/settings/notifications/notifications.tsx
index 8dfa52963d..e7bbd92a4b 100644
--- a/app/screens/settings/notifications/notifications.tsx
+++ b/app/screens/settings/notifications/notifications.tsx
@@ -6,7 +6,7 @@ import {useIntl} from 'react-intl';
import {General, Screens} from '@constants';
import {t} from '@i18n';
-import {goToScreen} from '@screens/navigation';
+import {gotoSettingsScreen} from '@screens/settings/config';
import {getEmailInterval, getEmailIntervalTexts, getNotificationProps} from '@utils/user';
import SettingContainer from '../setting_container';
@@ -58,8 +58,7 @@ const Notifications = ({
const id = isCRTEnabled ? t('notification_settings.mentions') : t('notification_settings.mentions_replies');
const defaultMessage = isCRTEnabled ? 'Mentions' : 'Mentions and Replies';
const title = intl.formatMessage({id, defaultMessage});
-
- goToScreen(screen, title);
+ gotoSettingsScreen(screen, title);
}, [isCRTEnabled]);
const goToNotificationSettingsPush = useCallback(() => {
@@ -69,7 +68,7 @@ const Notifications = ({
defaultMessage: 'Push Notifications',
});
- goToScreen(screen, title);
+ gotoSettingsScreen(screen, title);
}, []);
const goToNotificationAutoResponder = useCallback(() => {
@@ -78,13 +77,13 @@ const Notifications = ({
id: 'notification_settings.auto_responder',
defaultMessage: 'Automatic Replies',
});
- goToScreen(screen, title);
+ gotoSettingsScreen(screen, title);
}, []);
const goToEmailSettings = useCallback(() => {
const screen = Screens.SETTINGS_NOTIFICATION_EMAIL;
const title = intl.formatMessage({id: 'notification_settings.email', defaultMessage: 'Email Notifications'});
- goToScreen(screen, title);
+ gotoSettingsScreen(screen, title);
}, []);
return (
diff --git a/types/screens/settings.d.ts b/types/screens/settings.d.ts
index 500b68d5be..64c43e2728 100644
--- a/types/screens/settings.d.ts
+++ b/types/screens/settings.d.ts
@@ -1,4 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-type PushStatus = 'away' | 'online' | 'offline' | 'none' | 'mention' | 'all' | 'ooo' | 'dnd' | 'default' ;
+type UserNotifyPropsPush = typeof UserNotifyPropsPush.push;
+type UserNotifyPropsPushStatus = typeof UserNotifyPropsPush.push_status;
+type UserNotifyPropsPushThreads = typeof UserNotifyPropsPush.push_threads;