Files
mostlymatter/webapp/components/user_settings/user_settings_notifications.jsx
Corey Hulen 2e5617c29b PLT-2057 User as a first class object (#2648)
* Adding TeamMember to system

* Fixing all unit tests on the backend

* Fixing merge conflicts

* Fixing merge conflict

* Adding javascript unit tests

* Adding TeamMember to system

* Fixing all unit tests on the backend

* Fixing merge conflicts

* Fixing merge conflict

* Adding javascript unit tests

* Adding client side unit test

* Cleaning up the clint side tests

* Fixing msg

* Adding more client side unit tests

* Adding more using tests

* Adding last bit of client side unit tests and adding make cmd

* Fixing bad merge

* Fixing libraries

* Updating to new client side API

* Fixing borken unit test

* Fixing unit tests

* ugg...trying to beat gofmt

* ugg...trying to beat gofmt

* Cleaning up remainder of the server side routes

* Adding inital load api

* Increased coverage of webhook unit tests (#2660)

* Adding loading ... to root html

* Fixing bad merge

* Removing explicit content type so superagent will guess corectly (#2685)

* Fixing merge and unit tests

* Adding create team UI

* Fixing signup flows

* Adding LDAP unit tests and enterprise unit test helper (#2702)

* Add the ability to reset MFA from the commandline (#2706)

* Fixing compliance unit tests

* Fixing client side tests

* Adding open server to system console

* Moving websocket connection

* Fixing unit test

* Fixing unit tests

* Fixing unit tests

* Adding nickname and more LDAP unit tests (#2717)

* Adding join open teams

* Cleaning up all TODOs in the code

* Fixing web sockets

* Removing unused webockets file

* PLT-2533 Add the ability to reset a user's MFA from the system console (#2715)

* Add the ability to reset a user's MFA from the system console

* Add client side unit test for adminResetMfa

* Reorganizing authentication to fix LDAP error message (#2723)

* Fixing failing unit test

* Initial upgrade db code

* Adding upgrade script

* Fixing upgrade script after running on core

* Update OAuth and Claim routes to work with user model changes (#2739)

* Fixing perminant deletion. Adding ability to delete all user and the entire database (#2740)

* Fixing team invite ldap login call (#2741)

* Fixing bluebar and some img stuff

* Fix all the different file upload web utils (#2743)

* Fixing invalid session redirect (#2744)

* Redirect on bad channel name (#2746)

* Fixing a bunch of issue and removing dead code

* Patch to fix error message on leave channel (#2747)

* Setting EnableOpenServer to false by default

* Fixing config

* Fixing upgrade

* Fixing reported bugs

* Bug fixes for PLT-2057

* PLT-2563 Redo password recovery to use a database table (#2745)

* Redo password recovery to use a database table

* Update reset password audits

* Split out admin and user reset password APIs to be separate

* Delete password recovery when user is permanently deleted

* Consolidate password resetting into a single function

* Removed private channels as an option for outgoing webhooks (#2752)

* PLT-2577/PLT-2552 Fixes for backstage (#2753)

* Added URL to incoming webhook list

* Fixed client functions for adding/removing integrations

* Disallowed slash commands without trigger words

* Fixed clientside handling of errors on AddCommand page

* Minor auth cleanup (#2758)

* Changed EditPostModal to just close if you save without making any changes (#2759)

* Renamed client -> Client in async_client.jsx and fixed eslint warnings (#2756)

* Fixed url in channel info modal (#2755)

* Fixing reported issues

* Moving to version 3 of the apis

* Fixing command unit tests (#2760)

* Adding team admins

* Fixing DM issue

* Fixing eslint error

* Properly set EditPostModal's originalText state in all cases (#2762)

* Update client config check to assume features is defined if server is licensed (#2772)

* Fixing url link

* Fixing issue with websocket crashing when sending messages to different teams
2016-04-21 22:37:01 -07:00

837 строки
30 KiB
JavaScript
Исходник Ответственный История

Этот файл содержит неоднозначные символы Юникода
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import $ from 'jquery';
import ReactDOM from 'react-dom';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import UserStore from 'stores/user_store.jsx';
import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
function getNotificationsStateFromStores() {
var user = UserStore.getCurrentUser();
var soundNeeded = !Utils.isBrowserFirefox();
var sound = 'true';
if (user.notify_props && user.notify_props.desktop_sound) {
sound = user.notify_props.desktop_sound;
}
var desktop = 'default';
if (user.notify_props && user.notify_props.desktop) {
desktop = user.notify_props.desktop;
}
var email = 'true';
if (user.notify_props && user.notify_props.email) {
email = user.notify_props.email;
}
var usernameKey = false;
var mentionKey = false;
var customKeys = '';
var firstNameKey = false;
var allKey = false;
var channelKey = false;
if (user.notify_props) {
if (user.notify_props.mention_keys) {
var keys = user.notify_props.mention_keys.split(',');
if (keys.indexOf(user.username) === -1) {
usernameKey = false;
} else {
usernameKey = true;
keys.splice(keys.indexOf(user.username), 1);
}
if (keys.indexOf('@' + user.username) === -1) {
mentionKey = false;
} else {
mentionKey = true;
keys.splice(keys.indexOf('@' + user.username), 1);
}
customKeys = keys.join(',');
}
if (user.notify_props.first_name) {
firstNameKey = user.notify_props.first_name === 'true';
}
if (user.notify_props.all) {
allKey = user.notify_props.all === 'true';
}
if (user.notify_props.channel) {
channelKey = user.notify_props.channel === 'true';
}
}
return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound,
usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0,
firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
}
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'
},
close: {
id: 'user.settings.notifications.close',
defaultMessage: 'Close'
}
});
import React from 'react';
class NotificationsTab extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.updateSection = this.updateSection.bind(this);
this.updateState = this.updateState.bind(this);
this.onListenerChange = this.onListenerChange.bind(this);
this.handleNotifyRadio = this.handleNotifyRadio.bind(this);
this.handleEmailRadio = this.handleEmailRadio.bind(this);
this.handleSoundRadio = this.handleSoundRadio.bind(this);
this.updateUsernameKey = this.updateUsernameKey.bind(this);
this.updateMentionKey = this.updateMentionKey.bind(this);
this.updateFirstNameKey = this.updateFirstNameKey.bind(this);
this.updateAllKey = this.updateAllKey.bind(this);
this.updateChannelKey = this.updateChannelKey.bind(this);
this.updateCustomMentionKeys = this.updateCustomMentionKeys.bind(this);
this.onCustomChange = this.onCustomChange.bind(this);
this.state = getNotificationsStateFromStores();
}
handleSubmit() {
var data = {};
data.user_id = this.props.user.id;
data.email = this.state.enableEmail;
data.desktop_sound = this.state.enableSound;
data.desktop = this.state.notifyLevel;
var mentionKeys = [];
if (this.state.usernameKey) {
mentionKeys.push(this.props.user.username);
}
if (this.state.mentionKey) {
mentionKeys.push('@' + this.props.user.username);
}
var stringKeys = mentionKeys.join(',');
if (this.state.customKeys.length > 0 && this.state.customKeysChecked) {
stringKeys += ',' + this.state.customKeys;
}
data.mention_keys = stringKeys;
data.first_name = this.state.firstNameKey.toString();
data.all = this.state.allKey.toString();
data.channel = this.state.channelKey.toString();
Client.updateUserNotifyProps(data,
function success() {
this.props.updateSection('');
AsyncClient.getMe();
}.bind(this),
function failure(err) {
this.setState({serverError: err.message});
}.bind(this)
);
}
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: notifyLevel});
ReactDOM.findDOMNode(this.refs.wrapper).focus();
}
handleEmailRadio(enableEmail) {
this.setState({enableEmail: enableEmail});
ReactDOM.findDOMNode(this.refs.wrapper).focus();
}
handleSoundRadio(enableSound) {
this.setState({enableSound: enableSound});
ReactDOM.findDOMNode(this.refs.wrapper).focus();
}
updateUsernameKey(val) {
this.setState({usernameKey: val});
}
updateMentionKey(val) {
this.setState({mentionKey: val});
}
updateFirstNameKey(val) {
this.setState({firstNameKey: val});
}
updateAllKey(val) {
this.setState({allKey: val});
}
updateChannelKey(val) {
this.setState({channelKey: val});
}
updateCustomMentionKeys() {
var checked = ReactDOM.findDOMNode(this.refs.customcheck).checked;
if (checked) {
var text = ReactDOM.findDOMNode(this.refs.custommentions).value;
// remove all spaces and split string into individual keys
this.setState({customKeys: text.replace(/ /g, ''), customKeysChecked: true});
} else {
this.setState({customKeys: '', customKeysChecked: false});
}
}
onCustomChange() {
ReactDOM.findDOMNode(this.refs.customcheck).checked = true;
this.updateCustomMentionKeys();
}
render() {
const {formatMessage} = this.props.intl;
var serverError = null;
if (this.state.serverError) {
serverError = this.state.serverError;
}
var user = this.props.user;
var desktopSection;
var handleUpdateDesktopSection;
if (this.props.activeSection === 'desktop') {
var notifyActive = [false, false, false];
if (this.state.notifyLevel === 'mention') {
notifyActive[1] = true;
} else if (this.state.notifyLevel === 'none') {
notifyActive[2] = true;
} else {
notifyActive[0] = true;
}
let inputs = [];
inputs.push(
<div key='userNotificationLevelOption'>
<div className='radio'>
<label>
<input type='radio'
checked={notifyActive[0]}
onChange={this.handleNotifyRadio.bind(this, 'all')}
/>
<FormattedMessage
id='user.settings.notification.allActivity'
defaultMessage='For all activity'
/>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
type='radio'
checked={notifyActive[1]}
onChange={this.handleNotifyRadio.bind(this, 'mention')}
/>
<FormattedMessage
id='user.settings.notifications.onlyMentions'
defaultMessage='Only for mentions and direct messages'
/>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
type='radio'
checked={notifyActive[2]}
onChange={this.handleNotifyRadio.bind(this, 'none')}
/>
<FormattedMessage
id='user.settings.notifications.never'
defaultMessage='Never'
/>
</label>
</div>
</div>
);
const extraInfo = (
<span>
<FormattedMessage
id='user.settings.notifications.info'
defaultMessage='Desktop notifications are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
/>
</span>
);
desktopSection = (
<SettingItemMax
title={formatMessage(holders.desktop)}
extraInfo={extraInfo}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
updateSection={this.handleCancel}
/>
);
} else {
let describe = '';
if (this.state.notifyLevel === 'mention') {
describe = (
<FormattedMessage
id='user.settings.notifications.onlyMentions'
defaultMessage='Only for mentions and direct messages'
/>
);
} else if (this.state.notifyLevel === 'none') {
describe = (
<FormattedMessage
id='user.settings.notifications.never'
defaultMessage='Never'
/>
);
} else {
describe = (
<FormattedMessage
id='user.settings.notification.allActivity'
defaultMessage='For all activity'
/>
);
}
handleUpdateDesktopSection = function updateDesktopSection() {
this.props.updateSection('desktop');
}.bind(this);
desktopSection = (
<SettingItemMin
title={formatMessage(holders.desktop)}
describe={describe}
updateSection={handleUpdateDesktopSection}
/>
);
}
var soundSection;
var handleUpdateSoundSection;
if (this.props.activeSection === 'sound' && this.state.soundNeeded) {
var soundActive = [false, false];
if (this.state.enableSound === 'false') {
soundActive[1] = true;
} else {
soundActive[0] = true;
}
let inputs = [];
inputs.push(
<div key='userNotificationSoundOptions'>
<div className='radio'>
<label>
<input
type='radio'
checked={soundActive[0]}
onChange={this.handleSoundRadio.bind(this, 'true')}
/>
<FormattedMessage
id='user.settings.notifications.on'
defaultMessage='On'
/>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
type='radio'
checked={soundActive[1]}
onChange={this.handleSoundRadio.bind(this, 'false')}
/>
<FormattedMessage
id='user.settings.notifications.off'
defaultMessage='Off'
/>
</label>
<br/>
</div>
</div>
);
const extraInfo = (
<span>
<FormattedMessage
id='user.settings.notifications.sounds_info'
defaultMessage='Desktop notifications sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
/>
</span>
);
soundSection = (
<SettingItemMax
title={formatMessage(holders.desktopSounds)}
extraInfo={extraInfo}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
updateSection={this.handleCancel}
/>
);
} else {
let describe = '';
if (!this.state.soundNeeded) {
describe = (
<FormattedMessage
id='user.settings.notification.soundConfig'
defaultMessage='Please configure notification sounds in your browser settings'
/>
);
} else if (this.state.enableSound === 'false') {
describe = (
<FormattedMessage
id='user.settings.notifications.off'
defaultMessage='Off'
/>
);
} else {
describe = (
<FormattedMessage
id='user.settings.notifications.on'
defaultMessage='On'
/>
);
}
handleUpdateSoundSection = function updateSoundSection() {
this.props.updateSection('sound');
}.bind(this);
soundSection = (
<SettingItemMin
title={formatMessage(holders.desktopSounds)}
describe={describe}
updateSection={handleUpdateSoundSection}
disableOpen={!this.state.soundNeeded}
/>
);
}
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'
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'
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 youve 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') {
let inputs = [];
let handleUpdateFirstNameKey;
let handleUpdateUsernameKey;
let handleUpdateMentionKey;
let handleUpdateAllKey;
let handleUpdateChannelKey;
if (user.first_name) {
handleUpdateFirstNameKey = function handleFirstNameKeyChange(e) {
this.updateFirstNameKey(e.target.checked);
}.bind(this);
inputs.push(
<div key='userNotificationFirstNameOption'>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.firstNameKey}
onChange={handleUpdateFirstNameKey}
/>
<FormattedMessage
id='user.settings.notifications.sensitiveName'
defaultMessage='Your case sensitive first name "{first_name}"'
values={{
first_name: user.first_name
}}
/>
</label>
</div>
</div>
);
}
handleUpdateUsernameKey = function handleUsernameKeyChange(e) {
this.updateUsernameKey(e.target.checked);
}.bind(this);
inputs.push(
<div key='userNotificationUsernameOption'>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.usernameKey}
onChange={handleUpdateUsernameKey}
/>
<FormattedMessage
id='user.settings.notifications.sensitiveUsername'
defaultMessage='Your non-case sensitive username "{username}"'
values={{
username: user.username
}}
/>
</label>
</div>
</div>
);
handleUpdateMentionKey = function handleMentionKeyChange(e) {
this.updateMentionKey(e.target.checked);
}.bind(this);
inputs.push(
<div key='userNotificationMentionOption'>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.mentionKey}
onChange={handleUpdateMentionKey}
/>
<FormattedMessage
id='user.settings.notifications.usernameMention'
defaultMessage='Your username mentioned "@{username}"'
values={{
username: user.username
}}
/>
</label>
</div>
</div>
);
handleUpdateAllKey = function handleAllKeyChange(e) {
this.updateAllKey(e.target.checked);
}.bind(this);
inputs.push(
<div key='userNotificationAllOption'>
<div className='checkbox hidden'>
<label>
<input
type='checkbox'
checked={this.state.allKey}
onChange={handleUpdateAllKey}
/>
<FormattedMessage
id='user.settings.notifications.teamWide'
defaultMessage='Team-wide mentions "@all"'
/>
</label>
</div>
</div>
);
handleUpdateChannelKey = function handleChannelKeyChange(e) {
this.updateChannelKey(e.target.checked);
}.bind(this);
inputs.push(
<div key='userNotificationChannelOption'>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.channelKey}
onChange={handleUpdateChannelKey}
/>
<FormattedMessage
id='user.settings.notifications.channelWide'
defaultMessage='Channel-wide mentions "@channel"'
/>
</label>
</div>
</div>
);
inputs.push(
<div key='userNotificationCustomOption'>
<div className='checkbox'>
<label>
<input
ref='customcheck'
type='checkbox'
checked={this.state.customKeysChecked}
onChange={this.updateCustomMentionKeys}
/>
<FormattedMessage
id='user.settings.notifications.sensitiveWords'
defaultMessage='Other non-case sensitive words, separated by commas:'
/>
</label>
</div>
<input
ref='custommentions'
className='form-control mentions-input'
type='text'
defaultValue={this.state.customKeys}
onChange={this.onCustomChange}
/>
</div>
);
keysSection = (
<SettingItemMax
title={formatMessage(holders.wordsTrigger)}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
updateSection={this.handleCancel}
/>
);
} else {
let keys = [];
if (this.state.firstNameKey) {
keys.push(user.first_name);
}
if (this.state.usernameKey) {
keys.push(user.username);
}
if (this.state.mentionKey) {
keys.push('@' + user.username);
}
// if (this.state.allKey) {
// keys.push('@all');
// }
if (this.state.channelKey) {
keys.push('@channel');
}
if (this.state.customKeys.length > 0) {
keys = keys.concat(this.state.customKeys.split(','));
}
let describe = '';
for (var i = 0; i < keys.length; i++) {
describe += '"' + keys[i] + '", ';
}
if (describe.length > 0) {
describe = describe.substring(0, describe.length - 2);
} else {
describe = (
<FormattedMessage
id='user.settings.notifications.noWords'
defaultMessage='No words configured'
/>
);
}
handleUpdateKeysSection = function updateKeysSection() {
this.props.updateSection('keys');
}.bind(this);
keysSection = (
<SettingItemMin
title={formatMessage(holders.wordsTrigger)}
describe={describe}
updateSection={handleUpdateKeysSection}
/>
);
}
return (
<div>
<div className='modal-header'>
<button
type='button'
className='close'
data-dismiss='modal'
aria-label={formatMessage(holders.close)}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
ref='title'
>
<div className='modal-back'>
<i
className='fa fa-angle-left'
onClick={this.props.collapseModal}
/>
</div>
<FormattedMessage
id='user.settings.notifications.title'
defaultMessage='Notification Settings'
/>
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>
<FormattedMessage
id='user.settings.notifications.header'
defaultMessage='Notifications'
/>
</h3>
<div className='divider-dark first'/>
{desktopSection}
<div className='divider-light'/>
{soundSection}
<div className='divider-light'/>
{emailSection}
<div className='divider-light'/>
{keysSection}
<div className='divider-dark'/>
</div>
</div>
);
}
}
NotificationsTab.defaultProps = {
user: null,
activeSection: '',
activeTab: ''
};
NotificationsTab.propTypes = {
intl: intlShape.isRequired,
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
activeSection: React.PropTypes.string,
activeTab: React.PropTypes.string,
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
export default injectIntl(NotificationsTab);