diff options
author | Corey Hulen <corey@hulen.com> | 2016-07-06 13:40:59 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-06 13:40:59 -0800 |
commit | d5f243dad694d6746ec2b6560a81212a78d8c975 (patch) | |
tree | 7f1de697c906ff909f26b739eebaa77f18edf790 /webapp | |
parent | 3eee51f74e893f3182519ad0edb72dd5d8b107fd (diff) | |
download | chat-d5f243dad694d6746ec2b6560a81212a78d8c975.tar.gz chat-d5f243dad694d6746ec2b6560a81212a78d8c975.tar.bz2 chat-d5f243dad694d6746ec2b6560a81212a78d8c975.zip |
PLT-2863 adding remove user from team (#3429)
* PLT-2863 adding remove user from team
* PLT-2863 adding the client side UI
* Fixing trailing space
* Fixing reported issues
* Adding documentatino
* Switching to final javascript driver
Diffstat (limited to 'webapp')
-rw-r--r-- | webapp/actions/global_actions.jsx | 7 | ||||
-rw-r--r-- | webapp/actions/websocket_actions.jsx | 17 | ||||
-rw-r--r-- | webapp/components/admin_console/team_users.jsx | 4 | ||||
-rw-r--r-- | webapp/components/admin_console/user_item.jsx | 35 | ||||
-rw-r--r-- | webapp/components/filtered_user_list.jsx | 15 | ||||
-rw-r--r-- | webapp/components/leave_team_modal.jsx | 115 | ||||
-rw-r--r-- | webapp/components/navbar_dropdown.jsx | 14 | ||||
-rw-r--r-- | webapp/components/needs_team.jsx | 2 | ||||
-rw-r--r-- | webapp/components/team_members_dropdown.jsx | 36 | ||||
-rw-r--r-- | webapp/i18n/en.json | 2 | ||||
-rw-r--r-- | webapp/package.json | 2 | ||||
-rw-r--r-- | webapp/stores/modal_store.jsx | 1 | ||||
-rw-r--r-- | webapp/stores/team_store.jsx | 10 | ||||
-rw-r--r-- | webapp/utils/constants.jsx | 2 |
14 files changed, 257 insertions, 5 deletions
diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index 4baed20c3..aa51f6f62 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -288,6 +288,13 @@ export function showInviteMemberModal() { }); } +export function showLeaveTeamModal() { + AppDispatcher.handleViewAction({ + type: ActionTypes.TOGGLE_LEAVE_TEAM_MODAL, + value: true + }); +} + export function showRegisterAppModal() { AppDispatcher.handleViewAction({ type: ActionTypes.TOGGLE_REGISTER_APP_MODAL, diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx index 17f84638d..9d9cf62b7 100644 --- a/webapp/actions/websocket_actions.jsx +++ b/webapp/actions/websocket_actions.jsx @@ -135,6 +135,10 @@ function handleMessage(msg) { handleNewUserEvent(); break; + case SocketEvents.LEAVE_TEAM: + handleLeaveTeamEvent(msg); + break; + case SocketEvents.USER_ADDED: handleUserAddedEvent(msg); break; @@ -219,6 +223,19 @@ function handleNewUserEvent() { AsyncClient.getChannelExtraInfo(); } +function handleLeaveTeamEvent(msg) { + if (UserStore.getCurrentId() === msg.user_id) { + TeamStore.removeTeamMember(msg.team_id); + + // if the are on the team begin removed redirect them to the root + if (TeamStore.getCurrentId() === msg.team_id) { + browserHistory.push('/'); + } + } else if (TeamStore.getCurrentId() === msg.team_id) { + GlobalActions.emitProfilesForDmList(); + } +} + function handleDirectAddedEvent(msg) { AsyncClient.getChannel(msg.channel_id); AsyncClient.getDirectProfiles(); diff --git a/webapp/components/admin_console/team_users.jsx b/webapp/components/admin_console/team_users.jsx index b6bba3182..3ec375627 100644 --- a/webapp/components/admin_console/team_users.jsx +++ b/webapp/components/admin_console/team_users.jsx @@ -186,6 +186,10 @@ export default class UserList extends React.Component { var memberList = this.state.users.map((user) => { var teamMember = this.getTeamMemberForUser(user.id); + if (teamMember.delete_at > 0) { + return null; + } + return ( <UserItem team={this.state.team} diff --git a/webapp/components/admin_console/user_item.jsx b/webapp/components/admin_console/user_item.jsx index edded5aab..e6c4f637c 100644 --- a/webapp/components/admin_console/user_item.jsx +++ b/webapp/components/admin_console/user_item.jsx @@ -18,6 +18,7 @@ export default class UserItem extends React.Component { super(props); this.handleMakeMember = this.handleMakeMember.bind(this); + this.handleRemoveFromTeam = this.handleRemoveFromTeam.bind(this); this.handleMakeActive = this.handleMakeActive.bind(this); this.handleMakeNotActive = this.handleMakeNotActive.bind(this); this.handleMakeAdmin = this.handleMakeAdmin.bind(this); @@ -56,6 +57,19 @@ export default class UserItem extends React.Component { } } + handleRemoveFromTeam() { + Client.removeUserFromTeam( + this.props.team.id, + this.props.user.id, + () => { + this.props.refreshProfiles(); + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + } + handleMakeActive(e) { e.preventDefault(); Client.updateActive(this.props.user.id, true, @@ -222,6 +236,7 @@ export default class UserItem extends React.Component { ); } + const me = UserStore.getCurrentUser(); const email = user.email; let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin'; let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin'; @@ -299,6 +314,24 @@ export default class UserItem extends React.Component { ); } + let removeFromTeam = null; + if (this.props.user.id !== me.id) { + removeFromTeam = ( + <li role='presentation'> + <a + role='menuitem' + href='#' + onClick={this.handleRemoveFromTeam} + > + <FormattedMessage + id='team_members_dropdown.leave_team' + defaultMessage='Remove From Team' + /> + </a> + </li> + ); + } + let makeActive = null; if (showMakeActive) { makeActive = ( @@ -428,7 +461,6 @@ export default class UserItem extends React.Component { passwordReset = null; } - const me = UserStore.getCurrentUser(); let makeDemoteModal = null; if (this.props.user.id === me.id) { const title = ( @@ -511,6 +543,7 @@ export default class UserItem extends React.Component { className='dropdown-menu member-menu' role='menu' > + {removeFromTeam} {makeAdmin} {makeMember} {makeActive} diff --git a/webapp/components/filtered_user_list.jsx b/webapp/components/filtered_user_list.jsx index b6d8f11f9..67d038fd9 100644 --- a/webapp/components/filtered_user_list.jsx +++ b/webapp/components/filtered_user_list.jsx @@ -39,17 +39,24 @@ class FilteredUserList extends React.Component { this.state = { filter: '', users: this.filterUsers(props.teamMembers, props.users), - selected: 'team' + selected: 'team', + teamMembers: props.teamMembers }; } - componentWillUpdate(nextProps) { + componentWillReceiveProps(nextProps) { // assume the user list is immutable if (this.props.users !== nextProps.users) { this.setState({ users: this.filterUsers(nextProps.teamMembers, nextProps.users) }); } + + if (this.props.teamMembers !== nextProps.teamMembers) { + this.setState({ + users: this.filterUsers(nextProps.teamMembers, nextProps.users) + }); + } } componentDidMount() { @@ -70,6 +77,10 @@ class FilteredUserList extends React.Component { var filteredUsers = users.filter((user) => { for (const index in teamMembers) { if (teamMembers.hasOwnProperty(index) && teamMembers[index].user_id === user.id) { + if (teamMembers[index].delete_at > 0) { + return false; + } + return true; } } diff --git a/webapp/components/leave_team_modal.jsx b/webapp/components/leave_team_modal.jsx new file mode 100644 index 000000000..7263f23d4 --- /dev/null +++ b/webapp/components/leave_team_modal.jsx @@ -0,0 +1,115 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import Constants from 'utils/constants.jsx'; +const ActionTypes = Constants.ActionTypes; +import * as GlobalActions from 'actions/global_actions.jsx'; +import ModalStore from 'stores/modal_store.jsx'; +import UserStore from 'stores/user_store.jsx'; + +import {intlShape, injectIntl, FormattedMessage} from 'react-intl'; + +import {Modal} from 'react-bootstrap'; + +import React from 'react'; + +class LeaveTeamModal extends React.Component { + constructor(props) { + super(props); + + this.handleToggle = this.handleToggle.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + this.handleHide = this.handleHide.bind(this); + + this.state = { + show: false + }; + } + + componentDidMount() { + ModalStore.addModalListener(ActionTypes.TOGGLE_LEAVE_TEAM_MODAL, this.handleToggle); + } + + componentWillUnmount() { + ModalStore.removeModalListener(ActionTypes.TOGGLE_LEAVE_TEAM_MODAL, this.handleToggle); + } + + handleToggle(value) { + this.setState({ + show: value + }); + } + + handleSubmit() { + GlobalActions.emitLeaveTeam(); + + this.setState({ + show: false + }); + } + + handleHide() { + this.setState({ + show: false + }); + } + + render() { + var currentUser = UserStore.getCurrentUser(); + + if (currentUser != null) { + return ( + <Modal + className='modal-confirm' + show={this.state.show} + onHide={this.handleHide} + > + <Modal.Header closeButton={false}> + <Modal.Title> + <FormattedMessage + id='leave_team_modal.title' + defaultMessage='Leave the team?' + /> + </Modal.Title> + </Modal.Header> + <Modal.Body> + <FormattedMessage + id='leave_team_modal.desc' + defaultMessage='You will be removed from all public channels and private groups. If the team is private you will not be able to rejoin the team. Are you sure?' + /> + </Modal.Body> + <Modal.Footer> + <button + type='button' + className='btn btn-default' + onClick={this.handleHide} + > + <FormattedMessage + id='leave_team_modal.no' + defaultMessage='No' + /> + </button> + <button + type='button' + className='btn btn-danger' + onClick={this.handleSubmit} + > + <FormattedMessage + id='leave_team_modal.yes' + defaultMessage='Yes' + /> + </button> + </Modal.Footer> + </Modal> + ); + } + + return null; + } +} + +LeaveTeamModal.propTypes = { + intl: intlShape.isRequired +}; + +export default injectIntl(LeaveTeamModal); diff --git a/webapp/components/navbar_dropdown.jsx b/webapp/components/navbar_dropdown.jsx index ab228dcb3..c660bc164 100644 --- a/webapp/components/navbar_dropdown.jsx +++ b/webapp/components/navbar_dropdown.jsx @@ -236,6 +236,20 @@ export default class NavbarDropdown extends React.Component { ); } + teams.push( + <li key='leaveTeam_li'> + <a + href='#' + onClick={GlobalActions.showLeaveTeamModal} + > + <FormattedMessage + id='navbar_dropdown.leave' + defaultMessage='Leave Team' + /> + </a> + </li> + ); + if (this.state.teamMembers && this.state.teamMembers.length > 1) { teams.push( <li diff --git a/webapp/components/needs_team.jsx b/webapp/components/needs_team.jsx index 19ad38887..07b90636d 100644 --- a/webapp/components/needs_team.jsx +++ b/webapp/components/needs_team.jsx @@ -34,6 +34,7 @@ import RemovedFromChannelModal from 'components/removed_from_channel_modal.jsx'; import RegisterAppModal from 'components/register_app_modal.jsx'; import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx'; import InviteMemberModal from 'components/invite_member_modal.jsx'; +import LeaveTeamModal from 'components/leave_team_modal.jsx'; import SelectTeamModal from 'components/admin_console/select_team_modal.jsx'; export default class NeedsTeam extends React.Component { @@ -129,6 +130,7 @@ export default class NeedsTeam extends React.Component { <GetPublicLinkModal/> <GetTeamInviteLinkModal/> <InviteMemberModal/> + <LeaveTeamModal/> <ImportThemeModal/> <TeamSettingsModal/> <MoreChannelsModal/> diff --git a/webapp/components/team_members_dropdown.jsx b/webapp/components/team_members_dropdown.jsx index 2b40da9cf..43449635d 100644 --- a/webapp/components/team_members_dropdown.jsx +++ b/webapp/components/team_members_dropdown.jsx @@ -19,6 +19,7 @@ export default class TeamMembersDropdown extends React.Component { super(props); this.handleMakeMember = this.handleMakeMember.bind(this); + this.handleRemoveFromTeam = this.handleRemoveFromTeam.bind(this); this.handleMakeActive = this.handleMakeActive.bind(this); this.handleMakeNotActive = this.handleMakeNotActive.bind(this); this.handleMakeAdmin = this.handleMakeAdmin.bind(this); @@ -52,6 +53,19 @@ export default class TeamMembersDropdown extends React.Component { ); } } + handleRemoveFromTeam() { + Client.removeUserFromTeam( + '', + this.props.user.id, + () => { + AsyncClient.getTeamMembers(TeamStore.getCurrentId()); + AsyncClient.getProfiles(); + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + } handleMakeActive() { Client.updateActive(this.props.user.id, true, () => { @@ -171,6 +185,7 @@ export default class TeamMembersDropdown extends React.Component { ); } + const me = UserStore.getCurrentUser(); let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin'; let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin'; let showMakeActive = false; @@ -225,6 +240,24 @@ export default class TeamMembersDropdown extends React.Component { ); } + let removeFromTeam = null; + if (this.props.user.id !== me.id) { + removeFromTeam = ( + <li role='presentation'> + <a + role='menuitem' + href='#' + onClick={this.handleRemoveFromTeam} + > + <FormattedMessage + id='team_members_dropdown.leave_team' + defaultMessage='Remove From Team' + /> + </a> + </li> + ); + } + let makeActive = null; if (showMakeActive) { // makeActive = ( @@ -260,7 +293,7 @@ export default class TeamMembersDropdown extends React.Component { // </li> // ); } - const me = UserStore.getCurrentUser(); + let makeDemoteModal = null; if (this.props.user.id === me.id) { const title = ( @@ -321,6 +354,7 @@ export default class TeamMembersDropdown extends React.Component { className='dropdown-menu member-menu' role='menu' > + {removeFromTeam} {makeAdmin} {makeMember} {makeActive} diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 945c9c4a9..322c9ccad 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -1202,6 +1202,7 @@ "navbar_dropdown.accountSettings": "Account Settings", "navbar_dropdown.console": "System Console", "navbar_dropdown.create": "Create a New Team", + "navbar_dropdown.leave": "Leave Team", "navbar_dropdown.emoji": "Custom Emoji", "navbar_dropdown.help": "Help", "navbar_dropdown.integrations": "Integrations", @@ -1414,6 +1415,7 @@ "team_members_dropdown.makeAdmin": "Make Team Admin", "team_members_dropdown.makeInactive": "Make Inactive", "team_members_dropdown.makeMember": "Make Member", + "team_members_dropdown.leave_team": "Remove From Team", "team_members_dropdown.member": "Member", "team_members_dropdown.systemAdmin": "System Admin", "team_members_dropdown.teamAdmin": "Team Admin", diff --git a/webapp/package.json b/webapp/package.json index b3066542e..468325e7d 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,7 +18,7 @@ "keymirror": "0.1.1", "marked": "mattermost/marked#12d2be4cdf54d4ec95fead934e18840b6a2c1a7b", "match-at": "0.1.0", - "mattermost": "mattermost/mattermost-javascript#18527e6c4a9aea69aa7845a62d9618b357faa4e7", + "mattermost": "mattermost/mattermost-javascript#c72a75ca4ac135e2d476fc048ef7adc450e6739f", "object-assign": "4.1.0", "perfect-scrollbar": "0.6.11", "react": "15.0.2", diff --git a/webapp/stores/modal_store.jsx b/webapp/stores/modal_store.jsx index 0595daaf9..0209f3993 100644 --- a/webapp/stores/modal_store.jsx +++ b/webapp/stores/modal_store.jsx @@ -33,6 +33,7 @@ class ModalStoreClass extends EventEmitter { switch (type) { case ActionTypes.TOGGLE_IMPORT_THEME_MODAL: case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL: + case ActionTypes.TOGGLE_LEAVE_TEAM_MODAL: case ActionTypes.TOGGLE_DELETE_POST_MODAL: case ActionTypes.TOGGLE_GET_POST_LINK_MODAL: case ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL: diff --git a/webapp/stores/team_store.jsx b/webapp/stores/team_store.jsx index c35c467ae..f4383589a 100644 --- a/webapp/stores/team_store.jsx +++ b/webapp/stores/team_store.jsx @@ -139,6 +139,16 @@ class TeamStoreClass extends EventEmitter { this.team_members.push(member); } + removeTeamMember(teamId) { + for (var index in this.team_members) { + if (this.team_members.hasOwnProperty(index)) { + if (this.team_members[index].team_id === teamId) { + Reflect.deleteProperty(this.team_members, index); + } + } + } + } + getTeamMembers() { return this.team_members; } diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index efae8a050..f0cea9e52 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -113,6 +113,7 @@ export default { TOGGLE_IMPORT_THEME_MODAL: null, TOGGLE_INVITE_MEMBER_MODAL: null, + TOGGLE_LEAVE_TEAM_MODAL: null, TOGGLE_DELETE_POST_MODAL: null, TOGGLE_GET_POST_LINK_MODAL: null, TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null, @@ -160,6 +161,7 @@ export default { CHANNEL_VIEWED: 'channel_viewed', DIRECT_ADDED: 'direct_added', NEW_USER: 'new_user', + LEAVE_TEAM: 'leave_team', USER_ADDED: 'user_added', USER_REMOVED: 'user_removed', TYPING: 'typing', |