Merge pull request #1600 from hmhealey/plt1152

PLT-1152 Fix cross-contamination between invite member modals
Этот коммит содержится в:
Joram Wilander
2015-12-04 08:21:39 -05:00
родитель 93db32da7b bc173ebcb4
Коммит 04727955cd
8 изменённых файлов: 53 добавлений и 100 удалений

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

@@ -40,7 +40,6 @@ export default class ChannelHeader extends React.Component {
const state = this.getStateFromStores(); const state = this.getStateFromStores();
state.showEditChannelPurposeModal = false; state.showEditChannelPurposeModal = false;
state.showInviteModal = false;
state.showMembersModal = false; state.showMembersModal = false;
this.state = state; this.state = state;
} }
@@ -201,13 +200,13 @@ export default class ChannelHeader extends React.Component {
key='add_members' key='add_members'
role='presentation' role='presentation'
> >
<a <ToggleModalButton
role='menuitem' role='menuitem'
href='#' dialogType={ChannelInviteModal}
onClick={() => this.setState({showInviteModal: true})} dialogProps={{channel}}
> >
{'Add Members'} {'Add Members'}
</a> </ToggleModalButton>
</li> </li>
); );
@@ -402,13 +401,10 @@ export default class ChannelHeader extends React.Component {
onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})} onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
channel={channel} channel={channel}
/> />
<ChannelInviteModal
show={this.state.showInviteModal}
onModalDismissed={() => this.setState({showInviteModal: false})}
/>
<ChannelMembersModal <ChannelMembersModal
show={this.state.showMembersModal} show={this.state.showMembersModal}
onModalDismissed={() => this.setState({showMembersModal: false})} onModalDismissed={() => this.setState({showMembersModal: false})}
channel={channel}
/> />
</div> </div>
); );

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

@@ -53,15 +53,8 @@ export default class ChannelInviteModal extends React.Component {
return a.username.localeCompare(b.username); return a.username.localeCompare(b.username);
}); });
var channelName = '';
if (ChannelStore.getCurrent()) {
channelName = ChannelStore.getCurrent().display_name;
}
return { return {
nonmembers, nonmembers,
memberIds,
channelName,
loading loading
}; };
} }
@@ -94,28 +87,14 @@ export default class ChannelInviteModal extends React.Component {
} }
} }
handleInvite(userId) { handleInvite(userId) {
// Make sure the user isn't already a member of the channel
if (this.state.memberIds.indexOf(userId) > -1) {
return;
}
var data = {}; var data = {};
data.user_id = userId; data.user_id = userId;
Client.addChannelMember(ChannelStore.getCurrentId(), data, Client.addChannelMember(
this.props.channel.id,
data,
() => { () => {
var nonmembers = this.state.nonmembers; this.setState({inviteError: null});
var memberIds = this.state.memberIds;
for (var i = 0; i < nonmembers.length; i++) {
if (userId === nonmembers[i].id) {
nonmembers[i].invited = true;
memberIds.push(userId);
break;
}
}
this.setState({inviteError: null, memberIds, nonmembers});
AsyncClient.getChannelExtraInfo(); AsyncClient.getChannelExtraInfo();
}, },
(err) => { (err) => {
@@ -157,10 +136,10 @@ export default class ChannelInviteModal extends React.Component {
<Modal <Modal
dialogClassName='more-modal' dialogClassName='more-modal'
show={this.props.show} show={this.props.show}
onHide={this.props.onModalDismissed} onHide={this.props.onHide}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title>{'Add New Members to '}<span className='name'>{this.state.channelName}</span></Modal.Title> <Modal.Title>{'Add New Members to '}<span className='name'>{this.props.channel.display_nam}</span></Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body <Modal.Body
ref='modalBody' ref='modalBody'
@@ -173,7 +152,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.onModalDismissed} onClick={this.props.onHide}
> >
{'Close'} {'Close'}
</button> </button>
@@ -185,5 +164,6 @@ export default class ChannelInviteModal extends React.Component {
ChannelInviteModal.propTypes = { ChannelInviteModal.propTypes = {
show: React.PropTypes.bool.isRequired, show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func.isRequired onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired
}; };

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

@@ -69,16 +69,9 @@ export default class ChannelMembersModal extends React.Component {
memberList.sort(compareByUsername); memberList.sort(compareByUsername);
nonmemberList.sort(compareByUsername); nonmemberList.sort(compareByUsername);
const channel = ChannelStore.getCurrent();
let channelName = '';
if (channel) {
channelName = channel.display_name;
}
return { return {
nonmemberList, nonmemberList,
memberList, memberList
channelName
}; };
} }
onShow() { onShow() {
@@ -169,7 +162,7 @@ export default class ChannelMembersModal extends React.Component {
onHide={this.props.onModalDismissed} onHide={this.props.onModalDismissed}
> >
<Modal.Header closeButton={true}> <Modal.Header closeButton={true}>
<Modal.Title><span className='name'>{this.state.channelName}</span>{' Members'}</Modal.Title> <Modal.Title><span className='name'>{this.props.channel.display_name}</span>{' Members'}</Modal.Title>
<a <a
className='btn btn-md btn-primary' className='btn btn-md btn-primary'
href='#' href='#'
@@ -205,7 +198,8 @@ export default class ChannelMembersModal extends React.Component {
</Modal> </Modal>
<ChannelInviteModal <ChannelInviteModal
show={this.state.showInviteModal} show={this.state.showInviteModal}
onModalDismissed={() => this.setState({showInviteModal: false})} onHide={() => this.setState({showInviteModal: false})}
channel={this.props.channel}
/> />
</div> </div>
); );
@@ -218,5 +212,6 @@ ChannelMembersModal.defaultProps = {
ChannelMembersModal.propTypes = { ChannelMembersModal.propTypes = {
show: React.PropTypes.bool.isRequired, show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func.isRequired onModalDismissed: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired
}; };

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

@@ -31,9 +31,7 @@ export default class MemberListItem extends React.Component {
var timestamp = UserStore.getCurrentUser().update_at; var timestamp = UserStore.getCurrentUser().update_at;
var invite; var invite;
if (member.invited && this.props.handleInvite) { if (this.props.handleInvite) {
invite = <span className='member-role'>Added</span>;
} else if (this.props.handleInvite) {
invite = ( invite = (
<a <a
onClick={this.handleInvite} onClick={this.handleInvite}

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

@@ -44,7 +44,6 @@ export default class Navbar extends React.Component {
state.showEditChannelPurposeModal = false; state.showEditChannelPurposeModal = false;
state.showEditChannelHeaderModal = false; state.showEditChannelHeaderModal = false;
state.showMembersModal = false; state.showMembersModal = false;
state.showInviteModal = false;
this.state = state; this.state = state;
} }
getStateFromStores() { getStateFromStores() {
@@ -171,13 +170,13 @@ export default class Navbar extends React.Component {
if (!isDirect && !ChannelStore.isDefault(channel)) { if (!isDirect && !ChannelStore.isDefault(channel)) {
addMembersOption = ( addMembersOption = (
<li role='presentation'> <li role='presentation'>
<a <ToggleModalButton
role='menuitem' role='menuitem'
href='#' dialogType={ChannelInviteModal}
onClick={() => this.setState({showInviteModal: true})} dialogProps={{channel}}
> >
{'Add Members'} {'Add Members'}
</a> </ToggleModalButton>
</li> </li>
); );
@@ -475,10 +474,7 @@ export default class Navbar extends React.Component {
<ChannelMembersModal <ChannelMembersModal
show={this.state.showMembersModal} show={this.state.showMembersModal}
onModalDismissed={() => this.setState({showMembersModal: false})} onModalDismissed={() => this.setState({showMembersModal: false})}
/> channel={{channel}}
<ChannelInviteModal
show={this.state.showInviteModal}
onModalDismissed={() => this.setState({showInviteModal: false})}
/> />
</div> </div>
); );

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

@@ -3,7 +3,6 @@
import PostsView from './posts_view.jsx'; import PostsView from './posts_view.jsx';
import LoadingScreen from './loading_screen.jsx'; import LoadingScreen from './loading_screen.jsx';
import ChannelInviteModal from './channel_invite_modal.jsx';
import ChannelStore from '../stores/channel_store.jsx'; import ChannelStore from '../stores/channel_store.jsx';
import PostStore from '../stores/post_store.jsx'; import PostStore from '../stores/post_store.jsx';
@@ -13,7 +12,7 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Constants from '../utils/constants.jsx'; import Constants from '../utils/constants.jsx';
import {createChannelIntroMessage} from '../utils/channel_intro_mssages.jsx'; import {createChannelIntroMessage} from '../utils/channel_intro_messages.jsx';
export default class PostsViewContainer extends React.Component { export default class PostsViewContainer extends React.Component {
constructor() { constructor() {
@@ -177,7 +176,7 @@ export default class PostsViewContainer extends React.Component {
loadMorePostsBottomClicked={() => {}} loadMorePostsBottomClicked={() => {}}
showMoreMessagesTop={!this.state.atTop[this.state.currentChannelIndex]} showMoreMessagesTop={!this.state.atTop[this.state.currentChannelIndex]}
showMoreMessagesBottom={false} showMoreMessagesBottom={false}
introText={channel ? createChannelIntroMessage(channel, () => this.setState({showInviteModal: true})) : null} introText={channel ? createChannelIntroMessage(channel) : null}
messageSeparatorTime={this.state.currentLastViewed} messageSeparatorTime={this.state.currentLastViewed}
/> />
); );
@@ -194,10 +193,6 @@ export default class PostsViewContainer extends React.Component {
return ( return (
<div id='post-list'> <div id='post-list'>
{postListCtls} {postListCtls}
<ChannelInviteModal
show={this.state.showInviteModal}
onModalDismissed={() => this.setState({showInviteModal: false})}
/>
</div> </div>
); );
} }

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

@@ -167,18 +167,7 @@ class ChannelStoreClass extends EventEmitter {
this.emitChange(); this.emitChange();
} }
getCurrentExtraInfo() { getCurrentExtraInfo() {
var currentId = this.getCurrentId(); return this.getExtraInfo(this.getCurrentId());
var extra = null;
if (currentId) {
extra = this.pGetExtraInfos()[currentId];
}
if (extra == null) {
extra = {members: []};
}
return extra;
} }
getExtraInfo(channelId) { getExtraInfo(channelId) {
var extra = null; var extra = null;
@@ -187,7 +176,10 @@ class ChannelStoreClass extends EventEmitter {
extra = this.pGetExtraInfos()[channelId]; extra = this.pGetExtraInfos()[channelId];
} }
if (extra == null) { if (extra) {
// create a defensive copy
extra = JSON.parse(JSON.stringify(extra));
} else {
extra = {members: []}; extra = {members: []};
} }

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

@@ -2,6 +2,7 @@
// See License.txt for license information. // See License.txt for license information.
import * as Utils from './utils.jsx'; import * as Utils from './utils.jsx';
import ChannelInviteModal from '../components/channel_invite_modal.jsx';
import EditChannelHeaderModal from '../components/edit_channel_header_modal.jsx'; import EditChannelHeaderModal from '../components/edit_channel_header_modal.jsx';
import ToggleModalButton from '../components/toggle_modal_button.jsx'; import ToggleModalButton from '../components/toggle_modal_button.jsx';
import UserProfile from '../components/user_profile.jsx'; import UserProfile from '../components/user_profile.jsx';
@@ -10,15 +11,15 @@ import Constants from '../utils/constants.jsx';
import TeamStore from '../stores/team_store.jsx'; import TeamStore from '../stores/team_store.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx'; import * as EventHelpers from '../dispatcher/event_helpers.jsx';
export function createChannelIntroMessage(channel, showInviteModal) { export function createChannelIntroMessage(channel) {
if (channel.type === 'D') { if (channel.type === 'D') {
return createDMIntroMessage(channel); return createDMIntroMessage(channel);
} else if (ChannelStore.isDefault(channel)) { } else if (ChannelStore.isDefault(channel)) {
return createDefaultIntroMessage(channel); return createDefaultIntroMessage(channel);
} else if (channel.name === Constants.OFFTOPIC_CHANNEL) { } else if (channel.name === Constants.OFFTOPIC_CHANNEL) {
return createOffTopicIntroMessage(channel, showInviteModal); return createOffTopicIntroMessage(channel);
} else if (channel.type === 'O' || channel.type === 'P') { } else if (channel.type === 'O' || channel.type === 'P') {
return createStandardIntroMessage(channel, showInviteModal); return createStandardIntroMessage(channel);
} }
} }
@@ -62,7 +63,7 @@ export function createDMIntroMessage(channel) {
); );
} }
export function createOffTopicIntroMessage(channel, showInviteModal) { export function createOffTopicIntroMessage(channel) {
return ( return (
<div className='channel-intro'> <div className='channel-intro'>
<h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4> <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
@@ -71,13 +72,7 @@ export function createOffTopicIntroMessage(channel, showInviteModal) {
<br/> <br/>
</p> </p>
{createSetHeaderButton(channel)} {createSetHeaderButton(channel)}
<a {createInviteChannelMemberButton(channel, 'channel')}
href='#'
className='intro-links'
onClick={showInviteModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this channel'}
</a>
</div> </div>
); );
} }
@@ -122,7 +117,7 @@ export function createDefaultIntroMessage(channel) {
); );
} }
export function createStandardIntroMessage(channel, showInviteModal) { export function createStandardIntroMessage(channel) {
var uiName = channel.display_name; var uiName = channel.display_name;
var creatorName = ''; var creatorName = '';
@@ -162,17 +157,23 @@ export function createStandardIntroMessage(channel, showInviteModal) {
<br/> <br/>
</p> </p>
{createSetHeaderButton(channel)} {createSetHeaderButton(channel)}
<a {createInviteChannelMemberButton(channel, uiType)}
className='intro-links'
href='#'
onClick={showInviteModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this ' + uiType}
</a>
</div> </div>
); );
} }
function createInviteChannelMemberButton(channel, uiType) {
return (
<ToggleModalButton
className='intro-links'
dialogType={ChannelInviteModal}
dialogProps={{channel}}
>
<i className='fa fa-user-plus'></i>{'Invite others to this ' + uiType}
</ToggleModalButton>
);
}
function createSetHeaderButton(channel) { function createSetHeaderButton(channel) {
return ( return (
<ToggleModalButton <ToggleModalButton