[NEW] Unify members section (#4399)
* create useUserPermissions hook * create CheckRadioButton component * fix return * create MembersSection component * apply MembersSection and header filter * fix re-render and testID * fix detox tests * rename to RadioButton * move the component closer to the screen * remove useUserPermissions * remove theme prop * migrate to hooks * fix team permissions * remove theme prop from UserItem * remove options prop * fix Member * remove commented test * fixes * fix for room not joined * add room members events * adds empty option * add members filter and pagination * clear RoomMembersView * remove unused styles * Update app/views/RoomMembersView/index.tsx Co-authored-by: Diego Mello <diegolmello@gmail.com> * wip * Temp workaround for SearchBox background color * Rename import * Fix missing params for 5.0 * Fix e2e tests Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
ccbc84f9a8
commit
cbc6892084
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { RadioButton as RadioButtonUiLib } from 'react-native-ui-lib';
|
||||||
|
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
|
export const RadioButton = ({ check, testID, size }: { check: boolean; testID?: string; size?: number }): React.ReactElement => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return (
|
||||||
|
<RadioButtonUiLib
|
||||||
|
testID={testID}
|
||||||
|
selected={check}
|
||||||
|
size={size || 20}
|
||||||
|
color={check ? colors.tintActive : colors.auxiliaryTintColor}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -4,9 +4,8 @@ import { Pressable, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-n
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import { CustomIcon, TIconsName } from './CustomIcon';
|
import { CustomIcon, TIconsName } from './CustomIcon';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../lib/constants';
|
|
||||||
import { isIOS } from '../lib/methods/helpers';
|
import { isIOS } from '../lib/methods/helpers';
|
||||||
import { TSupportedThemes } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
|
@ -47,34 +46,36 @@ interface IUserItem {
|
||||||
onLongPress?: () => void;
|
onLongPress?: () => void;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
icon?: TIconsName | null;
|
icon?: TIconsName | null;
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, theme }: IUserItem) => (
|
const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon }: IUserItem): React.ReactElement => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
android_ripple={{
|
android_ripple={{
|
||||||
color: themes[theme].bannerBackground
|
color: colors.bannerBackground
|
||||||
}}
|
}}
|
||||||
style={({ pressed }: any) => ({
|
style={({ pressed }: any) => ({
|
||||||
backgroundColor: isIOS && pressed ? themes[theme].bannerBackground : 'transparent'
|
backgroundColor: isIOS && pressed ? colors.bannerBackground : 'transparent'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<View style={[styles.container, styles.button, style]}>
|
<View style={[styles.container, styles.button, style]}>
|
||||||
<Avatar text={username} size={30} style={styles.avatar} />
|
<Avatar text={username} size={30} style={styles.avatar} />
|
||||||
<View style={styles.textContainer}>
|
<View style={styles.textContainer}>
|
||||||
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>
|
<Text style={[styles.name, { color: colors.titleText }]} numberOfLines={1}>
|
||||||
{name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
<Text style={[styles.username, { color: colors.auxiliaryText }]} numberOfLines={1}>
|
||||||
@{username}
|
@{username}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
{icon ? <CustomIcon name={icon} size={22} color={themes[theme].actionTintColor} style={styles.icon} /> : null}
|
{icon ? <CustomIcon name={icon} size={22} color={colors.actionTintColor} style={styles.icon} /> : null}
|
||||||
</View>
|
</View>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default UserItem;
|
export default UserItem;
|
||||||
|
|
|
@ -356,6 +356,7 @@
|
||||||
"No_mentioned_messages": "No mentioned messages",
|
"No_mentioned_messages": "No mentioned messages",
|
||||||
"No_pinned_messages": "No pinned messages",
|
"No_pinned_messages": "No pinned messages",
|
||||||
"No_results_found": "No results found",
|
"No_results_found": "No results found",
|
||||||
|
"No_members_found": "No members found",
|
||||||
"No_starred_messages": "No starred messages",
|
"No_starred_messages": "No starred messages",
|
||||||
"No_thread_messages": "No thread messages",
|
"No_thread_messages": "No thread messages",
|
||||||
"No_label_provided": "No {{label}} provided.",
|
"No_label_provided": "No {{label}} provided.",
|
||||||
|
|
|
@ -334,6 +334,7 @@
|
||||||
"No_mentioned_messages": "Não há menções",
|
"No_mentioned_messages": "Não há menções",
|
||||||
"No_pinned_messages": "Não há mensagens fixadas",
|
"No_pinned_messages": "Não há mensagens fixadas",
|
||||||
"No_results_found": "Nenhum resultado encontrado",
|
"No_results_found": "Nenhum resultado encontrado",
|
||||||
|
"No_members_found": "Nenhum usuário encontrado",
|
||||||
"No_starred_messages": "Não há mensagens favoritas",
|
"No_starred_messages": "Não há mensagens favoritas",
|
||||||
"No_thread_messages": "Não há tópicos",
|
"No_thread_messages": "Não há tópicos",
|
||||||
"No_label_provided": "Sem {{label}}.",
|
"No_label_provided": "Sem {{label}}.",
|
||||||
|
|
|
@ -85,7 +85,7 @@ export function hasRole(role): boolean {
|
||||||
return userRoles.indexOf(role) > -1;
|
return userRoles.indexOf(role) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function hasPermission(permissions, rid?: any): boolean[] {
|
export async function hasPermission(permissions, rid?: any): Promise<boolean[]> {
|
||||||
let roomRoles = [];
|
let roomRoles = [];
|
||||||
if (rid) {
|
if (rid) {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
|
|
@ -272,6 +272,10 @@ export default {
|
||||||
RA_MOVE_TO_TEAM_F: 'ra_move_to_team_f',
|
RA_MOVE_TO_TEAM_F: 'ra_move_to_team_f',
|
||||||
RA_SEARCH_TEAM: 'ra_search_team',
|
RA_SEARCH_TEAM: 'ra_search_team',
|
||||||
|
|
||||||
|
// ROOM MEMBERS ACTIONS VIEW
|
||||||
|
RM_GO_SELECTEDUSERS: 'rm_go_selected_users',
|
||||||
|
RM_GO_INVITEUSERS: 'rm_go_invite_users',
|
||||||
|
|
||||||
// ROOM INFO VIEW
|
// ROOM INFO VIEW
|
||||||
RI_GO_RI_EDIT: 'ri_go_ri_edit',
|
RI_GO_RI_EDIT: 'ri_go_ri_edit',
|
||||||
RI_GO_LIVECHAT_EDIT: 'ri_go_livechat_edit',
|
RI_GO_LIVECHAT_EDIT: 'ri_go_livechat_edit',
|
||||||
|
|
|
@ -17,10 +17,15 @@ function replace(name: string, params: any) {
|
||||||
navigationRef.current?.dispatch(StackActions.replace(name, params));
|
navigationRef.current?.dispatch(StackActions.replace(name, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function popToTop() {
|
||||||
|
navigationRef.current?.dispatch(StackActions.popToTop());
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
navigationRef,
|
navigationRef,
|
||||||
routeNameRef,
|
routeNameRef,
|
||||||
navigate,
|
navigate,
|
||||||
back,
|
back,
|
||||||
replace
|
replace,
|
||||||
|
popToTop
|
||||||
};
|
};
|
||||||
|
|
|
@ -95,7 +95,7 @@ const ChatsStackNavigator = () => {
|
||||||
<ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} />
|
<ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} />
|
||||||
<ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
|
<ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
|
||||||
<ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
|
<ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
|
||||||
<ChatsStack.Screen name='RoomMembersView' component={RoomMembersView} options={RoomMembersView.navigationOptions} />
|
<ChatsStack.Screen name='RoomMembersView' component={RoomMembersView} />
|
||||||
<ChatsStack.Screen name='DiscussionsView' component={DiscussionsView} />
|
<ChatsStack.Screen name='DiscussionsView' component={DiscussionsView} />
|
||||||
<ChatsStack.Screen
|
<ChatsStack.Screen
|
||||||
name='SearchMessagesView'
|
name='SearchMessagesView'
|
||||||
|
|
|
@ -127,7 +127,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
|
||||||
<ModalStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
|
<ModalStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
|
||||||
<ModalStack.Screen name='SelectListView' component={SelectListView} />
|
<ModalStack.Screen name='SelectListView' component={SelectListView} />
|
||||||
<ModalStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
|
<ModalStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
|
||||||
<ModalStack.Screen name='RoomMembersView' component={RoomMembersView} options={RoomMembersView.navigationOptions} />
|
<ModalStack.Screen name='RoomMembersView' component={RoomMembersView} />
|
||||||
<ModalStack.Screen
|
<ModalStack.Screen
|
||||||
name='SearchMessagesView'
|
name='SearchMessagesView'
|
||||||
component={SearchMessagesView}
|
component={SearchMessagesView}
|
||||||
|
|
|
@ -64,6 +64,7 @@ export type ModalStackParamList = {
|
||||||
RoomMembersView: {
|
RoomMembersView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
room: TSubscriptionModel;
|
room: TSubscriptionModel;
|
||||||
|
joined?: boolean;
|
||||||
};
|
};
|
||||||
DiscussionsView: {
|
DiscussionsView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
|
|
|
@ -75,6 +75,7 @@ export type ChatsStackParamList = {
|
||||||
RoomMembersView: {
|
RoomMembersView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
room: ISubscription;
|
room: ISubscription;
|
||||||
|
joined?: boolean;
|
||||||
};
|
};
|
||||||
DiscussionsView: {
|
DiscussionsView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
|
|
|
@ -331,20 +331,15 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem = ({ item }: { item: IOtherUser }) => {
|
renderItem = ({ item }: { item: IOtherUser }) => (
|
||||||
const { theme } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<UserItem
|
<UserItem
|
||||||
name={item.fname}
|
name={item.fname}
|
||||||
username={item.name}
|
username={item.name}
|
||||||
onPress={() => this.removeUser(item)}
|
onPress={() => this.removeUser(item)}
|
||||||
testID={`create-channel-view-item-${item.name}`}
|
testID={`create-channel-view-item-${item.name}`}
|
||||||
icon='check'
|
icon='check'
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderInvitedList = () => {
|
renderInvitedList = () => {
|
||||||
const { users, theme } = this.props;
|
const { users, theme } = this.props;
|
||||||
|
|
|
@ -290,7 +290,6 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
|
||||||
onPress={() => this.goRoom(itemModel)}
|
onPress={() => this.goRoom(itemModel)}
|
||||||
testID={`new-message-view-item-${item.name}`}
|
testID={`new-message-view-item-${item.name}`}
|
||||||
style={style}
|
style={style}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,10 +64,6 @@ interface IRoomActionsViewProps extends IActionSheetProvider, IBaseScreen<ChatsS
|
||||||
encryptionEnabled: boolean;
|
encryptionEnabled: boolean;
|
||||||
fontScale: number;
|
fontScale: number;
|
||||||
serverVersion: string | null;
|
serverVersion: string | null;
|
||||||
addUserToJoinedRoomPermission?: string[];
|
|
||||||
addUserToAnyCRoomPermission?: string[];
|
|
||||||
addUserToAnyPRoomPermission?: string[];
|
|
||||||
createInviteLinksPermission?: string[];
|
|
||||||
editRoomPermission?: string[];
|
editRoomPermission?: string[];
|
||||||
toggleRoomE2EEncryptionPermission?: string[];
|
toggleRoomE2EEncryptionPermission?: string[];
|
||||||
viewBroadcastMemberListPermission?: string[];
|
viewBroadcastMemberListPermission?: string[];
|
||||||
|
@ -94,8 +90,6 @@ interface IRoomActionsViewState {
|
||||||
joined: boolean;
|
joined: boolean;
|
||||||
canViewMembers: boolean;
|
canViewMembers: boolean;
|
||||||
canAutoTranslate: boolean;
|
canAutoTranslate: boolean;
|
||||||
canAddUser: boolean;
|
|
||||||
canInviteUser: boolean;
|
|
||||||
canEdit: boolean;
|
canEdit: boolean;
|
||||||
canToggleEncryption: boolean;
|
canToggleEncryption: boolean;
|
||||||
canCreateTeam: boolean;
|
canCreateTeam: boolean;
|
||||||
|
@ -146,8 +140,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
joined: !!room,
|
joined: !!room,
|
||||||
canViewMembers: false,
|
canViewMembers: false,
|
||||||
canAutoTranslate: false,
|
canAutoTranslate: false,
|
||||||
canAddUser: false,
|
|
||||||
canInviteUser: false,
|
|
||||||
canEdit: false,
|
canEdit: false,
|
||||||
canToggleEncryption: false,
|
canToggleEncryption: false,
|
||||||
canCreateTeam: false,
|
canCreateTeam: false,
|
||||||
|
@ -206,8 +198,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
}
|
}
|
||||||
|
|
||||||
const canAutoTranslate = canAutoTranslateMethod();
|
const canAutoTranslate = canAutoTranslateMethod();
|
||||||
const canAddUser = await this.canAddUser();
|
|
||||||
const canInviteUser = await this.canInviteUser();
|
|
||||||
const canEdit = await this.canEdit();
|
const canEdit = await this.canEdit();
|
||||||
const canToggleEncryption = await this.canToggleEncryption();
|
const canToggleEncryption = await this.canToggleEncryption();
|
||||||
const canViewMembers = await this.canViewMembers();
|
const canViewMembers = await this.canViewMembers();
|
||||||
|
@ -217,8 +207,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
canAutoTranslate,
|
canAutoTranslate,
|
||||||
canAddUser,
|
|
||||||
canInviteUser,
|
|
||||||
canEdit,
|
canEdit,
|
||||||
canToggleEncryption,
|
canToggleEncryption,
|
||||||
canViewMembers,
|
canViewMembers,
|
||||||
|
@ -261,40 +249,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
canAddUser = async () => {
|
|
||||||
const { room, joined } = this.state;
|
|
||||||
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
|
|
||||||
const { rid, t } = room;
|
|
||||||
let canAddUser = false;
|
|
||||||
|
|
||||||
const userInRoom = joined;
|
|
||||||
const permissions = await hasPermission(
|
|
||||||
[addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission],
|
|
||||||
rid
|
|
||||||
);
|
|
||||||
|
|
||||||
if (userInRoom && permissions[0]) {
|
|
||||||
canAddUser = true;
|
|
||||||
}
|
|
||||||
if (t === 'c' && permissions[1]) {
|
|
||||||
canAddUser = true;
|
|
||||||
}
|
|
||||||
if (t === 'p' && permissions[2]) {
|
|
||||||
canAddUser = true;
|
|
||||||
}
|
|
||||||
return canAddUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
canInviteUser = async () => {
|
|
||||||
const { room } = this.state;
|
|
||||||
const { createInviteLinksPermission } = this.props;
|
|
||||||
const { rid } = room;
|
|
||||||
const permissions = await hasPermission([createInviteLinksPermission], rid);
|
|
||||||
|
|
||||||
const canInviteUser = permissions[0];
|
|
||||||
return canInviteUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
canEdit = async () => {
|
canEdit = async () => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { editRoomPermission } = this.props;
|
const { editRoomPermission } = this.props;
|
||||||
|
@ -1135,7 +1089,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { room, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate } = this.state;
|
const { room, membersCount, canViewMembers, joined, canAutoTranslate } = this.state;
|
||||||
const { rid, t, prid } = room;
|
const { rid, t, prid } = room;
|
||||||
const isGroupChatHandler = isGroupChat(room);
|
const isGroupChatHandler = isGroupChat(room);
|
||||||
|
|
||||||
|
@ -1154,7 +1108,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
<List.Item
|
<List.Item
|
||||||
title='Members'
|
title='Members'
|
||||||
subtitle={membersCount > 0 ? `${membersCount} ${I18n.t('members')}` : undefined}
|
subtitle={membersCount > 0 ? `${membersCount} ${I18n.t('members')}` : undefined}
|
||||||
onPress={() => this.onPressTouchable({ route: 'RoomMembersView', params: { rid, room } })}
|
onPress={() => this.onPressTouchable({ route: 'RoomMembersView', params: { rid, room, joined: this.joined } })}
|
||||||
testID='room-actions-members'
|
testID='room-actions-members'
|
||||||
left={() => <List.Icon name='team' />}
|
left={() => <List.Icon name='team' />}
|
||||||
showActionIndicator
|
showActionIndicator
|
||||||
|
@ -1164,45 +1118,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{['c', 'p'].includes(t) && canAddUser ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Add_users'
|
|
||||||
onPress={() =>
|
|
||||||
this.onPressTouchable({
|
|
||||||
route: 'SelectedUsersView',
|
|
||||||
params: {
|
|
||||||
title: I18n.t('Add_users'),
|
|
||||||
nextAction: this.addUser
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
testID='room-actions-add-user'
|
|
||||||
left={() => <List.Icon name='add' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{['c', 'p'].includes(t) && canInviteUser ? (
|
|
||||||
<>
|
|
||||||
<List.Item
|
|
||||||
title='Invite_users'
|
|
||||||
onPress={() =>
|
|
||||||
this.onPressTouchable({
|
|
||||||
route: 'InviteUsersView',
|
|
||||||
params: { rid }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
testID='room-actions-invite-user'
|
|
||||||
left={() => <List.Icon name='user-add' />}
|
|
||||||
showActionIndicator
|
|
||||||
/>
|
|
||||||
<List.Separator />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{['c', 'p', 'd'].includes(t) && !prid ? (
|
{['c', 'p', 'd'].includes(t) && !prid ? (
|
||||||
<>
|
<>
|
||||||
<List.Item
|
<List.Item
|
||||||
|
@ -1384,10 +1299,6 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
encryptionEnabled: state.encryption.enabled,
|
encryptionEnabled: state.encryption.enabled,
|
||||||
serverVersion: state.server.version,
|
serverVersion: state.server.version,
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
|
|
||||||
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
|
|
||||||
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
|
|
||||||
createInviteLinksPermission: state.permissions['create-invite-links'],
|
|
||||||
editRoomPermission: state.permissions['edit-room'],
|
editRoomPermission: state.permissions['edit-room'],
|
||||||
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
|
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
|
||||||
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
|
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { CompositeNavigationProp, useNavigation } from '@react-navigation/native';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
import { setLoading } from '../../../actions/selectedUsers';
|
||||||
|
import * as List from '../../../containers/List';
|
||||||
|
import { TSubscriptionModel } from '../../../definitions';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
import { usePermissions } from '../../../lib/hooks';
|
||||||
|
import log, { events, logEvent } from '../../../lib/methods/helpers/log';
|
||||||
|
import { Services } from '../../../lib/services';
|
||||||
|
import { MasterDetailInsideStackParamList } from '../../../stacks/MasterDetailStack/types';
|
||||||
|
import { ChatsStackParamList } from '../../../stacks/types';
|
||||||
|
|
||||||
|
type TNavigation = CompositeNavigationProp<
|
||||||
|
StackNavigationProp<ChatsStackParamList, 'RoomActionsView'>,
|
||||||
|
StackNavigationProp<MasterDetailInsideStackParamList>
|
||||||
|
>;
|
||||||
|
|
||||||
|
interface IActionsSection {
|
||||||
|
rid: TSubscriptionModel['rid'];
|
||||||
|
t: TSubscriptionModel['t'];
|
||||||
|
joined: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ActionsSection({ rid, t, joined }: IActionsSection): React.ReactElement {
|
||||||
|
const { navigate, pop } = useNavigation<TNavigation>();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission, createInviteLinksPermission] =
|
||||||
|
usePermissions(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room', 'create-invite-links'], rid);
|
||||||
|
|
||||||
|
const canAddUser =
|
||||||
|
(joined && addUserToJoinedRoomPermission) ||
|
||||||
|
(t === 'c' && addUserToAnyCRoomPermission) ||
|
||||||
|
(t === 'p' && addUserToAnyPRoomPermission) ||
|
||||||
|
false;
|
||||||
|
|
||||||
|
const canInviteUser = createInviteLinksPermission;
|
||||||
|
|
||||||
|
const handleOnPress = ({
|
||||||
|
route,
|
||||||
|
params
|
||||||
|
}: {
|
||||||
|
route: keyof ChatsStackParamList;
|
||||||
|
params: ChatsStackParamList[keyof ChatsStackParamList];
|
||||||
|
}) => {
|
||||||
|
navigate(route, params);
|
||||||
|
// @ts-ignore
|
||||||
|
logEvent(events[`RM_GO_${route.replace('View', '').toUpperCase()}`]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addUser = async () => {
|
||||||
|
try {
|
||||||
|
dispatch(setLoading(true));
|
||||||
|
await Services.addUsersToRoom(rid);
|
||||||
|
pop();
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
} finally {
|
||||||
|
dispatch(setLoading(false));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ paddingTop: canAddUser || canInviteUser ? 16 : 0, paddingBottom: canAddUser || canInviteUser ? 8 : 0 }}>
|
||||||
|
{['c', 'p'].includes(t) && canAddUser ? (
|
||||||
|
<>
|
||||||
|
<List.Separator />
|
||||||
|
<List.Item
|
||||||
|
title='Add_users'
|
||||||
|
onPress={() =>
|
||||||
|
handleOnPress({
|
||||||
|
route: 'SelectedUsersView',
|
||||||
|
params: {
|
||||||
|
title: i18n.t('Add_users'),
|
||||||
|
nextAction: addUser
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
testID='room-actions-add-user'
|
||||||
|
left={() => <List.Icon name='add' />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{['c', 'p'].includes(t) && canInviteUser ? (
|
||||||
|
<>
|
||||||
|
<List.Item
|
||||||
|
title='Invite_users'
|
||||||
|
onPress={() =>
|
||||||
|
handleOnPress({
|
||||||
|
route: 'InviteUsersView',
|
||||||
|
params: { rid }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
testID='room-actions-invite-user'
|
||||||
|
left={() => <List.Icon name='user-add' />}
|
||||||
|
showActionIndicator
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,258 @@
|
||||||
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
|
import { LISTENER } from '../../containers/Toast';
|
||||||
|
import { IUser, SubscriptionType, TSubscriptionModel, TUserModel } from '../../definitions';
|
||||||
|
import I18n from '../../i18n';
|
||||||
|
import { getRoomTitle, showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers';
|
||||||
|
import EventEmitter from '../../lib/methods/helpers/events';
|
||||||
|
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
||||||
|
import log from '../../lib/methods/helpers/log';
|
||||||
|
import appNavigation from '../../lib/navigation/appNavigation';
|
||||||
|
import { Services } from '../../lib/services';
|
||||||
|
import database from '../../lib/database';
|
||||||
|
import { RoomTypes } from '../../lib/methods';
|
||||||
|
|
||||||
|
export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
|
||||||
|
|
||||||
|
const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => {
|
||||||
|
if (isMasterDetail) {
|
||||||
|
appNavigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
|
appNavigation.popToTop();
|
||||||
|
}
|
||||||
|
goRoom({ item, isMasterDetail });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => {
|
||||||
|
const userRoleResult = roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
||||||
|
return userRoleResult?.roles.includes(role);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchRoomMembersRoles = async (roomType: TRoomType, rid: string, updateState: any): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const type = roomType;
|
||||||
|
const result = await Services.getRoomRoles(rid, type);
|
||||||
|
if (result?.success) {
|
||||||
|
updateState({ roomRoles: result.roles });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleMute = async (user: TUserModel, rid: string) => {
|
||||||
|
try {
|
||||||
|
await Services.toggleMuteUserInRoom(rid, user?.username, !user?.muted);
|
||||||
|
EventEmitter.emit(LISTENER, {
|
||||||
|
message: I18n.t('User_has_been_key', { key: user?.muted ? I18n.t('unmuted') : I18n.t('muted') })
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleModerator = async (
|
||||||
|
selectedUser: TUserModel,
|
||||||
|
isModerator: boolean,
|
||||||
|
room: TSubscriptionModel,
|
||||||
|
username: string,
|
||||||
|
callback: () => Promise<void>
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await Services.toggleRoomModerator({
|
||||||
|
roomId: room.rid,
|
||||||
|
t: room.t,
|
||||||
|
userId: selectedUser._id,
|
||||||
|
isModerator
|
||||||
|
});
|
||||||
|
const message = isModerator
|
||||||
|
? 'User__username__is_now_a_moderator_of__room_name_'
|
||||||
|
: 'User__username__removed_from__room_name__moderators';
|
||||||
|
EventEmitter.emit(LISTENER, {
|
||||||
|
message: I18n.t(message, {
|
||||||
|
username,
|
||||||
|
room_name: getRoomTitle(room)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
callback();
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const navToDirectMessage = async (item: IUser, isMasterDetail: boolean): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const db = database.active;
|
||||||
|
const subsCollection = db.get('subscriptions');
|
||||||
|
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
||||||
|
if (query.length) {
|
||||||
|
const [room] = query;
|
||||||
|
handleGoRoom(room, isMasterDetail);
|
||||||
|
} else {
|
||||||
|
const result = await Services.createDirectMessage(item.username);
|
||||||
|
if (result.success) {
|
||||||
|
handleGoRoom({ rid: result.room?._id as string, name: item.username, t: SubscriptionType.DIRECT }, isMasterDetail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFromTeam = async (
|
||||||
|
selectedUser: IUser,
|
||||||
|
updateState: Function,
|
||||||
|
room: TSubscriptionModel,
|
||||||
|
members: TUserModel[],
|
||||||
|
selected?: any
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const userId = selectedUser._id;
|
||||||
|
const result = await Services.removeTeamMember({
|
||||||
|
teamId: room.teamId,
|
||||||
|
userId,
|
||||||
|
...(selected && { rooms: selected })
|
||||||
|
});
|
||||||
|
if (result.success) {
|
||||||
|
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
||||||
|
EventEmitter.emit(LISTENER, { message });
|
||||||
|
const newMembers = members.filter(member => member._id !== userId);
|
||||||
|
updateState({
|
||||||
|
members: newMembers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
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')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleRemoveFromTeam = async (
|
||||||
|
selectedUser: TUserModel,
|
||||||
|
updateState: Function,
|
||||||
|
room: TSubscriptionModel,
|
||||||
|
members: TUserModel[]
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const result = await Services.teamListRoomsOfUser({ teamId: room.teamId as string, userId: selectedUser._id });
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
if (result.rooms?.length) {
|
||||||
|
const teamChannels = result.rooms.map((r: any) => ({
|
||||||
|
rid: r._id,
|
||||||
|
name: r.name,
|
||||||
|
teamId: r.teamId,
|
||||||
|
alert: r.isLastOwner
|
||||||
|
}));
|
||||||
|
appNavigation.navigate('SelectListView', {
|
||||||
|
title: 'Remove_Member',
|
||||||
|
infoText: 'Remove_User_Team_Channels',
|
||||||
|
data: teamChannels,
|
||||||
|
nextAction: (selected: any) => removeFromTeam(selectedUser, updateState, room, members, 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: () => removeFromTeam(selectedUser, updateState, room, members)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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: () => removeFromTeam(selectedUser, updateState, room, members)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleLeader = async (
|
||||||
|
selectedUser: TUserModel,
|
||||||
|
isLeader: boolean,
|
||||||
|
room: TSubscriptionModel,
|
||||||
|
username: string,
|
||||||
|
callback: () => Promise<void>
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await Services.toggleRoomLeader({
|
||||||
|
roomId: room.rid,
|
||||||
|
t: room.t,
|
||||||
|
userId: selectedUser._id,
|
||||||
|
isLeader
|
||||||
|
});
|
||||||
|
const message = isLeader
|
||||||
|
? 'User__username__is_now_a_leader_of__room_name_'
|
||||||
|
: 'User__username__removed_from__room_name__leaders';
|
||||||
|
EventEmitter.emit(LISTENER, {
|
||||||
|
message: I18n.t(message, {
|
||||||
|
username,
|
||||||
|
room_name: getRoomTitle(room)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
callback();
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleRemoveUserFromRoom = async (
|
||||||
|
selectedUser: TUserModel,
|
||||||
|
room: TSubscriptionModel,
|
||||||
|
callback: Function
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const userId = selectedUser._id;
|
||||||
|
await Services.removeUserFromRoom({ roomId: room.rid, t: room.t as RoomTypes, userId });
|
||||||
|
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
||||||
|
EventEmitter.emit(LISTENER, { message });
|
||||||
|
callback();
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleIgnore = async (selectedUser: TUserModel, ignore: boolean, rid: string) => {
|
||||||
|
try {
|
||||||
|
await Services.ignoreUser({
|
||||||
|
rid,
|
||||||
|
userId: selectedUser._id,
|
||||||
|
ignore
|
||||||
|
});
|
||||||
|
const message = I18n.t(ignore ? 'User_has_been_ignored' : 'User_has_been_unignored');
|
||||||
|
EventEmitter.emit(LISTENER, { message });
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleOwner = async (
|
||||||
|
selectedUser: TUserModel,
|
||||||
|
isOwner: boolean,
|
||||||
|
username: string,
|
||||||
|
room: TSubscriptionModel,
|
||||||
|
callback: Function
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await Services.toggleRoomOwner({
|
||||||
|
roomId: room.rid,
|
||||||
|
t: room.t,
|
||||||
|
userId: selectedUser._id,
|
||||||
|
isOwner
|
||||||
|
});
|
||||||
|
const message = isOwner ? 'User__username__is_now_a_owner_of__room_name_' : 'User__username__removed_from__room_name__owners';
|
||||||
|
EventEmitter.emit(LISTENER, {
|
||||||
|
message: I18n.t(message, {
|
||||||
|
username,
|
||||||
|
room_name: getRoomTitle(room)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
|
@ -1,123 +1,103 @@
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { NavigationProp, RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||||
import React from 'react';
|
import React, { useEffect, useReducer } from 'react';
|
||||||
import { FlatList } from 'react-native';
|
import { FlatList, Text, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Observable, Subscription } from 'rxjs';
|
|
||||||
|
|
||||||
import { themes } from '../../lib/constants';
|
import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
|
||||||
import { TActionSheetOptions, TActionSheetOptionsItem, withActionSheet } from '../../containers/ActionSheet';
|
|
||||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
|
import { CustomIcon } from '../../containers/CustomIcon';
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import * as List from '../../containers/List';
|
import * as List from '../../containers/List';
|
||||||
|
import { RadioButton } from '../../containers/RadioButton';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import SearchBox from '../../containers/SearchBox';
|
import SearchBox from '../../containers/SearchBox';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { LISTENER } from '../../containers/Toast';
|
|
||||||
import { IApplicationState, IBaseScreen, IUser, SubscriptionType, TSubscriptionModel, TUserModel } from '../../definitions';
|
|
||||||
import I18n from '../../i18n';
|
|
||||||
import database from '../../lib/database';
|
|
||||||
import { CustomIcon } from '../../containers/CustomIcon';
|
|
||||||
import UserItem from '../../containers/UserItem';
|
import UserItem from '../../containers/UserItem';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { TSubscriptionModel, TUserModel } from '../../definitions';
|
||||||
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
|
import I18n from '../../i18n';
|
||||||
import { TSupportedThemes, withTheme } from '../../theme';
|
import { useAppSelector, usePermissions } from '../../lib/hooks';
|
||||||
import EventEmitter from '../../lib/methods/helpers/events';
|
import { getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
|
||||||
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
|
||||||
import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers/info';
|
|
||||||
import log from '../../lib/methods/helpers/log';
|
import log from '../../lib/methods/helpers/log';
|
||||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||||
import { TSupportedPermissions } from '../../reducers/permissions';
|
|
||||||
import { RoomTypes } from '../../lib/methods';
|
|
||||||
import { compareServerVersion, debounce, getRoomTitle, hasPermission, isGroupChat } from '../../lib/methods/helpers';
|
|
||||||
import styles from './styles';
|
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
import { TSupportedPermissions } from '../../reducers/permissions';
|
||||||
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import ActionsSection from './components/ActionsSection';
|
||||||
|
import {
|
||||||
|
fetchRole,
|
||||||
|
fetchRoomMembersRoles,
|
||||||
|
handleIgnore,
|
||||||
|
handleLeader,
|
||||||
|
handleModerator,
|
||||||
|
handleMute,
|
||||||
|
handleOwner,
|
||||||
|
handleRemoveFromTeam,
|
||||||
|
handleRemoveUserFromRoom,
|
||||||
|
navToDirectMessage,
|
||||||
|
TRoomType
|
||||||
|
} from './helpers';
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
const PAGE_SIZE = 25;
|
const PAGE_SIZE = 25;
|
||||||
|
|
||||||
interface IRoomMembersViewProps extends IBaseScreen<ModalStackParamList, 'RoomMembersView'> {
|
|
||||||
rid: string;
|
|
||||||
members: string[];
|
|
||||||
baseUrl: string;
|
|
||||||
room: TSubscriptionModel;
|
|
||||||
user: {
|
|
||||||
id: string;
|
|
||||||
token: string;
|
|
||||||
roles: string[];
|
|
||||||
};
|
|
||||||
showActionSheet: (params: TActionSheetOptions) => {};
|
|
||||||
theme: TSupportedThemes;
|
|
||||||
isMasterDetail: boolean;
|
|
||||||
useRealName: boolean;
|
|
||||||
muteUserPermission: string[];
|
|
||||||
setLeaderPermission: string[];
|
|
||||||
setOwnerPermission: string[];
|
|
||||||
setModeratorPermission: string[];
|
|
||||||
removeUserPermission: string[];
|
|
||||||
editTeamMemberPermission: string[];
|
|
||||||
viewAllTeamChannelsPermission: string[];
|
|
||||||
viewAllTeamsPermission: string[];
|
|
||||||
serverVersion: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRoomMembersViewState {
|
interface IRoomMembersViewState {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
allUsers: boolean;
|
allUsers: boolean;
|
||||||
filtering: string;
|
filtering: string;
|
||||||
rid: string;
|
|
||||||
members: TUserModel[];
|
members: TUserModel[];
|
||||||
membersFiltered: TUserModel[];
|
|
||||||
room: TSubscriptionModel;
|
room: TSubscriptionModel;
|
||||||
end: boolean;
|
end: boolean;
|
||||||
|
roomRoles: any;
|
||||||
|
filter: string;
|
||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMembersViewState> {
|
const RightIcon = ({ check, label }: { check: boolean; label: string }) => {
|
||||||
private mounted: boolean;
|
const { colors } = useTheme();
|
||||||
private permissions: { [key in TSupportedPermissions]?: boolean };
|
return (
|
||||||
private roomObservable!: Observable<TSubscriptionModel>;
|
<CustomIcon
|
||||||
private subscription!: Subscription;
|
testID={check ? `action-sheet-set-${label}-checked` : `action-sheet-set-${label}-unchecked`}
|
||||||
private roomRoles: any;
|
name={check ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||||
|
size={20}
|
||||||
|
color={check ? colors.tintActive : colors.auxiliaryTintColor}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
constructor(props: IRoomMembersViewProps) {
|
const RoomMembersView = (): React.ReactElement => {
|
||||||
super(props);
|
const { showActionSheet } = useActionSheet();
|
||||||
this.mounted = false;
|
const { colors } = useTheme();
|
||||||
this.permissions = {};
|
|
||||||
const rid = props.route.params?.rid;
|
const { params } = useRoute<RouteProp<ModalStackParamList, 'RoomMembersView'>>();
|
||||||
const room = props.route.params?.room;
|
const navigation = useNavigation<NavigationProp<ModalStackParamList, 'RoomMembersView'>>();
|
||||||
this.state = {
|
|
||||||
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
|
|
||||||
|
const useRealName = useAppSelector(state => state.settings.UI_Use_Real_Name);
|
||||||
|
const user = useAppSelector(state => getUserSelector(state));
|
||||||
|
|
||||||
|
const [state, updateState] = useReducer(
|
||||||
|
(state: IRoomMembersViewState, newState: Partial<IRoomMembersViewState>) => ({ ...state, ...newState }),
|
||||||
|
{
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
allUsers: false,
|
allUsers: false,
|
||||||
filtering: '',
|
filtering: '',
|
||||||
rid,
|
|
||||||
members: [],
|
members: [],
|
||||||
membersFiltered: [],
|
room: params.room || ({} as TSubscriptionModel),
|
||||||
room: room || ({} as TSubscriptionModel),
|
|
||||||
end: false,
|
end: false,
|
||||||
|
roomRoles: null,
|
||||||
|
filter: '',
|
||||||
page: 0
|
page: 0
|
||||||
};
|
|
||||||
if (room && room.observe) {
|
|
||||||
this.roomObservable = room.observe();
|
|
||||||
this.subscription = this.roomObservable.subscribe(changes => {
|
|
||||||
if (this.mounted) {
|
|
||||||
this.setState({ room: changes });
|
|
||||||
} else {
|
|
||||||
this.setState({ room: changes });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.setHeader();
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
async componentDidMount() {
|
const teamPermissions: TSupportedPermissions[] = state.room.teamMain
|
||||||
const { room } = this.state;
|
? ['edit-team-member', 'view-all-team-channels', 'view-all-teams']
|
||||||
this.mounted = true;
|
: [];
|
||||||
this.fetchMembers();
|
|
||||||
|
|
||||||
if (isGroupChat(room)) {
|
const [
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
muteUserPermission,
|
muteUserPermission,
|
||||||
setLeaderPermission,
|
setLeaderPermission,
|
||||||
setOwnerPermission,
|
setOwnerPermission,
|
||||||
|
@ -126,186 +106,97 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
||||||
editTeamMemberPermission,
|
editTeamMemberPermission,
|
||||||
viewAllTeamChannelsPermission,
|
viewAllTeamChannelsPermission,
|
||||||
viewAllTeamsPermission
|
viewAllTeamsPermission
|
||||||
} = this.props;
|
] = usePermissions(['mute-user', 'set-leader', 'set-owner', 'set-moderator', 'remove-user', ...teamPermissions], params.rid);
|
||||||
|
|
||||||
const result = await hasPermission(
|
useEffect(() => {
|
||||||
[
|
const subscription = params?.room?.observe && params.room.observe().subscribe(changes => updateState({ room: changes }));
|
||||||
|
setHeader(true);
|
||||||
|
fetchMembers(true);
|
||||||
|
return () => subscription?.unsubscribe();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchRoles = () => {
|
||||||
|
if (isGroupChat(state.room)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
muteUserPermission ||
|
||||||
|
setLeaderPermission ||
|
||||||
|
setOwnerPermission ||
|
||||||
|
setModeratorPermission ||
|
||||||
|
removeUserPermission ||
|
||||||
|
editTeamMemberPermission ||
|
||||||
|
viewAllTeamChannelsPermission ||
|
||||||
|
viewAllTeamsPermission
|
||||||
|
) {
|
||||||
|
fetchRoomMembersRoles(state.room.t as any, state.room.rid, updateState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchRoles();
|
||||||
|
}, [
|
||||||
muteUserPermission,
|
muteUserPermission,
|
||||||
setLeaderPermission,
|
setLeaderPermission,
|
||||||
setOwnerPermission,
|
setOwnerPermission,
|
||||||
setModeratorPermission,
|
setModeratorPermission,
|
||||||
removeUserPermission,
|
removeUserPermission,
|
||||||
...(room.teamMain ? [editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission] : [])
|
editTeamMemberPermission,
|
||||||
],
|
viewAllTeamChannelsPermission,
|
||||||
room.rid
|
viewAllTeamsPermission
|
||||||
);
|
]);
|
||||||
|
|
||||||
this.permissions = {
|
const toggleStatus = (status: boolean) => {
|
||||||
'mute-user': result[0],
|
try {
|
||||||
'set-leader': result[1],
|
updateState({ members: [], allUsers: status, end: false });
|
||||||
'set-owner': result[2],
|
fetchMembers(status);
|
||||||
'set-moderator': result[3],
|
setHeader(status);
|
||||||
'remove-user': result[4],
|
} catch (e) {
|
||||||
...(room.teamMain
|
log(e);
|
||||||
? {
|
|
||||||
'edit-team-member': result[5],
|
|
||||||
'view-all-team-channels': result[6],
|
|
||||||
'view-all-teams': result[7]
|
|
||||||
}
|
}
|
||||||
: {})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
|
const setHeader = (allUsers: boolean) => {
|
||||||
if (hasSinglePermission) {
|
|
||||||
this.fetchRoomMembersRoles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.subscription && this.subscription.unsubscribe) {
|
|
||||||
this.subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeader = () => {
|
|
||||||
const { allUsers } = this.state;
|
|
||||||
const { navigation } = this.props;
|
|
||||||
const toggleText = allUsers ? I18n.t('Online') : I18n.t('All');
|
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
title: I18n.t('Members'),
|
title: I18n.t('Members'),
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
<HeaderButton.Container>
|
<HeaderButton.Container>
|
||||||
<HeaderButton.Item title={toggleText} onPress={this.toggleStatus} testID='room-members-view-toggle-status' />
|
<HeaderButton.Item
|
||||||
|
iconName='filter'
|
||||||
|
onPress={() =>
|
||||||
|
showActionSheet({
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: I18n.t('Online'),
|
||||||
|
onPress: () => toggleStatus(true),
|
||||||
|
right: () => <RadioButton check={allUsers} />,
|
||||||
|
testID: 'room-members-view-toggle-status-online'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: I18n.t('All'),
|
||||||
|
onPress: () => toggleStatus(false),
|
||||||
|
right: () => <RadioButton check={!allUsers} />,
|
||||||
|
testID: 'room-members-view-toggle-status-all'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
testID='room-members-view-filter'
|
||||||
|
/>
|
||||||
</HeaderButton.Container>
|
</HeaderButton.Container>
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
get isServerVersionLowerThan3_16() {
|
const getUserDisplayName = (user: TUserModel) => (useRealName ? user.name : user.username) || user.username;
|
||||||
const { serverVersion } = this.props;
|
|
||||||
return compareServerVersion(serverVersion, 'lowerThan', '3.16.0');
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearchChangeText = debounce((text: string) => {
|
const onPressUser = (selectedUser: TUserModel) => {
|
||||||
const { members } = this.state;
|
const { room, roomRoles, members } = state;
|
||||||
text = text.trim();
|
|
||||||
if (this.isServerVersionLowerThan3_16) {
|
|
||||||
let membersFiltered: TUserModel[] = [];
|
|
||||||
|
|
||||||
if (members && members.length > 0 && text) {
|
|
||||||
membersFiltered = members.filter(
|
|
||||||
m => m.username.toLowerCase().match(text.toLowerCase()) || m.name?.toLowerCase().match(text.toLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.setState({ filtering: text, membersFiltered });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ filtering: text, page: 0, members: [], end: false }, () => {
|
|
||||||
this.fetchMembers();
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
navToDirectMessage = async (item: IUser) => {
|
|
||||||
try {
|
|
||||||
const db = database.active;
|
|
||||||
const subsCollection = db.get('subscriptions');
|
|
||||||
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
|
||||||
if (query.length) {
|
|
||||||
const [room] = query;
|
|
||||||
this.goRoom(room);
|
|
||||||
} else {
|
|
||||||
const result = await Services.createDirectMessage(item.username);
|
|
||||||
if (result.success) {
|
|
||||||
this.goRoom({ rid: result.room?._id as string, name: item.username, t: SubscriptionType.DIRECT });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleRemoveFromTeam = async (selectedUser: TUserModel) => {
|
|
||||||
try {
|
|
||||||
const { navigation } = this.props;
|
|
||||||
const { room } = this.state;
|
|
||||||
|
|
||||||
const result = await Services.teamListRoomsOfUser({ teamId: room.teamId as string, userId: selectedUser._id });
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
if (result.rooms?.length) {
|
|
||||||
const teamChannels = result.rooms.map((r: any) => ({
|
|
||||||
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: any) => 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: IUser, selected?: any) => {
|
|
||||||
try {
|
|
||||||
const { members, membersFiltered, room } = this.state;
|
|
||||||
const { navigation } = this.props;
|
|
||||||
|
|
||||||
const userId = selectedUser._id;
|
|
||||||
const result = await Services.removeTeamMember({
|
|
||||||
teamId: room.teamId,
|
|
||||||
userId,
|
|
||||||
...(selected && { rooms: selected })
|
|
||||||
});
|
|
||||||
if (result.success) {
|
|
||||||
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
|
||||||
EventEmitter.emit(LISTENER, { message });
|
|
||||||
const newMembers = members.filter(member => member._id !== userId);
|
|
||||||
const newMembersFiltered = this.isServerVersionLowerThan3_16
|
|
||||||
? membersFiltered.filter(member => member._id !== userId)
|
|
||||||
: [];
|
|
||||||
this.setState({
|
|
||||||
members: newMembers,
|
|
||||||
membersFiltered: newMembersFiltered
|
|
||||||
});
|
|
||||||
// @ts-ignore - This is just to force a reload
|
|
||||||
navigation.navigate('RoomMembersView');
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
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: TUserModel) => {
|
|
||||||
const { room } = this.state;
|
|
||||||
const { showActionSheet, user, theme } = this.props;
|
|
||||||
|
|
||||||
const options: TActionSheetOptionsItem[] = [
|
const options: TActionSheetOptionsItem[] = [
|
||||||
{
|
{
|
||||||
icon: 'message',
|
icon: 'message',
|
||||||
title: I18n.t('Direct_message'),
|
title: I18n.t('Direct_message'),
|
||||||
onPress: () => this.navToDirectMessage(selectedUser)
|
onPress: () => navToDirectMessage(selectedUser, isMasterDetail)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -316,12 +207,12 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
||||||
options.push({
|
options.push({
|
||||||
icon: 'ignore',
|
icon: 'ignore',
|
||||||
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
||||||
onPress: () => this.handleIgnore(selectedUser, !isIgnored),
|
onPress: () => handleIgnore(selectedUser, !isIgnored, room.rid),
|
||||||
testID: 'action-sheet-ignore-user'
|
testID: 'action-sheet-ignore-user'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.permissions['mute-user']) {
|
if (muteUserPermission) {
|
||||||
const { muted = [] } = room;
|
const { muted = [] } = room;
|
||||||
const userIsMuted = muted.find?.(m => m === selectedUser.username);
|
const userIsMuted = muted.find?.(m => m === selectedUser.username);
|
||||||
selectedUser.muted = !!userIsMuted;
|
selectedUser.muted = !!userIsMuted;
|
||||||
|
@ -334,7 +225,7 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
||||||
roomName: getRoomTitle(room)
|
roomName: getRoomTitle(room)
|
||||||
}),
|
}),
|
||||||
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
||||||
onPress: () => this.handleMute(selectedUser)
|
onPress: () => handleMute(selectedUser, room.rid)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
testID: 'action-sheet-mute-user'
|
testID: 'action-sheet-mute-user'
|
||||||
|
@ -342,78 +233,63 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
||||||
}
|
}
|
||||||
|
|
||||||
// Owner
|
// Owner
|
||||||
if (this.permissions['set-owner']) {
|
if (setOwnerPermission) {
|
||||||
const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
const isOwner = fetchRole('owner', selectedUser, roomRoles);
|
||||||
const isOwner = userRoleResult?.roles.includes('owner');
|
|
||||||
options.push({
|
options.push({
|
||||||
icon: 'shield-check',
|
icon: 'shield-check',
|
||||||
title: I18n.t('Owner'),
|
title: I18n.t('Owner'),
|
||||||
onPress: () => this.handleOwner(selectedUser, !isOwner),
|
onPress: () =>
|
||||||
right: () => (
|
handleOwner(selectedUser, !isOwner, getUserDisplayName(selectedUser), room, () =>
|
||||||
<CustomIcon
|
fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
|
||||||
testID={isOwner ? 'action-sheet-set-owner-checked' : 'action-sheet-set-owner-unchecked'}
|
|
||||||
name={isOwner ? 'checkbox-checked' : 'checkbox-unchecked'}
|
|
||||||
size={20}
|
|
||||||
color={isOwner ? themes[theme].tintActive : themes[theme].auxiliaryTintColor}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
|
right: () => <RightIcon check={isOwner} label='owner' />,
|
||||||
testID: 'action-sheet-set-owner'
|
testID: 'action-sheet-set-owner'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leader
|
// Leader
|
||||||
if (this.permissions['set-leader']) {
|
if (setLeaderPermission) {
|
||||||
const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
const isLeader = fetchRole('leader', selectedUser, roomRoles);
|
||||||
const isLeader = userRoleResult?.roles.includes('leader');
|
|
||||||
options.push({
|
options.push({
|
||||||
icon: 'shield-alt',
|
icon: 'shield-alt',
|
||||||
title: I18n.t('Leader'),
|
title: I18n.t('Leader'),
|
||||||
onPress: () => this.handleLeader(selectedUser, !isLeader),
|
onPress: () =>
|
||||||
right: () => (
|
handleLeader(selectedUser, !isLeader, room, getUserDisplayName(selectedUser), () =>
|
||||||
<CustomIcon
|
fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
|
||||||
testID={isLeader ? 'action-sheet-set-leader-checked' : 'action-sheet-set-leader-unchecked'}
|
|
||||||
name={isLeader ? 'checkbox-checked' : 'checkbox-unchecked'}
|
|
||||||
size={20}
|
|
||||||
color={isLeader ? themes[theme].tintActive : themes[theme].auxiliaryTintColor}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
|
right: () => <RightIcon check={isLeader} label='leader' />,
|
||||||
testID: 'action-sheet-set-leader'
|
testID: 'action-sheet-set-leader'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moderator
|
// Moderator
|
||||||
if (this.permissions['set-moderator']) {
|
if (setModeratorPermission) {
|
||||||
const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
const isModerator = fetchRole('moderator', selectedUser, roomRoles);
|
||||||
const isModerator = userRoleResult?.roles.includes('moderator');
|
|
||||||
options.push({
|
options.push({
|
||||||
icon: 'shield',
|
icon: 'shield',
|
||||||
title: I18n.t('Moderator'),
|
title: I18n.t('Moderator'),
|
||||||
onPress: () => this.handleModerator(selectedUser, !isModerator),
|
onPress: () =>
|
||||||
right: () => (
|
handleModerator(selectedUser, !isModerator, room, getUserDisplayName(selectedUser), () =>
|
||||||
<CustomIcon
|
fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
|
||||||
testID={isModerator ? 'action-sheet-set-moderator-checked' : 'action-sheet-set-moderator-unchecked'}
|
|
||||||
name={isModerator ? 'checkbox-checked' : 'checkbox-unchecked'}
|
|
||||||
size={20}
|
|
||||||
color={isModerator ? themes[theme].tintActive : themes[theme].auxiliaryTintColor}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
|
right: () => <RightIcon check={isModerator} label='moderator' />,
|
||||||
testID: 'action-sheet-set-moderator'
|
testID: 'action-sheet-set-moderator'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from team
|
// Remove from team
|
||||||
if (this.permissions['edit-team-member']) {
|
if (editTeamMemberPermission) {
|
||||||
options.push({
|
options.push({
|
||||||
icon: 'logout',
|
icon: 'logout',
|
||||||
danger: true,
|
danger: true,
|
||||||
title: I18n.t('Remove_from_Team'),
|
title: I18n.t('Remove_from_Team'),
|
||||||
onPress: () => this.handleRemoveFromTeam(selectedUser),
|
onPress: () => handleRemoveFromTeam(selectedUser, updateState, room, members),
|
||||||
testID: 'action-sheet-remove-from-team'
|
testID: 'action-sheet-remove-from-team'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from room
|
// Remove from room
|
||||||
if (this.permissions['remove-user'] && !room.teamMain) {
|
if (removeUserPermission && !room.teamMain) {
|
||||||
options.push({
|
options.push({
|
||||||
icon: 'logout',
|
icon: 'logout',
|
||||||
title: I18n.t('Remove_from_room'),
|
title: I18n.t('Remove_from_room'),
|
||||||
|
@ -422,7 +298,13 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
message: I18n.t('The_user_will_be_removed_from_s', { s: getRoomTitle(room) }),
|
message: I18n.t('The_user_will_be_removed_from_s', { s: getRoomTitle(room) }),
|
||||||
confirmationText: I18n.t('Yes_remove_user'),
|
confirmationText: I18n.t('Yes_remove_user'),
|
||||||
onPress: () => this.handleRemoveUserFromRoom(selectedUser)
|
onPress: () => {
|
||||||
|
handleRemoveUserFromRoom(selectedUser, room, () =>
|
||||||
|
updateState({
|
||||||
|
members: members.filter(member => member._id !== selectedUser._id)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
testID: 'action-sheet-remove-from-room'
|
testID: 'action-sheet-remove-from-room'
|
||||||
|
@ -435,256 +317,83 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleStatus = () => {
|
const fetchMembers = async (status: boolean) => {
|
||||||
try {
|
const { members, isLoading, end, room, filter, page } = state;
|
||||||
const { allUsers } = this.state;
|
|
||||||
this.setState({ members: [], allUsers: !allUsers, end: false, page: 0 }, () => {
|
|
||||||
this.fetchMembers();
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchRoomMembersRoles = async () => {
|
|
||||||
try {
|
|
||||||
const { room } = this.state;
|
|
||||||
const type = room.t as SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
|
|
||||||
const result = await Services.getRoomRoles(room.rid, type);
|
|
||||||
if (result?.success) {
|
|
||||||
this.roomRoles = result.roles;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchMembers = async () => {
|
|
||||||
const { rid, members, isLoading, allUsers, end, room, filtering, page } = this.state;
|
|
||||||
const { t } = room;
|
const { t } = room;
|
||||||
|
|
||||||
if (isLoading || end) {
|
if (isLoading || end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ isLoading: true });
|
updateState({ isLoading: true });
|
||||||
try {
|
try {
|
||||||
const membersResult = await Services.getRoomMembers({
|
const membersResult = await Services.getRoomMembers({
|
||||||
rid,
|
rid: room.rid,
|
||||||
roomType: t,
|
roomType: t,
|
||||||
type: allUsers ? 'all' : 'online',
|
type: !status ? 'all' : 'online',
|
||||||
filter: filtering,
|
filter,
|
||||||
skip: PAGE_SIZE * page,
|
skip: PAGE_SIZE * page,
|
||||||
limit: PAGE_SIZE,
|
limit: PAGE_SIZE,
|
||||||
allUsers
|
allUsers: !status
|
||||||
});
|
});
|
||||||
const end = membersResult?.length < PAGE_SIZE;
|
const end = membersResult?.length < PAGE_SIZE;
|
||||||
const membersResultFiltered = membersResult?.filter((member: TUserModel) => !members.some(m => m._id === member._id));
|
const membersResultFiltered = membersResult?.filter((member: TUserModel) => !members.some(m => m._id === member._id));
|
||||||
this.setState({
|
updateState({
|
||||||
members: members.concat(membersResultFiltered || []),
|
members: [...members, ...membersResultFiltered],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
end,
|
end,
|
||||||
page: page + 1
|
page: page + 1
|
||||||
});
|
});
|
||||||
this.setHeader();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
this.setState({ isLoading: false });
|
updateState({ isLoading: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
goRoom = (item: TGoRoomItem) => {
|
const filteredMembers =
|
||||||
const { navigation, isMasterDetail } = this.props;
|
state.members && state.members.length > 0 && state.filter
|
||||||
if (isMasterDetail) {
|
? state.members.filter(
|
||||||
// @ts-ignore
|
m =>
|
||||||
navigation.navigate('DrawerNavigator');
|
m.username.toLowerCase().match(state.filter.toLowerCase()) || m.name?.toLowerCase().match(state.filter.toLowerCase())
|
||||||
} else {
|
)
|
||||||
navigation.popToTop();
|
: null;
|
||||||
}
|
|
||||||
goRoom({ item, isMasterDetail });
|
|
||||||
};
|
|
||||||
|
|
||||||
getUserDisplayName = (user: TUserModel) => {
|
|
||||||
const { useRealName } = this.props;
|
|
||||||
return (useRealName ? user.name : user.username) || user.username;
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMute = async (user: TUserModel) => {
|
|
||||||
const { rid } = this.state;
|
|
||||||
try {
|
|
||||||
await Services.toggleMuteUserInRoom(rid, user?.username, !user?.muted);
|
|
||||||
EventEmitter.emit(LISTENER, {
|
|
||||||
message: I18n.t('User_has_been_key', { key: user?.muted ? I18n.t('unmuted') : I18n.t('muted') })
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleOwner = async (selectedUser: TUserModel, isOwner: boolean) => {
|
|
||||||
try {
|
|
||||||
const { room } = this.state;
|
|
||||||
await Services.toggleRoomOwner({
|
|
||||||
roomId: room.rid,
|
|
||||||
t: room.t,
|
|
||||||
userId: selectedUser._id,
|
|
||||||
isOwner
|
|
||||||
});
|
|
||||||
const message = isOwner
|
|
||||||
? 'User__username__is_now_a_owner_of__room_name_'
|
|
||||||
: 'User__username__removed_from__room_name__owners';
|
|
||||||
EventEmitter.emit(LISTENER, {
|
|
||||||
message: I18n.t(message, {
|
|
||||||
username: this.getUserDisplayName(selectedUser),
|
|
||||||
room_name: getRoomTitle(room)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
this.fetchRoomMembersRoles();
|
|
||||||
};
|
|
||||||
|
|
||||||
handleLeader = async (selectedUser: TUserModel, isLeader: boolean) => {
|
|
||||||
try {
|
|
||||||
const { room } = this.state;
|
|
||||||
await Services.toggleRoomLeader({
|
|
||||||
roomId: room.rid,
|
|
||||||
t: room.t,
|
|
||||||
userId: selectedUser._id,
|
|
||||||
isLeader
|
|
||||||
});
|
|
||||||
const message = isLeader
|
|
||||||
? 'User__username__is_now_a_leader_of__room_name_'
|
|
||||||
: 'User__username__removed_from__room_name__leaders';
|
|
||||||
EventEmitter.emit(LISTENER, {
|
|
||||||
message: I18n.t(message, {
|
|
||||||
username: this.getUserDisplayName(selectedUser),
|
|
||||||
room_name: getRoomTitle(room)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
this.fetchRoomMembersRoles();
|
|
||||||
};
|
|
||||||
|
|
||||||
handleModerator = async (selectedUser: TUserModel, isModerator: boolean) => {
|
|
||||||
try {
|
|
||||||
const { room } = this.state;
|
|
||||||
await Services.toggleRoomModerator({
|
|
||||||
roomId: room.rid,
|
|
||||||
t: room.t,
|
|
||||||
userId: selectedUser._id,
|
|
||||||
isModerator
|
|
||||||
});
|
|
||||||
const message = isModerator
|
|
||||||
? 'User__username__is_now_a_moderator_of__room_name_'
|
|
||||||
: 'User__username__removed_from__room_name__moderators';
|
|
||||||
EventEmitter.emit(LISTENER, {
|
|
||||||
message: I18n.t(message, {
|
|
||||||
username: this.getUserDisplayName(selectedUser),
|
|
||||||
room_name: getRoomTitle(room)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
this.fetchRoomMembersRoles();
|
|
||||||
};
|
|
||||||
|
|
||||||
handleIgnore = async (selectedUser: TUserModel, ignore: boolean) => {
|
|
||||||
try {
|
|
||||||
const { room } = this.state;
|
|
||||||
await Services.ignoreUser({
|
|
||||||
rid: room.rid,
|
|
||||||
userId: selectedUser._id,
|
|
||||||
ignore
|
|
||||||
});
|
|
||||||
const message = I18n.t(ignore ? 'User_has_been_ignored' : 'User_has_been_unignored');
|
|
||||||
EventEmitter.emit(LISTENER, { message });
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleRemoveUserFromRoom = async (selectedUser: TUserModel) => {
|
|
||||||
try {
|
|
||||||
const { room, members, membersFiltered } = this.state;
|
|
||||||
const userId = selectedUser._id;
|
|
||||||
// TODO: interface SubscriptionType on IRoom is wrong
|
|
||||||
await Services.removeUserFromRoom({ roomId: room.rid, t: room.t as RoomTypes, userId });
|
|
||||||
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
|
||||||
EventEmitter.emit(LISTENER, { message });
|
|
||||||
this.setState({
|
|
||||||
members: members.filter(member => member._id !== userId),
|
|
||||||
membersFiltered: this.isServerVersionLowerThan3_16 ? membersFiltered.filter(member => member._id !== userId) : []
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSearchBar = () => <SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='room-members-view-search' />;
|
|
||||||
|
|
||||||
renderItem = ({ item }: { item: TUserModel }) => {
|
|
||||||
const { theme } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<UserItem
|
|
||||||
name={item.name as string}
|
|
||||||
username={item.username}
|
|
||||||
onPress={() => this.onPressUser(item)}
|
|
||||||
testID={`room-members-view-item-${item.username}`}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { filtering, members, membersFiltered, isLoading } = this.state;
|
|
||||||
const { theme } = this.props;
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView testID='room-members-view'>
|
<SafeAreaView testID='room-members-view'>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<FlatList
|
<FlatList
|
||||||
data={!!filtering && this.isServerVersionLowerThan3_16 ? membersFiltered : members}
|
data={filteredMembers || state.members}
|
||||||
renderItem={this.renderItem}
|
renderItem={({ item }) => (
|
||||||
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
|
<View style={{ backgroundColor: colors.backgroundColor }}>
|
||||||
|
<UserItem
|
||||||
|
name={item.name as string}
|
||||||
|
username={item.username}
|
||||||
|
onPress={() => onPressUser(item)}
|
||||||
|
testID={`room-members-view-item-${item.username}`}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
style={styles.list}
|
||||||
keyExtractor={item => item._id}
|
keyExtractor={item => item._id}
|
||||||
ItemSeparatorComponent={List.Separator}
|
ItemSeparatorComponent={List.Separator}
|
||||||
ListHeaderComponent={this.renderSearchBar}
|
ListHeaderComponent={
|
||||||
ListFooterComponent={() => {
|
<>
|
||||||
if (isLoading) {
|
<ActionsSection joined={params.joined as boolean} rid={state.room.rid} t={state.room.t} />
|
||||||
return <ActivityIndicator />;
|
<View style={{ backgroundColor: colors.backgroundColor }}>
|
||||||
|
<SearchBox onChangeText={text => updateState({ filter: text.trim() })} testID='room-members-view-search' />
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
return null;
|
ListFooterComponent={() => (state.isLoading ? <ActivityIndicator /> : null)}
|
||||||
}}
|
|
||||||
onEndReachedThreshold={0.1}
|
onEndReachedThreshold={0.1}
|
||||||
onEndReached={this.fetchMembers}
|
onEndReached={() => fetchMembers(state.allUsers)}
|
||||||
maxToRenderPerBatch={5}
|
ListEmptyComponent={() =>
|
||||||
windowSize={10}
|
state.end ? <Text style={[styles.noResult, { color: colors.titleText }]}>{I18n.t('No_members_found')}</Text> : null
|
||||||
|
}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state: IApplicationState) => ({
|
export default RoomMembersView;
|
||||||
baseUrl: state.server.server,
|
|
||||||
user: getUserSelector(state),
|
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
|
||||||
useRealName: state.settings.UI_Use_Real_Name,
|
|
||||||
muteUserPermission: state.permissions['mute-user'],
|
|
||||||
setLeaderPermission: state.permissions['set-leader'],
|
|
||||||
setOwnerPermission: state.permissions['set-owner'],
|
|
||||||
setModeratorPermission: state.permissions['set-moderator'],
|
|
||||||
removeUserPermission: state.permissions['remove-user'],
|
|
||||||
editTeamMemberPermission: state.permissions['edit-team-member'],
|
|
||||||
viewAllTeamChannelsPermission: state.permissions['view-all-team-channels'],
|
|
||||||
viewAllTeamsPermission: state.permissions['view-all-teams'],
|
|
||||||
serverVersion: state.server.version
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(withActionSheet(RoomMembersView)));
|
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import sharedStyles from '../Styles';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
list: {
|
list: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
item: {
|
noResult: {
|
||||||
flexDirection: 'row',
|
fontSize: 16,
|
||||||
paddingVertical: 10,
|
paddingVertical: 56,
|
||||||
paddingHorizontal: 16,
|
...sharedStyles.textSemibold,
|
||||||
alignItems: 'center'
|
...sharedStyles.textAlignCenter
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
marginRight: 16
|
|
||||||
},
|
|
||||||
separator: {
|
|
||||||
height: StyleSheet.hairlineWidth,
|
|
||||||
marginLeft: 60
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -211,19 +211,15 @@ class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelect
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderSelectedItem = ({ item }: { item: ISelectedUser }) => {
|
renderSelectedItem = ({ item }: { item: ISelectedUser }) => (
|
||||||
const { theme } = this.props;
|
|
||||||
return (
|
|
||||||
<UserItem
|
<UserItem
|
||||||
name={item.fname}
|
name={item.fname}
|
||||||
username={item.name}
|
username={item.name}
|
||||||
onPress={() => this._onPressSelectedItem(item)}
|
onPress={() => this._onPressSelectedItem(item)}
|
||||||
testID={`selected-user-${item.name}`}
|
testID={`selected-user-${item.name}`}
|
||||||
style={{ paddingRight: 15 }}
|
style={{ paddingRight: 15 }}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderItem = ({ item, index }: { item: ISelectedUser; index: number }) => {
|
renderItem = ({ item, index }: { item: ISelectedUser; index: number }) => {
|
||||||
const { search, chats } = this.state;
|
const { search, chats } = this.state;
|
||||||
|
@ -249,7 +245,6 @@ class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelect
|
||||||
testID={`select-users-view-item-${item.name}`}
|
testID={`select-users-view-item-${item.name}`}
|
||||||
icon={this.isChecked(username) ? 'check' : null}
|
icon={this.isChecked(username) ? 'check' : null}
|
||||||
style={style}
|
style={style}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -146,10 +146,6 @@ describe('Room actions screen', () => {
|
||||||
await expect(element(by.id('room-actions-members'))).toExist();
|
await expect(element(by.id('room-actions-members'))).toExist();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have add user', async () => {
|
|
||||||
await expect(element(by.id('room-actions-add-user'))).toExist();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have files', async () => {
|
it('should have files', async () => {
|
||||||
await expect(element(by.id('room-actions-files'))).toExist();
|
await expect(element(by.id('room-actions-files'))).toExist();
|
||||||
});
|
});
|
||||||
|
@ -303,24 +299,12 @@ describe('Room actions screen', () => {
|
||||||
.withTimeout(4000);
|
.withTimeout(4000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have notification audio option', async () => {
|
|
||||||
await waitFor(element(by.id('notification-preference-view-audio')))
|
|
||||||
.toExist()
|
|
||||||
.withTimeout(4000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have notification sound option', async () => {
|
it('should have notification sound option', async () => {
|
||||||
await waitFor(element(by.id('notification-preference-view-sound')))
|
await waitFor(element(by.id('notification-preference-view-sound')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(4000);
|
.withTimeout(4000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have notification duration option', async () => {
|
|
||||||
await waitFor(element(by.id('notification-preference-view-notification-duration')))
|
|
||||||
.toExist()
|
|
||||||
.withTimeout(4000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have email alert option', async () => {
|
it('should have email alert option', async () => {
|
||||||
await waitFor(element(by.id('notification-preference-view-email-alert')))
|
await waitFor(element(by.id('notification-preference-view-email-alert')))
|
||||||
.toExist()
|
.toExist()
|
||||||
|
@ -361,6 +345,14 @@ describe('Room actions screen', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add users to the room', async () => {
|
it('should add users to the room', async () => {
|
||||||
|
await waitFor(element(by.id('room-actions-members')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-actions-members')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
|
||||||
await waitFor(element(by.id('room-actions-add-user')))
|
await waitFor(element(by.id('room-actions-add-user')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(4000);
|
.withTimeout(4000);
|
||||||
|
@ -392,19 +384,14 @@ describe('Room actions screen', () => {
|
||||||
|
|
||||||
await element(by.id('selected-users-view-submit')).tap();
|
await element(by.id('selected-users-view-submit')).tap();
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
await waitFor(element(by.id('room-actions-members')))
|
|
||||||
.toExist()
|
|
||||||
.withTimeout(10000);
|
|
||||||
await element(by.id('room-actions-members')).tap();
|
|
||||||
await element(by.id('room-members-view-toggle-status')).tap();
|
|
||||||
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
|
||||||
.toExist()
|
|
||||||
.withTimeout(60000);
|
|
||||||
await backToActions();
|
await backToActions();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Room Members', () => {
|
describe('Room Members', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
await waitFor(element(by.id('room-actions-members')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
await element(by.id('room-actions-members')).tap();
|
await element(by.id('room-actions-members')).tap();
|
||||||
await waitFor(element(by.id('room-members-view')))
|
await waitFor(element(by.id('room-members-view')))
|
||||||
.toExist()
|
.toExist()
|
||||||
|
@ -442,13 +429,30 @@ describe('Room actions screen', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should show all users', async () => {
|
it('should show all users', async () => {
|
||||||
await element(by.id('room-members-view-toggle-status')).tap();
|
await waitFor(element(by.id('room-members-view-filter')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(10000);
|
||||||
|
await element(by.id('room-members-view-filter')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view-toggle-status-all')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-members-view-toggle-status-all')).tap();
|
||||||
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(60000);
|
.withTimeout(60000);
|
||||||
|
await tapBack();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter user', async () => {
|
it('should filter user', async () => {
|
||||||
|
await waitFor(element(by.id('room-actions-members')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-actions-members')).tap();
|
||||||
|
await element(by.id('room-members-view-filter')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view-toggle-status-all')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-members-view-toggle-status-all')).tap();
|
||||||
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(60000);
|
.withTimeout(60000);
|
||||||
|
@ -595,11 +599,21 @@ describe('Room actions screen', () => {
|
||||||
await waitFor(element(by.id('room-actions-view')))
|
await waitFor(element(by.id('room-actions-view')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(5000);
|
.withTimeout(5000);
|
||||||
|
await waitFor(element(by.id('room-actions-members')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
await element(by.id('room-actions-members')).tap();
|
await element(by.id('room-actions-members')).tap();
|
||||||
await waitFor(element(by.id('room-members-view')))
|
await waitFor(element(by.id('room-members-view')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(2000);
|
.withTimeout(2000);
|
||||||
await element(by.id('room-members-view-toggle-status')).tap();
|
await waitFor(element(by.id('room-members-view-filter')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(10000);
|
||||||
|
await element(by.id('room-members-view-filter')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view-toggle-status-all')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-members-view-toggle-status-all')).tap();
|
||||||
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(60000);
|
.withTimeout(60000);
|
||||||
|
@ -625,6 +639,7 @@ describe('Room actions screen', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should block/unblock user', async () => {
|
it('should block/unblock user', async () => {
|
||||||
|
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
|
||||||
await waitFor(element(by.id('room-actions-block-user'))).toExist();
|
await waitFor(element(by.id('room-actions-block-user'))).toExist();
|
||||||
await element(by.id('room-actions-block-user')).tap();
|
await element(by.id('room-actions-block-user')).tap();
|
||||||
await waitFor(element(by[textMatcher]('Unblock user')))
|
await waitFor(element(by[textMatcher]('Unblock user')))
|
||||||
|
|
|
@ -266,6 +266,11 @@ describe('Team', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add users to the team', async () => {
|
it('should add users to the team', async () => {
|
||||||
|
await element(by.id('room-actions-members')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
|
||||||
await waitFor(element(by.id('room-actions-add-user')))
|
await waitFor(element(by.id('room-actions-add-user')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(10000);
|
.withTimeout(10000);
|
||||||
|
@ -296,11 +301,17 @@ describe('Team', () => {
|
||||||
|
|
||||||
await element(by.id('selected-users-view-submit')).tap();
|
await element(by.id('selected-users-view-submit')).tap();
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
|
await tapBack();
|
||||||
|
await sleep(300);
|
||||||
await waitFor(element(by.id('room-actions-members')))
|
await waitFor(element(by.id('room-actions-members')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(10000);
|
.withTimeout(10000);
|
||||||
await element(by.id('room-actions-members')).tap();
|
await element(by.id('room-actions-members')).tap();
|
||||||
await element(by.id('room-members-view-toggle-status')).tap();
|
await element(by.id('room-members-view-filter')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view-toggle-status-all')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-members-view-toggle-status-all')).tap();
|
||||||
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(60000);
|
.withTimeout(60000);
|
||||||
|
@ -358,7 +369,11 @@ describe('Team', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show all users', async () => {
|
it('should show all users', async () => {
|
||||||
await element(by.id('room-members-view-toggle-status')).tap();
|
await element(by.id('room-members-view-filter')).tap();
|
||||||
|
await waitFor(element(by.id('room-members-view-toggle-status-all')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-members-view-toggle-status-all')).tap();
|
||||||
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(60000);
|
.withTimeout(60000);
|
||||||
|
|
Loading…
Reference in New Issue