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
Этот коммит содержится в:
Joram Wilander
2016-11-04 11:21:14 -04:00
коммит произвёл Harrison Healey
родитель dbdd719c51
Коммит e6f7a47c99
17 изменённых файлов: 231 добавлений и 243 удалений

Просмотреть файл

@@ -1,8 +1,6 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
import $ from 'jquery';
import {Modal} from 'react-bootstrap';
import LoadingScreen from './loading_screen.jsx'; import LoadingScreen from './loading_screen.jsx';
import AuditTable from './audit_table.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 AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx'; import * as Utils from 'utils/utils.jsx';
import {intlShape, injectIntl, FormattedMessage} from 'react-intl'; import $ from 'jquery';
import React from 'react'; 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) { constructor(props) {
super(props); super(props);
@@ -25,45 +25,44 @@ class AccessHistoryModal extends React.Component {
const state = this.getStateFromStoresForAudits(); const state = this.getStateFromStoresForAudits();
state.moreInfo = []; state.moreInfo = [];
state.show = true;
this.state = state; this.state = state;
} }
getStateFromStoresForAudits() { getStateFromStoresForAudits() {
return { return {
audits: UserStore.getAudits() audits: UserStore.getAudits()
}; };
} }
onShow() { onShow() {
AsyncClient.getAudits(); AsyncClient.getAudits();
if (!Utils.isMobile()) { if (!Utils.isMobile()) {
$('.modal-body').perfectScrollbar(); $('.modal-body').perfectScrollbar();
} }
} }
onHide() { onHide() {
this.setState({moreInfo: []}); this.setState({show: false});
this.props.onHide();
} }
componentDidMount() { componentDidMount() {
UserStore.addAuditsChangeListener(this.onAuditChange); UserStore.addAuditsChangeListener(this.onAuditChange);
this.onShow();
}
if (this.props.show) {
this.onShow();
}
}
componentDidUpdate(prevProps) {
if (this.props.show && !prevProps.show) {
this.onShow();
}
}
componentWillUnmount() { componentWillUnmount() {
UserStore.removeAuditsChangeListener(this.onAuditChange); UserStore.removeAuditsChangeListener(this.onAuditChange);
} }
onAuditChange() { onAuditChange() {
var newState = this.getStateFromStoresForAudits(); var newState = this.getStateFromStoresForAudits();
if (!Utils.areObjectsEqual(newState.audits, this.state.audits)) { if (!Utils.areObjectsEqual(newState.audits, this.state.audits)) {
this.setState(newState); this.setState(newState);
} }
} }
render() { render() {
var content; var content;
if (this.state.audits.loading) { if (this.state.audits.loading) {
@@ -80,8 +79,9 @@ class AccessHistoryModal extends React.Component {
return ( return (
<Modal <Modal
show={this.props.show} show={this.state.show}
onHide={this.onHide} onHide={this.onHide}
onExited={this.props.onHide}
bsSize='large' bsSize='large'
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
@@ -101,9 +101,5 @@ class AccessHistoryModal extends React.Component {
} }
AccessHistoryModal.propTypes = { AccessHistoryModal.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired onHide: React.PropTypes.func.isRequired
}; };
export default injectIntl(AccessHistoryModal);

Просмотреть файл

@@ -1,17 +1,18 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
import $ from 'jquery'; import LoadingScreen from './loading_screen.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
import Client from 'client/web_client.jsx'; import Client from 'client/web_client.jsx';
import * as AsyncClient from 'utils/async_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 * as Utils from 'utils/utils.jsx';
import {FormattedMessage, FormattedTime, FormattedDate} from 'react-intl'; import $ from 'jquery';
import React from 'react'; import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage, FormattedTime, FormattedDate} from 'react-intl';
export default class ActivityLogModal extends React.Component { export default class ActivityLogModal extends React.Component {
constructor(props) { constructor(props) {
@@ -25,9 +26,11 @@ export default class ActivityLogModal extends React.Component {
const state = this.getStateFromStores(); const state = this.getStateFromStores();
state.moreInfo = []; state.moreInfo = [];
state.show = true;
this.state = state; this.state = state;
} }
getStateFromStores() { getStateFromStores() {
return { return {
sessions: UserStore.getSessions(), sessions: UserStore.getSessions(),
@@ -35,6 +38,7 @@ export default class ActivityLogModal extends React.Component {
clientError: null clientError: null
}; };
} }
submitRevoke(altId, e) { submitRevoke(altId, e) {
e.preventDefault(); e.preventDefault();
var modalContent = $(e.target).closest('.modal-content'); var modalContent = $(e.target).closest('.modal-content');
@@ -53,42 +57,40 @@ export default class ActivityLogModal extends React.Component {
} }
); );
} }
onShow() { onShow() {
AsyncClient.getSessions(); AsyncClient.getSessions();
if (!Utils.isMobile()) { if (!Utils.isMobile()) {
$('.modal-body').perfectScrollbar(); $('.modal-body').perfectScrollbar();
} }
} }
onHide() { onHide() {
this.setState({moreInfo: []}); this.setState({show: false});
this.props.onHide();
} }
componentDidMount() { componentDidMount() {
UserStore.addSessionsChangeListener(this.onListenerChange); UserStore.addSessionsChangeListener(this.onListenerChange);
this.onShow();
}
if (this.props.show) {
this.onShow();
}
}
componentDidUpdate(prevProps) {
if (this.props.show && !prevProps.show) {
this.onShow();
}
}
componentWillUnmount() { componentWillUnmount() {
UserStore.removeSessionsChangeListener(this.onListenerChange); UserStore.removeSessionsChangeListener(this.onListenerChange);
} }
onListenerChange() { onListenerChange() {
const newState = this.getStateFromStores(); const newState = this.getStateFromStores();
if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) { if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) {
this.setState(newState); this.setState(newState);
} }
} }
handleMoreInfo(index) { handleMoreInfo(index) {
const newMoreInfo = this.state.moreInfo; const newMoreInfo = this.state.moreInfo;
newMoreInfo[index] = true; newMoreInfo[index] = true;
this.setState({moreInfo: newMoreInfo}); this.setState({moreInfo: newMoreInfo});
} }
render() { render() {
const activityList = []; const activityList = [];
@@ -263,8 +265,9 @@ export default class ActivityLogModal extends React.Component {
return ( return (
<Modal <Modal
show={this.props.show} show={this.state.show}
onHide={this.onHide} onHide={this.onHide}
onExited={this.props.onHide}
bsSize='large' bsSize='large'
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
@@ -290,6 +293,5 @@ export default class ActivityLogModal extends React.Component {
} }
ActivityLogModal.propTypes = { ActivityLogModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired onHide: React.PropTypes.func.isRequired
}; };

Просмотреть файл

@@ -450,6 +450,7 @@ export default class ChannelHeader extends React.Component {
role='presentation' role='presentation'
> >
<ToggleModalButton <ToggleModalButton
ref='channelInviteModalButton'
role='menuitem' role='menuitem'
dialogType={ChannelInviteModal} dialogType={ChannelInviteModal}
dialogProps={{channel, currentUser: this.state.currentUser}} dialogProps={{channel, currentUser: this.state.currentUser}}
@@ -662,6 +663,28 @@ export default class ChannelHeader extends React.Component {
</OverlayTrigger> </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 ( return (
<div <div
id='channel-header' id='channel-header'
@@ -753,17 +776,8 @@ export default class ChannelHeader extends React.Component {
</tr> </tr>
</tbody> </tbody>
</table> </table>
<EditChannelPurposeModal {editPurposeModal}
show={this.state.showEditChannelPurposeModal} {channelMembersModal}
onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
channel={channel}
/>
<ChannelMembersModal
show={this.state.showMembersModal}
onModalDismissed={() => this.setState({showMembersModal: false})}
channel={channel}
isAdmin={isAdmin}
/>
<RenameChannelModal <RenameChannelModal
show={this.state.showRenameChannelModal} show={this.state.showRenameChannelModal}
onHide={this.hideRenameChannelModal} onHide={this.hideRenameChannelModal}

Просмотреть файл

@@ -11,16 +11,16 @@ import * as TextFormatting from 'utils/text_formatting.jsx';
import React from 'react'; import React from 'react';
export default class ChannelInfoModal extends React.Component { export default class ChannelInfoModal extends React.Component {
shouldComponentUpdate(nextProps) { constructor(props) {
if (nextProps.show !== this.props.show) { super(props);
return true;
}
if (!Utils.areObjectsEqual(nextProps.channel, this.props.channel)) { this.onHide = this.onHide.bind(this);
return true;
}
return false; this.state = {show: true};
}
onHide() {
this.setState({show: false});
} }
render() { render() {
@@ -83,8 +83,9 @@ export default class ChannelInfoModal extends React.Component {
return ( return (
<Modal <Modal
dialogClassName='about-modal' dialogClassName='about-modal'
show={this.props.show} show={this.state.show}
onHide={this.props.onHide} onHide={this.onHide}
onExited={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -123,7 +124,6 @@ export default class ChannelInfoModal extends React.Component {
} }
ChannelInfoModal.propTypes = { ChannelInfoModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired, onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired channel: React.PropTypes.object.isRequired
}; };

Просмотреть файл

@@ -26,6 +26,7 @@ export default class ChannelInviteModal extends React.Component {
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.onStatusChange = this.onStatusChange.bind(this); this.onStatusChange = this.onStatusChange.bind(this);
this.onHide = this.onHide.bind(this);
this.handleInviteError = this.handleInviteError.bind(this); this.handleInviteError = this.handleInviteError.bind(this);
this.nextPage = this.nextPage.bind(this); this.nextPage = this.nextPage.bind(this);
this.search = this.search.bind(this); this.search = this.search.bind(this);
@@ -36,34 +37,27 @@ export default class ChannelInviteModal extends React.Component {
const teamStats = TeamStore.getCurrentStats(); const teamStats = TeamStore.getCurrentStats();
this.state = { this.state = {
users: [], users: null,
total: teamStats.member_count - channelStats.member_count, total: teamStats.member_count - channelStats.member_count,
show: true,
search: false, search: false,
statusChange: false statusChange: false
}; };
} }
componentWillReceiveProps(nextProps) { componentDidMount() {
if (!this.props.show && nextProps.show) { TeamStore.addStatsChangeListener(this.onChange);
TeamStore.addStatsChangeListener(this.onChange); ChannelStore.addStatsChangeListener(this.onChange);
ChannelStore.addStatsChangeListener(this.onChange); UserStore.addNotInChannelChangeListener(this.onChange);
UserStore.addNotInChannelChangeListener(this.onChange); UserStore.addStatusesChangeListener(this.onStatusChange);
UserStore.addStatusesChangeListener(this.onStatusChange);
this.onChange(); AsyncClient.getProfilesNotInChannel(this.props.channel.id, 0);
AsyncClient.getProfilesNotInChannel(this.props.channel.id, 0); AsyncClient.getTeamStats(TeamStore.getCurrentId());
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);
}
} }
componentWillUnmount() { componentWillUnmount() {
TeamStore.removeStatsChangeListener(this.onChange);
ChannelStore.removeStatsChangeListener(this.onChange); ChannelStore.removeStatsChangeListener(this.onChange);
ChannelStore.removeChangeListener(this.onChange);
UserStore.removeNotInChannelChangeListener(this.onChange); UserStore.removeNotInChannelChangeListener(this.onChange);
UserStore.removeStatusesChangeListener(this.onStatusChange); UserStore.removeStatusesChangeListener(this.onStatusChange);
} }
@@ -90,6 +84,10 @@ export default class ChannelInviteModal extends React.Component {
}); });
} }
onHide() {
this.setState({show: false});
}
handleInviteError(err) { handleInviteError(err) {
if (err) { if (err) {
this.setState({ this.setState({
@@ -159,8 +157,9 @@ export default class ChannelInviteModal extends React.Component {
return ( return (
<Modal <Modal
dialogClassName='more-modal' dialogClassName='more-modal'
show={this.props.show} show={this.state.show}
onHide={this.props.onHide} onHide={this.onHide}
onExited={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -179,7 +178,7 @@ export default class ChannelInviteModal extends React.Component {
<button <button
type='button' type='button'
className='btn btn-default' className='btn btn-default'
onClick={this.props.onHide} onClick={this.onHide}
> >
<FormattedMessage <FormattedMessage
id='channel_invite.close' id='channel_invite.close'
@@ -193,7 +192,6 @@ export default class ChannelInviteModal extends React.Component {
} }
ChannelInviteModal.propTypes = { ChannelInviteModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired, onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired channel: React.PropTypes.object.isRequired
}; };

Просмотреть файл

@@ -3,7 +3,6 @@
import SearchableUserList from './searchable_user_list.jsx'; import SearchableUserList from './searchable_user_list.jsx';
import LoadingScreen from './loading_screen.jsx'; import LoadingScreen from './loading_screen.jsx';
import ChannelInviteModal from './channel_invite_modal.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_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.onChange = this.onChange.bind(this);
this.onStatusChange = this.onStatusChange.bind(this); this.onStatusChange = this.onStatusChange.bind(this);
this.onHide = this.onHide.bind(this);
this.handleRemove = this.handleRemove.bind(this); this.handleRemove = this.handleRemove.bind(this);
this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this); this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this);
this.search = this.search.bind(this); this.search = this.search.bind(this);
@@ -40,25 +40,18 @@ export default class ChannelMembersModal extends React.Component {
this.state = { this.state = {
users: [], users: [],
total: stats.member_count, total: stats.member_count,
showInviteModal: false, show: true,
search: false, search: false,
statusChange: false statusChange: false
}; };
} }
componentWillReceiveProps(nextProps) { componentDidMount() {
if (!this.props.show && nextProps.show) { ChannelStore.addStatsChangeListener(this.onChange);
ChannelStore.addStatsChangeListener(this.onChange); UserStore.addInChannelChangeListener(this.onChange);
UserStore.addInChannelChangeListener(this.onChange); UserStore.addStatusesChangeListener(this.onStatusChange);
UserStore.addStatusesChangeListener(this.onStatusChange);
this.onChange(); AsyncClient.getProfilesInChannel(this.props.channel.id, 0);
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);
}
} }
componentWillUnmount() { componentWillUnmount() {
@@ -87,6 +80,10 @@ export default class ChannelMembersModal extends React.Component {
}); });
} }
onHide() {
this.setState({show: false});
}
handleRemove(user) { handleRemove(user) {
const userId = user.id; const userId = user.id;
@@ -175,8 +172,9 @@ export default class ChannelMembersModal extends React.Component {
<div> <div>
<Modal <Modal
dialogClassName='more-modal' dialogClassName='more-modal'
show={this.props.show} show={this.state.show}
onHide={this.props.onModalDismissed} onHide={this.onHide}
onExited={this.props.onModalDismissed}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -190,8 +188,8 @@ export default class ChannelMembersModal extends React.Component {
className='btn btn-md btn-primary' className='btn btn-md btn-primary'
href='#' href='#'
onClick={() => { onClick={() => {
this.setState({showInviteModal: true}); this.props.showInviteModal();
this.props.onModalDismissed(); this.onHide();
}} }}
> >
<FormattedMessage <FormattedMessage
@@ -209,7 +207,7 @@ export default class ChannelMembersModal extends React.Component {
<button <button
type='button' type='button'
className='btn btn-default' className='btn btn-default'
onClick={this.props.onModalDismissed} onClick={this.onHide}
> >
<FormattedMessage <FormattedMessage
id='channel_members_modal.close' id='channel_members_modal.close'
@@ -218,23 +216,14 @@ export default class ChannelMembersModal extends React.Component {
</button> </button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
<ChannelInviteModal
show={this.state.showInviteModal}
onHide={() => this.setState({showInviteModal: false})}
channel={this.props.channel}
/>
</div> </div>
); );
} }
} }
ChannelMembersModal.defaultProps = {
show: false
};
ChannelMembersModal.propTypes = { ChannelMembersModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func.isRequired, onModalDismissed: React.PropTypes.func.isRequired,
showInviteModal: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired, channel: React.PropTypes.object.isRequired,
isAdmin: React.PropTypes.bool.isRequired isAdmin: React.PropTypes.bool.isRequired
}; };

Просмотреть файл

@@ -1,23 +1,23 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
import $ from 'jquery'; import SettingItemMin from 'components/setting_item_min.jsx';
import {Modal} from 'react-bootstrap'; import SettingItemMax from 'components/setting_item_max.jsx';
import SettingItemMin from './setting_item_min.jsx';
import SettingItemMax from './setting_item_max.jsx';
import Client from 'client/web_client.jsx'; import Client from 'client/web_client.jsx';
import ChannelStore from 'stores/channel_store.jsx'; import ChannelStore from 'stores/channel_store.jsx';
import {FormattedMessage} from 'react-intl'; import $ from 'jquery';
import React from 'react'; import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
export default class ChannelNotificationsModal extends React.Component { export default class ChannelNotificationsModal extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.updateSection = this.updateSection.bind(this); this.updateSection = this.updateSection.bind(this);
this.onHide = this.onHide.bind(this);
this.handleSubmitNotifyLevel = this.handleSubmitNotifyLevel.bind(this); this.handleSubmitNotifyLevel = this.handleSubmitNotifyLevel.bind(this);
this.handleUpdateNotifyLevel = this.handleUpdateNotifyLevel.bind(this); this.handleUpdateNotifyLevel = this.handleUpdateNotifyLevel.bind(this);
@@ -29,24 +29,23 @@ export default class ChannelNotificationsModal extends React.Component {
this.state = { this.state = {
activeSection: '', activeSection: '',
notifyLevel: '', show: true,
unreadLevel: '' notifyLevel: props.channelMember.notify_props.desktop,
unreadLevel: props.channelMember.notify_props.mark_unread
}; };
} }
updateSection(section) { updateSection(section) {
if ($('.section-max').length) { if ($('.section-max').length) {
$('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update'); $('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update');
} }
this.setState({activeSection: section}); this.setState({activeSection: section});
} }
componentWillReceiveProps(nextProps) {
if (!this.props.show && nextProps.show) { onHide() {
this.setState({ this.setState({show: false});
notifyLevel: nextProps.channelMember.notify_props.desktop,
unreadLevel: nextProps.channelMember.notify_props.mark_unread
});
}
} }
handleSubmitNotifyLevel() { handleSubmitNotifyLevel() {
var channelId = this.props.channel.id; var channelId = this.props.channel.id;
var notifyLevel = this.state.notifyLevel; var notifyLevel = this.state.notifyLevel;
@@ -75,9 +74,11 @@ export default class ChannelNotificationsModal extends React.Component {
} }
); );
} }
handleUpdateNotifyLevel(notifyLevel) { handleUpdateNotifyLevel(notifyLevel) {
this.setState({notifyLevel}); this.setState({notifyLevel});
} }
createNotifyLevelSection(serverError) { createNotifyLevelSection(serverError) {
// Get glabal user setting for notifications // Get glabal user setting for notifications
const globalNotifyLevel = this.props.currentUser.notify_props ? this.props.currentUser.notify_props.desktop : 'all'; 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 ( return (
<Modal <Modal
show={this.props.show} show={this.state.show}
dialogClassName='settings-modal' dialogClassName='settings-modal'
onHide={this.props.onHide} onHide={this.onHide}
onExited={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -417,7 +419,6 @@ export default class ChannelNotificationsModal extends React.Component {
} }
ChannelNotificationsModal.propTypes = { ChannelNotificationsModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired, onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired, channel: React.PropTypes.object.isRequired,
channelMember: React.PropTypes.object.isRequired, channelMember: React.PropTypes.object.isRequired,

Просмотреть файл

@@ -20,6 +20,9 @@ export default class DeleteChannelModal extends React.Component {
super(props); super(props);
this.handleDelete = this.handleDelete.bind(this); this.handleDelete = this.handleDelete.bind(this);
this.onHide = this.onHide.bind(this);
this.state = {show: true};
} }
handleDelete() { handleDelete() {
@@ -39,6 +42,10 @@ export default class DeleteChannelModal extends React.Component {
); );
} }
onHide() {
this.setState({show: false});
}
render() { render() {
let channelTerm = ( let channelTerm = (
<FormattedMessage <FormattedMessage
@@ -57,8 +64,9 @@ export default class DeleteChannelModal extends React.Component {
return ( return (
<Modal <Modal
show={this.props.show} show={this.state.show}
onHide={this.props.onHide} onHide={this.onHide}
onExited={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<h4 className='modal-title'> <h4 className='modal-title'>
@@ -84,7 +92,7 @@ export default class DeleteChannelModal extends React.Component {
<button <button
type='button' type='button'
className='btn btn-default' className='btn btn-default'
onClick={this.props.onHide} onClick={this.onHide}
> >
<FormattedMessage <FormattedMessage
id='delete_channel.cancel' id='delete_channel.cancel'
@@ -109,7 +117,6 @@ export default class DeleteChannelModal extends React.Component {
} }
DeleteChannelModal.propTypes = { DeleteChannelModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired, onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired channel: React.PropTypes.object.isRequired
}; };

Просмотреть файл

@@ -36,38 +36,21 @@ class EditChannelHeaderModal extends React.Component {
this.state = { this.state = {
header: props.channel.header, header: props.channel.header,
show: true,
serverError: '', serverError: '',
submitted: false submitted: false
}; };
} }
componentDidMount() { componentDidMount() {
if (this.props.show) {
this.onShow();
}
PreferenceStore.addChangeListener(this.onPreferenceChange); PreferenceStore.addChangeListener(this.onPreferenceChange);
this.onShow();
} }
componentWillUnmount() { componentWillUnmount() {
PreferenceStore.removeChangeListener(this.onPreferenceChange); 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) { handleChange(e) {
this.setState({ this.setState({
header: e.target.value header: e.target.value
@@ -110,12 +93,7 @@ class EditChannelHeaderModal extends React.Component {
} }
onHide() { onHide() {
this.setState({ this.setState({show: false});
serverError: '',
header: this.props.channel.header
});
this.props.onHide();
} }
handleKeyDown(e) { handleKeyDown(e) {
@@ -156,8 +134,9 @@ class EditChannelHeaderModal extends React.Component {
return ( return (
<Modal <Modal
show={this.props.show} show={this.state.show}
onHide={this.onHide} onHide={this.onHide}
onExited={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -213,7 +192,6 @@ class EditChannelHeaderModal extends React.Component {
EditChannelHeaderModal.propTypes = { EditChannelHeaderModal.propTypes = {
intl: intlShape.isRequired, intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired, onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired channel: React.PropTypes.object.isRequired
}; };

Просмотреть файл

@@ -1,17 +1,16 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
import $ from 'jquery'; import PreferenceStore from 'stores/preference_store.jsx';
import ReactDOM from 'react-dom';
import * as AsyncClient from 'utils/async_client.jsx'; import * as AsyncClient from 'utils/async_client.jsx';
import Client from 'client/web_client.jsx'; import Client from 'client/web_client.jsx';
import Constants from 'utils/constants.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 * as Utils from 'utils/utils.jsx';
import React from 'react'; import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
export default class EditChannelPurposeModal extends React.Component { export default class EditChannelPurposeModal extends React.Component {
constructor(props) { constructor(props) {
@@ -26,30 +25,22 @@ export default class EditChannelPurposeModal extends React.Component {
this.state = { this.state = {
serverError: '', serverError: '',
show: true,
submitted: false submitted: false
}; };
} }
componentDidMount() { componentDidMount() {
PreferenceStore.addChangeListener(this.onPreferenceChange); PreferenceStore.addChangeListener(this.onPreferenceChange);
Utils.placeCaretAtEnd(this.refs.purpose);
} }
componentWillUnmount() { componentWillUnmount() {
PreferenceStore.removeChangeListener(this.onPreferenceChange); PreferenceStore.removeChangeListener(this.onPreferenceChange);
} }
componentDidUpdate() {
if (this.props.show) {
$(ReactDOM.findDOMNode(this.refs.purpose)).focus();
}
}
handleHide() { handleHide() {
this.setState({serverError: '', submitted: false}); this.setState({show: false});
if (this.props.onModalDismissed) {
this.props.onModalDismissed();
}
} }
onPreferenceChange() { onPreferenceChange() {
@@ -75,7 +66,7 @@ export default class EditChannelPurposeModal extends React.Component {
Client.updateChannelPurpose( Client.updateChannelPurpose(
this.props.channel.id, this.props.channel.id,
ReactDOM.findDOMNode(this.refs.purpose).value.trim(), this.refs.purpose.value.trim(),
() => { () => {
AsyncClient.getChannel(this.props.channel.id); AsyncClient.getChannel(this.props.channel.id);
@@ -92,10 +83,6 @@ export default class EditChannelPurposeModal extends React.Component {
} }
render() { render() {
if (!this.props.show) {
return null;
}
let serverError = null; let serverError = null;
if (this.state.serverError) { if (this.state.serverError) {
serverError = ( serverError = (
@@ -145,8 +132,9 @@ export default class EditChannelPurposeModal extends React.Component {
<Modal <Modal
className='modal-edit-channel-purpose' className='modal-edit-channel-purpose'
ref='modal' ref='modal'
show={this.props.show} show={this.state.show}
onHide={this.handleHide} onHide={this.handleHide}
onExited={this.props.onModalDismissed}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -202,7 +190,6 @@ export default class EditChannelPurposeModal extends React.Component {
} }
EditChannelPurposeModal.propTypes = { EditChannelPurposeModal.propTypes = {
show: React.PropTypes.bool.isRequired,
channel: React.PropTypes.object, channel: React.PropTypes.object,
onModalDismissed: React.PropTypes.func.isRequired onModalDismissed: React.PropTypes.func.isRequired
}; };

Просмотреть файл

@@ -34,13 +34,11 @@ export default class MoreDirectChannels extends React.Component {
this.toggleList = this.toggleList.bind(this); this.toggleList = this.toggleList.bind(this);
this.nextPage = this.nextPage.bind(this); this.nextPage = this.nextPage.bind(this);
this.search = this.search.bind(this); this.search = this.search.bind(this);
this.loadComplete = this.loadComplete.bind(this);
this.state = { this.state = {
users: UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true, true), users: null,
loadingDMChannel: -1, loadingDMChannel: -1,
listType: 'team', listType: 'team',
loading: false,
search: false search: false
}; };
} }
@@ -62,10 +60,6 @@ export default class MoreDirectChannels extends React.Component {
TeamStore.removeChangeListener(this.onChange); TeamStore.removeChangeListener(this.onChange);
} }
loadComplete() {
this.setState({loading: false});
}
handleHide() { handleHide() {
if (this.props.onModalDismissed) { if (this.props.onModalDismissed) {
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 ( return (
<Modal <Modal
dialogClassName='more-modal more-direct-channels' dialogClassName='more-modal more-direct-channels'
@@ -254,7 +243,7 @@ export default class MoreDirectChannels extends React.Component {
<SearchableUserList <SearchableUserList
key={'moreDirectChannelsList_' + this.state.listType} key={'moreDirectChannelsList_' + this.state.listType}
style={{maxHeight}} style={{maxHeight}}
users={users} users={this.state.users}
usersPerPage={USERS_PER_PAGE} usersPerPage={USERS_PER_PAGE}
nextPage={this.nextPage} nextPage={this.nextPage}
search={this.search} search={this.search}

Просмотреть файл

@@ -730,21 +730,23 @@ export default class Navbar extends React.Component {
); );
} }
editChannelHeaderModal = ( if (this.state.showEditChannelHeaderModal) {
<EditChannelHeaderModal editChannelHeaderModal = (
show={this.state.showEditChannelHeaderModal} <EditChannelHeaderModal
onHide={() => this.setState({showEditChannelHeaderModal: false})} onHide={() => this.setState({showEditChannelHeaderModal: false})}
channel={channel} channel={channel}
/> />
); );
}
editChannelPurposeModal = ( if (this.state.showEditChannelPurposeModal) {
<EditChannelPurposeModal editChannelPurposeModal = (
show={this.state.showEditChannelPurposeModal} <EditChannelPurposeModal
onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})} onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
channel={channel} channel={channel}
/> />
); );
}
renameChannelModal = ( renameChannelModal = (
<RenameChannelModal <RenameChannelModal
@@ -754,14 +756,16 @@ export default class Navbar extends React.Component {
/> />
); );
channelMembersModal = ( if (this.state.showMembersModal) {
<ChannelMembersModal channelMembersModal = (
show={this.state.showMembersModal} <ChannelMembersModal
onModalDismissed={this.hideMembersModal} show={true}
channel={channel} onModalDismissed={this.hideMembersModal}
isAdmin={isAdmin} channel={channel}
/> isAdmin={isAdmin}
); />
);
}
channelSwitchModal = ( channelSwitchModal = (
<ChannelSwitchModal <ChannelSwitchModal

Просмотреть файл

@@ -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 ( return (
<div <div
className='sidebar--left' className='sidebar--left'
@@ -726,10 +736,7 @@ export default class Sidebar extends React.Component {
channelType={this.state.newChannelModalType} channelType={this.state.newChannelModalType}
onModalDismissed={this.hideNewChannelModal} onModalDismissed={this.hideNewChannelModal}
/> />
<MoreDirectChannels {moreDirectChannelsModal}
show={this.state.showDirectChannelsModal}
onModalDismissed={this.hideMoreDirectChannelsModal}
/>
<SidebarHeader <SidebarHeader
teamDisplayName={this.state.currentTeam.display_name} teamDisplayName={this.state.currentTeam.display_name}

Просмотреть файл

@@ -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 ( return (
<Dropdown <Dropdown
open={this.state.showDropdown} open={this.state.showDropdown}
@@ -508,11 +518,7 @@ export default class SidebarHeaderDropdown extends React.Component {
show={this.state.showUserSettingsModal} show={this.state.showUserSettingsModal}
onModalDismissed={() => this.setState({showUserSettingsModal: false})} onModalDismissed={() => this.setState({showUserSettingsModal: false})}
/> />
<TeamMembersModal {teamMembersModal}
show={this.state.showTeamMembersModal}
onHide={this.hideTeamMembersModal}
isAdmin={isAdmin}
/>
<AboutBuildModal <AboutBuildModal
show={this.state.showAboutModal} show={this.state.showAboutModal}
onModalDismissed={this.aboutModalDismissed} onModalDismissed={this.aboutModalDismissed}

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // 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 TeamMembersModal from './team_members_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx'; import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx'; import UserSettingsModal from './user_settings/user_settings_modal.jsx';

Просмотреть файл

@@ -16,9 +16,11 @@ export default class TeamMembersModal extends React.Component {
super(props); super(props);
this.teamChanged = this.teamChanged.bind(this); this.teamChanged = this.teamChanged.bind(this);
this.onHide = this.onHide.bind(this);
this.state = { 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()}); this.setState({team: TeamStore.getCurrent()});
} }
onHide() {
this.setState({show: false});
}
render() { render() {
let teamDisplayName = ''; let teamDisplayName = '';
if (this.state.team) { if (this.state.team) {
@@ -48,8 +54,9 @@ export default class TeamMembersModal extends React.Component {
return ( return (
<Modal <Modal
dialogClassName='more-modal' dialogClassName='more-modal'
show={this.props.show} show={this.state.show}
onHide={this.props.onHide} onHide={this.onHide}
onExited={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title> <Modal.Title>
@@ -72,7 +79,7 @@ export default class TeamMembersModal extends React.Component {
<button <button
type='button' type='button'
className='btn btn-default' className='btn btn-default'
onClick={this.props.onHide} onClick={this.onHide}
> >
<FormattedMessage <FormattedMessage
id='team_member_modal.close' id='team_member_modal.close'
@@ -86,7 +93,6 @@ export default class TeamMembersModal extends React.Component {
} }
TeamMembersModal.propTypes = { TeamMembersModal.propTypes = {
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired, onHide: React.PropTypes.func.isRequired,
isAdmin: React.PropTypes.bool.isRequired isAdmin: React.PropTypes.bool.isRequired
}; };

Просмотреть файл

@@ -16,7 +16,9 @@ export default class ModalToggleButton extends React.Component {
} }
show(e) { show(e) {
e.preventDefault(); if (e) {
e.preventDefault();
}
this.setState({show: true}); 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 let dialog;
const dialog = React.createElement(dialogType, Object.assign({}, dialogProps, { if (this.state.show) {
show: this.state.show, // this assumes that all modals will have an onHide event and will show when mounted
onHide: () => { dialog = React.createElement(dialogType, Object.assign({}, dialogProps, {
this.hide(); onHide: () => {
this.hide();
if (dialogProps.onHide) { if (dialogProps.onHide) {
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 // 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 // renders modals at the top level of the DOM instead of where you specify in the virtual DOM