MM-36980, MM-37030: Opens the thread on push notification and Notifications settings (#5618)
* MM-36980: Opens the thread on push notification This commit enables tapping on a push notification for a reply to open the thread. This is done only if the user has CRT set to 'on'. * Refactor, and handle in app notifications * Minor change * Fixes erroneous check * Fixes on dismiss in app notification * Fixes in app push notification for CRT * Adds CRT notification settings for android Adds support for CRT (*_threads) notification settings (notify_props). Adds android comoponents to toggle those settings for push notifications and email notifications. * Adds CRT notification settings for iOS Adds iOS components to toggle those CRT notifications settings for push notifications and email notifications. * Fixes bad JSON and intl ids * Fixes i18n ids * Fixes tests * Adds email_threads to the default notify_props * Fixes push_threads notify_prop default value * Fixes style, and channel missing on thread open * Fixes test * Fixes click notification to open thread Previous handling of opening the thread on notification clicked fell into an infinite loop when the app was closed. This commit fixes that by adding the post to selectedPostId reducer and then emitting an event to open the thread on channel_base, and only if the app was started by the notification. When the app is in the background emitting the event from handleNotification works just fine. When the app is in the foreground the notification clicked gets handled elsewhere and this commit does not change that. * Removes reply settings when CRT is ON "Mentions and Replies" section becomes just "Mentions" when the user has the Collapsed Reply Threads set to "ON". * Fixes prop types Co-authored-by: Kyriakos Ziakoulis <koox00@Kyriakoss-MacBook-Pro.local>
This commit is contained in:
parent
8228ed3400
commit
6896129c73
29 changed files with 884 additions and 22 deletions
|
|
@ -8,7 +8,7 @@ import {NavigationTypes, ViewTypes} from '@constants';
|
|||
import {ChannelTypes, GeneralTypes, TeamTypes} from '@mm-redux/action_types';
|
||||
import {getChannelAndMyMember} from '@mm-redux/actions/channels';
|
||||
import {getDataRetentionPolicy} from '@mm-redux/actions/general';
|
||||
import {receivedNewPost} from '@mm-redux/actions/posts';
|
||||
import {receivedNewPost, selectPost} from '@mm-redux/actions/posts';
|
||||
import {getMyTeams, getMyTeamMembers, getMyTeamUnreads} from '@mm-redux/actions/teams';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
|
|
@ -98,6 +98,10 @@ export function loadFromPushNotification(notification, isInitialNotification) {
|
|||
|
||||
dispatch(handleSelectTeamAndChannel(teamId, channelId));
|
||||
|
||||
const {root_id: rootId} = notification.payload || {};
|
||||
if (isCollapsedThreadsEnabled(state) && rootId) {
|
||||
dispatch(selectPost(rootId));
|
||||
}
|
||||
return {data: true};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import {autoUpdateTimezone} from '@mm-redux/actions/timezone';
|
|||
import {General} from '@mm-redux/constants';
|
||||
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
|
||||
import {getConfig} from '@mm-redux/selectors/entities/general';
|
||||
import {isPostSelected} from '@mm-redux/selectors/entities/posts';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone';
|
||||
import {getCurrentUser, getUser} from '@mm-redux/selectors/entities/users';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
|
|
@ -334,7 +336,10 @@ class GlobalEventHandler {
|
|||
const state = getState();
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
|
||||
if (payload?.channel_id !== currentChannelId) {
|
||||
if (
|
||||
payload?.channel_id !== currentChannelId ||
|
||||
(payload?.root_id && isCollapsedThreadsEnabled(state) && !isPostSelected(state, payload?.root_id))
|
||||
) {
|
||||
const screen = 'Notification';
|
||||
const passProps = {
|
||||
notification,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {NavigationTypes, ViewTypes} from '@constants';
|
|||
import {getLocalizedMessage} from '@i18n';
|
||||
import {setDeviceToken} from '@mm-redux/actions/general';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {getCurrentLocale} from '@selectors/i18n';
|
||||
import {getBadgeCount} from '@selectors/views';
|
||||
|
|
@ -31,7 +32,7 @@ import Store from '@store/store';
|
|||
import {waitForHydration} from '@store/utils';
|
||||
import {t} from '@utils/i18n';
|
||||
|
||||
import type {DispatchFunc} from '@mm-redux/types/actions';
|
||||
import type {DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
|
||||
|
||||
const CATEGORY = 'CAN_REPLY';
|
||||
const REPLY_ACTION = 'REPLY_ACTION';
|
||||
|
|
@ -160,6 +161,7 @@ class PushNotifications {
|
|||
|
||||
if (Store.redux && payload) {
|
||||
const dispatch = Store.redux.dispatch as DispatchFunc;
|
||||
const getState = Store.redux.getState as GetStateFunc;
|
||||
|
||||
waitForHydration(Store.redux, async () => {
|
||||
switch (payload.type) {
|
||||
|
|
@ -182,6 +184,13 @@ class PushNotifications {
|
|||
|
||||
await dismissAllModals();
|
||||
await popToRoot();
|
||||
|
||||
if (!isInitialNotification) {
|
||||
const {root_id: rootId, channel_id: channelId} = notification.payload || {};
|
||||
if (rootId && isCollapsedThreadsEnabled(getState())) {
|
||||
EventEmitter.emit('goToThread', {id: rootId, channel_id: channelId});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -654,3 +654,20 @@ export const makeIsPostCommentMention = (): ((b: GlobalState, postId: $ID<Post>,
|
|||
export function getExpandedLink(state: GlobalState, link: string): string {
|
||||
return state.entities.posts.expandedURLs[link];
|
||||
}
|
||||
|
||||
export function isPostSelected(state: GlobalState, id: $ID<Post>) {
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return state.entities.posts.selectedPostId === id;
|
||||
}
|
||||
|
||||
export function getSelectedPost(state: GlobalState): Post | null {
|
||||
const selectedPostId = state.entities.posts.selectedPostId;
|
||||
if (selectedPostId) {
|
||||
return getPost(state, selectedPostId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@ export type UserNotifyProps = {
|
|||
auto_responder_active?: 'true' | 'false';
|
||||
auto_responder_message?: string;
|
||||
desktop: 'default' | 'all' | 'mention' | 'none';
|
||||
desktop_threads?: 'all' | 'mention';
|
||||
desktop_notification_sound?: string;
|
||||
desktop_sound: 'true' | 'false';
|
||||
email: 'true' | 'false';
|
||||
email_threads?: 'all' | 'mention';
|
||||
mark_unread: 'all' | 'mention';
|
||||
push: 'default' | 'all' | 'mention' | 'none';
|
||||
push_threads?: 'all' | 'mention';
|
||||
push_status: 'ooo' | 'offline' | 'away' | 'dnd' | 'online';
|
||||
comments: 'never' | 'root' | 'any';
|
||||
first_name: 'true' | 'false';
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ export default class ChannelBase extends PureComponent {
|
|||
showTermsOfService: PropTypes.bool,
|
||||
skipMetrics: PropTypes.bool,
|
||||
viewingGlobalThreads: PropTypes.bool,
|
||||
selectedPost: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
channel_id: PropTypes.string.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -195,6 +199,9 @@ export default class ChannelBase extends PureComponent {
|
|||
loadChannels = (teamId) => {
|
||||
const {loadChannelsForTeam, selectInitialChannel} = this.props.actions;
|
||||
if (EphemeralStore.getStartFromNotification()) {
|
||||
if (this.props.selectedPost) {
|
||||
EventEmitter.emit('goToThread', this.props.selectedPost);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Switch to channel from a push notification');
|
||||
EphemeralStore.setStartFromNotification(false);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {ViewTypes} from '@constants';
|
|||
import {getChannelStats} from '@mm-redux/actions/channels';
|
||||
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
|
||||
import {getServerVersion} from '@mm-redux/selectors/entities/general';
|
||||
import {getSelectedPost} from '@mm-redux/selectors/entities/posts';
|
||||
import {getTheme, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeam} from '@mm-redux/selectors/entities/teams';
|
||||
import {getCurrentUserId, getCurrentUserRoles, shouldShowTermsOfService} from '@mm-redux/selectors/entities/users';
|
||||
|
|
@ -46,6 +47,7 @@ function mapStateToProps(state) {
|
|||
currentUserId,
|
||||
isSupportedServer,
|
||||
isSystemAdmin,
|
||||
selectedPost: getSelectedPost(state),
|
||||
showTermsOfService: shouldShowTermsOfService(state),
|
||||
teamName: currentTeam?.display_name,
|
||||
theme: getTheme(state),
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import * as Animatable from 'react-native-animatable';
|
|||
import {PanGestureHandler} from 'react-native-gesture-handler';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
import {useDispatch} from 'react-redux';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {popToRoot, dismissAllModals, dismissOverlay} from '@actions/navigation';
|
||||
import {loadFromPushNotification} from '@actions/views/root';
|
||||
import {NavigationTypes} from '@constants';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ const Notification = ({componentId, notification}: NotificationProps) => {
|
|||
const dispatch = useDispatch();
|
||||
const dismissTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const tapped = useRef<boolean>(false);
|
||||
const collapsedThreadsEnabled = useSelector(isCollapsedThreadsEnabled);
|
||||
|
||||
const animateDismissOverlay = () => {
|
||||
cancelDismissTimer();
|
||||
|
|
@ -100,6 +102,11 @@ const Notification = ({componentId, notification}: NotificationProps) => {
|
|||
if (componentId === screen && tapped.current) {
|
||||
await dismissAllModals();
|
||||
await popToRoot();
|
||||
|
||||
const {root_id: rootId, channel_id: channelId} = notification.payload || {};
|
||||
if (rootId && collapsedThreadsEnabled) {
|
||||
EventEmitter.emit('goToThread', {id: rootId, channel_id: channelId});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ NotificationSettings {
|
|||
"textComponent": "span",
|
||||
"timeZone": null,
|
||||
},
|
||||
"isCollapsedThreadsEnabled": false,
|
||||
"theme": Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
|
|
@ -193,6 +194,7 @@ NotificationSettings {
|
|||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
isCollapsedThreadsEnabled={false}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
@ -404,3 +406,410 @@ NotificationSettings {
|
|||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`NotificationSettings should match snapshot, when CRT is ON 1`] = `
|
||||
NotificationSettings {
|
||||
"context": Object {
|
||||
"intl": Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": "span",
|
||||
"timeZone": null,
|
||||
},
|
||||
},
|
||||
"goToNotificationSettingsAutoResponder": [Function],
|
||||
"goToNotificationSettingsEmail": [Function],
|
||||
"goToNotificationSettingsMentions": [Function],
|
||||
"goToNotificationSettingsMobile": [Function],
|
||||
"handlePress": [Function],
|
||||
"props": Object {
|
||||
"actions": Object {
|
||||
"updateMe": [MockFunction],
|
||||
},
|
||||
"componentId": "component-id",
|
||||
"currentUser": Object {
|
||||
"id": "current_user_id",
|
||||
},
|
||||
"currentUserStatus": "status",
|
||||
"enableAutoResponder": false,
|
||||
"intl": Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": "span",
|
||||
"timeZone": null,
|
||||
},
|
||||
"isCollapsedThreadsEnabled": true,
|
||||
"theme": Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
},
|
||||
"updateMeRequest": Object {},
|
||||
},
|
||||
"refs": Object {},
|
||||
"sanitizeNotificationProps": [Function],
|
||||
"saveAutoResponder": [Function],
|
||||
"saveNotificationProps": [Function],
|
||||
"setState": [Function],
|
||||
"shouldSaveAutoResponder": [Function],
|
||||
"state": null,
|
||||
"updater": Updater {
|
||||
"_callbacks": Array [],
|
||||
"_renderer": ReactShallowRenderer {
|
||||
"_context": Object {
|
||||
"intl": Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": "span",
|
||||
"timeZone": null,
|
||||
},
|
||||
},
|
||||
"_didScheduleRenderPhaseUpdate": false,
|
||||
"_dispatcher": Object {
|
||||
"readContext": [Function],
|
||||
"useCallback": [Function],
|
||||
"useContext": [Function],
|
||||
"useDebugValue": [Function],
|
||||
"useDeferredValue": [Function],
|
||||
"useEffect": [Function],
|
||||
"useImperativeHandle": [Function],
|
||||
"useLayoutEffect": [Function],
|
||||
"useMemo": [Function],
|
||||
"useReducer": [Function],
|
||||
"useRef": [Function],
|
||||
"useResponder": [Function],
|
||||
"useState": [Function],
|
||||
"useTransition": [Function],
|
||||
},
|
||||
"_element": <NotificationSettings
|
||||
actions={
|
||||
Object {
|
||||
"updateMe": [MockFunction],
|
||||
}
|
||||
}
|
||||
componentId="component-id"
|
||||
currentUser={
|
||||
Object {
|
||||
"id": "current_user_id",
|
||||
}
|
||||
}
|
||||
currentUserStatus="status"
|
||||
enableAutoResponder={false}
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": "span",
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
isCollapsedThreadsEnabled={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
updateMeRequest={Object {}}
|
||||
/>,
|
||||
"_firstWorkInProgressHook": null,
|
||||
"_forcedUpdate": false,
|
||||
"_instance": [Circular],
|
||||
"_isReRender": false,
|
||||
"_newState": null,
|
||||
"_numberOfReRenders": 0,
|
||||
"_renderPhaseUpdates": null,
|
||||
"_rendered": <RNCSafeAreaView
|
||||
edges={
|
||||
Array [
|
||||
"left",
|
||||
"right",
|
||||
]
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
testID="notification_settings.screen"
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<ScrollView
|
||||
alwaysBounceVertical={false}
|
||||
contentContainerStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(61,60,64,0.06)",
|
||||
"flex": 1,
|
||||
"paddingTop": 35,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(61,60,64,0.1)",
|
||||
"height": 1,
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage="Mentions"
|
||||
i18nId="mobile.notification_settings.mentions"
|
||||
iconName="at"
|
||||
isDestructor={false}
|
||||
isLink={false}
|
||||
onPress={[Function]}
|
||||
separator={true}
|
||||
showArrow={true}
|
||||
testID="notification_settings.mentions_replies.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage="Mobile"
|
||||
i18nId="mobile.notification_settings.mobile"
|
||||
iconName="cellphone"
|
||||
isDestructor={false}
|
||||
isLink={false}
|
||||
onPress={[Function]}
|
||||
separator={true}
|
||||
showArrow={true}
|
||||
testID="notification_settings.mobile.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SettingsItem
|
||||
defaultMessage="Email"
|
||||
i18nId="mobile.notification_settings.email"
|
||||
iconName="email-outline"
|
||||
isDestructor={false}
|
||||
isLink={false}
|
||||
onPress={[Function]}
|
||||
separator={false}
|
||||
showArrow={true}
|
||||
testID="notification_settings.email.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(61,60,64,0.1)",
|
||||
"height": 1,
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</ScrollView>
|
||||
</RNCSafeAreaView>,
|
||||
"_rendering": false,
|
||||
"_updater": [Circular],
|
||||
"_workInProgressHook": null,
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux';
|
|||
|
||||
import {updateMe} from '@mm-redux/actions/users';
|
||||
import {getConfig} from '@mm-redux/selectors/entities/general';
|
||||
import {getMyPreferences, getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getMyPreferences, getTheme, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentUser, getStatusForUserId} from '@mm-redux/selectors/entities/users';
|
||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||
import {isLandscape} from '@selectors/device';
|
||||
|
|
@ -29,6 +29,7 @@ function mapStateToProps(state) {
|
|||
theme: getTheme(state),
|
||||
enableAutoResponder,
|
||||
isLandscape: isLandscape(state),
|
||||
isCollapsedThreadsEnabled: isCollapsedThreadsEnabled(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export default class NotificationSettings extends PureComponent {
|
|||
updateMeRequest: PropTypes.object.isRequired,
|
||||
currentUserStatus: PropTypes.string.isRequired,
|
||||
enableAutoResponder: PropTypes.bool.isRequired,
|
||||
isCollapsedThreadsEnabled: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
|
|
@ -89,13 +90,15 @@ export default class NotificationSettings extends PureComponent {
|
|||
};
|
||||
|
||||
goToNotificationSettingsMentions = () => {
|
||||
const {currentUser} = this.props;
|
||||
const {currentUser, isCollapsedThreadsEnabled} = this.props;
|
||||
const {intl} = this.context;
|
||||
const screen = 'NotificationSettingsMentions';
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.notification_settings.mentions_replies',
|
||||
defaultMessage: 'Mentions and Replies',
|
||||
});
|
||||
|
||||
const id = isCollapsedThreadsEnabled ? 'mobile.notification_settings.mentions' : 'mobile.notification.notification_settings.mentions_replies';
|
||||
const defaultMessage = isCollapsedThreadsEnabled ? 'Mentions' : 'Mentions and Replies';
|
||||
|
||||
const title = intl.formatMessage({id, defaultMessage});
|
||||
|
||||
const passProps = {
|
||||
currentUser,
|
||||
onBack: this.saveNotificationProps,
|
||||
|
|
@ -190,7 +193,7 @@ export default class NotificationSettings extends PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {theme, enableAutoResponder} = this.props;
|
||||
const {theme, enableAutoResponder, isCollapsedThreadsEnabled} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
const showArrow = Platform.OS === 'ios';
|
||||
|
||||
|
|
@ -210,6 +213,9 @@ export default class NotificationSettings extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
const mentionsI18nId = isCollapsedThreadsEnabled ? t('mobile.notification_settings.mentions') : t('mobile.notification_settings.mentions_replies');
|
||||
const mentionsI18nDefault = isCollapsedThreadsEnabled ? 'Mentions' : 'Mentions and Replies';
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
edges={['left', 'right']}
|
||||
|
|
@ -223,8 +229,8 @@ export default class NotificationSettings extends PureComponent {
|
|||
>
|
||||
<View style={style.divider}/>
|
||||
<SettingsItem
|
||||
defaultMessage='Mentions and Replies'
|
||||
i18nId={t('mobile.notification_settings.mentions_replies')}
|
||||
defaultMessage={mentionsI18nDefault}
|
||||
i18nId={mentionsI18nId}
|
||||
iconName='at'
|
||||
onPress={() => this.handlePress(this.goToNotificationSettingsMentions)}
|
||||
separator={true}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ describe('NotificationSettings', () => {
|
|||
updateMeRequest: {},
|
||||
currentUserStatus: 'status',
|
||||
enableAutoResponder: false,
|
||||
isCollapsedThreadsEnabled: false,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
|
|
@ -31,6 +32,17 @@ describe('NotificationSettings', () => {
|
|||
expect(wrapper.instance()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, when CRT is ON', () => {
|
||||
const wrapper = shallowWithIntl(
|
||||
<NotificationSettings
|
||||
{...baseProps}
|
||||
isCollapsedThreadsEnabled={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.instance()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should include previous notification props when saving new ones', () => {
|
||||
const wrapper = shallowWithIntl(
|
||||
<NotificationSettings {...baseProps}/>,
|
||||
|
|
|
|||
|
|
@ -147,3 +147,55 @@ exports[`NotificationSettingsEmailAndroid should match snapshot 2`] = `
|
|||
</View>
|
||||
</Modal>
|
||||
`;
|
||||
|
||||
exports[`NotificationSettingsEmailAndroid should match snapshot, renderEmailThreadsSection 1`] = `
|
||||
<View>
|
||||
<sectionItem
|
||||
action={[Function]}
|
||||
actionType="toggle"
|
||||
description={
|
||||
<InjectIntl(FormattedText)
|
||||
defaultMessage="Notify me about all replies to threads I'm following"
|
||||
id="user.settings.notifications.email_threads.description"
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<InjectIntl(FormattedText)
|
||||
defaultMessage="Thread reply notifications"
|
||||
id="user.settings.notifications.email_threads.title_android"
|
||||
/>
|
||||
}
|
||||
selected={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View />
|
||||
</View>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -135,3 +135,86 @@ exports[`NotificationSettingsEmailIos should match snapshot, renderEmailSection
|
|||
</View>
|
||||
</section>
|
||||
`;
|
||||
|
||||
exports[`NotificationSettingsEmailIos should match snapshot, renderEmailThreadsSection 1`] = `
|
||||
<section
|
||||
footerDefaultMessage="When enabled, any reply to a thread you're following will send an email notification."
|
||||
footerId="user.settings.notifications.email_threads.info"
|
||||
headerDefaultMessage="THREAD REPLY NOTIFICATIONS"
|
||||
headerId="user.settings.notifications.email_threads.title"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
>
|
||||
<sectionItem
|
||||
action={[Function]}
|
||||
actionType="toggle"
|
||||
description={<View />}
|
||||
label={
|
||||
<InjectIntl(FormattedText)
|
||||
defaultMessage="Notify me about all replies to threads I'm following"
|
||||
id="user.settings.notifications.email_threads.description"
|
||||
/>
|
||||
}
|
||||
selected={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={Object {}}
|
||||
/>
|
||||
</section>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {getConfig} from '@mm-redux/selectors/entities/general';
|
|||
import {
|
||||
get as getPreference,
|
||||
getTheme,
|
||||
isCollapsedThreadsEnabled,
|
||||
} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentUser} from '@mm-redux/selectors/entities/users';
|
||||
import {getNotificationProps} from '@utils/notify_props';
|
||||
|
|
@ -38,6 +39,7 @@ function mapStateToProps(state) {
|
|||
emailInterval,
|
||||
sendEmailNotifications,
|
||||
theme: getTheme(state),
|
||||
isCollapsedThreadsEnabled: isCollapsedThreadsEnabled(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ class NotificationSettingsEmailAndroid extends NotificationSettingsEmailBase {
|
|||
this.saveEmailNotifyProps();
|
||||
};
|
||||
|
||||
handleEmailThreadsChanged = (value) => {
|
||||
let emailThreads = 'mention';
|
||||
if (value) {
|
||||
emailThreads = 'all';
|
||||
}
|
||||
|
||||
this.setEmailThreads(emailThreads, this.saveEmailThreadsNotifyProps);
|
||||
};
|
||||
|
||||
showEmailModal = () => {
|
||||
this.setState({showEmailNotificationsModal: true});
|
||||
};
|
||||
|
|
@ -95,6 +104,34 @@ class NotificationSettingsEmailAndroid extends NotificationSettingsEmailBase {
|
|||
);
|
||||
}
|
||||
|
||||
renderEmailThreadsSection(style) {
|
||||
const {theme} = this.props;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email_threads.title_android'
|
||||
defaultMessage='Thread reply notifications'
|
||||
/>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email_threads.description'
|
||||
defaultMessage={'Notify me about all replies to threads I\'m following'}
|
||||
/>
|
||||
)}
|
||||
action={this.handleEmailThreadsChanged}
|
||||
actionType='toggle'
|
||||
selected={this.state.emailThreads === 'all'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderEmailNotificationsModal(style) {
|
||||
const {intl} = this.context;
|
||||
const {
|
||||
|
|
@ -227,7 +264,7 @@ class NotificationSettingsEmailAndroid extends NotificationSettingsEmailBase {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const {theme, isCollapsedThreadsEnabled, notifyProps} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
|
|
@ -242,6 +279,9 @@ class NotificationSettingsEmailAndroid extends NotificationSettingsEmailBase {
|
|||
>
|
||||
{this.renderEmailSection()}
|
||||
<View style={style.separator}/>
|
||||
{isCollapsedThreadsEnabled && notifyProps.email === 'true' && (
|
||||
this.renderEmailThreadsSection(style)
|
||||
)}
|
||||
{this.renderEmailNotificationsModal(style)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ describe('NotificationSettingsEmailAndroid', () => {
|
|||
currentUser: {id: 'current_user_id'},
|
||||
notifyProps: {
|
||||
email: 'true',
|
||||
email_threads: 'all',
|
||||
},
|
||||
emailInterval: '30',
|
||||
enableEmailBatching: false,
|
||||
|
|
@ -24,6 +25,7 @@ describe('NotificationSettingsEmailAndroid', () => {
|
|||
sendEmailNotifications: true,
|
||||
theme: Preferences.THEMES.default,
|
||||
componentId: 'component-id',
|
||||
isCollapsedThreadsEnabled: false,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
|
|
@ -49,6 +51,33 @@ describe('NotificationSettingsEmailAndroid', () => {
|
|||
expect(wrapper.instance().renderEmailNotificationsModal(style)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, renderEmailThreadsSection', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
isCollapsedThreadsEnabled: true,
|
||||
};
|
||||
|
||||
const wrapper = shallowWithIntl(
|
||||
<NotificationSettingsEmailAndroid {...props}/>,
|
||||
);
|
||||
|
||||
const style = {
|
||||
divider: {},
|
||||
modal: {},
|
||||
modalBody: {},
|
||||
modalTitleContainer: {},
|
||||
modalTitle: {},
|
||||
modalOptionDisabled: {},
|
||||
modalHelpText: {},
|
||||
modalFooter: {},
|
||||
modalFooterContainer: {},
|
||||
modalFooterOptionContainer: {},
|
||||
modalFooterOption: {},
|
||||
};
|
||||
|
||||
expect(wrapper.instance().renderEmailThreadsSection(style)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match state on setEmailInterval', () => {
|
||||
const wrapper = shallowWithIntl(
|
||||
<NotificationSettingsEmailAndroid {...baseProps}/>,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import NotificationSettingsEmailBase from './notification_settings_email_base';
|
||||
|
||||
class NotificationSettingsEmailIos extends NotificationSettingsEmailBase {
|
||||
handleEmailThreadsChanged = (value) => {
|
||||
let emailThreads = 'mention';
|
||||
if (value) {
|
||||
emailThreads = 'all';
|
||||
}
|
||||
|
||||
this.setEmailThreads(emailThreads, this.saveEmailThreadsNotifyProps);
|
||||
};
|
||||
|
||||
renderEmailSection() {
|
||||
const {
|
||||
enableEmailBatching,
|
||||
|
|
@ -113,8 +122,37 @@ class NotificationSettingsEmailIos extends NotificationSettingsEmailBase {
|
|||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
renderEmailThreadsSection(style) {
|
||||
const {theme} = this.props;
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId={t('user.settings.notifications.email_threads.title')}
|
||||
headerDefaultMessage='THREAD REPLY NOTIFICATIONS'
|
||||
footerId={t('user.settings.notifications.email_threads.info')}
|
||||
footerDefaultMessage={'When enabled, any reply to a thread you\'re following will send an email notification.'}
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='user.settings.notifications.email_threads.description'
|
||||
defaultMessage={'Notify me about all replies to threads I\'m following'}
|
||||
/>
|
||||
)}
|
||||
description={<View/>}
|
||||
action={this.handleEmailThreadsChanged}
|
||||
actionType='toggle'
|
||||
selected={this.state.emailThreads === 'all'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {theme, isCollapsedThreadsEnabled, notifyProps} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
|
|
@ -130,6 +168,9 @@ class NotificationSettingsEmailIos extends NotificationSettingsEmailBase {
|
|||
alwaysBounceVertical={false}
|
||||
>
|
||||
{this.renderEmailSection()}
|
||||
{isCollapsedThreadsEnabled && notifyProps.email === 'true' && (
|
||||
this.renderEmailThreadsSection(style)
|
||||
)}
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ describe('NotificationSettingsEmailIos', () => {
|
|||
currentUser: {id: 'current_user_id'},
|
||||
notifyProps: {
|
||||
email: 'true',
|
||||
email_threads: 'all',
|
||||
},
|
||||
emailInterval: '30',
|
||||
enableEmailBatching: false,
|
||||
|
|
@ -32,6 +33,7 @@ describe('NotificationSettingsEmailIos', () => {
|
|||
sendEmailNotifications: true,
|
||||
theme: Preferences.THEMES.default,
|
||||
componentId: 'component-id',
|
||||
isCollapsedThreadsEnabled: false,
|
||||
};
|
||||
|
||||
test('should match snapshot, renderEmailSection', () => {
|
||||
|
|
@ -42,6 +44,18 @@ describe('NotificationSettingsEmailIos', () => {
|
|||
expect(wrapper.instance().renderEmailSection()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, renderEmailThreadsSection', () => {
|
||||
const style = {
|
||||
separator: {},
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<NotificationSettingsEmailIos {...{...baseProps, isCollapsedThreadsEnabled: true}}/>,
|
||||
);
|
||||
|
||||
expect(wrapper.instance().renderEmailThreadsSection(style)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should save preference on back button only if email interval has changed', () => {
|
||||
const wrapper = shallow(
|
||||
<NotificationSettingsEmailIos {...baseProps}/>,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export default class NotificationSettingsEmailBase extends PureComponent {
|
|||
emailInterval,
|
||||
newInterval: this.computeEmailInterval(notifyProps?.email === 'true' && sendEmailNotifications, enableEmailBatching, emailInterval),
|
||||
showEmailNotificationsModal: false,
|
||||
emailThreads: notifyProps.email_threads,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +84,16 @@ export default class NotificationSettingsEmailBase extends PureComponent {
|
|||
this.setState({newInterval: value});
|
||||
};
|
||||
|
||||
setEmailThreads = (value, callback) => {
|
||||
this.setState({emailThreads: value}, callback);
|
||||
};
|
||||
|
||||
saveEmailThreadsNotifyProps = () => {
|
||||
const {emailThreads} = this.state;
|
||||
const {actions, notifyProps} = this.props;
|
||||
actions.updateMe({notify_props: {...notifyProps, email_threads: emailThreads}});
|
||||
}
|
||||
|
||||
saveEmailNotifyProps = () => {
|
||||
const {emailInterval, newInterval} = this.state;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getTheme, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
|
||||
import NotificationSettingsMentions from './notification_settings_mentions';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
isCollapsedThreadsEnabled: isCollapsedThreadsEnabled(state),
|
||||
theme: getTheme(state),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,8 +360,12 @@ class NotificationSettingsMentionsAndroid extends NotificationSettingsMentionsBa
|
|||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
{this.renderReplySection()}
|
||||
<View style={style.separator}/>
|
||||
{!this.props.isCollapsedThreadsEnabled && (
|
||||
<>
|
||||
{this.renderReplySection()}
|
||||
<View style={style.separator}/>
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
{this.renderKeywordsModal(style)}
|
||||
{this.renderReplyModal(style)}
|
||||
|
|
|
|||
|
|
@ -200,7 +200,9 @@ class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase {
|
|||
alwaysBounceVertical={false}
|
||||
>
|
||||
{this.renderMentionSection(style)}
|
||||
{this.renderReplySection(style)}
|
||||
{!this.props.isCollapsedThreadsEnabled && (
|
||||
this.renderReplySection(style)
|
||||
)}
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux';
|
|||
|
||||
import {updateMe} from '@mm-redux/actions/users';
|
||||
import {getConfig} from '@mm-redux/selectors/entities/general';
|
||||
import {getTeammateNameDisplaySetting, getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getTeammateNameDisplaySetting, getTheme, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentUser} from '@mm-redux/selectors/entities/users';
|
||||
|
||||
import NotificationSettingsMobile from './notification_settings_mobile';
|
||||
|
|
@ -23,6 +23,7 @@ function mapStateToProps(state) {
|
|||
theme,
|
||||
updateMeRequest,
|
||||
currentUser,
|
||||
isCollapsedThreadsEnabled: isCollapsedThreadsEnabled(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
|||
this.setState({newPush: value});
|
||||
};
|
||||
|
||||
onMobilePushThreadChanged = (value) => {
|
||||
let pushThreads = 'mention';
|
||||
if (value) {
|
||||
pushThreads = 'all';
|
||||
}
|
||||
|
||||
this.setMobilePushThreads(pushThreads, this.saveNotificationProps);
|
||||
};
|
||||
|
||||
onMobilePushStatusChanged = (value) => {
|
||||
this.setState({newPushStatus: value});
|
||||
};
|
||||
|
|
@ -611,6 +620,34 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
|||
);
|
||||
}
|
||||
|
||||
renderMobilePushThreadsSection(style) {
|
||||
const {theme} = this.props;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.push_threads.title_android'
|
||||
defaultMessage='Thread reply notifications'
|
||||
/>
|
||||
)}
|
||||
description={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.push_threads.description'
|
||||
defaultMessage={'Notify me about all replies to threads I\'m following'}
|
||||
/>
|
||||
)}
|
||||
action={this.onMobilePushThreadChanged}
|
||||
actionType='toggle'
|
||||
selected={this.state.push_threads === 'all'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderNotificationOptions(style) {
|
||||
if (Platform.Version >= 26) {
|
||||
return null;
|
||||
|
|
@ -645,6 +682,7 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
|||
mention_keys: mentionKeys,
|
||||
push,
|
||||
push_status: pushStatus,
|
||||
push_threads: pushThreads,
|
||||
} = this.state;
|
||||
|
||||
const {currentUser} = this.props;
|
||||
|
|
@ -658,6 +696,7 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
|||
mention_keys: mentionKeys,
|
||||
push,
|
||||
push_status: pushStatus,
|
||||
push_threads: pushThreads,
|
||||
user_id: currentUser.id,
|
||||
};
|
||||
|
||||
|
|
@ -737,7 +776,7 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const {theme, isCollapsedThreadsEnabled} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
|
|
@ -753,6 +792,9 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
|
|||
>
|
||||
{this.renderMobilePushSection()}
|
||||
<View style={style.separator}/>
|
||||
{isCollapsedThreadsEnabled && this.state.push === 'mention' && (
|
||||
this.renderMobilePushThreadsSection(style)
|
||||
)}
|
||||
{this.renderMobilePushStatusSection(style)}
|
||||
{this.renderNotificationOptions(style)}
|
||||
{this.renderMobileTestSection(style)}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import NotificationSettingsMobileBase from './notification_settings_mobile_base';
|
||||
|
||||
class NotificationSettingsMobileIos extends NotificationSettingsMobileBase {
|
||||
onMobilePushThreadChanged = (value) => {
|
||||
let pushThreads = 'mention';
|
||||
if (value) {
|
||||
pushThreads = 'all';
|
||||
}
|
||||
|
||||
this.setMobilePushThreads(pushThreads, this.saveNotificationProps);
|
||||
};
|
||||
|
||||
renderMobilePushSection(style) {
|
||||
const {config, theme} = this.props;
|
||||
|
||||
|
|
@ -88,6 +97,35 @@ class NotificationSettingsMobileIos extends NotificationSettingsMobileBase {
|
|||
);
|
||||
}
|
||||
|
||||
renderMobilePushThreadsSection(style) {
|
||||
const {theme} = this.props;
|
||||
|
||||
return (
|
||||
<Section
|
||||
headerId={t('mobile.notification_settings.push_threads.title')}
|
||||
headerDefaultMessage='THREAD REPLY NOTIFICATIONS'
|
||||
footerId={t('mobile.notification_settings.push_threads.info')}
|
||||
footerDefaultMessage={'When enabled, any reply to a thread you\'re following will send a mobile push notification'}
|
||||
theme={theme}
|
||||
>
|
||||
<SectionItem
|
||||
label={(
|
||||
<FormattedText
|
||||
id='mobile.notification_settings.push_threads.description'
|
||||
defaultMessage={'Notify me about all replies to threads I\'m following'}
|
||||
/>
|
||||
)}
|
||||
description={<View/>}
|
||||
action={this.onMobilePushThreadChanged}
|
||||
actionType='toggle'
|
||||
selected={this.state.push_threads === 'all'}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={style.separator}/>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
renderMobilePushStatusSection(style) {
|
||||
const {config, theme} = this.props;
|
||||
|
||||
|
|
@ -148,7 +186,7 @@ class NotificationSettingsMobileIos extends NotificationSettingsMobileBase {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const {theme, isCollapsedThreadsEnabled} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
|
|
@ -167,6 +205,9 @@ class NotificationSettingsMobileIos extends NotificationSettingsMobileBase {
|
|||
alwaysBounceVertical={false}
|
||||
>
|
||||
{this.renderMobilePushSection(style)}
|
||||
{isCollapsedThreadsEnabled && this.state.push === 'mention' && (
|
||||
this.renderMobilePushThreadsSection(style)
|
||||
)}
|
||||
{this.renderMobilePushStatusSection(style)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ export default class NotificationSettingsMobileBase extends PureComponent {
|
|||
this.setState({push}, callback);
|
||||
};
|
||||
|
||||
setMobilePushThreads = (value, callback) => {
|
||||
this.setState({push_threads: value}, callback);
|
||||
};
|
||||
|
||||
setMobilePushStatus = (value, callback) => {
|
||||
this.setState({push_status: value}, callback);
|
||||
};
|
||||
|
|
@ -100,6 +104,7 @@ export default class NotificationSettingsMobileBase extends PureComponent {
|
|||
mention_keys: mentionKeys,
|
||||
push,
|
||||
push_status: pushStatus,
|
||||
push_threads: pushThreads,
|
||||
} = this.state;
|
||||
|
||||
this.props.onBack({
|
||||
|
|
@ -111,6 +116,7 @@ export default class NotificationSettingsMobileBase extends PureComponent {
|
|||
mention_keys: mentionKeys,
|
||||
push,
|
||||
push_status: pushStatus,
|
||||
push_threads: pushThreads,
|
||||
user_id: this.props.currentUser.id,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,9 +12,11 @@ export function getNotificationProps(user) {
|
|||
desktop: 'all',
|
||||
desktop_sound: 'true',
|
||||
email: 'true',
|
||||
email_threads: 'all',
|
||||
mention_keys: user ? `${user.username},@${user.username}` : '',
|
||||
push: 'mention',
|
||||
push_status: 'online',
|
||||
push_threads: 'all',
|
||||
};
|
||||
|
||||
if (!user || !user.first_name) {
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@
|
|||
"mobile.notification_settings.email": "Email",
|
||||
"mobile.notification_settings.email_title": "Email Notifications",
|
||||
"mobile.notification_settings.email.send": "SEND EMAIL NOTIFICATIONS",
|
||||
"mobile.notification_settings.mentions": "Mentions",
|
||||
"mobile.notification_settings.mentions_replies": "Mentions and Replies",
|
||||
"mobile.notification_settings.mentions.channelWide": "Channel-wide mentions",
|
||||
"mobile.notification_settings.mentions.reply_title": "Send Reply notifications for",
|
||||
|
|
@ -438,6 +439,10 @@
|
|||
"mobile.notification_settings.modal_cancel": "CANCEL",
|
||||
"mobile.notification_settings.modal_save": "SAVE",
|
||||
"mobile.notification_settings.ooo_auto_responder": "Automatic Direct Message Replies",
|
||||
"mobile.notification_settings.push_threads.description": "Notify me about all replies to threads I'm following",
|
||||
"mobile.notification_settings.push_threads.info": "When enabled, any reply to a thread you're following will send a mobile push notification.",
|
||||
"mobile.notification_settings.push_threads.title": "THREAD REPLY NOTIFICATIONS",
|
||||
"mobile.notification_settings.push_threads.title_android": "Thread reply notifications",
|
||||
"mobile.notification_settings.save_failed_description": "The notification settings failed to save due to a connection issue. Please try again.",
|
||||
"mobile.notification_settings.save_failed_title": "Connection issue",
|
||||
"mobile.oauth.failed_to_login": "Your login attempt failed. Please try again.",
|
||||
|
|
@ -739,6 +744,10 @@
|
|||
"user.settings.modal.notifications": "Notifications",
|
||||
"user.settings.notifications.allActivity": "For all activity",
|
||||
"user.settings.notifications.comments": "Reply notifications",
|
||||
"user.settings.notifications.email_threads.description": "Notify me about all replies to threads I'm following.",
|
||||
"user.settings.notifications.email_threads.info": "When enabled, any reply to a thread you're following will send an email notification.",
|
||||
"user.settings.notifications.email_threads.title": "THREAD REPLY NOTIFICATIONS",
|
||||
"user.settings.notifications.email_threads.title_android": "Thread reply notifications",
|
||||
"user.settings.notifications.email.disabled": "Email notifications are not enabled",
|
||||
"user.settings.notifications.email.everyHour": "Every hour",
|
||||
"user.settings.notifications.email.immediately": "Immediately",
|
||||
|
|
|
|||
Loading…
Reference in a new issue