PLT-2992 Added the ability to use different themes for each team (#3411)
* Cleaned up user_settings_theme.jsx and import_theme_modal.jsx * Made ImportThemeModal use a callback to return the theme to the user settings modal instead of saving it directly * Moved user theme from model to preferences * Added serverside API to delete preferences TODO update package with client stuff * Changed constants.jsx so that Preferences and ActionTypes can be imported on their own * Updated ThemeProps migration code to properly rename solarized code themes * Fixed warnings thrown by AppDispatcher * Added clientside UI to support team-specific themes * Removed debugging code from test * Fixed setting a user's theme when they haven't set their theme before
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
8e810bc2eb
Коммит
caabfbcdd5
@@ -1,10 +1,15 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import Client from 'utils/web_client.jsx';
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'utils/web_client.jsx';
|
||||
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {ActionTypes, Preferences} from 'utils/constants.jsx';
|
||||
|
||||
export function switchFromLdapToEmail(email, password, ldapPassword, onSuccess, onError) {
|
||||
Client.ldapToEmail(
|
||||
@@ -28,3 +33,52 @@ export function getMoreDmList() {
|
||||
AsyncClient.getProfilesForDirectMessageList();
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
}
|
||||
|
||||
export function saveTheme(teamId, theme, onSuccess, onError) {
|
||||
AsyncClient.savePreference(
|
||||
Preferences.CATEGORY_THEME,
|
||||
teamId,
|
||||
JSON.stringify(theme),
|
||||
() => {
|
||||
onThemeSaved(teamId, theme, onSuccess);
|
||||
},
|
||||
(err) => {
|
||||
onError(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function onThemeSaved(teamId, theme, onSuccess) {
|
||||
const themePreferences = PreferenceStore.getCategory(Preferences.CATEGORY_THEME);
|
||||
|
||||
if (teamId !== '' && themePreferences.size > 1) {
|
||||
// no extra handling to be done to delete team-specific themes
|
||||
onSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
const toDelete = [];
|
||||
|
||||
for (const [name] of themePreferences) {
|
||||
if (name === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
toDelete.push({
|
||||
user_id: UserStore.getCurrentId(),
|
||||
category: Preferences.CATEGORY_THEME,
|
||||
name
|
||||
});
|
||||
}
|
||||
|
||||
// we're saving a new global theme so delete any team-specific ones
|
||||
AsyncClient.deletePreferences(toDelete);
|
||||
|
||||
// delete them locally before we hear from the server so that the UI flow is smoother
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.DELETED_PREFERENCES,
|
||||
preferences: toDelete
|
||||
});
|
||||
|
||||
onSuccess();
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 UserActions from 'actions/user_actions.jsx';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, 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;
|
||||
|
||||
if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) {
|
||||
theme = Object.assign({}, user.theme_props);
|
||||
} else {
|
||||
theme = $.extend(true, {}, Constants.THEMES.default);
|
||||
}
|
||||
|
||||
let type = 'premade';
|
||||
if (theme.type === 'custom') {
|
||||
type = 'custom';
|
||||
}
|
||||
const teamId = TeamStore.getCurrentId();
|
||||
|
||||
const theme = PreferenceStore.getTheme(teamId);
|
||||
if (!theme.codeTheme) {
|
||||
theme.codeTheme = Constants.DEFAULT_CODE_THEME;
|
||||
}
|
||||
|
||||
return {theme, type};
|
||||
let showAllTeamsCheckbox = false;
|
||||
let applyToAllTeams = true;
|
||||
|
||||
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 {
|
||||
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);
|
||||
|
||||
@@ -9,7 +9,7 @@ const PayloadSources = Constants.PayloadSources;
|
||||
const AppDispatcher = Object.assign(new Flux.Dispatcher(), {
|
||||
handleServerAction: function performServerAction(action) {
|
||||
if (!action.type) {
|
||||
console.warning('handleServerAction called with undefined action type'); // eslint-disable-line no-console
|
||||
console.warn('handleServerAction called with undefined action type'); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
var payload = {
|
||||
@@ -21,7 +21,7 @@ const AppDispatcher = Object.assign(new Flux.Dispatcher(), {
|
||||
|
||||
handleViewAction: function performViewAction(action) {
|
||||
if (!action.type) {
|
||||
console.warning('handleViewAction called with undefined action type'); // eslint-disable-line no-console
|
||||
console.warn('handleViewAction called with undefined action type'); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
var payload = {
|
||||
|
||||
@@ -475,3 +475,8 @@
|
||||
.no-resize {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.user-settings__submit-checkbox {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,16 @@ class PreferenceStoreClass extends EventEmitter {
|
||||
return parseInt(this.preferences.get(key), 10);
|
||||
}
|
||||
|
||||
getObject(category, name, defaultValue = null) {
|
||||
const key = this.getKey(category, name);
|
||||
|
||||
if (!this.preferences.has(key)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return JSON.parse(this.preferences.get(key));
|
||||
}
|
||||
|
||||
getCategory(category) {
|
||||
const prefix = category + '--';
|
||||
|
||||
@@ -78,6 +88,10 @@ class PreferenceStoreClass extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
deletePreference(preference) {
|
||||
this.preferences.delete(this.getKey(preference.category, preference.name));
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.preferences.clear();
|
||||
}
|
||||
@@ -94,6 +108,18 @@ class PreferenceStoreClass extends EventEmitter {
|
||||
this.removeListener(CHANGE_EVENT, callback);
|
||||
}
|
||||
|
||||
getTheme(teamId) {
|
||||
if (this.preferences.has(this.getKey(Constants.Preferences.CATEGORY_THEME, teamId))) {
|
||||
return this.getObject(Constants.Preferences.CATEGORY_THEME, teamId);
|
||||
}
|
||||
|
||||
if (this.preferences.has(this.getKey(Constants.Preferences.CATEGORY_THEME, ''))) {
|
||||
return this.getObject(Constants.Preferences.CATEGORY_THEME, '');
|
||||
}
|
||||
|
||||
return Constants.THEMES.default;
|
||||
}
|
||||
|
||||
handleEventPayload(payload) {
|
||||
const action = payload.action;
|
||||
|
||||
@@ -108,6 +134,12 @@ class PreferenceStoreClass extends EventEmitter {
|
||||
this.setPreferencesFromServer(action.preferences);
|
||||
this.emitChange();
|
||||
break;
|
||||
case ActionTypes.DELETED_PREFERENCES:
|
||||
for (const preference of action.preferences) {
|
||||
this.deletePreference(preference);
|
||||
}
|
||||
this.emitChange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -852,6 +852,29 @@ export function savePreferences(preferences, success, error) {
|
||||
);
|
||||
}
|
||||
|
||||
export function deletePreferences(preferences, success, error) {
|
||||
Client.deletePreferences(
|
||||
preferences,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.DELETED_PREFERENCES,
|
||||
preferences
|
||||
});
|
||||
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
dispatchError(err, 'deletePreferences');
|
||||
|
||||
if (error) {
|
||||
error();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getSuggestedCommands(command, suggestionId, component) {
|
||||
Client.listCommands(
|
||||
(data) => {
|
||||
|
||||
@@ -33,100 +33,125 @@ import mattermostDarkThemeImage from 'images/themes/mattermost_dark.png';
|
||||
import mattermostThemeImage from 'images/themes/mattermost.png';
|
||||
import windows10ThemeImage from 'images/themes/windows_dark.png';
|
||||
|
||||
export default {
|
||||
ActionTypes: keyMirror({
|
||||
RECEIVED_ERROR: null,
|
||||
export const Preferences = {
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
|
||||
DISPLAY_PREFER_NICKNAME: 'nickname_full_name',
|
||||
DISPLAY_PREFER_FULL_NAME: 'full_name',
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
|
||||
TUTORIAL_STEP: 'tutorial_step',
|
||||
CHANNEL_DISPLAY_MODE: 'channel_display_mode',
|
||||
CHANNEL_DISPLAY_MODE_CENTERED: 'centered',
|
||||
CHANNEL_DISPLAY_MODE_FULL_SCREEN: 'full',
|
||||
CHANNEL_DISPLAY_MODE_DEFAULT: 'centered',
|
||||
MESSAGE_DISPLAY: 'message_display',
|
||||
MESSAGE_DISPLAY_CLEAN: 'clean',
|
||||
MESSAGE_DISPLAY_COMPACT: 'compact',
|
||||
MESSAGE_DISPLAY_DEFAULT: 'clean',
|
||||
COLLAPSE_DISPLAY: 'collapse_previews',
|
||||
COLLAPSE_DISPLAY_DEFAULT: 'false',
|
||||
USE_MILITARY_TIME: 'use_military_time',
|
||||
CATEGORY_THEME: 'theme'
|
||||
};
|
||||
|
||||
CLICK_CHANNEL: null,
|
||||
CREATE_CHANNEL: null,
|
||||
LEAVE_CHANNEL: null,
|
||||
CREATE_POST: null,
|
||||
CREATE_COMMENT: null,
|
||||
POST_DELETED: null,
|
||||
REMOVE_POST: null,
|
||||
export const ActionTypes = keyMirror({
|
||||
RECEIVED_ERROR: null,
|
||||
|
||||
RECEIVED_CHANNELS: null,
|
||||
RECEIVED_CHANNEL: null,
|
||||
RECEIVED_MORE_CHANNELS: null,
|
||||
RECEIVED_CHANNEL_EXTRA_INFO: null,
|
||||
CLICK_CHANNEL: null,
|
||||
CREATE_CHANNEL: null,
|
||||
LEAVE_CHANNEL: null,
|
||||
CREATE_POST: null,
|
||||
CREATE_COMMENT: null,
|
||||
POST_DELETED: null,
|
||||
REMOVE_POST: null,
|
||||
|
||||
FOCUS_POST: null,
|
||||
RECEIVED_POSTS: null,
|
||||
RECEIVED_FOCUSED_POST: null,
|
||||
RECEIVED_POST: null,
|
||||
RECEIVED_EDIT_POST: null,
|
||||
RECEIVED_SEARCH: null,
|
||||
RECEIVED_SEARCH_TERM: null,
|
||||
RECEIVED_POST_SELECTED: null,
|
||||
RECEIVED_MENTION_DATA: null,
|
||||
RECEIVED_ADD_MENTION: null,
|
||||
RECEIVED_CHANNELS: null,
|
||||
RECEIVED_CHANNEL: null,
|
||||
RECEIVED_MORE_CHANNELS: null,
|
||||
RECEIVED_CHANNEL_EXTRA_INFO: null,
|
||||
|
||||
RECEIVED_PROFILES_FOR_DM_LIST: null,
|
||||
RECEIVED_PROFILES: null,
|
||||
RECEIVED_DIRECT_PROFILES: null,
|
||||
RECEIVED_ME: null,
|
||||
RECEIVED_SESSIONS: null,
|
||||
RECEIVED_AUDITS: null,
|
||||
RECEIVED_TEAMS: null,
|
||||
RECEIVED_STATUSES: null,
|
||||
RECEIVED_PREFERENCE: null,
|
||||
RECEIVED_PREFERENCES: null,
|
||||
RECEIVED_FILE_INFO: null,
|
||||
RECEIVED_ANALYTICS: null,
|
||||
FOCUS_POST: null,
|
||||
RECEIVED_POSTS: null,
|
||||
RECEIVED_FOCUSED_POST: null,
|
||||
RECEIVED_POST: null,
|
||||
RECEIVED_EDIT_POST: null,
|
||||
RECEIVED_SEARCH: null,
|
||||
RECEIVED_SEARCH_TERM: null,
|
||||
RECEIVED_POST_SELECTED: null,
|
||||
RECEIVED_MENTION_DATA: null,
|
||||
RECEIVED_ADD_MENTION: null,
|
||||
|
||||
RECEIVED_INCOMING_WEBHOOKS: null,
|
||||
RECEIVED_INCOMING_WEBHOOK: null,
|
||||
REMOVED_INCOMING_WEBHOOK: null,
|
||||
RECEIVED_OUTGOING_WEBHOOKS: null,
|
||||
RECEIVED_OUTGOING_WEBHOOK: null,
|
||||
UPDATED_OUTGOING_WEBHOOK: null,
|
||||
REMOVED_OUTGOING_WEBHOOK: null,
|
||||
RECEIVED_COMMANDS: null,
|
||||
RECEIVED_COMMAND: null,
|
||||
UPDATED_COMMAND: null,
|
||||
REMOVED_COMMAND: null,
|
||||
RECEIVED_PROFILES_FOR_DM_LIST: null,
|
||||
RECEIVED_PROFILES: null,
|
||||
RECEIVED_DIRECT_PROFILES: null,
|
||||
RECEIVED_ME: null,
|
||||
RECEIVED_SESSIONS: null,
|
||||
RECEIVED_AUDITS: null,
|
||||
RECEIVED_TEAMS: null,
|
||||
RECEIVED_STATUSES: null,
|
||||
RECEIVED_PREFERENCE: null,
|
||||
RECEIVED_PREFERENCES: null,
|
||||
DELETED_PREFERENCES: null,
|
||||
RECEIVED_FILE_INFO: null,
|
||||
RECEIVED_ANALYTICS: null,
|
||||
|
||||
RECEIVED_CUSTOM_EMOJIS: null,
|
||||
RECEIVED_CUSTOM_EMOJI: null,
|
||||
UPDATED_CUSTOM_EMOJI: null,
|
||||
REMOVED_CUSTOM_EMOJI: null,
|
||||
RECEIVED_INCOMING_WEBHOOKS: null,
|
||||
RECEIVED_INCOMING_WEBHOOK: null,
|
||||
REMOVED_INCOMING_WEBHOOK: null,
|
||||
RECEIVED_OUTGOING_WEBHOOKS: null,
|
||||
RECEIVED_OUTGOING_WEBHOOK: null,
|
||||
UPDATED_OUTGOING_WEBHOOK: null,
|
||||
REMOVED_OUTGOING_WEBHOOK: null,
|
||||
RECEIVED_COMMANDS: null,
|
||||
RECEIVED_COMMAND: null,
|
||||
UPDATED_COMMAND: null,
|
||||
REMOVED_COMMAND: null,
|
||||
|
||||
RECEIVED_MSG: null,
|
||||
RECEIVED_CUSTOM_EMOJIS: null,
|
||||
RECEIVED_CUSTOM_EMOJI: null,
|
||||
UPDATED_CUSTOM_EMOJI: null,
|
||||
REMOVED_CUSTOM_EMOJI: null,
|
||||
|
||||
RECEIVED_MY_TEAM: null,
|
||||
CREATED_TEAM: null,
|
||||
RECEIVED_MSG: null,
|
||||
|
||||
RECEIVED_CONFIG: null,
|
||||
RECEIVED_LOGS: null,
|
||||
RECEIVED_SERVER_AUDITS: null,
|
||||
RECEIVED_SERVER_COMPLIANCE_REPORTS: null,
|
||||
RECEIVED_ALL_TEAMS: null,
|
||||
RECEIVED_ALL_TEAM_LISTINGS: null,
|
||||
RECEIVED_TEAM_MEMBERS: null,
|
||||
RECEIVED_MEMBERS_FOR_TEAM: null,
|
||||
RECEIVED_MY_TEAM: null,
|
||||
CREATED_TEAM: null,
|
||||
|
||||
RECEIVED_LOCALE: null,
|
||||
RECEIVED_CONFIG: null,
|
||||
RECEIVED_LOGS: null,
|
||||
RECEIVED_SERVER_AUDITS: null,
|
||||
RECEIVED_SERVER_COMPLIANCE_REPORTS: null,
|
||||
RECEIVED_ALL_TEAMS: null,
|
||||
RECEIVED_ALL_TEAM_LISTINGS: null,
|
||||
RECEIVED_TEAM_MEMBERS: null,
|
||||
RECEIVED_MEMBERS_FOR_TEAM: null,
|
||||
|
||||
SHOW_SEARCH: null,
|
||||
RECEIVED_LOCALE: null,
|
||||
|
||||
USER_TYPING: null,
|
||||
SHOW_SEARCH: null,
|
||||
|
||||
TOGGLE_IMPORT_THEME_MODAL: null,
|
||||
TOGGLE_INVITE_MEMBER_MODAL: null,
|
||||
TOGGLE_LEAVE_TEAM_MODAL: null,
|
||||
TOGGLE_DELETE_POST_MODAL: null,
|
||||
TOGGLE_GET_POST_LINK_MODAL: null,
|
||||
TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null,
|
||||
TOGGLE_REGISTER_APP_MODAL: null,
|
||||
TOGGLE_GET_PUBLIC_LINK_MODAL: null,
|
||||
USER_TYPING: null,
|
||||
|
||||
SUGGESTION_PRETEXT_CHANGED: null,
|
||||
SUGGESTION_RECEIVED_SUGGESTIONS: null,
|
||||
SUGGESTION_CLEAR_SUGGESTIONS: null,
|
||||
SUGGESTION_COMPLETE_WORD: null,
|
||||
SUGGESTION_SELECT_NEXT: null,
|
||||
SUGGESTION_SELECT_PREVIOUS: null
|
||||
}),
|
||||
TOGGLE_IMPORT_THEME_MODAL: null,
|
||||
TOGGLE_INVITE_MEMBER_MODAL: null,
|
||||
TOGGLE_LEAVE_TEAM_MODAL: null,
|
||||
TOGGLE_DELETE_POST_MODAL: null,
|
||||
TOGGLE_GET_POST_LINK_MODAL: null,
|
||||
TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null,
|
||||
TOGGLE_REGISTER_APP_MODAL: null,
|
||||
TOGGLE_GET_PUBLIC_LINK_MODAL: null,
|
||||
|
||||
SUGGESTION_PRETEXT_CHANGED: null,
|
||||
SUGGESTION_RECEIVED_SUGGESTIONS: null,
|
||||
SUGGESTION_CLEAR_SUGGESTIONS: null,
|
||||
SUGGESTION_COMPLETE_WORD: null,
|
||||
SUGGESTION_SELECT_NEXT: null,
|
||||
SUGGESTION_SELECT_PREVIOUS: null
|
||||
});
|
||||
|
||||
export const Constants = {
|
||||
Preferences,
|
||||
ActionTypes,
|
||||
|
||||
PayloadSources: keyMirror({
|
||||
SERVER_ACTION: null,
|
||||
@@ -174,7 +199,6 @@ export default {
|
||||
FULLNAME: 'fullname',
|
||||
NICKNAME: 'nickname',
|
||||
EMAIL: 'email',
|
||||
THEME: 'theme',
|
||||
LANGUAGE: 'language'
|
||||
},
|
||||
|
||||
@@ -551,25 +575,6 @@ export default {
|
||||
Ubuntu: 'font--ubuntu'
|
||||
},
|
||||
DEFAULT_FONT: 'Open Sans',
|
||||
Preferences: {
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
|
||||
DISPLAY_PREFER_NICKNAME: 'nickname_full_name',
|
||||
DISPLAY_PREFER_FULL_NAME: 'full_name',
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
|
||||
TUTORIAL_STEP: 'tutorial_step',
|
||||
CHANNEL_DISPLAY_MODE: 'channel_display_mode',
|
||||
CHANNEL_DISPLAY_MODE_CENTERED: 'centered',
|
||||
CHANNEL_DISPLAY_MODE_FULL_SCREEN: 'full',
|
||||
CHANNEL_DISPLAY_MODE_DEFAULT: 'full',
|
||||
MESSAGE_DISPLAY: 'message_display',
|
||||
MESSAGE_DISPLAY_CLEAN: 'clean',
|
||||
MESSAGE_DISPLAY_COMPACT: 'compact',
|
||||
MESSAGE_DISPLAY_DEFAULT: 'clean',
|
||||
COLLAPSE_DISPLAY: 'collapse_previews',
|
||||
COLLAPSE_DISPLAY_DEFAULT: 'false',
|
||||
USE_MILITARY_TIME: 'use_military_time'
|
||||
},
|
||||
TutorialSteps: {
|
||||
INTRO_SCREENS: 0,
|
||||
POST_POPOVER: 1,
|
||||
@@ -779,3 +784,5 @@ export default {
|
||||
PERMISSIONS_TEAM_ADMIN: 'team_admin',
|
||||
PERMISSIONS_SYSTEM_ADMIN: 'system_admin'
|
||||
};
|
||||
|
||||
export default Constants;
|
||||
|
||||
Ссылка в новой задаче
Block a user