From e4a0a4b4ec60375830096677c6163bb00ea11867 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Thu, 5 Jul 2018 01:17:59 +0800 Subject: [PATCH] [MM-10215] Fix saving of email notification props when email batching is enabled (#1654) * fix saving of email notification props when email batching is enabled * fix email notification settings for android * make screen for Android email notification * update per comment * leave as is "...email.everyXMinutes", and fix prop type error on emailInterval * add "mobile." prefix to newly introduced translation ID --- .../radio_button/radio_button_group.js | 42 ++- .../notification_settings.js | 291 ++-------------- ...cation_settings_email.android.test.js.snap | 128 +++++++ ...tification_settings_email.ios.test.js.snap | 71 ++++ .../notification_settings_email/index.js | 36 +- .../notification_settings_email.android.js | 321 ++++++++++++++++++ ...otification_settings_email.android.test.js | 102 ++++++ ....js => notification_settings_email.ios.js} | 141 ++------ .../notification_settings_email.ios.test.js | 89 +++++ .../notification_settings_email_base.js | 99 ++++++ .../notification_settings_mentions.android.js | 4 +- assets/base/i18n/en.json | 1 + 12 files changed, 909 insertions(+), 416 deletions(-) create mode 100644 app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.android.test.js.snap create mode 100644 app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.ios.test.js.snap create mode 100644 app/screens/settings/notification_settings_email/notification_settings_email.android.js create mode 100644 app/screens/settings/notification_settings_email/notification_settings_email.android.test.js rename app/screens/settings/notification_settings_email/{notification_settings_email.js => notification_settings_email.ios.js} (53%) create mode 100644 app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js create mode 100644 app/screens/settings/notification_settings_email/notification_settings_email_base.js diff --git a/app/components/radio_button/radio_button_group.js b/app/components/radio_button/radio_button_group.js index 92728608b..b7dd3bda2 100644 --- a/app/components/radio_button/radio_button_group.js +++ b/app/components/radio_button/radio_button_group.js @@ -17,34 +17,30 @@ export default class RadioButtonGroup extends PureComponent { options: [] }; - state = {}; - constructor(props) { super(props); - this.selected = null; - if (this.props.options.length) { - this.props.options.forEach((option) => { - const { - value, - checked, - } = option; + this.state = { + selected: this.getSelectedValue(props.options), + }; + } - if (!this.state.selected && checked) { - this.selected = value; - } - }); + componentWillReceiveProps(nextProps) { + if (this.props.options !== nextProps.options) { + this.setState({selected: this.getSelectedValue(nextProps.options)}); + } + } + + getSelectedValue = (options = []) => { + let selected; + for (const option in options) { + if (option.checked) { + selected = option.value; + break; + } } - this.state = {selected: this.selected}; - } - - get value() { - return this.state.selected; - } - - set value(value) { - this.onChange(value); + return selected; } onChange = (value) => { @@ -81,7 +77,7 @@ export default class RadioButtonGroup extends PureComponent { label={label} disabled={disabled} onCheck={this.onChange} - checked={this.state.selected && value === this.state.selected} + checked={option.checked} /> ); }); diff --git a/app/screens/settings/notification_settings/notification_settings.js b/app/screens/settings/notification_settings/notification_settings.js index 4b3a5ad90..fe0c5c634 100644 --- a/app/screens/settings/notification_settings/notification_settings.js +++ b/app/screens/settings/notification_settings/notification_settings.js @@ -6,19 +6,14 @@ import PropTypes from 'prop-types'; import {injectIntl, intlShape} from 'react-intl'; import { Alert, - Modal, Platform, ScrollView, - TouchableOpacity, View, } from 'react-native'; import deepEqual from 'deep-equal'; -import {General, Preferences, RequestStatus} from 'mattermost-redux/constants'; -import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils'; +import {General, RequestStatus} from 'mattermost-redux/constants'; -import FormattedText from 'app/components/formatted_text'; -import RadioButtonGroup from 'app/components/radio_button'; import StatusBar from 'app/components/status_bar'; import NotificationPreferences from 'app/notification_preferences'; import SettingsItem from 'app/screens/settings/settings_item'; @@ -31,10 +26,8 @@ class NotificationSettings extends PureComponent { actions: PropTypes.shape({ handleUpdateUserNotifyProps: PropTypes.func.isRequired, }), - config: PropTypes.object.isRequired, currentUser: PropTypes.object.isRequired, intl: intlShape.isRequired, - myPreferences: PropTypes.object.isRequired, navigator: PropTypes.object, theme: PropTypes.object.isRequired, updateMeRequest: PropTypes.object.isRequired, @@ -42,10 +35,6 @@ class NotificationSettings extends PureComponent { enableAutoResponder: PropTypes.bool.isRequired, }; - state = { - showEmailNotificationsModal: false, - }; - componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { setNavigatorStyles(this.props.navigator, nextProps.theme); @@ -94,30 +83,25 @@ class NotificationSettings extends PureComponent { }; goToNotificationSettingsEmail = () => { - if (Platform.OS === 'ios') { - const {currentUser, intl, navigator, theme} = this.props; - navigator.push({ - backButtonTitle: '', - screen: 'NotificationSettingsEmail', - title: intl.formatMessage({ - id: 'mobile.notification_settings.email_title', - defaultMessage: 'Email Notifications', - }), - animated: true, - navigatorStyle: { - navBarTextColor: theme.sidebarHeaderTextColor, - navBarBackgroundColor: theme.sidebarHeaderBg, - navBarButtonColor: theme.sidebarHeaderTextColor, - screenBackgroundColor: theme.centerChannelBg, - }, - passProps: { - currentUser, - onBack: this.saveNotificationProps, - }, - }); - } else { - this.setState({showEmailNotificationsModal: true}); - } + const {currentUser, intl, navigator, theme} = this.props; + navigator.push({ + backButtonTitle: '', + screen: 'NotificationSettingsEmail', + title: intl.formatMessage({ + id: 'mobile.notification_settings.email_title', + defaultMessage: 'Email Notifications', + }), + animated: true, + navigatorStyle: { + navBarTextColor: theme.sidebarHeaderTextColor, + navBarBackgroundColor: theme.sidebarHeaderBg, + navBarButtonColor: theme.sidebarHeaderTextColor, + screenBackgroundColor: theme.centerChannelBg, + }, + passProps: { + currentUserId: currentUser.id, + }, + }); }; goToNotificationSettingsMentions = () => { @@ -168,34 +152,6 @@ class NotificationSettings extends PureComponent { }); }; - setEmailNotifications = (emailSetting) => { - this.setState({emailSetting}); - }; - - saveEmailNotifications = () => { - const {config, currentUser} = this.props; - const {emailSetting} = this.state; - - this.setState({showEmailNotificationsModal: false}); - - if (emailSetting) { - let email = emailSetting; - let interval; - - const emailBatchingEnabled = config.EnableEmailBatching === 'true'; - if (emailBatchingEnabled && emailSetting !== 'false') { - interval = emailSetting; - email = 'true'; - } - - this.saveNotificationProps({ - ...getNotificationProps(currentUser), - email, - interval, - }); - } - }; - saveNotificationProps = (notifyProps) => { const {currentUser} = this.props; const {user_id: userId} = notifyProps; @@ -204,10 +160,6 @@ class NotificationSettings extends PureComponent { user_id: userId, }; - if (notifyProps.interval) { - previousProps.interval = notifyProps.interval; - } - if (!deepEqual(previousProps, notifyProps)) { this.props.actions.handleUpdateUserNotifyProps(notifyProps); } @@ -247,154 +199,6 @@ class NotificationSettings extends PureComponent { } }; - renderEmailNotificationSettings = (style) => { - if (Platform.OS === 'ios') { - return null; - } - - const {config, currentUser, intl, myPreferences} = this.props; - const notifyProps = getNotificationProps(currentUser); - const sendEmailNotifications = config.SendEmailNotifications === 'true'; - const emailBatchingEnabled = sendEmailNotifications && config.EnableEmailBatching === 'true'; - - const never = notifyProps.email === 'false'; - let sendImmediatley = notifyProps.email === 'true'; - let sendImmediatleyValue = 'true'; - let fifteenMinutes; - let hourly; - let interval; - - if (emailBatchingEnabled) { - const emailPreferences = getPreferencesByCategory(myPreferences, Preferences.CATEGORY_NOTIFICATIONS); - if (emailPreferences.size) { - interval = emailPreferences.get(Preferences.EMAIL_INTERVAL).value; - } - } - - if (emailBatchingEnabled && notifyProps.email !== 'false') { - sendImmediatley = interval === Preferences.INTERVAL_IMMEDIATE.toString(); - fifteenMinutes = interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString(); - hourly = interval === Preferences.INTERVAL_HOUR.toString(); - sendImmediatleyValue = Preferences.INTERVAL_IMMEDIATE.toString(); - } - - let helpText; - if (sendEmailNotifications) { - helpText = ( - - ); - } - - const emailOptions = [{ - label: intl.formatMessage({ - id: 'user.settings.notifications.email.immediately', - defaultMessage: 'Immediately', - }), - value: sendImmediatleyValue, - checked: sendImmediatley, - }]; - - if (emailBatchingEnabled) { - emailOptions.push({ - label: intl.formatMessage({ - id: 'user.settings.notifications.email.everyXMinutes', - defaultMessage: 'Every {count, plural, one {minute} other {{count, number} minutes}}', - }, {count: Preferences.INTERVAL_FIFTEEN_MINUTES / 60}), - value: Preferences.INTERVAL_FIFTEEN_MINUTES.toString(), - checked: fifteenMinutes, - }, { - label: intl.formatMessage({ - id: 'user.settings.notifications.email.everyHour', - defaultMessage: 'Every hour', - }), - value: Preferences.INTERVAL_HOUR.toString(), - checked: hourly, - }); - } - - emailOptions.push({ - label: intl.formatMessage({ - id: 'user.settings.notifications.email.never', - defaultMessage: 'Never', - }), - value: 'false', - checked: never, - }); - - return ( - this.setState({showEmailNotificationsModal: false})} - > - - - - - - - {!sendEmailNotifications && - - } - {sendEmailNotifications && - - } - {helpText} - - - - - - - - {sendEmailNotifications && - - - - - - - } - - - - - - ); - }; - render() { const {theme, enableAutoResponder} = this.props; const style = getStyleSheet(theme); @@ -458,7 +262,6 @@ class NotificationSettings extends PureComponent { {autoResponder} - {this.renderEmailNotificationSettings(style)} ); } @@ -483,60 +286,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { height: 1, width: '100%', }, - modalOverlay: { - backgroundColor: changeOpacity('#000000', 0.6), - alignItems: 'center', - flex: 1, - }, - modal: { - backgroundColor: theme.centerChannelBg, - borderRadius: 4, - marginTop: 20, - width: '95%', - }, - modalBody: { - paddingHorizontal: 24, - }, - modalTitleContainer: { - marginBottom: 30, - marginTop: 20, - }, - modalTitle: { - color: theme.centerChannelColor, - fontSize: 19, - }, - modalOptionDisabled: { - color: changeOpacity(theme.centerChannelColor, 0.5), - fontSize: 17, - }, - modalHelpText: { - color: changeOpacity(theme.centerChannelColor, 0.5), - fontSize: 13, - marginTop: 20, - }, - modalFooter: { - alignItems: 'flex-end', - height: 58, - marginTop: 40, - width: '100%', - }, - modalFooterContainer: { - alignItems: 'center', - flex: 1, - flexDirection: 'row', - paddingRight: 24, - }, - modalFooterOptionContainer: { - alignItems: 'center', - height: 40, - justifyContent: 'center', - paddingHorizontal: 10, - paddingVertical: 5, - }, - modalFooterOption: { - color: theme.linkColor, - fontSize: 14, - }, }; }); diff --git a/app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.android.test.js.snap b/app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.android.test.js.snap new file mode 100644 index 000000000..5dd411882 --- /dev/null +++ b/app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.android.test.js.snap @@ -0,0 +1,128 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NotificationSettingsMobileAndroid should match snapshot 1`] = ` + + } + label={ + + } + theme={ + Object { + "centerChannelBg": "#aaa", + "centerChannelColor": "#aaa", + } + } +/> +`; + +exports[`NotificationSettingsMobileAndroid should match snapshot 2`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; diff --git a/app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.ios.test.js.snap b/app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.ios.test.js.snap new file mode 100644 index 000000000..2f1513e61 --- /dev/null +++ b/app/screens/settings/notification_settings_email/__snapshots__/notification_settings_email.ios.test.js.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NotificationSettingsEmailIos should match snapshot, renderEmailSection 1`] = ` +
+ + + } + selected={true} + theme={ + Object { + "centerChannelBg": "#aaa", + "centerChannelColor": "#aaa", + } + } + /> + + + } + selected={false} + theme={ + Object { + "centerChannelBg": "#aaa", + "centerChannelColor": "#aaa", + } + } + /> + +
+`; diff --git a/app/screens/settings/notification_settings_email/index.js b/app/screens/settings/notification_settings_email/index.js index 6d50d6103..9a6a58b42 100644 --- a/app/screens/settings/notification_settings_email/index.js +++ b/app/screens/settings/notification_settings_email/index.js @@ -1,19 +1,47 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; +import {Preferences} from 'mattermost-redux/constants'; + +import {savePreferences} from 'mattermost-redux/actions/preferences'; + import {getConfig} from 'mattermost-redux/selectors/entities/general'; -import {getMyPreferences, getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import { + get as getPreference, + getTheme, +} from 'mattermost-redux/selectors/entities/preferences'; import NotificationSettingsEmail from './notification_settings_email'; function mapStateToProps(state) { + const config = getConfig(state); + const sendEmailNotifications = config.SendEmailNotifications === 'true'; + const enableEmailBatching = config.EnableEmailBatching === 'true'; + const emailInterval = getPreference( + state, + Preferences.CATEGORY_NOTIFICATIONS, + Preferences.EMAIL_INTERVAL, + Preferences.INTERVAL_NEVER + ).toString(); + return { - config: getConfig(state), - myPreferences: getMyPreferences(state), + enableEmailBatching, + emailInterval, + sendEmailNotifications, + siteName: config.siteName || '', theme: getTheme(state), }; } -export default connect(mapStateToProps)(NotificationSettingsEmail); +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + savePreferences, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(NotificationSettingsEmail); diff --git a/app/screens/settings/notification_settings_email/notification_settings_email.android.js b/app/screens/settings/notification_settings_email/notification_settings_email.android.js new file mode 100644 index 000000000..3c30d77a3 --- /dev/null +++ b/app/screens/settings/notification_settings_email/notification_settings_email.android.js @@ -0,0 +1,321 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {intlShape} from 'react-intl'; +import { + Modal, + ScrollView, + TouchableOpacity, + View, +} from 'react-native'; + +import {Preferences} from 'mattermost-redux/constants'; + +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; + +import FormattedText from 'app/components/formatted_text'; +import RadioButtonGroup from 'app/components/radio_button'; +import SectionItem from 'app/screens/settings/section_item'; + +import NotificationSettingsEmailBase from './notification_settings_email_base'; + +class NotificationSettingsEmailAndroid extends NotificationSettingsEmailBase { + static contextTypes = { + intl: intlShape, + }; + + handleClose = () => { + this.setState({showEmailNotificationsModal: false}); + } + + handleSaveEmailNotification = () => { + this.saveEmailNotifyProps(); + this.handleClose(); + }; + + showEmailModal = () => { + this.setState({showEmailNotificationsModal: true}); + }; + + renderEmailSection() { + const { + sendEmailNotifications, + theme, + } = this.props; + const {interval} = this.state; + let i18nId; + let i18nMessage; + if (sendEmailNotifications) { + switch (interval) { + case Preferences.INTERVAL_IMMEDIATE.toString(): + i18nId = 'user.settings.notifications.email.immediately'; + i18nMessage = 'Immediately'; + break; + case Preferences.INTERVAL_HOUR.toString(): + i18nId = 'user.settings.notifications.email.everyHour'; + i18nMessage = 'Every hour'; + break; + case Preferences.INTERVAL_FIFTEEN_MINUTES.toString(): + i18nId = 'mobile.user.settings.notifications.email.fifteenMinutes'; + i18nMessage = 'Every 15 minutes'; + break; + case Preferences.INTERVAL_NEVER.toString(): + default: + i18nId = 'user.settings.notifications.email.never'; + i18nMessage = 'Never'; + break; + } + } else { + i18nId = 'user.settings.notifications.email.disabled'; + i18nMessage = 'Email notifications are not enabled'; + } + + return ( + + )} + label={( + + )} + action={this.showEmailModal} + actionType='default' + theme={theme} + /> + ); + } + + renderEmailNotificationsModal(style) { + const {intl} = this.context; + const { + enableEmailBatching, + sendEmailNotifications, + siteName, + } = this.props; + const {interval} = this.state; + + let helpText; + if (sendEmailNotifications) { + helpText = ( + + ); + } + + const emailOptions = [{ + label: intl.formatMessage({ + id: 'user.settings.notifications.email.immediately', + defaultMessage: 'Immediately', + }), + value: Preferences.INTERVAL_IMMEDIATE.toString(), + checked: interval === Preferences.INTERVAL_IMMEDIATE.toString(), + }]; + + if (enableEmailBatching) { + emailOptions.push({ + label: intl.formatMessage({ + id: 'mobile.user.settings.notifications.email.fifteenMinutes', + defaultMessage: 'Every 15 minutes', + }), + value: Preferences.INTERVAL_FIFTEEN_MINUTES.toString(), + checked: interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString(), + }, { + label: intl.formatMessage({ + id: 'user.settings.notifications.email.everyHour', + defaultMessage: 'Every hour', + }), + value: Preferences.INTERVAL_HOUR.toString(), + checked: interval === Preferences.INTERVAL_HOUR.toString(), + }); + } + + emailOptions.push({ + label: intl.formatMessage({ + id: 'user.settings.notifications.email.never', + defaultMessage: 'Never', + }), + value: Preferences.INTERVAL_NEVER.toString(), + checked: interval === Preferences.INTERVAL_NEVER.toString(), + }); + + return ( + + + + + + + + {!sendEmailNotifications && + + } + {sendEmailNotifications && + + } + {helpText} + + + + + + + + {sendEmailNotifications && + + + + + + + } + + + + + + ); + } + + render() { + const {theme} = this.props; + const style = getStyleSheet(theme); + + return ( + + + {this.renderEmailSection()} + + {this.renderEmailNotificationsModal(style)} + + + ); + } +} + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + flex: 1, + backgroundColor: theme.centerChannelBg, + }, + separator: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), + height: 1, + width: '100%', + }, + divider: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), + height: 1, + width: '100%', + }, + scrollView: { + flex: 1, + backgroundColor: changeOpacity(theme.centerChannelColor, 0.06), + }, + scrollViewContent: { + paddingVertical: 0, + }, + modalOverlay: { + backgroundColor: changeOpacity('#000000', 0.6), + alignItems: 'center', + flex: 1, + }, + modal: { + backgroundColor: theme.centerChannelBg, + borderRadius: 4, + marginTop: 20, + width: '95%', + }, + modalBody: { + paddingHorizontal: 24, + }, + modalTitleContainer: { + marginBottom: 30, + marginTop: 20, + }, + modalTitle: { + color: theme.centerChannelColor, + fontSize: 19, + }, + modalOptionDisabled: { + color: changeOpacity(theme.centerChannelColor, 0.5), + fontSize: 17, + }, + modalHelpText: { + color: changeOpacity(theme.centerChannelColor, 0.5), + fontSize: 13, + marginTop: 20, + }, + modalFooter: { + alignItems: 'flex-end', + height: 58, + marginTop: 40, + width: '100%', + }, + modalFooterContainer: { + alignItems: 'center', + flex: 1, + flexDirection: 'row', + paddingRight: 24, + }, + modalFooterOptionContainer: { + alignItems: 'center', + height: 40, + justifyContent: 'center', + paddingHorizontal: 10, + paddingVertical: 5, + }, + modalFooterOption: { + color: theme.linkColor, + fontSize: 14, + }, + }; +}); + +export default NotificationSettingsEmailAndroid; diff --git a/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js b/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js new file mode 100644 index 000000000..062f7ac24 --- /dev/null +++ b/app/screens/settings/notification_settings_email/notification_settings_email.android.test.js @@ -0,0 +1,102 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {configure} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +configure({adapter: new Adapter()}); + +import {shallowWithIntl} from 'test/intl-test-helper'; + +import NotificationSettingsMobileAndroid from './notification_settings_email.android.js'; + +describe('NotificationSettingsMobileAndroid', () => { + const baseProps = { + currentUserId: 'current_user_id', + emailInterval: '30', + enableEmailBatching: false, + navigator: {setOnNavigatorEvent: () => {}}, // eslint-disable-line no-empty-function + actions: { + savePreferences: () => {}, // eslint-disable-line no-empty-function + }, + sendEmailNotifications: true, + siteName: 'Mattermost', + theme: { + centerChannelBg: '#aaa', + centerChannelColor: '#aaa', + }, + }; + + test('should match snapshot', () => { + const wrapper = shallowWithIntl( + + ); + + const style = { + divider: {}, + modal: {}, + modalBody: {}, + modalTitleContainer: {}, + modalTitle: {}, + modalOptionDisabled: {}, + modalHelpText: {}, + modalFooter: {}, + modalFooterContainer: {}, + modalFooterOptionContainer: {}, + modalFooterOption: {}, + }; + + expect(wrapper.instance().renderEmailSection()).toMatchSnapshot(); + expect(wrapper.instance().renderEmailNotificationsModal(style)).toMatchSnapshot(); + }); + + test('should match state on setEmailNotifications', () => { + const wrapper = shallowWithIntl( + + ); + + wrapper.setState({email: 'false', interval: '0'}); + wrapper.instance().setEmailNotifications('30'); + expect(wrapper.state({email: 'true', interval: '30'})); + + wrapper.instance().setEmailNotifications('0'); + expect(wrapper.state({email: 'false', interval: '0'})); + + wrapper.instance().setEmailNotifications('3600'); + expect(wrapper.state({email: 'true', interval: '3600'})); + }); + + test('should match state on handleClose', () => { + const wrapper = shallowWithIntl( + + ); + + wrapper.setState({showEmailNotificationsModal: true}); + wrapper.instance().handleClose(); + expect(wrapper.state('showEmailNotificationsModal')).toEqual(false); + }); + + test('should saveEmailNotifyProps and handleClose on handleSaveEmailNotification', () => { + const wrapper = shallowWithIntl( + + ); + + const instance = wrapper.instance(); + instance.saveEmailNotifyProps = jest.fn(); + instance.handleClose = jest.fn(); + + instance.handleSaveEmailNotification(); + expect(instance.saveEmailNotifyProps).toHaveBeenCalledTimes(1); + expect(instance.handleClose).toHaveBeenCalledTimes(1); + }); + + test('should match state on showEmailModal', () => { + const wrapper = shallowWithIntl( + + ); + + wrapper.setState({showEmailNotificationsModal: false}); + wrapper.instance().showEmailModal(); + expect(wrapper.state('showEmailNotificationsModal')).toEqual(true); + }); +}); diff --git a/app/screens/settings/notification_settings_email/notification_settings_email.js b/app/screens/settings/notification_settings_email/notification_settings_email.ios.js similarity index 53% rename from app/screens/settings/notification_settings_email/notification_settings_email.js rename to app/screens/settings/notification_settings_email/notification_settings_email.ios.js index e29778335..dccb96804 100644 --- a/app/screens/settings/notification_settings_email/notification_settings_email.js +++ b/app/screens/settings/notification_settings_email/notification_settings_email.ios.js @@ -1,8 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {PureComponent} from 'react'; -import PropTypes from 'prop-types'; +import React from 'react'; import { ScrollView, @@ -10,120 +9,34 @@ import { } from 'react-native'; import {Preferences} from 'mattermost-redux/constants'; -import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils'; import FormattedText from 'app/components/formatted_text'; import StatusBar from 'app/components/status_bar'; -import {getNotificationProps} from 'app/utils/notify_props'; -import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import Section from 'app/screens/settings/section'; import SectionItem from 'app/screens/settings/section_item'; -export default class NotificationSettingsEmail extends PureComponent { - static propTypes = { - config: PropTypes.object.isRequired, - currentUser: PropTypes.object.isRequired, - myPreferences: PropTypes.object.isRequired, - navigator: PropTypes.object, - onBack: PropTypes.func.isRequired, - theme: PropTypes.object.isRequired, - }; +import NotificationSettingsEmailBase from './notification_settings_email_base'; - constructor(props) { - super(props); - - const {currentUser} = props; - const notifyProps = getNotificationProps(currentUser); - - props.navigator.setOnNavigatorEvent(this.onNavigatorEvent); - this.state = this.setStateFromNotifyProps(notifyProps); - } - - componentWillReceiveProps(nextProps) { - if (this.props.theme !== nextProps.theme) { - setNavigatorStyles(this.props.navigator, nextProps.theme); - } - } - - onNavigatorEvent = (event) => { - if (event.type === 'ScreenChangedEvent') { - switch (event.id) { - case 'willDisappear': - this.saveUserNotifyProps(); - break; - } - } - }; - - setEmailNotifications = (value) => { - const {config} = this.props; - let email = value; - let interval; - - const emailBatchingEnabled = config.EnableEmailBatching === 'true'; - if (emailBatchingEnabled && value !== 'false') { - interval = value; - email = 'true'; - } - - this.setState({ - email, - interval, - }); - }; - - setStateFromNotifyProps = (notifyProps) => { - const {config, myPreferences} = this.props; - let interval; - if (config.SendEmailNotifications === 'true' && config.EnableEmailBatching === 'true') { - const emailPreferences = getPreferencesByCategory(myPreferences, Preferences.CATEGORY_NOTIFICATIONS); - if (emailPreferences.size) { - interval = emailPreferences.get(Preferences.EMAIL_INTERVAL).value; - } - } - - return { - ...notifyProps, - interval, - }; - }; - - saveUserNotifyProps = () => { - this.props.onBack({ - ...this.state, - user_id: this.props.currentUser.id, - }); - }; - - renderEmailSection = () => { - const {config, theme} = this.props; +class NotificationSettingsEmailIos extends NotificationSettingsEmailBase { + renderEmailSection() { + const { + enableEmailBatching, + sendEmailNotifications, + siteName, + theme, + } = this.props; + const {interval} = this.state; const style = getStyleSheet(theme); - const sendEmailNotifications = config.SendEmailNotifications === 'true'; - const emailBatchingEnabled = sendEmailNotifications && config.EnableEmailBatching === 'true'; - - let sendImmediatley = this.state.email === 'true'; - let sendImmediatleyValue = 'true'; - let fifteenMinutes; - let hourly; - const never = this.state.email === 'false'; - - if (emailBatchingEnabled && this.state.email !== 'false') { - sendImmediatley = this.state.interval === Preferences.INTERVAL_IMMEDIATE.toString(); - fifteenMinutes = this.state.interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString(); - hourly = this.state.interval === Preferences.INTERVAL_HOUR.toString(); - - sendImmediatleyValue = Preferences.INTERVAL_IMMEDIATE.toString(); - } - return (
@@ -138,25 +51,24 @@ export default class NotificationSettingsEmail extends PureComponent { )} action={this.setEmailNotifications} actionType='select' - actionValue={sendImmediatleyValue} - selected={sendImmediatley} + actionValue={Preferences.INTERVAL_IMMEDIATE.toString()} + selected={interval === Preferences.INTERVAL_IMMEDIATE.toString()} theme={theme} /> - {emailBatchingEnabled && + {enableEmailBatching && )} action={this.setEmailNotifications} actionType='select' actionValue={Preferences.INTERVAL_FIFTEEN_MINUTES.toString()} - selected={fifteenMinutes} + selected={interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString()} theme={theme} /> @@ -170,7 +82,7 @@ export default class NotificationSettingsEmail extends PureComponent { action={this.setEmailNotifications} actionType='select' actionValue={Preferences.INTERVAL_HOUR.toString()} - selected={hourly} + selected={interval === Preferences.INTERVAL_HOUR.toString()} theme={theme} /> @@ -185,8 +97,8 @@ export default class NotificationSettingsEmail extends PureComponent { )} action={this.setEmailNotifications} actionType='select' - actionValue='false' - selected={never} + actionValue={Preferences.INTERVAL_NEVER.toString()} + selected={interval === Preferences.INTERVAL_NEVER.toString()} theme={theme} /> @@ -200,7 +112,7 @@ export default class NotificationSettingsEmail extends PureComponent { }
); - }; + } render() { const {theme} = this.props; @@ -227,11 +139,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { flex: 1, backgroundColor: theme.centerChannelBg, }, - input: { - color: theme.centerChannelColor, - fontSize: 12, - height: 40, - }, separator: { backgroundColor: changeOpacity(theme.centerChannelColor, 0.1), flex: 1, @@ -253,3 +160,5 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { }, }; }); + +export default NotificationSettingsEmailIos; diff --git a/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js b/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js new file mode 100644 index 000000000..044bc5837 --- /dev/null +++ b/app/screens/settings/notification_settings_email/notification_settings_email.ios.test.js @@ -0,0 +1,89 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {configure, shallow} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +configure({adapter: new Adapter()}); + +import NotificationSettingsEmailIos from './notification_settings_email.ios.js'; + +jest.mock('app/utils/theme', () => { + const original = require.requireActual('app/utils/theme'); + return { + ...original, + changeOpacity: jest.fn(), + }; +}); + +describe('NotificationSettingsEmailIos', () => { + const baseProps = { + currentUserId: 'current_user_id', + emailInterval: '30', + enableEmailBatching: false, + navigator: {setOnNavigatorEvent: () => {}}, // eslint-disable-line no-empty-function + actions: { + savePreferences: () => {}, // eslint-disable-line no-empty-function + }, + sendEmailNotifications: true, + siteName: 'Mattermost', + theme: { + centerChannelBg: '#aaa', + centerChannelColor: '#aaa', + }, + }; + + test('should match snapshot, renderEmailSection', () => { + const wrapper = shallow( + + ); + + expect(wrapper.instance().renderEmailSection()).toMatchSnapshot(); + }); + + test('should call saveEmailNotifyProps on onNavigatorEvent', () => { + const wrapper = shallow( + + ); + + const instance = wrapper.instance(); + instance.saveEmailNotifyProps = jest.fn(); + instance.onNavigatorEvent({type: 'ScreenChangedEvent', id: 'willDisappear'}); + + expect(instance.saveEmailNotifyProps).toHaveBeenCalledTimes(1); + }); + + test('should macth state on setEmailNotifications', () => { + const wrapper = shallow( + + ); + + wrapper.setState({email: 'false', interval: '0'}); + wrapper.instance().setEmailNotifications('30'); + expect(wrapper.state({email: 'true', interval: '30'})); + + wrapper.instance().setEmailNotifications('0'); + expect(wrapper.state({email: 'false', interval: '0'})); + + wrapper.instance().setEmailNotifications('3600'); + expect(wrapper.state({email: 'true', interval: '3600'})); + }); + + test('should call props.actions.savePreferences on saveUserNotifyProps', () => { + const props = {...baseProps, actions: {savePreferences: jest.fn()}}; + const wrapper = shallow( + + ); + + wrapper.setState({email: 'true', interval: '3600'}); + wrapper.instance().saveEmailNotifyProps(); + expect(props.actions.savePreferences).toHaveBeenCalledTimes(1); + expect(props.actions.savePreferences).toBeCalledWith( + 'current_user_id', + [ + {category: 'notifications', name: 'email', user_id: 'current_user_id', value: 'true'}, + {category: 'notifications', name: 'email_interval', user_id: 'current_user_id', value: '3600'}, + ] + ); + }); +}); diff --git a/app/screens/settings/notification_settings_email/notification_settings_email_base.js b/app/screens/settings/notification_settings_email/notification_settings_email_base.js new file mode 100644 index 000000000..7b9114080 --- /dev/null +++ b/app/screens/settings/notification_settings_email/notification_settings_email_base.js @@ -0,0 +1,99 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {PureComponent} from 'react'; +import PropTypes from 'prop-types'; + +import {Preferences} from 'mattermost-redux/constants'; +import {getEmailInterval} from 'mattermost-redux/utils/notify_props'; + +import {setNavigatorStyles} from 'app/utils/theme'; + +export default class NotificationSettingsEmailBase extends PureComponent { + static propTypes = { + actions: PropTypes.shape({ + savePreferences: PropTypes.func.isRequired, + }), + currentUserId: PropTypes.string.isRequired, + emailInterval: PropTypes.string.isRequired, + enableEmailBatching: PropTypes.bool.isRequired, + navigator: PropTypes.object, + sendEmailNotifications: PropTypes.bool.isRequired, + siteName: PropTypes.string, + theme: PropTypes.object.isRequired, + }; + + constructor(props) { + super(props); + + const { + emailInterval, + enableEmailBatching, + navigator, + sendEmailNotifications, + } = props; + + this.state = { + interval: getEmailInterval( + sendEmailNotifications, + enableEmailBatching, + parseInt(emailInterval, 10), + ).toString(), + showEmailNotificationsModal: false, + }; + + navigator.setOnNavigatorEvent(this.onNavigatorEvent); + } + + componentWillReceiveProps(nextProps) { + if (this.props.theme !== nextProps.theme) { + setNavigatorStyles(this.props.navigator, nextProps.theme); + } + + if ( + this.props.sendEmailNotifications !== nextProps.sendEmailNotifications || + this.props.enableEmailBatching !== nextProps.enableEmailBatching || + this.props.emailInterval !== nextProps.emailInterval + ) { + this.setState({ + interval: getEmailInterval( + nextProps.sendEmailNotifications, + nextProps.enableEmailBatching, + parseInt(nextProps.emailInterval, 10), + ).toString(), + }); + } + } + + onNavigatorEvent = (event) => { + if (event.type === 'ScreenChangedEvent') { + switch (event.id) { + case 'willDisappear': + this.saveEmailNotifyProps(); + break; + } + } + }; + + setEmailNotifications = (interval) => { + const {sendEmailNotifications} = this.props; + + let email = 'false'; + if (sendEmailNotifications && interval !== Preferences.INTERVAL_NEVER.toString()) { + email = 'true'; + } + + this.setState({ + email, + interval, + }); + }; + + saveEmailNotifyProps = () => { + const {currentUserId} = this.props; + const {email, interval} = this.state; + const emailNotify = {category: Preferences.CATEGORY_NOTIFICATIONS, user_id: currentUserId, name: 'email', value: email}; + const emailInterval = {category: Preferences.CATEGORY_NOTIFICATIONS, user_id: currentUserId, name: Preferences.EMAIL_INTERVAL, value: interval}; + this.props.actions.savePreferences(currentUserId, [emailNotify, emailInterval]); + }; +} diff --git a/app/screens/settings/notification_settings_mentions/notification_settings_mentions.android.js b/app/screens/settings/notification_settings_mentions/notification_settings_mentions.android.js index 20e32fb6a..d49ab0d09 100644 --- a/app/screens/settings/notification_settings_mentions/notification_settings_mentions.android.js +++ b/app/screens/settings/notification_settings_mentions/notification_settings_mentions.android.js @@ -20,7 +20,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import NotificationSettingsMentionsBase from './notification_settings_mention_base'; -class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase { +class NotificationSettingsMentionsAndroid extends NotificationSettingsMentionsBase { cancelMentionKeys = () => { this.setState({showKeywordsModal: false}); this.keywords = this.state.mention_keys; @@ -442,4 +442,4 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { }; }); -export default injectIntl(NotificationSettingsMentionsIos); +export default injectIntl(NotificationSettingsMentionsAndroid); diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 481edc80f..03a5aceff 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -3318,6 +3318,7 @@ "user.settings.notifications.email.disabled_long": "Email notifications have not been enabled by your System Administrator.", "user.settings.notifications.email.everyHour": "Every hour", "user.settings.notifications.email.everyXMinutes": "Every {count, plural, one {minute} other {{count, number} minutes}}", + "mobile.user.settings.notifications.email.fifteenMinutes": "Every 15 minutes", "user.settings.notifications.email.immediately": "Immediately", "user.settings.notifications.email.never": "Never", "user.settings.notifications.email.send": "Send email notifications",