MM-46443 : Gekidou - removes save button on settings screens (#6583)

This commit is contained in:
Avinash Lingaloo
2022-08-24 16:54:40 +04:00
committed by GitHub
parent dba66832a3
commit afa38ddee6
20 changed files with 331 additions and 381 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 (

View File

@@ -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 (
<SettingContainer testID='display_clock'>

View File

@@ -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<string | undefined>(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 (
<SettingContainer testID='display_theme'>
<ThemeTiles
allowedThemeKeys={allowedThemeKeys}
onThemeChange={setDisplayTheme}
selectedTheme={displayTheme}
onThemeChange={setThemePreference}
selectedTheme={initialTheme}
/>
{theme.type === 'custom' && (
<CustomTheme
setTheme={setDisplayTheme}
displayTheme={displayTheme}
setTheme={setThemePreference}
displayTheme={initialTheme}
/>
)}
</SettingContainer>

View File

@@ -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) => {
/>
<SettingSeparator/>
{!userTimezone.useAutomaticTimezone && (
<>
{/* <SettingSeparator/> */}
<SettingOption
action={goToSelectTimezone}
info={getTimezoneRegion(userTimezone.manualTimezone)}
label={intl.formatMessage({id: 'settings_display.timezone.manual', defaultMessage: 'Change timezone'})}
type='arrow'
/>
</>
<SettingOption
action={goToSelectTimezone}
info={getTimezoneRegion(userTimezone.manualTimezone)}
label={intl.formatMessage({id: 'settings_display.timezone.manual', defaultMessage: 'Change timezone'})}
type='arrow'
/>
)}
{/* <SettingSeparator/> */}
</SettingContainer>
);
};

View File

@@ -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<string[]>(EMPTY_TIMEZONES);
const [initialScrollIndex, setInitialScrollIndex] = useState<number|undefined>();
const [searchRegion, setSearchRegion] = useState<string|undefined>(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 (
<TimezoneRow
isSelected={timezone === manualTimezone}
isSelected={timezone === currentTimezone}
onPressTimezone={onPressTimezone}
timezone={timezone}
/>
);
}, [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
<FlatList
contentContainerStyle={styles.flexGrow}
data={searchRegion?.length ? filteredTimezones() : timezones}
extraData={manualTimezone}
getItemLayout={getItemLayout}
initialScrollIndex={initialScrollIndex}
keyExtractor={keyExtractor}

View File

@@ -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 {fetchStatusInBatch, updateMe} from '@actions/remote/user';
@@ -11,14 +11,13 @@ import {General} 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 {changeOpacity, makeStyleSheetFromTheme, getKeyboardAppearanceFromTheme} from '@utils/theme';
import {popTopScreen} from '@screens/navigation';
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {getNotificationProps} from '@utils/user';
import {getSaveButton} from '../config';
import SettingContainer from '../setting_container';
import SettingOption from '../setting_option';
import SettingSeparator from '../settings_separator';
@@ -34,7 +33,6 @@ const OOO = {
id: t('notification_settings.auto_responder.default_message'),
defaultMessage: 'Hello, I am out of office and unable to respond to messages.',
};
const SAVE_OOO_BUTTON_ID = 'notification_settings.auto_responder.save.button';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
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<boolean>(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<string>(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 (
<SettingContainer testID='notification_auto_responder'>

View File

@@ -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<string>(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 (
<SettingContainer testID='notification_email'>
@@ -182,7 +171,7 @@ const NotificationEmail = ({componentId, currentUser, emailInterval, enableEmail
</Text>
}
</SettingBlock>
{isCRTEnabled && notifyProps.email === 'true' && (
{isCRTEnabled && notifyInterval !== Preferences.INTERVAL_NEVER.toString() && (
<SettingBlock
footerText={emailFooterCRTText}
headerText={emailHeaderCRTText}

View File

@@ -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';
@@ -10,14 +10,14 @@ import FloatingTextInput from '@components/floating_text_input_label';
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 ReplySettings from '@screens/settings/notification_mention/reply_settings';
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {getNotificationProps} from '@utils/user';
import {getSaveButton} from '../config';
import SettingBlock from '../setting_block';
import SettingOption from '../setting_option';
import SettingSeparator from '../settings_separator';
@@ -29,8 +29,6 @@ const mentionHeaderText = {
defaultMessage: 'Keywords that trigger mentions',
};
const SAVE_MENTION_BUTTON_ID = 'SAVE_MENTION_BUTTON_ID';
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
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 (
<SettingBlock
headerText={mentionHeaderText}
>
{Boolean(currentUser?.firstName) && (
<>
<>
<SettingBlock
headerText={mentionHeaderText}
>
{Boolean(currentUser?.firstName) && (
<>
<SettingOption
action={onToggleFirstName}
description={intl.formatMessage({id: 'notification_settings.mentions.sensitiveName', defaultMessage: 'Your case sensitive first name'})}
label={currentUser.firstName}
selected={firstNameMentionOn}
type='toggle'
/>
<SettingSeparator/>
</>
)
}
{Boolean(currentUser?.username) && (
<SettingOption
action={onToggleFirstName}
description={intl.formatMessage({id: 'notification_settings.mentions.sensitiveName', defaultMessage: 'Your case sensitive first name'})}
label={currentUser.firstName}
selected={tglFirstName}
action={onToggleUserName}
description={intl.formatMessage({id: 'notification_settings.mentions.sensitiveUsername', defaultMessage: 'Your non-case sensitive username'})}
label={currentUser.username}
selected={usernameMentionOn}
type='toggle'
/>
<SettingSeparator/>
</>
)
}
{Boolean(currentUser?.username) && (
)}
<SettingSeparator/>
<SettingOption
action={onToggleUserName}
description={intl.formatMessage({id: 'notification_settings.mentions.sensitiveUsername', defaultMessage: 'Your non-case sensitive username'})}
label={currentUser.username}
selected={tglUserName}
action={onToggleChannel}
description={intl.formatMessage({id: 'notification_settings.mentions.channelWide', defaultMessage: 'Channel-wide mentions'})}
label='@channel, @all, @here'
selected={channelMentionOn}
type='toggle'
/>
<SettingSeparator/>
<FloatingTextInput
allowFontScaling={true}
autoCapitalize='none'
autoCorrect={false}
blurOnSubmit={true}
containerStyle={styles.containerStyle}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
label={intl.formatMessage({id: 'notification_settings.mentions.keywords', defaultMessage: 'Enter other keywords'})}
multiline={true}
onChangeText={onChangeText}
placeholder={intl.formatMessage({id: 'notification_settings.mentions..keywordsDescription', defaultMessage: 'Other words that trigger a mention'})}
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
returnKeyType='done'
textInputStyle={styles.input}
textAlignVertical='top'
theme={theme}
underlineColorAndroid='transparent'
value={mentionKeywords}
/>
<Text
style={styles.keywordLabelStyle}
>
{intl.formatMessage({id: 'notification_settings.mentions.keywordsLabel', defaultMessage: 'Keywords are not case-sensitive. Separate keywords with commas.'})}
</Text>
</SettingBlock>
{!isCRTEnabled && (
<ReplySettings
replyNotificationType={replyNotificationType}
setReplyNotificationType={setReplyNotificationType}
/>
)}
<SettingSeparator/>
<SettingOption
action={onToggleChannel}
description={intl.formatMessage({id: 'notification_settings.mentions.channelWide', defaultMessage: 'Channel-wide mentions'})}
label='@channel, @all, @here'
selected={tglChannel}
type='toggle'
/>
<SettingSeparator/>
<FloatingTextInput
allowFontScaling={true}
autoCapitalize='none'
autoCorrect={false}
blurOnSubmit={true}
containerStyle={styles.containerStyle}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
label={intl.formatMessage({id: 'notification_settings.mentions.keywords', defaultMessage: 'Enter other keywords'})}
multiline={true}
onChangeText={onChangeText}
placeholder={intl.formatMessage({id: 'notification_settings.mentions..keywordsDescription', defaultMessage: 'Other words that trigger a mention'})}
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
returnKeyType='done'
textInputStyle={styles.input}
textAlignVertical='top'
theme={theme}
underlineColorAndroid='transparent'
value={mentionKeys}
/>
<Text
style={styles.keywordLabelStyle}
>
{intl.formatMessage({id: 'notification_settings.mentions.keywordsLabel', defaultMessage: 'Keywords are not case-sensitive. Separate keywords with commas.'})}
</Text>
</SettingBlock>
</>
);
};

View File

@@ -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
<MentionSettings
currentUser={currentUser}
componentId={componentId}
isCRTEnabled={isCRTEnabled}
/>
{!isCRTEnabled && <ReplySettings/>}
</SettingContainer>
);
};

View File

@@ -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<SetStateAction<string>>;
}
const ReplySettings = ({replyNotificationType, setReplyNotificationType}: ReplySettingsProps) => {
const intl = useIntl();
return (

View File

@@ -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<PushStatus>(notifyProps.push);
const [pushStatus, setPushStatus] = useState<PushStatus>(notifyProps.push_status);
const [pushThread, setPushThreadPref] = useState<PushStatus>(notifyProps?.push_threads || 'all');
const intl = useIntl();
const theme = useTheme();
const [pushSend, setPushSend] = useState<UserNotifyPropsPush>(notifyProps.push);
const [pushStatus, setPushStatus] = useState<UserNotifyPropsPushStatus>(notifyProps.push_status);
const [pushThread, setPushThreadPref] = useState<UserNotifyPropsPushThreads>(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 (
<SettingContainer testID='notification_push'>

View File

@@ -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();

View File

@@ -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();

View File

@@ -17,7 +17,7 @@ const headerText = {
type MobilePushThreadProps = {
onMobilePushThreadChanged: (status: string) => void;
pushThread: PushStatus;
pushThread: UserNotifyPropsPushThreads;
}
const MobilePushThread = ({pushThread, onMobilePushThreadChanged}: MobilePushThreadProps) => {

View File

@@ -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 (

View File

@@ -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;