[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
This commit is contained in:
Saturnino Abril 2018-07-05 01:17:59 +08:00 committed by Elias Nahum
parent 665bb862a8
commit e4a0a4b4ec
12 changed files with 909 additions and 416 deletions

View file

@ -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}
/>
);
});

View file

@ -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 = (
<FormattedText
id='user.settings.notifications.emailInfo'
defaultMessage='Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
values={{siteName: config.SiteName}}
style={style.modalHelpText}
/>
);
}
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 (
<Modal
animationType='slide'
transparent={true}
visible={this.state.showEmailNotificationsModal}
onRequestClose={() => this.setState({showEmailNotificationsModal: false})}
>
<View style={style.modalOverlay}>
<View style={style.modal}>
<View style={style.modalBody}>
<View style={style.modalTitleContainer}>
<FormattedText
id='user.settings.notifications.email.send'
defaultMessage='Send email notifications'
style={style.modalTitle}
/>
</View>
{!sendEmailNotifications &&
<FormattedText
id='user.settings.general.emailHelp2'
defaultMessage='Email has been disabled by your System Administrator. No notification emails will be sent until it is enabled.'
style={style.modalOptionDisabled}
/>
}
{sendEmailNotifications &&
<RadioButtonGroup
name='emailSettings'
onSelect={this.setEmailNotifications}
options={emailOptions}
/>
}
{helpText}
</View>
<View style={style.modalFooter}>
<View style={style.divider}/>
<View style={style.modalFooterContainer}>
<TouchableOpacity
style={style.modalFooterOptionContainer}
onPress={this.saveEmailNotifications}
>
<FormattedText
id='mobile.notification_settings.modal_cancel'
defaultMessage='CANCEL'
style={style.modalFooterOption}
/>
</TouchableOpacity>
{sendEmailNotifications &&
<View>
<View style={{marginRight: 10}}/>
<TouchableOpacity
style={style.modalFooterOptionContainer}
onPress={this.saveEmailNotifications}
>
<FormattedText
id='mobile.notification_settings.modal_save'
defaultMessage='SAVE'
style={style.modalFooterOption}
/>
</TouchableOpacity>
</View>
}
</View>
</View>
</View>
</View>
</Modal>
);
};
render() {
const {theme, enableAutoResponder} = this.props;
const style = getStyleSheet(theme);
@ -458,7 +262,6 @@ class NotificationSettings extends PureComponent {
{autoResponder}
<View style={style.divider}/>
</ScrollView>
{this.renderEmailNotificationSettings(style)}
</View>
);
}
@ -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,
},
};
});

View file

@ -0,0 +1,128 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NotificationSettingsMobileAndroid should match snapshot 1`] = `
<sectionItem
action={[Function]}
actionType="default"
description={
<InjectIntl(FormattedText)
defaultMessage="Immediately"
id="user.settings.notifications.email.immediately"
/>
}
label={
<InjectIntl(FormattedText)
defaultMessage="Send email notifications"
id="user.settings.notifications.email.send"
/>
}
theme={
Object {
"centerChannelBg": "#aaa",
"centerChannelColor": "#aaa",
}
}
/>
`;
exports[`NotificationSettingsMobileAndroid should match snapshot 2`] = `
<Component
animationType="slide"
hardwareAccelerated={false}
onRequestClose={[Function]}
transparent={true}
visible={false}
>
<Component
style={undefined}
>
<Component
style={Object {}}
>
<Component
style={Object {}}
>
<Component
style={Object {}}
>
<InjectIntl(FormattedText)
defaultMessage="Send email notifications"
id="user.settings.notifications.email.send"
style={Object {}}
/>
</Component>
<RadioButtonGroup
name="emailSettings"
onSelect={[Function]}
options={
Array [
Object {
"checked": true,
"label": "Immediately",
"value": "30",
},
Object {
"checked": false,
"label": "Never",
"value": "0",
},
]
}
/>
<InjectIntl(FormattedText)
defaultMessage="Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes."
id="user.settings.notifications.emailInfo"
style={Object {}}
values={
Object {
"siteName": "Mattermost",
}
}
/>
</Component>
<Component
style={Object {}}
>
<Component
style={Object {}}
/>
<Component
style={Object {}}
>
<TouchableOpacity
activeOpacity={0.2}
onPress={[Function]}
style={Object {}}
>
<InjectIntl(FormattedText)
defaultMessage="CANCEL"
id="mobile.notification_settings.modal_cancel"
style={Object {}}
/>
</TouchableOpacity>
<Component>
<Component
style={
Object {
"marginRight": 10,
}
}
/>
<TouchableOpacity
activeOpacity={0.2}
onPress={[Function]}
style={Object {}}
>
<InjectIntl(FormattedText)
defaultMessage="SAVE"
id="mobile.notification_settings.modal_save"
style={Object {}}
/>
</TouchableOpacity>
</Component>
</Component>
</Component>
</Component>
</Component>
</Component>
`;

View file

@ -0,0 +1,71 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NotificationSettingsEmailIos should match snapshot, renderEmailSection 1`] = `
<section
disableFooter={false}
footerDefaultMessage="Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes."
footerId="user.settings.notifications.emailInfo"
footerValues={
Object {
"siteName": "Mattermost",
}
}
headerDefaultMessage="SEND EMAIL NOTIFICATIONS"
headerId="mobile.notification_settings.email.send"
theme={
Object {
"centerChannelBg": "#aaa",
"centerChannelColor": "#aaa",
}
}
>
<Component>
<sectionItem
action={[Function]}
actionType="select"
actionValue="30"
label={
<InjectIntl(FormattedText)
defaultMessage="Immediately"
id="user.settings.notifications.email.immediately"
/>
}
selected={true}
theme={
Object {
"centerChannelBg": "#aaa",
"centerChannelColor": "#aaa",
}
}
/>
<Component
style={
Object {
"backgroundColor": undefined,
"flex": 1,
"height": 1,
"marginLeft": 15,
}
}
/>
<sectionItem
action={[Function]}
actionType="select"
actionValue="0"
label={
<InjectIntl(FormattedText)
defaultMessage="Never"
id="user.settings.notifications.email.never"
/>
}
selected={false}
theme={
Object {
"centerChannelBg": "#aaa",
"centerChannelColor": "#aaa",
}
}
/>
</Component>
</section>
`;

View file

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

View file

@ -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 (
<SectionItem
description={(
<FormattedText
id={i18nId}
defaultMessage={i18nMessage}
/>
)}
label={(
<FormattedText
id='user.settings.notifications.email.send'
defaultMessage='Send email notifications'
/>
)}
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 = (
<FormattedText
id='user.settings.notifications.emailInfo'
defaultMessage='Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
values={{siteName}}
style={style.modalHelpText}
/>
);
}
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 (
<Modal
animationType='slide'
transparent={true}
visible={this.state.showEmailNotificationsModal}
onRequestClose={this.handleClose}
>
<View style={style.modalOverlay}>
<View style={style.modal}>
<View style={style.modalBody}>
<View style={style.modalTitleContainer}>
<FormattedText
id='user.settings.notifications.email.send'
defaultMessage='Send email notifications'
style={style.modalTitle}
/>
</View>
{!sendEmailNotifications &&
<FormattedText
id='user.settings.general.emailHelp2'
defaultMessage='Email has been disabled by your System Administrator. No notification emails will be sent until it is enabled.'
style={style.modalOptionDisabled}
/>
}
{sendEmailNotifications &&
<RadioButtonGroup
name='emailSettings'
onSelect={this.setEmailNotifications}
options={emailOptions}
/>
}
{helpText}
</View>
<View style={style.modalFooter}>
<View style={style.divider}/>
<View style={style.modalFooterContainer}>
<TouchableOpacity
style={style.modalFooterOptionContainer}
onPress={this.handleClose}
>
<FormattedText
id='mobile.notification_settings.modal_cancel'
defaultMessage='CANCEL'
style={style.modalFooterOption}
/>
</TouchableOpacity>
{sendEmailNotifications &&
<View>
<View style={{marginRight: 10}}/>
<TouchableOpacity
style={style.modalFooterOptionContainer}
onPress={this.handleSaveEmailNotification}
>
<FormattedText
id='mobile.notification_settings.modal_save'
defaultMessage='SAVE'
style={style.modalFooterOption}
/>
</TouchableOpacity>
</View>
}
</View>
</View>
</View>
</View>
</Modal>
);
}
render() {
const {theme} = this.props;
const style = getStyleSheet(theme);
return (
<View style={style.container}>
<ScrollView
style={style.scrollView}
contentContainerStyle={style.scrollViewContent}
alwaysBounceVertical={false}
>
{this.renderEmailSection()}
<View style={style.separator}/>
{this.renderEmailNotificationsModal(style)}
</ScrollView>
</View>
);
}
}
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;

View file

@ -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(
<NotificationSettingsMobileAndroid {...baseProps}/>
);
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(
<NotificationSettingsMobileAndroid {...baseProps}/>
);
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(
<NotificationSettingsMobileAndroid {...baseProps}/>
);
wrapper.setState({showEmailNotificationsModal: true});
wrapper.instance().handleClose();
expect(wrapper.state('showEmailNotificationsModal')).toEqual(false);
});
test('should saveEmailNotifyProps and handleClose on handleSaveEmailNotification', () => {
const wrapper = shallowWithIntl(
<NotificationSettingsMobileAndroid {...baseProps}/>
);
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(
<NotificationSettingsMobileAndroid {...baseProps}/>
);
wrapper.setState({showEmailNotificationsModal: false});
wrapper.instance().showEmailModal();
expect(wrapper.state('showEmailNotificationsModal')).toEqual(true);
});
});

View file

@ -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 (
<Section
headerId='mobile.notification_settings.email.send'
headerDefaultMessage='SEND EMAIL NOTIFICATIONS'
footerId='user.settings.notifications.emailInfo'
footerDefaultMessage='Email notifications are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
footerValues={{siteName: config.SiteName}}
footerValues={{siteName}}
disableFooter={!sendEmailNotifications}
theme={theme}
>
@ -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}
/>
<View style={style.separator}/>
{emailBatchingEnabled &&
{enableEmailBatching &&
<View>
<SectionItem
label={(
<FormattedText
id='user.settings.notifications.email.everyXMinutes'
defaultMessage='Every {count, plural, one {minute} other {{count, number} minutes}}'
values={{count: Preferences.INTERVAL_FIFTEEN_MINUTES / 60}}
id='mobile.user.settings.notifications.email.fifteenMinutes'
defaultMessage='Every 15 minutes'
/>
)}
action={this.setEmailNotifications}
actionType='select'
actionValue={Preferences.INTERVAL_FIFTEEN_MINUTES.toString()}
selected={fifteenMinutes}
selected={interval === Preferences.INTERVAL_FIFTEEN_MINUTES.toString()}
theme={theme}
/>
<View style={style.separator}/>
@ -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}
/>
<View style={style.separator}/>
@ -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}
/>
</View>
@ -200,7 +112,7 @@ export default class NotificationSettingsEmail extends PureComponent {
}
</Section>
);
};
}
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;

View file

@ -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(
<NotificationSettingsEmailIos {...baseProps}/>
);
expect(wrapper.instance().renderEmailSection()).toMatchSnapshot();
});
test('should call saveEmailNotifyProps on onNavigatorEvent', () => {
const wrapper = shallow(
<NotificationSettingsEmailIos {...baseProps}/>
);
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(
<NotificationSettingsEmailIos {...baseProps}/>
);
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(
<NotificationSettingsEmailIos {...props}/>
);
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'},
]
);
});
});

View file

@ -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]);
};
}

View file

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

View file

@ -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",