From efa7b8252fff96e52cca82d332fbf812305049b3 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 29 Oct 2015 17:49:38 -0400 Subject: Ported ConfirmModal and InviteMemberModal to React-Bootstrap --- web/react/components/confirm_modal.jsx | 82 +++++++------- web/react/components/invite_member_modal.jsx | 163 ++++++++++++--------------- web/react/components/navbar_dropdown.jsx | 13 ++- web/react/components/sidebar_right_menu.jsx | 19 +++- web/react/pages/channel.jsx | 6 - web/react/utils/channel_intro_mssages.jsx | 3 + 6 files changed, 142 insertions(+), 144 deletions(-) diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx index 12002f33f..60069b2b1 100644 --- a/web/react/components/confirm_modal.jsx +++ b/web/react/components/confirm_modal.jsx @@ -1,70 +1,66 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +const Modal = ReactBootstrap.Modal; + export default class ConfirmModal extends React.Component { constructor(props) { super(props); this.handleConfirm = this.handleConfirm.bind(this); - - this.state = {}; } + handleConfirm() { - $('#' + this.props.parent_id).attr('data-confirm', 'true'); - $('#' + this.props.parent_id).modal('hide'); - $('#' + this.props.id).modal('hide'); + if (this.props.onConfirm) { + this.props.onConfirm(); + } } + render() { return ( - + + {this.props.title} + + + {this.props.message} + + + + + + ); } } ConfirmModal.defaultProps = { - parent_id: '', - id: '', title: '', message: '', confirm_button: '' }; ConfirmModal.propTypes = { - parent_id: React.PropTypes.string, - id: React.PropTypes.string, + show: React.PropTypes.bool.isRequired, title: React.PropTypes.string, message: React.PropTypes.string, - confirm_button: React.PropTypes.string + confirm_button: React.PropTypes.string, + onConfirm: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired }; diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx index bea700725..71f72625d 100644 --- a/web/react/components/invite_member_modal.jsx +++ b/web/react/components/invite_member_modal.jsx @@ -7,11 +7,14 @@ var UserStore = require('../stores/user_store.jsx'); var TeamStore = require('../stores/team_store.jsx'); var ConfirmModal = require('./confirm_modal.jsx'); +const Modal = ReactBootstrap.Modal; + export default class InviteMemberModal extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); + this.handleHide = this.handleHide.bind(this); this.addInviteFields = this.addInviteFields.bind(this); this.clearFields = this.clearFields.bind(this); this.removeInviteFields = this.removeInviteFields.bind(this); @@ -22,38 +25,11 @@ export default class InviteMemberModal extends React.Component { emailErrors: {}, firstNameErrors: {}, lastNameErrors: {}, - emailEnabled: global.window.mm_config.SendEmailNotifications === 'true' + emailEnabled: global.window.mm_config.SendEmailNotifications === 'true', + showConfirmModal: false }; } - componentDidMount() { - var self = this; - $('#invite_member').on('hide.bs.modal', function hide(e) { - if ($('#invite_member').attr('data-confirm') === 'true') { - $('#invite_member').attr('data-confirm', 'false'); - return; - } - - var notEmpty = false; - for (var i = 0; i < self.state.inviteIds.length; i++) { - var index = self.state.inviteIds[i]; - if (ReactDOM.findDOMNode(self.refs['email' + index]).value.trim() !== '') { - notEmpty = true; - break; - } - } - - if (notEmpty) { - $('#confirm_invite_modal').modal('show'); - e.preventDefault(); - } - }); - - $('#invite_member').on('hidden.bs.modal', function show() { - self.clearFields(); - }); - } - handleSubmit() { if (!this.state.emailEnabled) { return; @@ -94,25 +70,55 @@ export default class InviteMemberModal extends React.Component { var data = {}; data.invites = invites; - Client.inviteMembers(data, - function success() { - $(ReactDOM.findDOMNode(this.refs.modal)).attr('data-confirm', 'true'); - $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide'); - }.bind(this), - function fail(err) { + Client.inviteMembers( + data, + () => { + this.handleHide(false); + }, + (err) => { if (err.message === 'This person is already on your team') { emailErrors[err.detailed_error] = err.message; this.setState({emailErrors: emailErrors}); } else { this.setState({serverError: err.message}); } - }.bind(this) + } ); } - componentDidUpdate() { - $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200); - $(ReactDOM.findDOMNode(this.refs.modalBody)).css('overflow-y', 'scroll'); + handleHide(requireConfirm) { + if (requireConfirm) { + var notEmpty = false; + for (var i = 0; i < this.state.inviteIds.length; i++) { + var index = this.state.inviteIds[i]; + if (ReactDOM.findDOMNode(this.refs['email' + index]).value.trim() !== '') { + notEmpty = true; + break; + } + } + + if (notEmpty) { + this.setState({ + showConfirmModal: true + }); + + return; + } + } + + this.clearFields(); + + this.setState({showConfirmModal: false}); + this.props.onModalDismissed(); + } + + 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(); + } + } } addInviteFields() { @@ -292,7 +298,7 @@ export default class InviteMemberModal extends React.Component { ); } else { var teamInviteLink = null; - if (currentUser && this.props.teamType === 'O') { + if (currentUser && TeamStore.getCurrent().type === 'O') { var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id; var link = ( @@ -302,11 +308,7 @@ export default class InviteMemberModal extends React.Component { data-target='#get_link' data-title='Team Invite' data-value={linkUrl} - onClick={ - function click() { - $('#invite_member').modal('hide'); - } - } + onClick={() => this.handleHide(this, false)} >Team Invite Link ); @@ -327,64 +329,49 @@ export default class InviteMemberModal extends React.Component { return (
- + {sendButton} + + this.setState({showConfirmModal: false})} />
); } - return
; + + return null; } } InviteMemberModal.propTypes = { - teamType: React.PropTypes.string + show: React.PropTypes.bool.isRequired, + onModalDismissed: React.PropTypes.func.isRequired }; diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index dc21fad21..7d7acf5b9 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -7,6 +7,7 @@ var UserStore = require('../stores/user_store.jsx'); var TeamStore = require('../stores/team_store.jsx'); var AboutBuildModal = require('./about_build_modal.jsx'); +var InviteMemberModal = require('./invite_member_modal.jsx'); var Constants = require('../utils/constants.jsx'); @@ -41,7 +42,10 @@ export default class NavbarDropdown extends React.Component { this.onListenerChange = this.onListenerChange.bind(this); this.aboutModalDismissed = this.aboutModalDismissed.bind(this); - this.state = getStateFromStores(); + const state = getStateFromStores(); + state.showAboutModal = false; + state.showInviteMemberModal = false; + this.state = state; } handleLogoutClick(e) { e.preventDefault(); @@ -96,8 +100,7 @@ export default class NavbarDropdown extends React.Component {
  • this.setState({showInviteMemberModal: true})} > {'Invite New Member'} @@ -268,6 +271,10 @@ export default class NavbarDropdown extends React.Component { show={this.state.showAboutModal} onModalDismissed={this.aboutModalDismissed} /> + this.setState({showInviteMemberModal: false})} + />
  • diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index 9350bbd42..5da8829ad 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +var InviteMemberModal = require('./invite_member_modal.jsx'); var UserStore = require('../stores/user_store.jsx'); var TeamStore = require('../stores/team_store.jsx'); var client = require('../utils/client.jsx'); @@ -15,6 +16,10 @@ export default class SidebarRightMenu extends React.Component { super(props); this.handleLogoutClick = this.handleLogoutClick.bind(this); + + this.state = { + showInviteMemberModal: false + }; } handleLogoutClick(e) { @@ -38,10 +43,12 @@ export default class SidebarRightMenu extends React.Component { inviteLink = (
  • - Invite New Member + this.setState({showInviteMemberModal: true})} + > + Invite New Member +
  • ); @@ -141,6 +148,10 @@ export default class SidebarRightMenu extends React.Component { >Report a Problem
    + this.setState({showInviteMemberModal: false})} + /> ); } diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 067dcde50..e0272d276 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -9,7 +9,6 @@ var ErrorStore = require('../stores/error_store.jsx'); var MentionList = require('../components/mention_list.jsx'); var GetLinkModal = require('../components/get_link_modal.jsx'); -var MemberInviteModal = require('../components/invite_member_modal.jsx'); var EditChannelModal = require('../components/edit_channel_modal.jsx'); var DeleteChannelModal = require('../components/delete_channel_modal.jsx'); var RenameChannelModal = require('../components/rename_channel_modal.jsx'); @@ -102,11 +101,6 @@ function setupChannelPage(props) { document.getElementById('team_members_modal') ); - ReactDOM.render( - , - document.getElementById('invite_member_modal') - ); - ReactDOM.render( , document.getElementById('edit_channel_modal') diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx index b3f868456..fdeb17bc1 100644 --- a/web/react/utils/channel_intro_mssages.jsx +++ b/web/react/utils/channel_intro_mssages.jsx @@ -186,6 +186,8 @@ export function createStandardIntroMessage(channel) { ); } + // TODO show channel_invite modal now that it's a React-Bootstrap modal + return (

    {'Beginning of ' + uiName}

    @@ -213,6 +215,7 @@ export function createStandardIntroMessage(channel) { > {'Invite others to this ' + uiType} +
    ); } -- cgit v1.2.3-1-g7c22 From 1675adcc7a30cec836c00d35b0624aa35d373eed Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 30 Oct 2015 10:45:54 -0400 Subject: Ported AccessHistoryModal and ActivityLogModal to React-Bootstrap --- web/react/components/access_history_modal.jsx | 70 +++++++++------------ web/react/components/activity_log_modal.jsx | 72 +++++++++------------- .../user_settings/user_settings_security.jsx | 29 +++++---- web/react/pages/channel.jsx | 12 ---- 4 files changed, 75 insertions(+), 108 deletions(-) diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index f0a31ce90..27959ec7e 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +var Modal = ReactBootstrap.Modal; var UserStore = require('../stores/user_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); var AsyncClient = require('../utils/async_client.jsx'); @@ -30,16 +31,23 @@ export default class AccessHistoryModal extends React.Component { } onShow() { AsyncClient.getAudits(); + + $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300); + if ($(window).width() > 768) { + $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar(); + } } onHide() { - $('#user_settings').modal('show'); this.setState({moreInfo: []}); + this.props.onModalDismissed(); } componentDidMount() { UserStore.addAuditsChangeListener(this.onAuditChange); - $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow); - - $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide); + } + componentDidUpdate(prevProps) { + if (this.props.show && !prevProps.show) { + this.onShow(); + } } componentWillUnmount() { UserStore.removeAuditsChangeListener(this.onAuditChange); @@ -380,43 +388,23 @@ export default class AccessHistoryModal extends React.Component { } return ( -
    - -
    + + + {'Access History'} + + + {content} + + ); } } + +AccessHistoryModal.propTypes = { + show: React.PropTypes.bool.isRequired, + onModalDismissed: React.PropTypes.func.isRequired +}; diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx index 2c944913f..6a24870f6 100644 --- a/web/react/components/activity_log_modal.jsx +++ b/web/react/components/activity_log_modal.jsx @@ -4,6 +4,7 @@ const UserStore = require('../stores/user_store.jsx'); const Client = require('../utils/client.jsx'); const AsyncClient = require('../utils/async_client.jsx'); +const Modal = ReactBootstrap.Modal; const LoadingScreen = require('./loading_screen.jsx'); const Utils = require('../utils/utils.jsx'); @@ -49,16 +50,23 @@ export default class ActivityLogModal extends React.Component { } onShow() { AsyncClient.getSessions(); + + $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300); + if ($(window).width() > 768) { + $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar(); + } } onHide() { - $('#user_settings').modal('show'); this.setState({moreInfo: []}); + this.props.onModalDismissed(); } componentDidMount() { UserStore.addSessionsChangeListener(this.onListenerChange); - $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow); - - $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide); + } + componentDidUpdate(prevProps) { + if (this.props.show && !prevProps.show) { + this.onShow(); + } } componentWillUnmount() { UserStore.removeSessionsChangeListener(this.onListenerChange); @@ -151,44 +159,24 @@ export default class ActivityLogModal extends React.Component { } return ( -
    - -
    + + + {'Active Sessions'} + +

    {'Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the \'Logout\' button below to end a session.'}

    + + {content} + +
    ); } } + +ActivityLogModal.propTypes = { + show: React.PropTypes.bool.isRequired, + onModalDismissed: 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..4d414008e 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'); @@ -18,7 +20,10 @@ export default class SecurityTab extends React.Component { 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; } submitPassword(e) { e.preventDefault(); @@ -75,12 +80,6 @@ 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 = ''; @@ -253,25 +252,29 @@ export default class SecurityTab extends React.Component { + this.setState({showAccessHistoryModal: false})} + /> + this.setState({showActivityLogModal: false})} + /> ); } diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index e0272d276..c1085075d 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -23,8 +23,6 @@ var ChannelMembersModal = require('../components/channel_members.jsx'); var ChannelInviteModal = require('../components/channel_invite_modal.jsx'); var TeamMembersModal = require('../components/team_members.jsx'); var ChannelInfoModal = require('../components/channel_info_modal.jsx'); -var AccessHistoryModal = require('../components/access_history_modal.jsx'); -var ActivityLogModal = require('../components/activity_log_modal.jsx'); var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx'); var RegisterAppModal = require('../components/register_app_modal.jsx'); var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx'); @@ -156,16 +154,6 @@ function setupChannelPage(props) { document.getElementById('post_deleted_modal') ); - ReactDOM.render( - , - document.getElementById('access_history_modal') - ); - - ReactDOM.render( - , - document.getElementById('activity_log_modal') - ); - ReactDOM.render( , document.getElementById('removed_from_channel_modal') -- cgit v1.2.3-1-g7c22 From 738568e5a9726b3a1b2536a20ab6627c5e9fb01e Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 30 Oct 2015 11:13:56 -0400 Subject: Ported UserSettingsModal to React-Bootstrap --- web/react/components/navbar_dropdown.jsx | 9 ++- web/react/components/sidebar_right_menu.jsx | 14 +++- .../user_settings/import_theme_modal.jsx | 1 - .../components/user_settings/user_settings.jsx | 2 +- .../user_settings/user_settings_appearance.jsx | 2 - .../user_settings/user_settings_modal.jsx | 88 ++++++++++++++-------- web/react/pages/channel.jsx | 6 -- web/sass-files/sass/partials/_settings.scss | 4 + 8 files changed, 78 insertions(+), 48 deletions(-) diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 7d7acf5b9..81ff11fb5 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -8,6 +8,7 @@ var TeamStore = require('../stores/team_store.jsx'); var AboutBuildModal = require('./about_build_modal.jsx'); var InviteMemberModal = require('./invite_member_modal.jsx'); +var UserSettingsModal = require('./user_settings/user_settings_modal.jsx'); var Constants = require('../utils/constants.jsx'); @@ -43,6 +44,7 @@ export default class NavbarDropdown extends React.Component { this.aboutModalDismissed = this.aboutModalDismissed.bind(this); const state = getStateFromStores(); + state.showUserSettingsModal = false; state.showAboutModal = false; state.showInviteMemberModal = false; this.state = state; @@ -221,8 +223,7 @@ export default class NavbarDropdown extends React.Component {
  • this.setState({showUserSettingsModal: true})} > {'Account Settings'} @@ -267,6 +268,10 @@ export default class NavbarDropdown extends React.Component { {'About Mattermost'}
  • + this.setState({showUserSettingsModal: false})} + /> Account Settings + onClick={() => this.setState({showUserSettingsModal: true})} + > + Account Settings + + {teamSettingsLink} {inviteLink} {teamLink} @@ -148,6 +152,10 @@ export default class SidebarRightMenu extends React.Component { >Report a Problem + this.setState({showUserSettingsModal: false})} + /> this.setState({showInviteMemberModal: false})} diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx index 1a9ac0ad3..384226b20 100644 --- a/web/react/components/user_settings/import_theme_modal.jsx +++ b/web/react/components/user_settings/import_theme_modal.jsx @@ -74,7 +74,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..ecba238f9 100644 --- a/web/react/components/user_settings/user_settings.jsx +++ b/web/react/components/user_settings/user_settings.jsx @@ -32,7 +32,7 @@ export default class UserSettings extends React.Component { onListenerChange() { var user = UserStore.getCurrentUser(); if (!utils.areStatesEqual(this.state.user, user)) { - this.setState({user: user}); + this.setState({user}); } } diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx index 7b4b54e27..42c3fd65d 100644 --- a/web/react/components/user_settings/user_settings_appearance.jsx +++ b/web/react/components/user_settings/user_settings_appearance.jsx @@ -126,10 +126,8 @@ export default class UserSettingsAppearance extends React.Component { $('.ps-container.modal-body').scrollTop(0); $('.ps-container.modal-body').perfectScrollbar('update'); - $('#user_settings').modal('hide'); } handleImportModal() { - $('#user_settings').modal('hide'); AppDispatcher.handleViewAction({ type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL, value: true diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx index 18dd490e7..9f29b912b 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +const Modal = ReactBootstrap.Modal; var SettingsSidebar = require('../settings_sidebar.jsx'); var UserSettings = require('./user_settings.jsx'); @@ -8,27 +9,61 @@ export default class UserSettingsModal extends React.Component { constructor(props) { super(props); + this.handleHide = this.handleHide.bind(this); + this.handleHidden = this.handleHidden.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: '' + }; } + componentDidMount() { - $('body').on('click', '.modal-back', function changeDisplay() { + $('body').on('click', '.settings-content .modal-back', () => { $(this).closest('.modal-dialog').removeClass('display--content'); }); - $('body').on('click', '.modal-header .close', () => { + $('body').on('click', '.settings-content .modal-header .close', () => { + this.handleHide(); + setTimeout(() => { $('.modal-dialog.display--content').removeClass('display--content'); }, 500); }); } + + 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(); + } + } + } + + handleHide() { + // called when the close button is pressed + this.props.onModalDismissed(); + } + + handleHidden() { + // called after the dialog is fully hidden and faded out + this.setState({ + active_tag: 'general', + active_section: '' + }); + } + updateTab(tab) { this.setState({active_tab: tab}); } + updateSection(section) { this.setState({active_section: section}); } + render() { var tabs = []; tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'}); @@ -46,33 +81,16 @@ export default class UserSettingsModal extends React.Component { tabs.push({name: 'advanced', uiName: 'Advanced', icon: 'glyphicon glyphicon-list-alt'}); return ( - ); @@ -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 From 02b9414e0f0169c99044ad96456290766ffcd585 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 30 Oct 2015 17:26:39 -0400 Subject: Added handling of keyboard focus to UserSettingsModal and its children --- web/react/components/confirm_modal.jsx | 1 - .../components/user_settings/user_settings.jsx | 3 +++ .../user_settings/user_settings_appearance.jsx | 7 +++++- .../user_settings/user_settings_modal.jsx | 23 +++++++++++++---- .../user_settings/user_settings_security.jsx | 29 ++++++++++++++++++---- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx index bc3a0b814..cdef1c1ea 100644 --- a/web/react/components/confirm_modal.jsx +++ b/web/react/components/confirm_modal.jsx @@ -20,7 +20,6 @@ export default class ConfirmModal extends React.Component { className='modal-confirm' show={this.props.show} onHide={this.props.onCancel} - enforceFocus={false} > {this.props.title} diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx index d569b9d6e..cebbbebce 100644 --- a/web/react/components/user_settings/user_settings.jsx +++ b/web/react/components/user_settings/user_settings.jsx @@ -56,6 +56,7 @@ export default class UserSettings extends React.Component { activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} + setEnforceFocus={this.props.setEnforceFocus} /> ); @@ -77,6 +78,7 @@ export default class UserSettings extends React.Component { activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} + setEnforceFocus={this.props.setEnforceFocus} setRequireConfirm={this.props.setRequireConfirm} /> @@ -134,5 +136,6 @@ UserSettings.propTypes = { activeSection: React.PropTypes.string, updateSection: React.PropTypes.func, updateTab: React.PropTypes.func, + setEnforceFocus: React.PropTypes.func.isRequired, setRequireConfirm: 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 b3584e992..28fc9018a 100644 --- a/web/react/components/user_settings/user_settings_appearance.jsx +++ b/web/react/components/user_settings/user_settings_appearance.jsx @@ -72,6 +72,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(); @@ -138,6 +140,8 @@ export default class UserSettingsAppearance extends React.Component { type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL, value: true }); + + this.props.setEnforceFocus(false); } render() { var serverError; @@ -258,5 +262,6 @@ UserSettingsAppearance.defaultProps = { UserSettingsAppearance.propTypes = { activeSection: React.PropTypes.string, updateTab: React.PropTypes.func, - setRequireConfirm: React.PropTypes.func.isRequired + setRequireConfirm: React.PropTypes.func.isRequired, + setEnforceFocus: 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 4cfc2b3d4..94b07f3a5 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -13,6 +13,7 @@ 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.handleCancelConfirmation = this.handleCancelConfirmation.bind(this); this.updateTab = this.updateTab.bind(this); this.updateSection = this.updateSection.bind(this); @@ -20,7 +21,8 @@ export default class UserSettingsModal extends React.Component { this.state = { active_tab: 'general', active_section: '', - showConfirmModal: false + showConfirmModal: false, + enforceFocus: true }; this.requireConfirm = false; @@ -78,7 +80,8 @@ export default class UserSettingsModal extends React.Component { handleConfirm() { this.setState({ - showConfirmModal: false + showConfirmModal: false, + enforceFocus: true }); this.requireConfirm = false; @@ -89,9 +92,17 @@ export default class UserSettingsModal extends React.Component { } } + handleCancelConfirmation() { + this.setState({ + showConfirmModal: false, + enforceFocus: true + }); + } + showConfirmModal() { this.setState({ - showConfirmModal: true + showConfirmModal: true, + enforceFocus: false }); } @@ -136,8 +147,9 @@ export default class UserSettingsModal extends React.Component { this.handleHide()} + onHide={this.handleHide} onExited={this.handleHidden} + enforceFocus={this.state.enforceFocus} > {'Account Settings'} @@ -158,6 +170,7 @@ export default class UserSettingsModal extends React.Component { activeSection={this.state.active_section} updateSection={this.updateSection} updateTab={this.updateTab} + setEnforceFocus={(enforceFocus) => this.setState({enforceFocus})} setRequireConfirm={(requireConfirm) => this.requireConfirm = requireConfirm} /> @@ -169,7 +182,7 @@ export default class UserSettingsModal extends React.Component { confirm_button='Yes, Discard' show={this.state.showConfirmModal} onConfirm={this.handleConfirm} - onCancel={() => this.setState({showConfirmModal: false})} + onCancel={this.handleCancelConfirmation} /> ); diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx index d4d6bf035..c2b8a0093 100644 --- a/web/react/components/user_settings/user_settings_security.jsx +++ b/web/react/components/user_settings/user_settings_security.jsx @@ -13,6 +13,9 @@ 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); @@ -24,6 +27,21 @@ export default class SecurityTab extends React.Component { 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(); @@ -238,7 +256,7 @@ export default class SecurityTab extends React.Component { this.setState({showAccessHistoryModal: true})} + onClick={this.showAccessHistoryModal} > View Access History @@ -246,18 +264,18 @@ export default class SecurityTab extends React.Component { this.setState({showActivityLogModal: true})} + onClick={this.showActivityLogModal} > View and Logout of Active Sessions this.setState({showAccessHistoryModal: false})} + onModalDismissed={this.hideModals} /> this.setState({showActivityLogModal: false})} + onModalDismissed={this.hideModals} /> ); @@ -272,5 +290,6 @@ SecurityTab.propTypes = { user: React.PropTypes.object, activeSection: React.PropTypes.string, updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func + updateTab: React.PropTypes.func, + setEnforceFocus: React.PropTypes.func.isRequired }; -- cgit v1.2.3-1-g7c22 From 6428878e20fa54b7bb6b2bf6e0aaaeb637affa59 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 2 Nov 2015 17:35:59 -0500 Subject: Moved InviteMemberModal to be controlled by a store --- web/react/components/invite_member_modal.jsx | 40 +++++++++++++++++---- web/react/components/navbar_dropdown.jsx | 7 +--- web/react/components/sidebar_right_menu.jsx | 9 ++--- .../user_settings/import_theme_modal.jsx | 5 +-- web/react/pages/channel.jsx | 6 ++++ web/react/stores/modal_store.jsx | 42 ++++++++++++++++++++++ web/react/stores/user_store.jsx | 20 ----------- web/react/utils/channel_intro_mssages.jsx | 6 ++-- web/react/utils/constants.jsx | 3 +- 9 files changed, 91 insertions(+), 47 deletions(-) create mode 100644 web/react/stores/modal_store.jsx diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx index 71f72625d..c09477a69 100644 --- a/web/react/components/invite_member_modal.jsx +++ b/web/react/components/invite_member_modal.jsx @@ -2,7 +2,10 @@ // See License.txt for license information. var utils = require('../utils/utils.jsx'); +var ActionTypes = require('../utils/constants.jsx').ActionTypes; +var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Client = require('../utils/client.jsx'); +var ModalStore = require('../stores/modal_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var TeamStore = require('../stores/team_store.jsx'); var ConfirmModal = require('./confirm_modal.jsx'); @@ -13,6 +16,7 @@ export default class InviteMemberModal extends React.Component { constructor(props) { super(props); + this.handleToggle = this.handleToggle.bind(this); this.handleSubmit = this.handleSubmit.bind(this); this.handleHide = this.handleHide.bind(this); this.addInviteFields = this.addInviteFields.bind(this); @@ -20,6 +24,7 @@ export default class InviteMemberModal extends React.Component { this.removeInviteFields = this.removeInviteFields.bind(this); this.state = { + show: false, inviteIds: [0], idCount: 0, emailErrors: {}, @@ -30,6 +35,20 @@ export default class InviteMemberModal extends React.Component { }; } + componentDidMount() { + ModalStore.addModalListener(ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, this.handleToggle); + } + + componentWillUnmount() { + ModalStore.removeModalListener(ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, this.handleToggle); + } + + handleToggle(value) { + this.setState({ + show: value + }); + } + handleSubmit() { if (!this.state.emailEnabled) { return; @@ -108,12 +127,14 @@ export default class InviteMemberModal extends React.Component { this.clearFields(); - this.setState({showConfirmModal: false}); - this.props.onModalDismissed(); + this.setState({ + show: false, + showConfirmModal: false + }); } - componentDidUpdate(prevProps) { - if (!prevProps.show && this.props.show) { + componentDidUpdate(prevProps, prevState) { + if (!prevState.show && this.state.show) { $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300); if ($(window).width() > 768) { $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar(); @@ -331,7 +352,7 @@ export default class InviteMemberModal extends React.Component {
    @@ -369,9 +390,14 @@ export default class InviteMemberModal extends React.Component { return null; } + + static show() { + AppDispatcher.handleViewAction({ + type: ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, + value: true + }); + } } InviteMemberModal.propTypes = { - show: React.PropTypes.bool.isRequired, - onModalDismissed: React.PropTypes.func.isRequired }; diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 81ff11fb5..6c4b278a0 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -46,7 +46,6 @@ export default class NavbarDropdown extends React.Component { const state = getStateFromStores(); state.showUserSettingsModal = false; state.showAboutModal = false; - state.showInviteMemberModal = false; this.state = state; } handleLogoutClick(e) { @@ -102,7 +101,7 @@ export default class NavbarDropdown extends React.Component {
  • this.setState({showInviteMemberModal: true})} + onClick={InviteMemberModal.show} > {'Invite New Member'} @@ -276,10 +275,6 @@ export default class NavbarDropdown extends React.Component { show={this.state.showAboutModal} onModalDismissed={this.aboutModalDismissed} /> - this.setState({showInviteMemberModal: false})} - />
  • diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index 51f9ab1bc..2135e3ef3 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -19,8 +19,7 @@ export default class SidebarRightMenu extends React.Component { this.handleLogoutClick = this.handleLogoutClick.bind(this); this.state = { - showUserSettingsModal: false, - showInviteMemberModal: false + showUserSettingsModal: false }; } @@ -47,7 +46,7 @@ export default class SidebarRightMenu extends React.Component {
  • this.setState({showInviteMemberModal: true})} + onClick={InviteMemberModal.show} > Invite New Member @@ -156,10 +155,6 @@ export default class SidebarRightMenu extends React.Component { show={this.state.showUserSettingsModal} onModalDismissed={() => this.setState({showUserSettingsModal: false})} /> - this.setState({showInviteMemberModal: false})} - />
  • ); } diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx index 384226b20..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}); diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 2e7a26d00..2f872effd 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -25,6 +25,7 @@ var ChannelInfoModal = require('../components/channel_info_modal.jsx'); var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx'); var RegisterAppModal = require('../components/register_app_modal.jsx'); var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx'); +var InviteMemberModal = require('../components/invite_member_modal.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var Constants = require('../utils/constants.jsx'); @@ -78,6 +79,11 @@ function setupChannelPage(props) { document.getElementById('get_link_modal') ); + ReactDOM.render( + , + document.getElementById('invite_member_modal') + ); + ReactDOM.render( , document.getElementById('import_theme_modal') diff --git a/web/react/stores/modal_store.jsx b/web/react/stores/modal_store.jsx new file mode 100644 index 000000000..dc65d48da --- /dev/null +++ b/web/react/stores/modal_store.jsx @@ -0,0 +1,42 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +const AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); +const EventEmitter = require('events').EventEmitter; + +const Constants = require('../utils/constants.jsx'); +const ActionTypes = Constants.ActionTypes; + +class ModalStoreClass extends EventEmitter { + constructor() { + super(); + + this.addModalListener = this.addModalListener.bind(this); + this.removeModalListener = this.removeModalListener.bind(this); + + this.handleEventPayload = this.handleEventPayload.bind(this); + this.dispatchToken = AppDispatcher.register(this.handleEventPayload); + } + + addModalListener(action, callback) { + this.on(action, callback); + } + + removeModalListener(action, callback) { + this.removeListener(action, callback); + } + + handleEventPayload(payload) { + const action = payload.action; + + switch (action.type) { + case ActionTypes.TOGGLE_IMPORT_THEME_MODAL: + case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL: + this.emit(action.type, action.value); + break; + } + } +} + +const ModalStore = new ModalStoreClass(); +export default ModalStore; diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index ce80c5ec9..2ad5a2ffa 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -13,7 +13,6 @@ var CHANGE_EVENT_SESSIONS = 'change_sessions'; var CHANGE_EVENT_AUDITS = 'change_audits'; var CHANGE_EVENT_TEAMS = 'change_teams'; var CHANGE_EVENT_STATUSES = 'change_statuses'; -var TOGGLE_IMPORT_MODAL_EVENT = 'toggle_import_modal'; class UserStoreClass extends EventEmitter { constructor() { @@ -34,9 +33,6 @@ class UserStoreClass extends EventEmitter { this.emitStatusesChange = this.emitStatusesChange.bind(this); this.addStatusesChangeListener = this.addStatusesChangeListener.bind(this); this.removeStatusesChangeListener = this.removeStatusesChangeListener.bind(this); - this.emitToggleImportModal = this.emitToggleImportModal.bind(this); - this.addImportModalListener = this.addImportModalListener.bind(this); - this.removeImportModalListener = this.removeImportModalListener.bind(this); this.getCurrentId = this.getCurrentId.bind(this); this.getCurrentUser = this.getCurrentUser.bind(this); this.setCurrentUser = this.setCurrentUser.bind(this); @@ -124,18 +120,6 @@ class UserStoreClass extends EventEmitter { this.removeListener(CHANGE_EVENT_STATUSES, callback); } - emitToggleImportModal(value) { - this.emit(TOGGLE_IMPORT_MODAL_EVENT, value); - } - - addImportModalListener(callback) { - this.on(TOGGLE_IMPORT_MODAL_EVENT, callback); - } - - removeImportModalListener(callback) { - this.removeListener(TOGGLE_IMPORT_MODAL_EVENT, callback); - } - getCurrentUser() { if (this.getProfiles()[global.window.mm_user.id] == null) { this.saveProfile(global.window.mm_user); @@ -350,10 +334,6 @@ UserStore.dispatchToken = AppDispatcher.register((payload) => { UserStore.pSetStatuses(action.statuses); UserStore.emitStatusesChange(); break; - case ActionTypes.TOGGLE_IMPORT_THEME_MODAL: - UserStore.emitToggleImportModal(action.value); - break; - default: } }); diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx index fdeb17bc1..161c79761 100644 --- a/web/react/utils/channel_intro_mssages.jsx +++ b/web/react/utils/channel_intro_mssages.jsx @@ -3,6 +3,7 @@ // See License.txt for license information. const Utils = require('./utils.jsx'); +const InviteMemberModal = require('../components/invite_member_modal.jsx'); const UserProfile = require('../components/user_profile.jsx'); const ChannelStore = require('../stores/channel_store.jsx'); const Constants = require('../utils/constants.jsx'); @@ -109,8 +110,7 @@ export function createDefaultIntroMessage(channel) { {'Invite others to this team'} @@ -186,8 +186,6 @@ export function createStandardIntroMessage(channel) { ); } - // TODO show channel_invite modal now that it's a React-Bootstrap modal - return (

    {'Beginning of ' + uiName}

    diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 8884d1d10..51cd41b6d 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -39,7 +39,8 @@ module.exports = { RECIEVED_LOGS: null, RECIEVED_ALL_TEAMS: null, - TOGGLE_IMPORT_THEME_MODAL: null + TOGGLE_IMPORT_THEME_MODAL: null, + TOGGLE_INVITE_MEMBER_MODAL: null }), PayloadSources: keyMirror({ -- cgit v1.2.3-1-g7c22 From be442b6221406fae96b2c3a74f1214e058b28e1d Mon Sep 17 00:00:00 2001 From: hmhealey Date: Tue, 3 Nov 2015 12:37:41 -0500 Subject: Changed TeamSettingsModal click handlers to not affect other modals --- web/react/components/team_settings_modal.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx index 17fe31c65..4d47db2a8 100644 --- a/web/react/components/team_settings_modal.jsx +++ b/web/react/components/team_settings_modal.jsx @@ -17,11 +17,13 @@ export default class TeamSettingsModal extends React.Component { }; } componentDidMount() { - $('body').on('click', '.modal-back', function handleBackClick() { + const modal = $(ReactDOM.findDOMNode(this.refs.modal)); + + modal.on('click', '.modal-back', function handleBackClick() { $(this).closest('.modal-dialog').removeClass('display--content'); $(this).closest('.modal-dialog').find('.settings-table .nav li.active').removeClass('active'); }); - $('body').on('click', '.modal-header .close', () => { + modal.on('click', '.modal-header .close', () => { setTimeout(() => { $('.modal-dialog.display--content').removeClass('display--content'); }, 500); -- cgit v1.2.3-1-g7c22 From a7ceba2e57f5b693afa51b0aefd04081646c6948 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Tue, 3 Nov 2015 14:09:09 -0500 Subject: Fixed theme confirmation dialog to work in all cases and removed some jquery magic related to it --- .../components/user_settings/user_settings.jsx | 31 ++++++++ .../user_settings/user_settings_advanced.jsx | 10 ++- .../user_settings/user_settings_appearance.jsx | 28 +++++-- .../user_settings/user_settings_developer.jsx | 11 ++- .../user_settings/user_settings_display.jsx | 10 ++- .../user_settings/user_settings_general.jsx | 10 ++- .../user_settings/user_settings_integrations.jsx | 10 ++- .../user_settings/user_settings_modal.jsx | 91 +++++++++++++++------- .../user_settings/user_settings_notifications.jsx | 14 +++- .../user_settings/user_settings_security.jsx | 11 ++- 10 files changed, 173 insertions(+), 53 deletions(-) diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx index cebbbebce..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,6 +30,10 @@ 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)) { @@ -41,10 +46,13 @@ export default class UserSettings extends React.Component { return (
    ); @@ -52,10 +60,13 @@ export default class UserSettings extends React.Component { return (
    @@ -64,10 +75,13 @@ export default class UserSettings extends React.Component { return (
    ); @@ -75,9 +89,12 @@ export default class UserSettings extends React.Component { return (
    @@ -87,8 +104,11 @@ export default class UserSettings extends React.Component { return (
    ); @@ -96,10 +116,13 @@ export default class UserSettings extends React.Component { return (
    ); @@ -107,10 +130,13 @@ export default class UserSettings extends React.Component { return (
    ); @@ -118,10 +144,13 @@ export default class UserSettings extends React.Component { return (
    ); @@ -136,6 +165,8 @@ UserSettings.propTypes = { activeSection: React.PropTypes.string, updateSection: 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 8d91d264a..2616981ba 100644 --- a/web/react/components/user_settings/user_settings_advanced.jsx +++ b/web/react/components/user_settings/user_settings_advanced.jsx @@ -126,6 +126,7 @@ export default class AdvancedSettingsDisplay extends React.Component { className='close' data-dismiss='modal' aria-label='Close' + onClick={this.props.closeModal} > @@ -133,7 +134,10 @@ export default class AdvancedSettingsDisplay extends React.Component { className='modal-title' ref='title' > - + {'Advanced Settings'}
    @@ -152,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 28fc9018a..425645c1f 100644 --- a/web/react/components/user_settings/user_settings_appearance.jsx +++ b/web/react/components/user_settings/user_settings_appearance.jsx @@ -20,7 +20,8 @@ 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(); @@ -42,8 +43,6 @@ export default class UserSettingsAppearance extends React.Component { } componentWillUnmount() { UserStore.removeChangeListener(this.onChange); - - this.handleClose(); } getStateFromStores() { const user = UserStore.getCurrentUser(); @@ -130,10 +129,19 @@ export default class UserSettingsAppearance extends React.Component { updateType(type) { this.setState({type}); } - handleClose() { + deactivate() { + const state = this.getStateFromStores(); + + Utils.applyTheme(state.theme); + } + resetFields() { const state = this.getStateFromStores(); + state.serverError = null; + this.setState(state); Utils.applyTheme(state.theme); + + this.props.setRequireConfirm(false); } handleImportModal() { AppDispatcher.handleViewAction({ @@ -212,7 +220,7 @@ export default class UserSettingsAppearance extends React.Component { {'Cancel'} @@ -226,8 +234,8 @@ export default class UserSettingsAppearance extends React.Component { @@ -235,7 +243,11 @@ export default class UserSettingsAppearance extends React.Component { className='modal-title' ref='title' > - {'Appearance Settings'} + + {'Appearance Settings'}
    @@ -262,6 +274,8 @@ UserSettingsAppearance.defaultProps = { UserSettingsAppearance.propTypes = { activeSection: React.PropTypes.string, 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} > @@ -70,7 +71,11 @@ export default class DeveloperTab extends React.Component { className='modal-title' ref='title' > - {'Developer Settings'} + + {'Developer 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 8c31fdd93..3c12ead23 100644 --- a/web/react/components/user_settings/user_settings_display.jsx +++ b/web/react/components/user_settings/user_settings_display.jsx @@ -217,6 +217,7 @@ export default class UserSettingsDisplay extends React.Component { className='close' data-dismiss='modal' aria-label='Close' + onClick={this.props.closeModal} > @@ -224,7 +225,10 @@ export default class UserSettingsDisplay extends React.Component { className='modal-title' ref='title' > - + {'Display Settings'}
    @@ -245,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 8cd71f01c..9f0c16194 100644 --- a/web/react/components/user_settings/user_settings_general.jsx +++ b/web/react/components/user_settings/user_settings_general.jsx @@ -564,6 +564,7 @@ export default class UserSettingsGeneralTab extends React.Component { className='close' data-dismiss='modal' aria-label='Close' + onClick={this.props.closeModal} > @@ -571,7 +572,10 @@ export default class UserSettingsGeneralTab extends React.Component { className='modal-title' ref='title' > - + {'General Settings'} @@ -598,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 95f02d9f4..744a6beea 100644 --- a/web/react/components/user_settings/user_settings_integrations.jsx +++ b/web/react/components/user_settings/user_settings_integrations.jsx @@ -92,6 +92,7 @@ export default class UserSettingsIntegrationsTab extends React.Component { className='close' data-dismiss='modal' aria-label='Close' + onClick={this.props.closeModal} > @@ -99,7 +100,10 @@ export default class UserSettingsIntegrationsTab extends React.Component { className='modal-title' ref='title' > - + {'Integration Settings'} @@ -120,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 94b07f3a5..4dcf32cb9 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -12,9 +12,14 @@ export default class UserSettingsModal extends React.Component { 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); @@ -28,27 +33,6 @@ export default class UserSettingsModal extends React.Component { this.requireConfirm = false; } - componentDidMount() { - $('body').on('click', '.settings-content .modal-back', () => { - 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(); - - if (!this.requireConfirm) { - setTimeout(() => { - $('.modal-dialog.display--content').removeClass('display--content'); - }, 500); - } - }); - } - componentDidUpdate(prevProps) { if (!prevProps.show && this.props.show) { $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300); @@ -58,15 +42,16 @@ export default class UserSettingsModal extends React.Component { } } - // called when the close button is pressed - handleHide(skipConfirm) { - if (!skipConfirm && this.requireConfirm) { - this.afterConfirm = () => this.handleHide(true); + // 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(); } @@ -78,6 +63,18 @@ export default class UserSettingsModal extends React.Component { }); } + // 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: '' + }); + } + handleConfirm() { this.setState({ showConfirmModal: false, @@ -97,20 +94,53 @@ export default class UserSettingsModal extends React.Component { showConfirmModal: false, enforceFocus: true }); + + this.afterConfirm = null; } - showConfirmModal() { + 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.afterConfirm = () => this.updateTab(tab, true); - this.showConfirmModal(); + this.showConfirmModal(() => this.updateTab(tab, true)); } else { + this.deactivateTab(); + this.setState({ active_tab: tab, active_section: '' @@ -120,8 +150,7 @@ export default class UserSettingsModal extends React.Component { updateSection(section, skipConfirm) { if (!skipConfirm && this.requireConfirm) { - this.afterConfirm = () => this.updateSection(section, true); - this.showConfirmModal(); + this.showConfirmModal(() => this.updateSection(section, true)); } else { this.setState({active_section: section}); } @@ -170,6 +199,8 @@ export default class UserSettingsModal extends React.Component { 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} /> diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx index 49a94b74a..c6f47804f 100644 --- a/web/react/components/user_settings/user_settings_notifications.jsx +++ b/web/react/components/user_settings/user_settings_notifications.jsx @@ -630,15 +630,19 @@ export default class NotificationsTab extends React.Component { className='close' data-dismiss='modal' aria-label='Close' + onClick={this.props.closeModal} > - +

    - - Notifications + + {'Notification Settings'}

    - +

    - Security Settings + + {'Security Settings'}

    @@ -291,5 +296,7 @@ SecurityTab.propTypes = { activeSection: React.PropTypes.string, updateSection: React.PropTypes.func, updateTab: React.PropTypes.func, + closeModal: React.PropTypes.func.isRequired, + collapseModal: React.PropTypes.func.isRequired, setEnforceFocus: React.PropTypes.func.isRequired }; -- cgit v1.2.3-1-g7c22