diff --git a/app/containers/RadioButton/index.tsx b/app/containers/RadioButton/index.tsx
new file mode 100644
index 000000000..b9c8efe5c
--- /dev/null
+++ b/app/containers/RadioButton/index.tsx
@@ -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 (
+
+ );
+};
diff --git a/app/containers/UserItem.tsx b/app/containers/UserItem.tsx
index b39948856..e044dd6ba 100644
--- a/app/containers/UserItem.tsx
+++ b/app/containers/UserItem.tsx
@@ -4,9 +4,8 @@ import { Pressable, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-n
import Avatar from './Avatar';
import { CustomIcon, TIconsName } from './CustomIcon';
import sharedStyles from '../views/Styles';
-import { themes } from '../lib/constants';
import { isIOS } from '../lib/methods/helpers';
-import { TSupportedThemes } from '../theme';
+import { useTheme } from '../theme';
const styles = StyleSheet.create({
button: {
@@ -47,34 +46,36 @@ interface IUserItem {
onLongPress?: () => void;
style?: StyleProp;
icon?: TIconsName | null;
- theme: TSupportedThemes;
}
-const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, theme }: IUserItem) => (
- ({
- backgroundColor: isIOS && pressed ? themes[theme].bannerBackground : 'transparent'
- })}
- >
-
-
-
-
- {name}
-
-
- @{username}
-
+const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon }: IUserItem): React.ReactElement => {
+ const { colors } = useTheme();
+ return (
+ ({
+ backgroundColor: isIOS && pressed ? colors.bannerBackground : 'transparent'
+ })}
+ >
+
+
+
+
+ {name}
+
+
+ @{username}
+
+
+ {icon ? : null}
- {icon ? : null}
-
-
-);
+
+ );
+};
export default UserItem;
diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json
index c865bbc37..5f0c0c134 100644
--- a/app/i18n/locales/en.json
+++ b/app/i18n/locales/en.json
@@ -356,6 +356,7 @@
"No_mentioned_messages": "No mentioned messages",
"No_pinned_messages": "No pinned messages",
"No_results_found": "No results found",
+ "No_members_found": "No members found",
"No_starred_messages": "No starred messages",
"No_thread_messages": "No thread messages",
"No_label_provided": "No {{label}} provided.",
diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json
index e30e7ad6c..694aef0d6 100644
--- a/app/i18n/locales/pt-BR.json
+++ b/app/i18n/locales/pt-BR.json
@@ -334,6 +334,7 @@
"No_mentioned_messages": "Não há menções",
"No_pinned_messages": "Não há mensagens fixadas",
"No_results_found": "Nenhum resultado encontrado",
+ "No_members_found": "Nenhum usuário encontrado",
"No_starred_messages": "Não há mensagens favoritas",
"No_thread_messages": "Não há tópicos",
"No_label_provided": "Sem {{label}}.",
diff --git a/app/lib/methods/helpers/helpers.ts b/app/lib/methods/helpers/helpers.ts
index d496b7167..bfacfc015 100644
--- a/app/lib/methods/helpers/helpers.ts
+++ b/app/lib/methods/helpers/helpers.ts
@@ -85,7 +85,7 @@ export function hasRole(role): boolean {
return userRoles.indexOf(role) > -1;
}
-export async function hasPermission(permissions, rid?: any): boolean[] {
+export async function hasPermission(permissions, rid?: any): Promise {
let roomRoles = [];
if (rid) {
const db = database.active;
diff --git a/app/lib/methods/helpers/log/events.ts b/app/lib/methods/helpers/log/events.ts
index ea4e57f48..b4562b6c5 100644
--- a/app/lib/methods/helpers/log/events.ts
+++ b/app/lib/methods/helpers/log/events.ts
@@ -272,6 +272,10 @@ export default {
RA_MOVE_TO_TEAM_F: 'ra_move_to_team_f',
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
RI_GO_RI_EDIT: 'ri_go_ri_edit',
RI_GO_LIVECHAT_EDIT: 'ri_go_livechat_edit',
diff --git a/app/lib/navigation/appNavigation.ts b/app/lib/navigation/appNavigation.ts
index c2ef51c75..7d2f73756 100644
--- a/app/lib/navigation/appNavigation.ts
+++ b/app/lib/navigation/appNavigation.ts
@@ -17,10 +17,15 @@ function replace(name: string, params: any) {
navigationRef.current?.dispatch(StackActions.replace(name, params));
}
+function popToTop() {
+ navigationRef.current?.dispatch(StackActions.popToTop());
+}
+
export default {
navigationRef,
routeNameRef,
navigate,
back,
- replace
+ replace,
+ popToTop
};
diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx
index 95fdac203..d5938cb58 100644
--- a/app/stacks/InsideStack.tsx
+++ b/app/stacks/InsideStack.tsx
@@ -95,7 +95,7 @@ const ChatsStackNavigator = () => {
-
+
{
-
+
{
- const { theme } = this.props;
-
- return (
- this.removeUser(item)}
- testID={`create-channel-view-item-${item.name}`}
- icon='check'
- theme={theme}
- />
- );
- };
+ renderItem = ({ item }: { item: IOtherUser }) => (
+ this.removeUser(item)}
+ testID={`create-channel-view-item-${item.name}`}
+ icon='check'
+ />
+ );
renderInvitedList = () => {
const { users, theme } = this.props;
diff --git a/app/views/NewMessageView.tsx b/app/views/NewMessageView.tsx
index c8f70ec93..2b6f65eb6 100644
--- a/app/views/NewMessageView.tsx
+++ b/app/views/NewMessageView.tsx
@@ -290,7 +290,6 @@ class NewMessageView extends React.Component this.goRoom(itemModel)}
testID={`new-message-view-item-${item.name}`}
style={style}
- theme={theme}
/>
);
};
diff --git a/app/views/RoomActionsView/index.tsx b/app/views/RoomActionsView/index.tsx
index df0707a70..f440de18b 100644
--- a/app/views/RoomActionsView/index.tsx
+++ b/app/views/RoomActionsView/index.tsx
@@ -64,10 +64,6 @@ interface IRoomActionsViewProps extends IActionSheetProvider, IBaseScreen {
- 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 () => {
const { room } = this.state;
const { editRoomPermission } = this.props;
@@ -1135,7 +1089,7 @@ class RoomActionsView extends React.Component 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'
left={() => }
showActionIndicator
@@ -1164,45 +1118,6 @@ class RoomActionsView extends React.Component
) : null}
- {['c', 'p'].includes(t) && canAddUser ? (
- <>
-
- this.onPressTouchable({
- route: 'SelectedUsersView',
- params: {
- title: I18n.t('Add_users'),
- nextAction: this.addUser
- }
- })
- }
- testID='room-actions-add-user'
- left={() => }
- showActionIndicator
- />
-
- >
- ) : null}
-
- {['c', 'p'].includes(t) && canInviteUser ? (
- <>
-
- this.onPressTouchable({
- route: 'InviteUsersView',
- params: { rid }
- })
- }
- testID='room-actions-invite-user'
- left={() => }
- showActionIndicator
- />
-
- >
- ) : null}
-
{['c', 'p', 'd'].includes(t) && !prid ? (
<>
({
encryptionEnabled: state.encryption.enabled,
serverVersion: state.server.version,
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'],
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
diff --git a/app/views/RoomMembersView/components/ActionsSection.tsx b/app/views/RoomMembersView/components/ActionsSection.tsx
new file mode 100644
index 000000000..6442db129
--- /dev/null
+++ b/app/views/RoomMembersView/components/ActionsSection.tsx
@@ -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,
+ StackNavigationProp
+>;
+
+interface IActionsSection {
+ rid: TSubscriptionModel['rid'];
+ t: TSubscriptionModel['t'];
+ joined: boolean;
+}
+
+export default function ActionsSection({ rid, t, joined }: IActionsSection): React.ReactElement {
+ const { navigate, pop } = useNavigation();
+ 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 (
+
+ {['c', 'p'].includes(t) && canAddUser ? (
+ <>
+
+
+ handleOnPress({
+ route: 'SelectedUsersView',
+ params: {
+ title: i18n.t('Add_users'),
+ nextAction: addUser
+ }
+ })
+ }
+ testID='room-actions-add-user'
+ left={() => }
+ showActionIndicator
+ />
+
+ >
+ ) : null}
+
+ {['c', 'p'].includes(t) && canInviteUser ? (
+ <>
+
+ handleOnPress({
+ route: 'InviteUsersView',
+ params: { rid }
+ })
+ }
+ testID='room-actions-invite-user'
+ left={() => }
+ showActionIndicator
+ />
+
+ >
+ ) : null}
+
+ );
+}
diff --git a/app/views/RoomMembersView/helpers.ts b/app/views/RoomMembersView/helpers.ts
new file mode 100644
index 000000000..f7576fb3e
--- /dev/null
+++ b/app/views/RoomMembersView/helpers.ts
@@ -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 => {
+ 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
+): Promise => {
+ 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 => {
+ 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 => {
+ 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
+): Promise => {
+ 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 => {
+ 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 => {
+ 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();
+};
diff --git a/app/views/RoomMembersView/index.tsx b/app/views/RoomMembersView/index.tsx
index 2805d8ae8..be7e70d9f 100644
--- a/app/views/RoomMembersView/index.tsx
+++ b/app/views/RoomMembersView/index.tsx
@@ -1,311 +1,202 @@
-import { Q } from '@nozbe/watermelondb';
-import React from 'react';
-import { FlatList } from 'react-native';
-import { connect } from 'react-redux';
-import { Observable, Subscription } from 'rxjs';
+import { NavigationProp, RouteProp, useNavigation, useRoute } from '@react-navigation/native';
+import React, { useEffect, useReducer } from 'react';
+import { FlatList, Text, View } from 'react-native';
-import { themes } from '../../lib/constants';
-import { TActionSheetOptions, TActionSheetOptionsItem, withActionSheet } from '../../containers/ActionSheet';
+import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
import ActivityIndicator from '../../containers/ActivityIndicator';
+import { CustomIcon } from '../../containers/CustomIcon';
import * as HeaderButton from '../../containers/HeaderButton';
import * as List from '../../containers/List';
+import { RadioButton } from '../../containers/RadioButton';
import SafeAreaView from '../../containers/SafeAreaView';
import SearchBox from '../../containers/SearchBox';
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 { getUserSelector } from '../../selectors/login';
-import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
-import { TSupportedThemes, withTheme } from '../../theme';
-import EventEmitter from '../../lib/methods/helpers/events';
-import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
-import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers/info';
+import { TSubscriptionModel, TUserModel } from '../../definitions';
+import I18n from '../../i18n';
+import { useAppSelector, usePermissions } from '../../lib/hooks';
+import { getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
+import { showConfirmationAlert } from '../../lib/methods/helpers/info';
import log from '../../lib/methods/helpers/log';
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 { 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;
-interface IRoomMembersViewProps extends IBaseScreen {
- 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 {
isLoading: boolean;
allUsers: boolean;
filtering: string;
- rid: string;
members: TUserModel[];
- membersFiltered: TUserModel[];
room: TSubscriptionModel;
end: boolean;
+ roomRoles: any;
+ filter: string;
page: number;
}
-class RoomMembersView extends React.Component {
- private mounted: boolean;
- private permissions: { [key in TSupportedPermissions]?: boolean };
- private roomObservable!: Observable;
- private subscription!: Subscription;
- private roomRoles: any;
+const RightIcon = ({ check, label }: { check: boolean; label: string }) => {
+ const { colors } = useTheme();
+ return (
+
+ );
+};
- constructor(props: IRoomMembersViewProps) {
- super(props);
- this.mounted = false;
- this.permissions = {};
- const rid = props.route.params?.rid;
- const room = props.route.params?.room;
- this.state = {
+const RoomMembersView = (): React.ReactElement => {
+ const { showActionSheet } = useActionSheet();
+ const { colors } = useTheme();
+
+ const { params } = useRoute>();
+ const navigation = useNavigation>();
+
+ 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) => ({ ...state, ...newState }),
+ {
isLoading: false,
allUsers: false,
filtering: '',
- rid,
members: [],
- membersFiltered: [],
- room: room || ({} as TSubscriptionModel),
+ room: params.room || ({} as TSubscriptionModel),
end: false,
+ roomRoles: null,
+ filter: '',
page: 0
+ }
+ );
+
+ const teamPermissions: TSupportedPermissions[] = state.room.teamMain
+ ? ['edit-team-member', 'view-all-team-channels', 'view-all-teams']
+ : [];
+
+ const [
+ muteUserPermission,
+ setLeaderPermission,
+ setOwnerPermission,
+ setModeratorPermission,
+ removeUserPermission,
+ editTeamMemberPermission,
+ viewAllTeamChannelsPermission,
+ viewAllTeamsPermission
+ ] = usePermissions(['mute-user', 'set-leader', 'set-owner', 'set-moderator', 'remove-user', ...teamPermissions], params.rid);
+
+ 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);
+ }
};
- 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 });
- }
- });
+ fetchRoles();
+ }, [
+ muteUserPermission,
+ setLeaderPermission,
+ setOwnerPermission,
+ setModeratorPermission,
+ removeUserPermission,
+ editTeamMemberPermission,
+ viewAllTeamChannelsPermission,
+ viewAllTeamsPermission
+ ]);
+
+ const toggleStatus = (status: boolean) => {
+ try {
+ updateState({ members: [], allUsers: status, end: false });
+ fetchMembers(status);
+ setHeader(status);
+ } catch (e) {
+ log(e);
}
- this.setHeader();
- }
+ };
- async componentDidMount() {
- const { room } = this.state;
- this.mounted = true;
- this.fetchMembers();
-
- if (isGroupChat(room)) {
- return;
- }
-
- const {
- muteUserPermission,
- setLeaderPermission,
- setOwnerPermission,
- setModeratorPermission,
- removeUserPermission,
- editTeamMemberPermission,
- viewAllTeamChannelsPermission,
- viewAllTeamsPermission
- } = this.props;
-
- const result = await hasPermission(
- [
- muteUserPermission,
- setLeaderPermission,
- setOwnerPermission,
- setModeratorPermission,
- removeUserPermission,
- ...(room.teamMain ? [editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission] : [])
- ],
- room.rid
- );
-
- this.permissions = {
- 'mute-user': result[0],
- 'set-leader': result[1],
- 'set-owner': result[2],
- 'set-moderator': result[3],
- 'remove-user': result[4],
- ...(room.teamMain
- ? {
- '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);
- 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');
+ const setHeader = (allUsers: boolean) => {
navigation.setOptions({
title: I18n.t('Members'),
headerRight: () => (
-
+
+ showActionSheet({
+ options: [
+ {
+ title: I18n.t('Online'),
+ onPress: () => toggleStatus(true),
+ right: () => ,
+ testID: 'room-members-view-toggle-status-online'
+ },
+ {
+ title: I18n.t('All'),
+ onPress: () => toggleStatus(false),
+ right: () => ,
+ testID: 'room-members-view-toggle-status-all'
+ }
+ ]
+ })
+ }
+ testID='room-members-view-filter'
+ />
)
});
};
- get isServerVersionLowerThan3_16() {
- const { serverVersion } = this.props;
- return compareServerVersion(serverVersion, 'lowerThan', '3.16.0');
- }
+ const getUserDisplayName = (user: TUserModel) => (useRealName ? user.name : user.username) || user.username;
- onSearchChangeText = debounce((text: string) => {
- const { members } = this.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 onPressUser = (selectedUser: TUserModel) => {
+ const { room, roomRoles, members } = state;
const options: TActionSheetOptionsItem[] = [
{
icon: 'message',
title: I18n.t('Direct_message'),
- onPress: () => this.navToDirectMessage(selectedUser)
+ onPress: () => navToDirectMessage(selectedUser, isMasterDetail)
}
];
@@ -316,12 +207,12 @@ class RoomMembersView extends React.Component this.handleIgnore(selectedUser, !isIgnored),
+ onPress: () => handleIgnore(selectedUser, !isIgnored, room.rid),
testID: 'action-sheet-ignore-user'
});
}
- if (this.permissions['mute-user']) {
+ if (muteUserPermission) {
const { muted = [] } = room;
const userIsMuted = muted.find?.(m => m === selectedUser.username);
selectedUser.muted = !!userIsMuted;
@@ -334,7 +225,7 @@ class RoomMembersView extends React.Component this.handleMute(selectedUser)
+ onPress: () => handleMute(selectedUser, room.rid)
});
},
testID: 'action-sheet-mute-user'
@@ -342,78 +233,63 @@ class RoomMembersView extends React.Component r.u._id === selectedUser._id);
- const isOwner = userRoleResult?.roles.includes('owner');
+ if (setOwnerPermission) {
+ const isOwner = fetchRole('owner', selectedUser, roomRoles);
options.push({
icon: 'shield-check',
title: I18n.t('Owner'),
- onPress: () => this.handleOwner(selectedUser, !isOwner),
- right: () => (
-
- ),
+ onPress: () =>
+ handleOwner(selectedUser, !isOwner, getUserDisplayName(selectedUser), room, () =>
+ fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
+ ),
+ right: () => ,
testID: 'action-sheet-set-owner'
});
}
// Leader
- if (this.permissions['set-leader']) {
- const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
- const isLeader = userRoleResult?.roles.includes('leader');
+ if (setLeaderPermission) {
+ const isLeader = fetchRole('leader', selectedUser, roomRoles);
options.push({
icon: 'shield-alt',
title: I18n.t('Leader'),
- onPress: () => this.handleLeader(selectedUser, !isLeader),
- right: () => (
-
- ),
+ onPress: () =>
+ handleLeader(selectedUser, !isLeader, room, getUserDisplayName(selectedUser), () =>
+ fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
+ ),
+ right: () => ,
testID: 'action-sheet-set-leader'
});
}
// Moderator
- if (this.permissions['set-moderator']) {
- const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
- const isModerator = userRoleResult?.roles.includes('moderator');
+ if (setModeratorPermission) {
+ const isModerator = fetchRole('moderator', selectedUser, roomRoles);
options.push({
icon: 'shield',
title: I18n.t('Moderator'),
- onPress: () => this.handleModerator(selectedUser, !isModerator),
- right: () => (
-
- ),
+ onPress: () =>
+ handleModerator(selectedUser, !isModerator, room, getUserDisplayName(selectedUser), () =>
+ fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
+ ),
+ right: () => ,
testID: 'action-sheet-set-moderator'
});
}
// Remove from team
- if (this.permissions['edit-team-member']) {
+ if (editTeamMemberPermission) {
options.push({
icon: 'logout',
danger: true,
title: I18n.t('Remove_from_Team'),
- onPress: () => this.handleRemoveFromTeam(selectedUser),
+ onPress: () => handleRemoveFromTeam(selectedUser, updateState, room, members),
testID: 'action-sheet-remove-from-team'
});
}
// Remove from room
- if (this.permissions['remove-user'] && !room.teamMain) {
+ if (removeUserPermission && !room.teamMain) {
options.push({
icon: 'logout',
title: I18n.t('Remove_from_room'),
@@ -422,7 +298,13 @@ class RoomMembersView extends React.Component this.handleRemoveUserFromRoom(selectedUser)
+ onPress: () => {
+ handleRemoveUserFromRoom(selectedUser, room, () =>
+ updateState({
+ members: members.filter(member => member._id !== selectedUser._id)
+ })
+ );
+ }
});
},
testID: 'action-sheet-remove-from-room'
@@ -435,256 +317,83 @@ class RoomMembersView extends React.Component {
- try {
- 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 fetchMembers = async (status: boolean) => {
+ const { members, isLoading, end, room, filter, page } = state;
const { t } = room;
if (isLoading || end) {
return;
}
- this.setState({ isLoading: true });
+ updateState({ isLoading: true });
try {
const membersResult = await Services.getRoomMembers({
- rid,
+ rid: room.rid,
roomType: t,
- type: allUsers ? 'all' : 'online',
- filter: filtering,
+ type: !status ? 'all' : 'online',
+ filter,
skip: PAGE_SIZE * page,
limit: PAGE_SIZE,
- allUsers
+ allUsers: !status
});
const end = membersResult?.length < PAGE_SIZE;
const membersResultFiltered = membersResult?.filter((member: TUserModel) => !members.some(m => m._id === member._id));
- this.setState({
- members: members.concat(membersResultFiltered || []),
+ updateState({
+ members: [...members, ...membersResultFiltered],
isLoading: false,
end,
page: page + 1
});
- this.setHeader();
} catch (e) {
log(e);
- this.setState({ isLoading: false });
+ updateState({ isLoading: false });
}
};
- goRoom = (item: TGoRoomItem) => {
- const { navigation, isMasterDetail } = this.props;
- if (isMasterDetail) {
- // @ts-ignore
- navigation.navigate('DrawerNavigator');
- } else {
- navigation.popToTop();
- }
- goRoom({ item, isMasterDetail });
- };
+ const filteredMembers =
+ state.members && state.members.length > 0 && state.filter
+ ? state.members.filter(
+ m =>
+ m.username.toLowerCase().match(state.filter.toLowerCase()) || m.name?.toLowerCase().match(state.filter.toLowerCase())
+ )
+ : null;
- 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 = () => this.onSearchChangeText(text)} testID='room-members-view-search' />;
-
- renderItem = ({ item }: { item: TUserModel }) => {
- const { theme } = this.props;
-
- return (
- this.onPressUser(item)}
- testID={`room-members-view-item-${item.username}`}
- theme={theme}
+ return (
+
+
+ (
+
+ onPressUser(item)}
+ testID={`room-members-view-item-${item.username}`}
+ />
+
+ )}
+ style={styles.list}
+ keyExtractor={item => item._id}
+ ItemSeparatorComponent={List.Separator}
+ ListHeaderComponent={
+ <>
+
+
+ updateState({ filter: text.trim() })} testID='room-members-view-search' />
+
+ >
+ }
+ ListFooterComponent={() => (state.isLoading ? : null)}
+ onEndReachedThreshold={0.1}
+ onEndReached={() => fetchMembers(state.allUsers)}
+ ListEmptyComponent={() =>
+ state.end ? {I18n.t('No_members_found')} : null
+ }
+ {...scrollPersistTaps}
/>
- );
- };
+
+ );
+};
- render() {
- const { filtering, members, membersFiltered, isLoading } = this.state;
- const { theme } = this.props;
- return (
-
-
- item._id}
- ItemSeparatorComponent={List.Separator}
- ListHeaderComponent={this.renderSearchBar}
- ListFooterComponent={() => {
- if (isLoading) {
- return ;
- }
- return null;
- }}
- onEndReachedThreshold={0.1}
- onEndReached={this.fetchMembers}
- maxToRenderPerBatch={5}
- windowSize={10}
- {...scrollPersistTaps}
- />
-
- );
- }
-}
-
-const mapStateToProps = (state: IApplicationState) => ({
- 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)));
+export default RoomMembersView;
diff --git a/app/views/RoomMembersView/styles.ts b/app/views/RoomMembersView/styles.ts
index d183ebb09..8531b8172 100644
--- a/app/views/RoomMembersView/styles.ts
+++ b/app/views/RoomMembersView/styles.ts
@@ -1,20 +1,15 @@
import { StyleSheet } from 'react-native';
+import sharedStyles from '../Styles';
+
export default StyleSheet.create({
list: {
flex: 1
},
- item: {
- flexDirection: 'row',
- paddingVertical: 10,
- paddingHorizontal: 16,
- alignItems: 'center'
- },
- avatar: {
- marginRight: 16
- },
- separator: {
- height: StyleSheet.hairlineWidth,
- marginLeft: 60
+ noResult: {
+ fontSize: 16,
+ paddingVertical: 56,
+ ...sharedStyles.textSemibold,
+ ...sharedStyles.textAlignCenter
}
});
diff --git a/app/views/SelectedUsersView.tsx b/app/views/SelectedUsersView.tsx
index 08a720836..a651a7178 100644
--- a/app/views/SelectedUsersView.tsx
+++ b/app/views/SelectedUsersView.tsx
@@ -211,19 +211,15 @@ class SelectedUsersView extends React.Component {
- const { theme } = this.props;
- return (
- this._onPressSelectedItem(item)}
- testID={`selected-user-${item.name}`}
- style={{ paddingRight: 15 }}
- theme={theme}
- />
- );
- };
+ renderSelectedItem = ({ item }: { item: ISelectedUser }) => (
+ this._onPressSelectedItem(item)}
+ testID={`selected-user-${item.name}`}
+ style={{ paddingRight: 15 }}
+ />
+ );
renderItem = ({ item, index }: { item: ISelectedUser; index: number }) => {
const { search, chats } = this.state;
@@ -249,7 +245,6 @@ class SelectedUsersView extends React.Component
);
};
diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js
index 74a0fdbf8..5acb56f0a 100644
--- a/e2e/tests/room/03-roomactions.spec.js
+++ b/e2e/tests/room/03-roomactions.spec.js
@@ -146,10 +146,6 @@ describe('Room actions screen', () => {
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 () => {
await expect(element(by.id('room-actions-files'))).toExist();
});
@@ -303,24 +299,12 @@ describe('Room actions screen', () => {
.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 () => {
await waitFor(element(by.id('notification-preference-view-sound')))
.toExist()
.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 () => {
await waitFor(element(by.id('notification-preference-view-email-alert')))
.toExist()
@@ -361,6 +345,14 @@ describe('Room actions screen', () => {
});
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')))
.toExist()
.withTimeout(4000);
@@ -392,19 +384,14 @@ describe('Room actions screen', () => {
await element(by.id('selected-users-view-submit')).tap();
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();
});
describe('Room Members', () => {
before(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()
@@ -442,13 +429,30 @@ describe('Room actions screen', () => {
};
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}`)))
.toExist()
.withTimeout(60000);
+ await tapBack();
});
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}`)))
.toExist()
.withTimeout(60000);
@@ -595,11 +599,21 @@ describe('Room actions screen', () => {
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
+ 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 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}`)))
.toExist()
.withTimeout(60000);
@@ -625,6 +639,7 @@ describe('Room actions screen', () => {
});
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 element(by.id('room-actions-block-user')).tap();
await waitFor(element(by[textMatcher]('Unblock user')))
diff --git a/e2e/tests/team/02-team.spec.js b/e2e/tests/team/02-team.spec.js
index 6994703cb..47a3213da 100644
--- a/e2e/tests/team/02-team.spec.js
+++ b/e2e/tests/team/02-team.spec.js
@@ -266,6 +266,11 @@ describe('Team', () => {
});
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')))
.toExist()
.withTimeout(10000);
@@ -296,11 +301,17 @@ describe('Team', () => {
await element(by.id('selected-users-view-submit')).tap();
await sleep(300);
+ await tapBack();
+ 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 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}`)))
.toExist()
.withTimeout(60000);
@@ -358,7 +369,11 @@ describe('Team', () => {
});
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}`)))
.toExist()
.withTimeout(60000);