summaryrefslogtreecommitdiffstats
path: root/web/react/components/user_settings
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2015-11-04 12:59:41 -0500
committerChristopher Speller <crspeller@gmail.com>2015-11-04 12:59:41 -0500
commitaee6d9b608aceb7b68ab1e1a583917c2c3ee4485 (patch)
tree8908ba8d14cafe8dd1532cc429920b8c243dd465 /web/react/components/user_settings
parent2840c317e5a88264aa044261558dda6d91ee1ff3 (diff)
parenta7ceba2e57f5b693afa51b0aefd04081646c6948 (diff)
downloadchat-aee6d9b608aceb7b68ab1e1a583917c2c3ee4485.tar.gz
chat-aee6d9b608aceb7b68ab1e1a583917c2c3ee4485.tar.bz2
chat-aee6d9b608aceb7b68ab1e1a583917c2c3ee4485.zip
Merge pull request #1287 from hmhealey/plt382
PLT-382 Refactored some modals to use React-Bootstrap and added a confirmation when discarding theme changes
Diffstat (limited to 'web/react/components/user_settings')
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx6
-rw-r--r--web/react/components/user_settings/user_settings.jsx40
-rw-r--r--web/react/components/user_settings/user_settings_advanced.jsx23
-rw-r--r--web/react/components/user_settings/user_settings_appearance.jsx70
-rw-r--r--web/react/components/user_settings/user_settings_developer.jsx11
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx20
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx25
-rw-r--r--web/react/components/user_settings/user_settings_integrations.jsx22
-rw-r--r--web/react/components/user_settings/user_settings_modal.jsx215
-rw-r--r--web/react/components/user_settings/user_settings_notifications.jsx28
-rw-r--r--web/react/components/user_settings/user_settings_security.jsx77
11 files changed, 355 insertions, 182 deletions
diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx
index 1a9ac0ad3..24da106d0 100644
--- a/web/react/components/user_settings/import_theme_modal.jsx
+++ b/web/react/components/user_settings/import_theme_modal.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+const ModalStore = require('../../stores/modal_store.jsx');
const UserStore = require('../../stores/user_store.jsx');
const Utils = require('../../utils/utils.jsx');
const Client = require('../../utils/client.jsx');
@@ -24,10 +25,10 @@ export default class ImportThemeModal extends React.Component {
};
}
componentDidMount() {
- UserStore.addImportModalListener(this.updateShow);
+ ModalStore.addModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow);
}
componentWillUnmount() {
- UserStore.removeImportModalListener(this.updateShow);
+ ModalStore.removeModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow);
}
updateShow(show) {
this.setState({show});
@@ -74,7 +75,6 @@ export default class ImportThemeModal extends React.Component {
this.setState({show: false});
Utils.applyTheme(theme);
- $('#user_settings').modal('show');
},
(err) => {
var state = this.getStateFromStores();
diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx
index 546e26ca3..e089ce973 100644
--- a/web/react/components/user_settings/user_settings.jsx
+++ b/web/react/components/user_settings/user_settings.jsx
@@ -16,6 +16,7 @@ export default class UserSettings extends React.Component {
constructor(props) {
super(props);
+ this.getActiveTab = this.getActiveTab.bind(this);
this.onListenerChange = this.onListenerChange.bind(this);
this.state = {user: UserStore.getCurrentUser()};
@@ -29,10 +30,14 @@ export default class UserSettings extends React.Component {
UserStore.removeChangeListener(this.onListenerChange);
}
+ getActiveTab() {
+ return this.refs.activeTab;
+ }
+
onListenerChange() {
var user = UserStore.getCurrentUser();
if (!utils.areStatesEqual(this.state.user, user)) {
- this.setState({user: user});
+ this.setState({user});
}
}
@@ -41,10 +46,13 @@ export default class UserSettings extends React.Component {
return (
<div>
<GeneralTab
+ ref='activeTab'
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
/>
</div>
);
@@ -52,10 +60,14 @@ export default class UserSettings extends React.Component {
return (
<div>
<SecurityTab
+ ref='activeTab'
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
+ setEnforceFocus={this.props.setEnforceFocus}
/>
</div>
);
@@ -63,10 +75,13 @@ export default class UserSettings extends React.Component {
return (
<div>
<NotificationsTab
+ ref='activeTab'
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
/>
</div>
);
@@ -74,9 +89,14 @@ export default class UserSettings extends React.Component {
return (
<div>
<AppearanceTab
+ ref='activeTab'
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
+ setEnforceFocus={this.props.setEnforceFocus}
+ setRequireConfirm={this.props.setRequireConfirm}
/>
</div>
);
@@ -84,8 +104,11 @@ export default class UserSettings extends React.Component {
return (
<div>
<DeveloperTab
+ ref='activeTab'
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
/>
</div>
);
@@ -93,10 +116,13 @@ export default class UserSettings extends React.Component {
return (
<div>
<IntegrationsTab
+ ref='activeTab'
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
/>
</div>
);
@@ -104,10 +130,13 @@ export default class UserSettings extends React.Component {
return (
<div>
<DisplayTab
+ ref='activeTab'
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
/>
</div>
);
@@ -115,10 +144,13 @@ export default class UserSettings extends React.Component {
return (
<div>
<AdvancedTab
+ ref='activeTab'
user={this.state.user}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
updateTab={this.props.updateTab}
+ closeModal={this.props.closeModal}
+ collapseModal={this.props.collapseModal}
/>
</div>
);
@@ -132,5 +164,9 @@ UserSettings.propTypes = {
activeTab: React.PropTypes.string,
activeSection: React.PropTypes.string,
updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func
+ updateTab: React.PropTypes.func,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired,
+ setEnforceFocus: React.PropTypes.func.isRequired,
+ 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..2616981ba 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;
@@ -139,6 +126,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
@@ -146,7 +134,10 @@ export default class AdvancedSettingsDisplay extends React.Component {
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
{'Advanced Settings'}
</h4>
</div>
@@ -165,5 +156,7 @@ AdvancedSettingsDisplay.propTypes = {
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string
+ activeSection: React.PropTypes.string,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx
index 7b4b54e27..425645c1f 100644
--- a/web/react/components/user_settings/user_settings_appearance.jsx
+++ b/web/react/components/user_settings/user_settings_appearance.jsx
@@ -20,13 +20,13 @@ export default class UserSettingsAppearance extends React.Component {
this.submitTheme = this.submitTheme.bind(this);
this.updateTheme = this.updateTheme.bind(this);
this.updateCodeTheme = this.updateCodeTheme.bind(this);
- this.handleClose = this.handleClose.bind(this);
+ this.deactivate = this.deactivate.bind(this);
+ this.resetFields = this.resetFields.bind(this);
this.handleImportModal = this.handleImportModal.bind(this);
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 +34,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 +43,13 @@ export default class UserSettingsAppearance extends React.Component {
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onChange);
- $('#user_settings').off('hidden.bs.modal', 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);
}
@@ -73,6 +71,8 @@ export default class UserSettingsAppearance extends React.Component {
if (!Utils.areStatesEqual(this.state, newState)) {
this.setState(newState);
}
+
+ this.props.setEnforceFocus(true);
}
submitTheme(e) {
e.preventDefault();
@@ -86,11 +86,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,37 +103,53 @@ 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() {
+ deactivate() {
const state = this.getStateFromStores();
- state.serverError = null;
- state.theme.codeTheme = this.originalCodeTheme;
Utils.applyTheme(state.theme);
-
+ }
+ resetFields() {
+ const state = this.getStateFromStores();
+ state.serverError = null;
this.setState(state);
- $('.ps-container.modal-body').scrollTop(0);
- $('.ps-container.modal-body').perfectScrollbar('update');
- $('#user_settings').modal('hide');
+ Utils.applyTheme(state.theme);
+
+ this.props.setRequireConfirm(false);
}
handleImportModal() {
- $('#user_settings').modal('hide');
AppDispatcher.handleViewAction({
type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL,
value: true
});
+
+ this.props.setEnforceFocus(false);
}
render() {
var serverError;
@@ -204,7 +220,7 @@ export default class UserSettingsAppearance extends React.Component {
<a
className='btn btn-sm theme'
href='#'
- onClick={this.handleClose}
+ onClick={this.resetFields}
>
{'Cancel'}
</a>
@@ -218,8 +234,8 @@ export default class UserSettingsAppearance extends React.Component {
<button
type='button'
className='close'
- data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
@@ -227,7 +243,11 @@ export default class UserSettingsAppearance extends React.Component {
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>{'Appearance Settings'}
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
+ {'Appearance Settings'}
</h4>
</div>
<div className='user-settings'>
@@ -253,5 +273,9 @@ UserSettingsAppearance.defaultProps = {
};
UserSettingsAppearance.propTypes = {
activeSection: React.PropTypes.string,
- updateTab: React.PropTypes.func
+ updateTab: React.PropTypes.func,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired,
+ setRequireConfirm: React.PropTypes.func.isRequired,
+ setEnforceFocus: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/user_settings/user_settings_developer.jsx b/web/react/components/user_settings/user_settings_developer.jsx
index c2d7a9710..e6adba1d4 100644
--- a/web/react/components/user_settings/user_settings_developer.jsx
+++ b/web/react/components/user_settings/user_settings_developer.jsx
@@ -63,6 +63,7 @@ export default class DeveloperTab extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
@@ -70,7 +71,11 @@ export default class DeveloperTab extends React.Component {
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>{'Developer Settings'}
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
+ {'Developer Settings'}
</h4>
</div>
<div className='user-settings'>
@@ -89,5 +94,7 @@ DeveloperTab.defaultProps = {
};
DeveloperTab.propTypes = {
activeSection: React.PropTypes.string,
- updateSection: React.PropTypes.func
+ updateSection: React.PropTypes.func,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: 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..3c12ead23 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;
@@ -227,6 +217,7 @@ export default class UserSettingsDisplay extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
@@ -234,7 +225,10 @@ export default class UserSettingsDisplay extends React.Component {
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
{'Display Settings'}
</h4>
</div>
@@ -255,5 +249,7 @@ UserSettingsDisplay.propTypes = {
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string
+ activeSection: React.PropTypes.string,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index 3adac197a..9f0c16194 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;
@@ -579,6 +564,7 @@ export default class UserSettingsGeneralTab extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
@@ -586,7 +572,10 @@ export default class UserSettingsGeneralTab extends React.Component {
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
{'General Settings'}
</h4>
</div>
@@ -613,5 +602,7 @@ UserSettingsGeneralTab.propTypes = {
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string
+ activeSection: React.PropTypes.string,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/user_settings/user_settings_integrations.jsx b/web/react/components/user_settings/user_settings_integrations.jsx
index 4a9915a1f..744a6beea 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;
@@ -104,6 +92,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
</button>
@@ -111,7 +100,10 @@ export default class UserSettingsIntegrationsTab extends React.Component {
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
{'Integration Settings'}
</h4>
</div>
@@ -132,5 +124,7 @@ UserSettingsIntegrationsTab.propTypes = {
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string
+ activeSection: React.PropTypes.string,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx
index 18dd490e7..4dcf32cb9 100644
--- a/web/react/components/user_settings/user_settings_modal.jsx
+++ b/web/react/components/user_settings/user_settings_modal.jsx
@@ -1,34 +1,161 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-var SettingsSidebar = require('../settings_sidebar.jsx');
-var UserSettings = require('./user_settings.jsx');
+const ConfirmModal = require('../confirm_modal.jsx');
+const Modal = ReactBootstrap.Modal;
+const SettingsSidebar = require('../settings_sidebar.jsx');
+const UserSettings = require('./user_settings.jsx');
export default class UserSettingsModal extends React.Component {
constructor(props) {
super(props);
+ this.handleHide = this.handleHide.bind(this);
+ this.handleHidden = this.handleHidden.bind(this);
+ this.handleCollapse = this.handleCollapse.bind(this);
+ this.handleConfirm = this.handleConfirm.bind(this);
+ this.handleCancelConfirmation = this.handleCancelConfirmation.bind(this);
+
+ this.deactivateTab = this.deactivateTab.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.collapseModal = this.collapseModal.bind(this);
+
this.updateTab = this.updateTab.bind(this);
this.updateSection = this.updateSection.bind(this);
- this.state = {active_tab: 'general', active_section: ''};
+ this.state = {
+ active_tab: 'general',
+ active_section: '',
+ showConfirmModal: false,
+ enforceFocus: true
+ };
+
+ this.requireConfirm = false;
+ }
+
+ componentDidUpdate(prevProps) {
+ if (!prevProps.show && this.props.show) {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300);
+ if ($(window).width() > 768) {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ }
+ }
+ }
+
+ // Called when the close button is pressed on the main modal
+ handleHide() {
+ if (this.requireConfirm) {
+ this.afterConfirm = () => this.handleHide();
+ this.showConfirmModal();
+
+ return false;
+ }
+
+ this.deactivateTab();
+ this.props.onModalDismissed();
}
- componentDidMount() {
- $('body').on('click', '.modal-back', function changeDisplay() {
- $(this).closest('.modal-dialog').removeClass('display--content');
+
+ // called after the dialog is fully hidden and faded out
+ handleHidden() {
+ this.setState({
+ active_tab: 'general',
+ active_section: ''
});
- $('body').on('click', '.modal-header .close', () => {
- setTimeout(() => {
- $('.modal-dialog.display--content').removeClass('display--content');
- }, 500);
+ }
+
+ // Called to hide the settings pane when on mobile
+ handleCollapse() {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).closest('.modal-dialog').removeClass('display--content');
+
+ this.deactivateTab();
+
+ this.setState({
+ active_tab: '',
+ active_section: ''
});
}
- updateTab(tab) {
- this.setState({active_tab: tab});
+
+ handleConfirm() {
+ this.setState({
+ showConfirmModal: false,
+ enforceFocus: true
+ });
+
+ this.requireConfirm = false;
+
+ if (this.afterConfirm) {
+ this.afterConfirm();
+ this.afterConfirm = null;
+ }
}
- updateSection(section) {
- this.setState({active_section: section});
+
+ handleCancelConfirmation() {
+ this.setState({
+ showConfirmModal: false,
+ enforceFocus: true
+ });
+
+ this.afterConfirm = null;
}
+
+ showConfirmModal(afterConfirm) {
+ this.setState({
+ showConfirmModal: true,
+ enforceFocus: false
+ });
+
+ if (afterConfirm) {
+ this.afterConfirm = afterConfirm;
+ }
+ }
+
+ // Called to let settings tab perform cleanup before being closed
+ deactivateTab() {
+ const activeTab = this.refs.userSettings.getActiveTab();
+ if (activeTab && activeTab.deactivate) {
+ activeTab.deactivate();
+ }
+ }
+
+ // Called by settings tabs when their close button is pressed
+ closeModal() {
+ if (this.requireConfirm) {
+ this.showConfirmModal(this.closeModal);
+ } else {
+ this.handleHide();
+ }
+ }
+
+ // Called by settings tabs when their back button is pressed
+ collapseModal() {
+ if (this.requireConfirm) {
+ this.showConfirmModal(this.collapseModal);
+ } else {
+ this.handleCollapse();
+ }
+ }
+
+ updateTab(tab, skipConfirm) {
+ if (!skipConfirm && this.requireConfirm) {
+ this.showConfirmModal(() => this.updateTab(tab, true));
+ } else {
+ this.deactivateTab();
+
+ this.setState({
+ active_tab: tab,
+ active_section: ''
+ });
+ }
+ }
+
+ updateSection(section, skipConfirm) {
+ if (!skipConfirm && this.requireConfirm) {
+ this.showConfirmModal(() => this.updateSection(section, true));
+ } else {
+ this.setState({active_section: section});
+ }
+ }
+
render() {
var tabs = [];
tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
@@ -46,33 +173,17 @@ export default class UserSettingsModal extends React.Component {
tabs.push({name: 'advanced', uiName: 'Advanced', icon: 'glyphicon glyphicon-list-alt'});
return (
- <div
- className='modal fade'
- ref='modal'
- id='user_settings'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
+ <Modal
+ dialogClassName='settings-modal'
+ show={this.props.show}
+ onHide={this.handleHide}
+ onExited={this.handleHidden}
+ enforceFocus={this.state.enforceFocus}
>
- <div className='modal-dialog settings-modal'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>{'×'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- {'Account Settings'}
- </h4>
- </div>
- <div className='modal-body'>
+ <Modal.Header closeButton={true}>
+ <Modal.Title>{'Account Settings'}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body ref='modalBody'>
<div className='settings-table'>
<div className='settings-links'>
<SettingsSidebar
@@ -83,17 +194,33 @@ export default class UserSettingsModal extends React.Component {
</div>
<div className='settings-content minimize-settings'>
<UserSettings
+ ref='userSettings'
activeTab={this.state.active_tab}
activeSection={this.state.active_section}
updateSection={this.updateSection}
updateTab={this.updateTab}
+ closeModal={this.closeModal}
+ collapseModal={this.collapseModal}
+ setEnforceFocus={(enforceFocus) => this.setState({enforceFocus})}
+ setRequireConfirm={(requireConfirm) => this.requireConfirm = requireConfirm}
/>
</div>
</div>
- </div>
- </div>
- </div>
- </div>
+ </Modal.Body>
+ <ConfirmModal
+ title='Discard Changes?'
+ message='You have unsaved changes, are you sure you want to discard them?'
+ confirm_button='Yes, Discard'
+ show={this.state.showConfirmModal}
+ onConfirm={this.handleConfirm}
+ onCancel={this.handleCancelConfirmation}
+ />
+ </Modal>
);
}
}
+
+UserSettingsModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onModalDismissed: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx
index 2b904763c..c6f47804f 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();
@@ -644,15 +630,19 @@ export default class NotificationsTab extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
- <span aria-hidden='true'>&times;</span>
+ <span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>
- Notifications
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
+ {'Notification Settings'}
</h4>
</div>
<div
@@ -686,5 +676,7 @@ NotificationsTab.propTypes = {
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
activeSection: React.PropTypes.string,
- activeTab: React.PropTypes.string
+ activeTab: React.PropTypes.string,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx
index 983a10df0..61d13ed8b 100644
--- a/web/react/components/user_settings/user_settings_security.jsx
+++ b/web/react/components/user_settings/user_settings_security.jsx
@@ -3,6 +3,8 @@
var SettingItemMin = require('../setting_item_min.jsx');
var SettingItemMax = require('../setting_item_max.jsx');
+var AccessHistoryModal = require('../access_history_modal.jsx');
+var ActivityLogModal = require('../activity_log_modal.jsx');
var Client = require('../../utils/client.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
var Constants = require('../../utils/constants.jsx');
@@ -11,14 +13,34 @@ export default class SecurityTab extends React.Component {
constructor(props) {
super(props);
+ this.showAccessHistoryModal = this.showAccessHistoryModal.bind(this);
+ this.showActivityLogModal = this.showActivityLogModal.bind(this);
+ this.hideModals = this.hideModals.bind(this);
this.submitPassword = this.submitPassword.bind(this);
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);
- this.state = this.setupInitialState();
+ const state = this.setupInitialState();
+ state.showAccessHistoryModal = false;
+ state.showActivityLogModal = false;
+ this.state = state;
+ }
+ showAccessHistoryModal() {
+ this.props.setEnforceFocus(false);
+ this.setState({showAccessHistoryModal: true});
+ }
+ showActivityLogModal() {
+ this.props.setEnforceFocus(false);
+ this.setState({showActivityLogModal: true});
+ }
+ hideModals() {
+ this.props.setEnforceFocus(true);
+ this.setState({
+ showAccessHistoryModal: false,
+ showActivityLogModal: false
+ });
}
submitPassword(e) {
e.preventDefault();
@@ -75,30 +97,9 @@ export default class SecurityTab extends React.Component {
updateConfirmPassword(e) {
this.setState({confirmPassword: e.target.value});
}
- handleHistoryOpen() {
- $('#user_settings').modal('hide');
- }
- handleDevicesOpen() {
- $('#user_settings').modal('hide');
- }
- 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) {
@@ -236,14 +237,19 @@ export default class SecurityTab extends React.Component {
className='close'
data-dismiss='modal'
aria-label='Close'
+ onClick={this.props.closeModal}
>
- <span aria-hidden='true'>&times;</span>
+ <span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
ref='title'
>
- <i className='modal-back'></i>Security Settings
+ <i
+ className='modal-back'
+ onClick={this.props.collapseModal}
+ />
+ {'Security Settings'}
</h4>
</div>
<div className='user-settings'>
@@ -253,25 +259,29 @@ export default class SecurityTab extends React.Component {
<div className='divider-dark'/>
<br></br>
<a
- data-toggle='modal'
className='security-links theme'
- data-target='#access-history'
href='#'
- onClick={this.handleHistoryOpen}
+ onClick={this.showAccessHistoryModal}
>
<i className='fa fa-clock-o'></i>View Access History
</a>
<b> </b>
<a
- data-toggle='modal'
className='security-links theme'
- data-target='#activity-log'
href='#'
- onClick={this.handleDevicesOpen}
+ onClick={this.showActivityLogModal}
>
<i className='fa fa-globe'></i>View and Logout of Active Sessions
</a>
</div>
+ <AccessHistoryModal
+ show={this.state.showAccessHistoryModal}
+ onModalDismissed={this.hideModals}
+ />
+ <ActivityLogModal
+ show={this.state.showActivityLogModal}
+ onModalDismissed={this.hideModals}
+ />
</div>
);
}
@@ -285,5 +295,8 @@ SecurityTab.propTypes = {
user: React.PropTypes.object,
activeSection: React.PropTypes.string,
updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func
+ updateTab: React.PropTypes.func,
+ closeModal: React.PropTypes.func.isRequired,
+ collapseModal: React.PropTypes.func.isRequired,
+ setEnforceFocus: React.PropTypes.func.isRequired
};