summaryrefslogtreecommitdiffstats
path: root/webapp/components
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components')
-rw-r--r--webapp/components/logged_in.jsx9
-rw-r--r--webapp/components/needs_team.jsx37
-rw-r--r--webapp/components/setting_item_max.jsx4
-rw-r--r--webapp/components/user_settings/import_theme_modal.jsx91
-rw-r--r--webapp/components/user_settings/user_settings_theme.jsx122
5 files changed, 155 insertions, 108 deletions
diff --git a/webapp/components/logged_in.jsx b/webapp/components/logged_in.jsx
index f637e9dc5..2ac858dfb 100644
--- a/webapp/components/logged_in.jsx
+++ b/webapp/components/logged_in.jsx
@@ -92,15 +92,6 @@ export default class LoggedIn extends React.Component {
id: user.id
});
}
-
- // Update CSS classes to match user theme
- if (user) {
- if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) {
- Utils.applyTheme(user.theme_props);
- } else {
- Utils.applyTheme(Constants.THEMES.default);
- }
- }
}
onUserChanged() {
diff --git a/webapp/components/needs_team.jsx b/webapp/components/needs_team.jsx
index 07b90636d..a8c7b3508 100644
--- a/webapp/components/needs_team.jsx
+++ b/webapp/components/needs_team.jsx
@@ -41,19 +41,34 @@ export default class NeedsTeam extends React.Component {
constructor(params) {
super(params);
- this.onChanged = this.onChanged.bind(this);
+ this.onTeamChanged = this.onTeamChanged.bind(this);
+ this.onPreferencesChanged = this.onPreferencesChanged.bind(this);
+
+ const team = TeamStore.getCurrent();
this.state = {
- team: TeamStore.getCurrent()
+ team,
+ theme: PreferenceStore.getTheme(team.id)
};
}
- onChanged() {
+ onTeamChanged() {
+ const team = TeamStore.getCurrent();
+
this.setState({
- team: TeamStore.getCurrent()
+ team,
+ theme: PreferenceStore.getTheme(team.id)
});
}
+ onPreferencesChanged(category) {
+ if (!category || category === Preferences.CATEGORY_THEME) {
+ this.setState({
+ theme: PreferenceStore.getTheme(this.state.team.id)
+ });
+ }
+ }
+
componentWillMount() {
// Go to tutorial if we are first arriving
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
@@ -63,7 +78,8 @@ export default class NeedsTeam extends React.Component {
}
componentDidMount() {
- TeamStore.addChangeListener(this.onChanged);
+ TeamStore.addChangeListener(this.onTeamChanged);
+ PreferenceStore.addChangeListener(this.onPreferencesChanged);
// Emit view action
GlobalActions.viewLoggedIn();
@@ -80,10 +96,19 @@ export default class NeedsTeam extends React.Component {
$(window).on('blur', () => {
window.isActive = false;
});
+
+ Utils.applyTheme(this.state.theme);
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ if (!Utils.areObjectsEqual(prevState.theme, this.state.theme)) {
+ Utils.applyTheme(this.state.theme);
+ }
}
componentWillUnmount() {
- TeamStore.removeChangeListener(this.onChanged);
+ TeamStore.removeChangeListener(this.onTeamChanged);
+ PreferenceStore.removeChangeListener(this.onPreferencesChanged);
$(window).off('focus');
$(window).off('blur');
}
diff --git a/webapp/components/setting_item_max.jsx b/webapp/components/setting_item_max.jsx
index ec496a765..ad765a7d6 100644
--- a/webapp/components/setting_item_max.jsx
+++ b/webapp/components/setting_item_max.jsx
@@ -84,6 +84,7 @@ export default class SettingItemMax extends React.Component {
</li>
<li className='setting-list-item'>
<hr/>
+ {this.props.submitExtra}
{serverError}
{clientError}
{submit}
@@ -113,5 +114,6 @@ SettingItemMax.propTypes = {
updateSection: React.PropTypes.func,
submit: React.PropTypes.func,
title: React.PropTypes.node,
- width: React.PropTypes.string
+ width: React.PropTypes.string,
+ submitExtra: React.PropTypes.node
};
diff --git a/webapp/components/user_settings/import_theme_modal.jsx b/webapp/components/user_settings/import_theme_modal.jsx
index 552659c4c..32c6837e8 100644
--- a/webapp/components/user_settings/import_theme_modal.jsx
+++ b/webapp/components/user_settings/import_theme_modal.jsx
@@ -1,30 +1,18 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import ReactDOM from 'react-dom';
import ModalStore from 'stores/modal_store.jsx';
-import UserStore from 'stores/user_store.jsx';
-import * as Utils from 'utils/utils.jsx';
-import Client from 'utils/web_client.jsx';
import {Modal} from 'react-bootstrap';
-import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
import Constants from 'utils/constants.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
-
-const holders = defineMessages({
- submitError: {
- id: 'user.settings.import_theme.submitError',
- defaultMessage: 'Invalid format, please try copying and pasting in again.'
- }
-});
+import {FormattedMessage} from 'react-intl';
const ActionTypes = Constants.ActionTypes;
import React from 'react';
-class ImportThemeModal extends React.Component {
+export default class ImportThemeModal extends React.Component {
constructor(props) {
super(props);
@@ -33,26 +21,42 @@ class ImportThemeModal extends React.Component {
this.handleChange = this.handleChange.bind(this);
this.state = {
+ value: '',
inputError: '',
- show: false
+ show: false,
+ callback: null
};
}
+
componentDidMount() {
ModalStore.addModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow);
}
+
componentWillUnmount() {
ModalStore.removeModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow);
}
- updateShow(show) {
- this.setState({show});
+
+ updateShow(show, args) {
+ this.setState({
+ show,
+ callback: args.callback
+ });
}
+
handleSubmit(e) {
e.preventDefault();
- const text = ReactDOM.findDOMNode(this.refs.input).value;
+ const text = this.state.value;
if (!this.isInputValid(text)) {
- this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
+ this.setState({
+ inputError: (
+ <FormattedMessage
+ id='user.settings.import_theme.submitError'
+ defaultMessage='Invalid format, please try copying and pasting in again.'
+ />
+ )
+ });
return;
}
@@ -81,26 +85,13 @@ class ImportThemeModal extends React.Component {
theme.mentionHighlightLink = '#2f81b7';
theme.codeTheme = 'github';
- const user = UserStore.getCurrentUser();
- user.theme_props = theme;
-
- Client.updateUser(user, Constants.UserUpdateEvents.THEME,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ME,
- me: data
- });
-
- this.setState({show: false});
- Utils.applyTheme(theme);
- },
- (err) => {
- var state = this.getStateFromStores();
- state.serverError = err;
- this.setState(state);
- }
- );
+ this.state.callback(theme);
+ this.setState({
+ show: false,
+ callback: null
+ });
}
+
isInputValid(text) {
if (text.length === 0) {
return false;
@@ -134,13 +125,25 @@ class ImportThemeModal extends React.Component {
return true;
}
+
handleChange(e) {
- if (this.isInputValid(e.target.value)) {
+ const value = e.target.value;
+ this.setState({value});
+
+ if (this.isInputValid(value)) {
this.setState({inputError: null});
} else {
- this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
+ this.setState({
+ inputError: (
+ <FormattedMessage
+ id='user.settings.import_theme.submitError'
+ defaultMessage='Invalid format, please try copying and pasting in again.'
+ />
+ )
+ });
}
}
+
render() {
return (
<span>
@@ -170,9 +173,9 @@ class ImportThemeModal extends React.Component {
<div className='form-group less'>
<div className='col-sm-9'>
<input
- ref='input'
type='text'
className='form-control'
+ value={this.state.value}
onChange={this.handleChange}
/>
<div className='input__help'>
@@ -210,9 +213,3 @@ class ImportThemeModal extends React.Component {
);
}
}
-
-ImportThemeModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(ImportThemeModal);
diff --git a/webapp/components/user_settings/user_settings_theme.jsx b/webapp/components/user_settings/user_settings_theme.jsx
index 94516ec8c..4ff08402a 100644
--- a/webapp/components/user_settings/user_settings_theme.jsx
+++ b/webapp/components/user_settings/user_settings_theme.jsx
@@ -8,28 +8,18 @@ import PremadeThemeChooser from './premade_theme_chooser.jsx';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
+import PreferenceStore from 'stores/preference_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
-import Client from 'utils/web_client.jsx';
-import * as Utils from 'utils/utils.jsx';
+import * as UserActions from 'actions/user_actions.jsx';
-import Constants from 'utils/constants.jsx';
+import * as Utils from 'utils/utils.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
+import {FormattedMessage} from 'react-intl';
-const ActionTypes = Constants.ActionTypes;
-
-const holders = defineMessages({
- themeTitle: {
- id: 'user.settings.display.theme.title',
- defaultMessage: 'Theme'
- },
- themeDescribe: {
- id: 'user.settings.display.theme.describe',
- defaultMessage: 'Open to manage your theme'
- }
-});
+import {ActionTypes, Constants, Preferences} from 'utils/constants.jsx';
import React from 'react';
@@ -47,6 +37,7 @@ export default class ThemeSetting extends React.Component {
this.originalTheme = Object.assign({}, this.state.theme);
}
+
componentDidMount() {
UserStore.addChangeListener(this.onChange);
@@ -54,17 +45,20 @@ export default class ThemeSetting extends React.Component {
$(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
}
}
+
componentDidUpdate() {
if (this.props.selected) {
$('.color-btn').removeClass('active-border');
$(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
}
}
+
componentWillReceiveProps(nextProps) {
if (this.props.selected && !nextProps.selected) {
this.resetFields();
}
}
+
componentWillUnmount() {
UserStore.removeChangeListener(this.onChange);
@@ -73,27 +67,35 @@ export default class ThemeSetting extends React.Component {
Utils.applyTheme(state.theme);
}
}
+
getStateFromStores() {
- const user = UserStore.getCurrentUser();
- let theme = null;
+ const teamId = TeamStore.getCurrentId();
- if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) {
- theme = Object.assign({}, user.theme_props);
- } else {
- theme = $.extend(true, {}, Constants.THEMES.default);
+ const theme = PreferenceStore.getTheme(teamId);
+ if (!theme.codeTheme) {
+ theme.codeTheme = Constants.DEFAULT_CODE_THEME;
}
- let type = 'premade';
- if (theme.type === 'custom') {
- type = 'custom';
- }
+ let showAllTeamsCheckbox = false;
+ let applyToAllTeams = true;
- if (!theme.codeTheme) {
- theme.codeTheme = Constants.DEFAULT_CODE_THEME;
+ if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.LDAP === 'true') {
+ // show the "apply to all teams" checkbox if the user is on more than one team
+ showAllTeamsCheckbox = Object.keys(TeamStore.getAll()).length > 1;
+
+ // check the "apply to all teams" checkbox by default if the user has any team-specific themes
+ applyToAllTeams = PreferenceStore.getCategory(Preferences.CATEGORY_THEME).size <= 1;
}
- return {theme, type};
+ return {
+ teamId: TeamStore.getCurrentId(),
+ theme,
+ type: theme.type || 'premade',
+ showAllTeamsCheckbox,
+ applyToAllTeams
+ };
}
+
onChange() {
const newState = this.getStateFromStores();
@@ -103,21 +105,20 @@ export default class ThemeSetting extends React.Component {
this.props.setEnforceFocus(true);
}
+
scrollToTop() {
$('.ps-container.modal-body').scrollTop(0);
}
+
submitTheme(e) {
e.preventDefault();
- var user = UserStore.getCurrentUser();
- user.theme_props = this.state.theme;
- Client.updateUser(user, Constants.UserUpdateEvents.THEME,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ME,
- me: data
- });
+ const teamId = this.state.applyToAllTeams ? '' : this.state.teamId;
+ UserActions.saveTheme(
+ teamId,
+ this.state.theme,
+ () => {
this.props.setRequireConfirm(false);
this.originalTheme = Object.assign({}, this.state.theme);
this.scrollToTop();
@@ -130,6 +131,7 @@ export default class ThemeSetting extends React.Component {
}
);
}
+
updateTheme(theme) {
let themeChanged = this.state.theme.length === theme.length;
if (!themeChanged) {
@@ -148,9 +150,11 @@ export default class ThemeSetting extends React.Component {
this.setState({theme});
Utils.applyTheme(theme);
}
+
updateType(type) {
this.setState({type});
}
+
resetFields() {
const state = this.getStateFromStores();
state.serverError = null;
@@ -161,17 +165,18 @@ export default class ThemeSetting extends React.Component {
this.props.setRequireConfirm(false);
}
+
handleImportModal() {
AppDispatcher.handleViewAction({
type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL,
- value: true
+ value: true,
+ callback: this.updateTheme
});
this.props.setEnforceFocus(false);
}
- render() {
- const {formatMessage} = this.props.intl;
+ render() {
var serverError;
if (this.state.serverError) {
serverError = this.state.serverError;
@@ -266,9 +271,29 @@ export default class ThemeSetting extends React.Component {
</div>
);
+ let allTeamsCheckbox = null;
+ if (this.state.showAllTeamsCheckbox) {
+ allTeamsCheckbox = (
+ <div className='checkbox user-settings__submit-checkbox'>
+ <label>
+ <input
+ type='checkbox'
+ checked={this.state.applyToAllTeams}
+ onChange={(e) => this.setState({applyToAllTeams: e.target.checked})}
+ />
+ <FormattedMessage
+ id='user.settings.display.theme.applyToAllTeams'
+ defaultMessage='Apply New Theme to All Teams'
+ />
+ </label>
+ </div>
+ );
+ }
+
themeUI = (
<SettingItemMax
inputs={inputs}
+ submitExtra={allTeamsCheckbox}
submit={this.submitTheme}
server_error={serverError}
width='full'
@@ -281,8 +306,18 @@ export default class ThemeSetting extends React.Component {
} else {
themeUI = (
<SettingItemMin
- title={formatMessage(holders.themeTitle)}
- describe={formatMessage(holders.themeDescribe)}
+ title={
+ <FormattedMessage
+ id='user.settings.display.theme.title'
+ defaultMessage='Theme'
+ />
+ }
+ describe={
+ <FormattedMessage
+ id='user.settings.display.theme.describe'
+ defaultMessage='Open to manage your theme'
+ />
+ }
updateSection={() => {
this.props.updateSection('theme');
}}
@@ -295,11 +330,8 @@ export default class ThemeSetting extends React.Component {
}
ThemeSetting.propTypes = {
- intl: intlShape.isRequired,
selected: React.PropTypes.bool.isRequired,
updateSection: React.PropTypes.func.isRequired,
setRequireConfirm: React.PropTypes.func.isRequired,
setEnforceFocus: React.PropTypes.func.isRequired
};
-
-export default injectIntl(ThemeSetting);