Fixing merge
Этот коммит содержится в:
@@ -6,7 +6,96 @@ import Constants from '../../utils/constants.jsx';
|
||||
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
const Popover = ReactBootstrap.Popover;
|
||||
|
||||
export default class CustomThemeChooser extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
sidebarBg: {
|
||||
id: 'user.settings.custom_theme.sidebarBg',
|
||||
defaultMessage: 'Sidebar BG'
|
||||
},
|
||||
sidebarText: {
|
||||
id: 'user.settings.custom_theme.sidebarText',
|
||||
defaultMessage: 'Sidebar Text'
|
||||
},
|
||||
sidebarHeaderBg: {
|
||||
id: 'user.settings.custom_theme.sidebarHeaderBg',
|
||||
defaultMessage: 'Sidebar Header BG'
|
||||
},
|
||||
sidebarHeaderTextColor: {
|
||||
id: 'user.settings.custom_theme.sidebarHeaderTextColor',
|
||||
defaultMessage: 'Sidebar Header Text'
|
||||
},
|
||||
sidebarUnreadText: {
|
||||
id: 'user.settings.custom_theme.sidebarUnreadText',
|
||||
defaultMessage: 'Sidebar Unread Text'
|
||||
},
|
||||
sidebarTextHoverBg: {
|
||||
id: 'user.settings.custom_theme.sidebarTextHoverBg',
|
||||
defaultMessage: 'Sidebar Text Hover BG'
|
||||
},
|
||||
sidebarTextActiveBorder: {
|
||||
id: 'user.settings.custom_theme.sidebarTextActiveBorder',
|
||||
defaultMessage: 'Sidebar Text Active Border'
|
||||
},
|
||||
sidebarTextActiveColor: {
|
||||
id: 'user.settings.custom_theme.sidebarTextActiveColor',
|
||||
defaultMessage: 'Sidebar Text Active Color'
|
||||
},
|
||||
onlineIndicator: {
|
||||
id: 'user.settings.custom_theme.onlineIndicator',
|
||||
defaultMessage: 'Online Indicator'
|
||||
},
|
||||
awayIndicator: {
|
||||
id: 'user.settings.custom_theme.awayIndicator',
|
||||
defaultMessage: 'Away Indicator'
|
||||
},
|
||||
mentionBj: {
|
||||
id: 'user.settings.custom_theme.mentionBj',
|
||||
defaultMessage: 'Mention Jewel BG'
|
||||
},
|
||||
mentionColor: {
|
||||
id: 'user.settings.custom_theme.mentionColor',
|
||||
defaultMessage: 'Mention Jewel Text'
|
||||
},
|
||||
centerChannelBg: {
|
||||
id: 'user.settings.custom_theme.centerChannelBg',
|
||||
defaultMessage: 'Center Channel BG'
|
||||
},
|
||||
centerChannelColor: {
|
||||
id: 'user.settings.custom_theme.centerChannelColor',
|
||||
defaultMessage: 'Center Channel Text'
|
||||
},
|
||||
newMessageSeparator: {
|
||||
id: 'user.settings.custom_theme.newMessageSeparator',
|
||||
defaultMessage: 'New Message Separator'
|
||||
},
|
||||
linkColor: {
|
||||
id: 'user.settings.custom_theme.linkColor',
|
||||
defaultMessage: 'Link Color'
|
||||
},
|
||||
buttonBg: {
|
||||
id: 'user.settings.custom_theme.buttonBg',
|
||||
defaultMessage: 'Button BG'
|
||||
},
|
||||
buttonColor: {
|
||||
id: 'user.settings.custom_theme.buttonColor',
|
||||
defaultMessage: 'Button Text'
|
||||
},
|
||||
mentionHighlightBg: {
|
||||
id: 'user.settings.custom_theme.mentionHighlightBg',
|
||||
defaultMessage: 'Mention Highlight BG'
|
||||
},
|
||||
mentionHighlightLink: {
|
||||
id: 'user.settings.custom_theme.mentionHighlightLink',
|
||||
defaultMessage: 'Mention Highlight Link'
|
||||
},
|
||||
codeTheme: {
|
||||
id: 'user.settings.custom_theme.codeTheme',
|
||||
defaultMessage: 'Code Theme'
|
||||
}
|
||||
});
|
||||
|
||||
class CustomThemeChooser extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -65,6 +154,7 @@ export default class CustomThemeChooser extends React.Component {
|
||||
this.props.updateTheme(theme);
|
||||
}
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const theme = this.props.theme;
|
||||
|
||||
const elements = [];
|
||||
@@ -102,7 +192,7 @@ export default class CustomThemeChooser extends React.Component {
|
||||
className='col-sm-4 form-group'
|
||||
key={'custom-theme-key' + index}
|
||||
>
|
||||
<label className='custom-label'>{element.uiName}</label>
|
||||
<label className='custom-label'>{formatMessage(messages[element.id])}</label>
|
||||
<div
|
||||
className='input-group theme-group group--code dropdown'
|
||||
id={element.id}
|
||||
@@ -135,7 +225,7 @@ export default class CustomThemeChooser extends React.Component {
|
||||
className='col-sm-4 form-group'
|
||||
key={'custom-theme-key' + index}
|
||||
>
|
||||
<label className='custom-label'>{element.uiName}</label>
|
||||
<label className='custom-label'>{formatMessage(messages[element.id])}</label>
|
||||
<div
|
||||
className='input-group color-picker'
|
||||
id={element.id}
|
||||
@@ -160,7 +250,10 @@ export default class CustomThemeChooser extends React.Component {
|
||||
const pasteBox = (
|
||||
<div className='col-sm-12'>
|
||||
<label className='custom-label'>
|
||||
{'Copy and paste to share theme colors:'}
|
||||
<FormattedMessage
|
||||
id='user.settings.custom_theme.copyPaste'
|
||||
defaultMessage='Copy and paste to share theme colors:'
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
@@ -185,6 +278,9 @@ export default class CustomThemeChooser extends React.Component {
|
||||
}
|
||||
|
||||
CustomThemeChooser.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
theme: React.PropTypes.object.isRequired,
|
||||
updateTheme: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(CustomThemeChooser);
|
||||
@@ -9,9 +9,19 @@ const Modal = ReactBootstrap.Modal;
|
||||
|
||||
import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
|
||||
import Constants from '../../utils/constants.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
submitError: {
|
||||
id: 'user.settings.import_theme.submitError',
|
||||
defaultMessage: 'Invalid format, please try copying and pasting in again.'
|
||||
}
|
||||
});
|
||||
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
|
||||
export default class ImportThemeModal extends React.Component {
|
||||
class ImportThemeModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -39,7 +49,7 @@ export default class ImportThemeModal extends React.Component {
|
||||
const text = ReactDOM.findDOMNode(this.refs.input).value;
|
||||
|
||||
if (!this.isInputValid(text)) {
|
||||
this.setState({inputError: 'Invalid format, please try copying and pasting in again.'});
|
||||
this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +135,7 @@ export default class ImportThemeModal extends React.Component {
|
||||
if (this.isInputValid(e.target.value)) {
|
||||
this.setState({inputError: null});
|
||||
} else {
|
||||
this.setState({inputError: 'Invalid format, please try copying and pasting in again.'});
|
||||
this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
@@ -136,7 +146,12 @@ export default class ImportThemeModal extends React.Component {
|
||||
onHide={() => this.setState({show: false})}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{'Import Slack Theme'}</Modal.Title>
|
||||
<Modal.Title>
|
||||
<FormattedMessage
|
||||
id='user.settings.import_theme.importHeader'
|
||||
defaultMessage='Import Slack Theme'
|
||||
/>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<form
|
||||
role='form'
|
||||
@@ -144,7 +159,10 @@ export default class ImportThemeModal extends React.Component {
|
||||
>
|
||||
<Modal.Body>
|
||||
<p>
|
||||
{'To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'}
|
||||
<FormattedMessage
|
||||
id='user.settings.import_theme.importBody'
|
||||
defaultMessage='To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'
|
||||
/>
|
||||
</p>
|
||||
<div className='form-group less'>
|
||||
<div className='col-sm-9'>
|
||||
@@ -166,7 +184,10 @@ export default class ImportThemeModal extends React.Component {
|
||||
className='btn btn-default'
|
||||
onClick={() => this.setState({show: false})}
|
||||
>
|
||||
{'Cancel'}
|
||||
<FormattedMessage
|
||||
id='user.settings.import_theme.cancel'
|
||||
defaultMessage='Cancel'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
onClick={this.handleSubmit}
|
||||
@@ -174,7 +195,10 @@ export default class ImportThemeModal extends React.Component {
|
||||
className='btn btn-primary'
|
||||
tabIndex='3'
|
||||
>
|
||||
{'Submit'}
|
||||
<FormattedMessage
|
||||
id='user.settings.import_theme.submit'
|
||||
defaultMessage='Submit'
|
||||
/>
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</form>
|
||||
@@ -183,3 +207,9 @@ export default class ImportThemeModal extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ImportThemeModal.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(ImportThemeModal);
|
||||
@@ -7,6 +7,8 @@ import Constants from '../../utils/constants.jsx';
|
||||
import ChannelStore from '../../stores/channel_store.jsx';
|
||||
import LoadingScreen from '../loading_screen.jsx';
|
||||
|
||||
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
|
||||
|
||||
export default class ManageIncomingHooks extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -126,7 +128,12 @@ export default class ManageIncomingHooks extends React.Component {
|
||||
<span className='word-break--all'>{Utils.getWindowLocationOrigin() + '/hooks/' + hook.id}</span>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<strong>{'Channel: '}</strong>{c.display_name}
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_in.channel'
|
||||
defaultMessage='Channel: '
|
||||
/>
|
||||
</strong>{c.display_name}
|
||||
</div>
|
||||
<a
|
||||
className={'webhook__remove'}
|
||||
@@ -147,12 +154,24 @@ export default class ManageIncomingHooks extends React.Component {
|
||||
} else if (hooks.length > 0) {
|
||||
displayHooks = hooks;
|
||||
} else {
|
||||
displayHooks = <div className='padding-top x2'>{'None'}</div>;
|
||||
displayHooks = (
|
||||
<div className='padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_in.none'
|
||||
defaultMessage='None'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const existingHooks = (
|
||||
<div className='webhooks__container'>
|
||||
<label className='control-label padding-top x2'>{'Existing incoming webhooks'}</label>
|
||||
<label className='control-label padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_in.existing'
|
||||
defaultMessage='Existing incoming webhooks'
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='webhooks__list'>
|
||||
{displayHooks}
|
||||
@@ -162,15 +181,16 @@ export default class ManageIncomingHooks extends React.Component {
|
||||
|
||||
return (
|
||||
<div key='addIncomingHook'>
|
||||
{'Create webhook URLs for use in external integrations. Please see '}
|
||||
<a
|
||||
href='http://mattermost.org/webhooks'
|
||||
target='_blank'
|
||||
>
|
||||
{'http://mattermost.org/webhooks'}
|
||||
</a>
|
||||
{' to learn more.'}
|
||||
<div><label className='control-label padding-top x2'>{'Add a new incoming webhook'}</label></div>
|
||||
<FormattedHTMLMessage
|
||||
id='user.settings.hooks_in.description'
|
||||
defaultMessage='Create webhook URLs for use in external integrations. Please see<a href="http://mattermost.org/webhooks" target="_blank">http://mattermost.org/webhooks</a> to learn more.'
|
||||
/>
|
||||
<div><label className='control-label padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_in.addTitle'
|
||||
defaultMessage='Add a new incoming webhook'
|
||||
/>
|
||||
</label></div>
|
||||
<div className='row padding-top'>
|
||||
<div className='col-sm-10 padding-bottom'>
|
||||
<select
|
||||
@@ -189,7 +209,10 @@ export default class ManageIncomingHooks extends React.Component {
|
||||
href='#'
|
||||
onClick={this.addNewHook}
|
||||
>
|
||||
{'Add'}
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_in.add'
|
||||
defaultMessage='Add'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import * as Client from '../../utils/client.jsx';
|
||||
import * as Utils from '../../utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
export default class ManageLanguage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -70,7 +72,12 @@ export default class ManageLanguage extends React.Component {
|
||||
return (
|
||||
<div key='changeLanguage'>
|
||||
<br/>
|
||||
<label className='control-label'>{'Change interface language'}</label>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.languages.change'
|
||||
defaultMessage='Change interface language'
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<select
|
||||
ref='language'
|
||||
@@ -87,7 +94,10 @@ export default class ManageLanguage extends React.Component {
|
||||
href='#'
|
||||
onClick={this.changeLanguage}
|
||||
>
|
||||
{'Set language'}
|
||||
<FormattedMessage
|
||||
id='user.settings.languages'
|
||||
defaultMessage='Set language'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,20 @@ import ChannelStore from '../../stores/channel_store.jsx';
|
||||
import * as Client from '../../utils/client.jsx';
|
||||
import Constants from '../../utils/constants.jsx';
|
||||
|
||||
export default class ManageOutgoingHooks extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
optional: {
|
||||
id: 'user.settings.hooks_out.optional',
|
||||
defaultMessage: 'Optional if channel selected'
|
||||
},
|
||||
callbackHolder: {
|
||||
id: 'user.settings.hooks_out.callbackHolder',
|
||||
defaultMessage: 'Each URL must start with http:// or https://'
|
||||
}
|
||||
});
|
||||
|
||||
class ManageOutgoingHooks extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -140,7 +153,10 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
key='select-channel'
|
||||
value=''
|
||||
>
|
||||
{'--- Select a channel ---'}
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.select'
|
||||
defaultMessage='--- Select a channel ---'
|
||||
/>
|
||||
</option>
|
||||
);
|
||||
|
||||
@@ -169,7 +185,12 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
if (c) {
|
||||
channelDiv = (
|
||||
<div className='padding-top'>
|
||||
<strong>{'Channel: '}</strong>{c.display_name}
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.channel'
|
||||
defaultMessage='Channel: '
|
||||
/>
|
||||
</strong>{c.display_name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -178,7 +199,12 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
if (hook.trigger_words && hook.trigger_words.length !== 0) {
|
||||
triggerDiv = (
|
||||
<div className='padding-top'>
|
||||
<strong>{'Trigger Words: '}</strong>{hook.trigger_words.join(', ')}
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.trigger'
|
||||
defaultMessage='Trigger Words: '
|
||||
/>
|
||||
</strong>{hook.trigger_words.join(', ')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -202,7 +228,10 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
href='#'
|
||||
onClick={this.regenToken.bind(this, hook.id)}
|
||||
>
|
||||
{'Regen Token'}
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.regen'
|
||||
defaultMessage='Regen Token'
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className='webhook__remove'
|
||||
@@ -223,12 +252,24 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
} else if (hooks.length > 0) {
|
||||
displayHooks = hooks;
|
||||
} else {
|
||||
displayHooks = <div className='padding-top x2'>{'None'}</div>;
|
||||
displayHooks = (
|
||||
<div className='padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.none'
|
||||
defaultMessage='None'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const existingHooks = (
|
||||
<div className='webhooks__container'>
|
||||
<label className='control-label padding-top x2'>{'Existing outgoing webhooks'}</label>
|
||||
<label className='control-label padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.existing'
|
||||
defaultMessage='Existing outgoing webhooks'
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='webhooks__list'>
|
||||
{displayHooks}
|
||||
@@ -240,19 +281,25 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
|
||||
return (
|
||||
<div key='addOutgoingHook'>
|
||||
{'Create webhooks to send new message events to an external integration. Please see '}
|
||||
<a
|
||||
href='http://mattermost.org/webhooks'
|
||||
target='_blank'
|
||||
>
|
||||
{'http://mattermost.org/webhooks'}
|
||||
</a>
|
||||
{' to learn more.'}
|
||||
<div><label className='control-label padding-top x2'>{'Add a new outgoing webhook'}</label></div>
|
||||
<FormattedHTMLMessage
|
||||
id='user.settings.hooks_out.addDescription'
|
||||
defaultMessage='Create webhooks to send new message events to an external integration. Please see <a href="http://mattermost.org/webhooks">http://mattermost.org/webhooks</a> to learn more.'
|
||||
/>
|
||||
<div><label className='control-label padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.addTitle'
|
||||
defaultMessage='Add a new outgoing webhook'
|
||||
/>
|
||||
</label></div>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='padding-top'>
|
||||
<div>
|
||||
<label className='control-label'>{'Channel'}</label>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.channel'
|
||||
defaultMessage='Channel: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<select
|
||||
ref='channelName'
|
||||
@@ -263,23 +310,43 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
{options}
|
||||
</select>
|
||||
</div>
|
||||
<div className='padding-top'>{'Only public channels can be used'}</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.only'
|
||||
defaultMessage='Only public channels can be used'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>{'Trigger Words:'}</label>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.trigger'
|
||||
defaultMessage='Trigger Words: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='triggerWords'
|
||||
className='form-control'
|
||||
value={this.state.triggerWords}
|
||||
onChange={this.updateTriggerWords}
|
||||
placeholder='Optional if channel selected'
|
||||
placeholder={this.props.intl.formatMessage(holders.optional)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.comma'
|
||||
defaultMessage='Comma separated words to trigger on'
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>{'Comma separated words to trigger on'}</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>{'Callback URLs:'}</label>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.callback'
|
||||
defaultMessage='Callback URLs: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<textarea
|
||||
ref='callbackURLs'
|
||||
@@ -288,10 +355,15 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
resize={false}
|
||||
rows={3}
|
||||
onChange={this.updateCallbackURLs}
|
||||
placeholder='Each URL must start with http:// or https://'
|
||||
placeholder={this.props.intl.formatMessage(holders.callbackHolder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>{'New line separated URLs that will receive the HTTP POST event'}</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.callbackDesc'
|
||||
defaultMessage='New line separated URLs that will receive the HTTP POST event'
|
||||
/>
|
||||
</div>
|
||||
{addError}
|
||||
</div>
|
||||
<div className='padding-top padding-bottom'>
|
||||
@@ -301,7 +373,10 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
disabled={disableButton}
|
||||
onClick={this.addNewHook}
|
||||
>
|
||||
{'Add'}
|
||||
<FormattedMessage
|
||||
id='user.settings.hooks_out.add'
|
||||
defaultMessage='Add'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -311,3 +386,9 @@ export default class ManageOutgoingHooks extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ManageOutgoingHooks.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(ManageOutgoingHooks);
|
||||
@@ -6,9 +6,55 @@ import SettingItemMin from '../setting_item_min.jsx';
|
||||
import SettingItemMax from '../setting_item_max.jsx';
|
||||
import Constants from '../../utils/constants.jsx';
|
||||
import PreferenceStore from '../../stores/preference_store.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
|
||||
|
||||
export default class AdvancedSettingsDisplay extends React.Component {
|
||||
const holders = defineMessages({
|
||||
sendTitle: {
|
||||
id: 'user.settings.advance.sendTitle',
|
||||
defaultMessage: 'Send messages on Ctrl + Enter'
|
||||
},
|
||||
on: {
|
||||
id: 'user.settings.advance.on',
|
||||
defaultMessage: 'On'
|
||||
},
|
||||
off: {
|
||||
id: 'user.settings.advance.off',
|
||||
defaultMessage: 'Off'
|
||||
},
|
||||
preReleaseTitle: {
|
||||
id: 'user.settings.advance.preReleaseTitle',
|
||||
defaultMessage: 'Preview pre-release features'
|
||||
},
|
||||
feature: {
|
||||
id: 'user.settings.advance.feature',
|
||||
defaultMessage: ' Feature '
|
||||
},
|
||||
features: {
|
||||
id: 'user.settings.advance.features',
|
||||
defaultMessage: ' Features '
|
||||
},
|
||||
enabled: {
|
||||
id: 'user.settings.advance.enabled',
|
||||
defaultMessage: 'enabled'
|
||||
},
|
||||
MARKDOWN_PREVIEW: {
|
||||
id: 'user.settings.advance.markdown_preview',
|
||||
defaultMessage: 'Show markdown preview option in message input box'
|
||||
},
|
||||
EMBED_PREVIEW: {
|
||||
id: 'user.settings.advance.embed_preview',
|
||||
defaultMessage: 'Show preview snippet of links below message'
|
||||
},
|
||||
LOC_PREVIEW: {
|
||||
id: 'user.settings.advance.loc_preview',
|
||||
defaultMessage: 'Show user language in display settings'
|
||||
}
|
||||
});
|
||||
|
||||
class AdvancedSettingsDisplay extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -104,6 +150,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
|
||||
render() {
|
||||
const serverError = this.state.serverError || null;
|
||||
const {formatMessage} = this.props.intl;
|
||||
let ctrlSendSection;
|
||||
|
||||
if (this.props.activeSection === 'advancedCtrlSend') {
|
||||
@@ -121,7 +168,10 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
checked={ctrlSendActive[0]}
|
||||
onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'true')}
|
||||
/>
|
||||
{'On'}
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -132,17 +182,26 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
checked={ctrlSendActive[1]}
|
||||
onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'false')}
|
||||
/>
|
||||
{'Off'}
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div><br/>{'If enabled \'Enter\' inserts a new line and \'Ctrl + Enter\' submits the message.'}</div>
|
||||
<div>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.sendDesc'
|
||||
defaultMessage="If enabled 'Enter' inserts a new line and 'Ctrl + Enter' submits the message."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
];
|
||||
|
||||
ctrlSendSection = (
|
||||
<SettingItemMax
|
||||
title='Send messages on Ctrl + Enter'
|
||||
title={formatMessage(holders.sendTitle)}
|
||||
inputs={inputs}
|
||||
submit={() => this.handleSubmit('send_on_ctrl_enter')}
|
||||
server_error={serverError}
|
||||
@@ -155,8 +214,8 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
} else {
|
||||
ctrlSendSection = (
|
||||
<SettingItemMin
|
||||
title='Send messages on Ctrl + Enter'
|
||||
describe={this.state.settings.send_on_ctrl_enter === 'true' ? 'On' : 'Off'}
|
||||
title={formatMessage(holders.sendTitle)}
|
||||
describe={this.state.settings.send_on_ctrl_enter === 'true' ? formatMessage(holders.on) : formatMessage(holders.off)}
|
||||
updateSection={() => this.props.updateSection('advancedCtrlSend')}
|
||||
/>
|
||||
);
|
||||
@@ -185,7 +244,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
this.toggleFeature(feature.label, e.target.checked);
|
||||
}}
|
||||
/>
|
||||
{feature.description}
|
||||
{formatMessage({id: 'user.settings.advance.' + feature.label})}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -195,13 +254,16 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
inputs.push(
|
||||
<div key='advancedPreviewFeatures_helptext'>
|
||||
<br/>
|
||||
{'Check any pre-released features you\'d like to preview. You may also need to refresh the page before the setting will take effect.'}
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.preReleaseDesc'
|
||||
defaultMessage="Check any pre-released features you'd like to preview. You may also need to refresh the page before the setting will take effect."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
previewFeaturesSection = (
|
||||
<SettingItemMax
|
||||
title='Preview pre-release features'
|
||||
title={formatMessage(holders.preReleaseTitle)}
|
||||
inputs={inputs}
|
||||
submit={this.saveEnabledFeatures}
|
||||
server_error={serverError}
|
||||
@@ -214,8 +276,8 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
} else {
|
||||
previewFeaturesSection = (
|
||||
<SettingItemMin
|
||||
title='Preview pre-release features'
|
||||
describe={this.state.enabledFeatures + (this.state.enabledFeatures === 1 ? ' Feature ' : ' Features ') + 'enabled'}
|
||||
title={formatMessage(holders.preReleaseTitle)}
|
||||
describe={this.state.enabledFeatures + (this.state.enabledFeatures === 1 ? formatMessage(holders.feature) : formatMessage(holders.features)) + formatMessage(holders.enabled)}
|
||||
updateSection={() => this.props.updateSection('advancedPreviewFeatures')}
|
||||
/>
|
||||
);
|
||||
@@ -242,11 +304,19 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Advanced Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.title'
|
||||
defaultMessage='Advanced Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Advanced Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.advance.title'
|
||||
defaultMessage='Advanced Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{ctrlSendSection}
|
||||
{previewFeaturesSectionDivider}
|
||||
@@ -259,6 +329,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
|
||||
}
|
||||
|
||||
AdvancedSettingsDisplay.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
@@ -266,3 +337,5 @@ AdvancedSettingsDisplay.propTypes = {
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(AdvancedSettingsDisplay);
|
||||
@@ -11,6 +11,9 @@ import * as Client from '../../utils/client.jsx';
|
||||
import * as Utils from '../../utils/utils.jsx';
|
||||
|
||||
import Constants from '../../utils/constants.jsx';
|
||||
|
||||
import {FormattedMessage} from 'mm-intl';
|
||||
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
|
||||
export default class UserSettingsAppearance extends React.Component {
|
||||
@@ -180,7 +183,10 @@ export default class UserSettingsAppearance extends React.Component {
|
||||
checked={!displayCustom}
|
||||
onChange={this.updateType.bind(this, 'premade')}
|
||||
/>
|
||||
{'Theme Colors'}
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.themeColors'
|
||||
defaultMessage='Theme Colors'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -191,7 +197,10 @@ export default class UserSettingsAppearance extends React.Component {
|
||||
checked={displayCustom}
|
||||
onChange={this.updateType.bind(this, 'custom')}
|
||||
/>
|
||||
{'Custom Theme'}
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.customTheme'
|
||||
defaultMessage='Custom Theme'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -203,14 +212,20 @@ export default class UserSettingsAppearance extends React.Component {
|
||||
href='#'
|
||||
onClick={this.submitTheme}
|
||||
>
|
||||
{'Save'}
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.save'
|
||||
defaultMessage='Save'
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className='btn btn-sm theme'
|
||||
href='#'
|
||||
onClick={this.resetFields}
|
||||
>
|
||||
{'Cancel'}
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.cancel'
|
||||
defaultMessage='Cancel'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,11 +250,19 @@ export default class UserSettingsAppearance extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Appearance Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.title'
|
||||
defaultMessage='Appearance Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Appearance Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.title'
|
||||
defaultMessage='Appearance Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{themeUI}
|
||||
<div className='divider-dark'/>
|
||||
@@ -248,7 +271,10 @@ export default class UserSettingsAppearance extends React.Component {
|
||||
className='theme'
|
||||
onClick={this.handleImportModal}
|
||||
>
|
||||
{'Import theme colors from Slack'}
|
||||
<FormattedMessage
|
||||
id='user.settings.appearance.import'
|
||||
defaultMessage='Import theme colors from Slack'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,20 @@ import SettingItemMin from '../setting_item_min.jsx';
|
||||
import SettingItemMax from '../setting_item_max.jsx';
|
||||
import * as EventHelpers from '../../dispatcher/event_helpers.jsx';
|
||||
|
||||
export default class DeveloperTab extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
applicationsPreview: {
|
||||
id: 'user.settings.developer.applicationsPreview',
|
||||
defaultMessage: 'Applications (Preview)'
|
||||
},
|
||||
thirdParty: {
|
||||
id: 'user.settings.developer.thirdParty',
|
||||
defaultMessage: 'Open to register a new third-party application'
|
||||
}
|
||||
});
|
||||
|
||||
class DeveloperTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -20,6 +33,7 @@ export default class DeveloperTab extends React.Component {
|
||||
render() {
|
||||
var appSection;
|
||||
var self = this;
|
||||
const {formatMessage} = this.props.intl;
|
||||
if (this.props.activeSection === 'app') {
|
||||
var inputs = [];
|
||||
|
||||
@@ -33,7 +47,10 @@ export default class DeveloperTab extends React.Component {
|
||||
className='btn btn-sm btn-primary'
|
||||
onClick={this.register}
|
||||
>
|
||||
{'Register New Application'}
|
||||
<FormattedMessage
|
||||
id='user.settings.developer.register'
|
||||
defaultMessage='Register New Application'
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,7 +58,7 @@ export default class DeveloperTab extends React.Component {
|
||||
|
||||
appSection = (
|
||||
<SettingItemMax
|
||||
title='Applications (Preview)'
|
||||
title={formatMessage(holders.applicationsPreview)}
|
||||
inputs={inputs}
|
||||
updateSection={function updateSection(e) {
|
||||
self.props.updateSection('');
|
||||
@@ -52,8 +69,8 @@ export default class DeveloperTab extends React.Component {
|
||||
} else {
|
||||
appSection = (
|
||||
<SettingItemMin
|
||||
title='Applications (Preview)'
|
||||
describe='Open to register a new third-party application'
|
||||
title={formatMessage(holders.applicationsPreview)}
|
||||
describe={formatMessage(holders.thirdParty)}
|
||||
updateSection={function updateSection() {
|
||||
self.props.updateSection('app');
|
||||
}}
|
||||
@@ -81,11 +98,19 @@ export default class DeveloperTab extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Developer Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.developer.title'
|
||||
defaultMessage='Developer Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Developer Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.developer.title'
|
||||
defaultMessage='Developer Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{appSection}
|
||||
<div className='divider-dark'/>
|
||||
@@ -99,8 +124,11 @@ DeveloperTab.defaultProps = {
|
||||
activeSection: ''
|
||||
};
|
||||
DeveloperTab.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
activeSection: React.PropTypes.string,
|
||||
updateSection: React.PropTypes.func,
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(DeveloperTab);
|
||||
@@ -10,6 +10,47 @@ import PreferenceStore from '../../stores/preference_store.jsx';
|
||||
import ManageLanguages from './manage_languages.jsx';
|
||||
import * as Utils from '../../utils/utils.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
normalClock: {
|
||||
id: 'user.settings.display.normalClock',
|
||||
defaultMessage: '12-hour clock (example: 4:00 PM)'
|
||||
},
|
||||
militaryClock: {
|
||||
id: 'user.settings.display.militaryClock',
|
||||
defaultMessage: '24-hour clock (example: 16:00)'
|
||||
},
|
||||
clockDisplay: {
|
||||
id: 'user.settings.display.clockDisplay',
|
||||
defaultMessage: 'Clock Display'
|
||||
},
|
||||
teammateDisplay: {
|
||||
id: 'user.settings.display.teammateDisplay',
|
||||
defaultMessage: 'Teammate Name Display'
|
||||
},
|
||||
showNickname: {
|
||||
id: 'user.settings.display.showNickname',
|
||||
defaultMessage: 'Show nickname if one exists, otherwise show first and last name'
|
||||
},
|
||||
showUsername: {
|
||||
id: 'user.settings.display.showUsername',
|
||||
defaultMessage: 'Show username (team default)'
|
||||
},
|
||||
showFullname: {
|
||||
id: 'user.settings.display.showFullname',
|
||||
defaultMessage: 'Show first and last name'
|
||||
},
|
||||
fontTitle: {
|
||||
id: 'user.settings.display.fontTitle',
|
||||
defaultMessage: 'Display Font'
|
||||
},
|
||||
language: {
|
||||
id: 'user.settings.display.language',
|
||||
defaultMessage: 'Language'
|
||||
}
|
||||
});
|
||||
|
||||
function getDisplayStateFromStores() {
|
||||
const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'});
|
||||
const nameFormat = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', {value: 'username'});
|
||||
@@ -22,7 +63,7 @@ function getDisplayStateFromStores() {
|
||||
};
|
||||
}
|
||||
|
||||
export default class UserSettingsDisplay extends React.Component {
|
||||
class UserSettingsDisplay extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -76,6 +117,7 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
this.updateState();
|
||||
}
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const serverError = this.state.serverError || null;
|
||||
let clockSection;
|
||||
let nameFormatSection;
|
||||
@@ -104,7 +146,10 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
checked={clockFormat[0]}
|
||||
onChange={this.handleClockRadio.bind(this, 'false')}
|
||||
/>
|
||||
{'12-hour clock (example: 4:00 PM)'}
|
||||
<FormattedMessage
|
||||
id='user.settings.display.normalClock'
|
||||
defaultMessage='12-hour clock (example: 4:00 PM)'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -115,17 +160,26 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
checked={clockFormat[1]}
|
||||
onChange={this.handleClockRadio.bind(this, 'true')}
|
||||
/>
|
||||
{'24-hour clock (example: 16:00)'}
|
||||
<FormattedMessage
|
||||
id='user.settings.display.militaryClock'
|
||||
defaultMessage='24-hour clock (example: 16:00)'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div><br/>{'Select how you prefer time displayed.'}</div>
|
||||
<div>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.display.preferTime'
|
||||
defaultMessage='Select how you prefer time displayed.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
];
|
||||
|
||||
clockSection = (
|
||||
<SettingItemMax
|
||||
title='Clock Display'
|
||||
title={formatMessage(holders.clockDisplay)}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
@@ -135,9 +189,9 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
} else {
|
||||
let describe = '';
|
||||
if (this.state.militaryTime === 'true') {
|
||||
describe = '24-hour clock (example: 16:00)';
|
||||
describe = formatMessage(holders.militaryClock);
|
||||
} else {
|
||||
describe = '12-hour clock (example: 4:00 PM)';
|
||||
describe = formatMessage(holders.normalClock);
|
||||
}
|
||||
|
||||
const handleUpdateClockSection = () => {
|
||||
@@ -146,16 +200,31 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
|
||||
clockSection = (
|
||||
<SettingItemMin
|
||||
title='Clock Display'
|
||||
title={formatMessage(holders.clockDisplay)}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateClockSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const showUsername = 'Show username (team default)';
|
||||
const showNickname = 'Show nickname if one exists, otherwise show first and last name';
|
||||
const showFullName = 'Show first and last name';
|
||||
const showUsername = (
|
||||
<FormattedMessage
|
||||
id='user.settings.display.showUsername'
|
||||
defaultMessage='Show username (team default)'
|
||||
/>
|
||||
);
|
||||
const showNickname = (
|
||||
<FormattedMessage
|
||||
id='user.settings.display.showNickname'
|
||||
defaultMessage='Show nickname if one exists, otherwise show first and last name'
|
||||
/>
|
||||
);
|
||||
const showFullName = (
|
||||
<FormattedMessage
|
||||
id='user.settings.display.showFullname'
|
||||
defaultMessage='Show first and last name'
|
||||
/>
|
||||
);
|
||||
if (this.props.activeSection === 'name_format') {
|
||||
const nameFormat = [false, false, false];
|
||||
if (this.state.nameFormat === 'nickname_full_name') {
|
||||
@@ -201,13 +270,19 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div><br/>{'Set how to display other user\'s names in posts and the Direct Messages list.'}</div>
|
||||
<div>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.display.nameOptsDesc'
|
||||
defaultMessage="Set how to display other user's names in posts and the Direct Messages list."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
];
|
||||
|
||||
nameFormatSection = (
|
||||
<SettingItemMax
|
||||
title='Teammate Name Display'
|
||||
title={formatMessage(holders.teammateDisplay)}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
@@ -220,16 +295,16 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
} else {
|
||||
let describe = '';
|
||||
if (this.state.nameFormat === 'username') {
|
||||
describe = showUsername;
|
||||
describe = formatMessage(holders.showUsername);
|
||||
} else if (this.state.nameFormat === 'full_name') {
|
||||
describe = showFullName;
|
||||
describe = formatMessage(holders.showFullName);
|
||||
} else {
|
||||
describe = showNickname;
|
||||
describe = formatMessage(holders.showNickname);
|
||||
}
|
||||
|
||||
nameFormatSection = (
|
||||
<SettingItemMin
|
||||
title='Teammate Name Display'
|
||||
title={formatMessage(holders.teammateDisplay)}
|
||||
describe={describe}
|
||||
updateSection={() => {
|
||||
this.props.updateSection('name_format');
|
||||
@@ -267,13 +342,19 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
{options}
|
||||
</select>
|
||||
</div>
|
||||
<div><br/>{'Select the font displayed in the Mattermost user interface.'}</div>
|
||||
<div>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.display.fontDesc'
|
||||
defaultMessage='Select the font displayed in the Mattermost user interface.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
];
|
||||
|
||||
fontSection = (
|
||||
<SettingItemMax
|
||||
title='Display Font'
|
||||
title={formatMessage(holders.fontTitle)}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
@@ -286,7 +367,7 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
} else {
|
||||
fontSection = (
|
||||
<SettingItemMin
|
||||
title='Display Font'
|
||||
title={formatMessage(holders.fontTitle)}
|
||||
describe={this.state.selectedFont}
|
||||
updateSection={() => {
|
||||
this.props.updateSection('font');
|
||||
@@ -307,7 +388,7 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
|
||||
languagesSection = (
|
||||
<SettingItemMax
|
||||
title={'Language'}
|
||||
title={formatMessage(holders.language)}
|
||||
width='medium'
|
||||
inputs={inputs}
|
||||
updateSection={(e) => {
|
||||
@@ -326,7 +407,7 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
|
||||
languagesSection = (
|
||||
<SettingItemMin
|
||||
title={'Language'}
|
||||
title={formatMessage(holders.language)}
|
||||
width='medium'
|
||||
describe={locale}
|
||||
updateSection={() => {
|
||||
@@ -357,11 +438,19 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Display Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.display.title'
|
||||
defaultMessage='Display Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Display Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.display.title'
|
||||
defaultMessage='Display Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{fontSection}
|
||||
<div className='divider-dark'/>
|
||||
@@ -377,6 +466,7 @@ export default class UserSettingsDisplay extends React.Component {
|
||||
}
|
||||
|
||||
UserSettingsDisplay.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
@@ -384,3 +474,5 @@ UserSettingsDisplay.propTypes = {
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(UserSettingsDisplay);
|
||||
@@ -13,7 +13,84 @@ import Constants from '../../utils/constants.jsx';
|
||||
import * as AsyncClient from '../../utils/async_client.jsx';
|
||||
import * as Utils from '../../utils/utils.jsx';
|
||||
|
||||
export default class UserSettingsGeneralTab extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
usernameReserved: {
|
||||
id: 'user.settings.general.usernameReserved',
|
||||
defaultMessage: 'This username is reserved, please choose a new one.'
|
||||
},
|
||||
usernameRestrictions: {
|
||||
id: 'user.settings.general.usernameRestrictions',
|
||||
defaultMessage: "'Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."
|
||||
},
|
||||
validEmail: {
|
||||
id: 'user.settings.general.validEmail',
|
||||
defaultMessage: 'Please enter a valid email address'
|
||||
},
|
||||
emailMatch: {
|
||||
id: 'user.settings.general.emailMatch',
|
||||
defaultMessage: 'The new emails you entered do not match.'
|
||||
},
|
||||
checkEmail: {
|
||||
id: 'user.settings.general.checkEmail',
|
||||
defaultMessage: 'Check your email at {email} to verify the address.'
|
||||
},
|
||||
newAddress: {
|
||||
id: 'user.settings.general.newAddress',
|
||||
defaultMessage: 'New Address: {email}<br />Check your email to verify the above address.'
|
||||
},
|
||||
checkEmailNoAddress: {
|
||||
id: 'user.settings.general.checkEmailNoAddress',
|
||||
defaultMessage: 'Check your email to verify your new address'
|
||||
},
|
||||
loginGitlab: {
|
||||
id: 'user.settings.general.loginGitlab',
|
||||
defaultMessage: 'Log in done through GitLab'
|
||||
},
|
||||
validImage: {
|
||||
id: 'user.settings.general.validImage',
|
||||
defaultMessage: 'Only JPG or PNG images may be used for profile pictures'
|
||||
},
|
||||
imageTooLarge: {
|
||||
id: 'user.settings.general.imageTooLarge',
|
||||
defaultMessage: 'Unable to upload profile image. File is too large.'
|
||||
},
|
||||
uploadImage: {
|
||||
id: 'user.settings.general.uploadImage',
|
||||
defaultMessage: "Click 'Edit' to upload an image."
|
||||
},
|
||||
imageUpdated: {
|
||||
id: 'user.settings.general.imageUpdated',
|
||||
defaultMessage: 'Image last updated {date}'
|
||||
},
|
||||
fullName: {
|
||||
id: 'user.settings.general.fullName',
|
||||
defaultMessage: 'Full Name'
|
||||
},
|
||||
nickname: {
|
||||
id: 'user.settings.general.nickname',
|
||||
defaultMessage: 'Nickname'
|
||||
},
|
||||
username: {
|
||||
id: 'user.settings.general.username',
|
||||
defaultMessage: 'Username'
|
||||
},
|
||||
email: {
|
||||
id: 'user.settings.general.email',
|
||||
defaultMessage: 'Email'
|
||||
},
|
||||
profilePicture: {
|
||||
id: 'user.settings.general.profilePicture',
|
||||
defaultMessage: 'Profile Picture'
|
||||
},
|
||||
close: {
|
||||
id: 'user.settings.general.close',
|
||||
defaultMessage: 'Close'
|
||||
}
|
||||
});
|
||||
|
||||
class UserSettingsGeneralTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.submitActive = false;
|
||||
@@ -42,12 +119,13 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
const user = Object.assign({}, this.props.user);
|
||||
const username = this.state.username.trim().toLowerCase();
|
||||
|
||||
const {formatMessage} = this.props.intl;
|
||||
const usernameError = Utils.isValidUsername(username);
|
||||
if (usernameError === 'Cannot use a reserved word as a username.') {
|
||||
this.setState({clientError: 'This username is reserved, please choose a new one.'});
|
||||
this.setState({clientError: formatMessage(holders.usernameReserved)});
|
||||
return;
|
||||
} else if (usernameError) {
|
||||
this.setState({clientError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
|
||||
this.setState({clientError: formatMessage(holders.usernameRestrictions, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -99,13 +177,14 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
const email = this.state.email.trim().toLowerCase();
|
||||
const confirmEmail = this.state.confirmEmail.trim().toLowerCase();
|
||||
|
||||
const {formatMessage} = this.props.intl;
|
||||
if (email === '' || !Utils.isEmail(email)) {
|
||||
this.setState({emailError: 'Please enter a valid email address.', clientError: '', serverError: ''});
|
||||
this.setState({emailError: formatMessage(holders.validEmail), clientError: '', serverError: ''});
|
||||
return;
|
||||
}
|
||||
|
||||
if (email !== confirmEmail) {
|
||||
this.setState({emailError: 'The new emails you entered do not match.', clientError: '', serverError: ''});
|
||||
this.setState({emailError: formatMessage(holders.emailMatch), clientError: '', serverError: ''});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +204,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
const verificationEnabled = global.window.mm_config.SendEmailNotifications === 'true' && global.window.mm_config.RequireEmailVerification === 'true' && emailUpdated;
|
||||
|
||||
if (verificationEnabled) {
|
||||
ErrorStore.storeLastError({message: 'Check your email at ' + user.email + ' to verify the address.'});
|
||||
ErrorStore.storeLastError({message: this.props.intl.formatMessage(holders.checkEmail, {email: user.email})});
|
||||
ErrorStore.emitChange();
|
||||
this.setState({emailChangeInProgress: true});
|
||||
}
|
||||
@@ -152,13 +231,14 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const {formatMessage} = this.props.intl;
|
||||
const picture = this.state.picture;
|
||||
|
||||
if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') {
|
||||
this.setState({clientError: 'Only JPG or PNG images may be used for profile pictures.'});
|
||||
this.setState({clientError: formatMessage(holders.validImage)});
|
||||
return;
|
||||
} else if (picture.size > Constants.MAX_FILE_SIZE) {
|
||||
this.setState({clientError: 'Unable to upload profile image. File is too large.'});
|
||||
this.setState({clientError: formatMessage(holders.imageTooLarge)});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -221,6 +301,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
}
|
||||
render() {
|
||||
const user = this.props.user;
|
||||
const {formatMessage, formatHTMLMessage} = this.props.intl;
|
||||
|
||||
let clientError = null;
|
||||
if (this.state.clientError) {
|
||||
@@ -244,7 +325,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
key='firstNameSetting'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>{'First Name'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.firstName'
|
||||
defaultMessage='First Name'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -261,7 +347,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
key='lastNameSetting'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>{'Last Name'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.lastName'
|
||||
defaultMessage='Last Name'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -284,20 +375,28 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
href='#'
|
||||
onClick={notifClick.bind(this)}
|
||||
>
|
||||
{'Notifications'}
|
||||
<FormattedMessage
|
||||
id='user.settings.general.notificationsLink'
|
||||
defaultMessage='Notifications'
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
|
||||
const extraInfo = (
|
||||
<span>
|
||||
{'By default, you will receive mention notifications when someone types your first name. '}
|
||||
{'Go to '} {notifLink} {'settings to change this default.'}
|
||||
<FormattedMessage
|
||||
id='user.settings.general.notificationsExtra'
|
||||
defaultMessage='By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.'
|
||||
values={{
|
||||
notify: (notifLink)
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
nameSection = (
|
||||
<SettingItemMax
|
||||
title='Full Name'
|
||||
title={formatMessage(holders.fullName)}
|
||||
inputs={inputs}
|
||||
submit={this.submitName}
|
||||
server_error={serverError}
|
||||
@@ -322,7 +421,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
|
||||
nameSection = (
|
||||
<SettingItemMin
|
||||
title='Full Name'
|
||||
title={formatMessage(holders.fullName)}
|
||||
describe={fullName}
|
||||
updateSection={() => {
|
||||
this.updateSection('name');
|
||||
@@ -333,7 +432,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
|
||||
let nicknameSection;
|
||||
if (this.props.activeSection === 'nickname') {
|
||||
let nicknameLabel = 'Nickname';
|
||||
let nicknameLabel = (
|
||||
<FormattedMessage
|
||||
id='user.settings.general.nickname'
|
||||
defaultMessage='Nickname'
|
||||
/>
|
||||
);
|
||||
if (Utils.isMobile()) {
|
||||
nicknameLabel = '';
|
||||
}
|
||||
@@ -357,13 +461,16 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
|
||||
const extraInfo = (
|
||||
<span>
|
||||
{'Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'}
|
||||
<FormattedMessage
|
||||
id='user.settings.general.nicknameExtra'
|
||||
defaultMessage='Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
nicknameSection = (
|
||||
<SettingItemMax
|
||||
title='Nickname'
|
||||
title={formatMessage(holders.nickname)}
|
||||
inputs={inputs}
|
||||
submit={this.submitNickname}
|
||||
server_error={serverError}
|
||||
@@ -378,7 +485,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
} else {
|
||||
nicknameSection = (
|
||||
<SettingItemMin
|
||||
title='Nickname'
|
||||
title={formatMessage(holders.nickname)}
|
||||
describe={UserStore.getCurrentUser().nickname}
|
||||
updateSection={() => {
|
||||
this.updateSection('nickname');
|
||||
@@ -389,7 +496,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
|
||||
let usernameSection;
|
||||
if (this.props.activeSection === 'username') {
|
||||
let usernameLabel = 'Username';
|
||||
let usernameLabel = (
|
||||
<FormattedMessage
|
||||
id='user.settings.general.username'
|
||||
defaultMessage='Username'
|
||||
/>
|
||||
);
|
||||
if (Utils.isMobile()) {
|
||||
usernameLabel = '';
|
||||
}
|
||||
@@ -411,11 +523,18 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraInfo = (<span>{'Pick something easy for teammates to recognize and recall.'}</span>);
|
||||
const extraInfo = (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.usernameInfo'
|
||||
defaultMessage='Pick something easy for teammates to recognize and recall.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
usernameSection = (
|
||||
<SettingItemMax
|
||||
title='Username'
|
||||
title={formatMessage(holders.username)}
|
||||
inputs={inputs}
|
||||
submit={this.submitUsername}
|
||||
server_error={serverError}
|
||||
@@ -430,7 +549,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
} else {
|
||||
usernameSection = (
|
||||
<SettingItemMin
|
||||
title='Username'
|
||||
title={formatMessage(holders.username)}
|
||||
describe={UserStore.getCurrentUser().username}
|
||||
updateSection={() => {
|
||||
this.updateSection('username');
|
||||
@@ -443,16 +562,41 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
if (this.props.activeSection === 'email') {
|
||||
const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
|
||||
const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
|
||||
let helpText = 'Email is used for sign-in, notifications, and password reset. Email requires verification if changed.';
|
||||
let helpText = (
|
||||
<FormattedMessage
|
||||
id='user.settings.general.emailHelp1'
|
||||
defaultMessage='Email is used for sign-in, notifications, and password reset. Email requires verification if changed.'
|
||||
/>
|
||||
);
|
||||
|
||||
if (!emailEnabled) {
|
||||
helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
|
||||
helpText = (
|
||||
<div className='setting-list__hint text-danger'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.emailHelp2'
|
||||
defaultMessage='Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (!emailVerificationEnabled) {
|
||||
helpText = 'Email is used for sign-in, notifications, and password reset.';
|
||||
helpText = (
|
||||
<FormattedMessage
|
||||
id='user.settings.general.emailHelp3'
|
||||
defaultMessage='Email is used for sign-in, notifications, and password reset.'
|
||||
/>
|
||||
);
|
||||
} else if (this.state.emailChangeInProgress) {
|
||||
const newEmail = UserStore.getCurrentUser().email;
|
||||
if (newEmail) {
|
||||
helpText = 'A verification email was sent to ' + newEmail + '.';
|
||||
helpText = (
|
||||
<FormattedMessage
|
||||
id='user.settings.general.emailHelp4'
|
||||
defaultMessage='A verification email was sent to {email}.'
|
||||
values={{
|
||||
email: newEmail
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +606,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
inputs.push(
|
||||
<div key='emailSetting'>
|
||||
<div className='form-group'>
|
||||
<label className='col-sm-5 control-label'>{'Primary Email'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.primaryEmail'
|
||||
defaultMessage='Primary Email'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -478,7 +627,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
inputs.push(
|
||||
<div key='confirmEmailSetting'>
|
||||
<div className='form-group'>
|
||||
<label className='col-sm-5 control-label'>{'Confirm Email'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.confirmEmail'
|
||||
defaultMessage='Confirm Email'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -499,7 +653,12 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
key='oauthEmailInfo'
|
||||
className='form-group'
|
||||
>
|
||||
<div className='setting-list__hint'>{'Log in occurs through GitLab. Email cannot be updated.'}</div>
|
||||
<div className='setting-list__hint'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.emailCantUpdate'
|
||||
defaultMessage='Log in occurs through GitLab. Email cannot be updated.'
|
||||
/>
|
||||
</div>
|
||||
{helpText}
|
||||
</div>
|
||||
);
|
||||
@@ -524,20 +683,20 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
if (this.state.emailChangeInProgress) {
|
||||
const newEmail = UserStore.getCurrentUser().email;
|
||||
if (newEmail) {
|
||||
describe = 'New Address: ' + newEmail + '\nCheck your email to verify the above address.';
|
||||
describe = formatHTMLMessage(holders.newAddress, {email: newEmail});
|
||||
} else {
|
||||
describe = 'Check your email to verify your new address';
|
||||
describe = formatMessage(holders.checkEmailNoAddress);
|
||||
}
|
||||
} else {
|
||||
describe = UserStore.getCurrentUser().email;
|
||||
}
|
||||
} else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
|
||||
describe = 'Log in done through GitLab';
|
||||
describe = formatMessage(holders.loginGitlab);
|
||||
}
|
||||
|
||||
emailSection = (
|
||||
<SettingItemMin
|
||||
title='Email'
|
||||
title={formatMessage(holders.email)}
|
||||
describe={describe}
|
||||
updateSection={() => {
|
||||
this.updateSection('email');
|
||||
@@ -550,7 +709,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
if (this.props.activeSection === 'picture') {
|
||||
pictureSection = (
|
||||
<SettingPicture
|
||||
title='Profile Picture'
|
||||
title={formatMessage(holders.profilePicture)}
|
||||
submit={this.submitPicture}
|
||||
src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + Utils.getSessionIndex()}
|
||||
server_error={serverError}
|
||||
@@ -566,13 +725,15 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
let minMessage = 'Click \'Edit\' to upload an image.';
|
||||
let minMessage = formatMessage(holders.uploadImage);
|
||||
if (user.last_picture_update) {
|
||||
minMessage = 'Image last updated ' + Utils.displayDate(user.last_picture_update);
|
||||
minMessage = formatMessage(holders.imageUpdated, {
|
||||
date: new Date(user.last_picture_update).toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'})
|
||||
});
|
||||
}
|
||||
pictureSection = (
|
||||
<SettingItemMin
|
||||
title='Profile Picture'
|
||||
title={formatMessage(holders.profilePicture)}
|
||||
describe={minMessage}
|
||||
updateSection={() => {
|
||||
this.updateSection('picture');
|
||||
@@ -588,7 +749,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
aria-label={formatMessage(holders.close)}
|
||||
onClick={this.props.closeModal}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
@@ -601,11 +762,19 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'General Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.general.title'
|
||||
defaultMessage='General Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'General Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.general.title'
|
||||
defaultMessage='General Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{nameSection}
|
||||
<div className='divider-light'/>
|
||||
@@ -624,6 +793,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
}
|
||||
|
||||
UserSettingsGeneralTab.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object.isRequired,
|
||||
updateSection: React.PropTypes.func.isRequired,
|
||||
updateTab: React.PropTypes.func.isRequired,
|
||||
@@ -631,3 +801,5 @@ UserSettingsGeneralTab.propTypes = {
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(UserSettingsGeneralTab);
|
||||
@@ -7,7 +7,28 @@ import ManageIncomingHooks from './manage_incoming_hooks.jsx';
|
||||
import ManageOutgoingHooks from './manage_outgoing_hooks.jsx';
|
||||
import ManageCommandHooks from './manage_command_hooks.jsx';
|
||||
|
||||
export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
inName: {
|
||||
id: 'user.settings.integrations.incomingWebhooks',
|
||||
defaultMessage: 'Incoming Webhooks'
|
||||
},
|
||||
inDesc: {
|
||||
id: 'user.settings.integrations.incomingWebhooksDescription',
|
||||
defaultMessage: 'Manage your incoming webhooks'
|
||||
},
|
||||
outName: {
|
||||
id: 'user.settings.integrations.outWebhooks',
|
||||
defaultMessage: 'Outgoing Webhooks'
|
||||
},
|
||||
outDesc: {
|
||||
id: 'user.settings.integrations.outWebhooksDescription',
|
||||
defaultMessage: 'Manage your outgoing webhooks'
|
||||
}
|
||||
});
|
||||
|
||||
class UserSettingsIntegrationsTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -23,6 +44,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
let outgoingHooksSection;
|
||||
let commandHooksSection;
|
||||
var inputs = [];
|
||||
const {formatMessage} = this.props.intl;
|
||||
|
||||
if (global.window.mm_config.EnableIncomingWebhooks === 'true') {
|
||||
if (this.props.activeSection === 'incoming-hooks') {
|
||||
@@ -32,7 +54,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
|
||||
incomingHooksSection = (
|
||||
<SettingItemMax
|
||||
title='Incoming Webhooks'
|
||||
title={formatMessage(holders.inName)}
|
||||
width='medium'
|
||||
inputs={inputs}
|
||||
updateSection={(e) => {
|
||||
@@ -44,9 +66,9 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
} else {
|
||||
incomingHooksSection = (
|
||||
<SettingItemMin
|
||||
title='Incoming Webhooks'
|
||||
title={formatMessage(holders.inName)}
|
||||
width='medium'
|
||||
describe='Manage your incoming webhooks'
|
||||
describe={formatMessage(holders.inDesc)}
|
||||
updateSection={() => {
|
||||
this.updateSection('incoming-hooks');
|
||||
}}
|
||||
@@ -63,7 +85,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
|
||||
outgoingHooksSection = (
|
||||
<SettingItemMax
|
||||
title='Outgoing Webhooks'
|
||||
title={formatMessage(holders.outName)}
|
||||
width='medium'
|
||||
inputs={inputs}
|
||||
updateSection={(e) => {
|
||||
@@ -75,9 +97,9 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
} else {
|
||||
outgoingHooksSection = (
|
||||
<SettingItemMin
|
||||
title='Outgoing Webhooks'
|
||||
title={formatMessage(holders.outName)}
|
||||
width='medium'
|
||||
describe='Manage your outgoing webhooks'
|
||||
describe={formatMessage(holders.outDesc)}
|
||||
updateSection={() => {
|
||||
this.updateSection('outgoing-hooks');
|
||||
}}
|
||||
@@ -137,11 +159,19 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Integration Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.integrations.title'
|
||||
defaultMessage='Integration Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Integration Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.integrations.title'
|
||||
defaultMessage='Integration Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{incomingHooksSection}
|
||||
<div className='divider-light'/>
|
||||
@@ -156,6 +186,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
|
||||
}
|
||||
|
||||
UserSettingsIntegrationsTab.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
@@ -163,3 +194,5 @@ UserSettingsIntegrationsTab.propTypes = {
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(UserSettingsIntegrationsTab);
|
||||
@@ -6,7 +6,56 @@ const Modal = ReactBootstrap.Modal;
|
||||
import SettingsSidebar from '../settings_sidebar.jsx';
|
||||
import UserSettings from './user_settings.jsx';
|
||||
|
||||
export default class UserSettingsModal extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
general: {
|
||||
id: 'user.settings.modal.general',
|
||||
defaultMessage: 'General'
|
||||
},
|
||||
security: {
|
||||
id: 'user.settings.modal.security',
|
||||
defaultMessage: 'Security'
|
||||
},
|
||||
notifications: {
|
||||
id: 'user.settings.modal.notifications',
|
||||
defaultMessage: 'Notifications'
|
||||
},
|
||||
appearance: {
|
||||
id: 'user.settings.modal.appearance',
|
||||
defaultMessage: 'Appearance'
|
||||
},
|
||||
developer: {
|
||||
id: 'user.settings.modal.developer',
|
||||
defaultMessage: 'Developer'
|
||||
},
|
||||
integrations: {
|
||||
id: 'user.settings.modal.integrations',
|
||||
defaultMessage: 'Integrations'
|
||||
},
|
||||
display: {
|
||||
id: 'user.settings.modal.display',
|
||||
defaultMessage: 'Display'
|
||||
},
|
||||
advanced: {
|
||||
id: 'user.settings.modal.advanced',
|
||||
defaultMessage: 'Advanced'
|
||||
},
|
||||
confirmTitle: {
|
||||
id: 'user.settings.modal.confirmTitle',
|
||||
defaultMessage: 'Discard Changes?'
|
||||
},
|
||||
confirmMsg: {
|
||||
id: 'user.settings.modal.confirmMsg',
|
||||
defaultMessage: 'You have unsaved changes, are you sure you want to discard them?'
|
||||
},
|
||||
confirmBtns: {
|
||||
id: 'user.settings.modal.confirmBtns',
|
||||
defaultMessage: 'Yes, Discard'
|
||||
}
|
||||
});
|
||||
|
||||
class UserSettingsModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -170,20 +219,21 @@ export default class UserSettingsModal extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
var tabs = [];
|
||||
tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
|
||||
tabs.push({name: 'security', uiName: 'Security', icon: 'glyphicon glyphicon-lock'});
|
||||
tabs.push({name: 'notifications', uiName: 'Notifications', icon: 'glyphicon glyphicon-exclamation-sign'});
|
||||
tabs.push({name: 'appearance', uiName: 'Appearance', icon: 'glyphicon glyphicon-wrench'});
|
||||
tabs.push({name: 'general', uiName: formatMessage(holders.general), icon: 'glyphicon glyphicon-cog'});
|
||||
tabs.push({name: 'security', uiName: formatMessage(holders.security), icon: 'glyphicon glyphicon-lock'});
|
||||
tabs.push({name: 'notifications', uiName: formatMessage(holders.notifications), icon: 'glyphicon glyphicon-exclamation-sign'});
|
||||
tabs.push({name: 'appearance', uiName: formatMessage(holders.appearance), icon: 'glyphicon glyphicon-wrench'});
|
||||
if (global.window.mm_config.EnableOAuthServiceProvider === 'true') {
|
||||
tabs.push({name: 'developer', uiName: 'Developer', icon: 'glyphicon glyphicon-th'});
|
||||
tabs.push({name: 'developer', uiName: formatMessage(holders.developer), icon: 'glyphicon glyphicon-th'});
|
||||
}
|
||||
|
||||
if (global.window.mm_config.EnableIncomingWebhooks === 'true' || global.window.mm_config.EnableOutgoingWebhooks === 'true') {
|
||||
tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'});
|
||||
tabs.push({name: 'integrations', uiName: formatMessage(holders.integrations), icon: 'glyphicon glyphicon-transfer'});
|
||||
}
|
||||
tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'});
|
||||
tabs.push({name: 'advanced', uiName: 'Advanced', icon: 'glyphicon glyphicon-list-alt'});
|
||||
tabs.push({name: 'display', uiName: formatMessage(holders.display), icon: 'glyphicon glyphicon-eye-open'});
|
||||
tabs.push({name: 'advanced', uiName: formatMessage(holders.advanced), icon: 'glyphicon glyphicon-list-alt'});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -194,7 +244,12 @@ export default class UserSettingsModal extends React.Component {
|
||||
enforceFocus={this.state.enforceFocus}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{'Account Settings'}</Modal.Title>
|
||||
<Modal.Title>
|
||||
<FormattedMessage
|
||||
id='user.settings.modal.title'
|
||||
defaultMessage='Account Settings'
|
||||
/>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body ref='modalBody'>
|
||||
<div className='settings-table'>
|
||||
@@ -221,9 +276,9 @@ export default class UserSettingsModal extends React.Component {
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<ConfirmModal
|
||||
title='Discard Changes?'
|
||||
message='You have unsaved changes, are you sure you want to discard them?'
|
||||
confirm_button='Yes, Discard'
|
||||
title={formatMessage(holders.confirmTitle)}
|
||||
message={formatMessage(holders.confirmMsg)}
|
||||
confirm_button={formatMessage(holders.confirmBtns)}
|
||||
show={this.state.showConfirmModal}
|
||||
onConfirm={this.handleConfirm}
|
||||
onCancel={this.handleCancelConfirmation}
|
||||
@@ -234,6 +289,9 @@ export default class UserSettingsModal extends React.Component {
|
||||
}
|
||||
|
||||
UserSettingsModal.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(UserSettingsModal);
|
||||
@@ -10,6 +10,8 @@ import * as Client from '../../utils/client.jsx';
|
||||
import * as AsyncClient from '../../utils/async_client.jsx';
|
||||
import * as Utils from '../../utils/utils.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
function getNotificationsStateFromStores() {
|
||||
var user = UserStore.getCurrentUser();
|
||||
var soundNeeded = !Utils.isBrowserFirefox();
|
||||
@@ -73,7 +75,30 @@ function getNotificationsStateFromStores() {
|
||||
firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
|
||||
}
|
||||
|
||||
export default class NotificationsTab extends React.Component {
|
||||
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'
|
||||
}
|
||||
});
|
||||
|
||||
class NotificationsTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -198,6 +223,7 @@ export default class NotificationsTab extends React.Component {
|
||||
this.updateCustomMentionKeys();
|
||||
}
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
var serverError = null;
|
||||
if (this.state.serverError) {
|
||||
serverError = this.state.serverError;
|
||||
@@ -227,7 +253,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={notifyActive[0]}
|
||||
onChange={this.handleNotifyRadio.bind(this, 'all')}
|
||||
/>
|
||||
{'For all activity'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notification.allActivity'
|
||||
defaultMessage='For all activity'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -238,7 +267,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={notifyActive[1]}
|
||||
onChange={this.handleNotifyRadio.bind(this, 'mention')}
|
||||
/>
|
||||
{'Only for mentions and direct messages'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.onlyMentions'
|
||||
defaultMessage='Only for mentions and direct messages'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -249,17 +281,27 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={notifyActive[2]}
|
||||
onChange={this.handleNotifyRadio.bind(this, 'none')}
|
||||
/>
|
||||
{'Never'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraInfo = <span>{'Desktop notifications are available on Firefox, Safari, and Chrome.'}</span>;
|
||||
const extraInfo = (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.info'
|
||||
defaultMessage='Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
desktopSection = (
|
||||
<SettingItemMax
|
||||
title='Send desktop notifications'
|
||||
title={formatMessage(holders.desktop)}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
@@ -270,11 +312,26 @@ export default class NotificationsTab extends React.Component {
|
||||
} else {
|
||||
let describe = '';
|
||||
if (this.state.notifyLevel === 'mention') {
|
||||
describe = 'Only for mentions and direct messages';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.onlyMentions'
|
||||
defaultMessage='Only for mentions and direct messages'
|
||||
/>
|
||||
);
|
||||
} else if (this.state.notifyLevel === 'none') {
|
||||
describe = 'Never';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
describe = 'For all activity';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notification.allActivity'
|
||||
defaultMessage='For all activity'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleUpdateDesktopSection = function updateDesktopSection() {
|
||||
@@ -283,7 +340,7 @@ export default class NotificationsTab extends React.Component {
|
||||
|
||||
desktopSection = (
|
||||
<SettingItemMin
|
||||
title='Send desktop notifications'
|
||||
title={formatMessage(holders.desktop)}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateDesktopSection}
|
||||
/>
|
||||
@@ -311,7 +368,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={soundActive[0]}
|
||||
onChange={this.handleSoundRadio.bind(this, 'true')}
|
||||
/>
|
||||
{'On'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -322,18 +382,28 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={soundActive[1]}
|
||||
onChange={this.handleSoundRadio.bind(this, 'false')}
|
||||
/>
|
||||
{'Off'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraInfo = <span>{'Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'}</span>;
|
||||
const extraInfo = (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.info'
|
||||
defaultMessage='Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
soundSection = (
|
||||
<SettingItemMax
|
||||
title='Desktop notification sounds'
|
||||
title={formatMessage(holders.desktopSounds)}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
@@ -344,11 +414,26 @@ export default class NotificationsTab extends React.Component {
|
||||
} else {
|
||||
let describe = '';
|
||||
if (!this.state.soundNeeded) {
|
||||
describe = 'Please configure notification sounds in your browser settings';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notification.soundConfig'
|
||||
defaultMessage='Please configure notification sounds in your browser settings'
|
||||
/>
|
||||
);
|
||||
} else if (this.state.enableSound === 'false') {
|
||||
describe = 'Off';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
describe = 'On';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleUpdateSoundSection = function updateSoundSection() {
|
||||
@@ -357,7 +442,7 @@ export default class NotificationsTab extends React.Component {
|
||||
|
||||
soundSection = (
|
||||
<SettingItemMin
|
||||
title='Desktop notification sounds'
|
||||
title={formatMessage(holders.desktopSounds)}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateSoundSection}
|
||||
disableOpen = {!this.state.soundNeeded}
|
||||
@@ -386,7 +471,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={emailActive[0]}
|
||||
onChange={this.handleEmailRadio.bind(this, 'true')}
|
||||
/>
|
||||
{'On'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -397,17 +485,28 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={emailActive[1]}
|
||||
onChange={this.handleEmailRadio.bind(this, 'false')}
|
||||
/>
|
||||
{'Off'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div><br/>{'Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from ' + global.window.mm_config.SiteName + ' for more than 5 minutes.'}</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='Email notifications'
|
||||
title={formatMessage(holders.emailNotifications)}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
@@ -417,9 +516,19 @@ export default class NotificationsTab extends React.Component {
|
||||
} else {
|
||||
let describe = '';
|
||||
if (this.state.enableEmail === 'false') {
|
||||
describe = 'Off';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
describe = 'On';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleUpdateEmailSection = function updateEmailSection() {
|
||||
@@ -428,7 +537,7 @@ export default class NotificationsTab extends React.Component {
|
||||
|
||||
emailSection = (
|
||||
<SettingItemMin
|
||||
title='Email notifications'
|
||||
title={formatMessage(holders.emailNotifications)}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateEmailSection}
|
||||
/>
|
||||
@@ -459,7 +568,13 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={this.state.firstNameKey}
|
||||
onChange={handleUpdateFirstNameKey}
|
||||
/>
|
||||
{'Your case sensitive first name "' + user.first_name + '"'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.sensitiveName'
|
||||
defaultMessage='Your case sensitive first name "{first_name}"'
|
||||
values={{
|
||||
first_name: user.first_name
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -478,7 +593,13 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={this.state.usernameKey}
|
||||
onChange={handleUpdateUsernameKey}
|
||||
/>
|
||||
{'Your non-case sensitive username "' + user.username + '"'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.sensitiveUsername'
|
||||
defaultMessage='Your non-case sensitive username "{username}"'
|
||||
values={{
|
||||
username: user.username
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -496,7 +617,13 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={this.state.mentionKey}
|
||||
onChange={handleUpdateMentionKey}
|
||||
/>
|
||||
{'Your username mentioned "@' + user.username + '"'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.usernameMention'
|
||||
defaultMessage='Your username mentioned "@{username}"'
|
||||
values={{
|
||||
username: user.username
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -514,7 +641,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={this.state.allKey}
|
||||
onChange={handleUpdateAllKey}
|
||||
/>
|
||||
{'Team-wide mentions "@all"'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.teamWide'
|
||||
defaultMessage='Team-wide mentions "@all"'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -532,7 +662,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={this.state.channelKey}
|
||||
onChange={handleUpdateChannelKey}
|
||||
/>
|
||||
{'Channel-wide mentions "@channel"'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.channelWide'
|
||||
defaultMessage='Channel-wide mentions "@channel"'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -548,7 +681,10 @@ export default class NotificationsTab extends React.Component {
|
||||
checked={this.state.customKeysChecked}
|
||||
onChange={this.updateCustomMentionKeys}
|
||||
/>
|
||||
{'Other non-case sensitive words, separated by commas:'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.sensitiveWords'
|
||||
defaultMessage='Other non-case sensitive words, separated by commas:'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
@@ -563,7 +699,7 @@ export default class NotificationsTab extends React.Component {
|
||||
|
||||
keysSection = (
|
||||
<SettingItemMax
|
||||
title='Words that trigger mentions'
|
||||
title={formatMessage(holders.wordsTrigger)}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
server_error={serverError}
|
||||
@@ -601,7 +737,12 @@ export default class NotificationsTab extends React.Component {
|
||||
if (describe.length > 0) {
|
||||
describe = describe.substring(0, describe.length - 2);
|
||||
} else {
|
||||
describe = 'No words configured';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.noWords'
|
||||
defaultMessage='No words configured'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleUpdateKeysSection = function updateKeysSection() {
|
||||
@@ -610,7 +751,7 @@ export default class NotificationsTab extends React.Component {
|
||||
|
||||
keysSection = (
|
||||
<SettingItemMin
|
||||
title='Words that trigger mentions'
|
||||
title={formatMessage(holders.wordsTrigger)}
|
||||
describe={describe}
|
||||
updateSection={handleUpdateKeysSection}
|
||||
/>
|
||||
@@ -624,7 +765,7 @@ export default class NotificationsTab extends React.Component {
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
aria-label={formatMessage(holders.close)}
|
||||
onClick={this.props.closeModal}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
@@ -637,14 +778,22 @@ export default class NotificationsTab extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Notification Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.title'
|
||||
defaultMessage='Notification Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
ref='wrapper'
|
||||
className='user-settings'
|
||||
>
|
||||
<h3 className='tab-header'>{'Notifications'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.header'
|
||||
defaultMessage='Notifications'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{desktopSection}
|
||||
<div className='divider-light'/>
|
||||
@@ -667,6 +816,7 @@ NotificationsTab.defaultProps = {
|
||||
activeTab: ''
|
||||
};
|
||||
NotificationsTab.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
@@ -675,3 +825,5 @@ NotificationsTab.propTypes = {
|
||||
closeModal: React.PropTypes.func.isRequired,
|
||||
collapseModal: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(NotificationsTab);
|
||||
@@ -13,7 +13,40 @@ import * as Client from '../../utils/client.jsx';
|
||||
import * as AsyncClient from '../../utils/async_client.jsx';
|
||||
import Constants from '../../utils/constants.jsx';
|
||||
|
||||
export default class SecurityTab extends React.Component {
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
currentPasswordError: {
|
||||
id: 'user.settings.security.currentPasswordError',
|
||||
defaultMessage: 'Please enter your current password'
|
||||
},
|
||||
passwordLengthError: {
|
||||
id: 'user.settings.security.passwordLengthError',
|
||||
defaultMessage: 'New passwords must be at least {chars} characters'
|
||||
},
|
||||
passwordMatchError: {
|
||||
id: 'user.settings.security.passwordMatchError',
|
||||
defaultMessage: 'The new passwords you entered do not match'
|
||||
},
|
||||
password: {
|
||||
id: 'user.settings.security.password',
|
||||
defaultMessage: 'Password'
|
||||
},
|
||||
lastUpdated: {
|
||||
id: 'user.settings.security.lastUpdated',
|
||||
defaultMessage: 'Last updated {date} at {time}'
|
||||
},
|
||||
method: {
|
||||
id: 'user.settings.security.method',
|
||||
defaultMessage: 'Sign-in Method'
|
||||
},
|
||||
close: {
|
||||
id: 'user.settings.security.close',
|
||||
defaultMessage: 'Close'
|
||||
}
|
||||
});
|
||||
|
||||
class SecurityTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -43,18 +76,19 @@ export default class SecurityTab extends React.Component {
|
||||
var newPassword = this.state.newPassword;
|
||||
var confirmPassword = this.state.confirmPassword;
|
||||
|
||||
const {formatMessage} = this.props.intl;
|
||||
if (currentPassword === '') {
|
||||
this.setState({passwordError: 'Please enter your current password', serverError: ''});
|
||||
this.setState({passwordError: formatMessage(holders.currentPasswordError), serverError: ''});
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length < Constants.MIN_PASSWORD_LENGTH) {
|
||||
this.setState({passwordError: 'New passwords must be at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
|
||||
this.setState({passwordError: formatMessage(holders.passwordLengthError, {chars: Constants.MIN_PASSWORD_LENGTH}), serverError: ''});
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
this.setState({passwordError: 'The new passwords you entered do not match', serverError: ''});
|
||||
this.setState({passwordError: formatMessage(holders.passwordMatchError), serverError: ''});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,6 +126,7 @@ export default class SecurityTab extends React.Component {
|
||||
}
|
||||
createPasswordSection() {
|
||||
let updateSectionStatus;
|
||||
const {formatMessage} = this.props.intl;
|
||||
|
||||
if (this.props.activeSection === 'password' && this.props.user.auth_service === '') {
|
||||
const inputs = [];
|
||||
@@ -101,7 +136,12 @@ export default class SecurityTab extends React.Component {
|
||||
key='currentPasswordUpdateForm'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>{'Current Password'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.currentPassword'
|
||||
defaultMessage='Current Password'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -117,7 +157,12 @@ export default class SecurityTab extends React.Component {
|
||||
key='newPasswordUpdateForm'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>{'New Password'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.newPassword'
|
||||
defaultMessage='New Password'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -133,7 +178,12 @@ export default class SecurityTab extends React.Component {
|
||||
key='retypeNewPasswordUpdateForm'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>{'Retype New Password'}</label>
|
||||
<label className='col-sm-5 control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.retypePassword'
|
||||
defaultMessage='Retype New Password'
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -153,7 +203,7 @@ export default class SecurityTab extends React.Component {
|
||||
|
||||
return (
|
||||
<SettingItemMax
|
||||
title='Password'
|
||||
title={formatMessage(holders.password)}
|
||||
inputs={inputs}
|
||||
submit={this.submitPassword}
|
||||
server_error={this.state.serverError}
|
||||
@@ -165,20 +215,16 @@ export default class SecurityTab extends React.Component {
|
||||
|
||||
var describe;
|
||||
var d = new Date(this.props.user.last_password_update);
|
||||
var hour = '12';
|
||||
if (d.getHours() % 12) {
|
||||
hour = String(d.getHours() % 12);
|
||||
}
|
||||
var min = String(d.getMinutes());
|
||||
if (d.getMinutes() < 10) {
|
||||
min = '0' + d.getMinutes();
|
||||
}
|
||||
var timeOfDay = ' am';
|
||||
if (d.getHours() >= 12) {
|
||||
timeOfDay = ' pm';
|
||||
}
|
||||
|
||||
describe = 'Last updated ' + Constants.MONTHS[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear() + ' at ' + hour + ':' + min + timeOfDay;
|
||||
const locale = global.window.mm_locale;
|
||||
describe = formatMessage(holders.lastUpdated, {
|
||||
date: d.toLocaleDateString(locale, {month: 'short', day: '2-digit', year: 'numeric'}),
|
||||
time: d.toLocaleTimeString(locale, {hours12: true, hour: '2-digit', minute: '2-digit'}) + timeOfDay
|
||||
});
|
||||
|
||||
updateSectionStatus = function updateSection() {
|
||||
this.props.updateSection('password');
|
||||
@@ -186,7 +232,7 @@ export default class SecurityTab extends React.Component {
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
title='Password'
|
||||
title={formatMessage(holders.password)}
|
||||
describe={describe}
|
||||
updateSection={updateSectionStatus}
|
||||
/>
|
||||
@@ -208,7 +254,10 @@ export default class SecurityTab extends React.Component {
|
||||
className='btn btn-primary'
|
||||
href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email)}
|
||||
>
|
||||
{'Switch to using email and password'}
|
||||
<FormattedMessage
|
||||
id='user.settings.security.switchEmail'
|
||||
defaultMessage='Switch to using email and password'
|
||||
/>
|
||||
</a>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -223,7 +272,10 @@ export default class SecurityTab extends React.Component {
|
||||
className='btn btn-primary'
|
||||
href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&new_type=' + Constants.GITLAB_SERVICE}
|
||||
>
|
||||
{'Switch to using GitLab SSO'}
|
||||
<FormattedMessage
|
||||
id='user.settings.security.switchGitlab'
|
||||
defaultMessage='Switch to using GitLab SSO'
|
||||
/>
|
||||
</a>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -238,7 +290,10 @@ export default class SecurityTab extends React.Component {
|
||||
className='btn btn-primary'
|
||||
href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&new_type=' + Constants.GOOGLE_SERVICE}
|
||||
>
|
||||
{'Switch to using Google SSO'}
|
||||
<FormattedMessage
|
||||
id='user.settings.security.switchGoogle'
|
||||
defaultMessage='Switch to using Google SSO'
|
||||
/>
|
||||
</a>
|
||||
<br/>
|
||||
</div>
|
||||
@@ -260,11 +315,18 @@ export default class SecurityTab extends React.Component {
|
||||
e.preventDefault();
|
||||
}.bind(this);
|
||||
|
||||
const extraInfo = <span>{'You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.'}</span>;
|
||||
const extraInfo = (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.oneSignin'
|
||||
defaultMessage='You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.'
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingItemMax
|
||||
title='Sign-in Method'
|
||||
title={this.props.intl.formatMessage(holders.method)}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
server_error={this.state.serverError}
|
||||
@@ -277,14 +339,24 @@ export default class SecurityTab extends React.Component {
|
||||
this.props.updateSection('signin');
|
||||
}.bind(this);
|
||||
|
||||
let describe = 'Email and Password';
|
||||
let describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.security.emailPwd'
|
||||
defaultMessage='Email and Password'
|
||||
/>
|
||||
);
|
||||
if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
|
||||
describe = 'GitLab SSO';
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.security.gitlab'
|
||||
defaultMessage='GitLab SSO'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
title='Sign-in Method'
|
||||
title={this.props.intl.formatMessage(holders.method)}
|
||||
describe={describe}
|
||||
updateSection={updateSectionStatus}
|
||||
/>
|
||||
@@ -309,7 +381,7 @@ export default class SecurityTab extends React.Component {
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
aria-label={this.props.intl.formatMessage(holders.close)}
|
||||
onClick={this.props.closeModal}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
@@ -322,11 +394,19 @@ export default class SecurityTab extends React.Component {
|
||||
className='modal-back'
|
||||
onClick={this.props.collapseModal}
|
||||
/>
|
||||
{'Security Settings'}
|
||||
<FormattedMessage
|
||||
id='user.settings.security.title'
|
||||
defaultMessage='Security Settings'
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Security Settings'}</h3>
|
||||
<h3 className='tab-header'>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.title'
|
||||
defaultMessage='Security Settings'
|
||||
/>
|
||||
</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{passwordSection}
|
||||
<div className='divider-light'/>
|
||||
@@ -337,14 +417,22 @@ export default class SecurityTab extends React.Component {
|
||||
className='security-links theme'
|
||||
dialogType={AccessHistoryModal}
|
||||
>
|
||||
<i className='fa fa-clock-o'></i>{'View Access History'}
|
||||
<i className='fa fa-clock-o'></i>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.viewHistory'
|
||||
defaultMessage='View Access History'
|
||||
/>
|
||||
</ToggleModalButton>
|
||||
<b> </b>
|
||||
<ToggleModalButton
|
||||
className='security-links theme'
|
||||
dialogType={ActivityLogModal}
|
||||
>
|
||||
<i className='fa fa-clock-o'></i>{'View and Logout of Active Sessions'}
|
||||
<i className='fa fa-clock-o'></i>
|
||||
<FormattedMessage
|
||||
id='user.settings.security.logoutActiveSessions'
|
||||
defaultMessage='View and Logout of Active Sessions'
|
||||
/>
|
||||
</ToggleModalButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -357,6 +445,7 @@ SecurityTab.defaultProps = {
|
||||
activeSection: ''
|
||||
};
|
||||
SecurityTab.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
user: React.PropTypes.object,
|
||||
activeSection: React.PropTypes.string,
|
||||
updateSection: React.PropTypes.func,
|
||||
@@ -365,3 +454,5 @@ SecurityTab.propTypes = {
|
||||
collapseModal: React.PropTypes.func.isRequired,
|
||||
setEnforceFocus: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(SecurityTab);
|
||||
Ссылка в новой задаче
Block a user