Merge pull request #1600 from hmhealey/plt1152
PLT-1152 Fix cross-contamination between invite member modals
Этот коммит содержится в:
@@ -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
|
||||||
Ссылка в новой задаче
Block a user