[NEW] Remove member from team (#3117)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Added SelectListView and logic for leaving team

* Added addTeamMember and removeTeamMember

* Minor tweak

* Minor tweak

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Remove unnecesary prop

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* Minor refactor to ListItem, add SelectListView to ModalStack, update handleLeaveTeam

* Minor tweaks

* Update SelectListView

* Update handleLeaveTeam, remove unnecessary method, add story

* Minor tweak

* Minor visual tweaks

* Update SelectListView.js

* Update RoomMembersView

* Updated SelectListView, RoomActionsView, leaveTeam method and string translations

* Update SelectListVIew

* Minor tweak

* Update SelectListView

* Minor tweak

* Minor tweaks

* Fix for List.Item subtitles being pushed down by title's flex

* Minor tweaks

* Update RoomActionsView

* Use showConfirmationAlert and showErrorAlert

* Remove addTeamMember, update removeTeamMember

* Update Alert

* Minor tweaks

* Minor tweaks

* Minor tweak

* Update showActionSheet on RoomMembersView

* Remove team main from query and move code around

* Fetch roles

* Update RoomMembersView and SelectListView

* Updated leaveTeam and handleRemoveFromTeam

* Fix validation

* Remove unnecessary function

* Added confirmationAlert for missing permissions case

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gerzon Z 2021-05-26 17:01:06 -04:00 committed by GitHub
parent 17c28e0b1b
commit 6dcb9a51f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 66 deletions

View File

@ -683,12 +683,9 @@
"No_threads_following": "You are not following any threads",
"No_threads_unread": "There are no unread threads",
"Messagebox_Send_to_channel": "Send to channel",
"Set_as_leader": "Set as leader",
"Set_as_moderator": "Set as moderator",
"Set_as_owner": "Set as owner",
"Remove_as_leader": "Remove as leader",
"Remove_as_moderator": "Remove as moderator",
"Remove_as_owner": "Remove as owner",
"Leader": "Leader",
"Moderator": "Moderator",
"Owner": "Owner",
"Remove_from_room": "Remove from room",
"Ignore": "Ignore",
"Unignore": "Unignore",
@ -732,9 +729,14 @@
"Leave_Team": "Leave Team",
"Select_Team_Channels": "Select the Team's channels you would like to leave.",
"Cannot_leave": "Cannot leave",
"Cannot_remove": "Cannot remove",
"Last_owner_team_room": "You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.",
"last-owner-can-not-be-removed": "Last owner cannot be removed",
"Removing_user_from_this_team": "You are removing {{user}} from this team",
"Remove_User_Team_Channels": "Select the channels you want the user to be removed from.",
"Remove_Member": "Remove Member",
"leaving_team": "leaving team",
"removing_team": "removing from team",
"member-does-not-exist": "Member does not exist",
"Load_More": "Load More",
"Load_Newer": "Load Newer",

View File

@ -22,6 +22,7 @@ const PERMISSIONS = [
'delete-p',
'edit-message',
'edit-room',
'edit-team-member',
'edit-team-channel',
'force-delete-message',
'mute-user',

View File

@ -778,10 +778,22 @@ const RocketChat = {
// RC 3.13.0
return this.post('teams.leave', { teamName, rooms });
},
removeTeamMember({
teamId, teamName, userId, rooms
}) {
// RC 3.13.0
return this.post('teams.removeMember', {
teamId, teamName, userId, rooms
});
},
updateTeamRoom({ roomId, isDefault }) {
// RC 3.13.0
return this.post('teams.updateRoom', { roomId, isDefault });
},
teamListRoomsOfUser({ teamId, userId }) {
// RC 3.13.0
return this.sdk.get('teams.listRoomsOfUser', { teamId, userId });
},
joinRoom(roomId, joinCode, type) {
// TODO: join code
// RC 0.48.0

View File

@ -5,7 +5,6 @@ import {
} from 'react-native';
import { connect } from 'react-redux';
import isEmpty from 'lodash/isEmpty';
import { Q } from '@nozbe/watermelondb';
import { compareServerVersion, methods } from '../../lib/utils';
import Touch from '../../utils/touch';
@ -433,14 +432,15 @@ class RoomActionsView extends React.Component {
const { navigation } = this.props;
try {
const db = database.active;
const subCollection = db.get('subscriptions');
const teamChannels = await subCollection.query(
Q.where('team_id', room.teamId),
Q.where('team_main', null)
);
const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id });
if (teamChannels.length) {
if (result.rooms?.length) {
const teamChannels = result.rooms.map(r => ({
rid: r._id,
name: r.name,
teamId: r.teamId,
alert: r.isLastOwner
}));
navigation.navigate('SelectListView', {
title: 'Leave_Team',
data: teamChannels,
@ -456,7 +456,11 @@ class RoomActionsView extends React.Component {
});
}
} catch (e) {
log(e);
showConfirmationAlert({
message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => this.handleLeaveTeam()
});
}
}

View File

@ -23,9 +23,10 @@ import { withTheme } from '../../theme';
import { themes } from '../../constants/colors';
import { getUserSelector } from '../../selectors/login';
import { withActionSheet } from '../../containers/ActionSheet';
import { showConfirmationAlert } from '../../utils/info';
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
import SafeAreaView from '../../containers/SafeAreaView';
import { goRoom } from '../../utils/goRoom';
import { CustomIcon } from '../../lib/Icons';
const PAGE_SIZE = 25;
@ -34,6 +35,9 @@ const PERMISSION_SET_LEADER = 'set-leader';
const PERMISSION_SET_OWNER = 'set-owner';
const PERMISSION_SET_MODERATOR = 'set-moderator';
const PERMISSION_REMOVE_USER = 'remove-user';
const PERMISSION_EDIT_TEAM_MEMBER = 'edit-team-member';
const PERMISION_VIEW_ALL_TEAMS = 'view-all-teams';
const PERMISSION_VIEW_ALL_TEAM_CHANNELS = 'view-all-team-channels';
class RoomMembersView extends React.Component {
static propTypes = {
@ -55,7 +59,10 @@ class RoomMembersView extends React.Component {
setLeaderPermission: PropTypes.array,
setOwnerPermission: PropTypes.array,
setModeratorPermission: PropTypes.array,
removeUserPermission: PropTypes.array
removeUserPermission: PropTypes.array,
editTeamMemberPermission: PropTypes.array,
viewAllTeamChannelsPermission: PropTypes.array,
viewAllTeamsPermission: PropTypes.array
}
constructor(props) {
@ -94,10 +101,11 @@ class RoomMembersView extends React.Component {
const { room } = this.state;
const {
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission
} = this.props;
const result = await RocketChat.hasPermission([
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, ...(room.teamMain ? [editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission] : [])
], room.rid);
this.permissions = {
@ -105,7 +113,12 @@ class RoomMembersView extends React.Component {
[PERMISSION_SET_LEADER]: result[1],
[PERMISSION_SET_OWNER]: result[2],
[PERMISSION_SET_MODERATOR]: result[3],
[PERMISSION_REMOVE_USER]: result[4]
[PERMISSION_REMOVE_USER]: result[4],
...(room.teamMain ? {
[PERMISSION_EDIT_TEAM_MEMBER]: result[5],
[PERMISSION_VIEW_ALL_TEAM_CHANNELS]: result[6],
[PERMISION_VIEW_ALL_TEAMS]: result[7]
} : {})
};
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
@ -163,9 +176,80 @@ class RoomMembersView extends React.Component {
}
}
handleRemoveFromTeam = async(selectedUser) => {
try {
const { navigation } = this.props;
const { room } = this.state;
const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: selectedUser._id });
if (result.rooms?.length) {
const teamChannels = result.rooms.map(r => ({
rid: r._id,
name: r.name,
teamId: r.teamId,
alert: r.isLastOwner
}));
navigation.navigate('SelectListView', {
title: 'Remove_Member',
infoText: 'Remove_User_Team_Channels',
data: teamChannels,
nextAction: selected => this.removeFromTeam(selectedUser, selected),
showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_remove'))
});
} else {
showConfirmationAlert({
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
onPress: () => this.removeFromTeam(selectedUser)
});
}
} catch (e) {
showConfirmationAlert({
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
onPress: () => this.removeFromTeam(selectedUser)
});
}
}
removeFromTeam = async(selectedUser, selected) => {
try {
const { members, membersFiltered, room } = this.state;
const { navigation } = this.props;
const userId = selectedUser._id;
const result = await RocketChat.removeTeamMember({
teamId: room.teamId,
teamName: room.name,
userId,
...(selected && { rooms: selected })
});
if (result.success) {
const message = I18n.t('User_has_been_removed_from_s', { s: RocketChat.getRoomTitle(room) });
EventEmitter.emit(LISTENER, { message });
const newMembers = members.filter(member => member._id !== userId);
const newMembersFiltered = membersFiltered.filter(member => member._id !== userId);
this.setState({
members: newMembers,
membersFiltered: newMembersFiltered
});
navigation.navigate('RoomMembersView');
}
} catch (e) {
log(e);
showErrorAlert(
e.data.error
? I18n.t(e.data.error)
: I18n.t('There_was_an_error_while_action', { action: I18n.t('removing_team') }),
I18n.t('Cannot_remove')
);
}
}
onPressUser = (selectedUser) => {
const { room } = this.state;
const { showActionSheet, user } = this.props;
const { showActionSheet, user, theme } = this.props;
const options = [{
icon: 'message',
@ -173,39 +257,6 @@ class RoomMembersView extends React.Component {
onPress: () => this.navToDirectMessage(selectedUser)
}];
// Owner
if (this.permissions['set-owner']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isOwner = userRoleResult?.roles.includes('owner');
options.push({
icon: 'shield-check',
title: I18n.t(isOwner ? 'Remove_as_owner' : 'Set_as_owner'),
onPress: () => this.handleOwner(selectedUser, !isOwner)
});
}
// Leader
if (this.permissions['set-leader']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isLeader = userRoleResult?.roles.includes('leader');
options.push({
icon: 'shield-alt',
title: I18n.t(isLeader ? 'Remove_as_leader' : 'Set_as_leader'),
onPress: () => this.handleLeader(selectedUser, !isLeader)
});
}
// Moderator
if (this.permissions['set-moderator']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isModerator = userRoleResult?.roles.includes('moderator');
options.push({
icon: 'shield',
title: I18n.t(isModerator ? 'Remove_as_moderator' : 'Set_as_moderator'),
onPress: () => this.handleModerator(selectedUser, !isModerator)
});
}
// Ignore
if (selectedUser._id !== user.id) {
const { ignored } = room;
@ -236,8 +287,54 @@ class RoomMembersView extends React.Component {
});
}
// Owner
if (this.permissions['set-owner']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isOwner = userRoleResult?.roles.includes('owner');
options.push({
icon: 'shield-check',
title: I18n.t('Owner'),
onPress: () => this.handleOwner(selectedUser, !isOwner),
right: () => <CustomIcon name={isOwner ? 'checkbox-checked' : 'checkbox-unchecked'} size={20} color={isOwner ? themes[theme].tintActive : themes[theme].auxiliaryTintColor} />
});
}
// Leader
if (this.permissions['set-leader']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isLeader = userRoleResult?.roles.includes('leader');
options.push({
icon: 'shield-alt',
title: I18n.t('Leader'),
onPress: () => this.handleLeader(selectedUser, !isLeader),
right: () => <CustomIcon name={isLeader ? 'checkbox-checked' : 'checkbox-unchecked'} size={20} color={isLeader ? themes[theme].tintActive : themes[theme].auxiliaryTintColor} />
});
}
// Moderator
if (this.permissions['set-moderator']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isModerator = userRoleResult?.roles.includes('moderator');
options.push({
icon: 'shield',
title: I18n.t('Moderator'),
onPress: () => this.handleModerator(selectedUser, !isModerator),
right: () => <CustomIcon name={isModerator ? 'checkbox-checked' : 'checkbox-unchecked'} size={20} color={isModerator ? themes[theme].tintActive : themes[theme].auxiliaryTintColor} />
});
}
// Remove from team
if (this.permissions['edit-team-member']) {
options.push({
icon: 'logout',
danger: true,
title: I18n.t('Remove_from_Team'),
onPress: () => this.handleRemoveFromTeam(selectedUser)
});
}
// Remove from room
if (this.permissions['remove-user']) {
if (this.permissions['remove-user'] && !room.teamMain) {
options.push({
icon: 'logout',
title: I18n.t('Remove_from_room'),
@ -477,7 +574,10 @@ const mapStateToProps = state => ({
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER]
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER],
editTeamMemberPermission: state.permissions[PERMISSION_EDIT_TEAM_MEMBER],
viewAllTeamChannelsPermission: state.permissions[PERMISSION_VIEW_ALL_TEAM_CHANNELS],
viewAllTeamsPermission: state.permissions[PERMISION_VIEW_ALL_TEAMS]
});
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));

View File

@ -14,7 +14,6 @@ import { themes } from '../constants/colors';
import { withTheme } from '../theme';
import SafeAreaView from '../containers/SafeAreaView';
import { animateNextTransition } from '../utils/layoutAnimation';
import Loading from '../containers/Loading';
const styles = StyleSheet.create({
buttonText: {
@ -41,8 +40,7 @@ class SelectListView extends React.Component {
this.showAlert = props.route?.params?.showAlert;
this.state = {
data,
selected: [],
loading: false
selected: []
};
this.setHeader();
}
@ -96,10 +94,8 @@ class SelectListView extends React.Component {
renderItem = ({ item }) => {
const { theme } = this.props;
const alert = item.roles.length;
const icon = item.t === 'p' ? 'channel-private' : 'channel-public';
const checked = this.isChecked(item.rid, item.roles) ? 'check' : null;
const checked = this.isChecked(item.rid) ? 'check' : null;
return (
<>
@ -108,8 +104,8 @@ class SelectListView extends React.Component {
title={item.name}
translateTitle={false}
testID={`select-list-view-item-${ item.name }`}
onPress={() => (alert ? this.showAlert() : this.toggleItem(item.rid))}
alert={alert}
onPress={() => (item.alert ? this.showAlert() : this.toggleItem(item.rid))}
alert={item.alert}
left={() => <List.Icon name={icon} color={themes[theme].controlText} />}
right={() => (checked ? <List.Icon name={checked} color={themes[theme].actionTintColor} /> : null)}
/>
@ -118,7 +114,7 @@ class SelectListView extends React.Component {
}
render() {
const { loading, data } = this.state;
const { data } = this.state;
const { theme } = this.props;
return (
@ -133,7 +129,6 @@ class SelectListView extends React.Component {
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
keyboardShouldPersistTaps='always'
/>
<Loading visible={loading} />
</SafeAreaView>
);
}