forked from Ivasoft/mattermost-mobile
MM-46443 : Gekidou - removes save button on settings screens (#6583)
This commit is contained in:
21
app/hooks/navigate_back.ts
Normal file
21
app/hooks/navigate_back.ts
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -17,7 +17,7 @@ const headerText = {
|
||||
|
||||
type MobilePushThreadProps = {
|
||||
onMobilePushThreadChanged: (status: string) => void;
|
||||
pushThread: PushStatus;
|
||||
pushThread: UserNotifyPropsPushThreads;
|
||||
}
|
||||
|
||||
const MobilePushThread = ({pushThread, onMobilePushThreadChanged}: MobilePushThreadProps) => {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
4
types/screens/settings.d.ts
vendored
4
types/screens/settings.d.ts
vendored
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user