PLT-3647 Email Batching (#3718)
* PLT-3647 Added config settings for email batching * PLT-3647 Refactored generation of email notification * PLT-3647 Added serverside code for email batching * PLT-3647 Updated settings UI to enable email batching * PLT-3647 Removed debug code * PLT-3647 Fixed 0-padding of minutes in batched notification * PLT-3647 Updated clientside UI for when email batching is disabled * Go fmt * PLT-3647 Changed email batching to be disabled by default * Updated batched email message * Added email batching toggle to system console * Changed Email Notifications > Immediate setting to a 30 second batch interval * Go fmt * Fixed link to Mattermost icon in batched email notification * Updated users to use 30 second email batching by default * Fully disabled email batching when clustering is enabled * Fixed email batching setting in the system console * Fixed casing of 'Send Email notifications' -> 'Send email notifications' * Updating UI Improvements for email batching (#3736) * Updated text for notification settings and SiteURL. * Prevented enabling email batching when SiteURL isn't set in the system console * Re-added a couple debug messages * Added warning text when clustering is enabled
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
dde158c57f
Коммит
8203fd16ce
@@ -69,7 +69,7 @@ export default class ConfigurationSettings extends AdminSettings {
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.service.siteURLDescription'
|
||||
defaultMessage='The URL, including port number and protocol, from which users will access Mattermost. Leave blank to automatically configure based on incoming traffic.'
|
||||
defaultMessage='The URL, including post number and protocol, that users will use to access Mattermost. Leave blank to automatically configure based on incoming traffic.'
|
||||
/>
|
||||
}
|
||||
value={this.state.siteURL}
|
||||
|
||||
@@ -14,13 +14,13 @@ const CONNECTION_SECURITY_HELP_TEXT = (
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.connectionSecurityNone'
|
||||
defaultMessage='None'
|
||||
/>
|
||||
</td>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.connectionSecurityNoneDescription'
|
||||
defaultMessage='Mattermost will connect over an unsecure connection.'
|
||||
@@ -28,13 +28,13 @@ const CONNECTION_SECURITY_HELP_TEXT = (
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.connectionSecurityTls'
|
||||
defaultMessage='TLS'
|
||||
/>
|
||||
</td>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.connectionSecurityTlsDescription'
|
||||
defaultMessage='Encrypts the communication between Mattermost and your server.'
|
||||
@@ -42,13 +42,13 @@ const CONNECTION_SECURITY_HELP_TEXT = (
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.connectionSecurityStart'
|
||||
defaultMessage='STARTTLS'
|
||||
/>
|
||||
</td>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.connectionSecurityStartDescription'
|
||||
defaultMessage='Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'
|
||||
|
||||
@@ -32,6 +32,7 @@ export default class EmailSettings extends AdminSettings {
|
||||
config.EmailSettings.SMTPServer = this.state.smtpServer;
|
||||
config.EmailSettings.SMTPPort = this.state.smtpPort;
|
||||
config.EmailSettings.ConnectionSecurity = this.state.connectionSecurity;
|
||||
config.EmailSettings.EnableEmailBatching = this.state.enableEmailBatching;
|
||||
config.ServiceSettings.EnableSecurityFixAlert = this.state.enableSecurityFixAlert;
|
||||
|
||||
return config;
|
||||
@@ -48,6 +49,7 @@ export default class EmailSettings extends AdminSettings {
|
||||
smtpServer: config.EmailSettings.SMTPServer,
|
||||
smtpPort: config.EmailSettings.SMTPPort,
|
||||
connectionSecurity: config.EmailSettings.ConnectionSecurity,
|
||||
enableEmailBatching: config.EmailSettings.EnableEmailBatching,
|
||||
enableSecurityFixAlert: config.ServiceSettings.EnableSecurityFixAlert
|
||||
};
|
||||
}
|
||||
@@ -64,6 +66,34 @@ export default class EmailSettings extends AdminSettings {
|
||||
}
|
||||
|
||||
renderSettings() {
|
||||
let enableEmailBatchingDisabledText = null;
|
||||
|
||||
if (this.props.config.ClusterSettings.Enable) {
|
||||
enableEmailBatchingDisabledText = (
|
||||
<span
|
||||
key='admin.email.enableEmailBatching.clusterEnabled'
|
||||
className='help-text'
|
||||
>
|
||||
<FormattedHTMLMessage
|
||||
id='admin.email.enableEmailBatching.clusterEnabled'
|
||||
defaultMessage='Email batching cannot be enabled unless the SiteURL is configured in <b>Configuration > SiteURL</b>.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
} else if (!this.props.config.ServiceSettings.SiteURL) {
|
||||
enableEmailBatchingDisabledText = (
|
||||
<span
|
||||
key='admin.email.enableEmailBatching.siteURL'
|
||||
className='help-text'
|
||||
>
|
||||
<FormattedHTMLMessage
|
||||
id='admin.email.enableEmailBatching.siteURL'
|
||||
defaultMessage='Email batching cannot be enabled unless the SiteURL is configured in <b>Configuration > SiteURL</b>.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsGroup>
|
||||
<BooleanSetting
|
||||
@@ -83,6 +113,26 @@ export default class EmailSettings extends AdminSettings {
|
||||
value={this.state.sendEmailNotifications}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<BooleanSetting
|
||||
id='enableEmailBatching'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.email.enableEmailBatchingTitle'
|
||||
defaultMessage='Enable Email Batching: '
|
||||
/>
|
||||
}
|
||||
helpText={[
|
||||
<FormattedHTMLMessage
|
||||
key='admin.email.enableEmailBatchingDesc'
|
||||
id='admin.email.enableEmailBatchingDesc'
|
||||
defaultMessage='When true, users can have email notifications for multiple direct messages and mentions combined into a single email, configurable in <b>Account Settings > Notifications</b>.'
|
||||
/>,
|
||||
enableEmailBatchingDisabledText
|
||||
]}
|
||||
value={this.state.enableEmailBatching && !this.props.config.ClusterSettings.Enable && this.props.config.ServiceSettings.SiteURL}
|
||||
onChange={this.handleChange}
|
||||
disabled={!this.state.sendEmailNotifications || this.props.config.ClusterSettings.Enable || !this.props.config.ServiceSettings.SiteURL}
|
||||
/>
|
||||
<TextSetting
|
||||
id='feedbackName'
|
||||
label={
|
||||
|
||||
@@ -204,7 +204,8 @@ export default class LogSettings extends AdminSettings {
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='help-text'>{'%T'}</td><td className='help-text'>
|
||||
<td>{'%T'}</td>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.log.formatTime'
|
||||
defaultMessage='Time (15:04:05 MST)'
|
||||
@@ -212,7 +213,8 @@ export default class LogSettings extends AdminSettings {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>{'%D'}</td><td className='help-text'>
|
||||
<td>{'%D'}</td>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.log.formatDateLong'
|
||||
defaultMessage='Date (2006/01/02)'
|
||||
@@ -220,7 +222,8 @@ export default class LogSettings extends AdminSettings {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>{'%d'}</td><td className='help-text'>
|
||||
<td>{'%d'}</td>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.log.formatDateShort'
|
||||
defaultMessage='Date (01/02/06)'
|
||||
@@ -228,7 +231,8 @@ export default class LogSettings extends AdminSettings {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>{'%L'}</td><td className='help-text'>
|
||||
<td>{'%L'}</td>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.log.formatLevel'
|
||||
defaultMessage='Level (DEBG, INFO, EROR)'
|
||||
@@ -236,7 +240,8 @@ export default class LogSettings extends AdminSettings {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>{'%S'}</td><td className='help-text'>
|
||||
<td>{'%S'}</td>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.log.formatSource'
|
||||
defaultMessage='Source'
|
||||
@@ -244,7 +249,8 @@ export default class LogSettings extends AdminSettings {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>{'%M'}</td><td className='help-text'>
|
||||
<td>{'%M'}</td>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.log.formatMessage'
|
||||
defaultMessage='Message'
|
||||
|
||||
@@ -15,13 +15,13 @@ const WEBSERVER_MODE_HELP_TEXT = (
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.webserverModeGzip'
|
||||
defaultMessage='gzip'
|
||||
/>
|
||||
</td>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.webserverModeGzipDescription'
|
||||
defaultMessage='The Mattermost server will serve static files compressed with gzip.'
|
||||
@@ -29,13 +29,13 @@ const WEBSERVER_MODE_HELP_TEXT = (
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.webserverModeUncompressed'
|
||||
defaultMessage='Uncompressed'
|
||||
/>
|
||||
</td>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.webserverModeUncompressedDescription'
|
||||
defaultMessage='The Mattermost server will serve static files uncompressed.'
|
||||
@@ -43,13 +43,13 @@ const WEBSERVER_MODE_HELP_TEXT = (
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.webserverModeDisabled'
|
||||
defaultMessage='Disabled'
|
||||
/>
|
||||
</td>
|
||||
<td className='help-text'>
|
||||
<td>
|
||||
<FormattedMessage
|
||||
id='admin.webserverModeDisabledDescription'
|
||||
defaultMessage='The Mattermost server will not serve static files.'
|
||||
|
||||
211
webapp/components/user_settings/email_notification_setting.jsx
Обычный файл
211
webapp/components/user_settings/email_notification_setting.jsx
Обычный файл
@@ -0,0 +1,211 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {savePreference} from 'utils/async_client.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import {localizeMessage} from 'utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import SettingItemMin from 'components/setting_item_min.jsx';
|
||||
import SettingItemMax from 'components/setting_item_max.jsx';
|
||||
|
||||
import {Preferences} from 'utils/constants.jsx';
|
||||
|
||||
const INTERVAL_IMMEDIATE = 30; // "immediate" is a 30 second interval
|
||||
const INTERVAL_FIFTEEN_MINUTES = 15 * 60;
|
||||
const INTERVAL_HOUR = 60 * 60;
|
||||
|
||||
export default class EmailNotificationSetting extends React.Component {
|
||||
static propTypes = {
|
||||
activeSection: React.PropTypes.string.isRequired,
|
||||
updateSection: React.PropTypes.func.isRequired,
|
||||
enableEmail: React.PropTypes.bool.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
onSubmit: React.PropTypes.func.isRequired,
|
||||
serverError: React.PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.submit = this.submit.bind(this);
|
||||
|
||||
this.expand = this.expand.bind(this);
|
||||
this.collapse = this.collapse.bind(this);
|
||||
|
||||
this.state = {
|
||||
emailInterval: PreferenceStore.getInt(Preferences.CATEGORY_NOTIFICATIONS, Preferences.EMAIL_INTERVAL, INTERVAL_IMMEDIATE)
|
||||
};
|
||||
}
|
||||
|
||||
handleChange(enableEmail, emailInterval) {
|
||||
this.props.onChange(enableEmail);
|
||||
this.setState({emailInterval});
|
||||
}
|
||||
|
||||
submit() {
|
||||
// until the rest of the notification settings are moved to preferences, we have to do this separately
|
||||
savePreference(Preferences.CATEGORY_NOTIFICATIONS, Preferences.EMAIL_INTERVAL, this.state.emailInterval.toString());
|
||||
|
||||
this.props.onSubmit();
|
||||
}
|
||||
|
||||
expand() {
|
||||
this.props.updateSection('email');
|
||||
}
|
||||
|
||||
collapse() {
|
||||
this.props.updateSection('');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.activeSection !== 'email') {
|
||||
let description;
|
||||
|
||||
if (this.props.enableEmail === 'true') {
|
||||
switch (this.state.emailInterval) {
|
||||
case INTERVAL_IMMEDIATE:
|
||||
description = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email.immediately'
|
||||
defaultMessage='Immediately'
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case INTERVAL_HOUR:
|
||||
description = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email.everyHour'
|
||||
defaultMessage='Every hour'
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
description = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email.everyXMinutes'
|
||||
defaultMessage='Every {count, plural, one {minute} other {{count, number} minutes}}'
|
||||
values={{count: this.state.emailInterval / 60}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
description = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
title={localizeMessage('user.settings.notifications.emailNotifications', 'Send Email notifications')}
|
||||
describe={description}
|
||||
updateSection={this.expand}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let batchingOptions = null;
|
||||
let batchingInfo = null;
|
||||
if (window.mm_config.EnableEmailBatching === 'true') {
|
||||
batchingOptions = (
|
||||
<div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
name='emailNotifications'
|
||||
checked={this.props.enableEmail === 'true' && this.state.emailInterval === INTERVAL_FIFTEEN_MINUTES}
|
||||
onChange={this.handleChange.bind(this, 'true', INTERVAL_FIFTEEN_MINUTES)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email.everyXMinutes'
|
||||
defaultMessage='Every {count} minutes'
|
||||
values={{count: INTERVAL_FIFTEEN_MINUTES / 60}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
name='emailNotifications'
|
||||
checked={this.props.enableEmail === 'true' && this.state.emailInterval === INTERVAL_HOUR}
|
||||
onChange={this.handleChange.bind(this, 'true', INTERVAL_HOUR)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.everyHour'
|
||||
defaultMessage='Every hour'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
batchingInfo = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.emailBatchingInfo'
|
||||
defaultMessage='Notifications are combined into a single email and sent at the maximum frequency selected here.'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItemMax
|
||||
title={localizeMessage('user.settings.notifications.emailNotifications', 'Send email notifications')}
|
||||
inputs={[
|
||||
<div key='userNotificationEmailOptions'>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
name='emailNotifications'
|
||||
checked={this.props.enableEmail === 'true' && this.state.emailInterval === INTERVAL_IMMEDIATE}
|
||||
onChange={this.handleChange.bind(this, 'true', INTERVAL_IMMEDIATE)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email.immediately'
|
||||
defaultMessage='Immediately'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{batchingOptions}
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
name='emailNotifications'
|
||||
checked={this.props.enableEmail === 'false'}
|
||||
onChange={this.handleChange.bind(this, 'false', INTERVAL_IMMEDIATE)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.emailInfo'
|
||||
defaultMessage='Email notifications that are sent for mentions and direct messages when you are offline or away from {siteName} for more than 5 minutes.'
|
||||
values={{
|
||||
siteName: global.window.mm_config.SiteName
|
||||
}}
|
||||
/>
|
||||
{' '}
|
||||
{batchingInfo}
|
||||
</div>
|
||||
</div>
|
||||
]}
|
||||
submit={this.submit}
|
||||
server_error={this.props.serverError}
|
||||
updateSection={this.collapse}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
import $ from 'jquery';
|
||||
import SettingItemMin from '../setting_item_min.jsx';
|
||||
import SettingItemMax from '../setting_item_max.jsx';
|
||||
import SettingItemMin from 'components/setting_item_min.jsx';
|
||||
import SettingItemMax from 'components/setting_item_max.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
@@ -12,7 +12,8 @@ import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as UserAgent from 'utils/user_agent.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
|
||||
import EmailNotificationSetting from './email_notification_setting.jsx';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
function getNotificationsStateFromStores() {
|
||||
var user = UserStore.getCurrentUser();
|
||||
@@ -30,9 +31,9 @@ function getNotificationsStateFromStores() {
|
||||
if (user.notify_props && user.notify_props.comments) {
|
||||
comments = user.notify_props.comments;
|
||||
}
|
||||
var email = 'true';
|
||||
var enableEmail = 'true';
|
||||
if (user.notify_props && user.notify_props.email) {
|
||||
email = user.notify_props.email;
|
||||
enableEmail = user.notify_props.email;
|
||||
}
|
||||
var push = 'mention';
|
||||
if (user.notify_props && user.notify_props.push) {
|
||||
@@ -78,7 +79,7 @@ function getNotificationsStateFromStores() {
|
||||
return {
|
||||
notifyLevel: desktop,
|
||||
notifyPushLevel: push,
|
||||
enableEmail: email,
|
||||
enableEmail,
|
||||
soundNeeded,
|
||||
enableSound: sound,
|
||||
usernameKey,
|
||||
@@ -91,36 +92,9 @@ function getNotificationsStateFromStores() {
|
||||
};
|
||||
}
|
||||
|
||||
const holders = defineMessages({
|
||||
desktop: {
|
||||
id: 'user.settings.notifications.desktop',
|
||||
defaultMessage: 'Send desktop notifications'
|
||||
},
|
||||
desktopSounds: {
|
||||
id: 'user.settings.notifications.desktopSounds',
|
||||
defaultMessage: 'Desktop notification sounds'
|
||||
},
|
||||
emailNotifications: {
|
||||
id: 'user.settings.notifications.emailNotifications',
|
||||
defaultMessage: 'Email notifications'
|
||||
},
|
||||
wordsTrigger: {
|
||||
id: 'user.settings.notifications.wordsTrigger',
|
||||
defaultMessage: 'Words that trigger mentions'
|
||||
},
|
||||
comments: {
|
||||
id: 'user.settings.notifications.comments',
|
||||
defaultMessage: 'Comment threads notifications'
|
||||
},
|
||||
close: {
|
||||
id: 'user.settings.notifications.close',
|
||||
defaultMessage: 'Close'
|
||||
}
|
||||
});
|
||||
|
||||
import React from 'react';
|
||||
|
||||
class NotificationsTab extends React.Component {
|
||||
export default class NotificationsTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -142,6 +116,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
this.state = getNotificationsStateFromStores();
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
var data = {};
|
||||
data.user_id = this.props.user.id;
|
||||
@@ -178,31 +153,38 @@ class NotificationsTab extends React.Component {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
handleCancel(e) {
|
||||
this.updateState();
|
||||
this.props.updateSection('');
|
||||
e.preventDefault();
|
||||
$('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update');
|
||||
}
|
||||
|
||||
updateSection(section) {
|
||||
this.updateState();
|
||||
this.props.updateSection(section);
|
||||
}
|
||||
|
||||
updateState() {
|
||||
const newState = getNotificationsStateFromStores();
|
||||
if (!Utils.areObjectsEqual(newState, this.state)) {
|
||||
this.setState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
UserStore.addChangeListener(this.onListenerChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
UserStore.removeChangeListener(this.onListenerChange);
|
||||
}
|
||||
|
||||
onListenerChange() {
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
handleNotifyRadio(notifyLevel) {
|
||||
this.setState({notifyLevel});
|
||||
this.refs.wrapper.focus();
|
||||
@@ -222,22 +204,28 @@ class NotificationsTab extends React.Component {
|
||||
this.setState({enableEmail});
|
||||
this.refs.wrapper.focus();
|
||||
}
|
||||
|
||||
handleSoundRadio(enableSound) {
|
||||
this.setState({enableSound});
|
||||
this.refs.wrapper.focus();
|
||||
}
|
||||
|
||||
updateUsernameKey(val) {
|
||||
this.setState({usernameKey: val});
|
||||
}
|
||||
|
||||
updateMentionKey(val) {
|
||||
this.setState({mentionKey: val});
|
||||
}
|
||||
|
||||
updateFirstNameKey(val) {
|
||||
this.setState({firstNameKey: val});
|
||||
}
|
||||
|
||||
updateChannelKey(val) {
|
||||
this.setState({channelKey: val});
|
||||
}
|
||||
|
||||
updateCustomMentionKeys() {
|
||||
var checked = this.refs.customcheck.checked;
|
||||
|
||||
@@ -250,10 +238,12 @@ class NotificationsTab extends React.Component {
|
||||
this.setState({customKeys: '', customKeysChecked: false});
|
||||
}
|
||||
}
|
||||
|
||||
onCustomChange() {
|
||||
this.refs.customcheck.checked = true;
|
||||
this.updateCustomMentionKeys();
|
||||
}
|
||||
|
||||
createPushNotificationSection() {
|
||||
var handleUpdateDesktopSection;
|
||||
if (this.props.activeSection === 'push') {
|
||||
@@ -312,8 +302,8 @@ class NotificationsTab extends React.Component {
|
||||
onChange={this.handlePushRadio.bind(this, 'none')}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.off'
|
||||
defaultMessage='Off'
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -346,7 +336,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
return (
|
||||
<SettingItemMax
|
||||
title={Utils.localizeMessage('user.settings.notifications.push', 'Mobile push notifications')}
|
||||
title={Utils.localizeMessage('user.settings.notifications.push', 'Send mobile push notifications')}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={submit}
|
||||
@@ -367,8 +357,8 @@ class NotificationsTab extends React.Component {
|
||||
} else if (this.state.notifyPushLevel === 'none') {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.off'
|
||||
defaultMessage='Off'
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
);
|
||||
} else if (global.window.mm_config.SendPushNotifications === 'false') {
|
||||
@@ -393,14 +383,14 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
title={Utils.localizeMessage('user.settings.notifications.push', 'Mobile push notifications')}
|
||||
title={Utils.localizeMessage('user.settings.notifications.push', 'Send mobile push notifications')}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateDesktopSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const serverError = this.state.serverError;
|
||||
|
||||
var user = this.props.user;
|
||||
@@ -479,7 +469,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
desktopSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.desktop)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.desktop', 'Send desktop notifications')}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
@@ -518,7 +508,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
desktopSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.desktop)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.desktop', 'Send desktop notifications')}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateDesktopSection}
|
||||
/>
|
||||
@@ -583,7 +573,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
soundSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.desktopSounds)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.desktopSounds', 'Desktop notification sounds')}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
@@ -622,7 +612,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
soundSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.desktopSounds)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.desktopSounds', 'Desktop notification sounds')}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateSoundSection}
|
||||
disableOpen={!this.state.soundNeeded}
|
||||
@@ -630,102 +620,6 @@ class NotificationsTab extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
var emailSection;
|
||||
var handleUpdateEmailSection;
|
||||
if (this.props.activeSection === 'email') {
|
||||
var emailActive = [false, false];
|
||||
if (this.state.enableEmail === 'false') {
|
||||
emailActive[1] = true;
|
||||
} else {
|
||||
emailActive[0] = true;
|
||||
}
|
||||
|
||||
let inputs = [];
|
||||
|
||||
inputs.push(
|
||||
<div key='userNotificationEmailOptions'>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
name='emailNotifications'
|
||||
checked={emailActive[0]}
|
||||
onChange={this.handleEmailRadio.bind(this, 'true')}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
name='emailNotifications'
|
||||
checked={emailActive[1]}
|
||||
onChange={this.handleEmailRadio.bind(this, 'false')}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div><br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.emailInfo'
|
||||
defaultMessage='Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.'
|
||||
values={{
|
||||
siteName: global.window.mm_config.SiteName
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
emailSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.emailNotifications)}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
updateSection={this.handleCancel}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
let describe = '';
|
||||
if (this.state.enableEmail === 'false') {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleUpdateEmailSection = function updateEmailSection() {
|
||||
this.props.updateSection('email');
|
||||
}.bind(this);
|
||||
|
||||
emailSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.emailNotifications)}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateEmailSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
var keysSection;
|
||||
var handleUpdateKeysSection;
|
||||
if (this.props.activeSection === 'keys') {
|
||||
@@ -859,7 +753,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
keysSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.wordsTrigger)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.wordsTrigger', 'Words that trigger mentions')}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
@@ -910,7 +804,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
keysSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.wordsTrigger)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.wordsTrigger', 'Words that trigger mentions')}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateKeysSection}
|
||||
/>
|
||||
@@ -991,7 +885,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
commentsSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.comments)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.comments', 'Comment threads notifications')}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
@@ -1030,7 +924,7 @@ class NotificationsTab extends React.Component {
|
||||
|
||||
commentsSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.comments)}
|
||||
title={Utils.localizeMessage('user.settings.notifications.comments', 'Comment threads notifications')}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateCommentsSection}
|
||||
/>
|
||||
@@ -1046,7 +940,6 @@ class NotificationsTab extends React.Component {
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
aria-label={formatMessage(holders.close)}
|
||||
onClick={this.props.closeModal}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
@@ -1082,7 +975,14 @@ class NotificationsTab extends React.Component {
|
||||
<div className='divider-light'/>
|
||||
{soundSection}
|
||||
<div className='divider-light'/>
|
||||
{emailSection}
|
||||
<EmailNotificationSetting
|
||||
activeSection={this.props.activeSection}
|
||||
updateSection={this.props.updateSection}
|
||||
enableEmail={this.state.enableEmail}
|
||||
onChange={this.handleEmailRadio}
|
||||
onSubmit={this.handleSubmit}
|
||||
serverError={this.state.serverError}
|
||||
/>
|
||||
<div className='divider-light'/>
|
||||
{pushNotificationSection}
|
||||
<div className='divider-light'/>
|
||||
@@ -1103,7 +1003,6 @@ NotificationsTab.defaultProps = {
|
||||
activeTab: ''
|
||||
};
|
||||
NotificationsTab.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
@@ -1112,5 +1011,3 @@ NotificationsTab.propTypes = {
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(NotificationsTab);
|
||||
|
||||
Ссылка в новой задаче
Block a user