diff options
author | Joram Wilander <jwawilander@gmail.com> | 2016-11-04 11:21:14 -0400 |
---|---|---|
committer | Harrison Healey <harrisonmhealey@gmail.com> | 2016-11-04 11:21:14 -0400 |
commit | e6f7a47c99075a39d862308659b904bfaf64a11a (patch) | |
tree | 863f6eb80a2367fb080437d9e037fca283e3c270 | |
parent | dbdd719c51d061dfc327644d4b2ca89a0595b4f1 (diff) | |
download | chat-e6f7a47c99075a39d862308659b904bfaf64a11a.tar.gz chat-e6f7a47c99075a39d862308659b904bfaf64a11a.tar.bz2 chat-e6f7a47c99075a39d862308659b904bfaf64a11a.zip |
PLT-4507 Don't mount modals until opened to make resetting state automatic (#4358)
* Don't mount modals until opened to make resetting state automatic
* Move dimiss handler to be fired after modal exit animation complete
-rw-r--r-- | webapp/components/access_history_modal.jsx | 38 | ||||
-rw-r--r-- | webapp/components/activity_log_modal.jsx | 38 | ||||
-rw-r--r-- | webapp/components/channel_header.jsx | 36 | ||||
-rw-r--r-- | webapp/components/channel_info_modal.jsx | 22 | ||||
-rw-r--r-- | webapp/components/channel_invite_modal.jsx | 42 | ||||
-rw-r--r-- | webapp/components/channel_members_modal.jsx | 49 | ||||
-rw-r--r-- | webapp/components/channel_notifications_modal.jsx | 37 | ||||
-rw-r--r-- | webapp/components/delete_channel_modal.jsx | 15 | ||||
-rw-r--r-- | webapp/components/edit_channel_header_modal.jsx | 32 | ||||
-rw-r--r-- | webapp/components/edit_channel_purpose_modal.jsx | 33 | ||||
-rw-r--r-- | webapp/components/more_direct_channels.jsx | 15 | ||||
-rw-r--r-- | webapp/components/navbar.jsx | 48 | ||||
-rw-r--r-- | webapp/components/sidebar.jsx | 15 | ||||
-rw-r--r-- | webapp/components/sidebar_header_dropdown.jsx | 16 | ||||
-rw-r--r-- | webapp/components/sidebar_right_menu.jsx | 2 | ||||
-rw-r--r-- | webapp/components/team_members_modal.jsx | 16 | ||||
-rw-r--r-- | webapp/components/toggle_modal_button.jsx | 24 |
17 files changed, 233 insertions, 245 deletions
diff --git a/webapp/components/access_history_modal.jsx b/webapp/components/access_history_modal.jsx index 9c49c3879..4ed2ad9ab 100644 --- a/webapp/components/access_history_modal.jsx +++ b/webapp/components/access_history_modal.jsx @@ -1,8 +1,6 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import $ from 'jquery'; -import {Modal} from 'react-bootstrap'; import LoadingScreen from './loading_screen.jsx'; import AuditTable from './audit_table.jsx'; @@ -11,11 +9,13 @@ import UserStore from 'stores/user_store.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; import * as Utils from 'utils/utils.jsx'; -import {intlShape, injectIntl, FormattedMessage} from 'react-intl'; - +import $ from 'jquery'; import React from 'react'; -class AccessHistoryModal extends React.Component { +import {Modal} from 'react-bootstrap'; +import {FormattedMessage} from 'react-intl'; + +export default class AccessHistoryModal extends React.Component { constructor(props) { super(props); @@ -25,45 +25,44 @@ class AccessHistoryModal extends React.Component { const state = this.getStateFromStoresForAudits(); state.moreInfo = []; + state.show = true; this.state = state; } + getStateFromStoresForAudits() { return { audits: UserStore.getAudits() }; } + onShow() { AsyncClient.getAudits(); if (!Utils.isMobile()) { $('.modal-body').perfectScrollbar(); } } + onHide() { - this.setState({moreInfo: []}); - this.props.onHide(); + this.setState({show: false}); } + componentDidMount() { UserStore.addAuditsChangeListener(this.onAuditChange); - - if (this.props.show) { - this.onShow(); - } - } - componentDidUpdate(prevProps) { - if (this.props.show && !prevProps.show) { - this.onShow(); - } + this.onShow(); } + componentWillUnmount() { UserStore.removeAuditsChangeListener(this.onAuditChange); } + onAuditChange() { var newState = this.getStateFromStoresForAudits(); if (!Utils.areObjectsEqual(newState.audits, this.state.audits)) { this.setState(newState); } } + render() { var content; if (this.state.audits.loading) { @@ -80,8 +79,9 @@ class AccessHistoryModal extends React.Component { return ( <Modal - show={this.props.show} + show={this.state.show} onHide={this.onHide} + onExited={this.props.onHide} bsSize='large' > <Modal.Header closeButton={true}> @@ -101,9 +101,5 @@ class AccessHistoryModal extends React.Component { } AccessHistoryModal.propTypes = { - intl: intlShape.isRequired, - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired }; - -export default injectIntl(AccessHistoryModal); diff --git a/webapp/components/activity_log_modal.jsx b/webapp/components/activity_log_modal.jsx index 2c093e1d2..b907668f0 100644 --- a/webapp/components/activity_log_modal.jsx +++ b/webapp/components/activity_log_modal.jsx @@ -1,17 +1,18 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import $ from 'jquery'; +import LoadingScreen from './loading_screen.jsx'; + import UserStore from 'stores/user_store.jsx'; + import Client from 'client/web_client.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; -import {Modal} from 'react-bootstrap'; -import LoadingScreen from './loading_screen.jsx'; import * as Utils from 'utils/utils.jsx'; -import {FormattedMessage, FormattedTime, FormattedDate} from 'react-intl'; - +import $ from 'jquery'; import React from 'react'; +import {Modal} from 'react-bootstrap'; +import {FormattedMessage, FormattedTime, FormattedDate} from 'react-intl'; export default class ActivityLogModal extends React.Component { constructor(props) { @@ -25,9 +26,11 @@ export default class ActivityLogModal extends React.Component { const state = this.getStateFromStores(); state.moreInfo = []; + state.show = true; this.state = state; } + getStateFromStores() { return { sessions: UserStore.getSessions(), @@ -35,6 +38,7 @@ export default class ActivityLogModal extends React.Component { clientError: null }; } + submitRevoke(altId, e) { e.preventDefault(); var modalContent = $(e.target).closest('.modal-content'); @@ -53,42 +57,40 @@ export default class ActivityLogModal extends React.Component { } ); } + onShow() { AsyncClient.getSessions(); if (!Utils.isMobile()) { $('.modal-body').perfectScrollbar(); } } + onHide() { - this.setState({moreInfo: []}); - this.props.onHide(); + this.setState({show: false}); } + componentDidMount() { UserStore.addSessionsChangeListener(this.onListenerChange); - - if (this.props.show) { - this.onShow(); - } - } - componentDidUpdate(prevProps) { - if (this.props.show && !prevProps.show) { - this.onShow(); - } + this.onShow(); } + componentWillUnmount() { UserStore.removeSessionsChangeListener(this.onListenerChange); } + onListenerChange() { const newState = this.getStateFromStores(); if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) { this.setState(newState); } } + handleMoreInfo(index) { const newMoreInfo = this.state.moreInfo; newMoreInfo[index] = true; this.setState({moreInfo: newMoreInfo}); } + render() { const activityList = []; @@ -263,8 +265,9 @@ export default class ActivityLogModal extends React.Component { return ( <Modal - show={this.props.show} + show={this.state.show} onHide={this.onHide} + onExited={this.props.onHide} bsSize='large' > <Modal.Header closeButton={true}> @@ -290,6 +293,5 @@ export default class ActivityLogModal extends React.Component { } ActivityLogModal.propTypes = { - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired }; diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx index c7842aaa3..dd0fd5b70 100644 --- a/webapp/components/channel_header.jsx +++ b/webapp/components/channel_header.jsx @@ -450,6 +450,7 @@ export default class ChannelHeader extends React.Component { role='presentation' > <ToggleModalButton + ref='channelInviteModalButton' role='menuitem' dialogType={ChannelInviteModal} dialogProps={{channel, currentUser: this.state.currentUser}} @@ -662,6 +663,28 @@ export default class ChannelHeader extends React.Component { </OverlayTrigger> ); + let channelMembersModal; + if (this.state.showMembersModal) { + channelMembersModal = ( + <ChannelMembersModal + onModalDismissed={() => this.setState({showMembersModal: false})} + showInviteModal={() => this.refs.channelInviteModalButton.show()} + channel={channel} + isAdmin={isAdmin} + /> + ); + } + + let editPurposeModal; + if (this.state.showEditChannelPurposeModal) { + editPurposeModal = ( + <EditChannelPurposeModal + onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})} + channel={channel} + /> + ); + } + return ( <div id='channel-header' @@ -753,17 +776,8 @@ export default class ChannelHeader extends React.Component { </tr> </tbody> </table> - <EditChannelPurposeModal - show={this.state.showEditChannelPurposeModal} - onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})} - channel={channel} - /> - <ChannelMembersModal - show={this.state.showMembersModal} - onModalDismissed={() => this.setState({showMembersModal: false})} - channel={channel} - isAdmin={isAdmin} - /> + {editPurposeModal} + {channelMembersModal} <RenameChannelModal show={this.state.showRenameChannelModal} onHide={this.hideRenameChannelModal} diff --git a/webapp/components/channel_info_modal.jsx b/webapp/components/channel_info_modal.jsx index 7e0ff3873..fce4b75db 100644 --- a/webapp/components/channel_info_modal.jsx +++ b/webapp/components/channel_info_modal.jsx @@ -11,16 +11,16 @@ import * as TextFormatting from 'utils/text_formatting.jsx'; import React from 'react'; export default class ChannelInfoModal extends React.Component { - shouldComponentUpdate(nextProps) { - if (nextProps.show !== this.props.show) { - return true; - } + constructor(props) { + super(props); - if (!Utils.areObjectsEqual(nextProps.channel, this.props.channel)) { - return true; - } + this.onHide = this.onHide.bind(this); + + this.state = {show: true}; + } - return false; + onHide() { + this.setState({show: false}); } render() { @@ -83,8 +83,9 @@ export default class ChannelInfoModal extends React.Component { return ( <Modal dialogClassName='about-modal' - show={this.props.show} - onHide={this.props.onHide} + show={this.state.show} + onHide={this.onHide} + onExited={this.props.onHide} > <Modal.Header closeButton={true}> <Modal.Title> @@ -123,7 +124,6 @@ export default class ChannelInfoModal extends React.Component { } ChannelInfoModal.propTypes = { - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired }; diff --git a/webapp/components/channel_invite_modal.jsx b/webapp/components/channel_invite_modal.jsx index 576a33dc5..29607136c 100644 --- a/webapp/components/channel_invite_modal.jsx +++ b/webapp/components/channel_invite_modal.jsx @@ -26,6 +26,7 @@ export default class ChannelInviteModal extends React.Component { this.onChange = this.onChange.bind(this); this.onStatusChange = this.onStatusChange.bind(this); + this.onHide = this.onHide.bind(this); this.handleInviteError = this.handleInviteError.bind(this); this.nextPage = this.nextPage.bind(this); this.search = this.search.bind(this); @@ -36,34 +37,27 @@ export default class ChannelInviteModal extends React.Component { const teamStats = TeamStore.getCurrentStats(); this.state = { - users: [], + users: null, total: teamStats.member_count - channelStats.member_count, + show: true, search: false, statusChange: false }; } - componentWillReceiveProps(nextProps) { - if (!this.props.show && nextProps.show) { - TeamStore.addStatsChangeListener(this.onChange); - ChannelStore.addStatsChangeListener(this.onChange); - UserStore.addNotInChannelChangeListener(this.onChange); - UserStore.addStatusesChangeListener(this.onStatusChange); - - this.onChange(); - AsyncClient.getProfilesNotInChannel(this.props.channel.id, 0); - AsyncClient.getTeamStats(TeamStore.getCurrentId()); - } else if (this.props.show && !nextProps.show) { - TeamStore.removeStatsChangeListener(this.onChange); - ChannelStore.removeStatsChangeListener(this.onChange); - UserStore.removeNotInChannelChangeListener(this.onChange); - UserStore.removeStatusesChangeListener(this.onStatusChange); - } + componentDidMount() { + TeamStore.addStatsChangeListener(this.onChange); + ChannelStore.addStatsChangeListener(this.onChange); + UserStore.addNotInChannelChangeListener(this.onChange); + UserStore.addStatusesChangeListener(this.onStatusChange); + + AsyncClient.getProfilesNotInChannel(this.props.channel.id, 0); + AsyncClient.getTeamStats(TeamStore.getCurrentId()); } componentWillUnmount() { + TeamStore.removeStatsChangeListener(this.onChange); ChannelStore.removeStatsChangeListener(this.onChange); - ChannelStore.removeChangeListener(this.onChange); UserStore.removeNotInChannelChangeListener(this.onChange); UserStore.removeStatusesChangeListener(this.onStatusChange); } @@ -90,6 +84,10 @@ export default class ChannelInviteModal extends React.Component { }); } + onHide() { + this.setState({show: false}); + } + handleInviteError(err) { if (err) { this.setState({ @@ -159,8 +157,9 @@ export default class ChannelInviteModal extends React.Component { return ( <Modal dialogClassName='more-modal' - show={this.props.show} - onHide={this.props.onHide} + show={this.state.show} + onHide={this.onHide} + onExited={this.props.onHide} > <Modal.Header closeButton={true}> <Modal.Title> @@ -179,7 +178,7 @@ export default class ChannelInviteModal extends React.Component { <button type='button' className='btn btn-default' - onClick={this.props.onHide} + onClick={this.onHide} > <FormattedMessage id='channel_invite.close' @@ -193,7 +192,6 @@ export default class ChannelInviteModal extends React.Component { } ChannelInviteModal.propTypes = { - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired }; diff --git a/webapp/components/channel_members_modal.jsx b/webapp/components/channel_members_modal.jsx index 75810cf05..85226fd02 100644 --- a/webapp/components/channel_members_modal.jsx +++ b/webapp/components/channel_members_modal.jsx @@ -3,7 +3,6 @@ import SearchableUserList from './searchable_user_list.jsx'; import LoadingScreen from './loading_screen.jsx'; -import ChannelInviteModal from './channel_invite_modal.jsx'; import UserStore from 'stores/user_store.jsx'; import ChannelStore from 'stores/channel_store.jsx'; @@ -28,6 +27,7 @@ export default class ChannelMembersModal extends React.Component { this.onChange = this.onChange.bind(this); this.onStatusChange = this.onStatusChange.bind(this); + this.onHide = this.onHide.bind(this); this.handleRemove = this.handleRemove.bind(this); this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this); this.search = this.search.bind(this); @@ -40,25 +40,18 @@ export default class ChannelMembersModal extends React.Component { this.state = { users: [], total: stats.member_count, - showInviteModal: false, + show: true, search: false, statusChange: false }; } - componentWillReceiveProps(nextProps) { - if (!this.props.show && nextProps.show) { - ChannelStore.addStatsChangeListener(this.onChange); - UserStore.addInChannelChangeListener(this.onChange); - UserStore.addStatusesChangeListener(this.onStatusChange); - - this.onChange(); - AsyncClient.getProfilesInChannel(this.props.channel.id, 0); - } else if (this.props.show && !nextProps.show) { - ChannelStore.removeStatsChangeListener(this.onChange); - UserStore.removeInChannelChangeListener(this.onChange); - UserStore.removeStatusesChangeListener(this.onStatusChange); - } + componentDidMount() { + ChannelStore.addStatsChangeListener(this.onChange); + UserStore.addInChannelChangeListener(this.onChange); + UserStore.addStatusesChangeListener(this.onStatusChange); + + AsyncClient.getProfilesInChannel(this.props.channel.id, 0); } componentWillUnmount() { @@ -87,6 +80,10 @@ export default class ChannelMembersModal extends React.Component { }); } + onHide() { + this.setState({show: false}); + } + handleRemove(user) { const userId = user.id; @@ -175,8 +172,9 @@ export default class ChannelMembersModal extends React.Component { <div> <Modal dialogClassName='more-modal' - show={this.props.show} - onHide={this.props.onModalDismissed} + show={this.state.show} + onHide={this.onHide} + onExited={this.props.onModalDismissed} > <Modal.Header closeButton={true}> <Modal.Title> @@ -190,8 +188,8 @@ export default class ChannelMembersModal extends React.Component { className='btn btn-md btn-primary' href='#' onClick={() => { - this.setState({showInviteModal: true}); - this.props.onModalDismissed(); + this.props.showInviteModal(); + this.onHide(); }} > <FormattedMessage @@ -209,7 +207,7 @@ export default class ChannelMembersModal extends React.Component { <button type='button' className='btn btn-default' - onClick={this.props.onModalDismissed} + onClick={this.onHide} > <FormattedMessage id='channel_members_modal.close' @@ -218,23 +216,14 @@ export default class ChannelMembersModal extends React.Component { </button> </Modal.Footer> </Modal> - <ChannelInviteModal - show={this.state.showInviteModal} - onHide={() => this.setState({showInviteModal: false})} - channel={this.props.channel} - /> </div> ); } } -ChannelMembersModal.defaultProps = { - show: false -}; - ChannelMembersModal.propTypes = { - show: React.PropTypes.bool.isRequired, onModalDismissed: React.PropTypes.func.isRequired, + showInviteModal: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired, isAdmin: React.PropTypes.bool.isRequired }; diff --git a/webapp/components/channel_notifications_modal.jsx b/webapp/components/channel_notifications_modal.jsx index 59ec40d84..ff6e69f70 100644 --- a/webapp/components/channel_notifications_modal.jsx +++ b/webapp/components/channel_notifications_modal.jsx @@ -1,23 +1,23 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import $ from 'jquery'; -import {Modal} from 'react-bootstrap'; -import SettingItemMin from './setting_item_min.jsx'; -import SettingItemMax from './setting_item_max.jsx'; +import SettingItemMin from 'components/setting_item_min.jsx'; +import SettingItemMax from 'components/setting_item_max.jsx'; import Client from 'client/web_client.jsx'; import ChannelStore from 'stores/channel_store.jsx'; -import {FormattedMessage} from 'react-intl'; - +import $ from 'jquery'; import React from 'react'; +import {Modal} from 'react-bootstrap'; +import {FormattedMessage} from 'react-intl'; export default class ChannelNotificationsModal extends React.Component { constructor(props) { super(props); this.updateSection = this.updateSection.bind(this); + this.onHide = this.onHide.bind(this); this.handleSubmitNotifyLevel = this.handleSubmitNotifyLevel.bind(this); this.handleUpdateNotifyLevel = this.handleUpdateNotifyLevel.bind(this); @@ -29,24 +29,23 @@ export default class ChannelNotificationsModal extends React.Component { this.state = { activeSection: '', - notifyLevel: '', - unreadLevel: '' + show: true, + notifyLevel: props.channelMember.notify_props.desktop, + unreadLevel: props.channelMember.notify_props.mark_unread }; } + updateSection(section) { if ($('.section-max').length) { $('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update'); } this.setState({activeSection: section}); } - componentWillReceiveProps(nextProps) { - if (!this.props.show && nextProps.show) { - this.setState({ - notifyLevel: nextProps.channelMember.notify_props.desktop, - unreadLevel: nextProps.channelMember.notify_props.mark_unread - }); - } + + onHide() { + this.setState({show: false}); } + handleSubmitNotifyLevel() { var channelId = this.props.channel.id; var notifyLevel = this.state.notifyLevel; @@ -75,9 +74,11 @@ export default class ChannelNotificationsModal extends React.Component { } ); } + handleUpdateNotifyLevel(notifyLevel) { this.setState({notifyLevel}); } + createNotifyLevelSection(serverError) { // Get glabal user setting for notifications const globalNotifyLevel = this.props.currentUser.notify_props ? this.props.currentUser.notify_props.desktop : 'all'; @@ -380,9 +381,10 @@ export default class ChannelNotificationsModal extends React.Component { return ( <Modal - show={this.props.show} + show={this.state.show} dialogClassName='settings-modal' - onHide={this.props.onHide} + onHide={this.onHide} + onExited={this.props.onHide} > <Modal.Header closeButton={true}> <Modal.Title> @@ -417,7 +419,6 @@ export default class ChannelNotificationsModal extends React.Component { } ChannelNotificationsModal.propTypes = { - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired, channelMember: React.PropTypes.object.isRequired, diff --git a/webapp/components/delete_channel_modal.jsx b/webapp/components/delete_channel_modal.jsx index 7ad213395..1b642861a 100644 --- a/webapp/components/delete_channel_modal.jsx +++ b/webapp/components/delete_channel_modal.jsx @@ -20,6 +20,9 @@ export default class DeleteChannelModal extends React.Component { super(props); this.handleDelete = this.handleDelete.bind(this); + this.onHide = this.onHide.bind(this); + + this.state = {show: true}; } handleDelete() { @@ -39,6 +42,10 @@ export default class DeleteChannelModal extends React.Component { ); } + onHide() { + this.setState({show: false}); + } + render() { let channelTerm = ( <FormattedMessage @@ -57,8 +64,9 @@ export default class DeleteChannelModal extends React.Component { return ( <Modal - show={this.props.show} - onHide={this.props.onHide} + show={this.state.show} + onHide={this.onHide} + onExited={this.props.onHide} > <Modal.Header closeButton={true}> <h4 className='modal-title'> @@ -84,7 +92,7 @@ export default class DeleteChannelModal extends React.Component { <button type='button' className='btn btn-default' - onClick={this.props.onHide} + onClick={this.onHide} > <FormattedMessage id='delete_channel.cancel' @@ -109,7 +117,6 @@ export default class DeleteChannelModal extends React.Component { } DeleteChannelModal.propTypes = { - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired }; diff --git a/webapp/components/edit_channel_header_modal.jsx b/webapp/components/edit_channel_header_modal.jsx index 6112668ba..490b9fb31 100644 --- a/webapp/components/edit_channel_header_modal.jsx +++ b/webapp/components/edit_channel_header_modal.jsx @@ -36,38 +36,21 @@ class EditChannelHeaderModal extends React.Component { this.state = { header: props.channel.header, + show: true, serverError: '', submitted: false }; } componentDidMount() { - if (this.props.show) { - this.onShow(); - } - PreferenceStore.addChangeListener(this.onPreferenceChange); + this.onShow(); } componentWillUnmount() { PreferenceStore.removeChangeListener(this.onPreferenceChange); } - componentWillReceiveProps(nextProps) { - if (this.props.channel.header !== nextProps.channel.header && !this.props.show) { - this.setState({ - header: nextProps.channel.header, - submitted: false - }); - } - } - - componentDidUpdate(prevProps) { - if (this.props.show && !prevProps.show) { - this.onShow(); - } - } - handleChange(e) { this.setState({ header: e.target.value @@ -110,12 +93,7 @@ class EditChannelHeaderModal extends React.Component { } onHide() { - this.setState({ - serverError: '', - header: this.props.channel.header - }); - - this.props.onHide(); + this.setState({show: false}); } handleKeyDown(e) { @@ -156,8 +134,9 @@ class EditChannelHeaderModal extends React.Component { return ( <Modal - show={this.props.show} + show={this.state.show} onHide={this.onHide} + onExited={this.props.onHide} > <Modal.Header closeButton={true}> <Modal.Title> @@ -213,7 +192,6 @@ class EditChannelHeaderModal extends React.Component { EditChannelHeaderModal.propTypes = { intl: intlShape.isRequired, - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired, channel: React.PropTypes.object.isRequired }; diff --git a/webapp/components/edit_channel_purpose_modal.jsx b/webapp/components/edit_channel_purpose_modal.jsx index 31d8e1bbd..bfb4d181a 100644 --- a/webapp/components/edit_channel_purpose_modal.jsx +++ b/webapp/components/edit_channel_purpose_modal.jsx @@ -1,17 +1,16 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import $ from 'jquery'; -import ReactDOM from 'react-dom'; +import PreferenceStore from 'stores/preference_store.jsx'; + import * as AsyncClient from 'utils/async_client.jsx'; import Client from 'client/web_client.jsx'; import Constants from 'utils/constants.jsx'; -import {FormattedMessage} from 'react-intl'; -import PreferenceStore from 'stores/preference_store.jsx'; -import {Modal} from 'react-bootstrap'; import * as Utils from 'utils/utils.jsx'; import React from 'react'; +import {Modal} from 'react-bootstrap'; +import {FormattedMessage} from 'react-intl'; export default class EditChannelPurposeModal extends React.Component { constructor(props) { @@ -26,30 +25,22 @@ export default class EditChannelPurposeModal extends React.Component { this.state = { serverError: '', + show: true, submitted: false }; } componentDidMount() { PreferenceStore.addChangeListener(this.onPreferenceChange); + Utils.placeCaretAtEnd(this.refs.purpose); } componentWillUnmount() { PreferenceStore.removeChangeListener(this.onPreferenceChange); } - componentDidUpdate() { - if (this.props.show) { - $(ReactDOM.findDOMNode(this.refs.purpose)).focus(); - } - } - handleHide() { - this.setState({serverError: '', submitted: false}); - - if (this.props.onModalDismissed) { - this.props.onModalDismissed(); - } + this.setState({show: false}); } onPreferenceChange() { @@ -75,7 +66,7 @@ export default class EditChannelPurposeModal extends React.Component { Client.updateChannelPurpose( this.props.channel.id, - ReactDOM.findDOMNode(this.refs.purpose).value.trim(), + this.refs.purpose.value.trim(), () => { AsyncClient.getChannel(this.props.channel.id); @@ -92,10 +83,6 @@ export default class EditChannelPurposeModal extends React.Component { } render() { - if (!this.props.show) { - return null; - } - let serverError = null; if (this.state.serverError) { serverError = ( @@ -145,8 +132,9 @@ export default class EditChannelPurposeModal extends React.Component { <Modal className='modal-edit-channel-purpose' ref='modal' - show={this.props.show} + show={this.state.show} onHide={this.handleHide} + onExited={this.props.onModalDismissed} > <Modal.Header closeButton={true}> <Modal.Title> @@ -202,7 +190,6 @@ export default class EditChannelPurposeModal extends React.Component { } EditChannelPurposeModal.propTypes = { - show: React.PropTypes.bool.isRequired, channel: React.PropTypes.object, onModalDismissed: React.PropTypes.func.isRequired }; diff --git a/webapp/components/more_direct_channels.jsx b/webapp/components/more_direct_channels.jsx index 7e57261b6..7c61d2f2e 100644 --- a/webapp/components/more_direct_channels.jsx +++ b/webapp/components/more_direct_channels.jsx @@ -34,13 +34,11 @@ export default class MoreDirectChannels extends React.Component { this.toggleList = this.toggleList.bind(this); this.nextPage = this.nextPage.bind(this); this.search = this.search.bind(this); - this.loadComplete = this.loadComplete.bind(this); this.state = { - users: UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true, true), + users: null, loadingDMChannel: -1, listType: 'team', - loading: false, search: false }; } @@ -62,10 +60,6 @@ export default class MoreDirectChannels extends React.Component { TeamStore.removeChangeListener(this.onChange); } - loadComplete() { - this.setState({loading: false}); - } - handleHide() { if (this.props.onModalDismissed) { this.props.onModalDismissed(); @@ -229,11 +223,6 @@ export default class MoreDirectChannels extends React.Component { ); } - let users = this.state.users; - if (this.state.loading) { - users = null; - } - return ( <Modal dialogClassName='more-modal more-direct-channels' @@ -254,7 +243,7 @@ export default class MoreDirectChannels extends React.Component { <SearchableUserList key={'moreDirectChannelsList_' + this.state.listType} style={{maxHeight}} - users={users} + users={this.state.users} usersPerPage={USERS_PER_PAGE} nextPage={this.nextPage} search={this.search} diff --git a/webapp/components/navbar.jsx b/webapp/components/navbar.jsx index 9852e0c59..9721ddee1 100644 --- a/webapp/components/navbar.jsx +++ b/webapp/components/navbar.jsx @@ -730,21 +730,23 @@ export default class Navbar extends React.Component { ); } - editChannelHeaderModal = ( - <EditChannelHeaderModal - show={this.state.showEditChannelHeaderModal} - onHide={() => this.setState({showEditChannelHeaderModal: false})} - channel={channel} - /> - ); + if (this.state.showEditChannelHeaderModal) { + editChannelHeaderModal = ( + <EditChannelHeaderModal + onHide={() => this.setState({showEditChannelHeaderModal: false})} + channel={channel} + /> + ); + } - editChannelPurposeModal = ( - <EditChannelPurposeModal - show={this.state.showEditChannelPurposeModal} - onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})} - channel={channel} - /> - ); + if (this.state.showEditChannelPurposeModal) { + editChannelPurposeModal = ( + <EditChannelPurposeModal + onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})} + channel={channel} + /> + ); + } renameChannelModal = ( <RenameChannelModal @@ -754,14 +756,16 @@ export default class Navbar extends React.Component { /> ); - channelMembersModal = ( - <ChannelMembersModal - show={this.state.showMembersModal} - onModalDismissed={this.hideMembersModal} - channel={channel} - isAdmin={isAdmin} - /> - ); + if (this.state.showMembersModal) { + channelMembersModal = ( + <ChannelMembersModal + show={true} + onModalDismissed={this.hideMembersModal} + channel={channel} + isAdmin={isAdmin} + /> + ); + } channelSwitchModal = ( <ChannelSwitchModal diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx index 21df429f7..08f89c2ff 100644 --- a/webapp/components/sidebar.jsx +++ b/webapp/components/sidebar.jsx @@ -715,6 +715,16 @@ export default class Sidebar extends React.Component { } } + let moreDirectChannelsModal; + if (this.state.showDirectChannelsModal) { + moreDirectChannelsModal = ( + <MoreDirectChannels + show={true} + onModalDismissed={this.hideMoreDirectChannelsModal} + /> + ); + } + return ( <div className='sidebar--left' @@ -726,10 +736,7 @@ export default class Sidebar extends React.Component { channelType={this.state.newChannelModalType} onModalDismissed={this.hideNewChannelModal} /> - <MoreDirectChannels - show={this.state.showDirectChannelsModal} - onModalDismissed={this.hideMoreDirectChannelsModal} - /> + {moreDirectChannelsModal} <SidebarHeader teamDisplayName={this.state.currentTeam.display_name} diff --git a/webapp/components/sidebar_header_dropdown.jsx b/webapp/components/sidebar_header_dropdown.jsx index d3d2979d5..b665eef52 100644 --- a/webapp/components/sidebar_header_dropdown.jsx +++ b/webapp/components/sidebar_header_dropdown.jsx @@ -437,6 +437,16 @@ export default class SidebarHeaderDropdown extends React.Component { ); } + let teamMembersModal; + if (this.state.showTeamMembersModal) { + teamMembersModal = ( + <TeamMembersModal + onHide={this.hideTeamMembersModal} + isAdmin={isAdmin} + /> + ); + } + return ( <Dropdown open={this.state.showDropdown} @@ -508,11 +518,7 @@ export default class SidebarHeaderDropdown extends React.Component { show={this.state.showUserSettingsModal} onModalDismissed={() => this.setState({showUserSettingsModal: false})} /> - <TeamMembersModal - show={this.state.showTeamMembersModal} - onHide={this.hideTeamMembersModal} - isAdmin={isAdmin} - /> + {teamMembersModal} <AboutBuildModal show={this.state.showAboutModal} onModalDismissed={this.aboutModalDismissed} diff --git a/webapp/components/sidebar_right_menu.jsx b/webapp/components/sidebar_right_menu.jsx index a11869cc2..86026967a 100644 --- a/webapp/components/sidebar_right_menu.jsx +++ b/webapp/components/sidebar_right_menu.jsx @@ -1,7 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; +import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import TeamMembersModal from './team_members_modal.jsx'; import ToggleModalButton from './toggle_modal_button.jsx'; import UserSettingsModal from './user_settings/user_settings_modal.jsx'; diff --git a/webapp/components/team_members_modal.jsx b/webapp/components/team_members_modal.jsx index e41268805..76ffafe27 100644 --- a/webapp/components/team_members_modal.jsx +++ b/webapp/components/team_members_modal.jsx @@ -16,9 +16,11 @@ export default class TeamMembersModal extends React.Component { super(props); this.teamChanged = this.teamChanged.bind(this); + this.onHide = this.onHide.bind(this); this.state = { - team: TeamStore.getCurrent() + team: TeamStore.getCurrent(), + show: true }; } @@ -34,6 +36,10 @@ export default class TeamMembersModal extends React.Component { this.setState({team: TeamStore.getCurrent()}); } + onHide() { + this.setState({show: false}); + } + render() { let teamDisplayName = ''; if (this.state.team) { @@ -48,8 +54,9 @@ export default class TeamMembersModal extends React.Component { return ( <Modal dialogClassName='more-modal' - show={this.props.show} - onHide={this.props.onHide} + show={this.state.show} + onHide={this.onHide} + onExited={this.props.onHide} > <Modal.Header closeButton={true}> <Modal.Title> @@ -72,7 +79,7 @@ export default class TeamMembersModal extends React.Component { <button type='button' className='btn btn-default' - onClick={this.props.onHide} + onClick={this.onHide} > <FormattedMessage id='team_member_modal.close' @@ -86,7 +93,6 @@ export default class TeamMembersModal extends React.Component { } TeamMembersModal.propTypes = { - show: React.PropTypes.bool.isRequired, onHide: React.PropTypes.func.isRequired, isAdmin: React.PropTypes.bool.isRequired }; diff --git a/webapp/components/toggle_modal_button.jsx b/webapp/components/toggle_modal_button.jsx index 6082901de..304ba386c 100644 --- a/webapp/components/toggle_modal_button.jsx +++ b/webapp/components/toggle_modal_button.jsx @@ -16,7 +16,9 @@ export default class ModalToggleButton extends React.Component { } show(e) { - e.preventDefault(); + if (e) { + e.preventDefault(); + } this.setState({show: true}); } @@ -37,17 +39,19 @@ export default class ModalToggleButton extends React.Component { }; } - // this assumes that all modals will have a show property and an onHide event - const dialog = React.createElement(dialogType, Object.assign({}, dialogProps, { - show: this.state.show, - onHide: () => { - this.hide(); + let dialog; + if (this.state.show) { + // this assumes that all modals will have an onHide event and will show when mounted + dialog = React.createElement(dialogType, Object.assign({}, dialogProps, { + onHide: () => { + this.hide(); - if (dialogProps.onHide) { - dialogProps.onHide(); + if (dialogProps.onHide) { + dialogProps.onHide(); + } } - } - })); + })); + } // nesting the dialog in the anchor tag looks like it shouldn't work, but it does due to how react-bootstrap // renders modals at the top level of the DOM instead of where you specify in the virtual DOM |