From e56d21a9208209d515b645f95d293eae51f51f8d Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 30 Oct 2015 16:33:51 -0400 Subject: Added a confirmation dialog for unsaved theme changes and removed unnecessary dialog close handling --- web/react/components/confirm_modal.jsx | 4 +- .../components/user_settings/user_settings.jsx | 4 +- .../user_settings/user_settings_advanced.jsx | 13 --- .../user_settings/user_settings_appearance.jsx | 43 +++++----- .../user_settings/user_settings_display.jsx | 10 --- .../user_settings/user_settings_general.jsx | 15 ---- .../user_settings/user_settings_integrations.jsx | 12 --- .../user_settings/user_settings_modal.jsx | 93 ++++++++++++++++++---- .../user_settings/user_settings_notifications.jsx | 14 ---- .../user_settings/user_settings_security.jsx | 16 ---- 10 files changed, 106 insertions(+), 118 deletions(-) (limited to 'web') diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx index 60069b2b1..bc3a0b814 100644 --- a/web/react/components/confirm_modal.jsx +++ b/web/react/components/confirm_modal.jsx @@ -11,9 +11,7 @@ export default class ConfirmModal extends React.Component { } handleConfirm() { - if (this.props.onConfirm) { - this.props.onConfirm(); - } + this.props.onConfirm(); } render() { diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx index ecba238f9..d569b9d6e 100644 --- a/web/react/components/user_settings/user_settings.jsx +++ b/web/react/components/user_settings/user_settings.jsx @@ -77,6 +77,7 @@ export default class UserSettings extends React.Component { activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} + setRequireConfirm={this.props.setRequireConfirm} /> ); @@ -132,5 +133,6 @@ UserSettings.propTypes = { activeTab: React.PropTypes.string, activeSection: React.PropTypes.string, updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func + updateTab: React.PropTypes.func, + setRequireConfirm: React.PropTypes.func.isRequired }; diff --git a/web/react/components/user_settings/user_settings_advanced.jsx b/web/react/components/user_settings/user_settings_advanced.jsx index 910444735..8d91d264a 100644 --- a/web/react/components/user_settings/user_settings_advanced.jsx +++ b/web/react/components/user_settings/user_settings_advanced.jsx @@ -13,7 +13,6 @@ export default class AdvancedSettingsDisplay extends React.Component { this.updateSection = this.updateSection.bind(this); this.updateSetting = this.updateSetting.bind(this); - this.handleClose = this.handleClose.bind(this); this.setupInitialState = this.setupInitialState.bind(this); this.state = this.setupInitialState(); @@ -59,18 +58,6 @@ export default class AdvancedSettingsDisplay extends React.Component { this.props.updateSection(section); } - handleClose() { - this.updateSection(''); - } - - componentDidMount() { - $('#user_settings').on('hidden.bs.modal', this.handleClose); - } - - componentWillUnmount() { - $('#user_settings').off('hidden.bs.modal', this.handleClose); - } - render() { const serverError = this.state.serverError || null; let ctrlSendSection; diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx index 42c3fd65d..b3584e992 100644 --- a/web/react/components/user_settings/user_settings_appearance.jsx +++ b/web/react/components/user_settings/user_settings_appearance.jsx @@ -25,8 +25,7 @@ export default class UserSettingsAppearance extends React.Component { this.state = this.getStateFromStores(); - this.originalTheme = this.state.theme; - this.originalCodeTheme = this.state.theme.codeTheme; + this.originalTheme = Object.assign({}, this.state.theme); } componentDidMount() { UserStore.addChangeListener(this.onChange); @@ -34,7 +33,6 @@ export default class UserSettingsAppearance extends React.Component { if (this.props.activeSection === 'theme') { $(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border'); } - $('#user_settings').on('hidden.bs.modal', this.handleClose); } componentDidUpdate() { if (this.props.activeSection === 'theme') { @@ -44,14 +42,15 @@ export default class UserSettingsAppearance extends React.Component { } componentWillUnmount() { UserStore.removeChangeListener(this.onChange); - $('#user_settings').off('hidden.bs.modal', this.handleClose); + + this.handleClose(); } getStateFromStores() { const user = UserStore.getCurrentUser(); let theme = null; if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) { - theme = user.theme_props; + theme = Object.assign({}, user.theme_props); } else { theme = $.extend(true, {}, Constants.THEMES.default); } @@ -86,11 +85,11 @@ export default class UserSettingsAppearance extends React.Component { me: data }); - $('#user_settings').off('hidden.bs.modal', this.handleClose); - this.props.updateTab('general'); + this.props.setRequireConfirm(false); + this.originalTheme = Object.assign({}, this.state.theme); + $('.ps-container.modal-body').scrollTop(0); $('.ps-container.modal-body').perfectScrollbar('update'); - $('#user_settings').modal('hide'); }, (err) => { var state = this.getStateFromStores(); @@ -103,29 +102,36 @@ export default class UserSettingsAppearance extends React.Component { if (!theme.codeTheme) { theme.codeTheme = this.state.theme.codeTheme; } + + let themeChanged = this.state.theme.length === theme.length; + if (!themeChanged) { + for (const field in theme) { + if (theme.hasOwnProperty(field)) { + if (this.state.theme[field] !== theme[field]) { + themeChanged = true; + break; + } + } + } + } + + this.props.setRequireConfirm(themeChanged); + this.setState({theme}); Utils.applyTheme(theme); } updateCodeTheme(codeTheme) { var theme = this.state.theme; theme.codeTheme = codeTheme; - this.setState({theme}); - Utils.applyTheme(theme); + this.updateTheme(theme); } updateType(type) { this.setState({type}); } handleClose() { const state = this.getStateFromStores(); - state.serverError = null; - state.theme.codeTheme = this.originalCodeTheme; Utils.applyTheme(state.theme); - - this.setState(state); - - $('.ps-container.modal-body').scrollTop(0); - $('.ps-container.modal-body').perfectScrollbar('update'); } handleImportModal() { AppDispatcher.handleViewAction({ @@ -251,5 +257,6 @@ UserSettingsAppearance.defaultProps = { }; UserSettingsAppearance.propTypes = { activeSection: React.PropTypes.string, - updateTab: React.PropTypes.func + updateTab: React.PropTypes.func, + setRequireConfirm: React.PropTypes.func.isRequired }; diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx index d086c78a9..8c31fdd93 100644 --- a/web/react/components/user_settings/user_settings_display.jsx +++ b/web/react/components/user_settings/user_settings_display.jsx @@ -25,7 +25,6 @@ export default class UserSettingsDisplay extends React.Component { this.handleClockRadio = this.handleClockRadio.bind(this); this.handleNameRadio = this.handleNameRadio.bind(this); this.updateSection = this.updateSection.bind(this); - this.handleClose = this.handleClose.bind(this); this.state = getDisplayStateFromStores(); } @@ -53,15 +52,6 @@ export default class UserSettingsDisplay extends React.Component { this.setState(getDisplayStateFromStores()); this.props.updateSection(section); } - handleClose() { - this.updateSection(''); - } - componentDidMount() { - $('#user_settings').on('hidden.bs.modal', this.handleClose); - } - componentWillUnmount() { - $('#user_settings').off('hidden.bs.modal', this.handleClose); - } render() { const serverError = this.state.serverError || null; let clockSection; diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx index 3adac197a..8cd71f01c 100644 --- a/web/react/components/user_settings/user_settings_general.jsx +++ b/web/react/components/user_settings/user_settings_general.jsx @@ -32,7 +32,6 @@ export default class UserSettingsGeneralTab extends React.Component { this.updatePicture = this.updatePicture.bind(this); this.updateSection = this.updateSection.bind(this); - this.handleClose = this.handleClose.bind(this); this.setupInitialState = this.setupInitialState.bind(this); this.state = this.setupInitialState(props); @@ -210,20 +209,6 @@ export default class UserSettingsGeneralTab extends React.Component { this.submitActive = false; this.props.updateSection(section); } - handleClose() { - $(ReactDOM.findDOMNode(this)).find('.form-control').each(function clearForms() { - this.value = ''; - }); - - this.setState(assign({}, this.setupInitialState(this.props), {clientError: null, serverError: null, emailError: null})); - this.props.updateSection(''); - } - componentDidMount() { - $('#user_settings').on('hidden.bs.modal', this.handleClose); - } - componentWillUnmount() { - $('#user_settings').off('hidden.bs.modal', this.handleClose); - } setupInitialState(props) { var user = props.user; diff --git a/web/react/components/user_settings/user_settings_integrations.jsx b/web/react/components/user_settings/user_settings_integrations.jsx index 4a9915a1f..95f02d9f4 100644 --- a/web/react/components/user_settings/user_settings_integrations.jsx +++ b/web/react/components/user_settings/user_settings_integrations.jsx @@ -11,24 +11,12 @@ export default class UserSettingsIntegrationsTab extends React.Component { super(props); this.updateSection = this.updateSection.bind(this); - this.handleClose = this.handleClose.bind(this); this.state = {}; } updateSection(section) { this.props.updateSection(section); } - handleClose() { - this.updateSection(''); - $('.ps-container.modal-body').scrollTop(0); - $('.ps-container.modal-body').perfectScrollbar('update'); - } - componentDidMount() { - $('#user_settings').on('hidden.bs.modal', this.handleClose); - } - componentWillUnmount() { - $('#user_settings').off('hidden.bs.modal', this.handleClose); - } render() { let incomingHooksSection; let outgoingHooksSection; diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx index 9f29b912b..4cfc2b3d4 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -1,9 +1,10 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +const ConfirmModal = require('../confirm_modal.jsx'); const Modal = ReactBootstrap.Modal; -var SettingsSidebar = require('../settings_sidebar.jsx'); -var UserSettings = require('./user_settings.jsx'); +const SettingsSidebar = require('../settings_sidebar.jsx'); +const UserSettings = require('./user_settings.jsx'); export default class UserSettingsModal extends React.Component { constructor(props) { @@ -11,26 +12,38 @@ export default class UserSettingsModal extends React.Component { this.handleHide = this.handleHide.bind(this); this.handleHidden = this.handleHidden.bind(this); + this.handleConfirm = this.handleConfirm.bind(this); this.updateTab = this.updateTab.bind(this); this.updateSection = this.updateSection.bind(this); this.state = { active_tab: 'general', - active_section: '' + active_section: '', + showConfirmModal: false }; + + this.requireConfirm = false; } componentDidMount() { $('body').on('click', '.settings-content .modal-back', () => { - $(this).closest('.modal-dialog').removeClass('display--content'); + if (!this.requireConfirm) { + $(this).closest('.modal-dialog').removeClass('display--content'); + } }); $('body').on('click', '.settings-content .modal-header .close', () => { + if (!this.props.show) { + return; + } + this.handleHide(); - setTimeout(() => { - $('.modal-dialog.display--content').removeClass('display--content'); - }, 500); + if (!this.requireConfirm) { + setTimeout(() => { + $('.modal-dialog.display--content').removeClass('display--content'); + }, 500); + } }); } @@ -43,25 +56,64 @@ export default class UserSettingsModal extends React.Component { } } - handleHide() { - // called when the close button is pressed + // called when the close button is pressed + handleHide(skipConfirm) { + if (!skipConfirm && this.requireConfirm) { + this.afterConfirm = () => this.handleHide(true); + this.showConfirmModal(); + + return false; + } + this.props.onModalDismissed(); } + // called after the dialog is fully hidden and faded out handleHidden() { - // called after the dialog is fully hidden and faded out this.setState({ - active_tag: 'general', + active_tab: 'general', active_section: '' }); } - updateTab(tab) { - this.setState({active_tab: tab}); + handleConfirm() { + this.setState({ + showConfirmModal: false + }); + + this.requireConfirm = false; + + if (this.afterConfirm) { + this.afterConfirm(); + this.afterConfirm = null; + } } - updateSection(section) { - this.setState({active_section: section}); + showConfirmModal() { + this.setState({ + showConfirmModal: true + }); + } + + updateTab(tab, skipConfirm) { + if (!skipConfirm && this.requireConfirm) { + this.afterConfirm = () => this.updateTab(tab, true); + this.showConfirmModal(); + } else { + this.setState({ + active_tab: tab, + active_section: '' + }); + } + } + + updateSection(section, skipConfirm) { + if (!skipConfirm && this.requireConfirm) { + this.afterConfirm = () => this.updateSection(section, true); + this.showConfirmModal(); + } else { + this.setState({active_section: section}); + } } render() { @@ -84,7 +136,7 @@ export default class UserSettingsModal extends React.Component { this.handleHide()} onExited={this.handleHidden} > @@ -106,10 +158,19 @@ export default class UserSettingsModal extends React.Component { activeSection={this.state.active_section} updateSection={this.updateSection} updateTab={this.updateTab} + setRequireConfirm={(requireConfirm) => this.requireConfirm = requireConfirm} /> + this.setState({showConfirmModal: false})} + /> ); } diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx index 2b904763c..49a94b74a 100644 --- a/web/react/components/user_settings/user_settings_notifications.jsx +++ b/web/react/components/user_settings/user_settings_notifications.jsx @@ -7,7 +7,6 @@ var SettingItemMax = require('../setting_item_max.jsx'); var client = require('../../utils/client.jsx'); var AsyncClient = require('../../utils/async_client.jsx'); var utils = require('../../utils/utils.jsx'); -var assign = require('object-assign'); function getNotificationsStateFromStores() { var user = UserStore.getCurrentUser(); @@ -77,7 +76,6 @@ export default class NotificationsTab extends React.Component { super(props); this.handleSubmit = this.handleSubmit.bind(this); - this.handleClose = this.handleClose.bind(this); this.updateSection = this.updateSection.bind(this); this.onListenerChange = this.onListenerChange.bind(this); this.handleNotifyRadio = this.handleNotifyRadio.bind(this); @@ -128,27 +126,15 @@ export default class NotificationsTab extends React.Component { }.bind(this) ); } - handleClose() { - $(ReactDOM.findDOMNode(this)).find('.form-control').each(function clearField() { - this.value = ''; - }); - - this.setState(assign({}, getNotificationsStateFromStores(), {serverError: null})); - - this.props.updateTab('general'); - } updateSection(section) { this.setState(getNotificationsStateFromStores()); this.props.updateSection(section); } componentDidMount() { UserStore.addChangeListener(this.onListenerChange); - $('#user_settings').on('hidden.bs.modal', this.handleClose); } componentWillUnmount() { UserStore.removeChangeListener(this.onListenerChange); - $('#user_settings').off('hidden.bs.modal', this.handleClose); - this.props.updateSection(''); } onListenerChange() { var newState = getNotificationsStateFromStores(); diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx index 4d414008e..d4d6bf035 100644 --- a/web/react/components/user_settings/user_settings_security.jsx +++ b/web/react/components/user_settings/user_settings_security.jsx @@ -17,7 +17,6 @@ export default class SecurityTab extends React.Component { this.updateCurrentPassword = this.updateCurrentPassword.bind(this); this.updateNewPassword = this.updateNewPassword.bind(this); this.updateConfirmPassword = this.updateConfirmPassword.bind(this); - this.handleClose = this.handleClose.bind(this); this.setupInitialState = this.setupInitialState.bind(this); const state = this.setupInitialState(); @@ -80,24 +79,9 @@ export default class SecurityTab extends React.Component { updateConfirmPassword(e) { this.setState({confirmPassword: e.target.value}); } - handleClose() { - $(ReactDOM.findDOMNode(this)).find('.form-control').each(function resetValue() { - this.value = ''; - }); - this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null}); - - this.props.updateTab('general'); - } setupInitialState() { return {currentPassword: '', newPassword: '', confirmPassword: ''}; } - componentDidMount() { - $('#user_settings').on('hidden.bs.modal', this.handleClose); - } - componentWillUnmount() { - $('#user_settings').off('hidden.bs.modal', this.handleClose); - this.props.updateSection(''); - } render() { var serverError; if (this.state.serverError) { -- cgit v1.2.3-1-g7c22