Add GM as DM feature support (#7515)

* Add GM as DM feature support

* Minor fix

* Address feedback

* Fix case for non set channel notify prop

* Fix strings
This commit is contained in:
Daniel Espino García 2023-09-26 18:35:40 +02:00 committed by GitHub
parent 0ec2b09fd5
commit ba52acb26f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 282 additions and 47 deletions

View file

@ -1,10 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {getActiveServerUrl} from '@app/init/credentials';
import {Tutorial} from '@constants';
import {GLOBAL_IDENTIFIERS} from '@constants/database';
import DatabaseManager from '@database/manager';
import {getActiveServerUrl} from '@init/credentials';
import {logError} from '@utils/log';
export const storeGlobal = async (id: string, value: unknown, prepareRecordsOnly = false) => {

View file

@ -1,10 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const ALL = 'all';
export const DEFAULT = 'default';
export const MENTION = 'mention';
export const NONE = 'none';
export const ALL = 'all' as const;
export const DEFAULT = 'default' as const;
export const MENTION = 'mention' as const;
export const NONE = 'none' as const;
export default {
ALL,

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const CATEGORIES_TO_KEEP: Record<string, string> = {
export const CATEGORIES_TO_KEEP = {
ADVANCED_SETTINGS: 'advanced_settings',
CHANNEL_APPROXIMATE_VIEW_TIME: 'channel_approximate_view_time',
CHANNEL_OPEN_TIME: 'channel_open_time',
@ -14,15 +14,21 @@ export const CATEGORIES_TO_KEEP: Record<string, string> = {
SIDEBAR_SETTINGS: 'sidebar_settings',
TEAMS_ORDER: 'teams_order',
THEME: 'theme',
SYSTEM_NOTICE: 'system_notice',
};
const CATEGORIES: Record<string, string> = {
const CATEGORIES = {
...CATEGORIES_TO_KEEP,
FAVORITE_CHANNEL: 'favorite_channel',
};
const NOTICES = {
GM_AS_DM: 'GMasDM',
};
const Preferences = {
CATEGORIES,
NOTICES,
COLLAPSED_REPLY_THREADS: 'collapsed_reply_threads',
COLLAPSED_REPLY_THREADS_OFF: 'off',
COLLAPSED_REPLY_THREADS_ON: 'on',

View file

@ -0,0 +1,4 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const GM_AS_DM_VERSION = [9, 1, 0];

View file

@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {GM_AS_DM_VERSION} from '@constants/versions';
import {isMinimumServerVersion} from '@utils/helpers';
import {observeConfigValue} from './system';
import type {Database} from '@nozbe/watermelondb';
export const observeHasGMasDMFeature = (database: Database) => {
return observeConfigValue(database, 'Version').pipe(
switchMap((v) => of$(isMinimumServerVersion(v, ...GM_AS_DM_VERSION))),
);
};

View file

@ -24,7 +24,9 @@ import EphemeralStore from '@store/ephemeral_store';
import ChannelPostList from './channel_post_list';
import ChannelHeader from './header';
import useGMasDMNotice from './use_gm_as_dm_notice';
import type PreferenceModel from '@typings/database/models/servers/preference';
import type {AvailableScreens} from '@typings/screens/navigation';
import type {KeyboardTrackingViewRef} from 'react-native-keyboard-tracking-view';
@ -36,6 +38,10 @@ type ChannelProps = {
isCallsEnabledInChannel: boolean;
showIncomingCalls: boolean;
isTabletView?: boolean;
dismissedGMasDMNotice: PreferenceModel[];
currentUserId: string;
channelType: ChannelType;
hasGMasDMFeature: boolean;
};
const edges: Edge[] = ['left', 'right'];
@ -55,7 +61,12 @@ const Channel = ({
isCallsEnabledInChannel,
showIncomingCalls,
isTabletView,
dismissedGMasDMNotice,
channelType,
currentUserId,
hasGMasDMFeature,
}: ChannelProps) => {
useGMasDMNotice(currentUserId, channelType, dismissedGMasDMNotice, hasGMasDMFeature);
const isTablet = useIsTablet();
const insets = useSafeAreaInsets();
const [shouldRenderPosts, setShouldRenderPosts] = useState(false);

View file

@ -27,6 +27,7 @@ type Props = {
isBot: boolean;
members?: ChannelMembershipModel[];
theme: Theme;
hasGMasDMFeature: boolean;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
@ -52,6 +53,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
textAlign: 'center',
...typography('Body', 200, 'Regular'),
},
boldText: {
...typography('Body', 200, 'SemiBold'),
},
profilesContainer: {
justifyContent: 'center',
alignItems: 'center',
@ -67,7 +71,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
},
}));
const DirectChannel = ({channel, currentUserId, isBot, members, theme}: Props) => {
const DirectChannel = ({
channel,
currentUserId,
isBot,
members,
theme,
hasGMasDMFeature,
}: Props) => {
const serverUrl = useServerUrl();
const styles = getStyleSheet(theme);
@ -89,11 +100,23 @@ const DirectChannel = ({channel, currentUserId, isBot, members, theme}: Props) =
/>
);
}
if (!hasGMasDMFeature) {
return (
<FormattedText
defaultMessage={'This is the start of your conversation with this group. Messages and files shared here are not shown to anyone else outside of the group.'}
id='intro.group_message.after_gm_as_dm'
style={styles.message}
/>
);
}
return (
<FormattedText
defaultMessage={'This is the start of your conversation with this group. Messages and files shared here are not shown to anyone else outside of the group.'}
defaultMessage={'This is the start of your conversation with this group. You\'ll be notified for <b>all activity</b> in this group message.'}
id='intro.group_message'
style={styles.message}
values={{
b: (chunk: string) => <Text style={styles.boldText}>{chunk}</Text>,
}}
/>
);
}, [channel.displayName, theme]);

View file

@ -8,6 +8,7 @@ import {switchMap} from 'rxjs/operators';
import {General} from '@constants';
import {observeChannelMembers} from '@queries/servers/channel';
import {observeHasGMasDMFeature} from '@queries/servers/features';
import {observeCurrentUserId} from '@queries/servers/system';
import {observeUser} from '@queries/servers/user';
import {getUserIdFromChannelName} from '@utils/user';
@ -23,6 +24,7 @@ const observeIsBot = (user: UserModel | undefined) => of$(Boolean(user?.isBot));
const enhanced = withObservables([], ({channel, database}: {channel: ChannelModel} & WithDatabaseArgs) => {
const currentUserId = observeCurrentUserId(database);
const members = observeChannelMembers(database, channel.id);
const hasGMasDMFeature = observeHasGMasDMFeature(database);
let isBot = of$(false);
if (channel.type === General.DM_CHANNEL) {
@ -40,6 +42,7 @@ const enhanced = withObservables([], ({channel, database}: {channel: ChannelMode
currentUserId,
isBot,
members,
hasGMasDMFeature,
};
});

View file

@ -12,8 +12,12 @@ import {
observeCurrentCall,
observeIncomingCalls,
} from '@calls/state';
import {Preferences} from '@constants';
import {withServerUrl} from '@context/server';
import {observeCurrentChannelId} from '@queries/servers/system';
import {observeCurrentChannel} from '@queries/servers/channel';
import {observeHasGMasDMFeature} from '@queries/servers/features';
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system';
import Channel from './channel';
@ -56,12 +60,21 @@ const enhanced = withObservables([], ({database, serverUrl}: EnhanceProps) => {
distinctUntilChanged(),
);
const dismissedGMasDMNotice = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.SYSTEM_NOTICE, Preferences.NOTICES.GM_AS_DM).observe();
const channelType = observeCurrentChannel(database).pipe(switchMap((c) => of$(c?.type)));
const currentUserId = observeCurrentUserId(database);
const hasGMasDMFeature = observeHasGMasDMFeature(database);
return {
channelId,
showJoinCallBanner,
isInACall,
showIncomingCalls,
isCallsEnabledInChannel: observeIsCallsEnabledInChannel(database, serverUrl, channelId),
dismissedGMasDMNotice,
channelType,
currentUserId,
hasGMasDMFeature,
};
});

View file

@ -0,0 +1,65 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useEffect} from 'react';
import {useIntl} from 'react-intl';
import {Alert} from 'react-native';
import {savePreference} from '@actions/remote/preference';
import {Preferences} from '@constants';
import {useServerUrl} from '@context/server';
import {getPreferenceAsBool} from '@helpers/api/preference';
import EphemeralStore from '@store/ephemeral_store';
import type PreferenceModel from '@typings/database/models/servers/preference';
const useGMasDMNotice = (userId: string, channelType: ChannelType, dismissedGMasDMNotice: PreferenceModel[], hasGMasDMFeature: boolean) => {
const intl = useIntl();
const serverUrl = useServerUrl();
useEffect(() => {
if (!hasGMasDMFeature) {
return;
}
const preferenceValue = getPreferenceAsBool(dismissedGMasDMNotice, Preferences.CATEGORIES.SYSTEM_NOTICE, Preferences.NOTICES.GM_AS_DM);
if (preferenceValue) {
return;
}
if (channelType !== 'G') {
return;
}
if (EphemeralStore.noticeShown.has(Preferences.NOTICES.GM_AS_DM)) {
return;
}
const onRemindMeLaterPress = () => {
EphemeralStore.noticeShown.add(Preferences.NOTICES.GM_AS_DM);
};
const onHideAndForget = () => {
EphemeralStore.noticeShown.add(Preferences.NOTICES.GM_AS_DM);
savePreference(serverUrl, [{category: Preferences.CATEGORIES.SYSTEM_NOTICE, name: Preferences.NOTICES.GM_AS_DM, value: 'true', user_id: userId}]);
};
// Show the GM as DM notice if needed
Alert.alert(
intl.formatMessage({id: 'system_notice.title.gm_as_dm', defaultMessage: 'Updates to Group Messages'}),
intl.formatMessage({id: 'system_noticy.body.gm_as_dm', defaultMessage: 'You will now be notified for all activity in your group messages along with a notification badge for every new message.\n\nYou can configure this in notification preferences for each group message.'}),
[
{
text: intl.formatMessage({id: 'system_notice.remind_me', defaultMessage: 'Remind Me Later'}),
onPress: onRemindMeLaterPress,
},
{
text: intl.formatMessage({id: 'system_notice.dont_show', defaultMessage: 'Don\'t Show Again'}),
onPress: onHideAndForget,
},
],
);
}, []);
};
export default useGMasDMNotice;

View file

@ -6,7 +6,9 @@ import withObservables from '@nozbe/with-observables';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {NotificationLevel} from '@constants';
import {observeChannel, observeChannelSettings} from '@queries/servers/channel';
import {observeHasGMasDMFeature} from '@queries/servers/features';
import {observeCurrentUser} from '@queries/servers/user';
import {getNotificationProps} from '@utils/user';
@ -19,17 +21,22 @@ type Props = WithDatabaseArgs & {
}
const enhanced = withObservables(['channelId'], ({channelId, database}: Props) => {
const displayName = observeChannel(database, channelId).pipe(switchMap((c) => of$(c?.displayName)));
const channel = observeChannel(database, channelId);
const channelType = channel.pipe(switchMap((c) => of$(c?.type)));
const displayName = channel.pipe(switchMap((c) => of$(c?.displayName)));
const settings = observeChannelSettings(database, channelId);
const userNotifyLevel = observeCurrentUser(database).pipe(switchMap((u) => of$(getNotificationProps(u).push)));
const notifyLevel = settings.pipe(
switchMap((s) => of$(s?.notifyProps.push)),
switchMap((s) => of$(s?.notifyProps.push || NotificationLevel.DEFAULT)),
);
const hasGMasDMFeature = observeHasGMasDMFeature(database);
return {
displayName,
notifyLevel,
userNotifyLevel,
channelType,
hasGMasDMFeature,
};
});

View file

@ -10,6 +10,7 @@ import {NotificationLevel, Screens} from '@constants';
import {useTheme} from '@context/theme';
import {t} from '@i18n';
import {goToScreen} from '@screens/navigation';
import {isTypeDMorGM} from '@utils/channel';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity} from '@utils/theme';
@ -20,6 +21,8 @@ type Props = {
displayName: string;
notifyLevel: NotificationLevel;
userNotifyLevel: NotificationLevel;
channelType: ChannelType;
hasGMasDMFeature: boolean;
}
const notificationLevel = (notifyLevel: NotificationLevel) => {
@ -50,7 +53,14 @@ const notificationLevel = (notifyLevel: NotificationLevel) => {
return {id, defaultMessage};
};
const NotificationPreference = ({channelId, displayName, notifyLevel, userNotifyLevel}: Props) => {
const NotificationPreference = ({
channelId,
displayName,
notifyLevel,
userNotifyLevel,
channelType,
hasGMasDMFeature,
}: Props) => {
const {formatMessage} = useIntl();
const theme = useTheme();
const title = formatMessage({id: 'channel_info.mobile_notifications', defaultMessage: 'Mobile Notifications'});
@ -74,13 +84,19 @@ const NotificationPreference = ({channelId, displayName, notifyLevel, userNotify
});
const notificationLevelToText = () => {
if (notifyLevel === NotificationLevel.DEFAULT) {
const userLevel = notificationLevel(userNotifyLevel);
return formatMessage(userLevel);
let notifyLevelToUse = notifyLevel;
if (notifyLevelToUse === NotificationLevel.DEFAULT) {
notifyLevelToUse = userNotifyLevel;
}
const channelLevel = notificationLevel(notifyLevel);
return formatMessage(channelLevel);
if (hasGMasDMFeature) {
if (notifyLevel === NotificationLevel.DEFAULT && notifyLevelToUse === NotificationLevel.MENTION && isTypeDMorGM(channelType)) {
notifyLevelToUse = NotificationLevel.ALL;
}
}
const messageDescriptor = notificationLevel(notifyLevelToUse);
return formatMessage(messageDescriptor);
};
return (

View file

@ -7,10 +7,12 @@ import {useSharedValue} from 'react-native-reanimated';
import {updateChannelNotifyProps} from '@actions/remote/channel';
import SettingsContainer from '@components/settings/container';
import {NotificationLevel} from '@constants';
import {useServerUrl} from '@context/server';
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
import useDidUpdate from '@hooks/did_update';
import useBackNavigation from '@hooks/navigate_back';
import {isTypeDMorGM} from '@utils/channel';
import {popTopScreen} from '../navigation';
@ -28,16 +30,29 @@ type Props = {
defaultThreadReplies: 'all' | 'mention';
isCRTEnabled: boolean;
isMuted: boolean;
notifyLevel?: NotificationLevel;
notifyLevel: NotificationLevel;
notifyThreadReplies?: 'all' | 'mention';
channelType: ChannelType;
hasGMasDMFeature: boolean;
}
const ChannelNotificationPreferences = ({channelId, componentId, defaultLevel, defaultThreadReplies, isCRTEnabled, isMuted, notifyLevel, notifyThreadReplies}: Props) => {
const ChannelNotificationPreferences = ({
channelId,
componentId,
defaultLevel,
defaultThreadReplies,
isCRTEnabled,
isMuted,
notifyLevel,
notifyThreadReplies,
channelType,
hasGMasDMFeature,
}: Props) => {
const serverUrl = useServerUrl();
const defaultNotificationReplies = defaultThreadReplies === 'all';
const diffNotificationLevel = notifyLevel !== 'default' && notifyLevel !== defaultLevel;
const diffNotificationLevel = notifyLevel !== NotificationLevel.DEFAULT && notifyLevel !== defaultLevel;
const notifyTitleTop = useSharedValue((isMuted ? MUTED_BANNER_HEIGHT : 0) + BLOCK_TITLE_HEIGHT);
const [notifyAbout, setNotifyAbout] = useState<NotificationLevel>((notifyLevel === undefined || notifyLevel === 'default') ? defaultLevel : notifyLevel);
const [notifyAbout, setNotifyAbout] = useState<NotificationLevel>(notifyLevel === NotificationLevel.DEFAULT ? defaultLevel : notifyLevel);
const [threadReplies, setThreadReplies] = useState<boolean>((notifyThreadReplies || defaultThreadReplies) === 'all');
const [resetDefaultVisible, setResetDefaultVisible] = useState(diffNotificationLevel || defaultNotificationReplies !== threadReplies);
@ -64,8 +79,13 @@ const ChannelNotificationPreferences = ({channelId, componentId, defaultLevel, d
const save = useCallback(() => {
const pushThreads = threadReplies ? 'all' : 'mention';
if (notifyLevel !== notifyAbout || (isCRTEnabled && pushThreads !== notifyThreadReplies)) {
const props: Partial<ChannelNotifyProps> = {push: notifyAbout};
let notifyAboutToUse = notifyAbout;
if (notifyAbout === defaultLevel) {
notifyAboutToUse = NotificationLevel.DEFAULT;
}
if (notifyLevel !== notifyAboutToUse || (isCRTEnabled && pushThreads !== notifyThreadReplies)) {
const props: Partial<ChannelNotifyProps> = {push: notifyAboutToUse};
if (isCRTEnabled) {
props.push_threads = pushThreads;
}
@ -73,11 +93,15 @@ const ChannelNotificationPreferences = ({channelId, componentId, defaultLevel, d
updateChannelNotifyProps(serverUrl, channelId, props);
}
popTopScreen(componentId);
}, [channelId, componentId, isCRTEnabled, notifyAbout, notifyLevel, notifyThreadReplies, serverUrl, threadReplies]);
}, [defaultLevel, channelId, componentId, isCRTEnabled, notifyAbout, notifyLevel, notifyThreadReplies, serverUrl, threadReplies]);
useBackNavigation(save);
useAndroidHardwareBackHandler(componentId, save);
const showThreadReplies = isCRTEnabled && (
!hasGMasDMFeature ||
!isTypeDMorGM(channelType)
);
return (
<SettingsContainer testID='push_notification_settings'>
{isMuted && <MutedBanner channelId={channelId}/>}
@ -94,7 +118,7 @@ const ChannelNotificationPreferences = ({channelId, componentId, defaultLevel, d
notifyTitleTop={notifyTitleTop}
onPress={onNotificationLevel}
/>
{isCRTEnabled &&
{showThreadReplies &&
<ThreadReplies
isSelected={threadReplies}
onPress={onSetThreadReplies}

View file

@ -4,11 +4,14 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {switchMap, combineLatestWith} from 'rxjs/operators';
import {observeChannelSettings, observeIsMutedSetting} from '@queries/servers/channel';
import {NotificationLevel} from '@constants';
import {observeChannel, observeChannelSettings, observeIsMutedSetting} from '@queries/servers/channel';
import {observeHasGMasDMFeature} from '@queries/servers/features';
import {observeIsCRTEnabled} from '@queries/servers/thread';
import {observeCurrentUser} from '@queries/servers/user';
import {isTypeDMorGM} from '@utils/channel';
import {getNotificationProps} from '@utils/user';
import ChannelNotificationPreferences from './channel_notification_preferences';
@ -24,9 +27,11 @@ const enhanced = withObservables([], ({channelId, database}: EnhancedProps) => {
const isCRTEnabled = observeIsCRTEnabled(database);
const isMuted = observeIsMutedSetting(database, channelId);
const notifyProps = observeCurrentUser(database).pipe(switchMap((u) => of$(getNotificationProps(u))));
const channelType = observeChannel(database, channelId).pipe(switchMap((c) => of$(c?.type)));
const hasGMasDMFeature = observeHasGMasDMFeature(database);
const notifyLevel = settings.pipe(
switchMap((s) => of$(s?.notifyProps.push)),
switchMap((s) => of$(s?.notifyProps.push || NotificationLevel.DEFAULT)),
);
const notifyThreadReplies = settings.pipe(
@ -35,7 +40,21 @@ const enhanced = withObservables([], ({channelId, database}: EnhancedProps) => {
const defaultLevel = notifyProps.pipe(
switchMap((n) => of$(n?.push)),
combineLatestWith(hasGMasDMFeature, channelType),
switchMap(([v, hasFeature, cType]) => {
const shouldShowwithGMasDMBehavior = hasFeature && isTypeDMorGM(cType);
let defaultLevelToUse = v;
if (shouldShowwithGMasDMBehavior) {
if (v === NotificationLevel.MENTION) {
defaultLevelToUse = NotificationLevel.ALL;
}
}
return of$(defaultLevelToUse);
}),
);
const defaultThreadReplies = notifyProps.pipe(
switchMap((n) => of$(n?.push_threads)),
);
@ -47,6 +66,8 @@ const enhanced = withObservables([], ({channelId, database}: EnhancedProps) => {
notifyThreadReplies,
defaultLevel,
defaultThreadReplies,
channelType,
hasGMasDMFeature,
};
});

View file

@ -40,7 +40,7 @@ const NOTIFY_OPTIONS: Record<string, NotifPrefOptions> = {
value: NotificationLevel.ALL,
},
[NotificationLevel.MENTION]: {
defaultMessage: 'Mentions, direct messages only',
defaultMessage: 'Mentions only',
id: t('channel_notification_preferences.notification.mention'),
testID: 'channel_notification_preferences.notification.mention',
value: NotificationLevel.MENTION,
@ -53,7 +53,13 @@ const NOTIFY_OPTIONS: Record<string, NotifPrefOptions> = {
},
};
const NotifyAbout = ({defaultLevel, isMuted, notifyLevel, notifyTitleTop, onPress}: Props) => {
const NotifyAbout = ({
defaultLevel,
isMuted,
notifyLevel,
notifyTitleTop,
onPress,
}: Props) => {
const {formatMessage} = useIntl();
const onLayout = useCallback((e: LayoutChangeEvent) => {
const {y} = e.nativeEvent.layout;
@ -61,6 +67,11 @@ const NotifyAbout = ({defaultLevel, isMuted, notifyLevel, notifyTitleTop, onPres
notifyTitleTop.value = y > 0 ? y + 10 : BLOCK_TITLE_HEIGHT;
}, []);
let notifyLevelToUse = notifyLevel;
if (notifyLevel === NotificationLevel.DEFAULT) {
notifyLevelToUse = defaultLevel;
}
return (
<SettingBlock
headerText={NOTIFY_ABOUT}
@ -77,7 +88,7 @@ const NotifyAbout = ({defaultLevel, isMuted, notifyLevel, notifyTitleTop, onPres
<SettingOption
action={onPress}
label={label}
selected={notifyLevel === key}
selected={notifyLevelToUse === key}
testID={testID}
type='select'
value={value}

View file

@ -36,7 +36,8 @@ const NOTIFY_OPTIONS_THREAD: Record<string, NotifPrefOptions> = {
const NotifyAbout = ({isSelected, notifyLevel, onPress}: Props) => {
const {formatMessage} = useIntl();
if ([NotificationLevel.NONE, NotificationLevel.ALL].includes(notifyLevel)) {
const hiddenStates: NotificationLevel[] = [NotificationLevel.NONE, NotificationLevel.ALL];
if (hiddenStates.includes(notifyLevel)) {
return null;
}

View file

@ -73,19 +73,24 @@ const NotificationPush = ({componentId, currentUser, isCRTEnabled, sendPushNotif
sendPushNotifications={sendPushNotifications}
setMobilePushPref={setPushSend}
/>
{Platform.OS === 'android' && (<SettingSeparator isGroupSeparator={true}/>)}
{isCRTEnabled && pushSend === 'mention' && (
<MobilePushThread
pushThread={pushThread}
onMobilePushThreadChanged={onMobilePushThreadChanged}
/>
<>
{Platform.OS === 'android' && (<SettingSeparator isGroupSeparator={true}/>)}
<MobilePushThread
pushThread={pushThread}
onMobilePushThreadChanged={onMobilePushThreadChanged}
/>
</>
)}
{Platform.OS === 'android' && (<SettingSeparator isGroupSeparator={true}/>)}
{sendPushNotifications && pushSend !== 'none' && (
<MobilePushStatus
pushStatus={pushStatus}
setMobilePushStatus={setPushStatus}
/>
<>
{Platform.OS === 'android' && (<SettingSeparator isGroupSeparator={true}/>)}
<MobilePushStatus
pushStatus={pushStatus}
setMobilePushStatus={setPushStatus}
/>
</>
)}
</SettingContainer>
);

View file

@ -55,7 +55,7 @@ const MobileSendPush = ({sendPushNotifications, pushStatus, setMobilePushPref}:
<SettingSeparator/>
<SettingOption
action={setMobilePushPref}
label={intl.formatMessage({id: 'notification_settings.pushNotification.mentions_only', defaultMessage: 'Mentions, direct messages only (default)'})}
label={intl.formatMessage({id: 'notification_settings.pushNotification.mentions_only', defaultMessage: 'Only for mentions, direct messages and group messages (default)'})}
selected={pushStatus === 'mention'}
testID='push_notification_settings.mentions_only.option'
type='select'

View file

@ -14,6 +14,8 @@ class EphemeralStore {
creatingChannel = false;
creatingDMorGMTeammates: string[] = [];
noticeShown = new Set<string>();
private pushProxyVerification: {[serverUrl: string]: string | undefined} = {};
private canJoinOtherTeams: {[serverUrl: string]: BehaviorSubject<boolean>} = {};

View file

@ -17,7 +17,7 @@ const ShareModule: NativeShareExtension|undefined = Platform.select({android: Na
// versions, and a non-equal minor version will ignore dot version.
// currentVersion is a string, e.g '4.6.0'
// minMajorVersion, minMinorVersion, minDotVersion are integers
export const isMinimumServerVersion = (currentVersion: string, minMajorVersion = 0, minMinorVersion = 0, minDotVersion = 0): boolean => {
export const isMinimumServerVersion = (currentVersion = '', minMajorVersion = 0, minMinorVersion = 0, minDotVersion = 0): boolean => {
if (!currentVersion || typeof currentVersion !== 'string') {
return false;
}

View file

@ -179,7 +179,7 @@
"channel_notification_preferences.muted_content": "You can change the notification settings, but you will not receive notifications until the channel is unmuted.",
"channel_notification_preferences.muted_title": "This channel is muted",
"channel_notification_preferences.notification.all": "All new messages",
"channel_notification_preferences.notification.mention": "Mentions, direct messages only",
"channel_notification_preferences.notification.mention": "Mentions only",
"channel_notification_preferences.notification.none": "Nothing",
"channel_notification_preferences.notification.thread_replies": "Notify me about replies to threads Im following in this channel",
"channel_notification_preferences.notify_about": "Notify me about...",
@ -341,6 +341,7 @@
"intro.created_by": "created by {creator} on {date}.",
"intro.direct_message": "This is the start of your conversation with {teammate}. Messages and files shared here are not shown to anyone else.",
"intro.group_message": "This is the start of your conversation with this group. Messages and files shared here are not shown to anyone else outside of the group.",
"intro.group_message.after_gm_as_dm": "This is the start of your conversation with this group. Messages and files shared here are not shown to anyone else outside of the group.",
"intro.private_channel": "Private Channel",
"intro.public_channel": "Public Channel",
"intro.townsquare": "Welcome to {name}. Everyone automatically becomes a member of this channel when they join the team.",
@ -765,7 +766,7 @@
"notification_settings.push_threads.replies": "Thread replies",
"notification_settings.pushNotification.all_new_messages": "All new messages",
"notification_settings.pushNotification.disabled_long": "Push notifications for mobile devices have been disabled by your System Administrator.",
"notification_settings.pushNotification.mentions_only": "Mentions, direct messages only (default)",
"notification_settings.pushNotification.mentions_only": "Only for mentions, direct messages and group messages (default)",
"notification_settings.pushNotification.nothing": "Nothing",
"notification_settings.send_notification.about": "Notify me about...",
"notification_settings.threads_mentions": "Mentions in threads",
@ -1016,6 +1017,10 @@
"suggestion.search.direct": "Direct Messages",
"suggestion.search.private": "Private Channels",
"suggestion.search.public": "Public Channels",
"system_notice.dont_show": "Don't Show Again",
"system_notice.remind_me": "Remind Me Later",
"system_notice.title.gm_as_dm": "Updates to Group Messages",
"system_noticy.body.gm_as_dm": "You will now be notified for all activity in your group messages along with a notification badge for every new message.\n\nYou can configure this in notification preferences for each group message.",
"team_list.no_other_teams.description": "To join another team, ask a Team Admin for an invitation, or create your own team.",
"team_list.no_other_teams.title": "No additional teams to join",
"terms_of_service.acceptButton": "Accept",