diff --git a/apps/meteor/client/views/hooks/useActionSpread.ts b/apps/meteor/client/views/hooks/useActionSpread.ts
index 21a089ba1443..b1c2fddc9e9b 100644
--- a/apps/meteor/client/views/hooks/useActionSpread.ts
+++ b/apps/meteor/client/views/hooks/useActionSpread.ts
@@ -1,14 +1,14 @@
-import { useMemo } from 'react';
+import { useMemo, ReactNode } from 'react';
-type Action = {
- label: string;
- icon: string;
- action: () => any;
+export type Action = {
+ label: ReactNode;
+ icon?: string;
+ action: () => void;
};
type MenuOption = {
- label: { label: string; icon: string };
- action: Function;
+ label: { label: ReactNode; icon?: string };
+ action: () => void;
};
const mapOptions = ([key, { action, label, icon }]: [string, Action]): [string, MenuOption] => [
diff --git a/apps/meteor/client/views/room/hooks/useUserHasRoomRole.ts b/apps/meteor/client/views/room/hooks/useUserHasRoomRole.ts
new file mode 100644
index 000000000000..cc9e93bdf318
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserHasRoomRole.ts
@@ -0,0 +1,8 @@
+import { IRole, IRoom, IUser } from '@rocket.chat/core-typings';
+import { useCallback } from 'react';
+
+import { RoomRoles } from '../../../../app/models/client';
+import { useReactiveValue } from '../../../hooks/useReactiveValue';
+
+export const useUserHasRoomRole = (uid: IUser['_id'], rid: IRoom['_id'], role: IRole['name']): boolean =>
+ useReactiveValue(useCallback(() => !!RoomRoles.findOne({ rid, 'u._id': uid, 'roles': role }), [uid, rid, role]));
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions.js b/apps/meteor/client/views/room/hooks/useUserInfoActions.js
deleted file mode 100644
index fc6fdabd4a1f..000000000000
--- a/apps/meteor/client/views/room/hooks/useUserInfoActions.js
+++ /dev/null
@@ -1,415 +0,0 @@
-import { Button, ButtonGroup, Icon, Modal, Box } from '@rocket.chat/fuselage';
-import { useAutoFocus, useMutableCallback } from '@rocket.chat/fuselage-hooks';
-import { escapeHTML } from '@rocket.chat/string-helpers';
-import {
- useSetModal,
- useToastMessageDispatch,
- useRoute,
- useUserId,
- useUserSubscription,
- useUserRoom,
- useUserSubscriptionByName,
- usePermission,
- useAllPermissions,
- useMethod,
- useTranslation,
-} from '@rocket.chat/ui-contexts';
-import React, { useCallback, useMemo } from 'react';
-
-import { RoomRoles } from '../../../../app/models/client';
-import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig';
-import { useEndpointActionExperimental } from '../../../hooks/useEndpointActionExperimental';
-import { useReactiveValue } from '../../../hooks/useReactiveValue';
-import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
-import RemoveUsersModal from '../../teams/contextualBar/members/RemoveUsersModal';
-import { useWebRTC } from './useWebRTC';
-
-const useUserHasRoomRole = (uid, rid, role) =>
- useReactiveValue(useCallback(() => !!RoomRoles.findOne({ rid, 'u._id': uid, 'roles': role }), [uid, rid, role]));
-
-const getShouldOpenDirectMessage = (currentSubscription, usernameSubscription, canOpenDirectMessage, username) => {
- const canOpenDm = canOpenDirectMessage || usernameSubscription;
- const directMessageIsNotAlreadyOpen = currentSubscription && currentSubscription.name !== username;
- return canOpenDm && directMessageIsNotAlreadyOpen;
-};
-
-const getUserIsMuted = (room, user, userCanPostReadonly) => {
- if (room && room.ro) {
- if (Array.isArray(room.unmuted) && room.unmuted.indexOf(user && user.username) !== -1) {
- return false;
- }
-
- if (userCanPostReadonly) {
- return Array.isArray(room.muted) && room.muted.indexOf(user && user.username) !== -1;
- }
-
- return true;
- }
-
- return room && Array.isArray(room.muted) && room.muted.indexOf(user && user.username) > -1;
-};
-
-const WarningModal = ({ text, confirmText, close, confirm, ...props }) => {
- const refAutoFocus = useAutoFocus(true);
- const t = useTranslation();
- return (
-
-
-
- {t('Are_you_sure')}
-
-
- {text}
-
-
-
-
-
-
-
- );
-};
-// TODO: Remove endpoint concatenation
-export const useUserInfoActions = (user = {}, rid, reload) => {
- const t = useTranslation();
- const dispatchToastMessage = useToastMessageDispatch();
- const directRoute = useRoute('direct');
-
- const setModal = useSetModal();
-
- const { _id: uid } = user;
- const ownUserId = useUserId();
-
- const closeModal = useMutableCallback(() => setModal(null));
-
- const room = useUserRoom(rid);
- const currentSubscription = useUserSubscription(rid);
- const usernameSubscription = useUserSubscriptionByName(user.username);
-
- const isLeader = useUserHasRoomRole(uid, rid, 'leader');
- const isModerator = useUserHasRoomRole(uid, rid, 'moderator');
- const isOwner = useUserHasRoomRole(uid, rid, 'owner');
-
- const otherUserCanPostReadonly = useAllPermissions('post-readonly', rid);
-
- const isIgnored = currentSubscription && currentSubscription.ignored && currentSubscription.ignored.indexOf(uid) > -1;
- const isMuted = getUserIsMuted(room, user, otherUserCanPostReadonly);
-
- const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels';
-
- const roomDirectives = room && room.t && roomCoordinator.getRoomDirectives(room.t);
-
- const [roomCanSetOwner, roomCanSetLeader, roomCanSetModerator, roomCanIgnore, roomCanBlock, roomCanMute, roomCanRemove] = [
- ...(roomDirectives && [
- roomDirectives.allowMemberAction(room, RoomMemberActions.SET_AS_OWNER),
- roomDirectives.allowMemberAction(room, RoomMemberActions.SET_AS_LEADER),
- roomDirectives.allowMemberAction(room, RoomMemberActions.SET_AS_MODERATOR),
- roomDirectives.allowMemberAction(room, RoomMemberActions.IGNORE),
- roomDirectives.allowMemberAction(room, RoomMemberActions.BLOCK),
- roomDirectives.allowMemberAction(room, RoomMemberActions.MUTE),
- roomDirectives.allowMemberAction(room, RoomMemberActions.REMOVE_USER),
- ]),
- ];
-
- const roomName = room && room.t && escapeHTML(roomCoordinator.getRoomName(room.t, room));
-
- const userCanSetOwner = usePermission('set-owner', rid);
- const userCanSetLeader = usePermission('set-leader', rid);
- const userCanSetModerator = usePermission('set-moderator', rid);
- const userCanMute = usePermission('mute-user', rid);
- const userCanRemove = usePermission('remove-user', rid);
- const userCanDirectMessage = usePermission('create-d');
- const { shouldAllowCalls, callInProgress, joinCall, startCall } = useWebRTC(rid);
-
- const shouldOpenDirectMessage = getShouldOpenDirectMessage(
- currentSubscription,
- usernameSubscription,
- userCanDirectMessage,
- user.username,
- );
-
- const openDirectDm = useMutableCallback(() =>
- directRoute.push({
- rid: user.username,
- }),
- );
-
- const openDirectMessageOption = useMemo(
- () =>
- shouldOpenDirectMessage && {
- label: t('Direct_Message'),
- icon: 'balloon',
- action: openDirectDm,
- },
- [openDirectDm, shouldOpenDirectMessage, t],
- );
-
- const videoCallOption = useMemo(() => {
- const handleJoinCall = () => {
- joinCall({ audio: true, video: true });
- };
- const handleStartCall = () => {
- startCall({ audio: true, video: true });
- };
- const action = callInProgress ? handleJoinCall : handleStartCall;
-
- return (
- shouldAllowCalls && {
- label: t(callInProgress ? 'Join_video_call' : 'Start_video_call'),
- icon: 'video',
- action,
- }
- );
- }, [callInProgress, shouldAllowCalls, t, joinCall, startCall]);
-
- const audioCallOption = useMemo(() => {
- const handleJoinCall = () => {
- joinCall({ audio: true, video: false });
- };
- const handleStartCall = () => {
- startCall({ audio: true, video: false });
- };
- const action = callInProgress ? handleJoinCall : handleStartCall;
-
- return (
- shouldAllowCalls && {
- label: t(callInProgress ? 'Join_audio_call' : 'Start_audio_call'),
- icon: 'mic',
- action,
- }
- );
- }, [callInProgress, shouldAllowCalls, t, joinCall, startCall]);
-
- const changeOwnerEndpoint = isOwner ? 'removeOwner' : 'addOwner';
- const changeOwnerMessage = isOwner ? 'User__username__removed_from__room_name__owners' : 'User__username__is_now_a_owner_of__room_name_';
- const changeOwner = useEndpointActionExperimental(
- 'POST',
- `${endpointPrefix}.${changeOwnerEndpoint}`,
- t(changeOwnerMessage, { username: user.username, room_name: roomName }),
- );
- const changeOwnerAction = useMutableCallback(async () => changeOwner({ roomId: rid, userId: uid }));
- const changeOwnerOption = useMemo(
- () =>
- roomCanSetOwner &&
- userCanSetOwner && {
- label: t(isOwner ? 'Remove_as_owner' : 'Set_as_owner'),
- icon: 'shield-check',
- action: changeOwnerAction,
- },
- [changeOwnerAction, isOwner, t, roomCanSetOwner, userCanSetOwner],
- );
-
- const changeLeaderEndpoint = isLeader ? 'removeLeader' : 'addLeader';
- const changeLeaderMessage = isLeader
- ? 'User__username__removed_from__room_name__leaders'
- : 'User__username__is_now_a_leader_of__room_name_';
- const changeLeader = useEndpointActionExperimental(
- 'POST',
- `${endpointPrefix}.${changeLeaderEndpoint}`,
- t(changeLeaderMessage, { username: user.username, room_name: roomName }),
- );
- const changeLeaderAction = useMutableCallback(() => changeLeader({ roomId: rid, userId: uid }));
- const changeLeaderOption = useMemo(
- () =>
- roomCanSetLeader &&
- userCanSetLeader && {
- label: t(isLeader ? 'Remove_as_leader' : 'Set_as_leader'),
- icon: 'shield-alt',
- action: changeLeaderAction,
- },
- [isLeader, roomCanSetLeader, t, userCanSetLeader, changeLeaderAction],
- );
-
- const changeModeratorEndpoint = isModerator ? 'removeModerator' : 'addModerator';
- const changeModeratorMessage = isModerator
- ? 'User__username__removed_from__room_name__moderators'
- : 'User__username__is_now_a_moderator_of__room_name_';
- const changeModerator = useEndpointActionExperimental(
- 'POST',
- `${endpointPrefix}.${changeModeratorEndpoint}`,
- t(changeModeratorMessage, { username: user.username, room_name: roomName }),
- );
- const changeModeratorAction = useMutableCallback(() => changeModerator({ roomId: rid, userId: uid }));
- const changeModeratorOption = useMemo(
- () =>
- roomCanSetModerator &&
- userCanSetModerator && {
- label: t(isModerator ? 'Remove_as_moderator' : 'Set_as_moderator'),
- icon: 'shield',
- action: changeModeratorAction,
- },
- [changeModeratorAction, isModerator, roomCanSetModerator, t, userCanSetModerator],
- );
-
- const ignoreUser = useMethod('ignoreUser');
- const ignoreUserAction = useMutableCallback(async () => {
- try {
- await ignoreUser({ rid, userId: uid, ignore: !isIgnored });
- if (isIgnored) {
- dispatchToastMessage({ type: 'success', message: t('User_has_been_unignored') });
- } else {
- dispatchToastMessage({ type: 'success', message: t('User_has_been_ignored') });
- }
- } catch (error) {
- dispatchToastMessage({ type: 'error', message: error });
- }
- });
- const ignoreUserOption = useMemo(
- () =>
- roomCanIgnore &&
- uid !== ownUserId && {
- label: t(isIgnored ? 'Unignore' : 'Ignore'),
- icon: 'ban',
- action: ignoreUserAction,
- },
- [ignoreUserAction, isIgnored, ownUserId, roomCanIgnore, t, uid],
- );
-
- const isUserBlocked = currentSubscription && currentSubscription.blocker;
- const toggleBlock = useMethod(isUserBlocked ? 'unblockUser' : 'blockUser');
- const toggleBlockUserAction = useMutableCallback(async () => {
- try {
- await toggleBlock({ rid, blocked: uid });
- dispatchToastMessage({
- type: 'success',
- message: t(isUserBlocked ? 'User_is_unblocked' : 'User_is_blocked'),
- });
- } catch (error) {
- dispatchToastMessage({ type: 'error', message: error });
- }
- });
- const toggleBlockUserOption = useMemo(
- () =>
- roomCanBlock &&
- uid !== ownUserId && {
- label: t(isUserBlocked ? 'Unblock' : 'Block'),
- icon: 'ban',
- action: toggleBlockUserAction,
- },
- [isUserBlocked, ownUserId, roomCanBlock, t, toggleBlockUserAction, uid],
- );
-
- const muteFn = useMethod(isMuted ? 'unmuteUserInRoom' : 'muteUserInRoom');
- const muteUserOption = useMemo(() => {
- const action = () => {
- const onConfirm = async () => {
- try {
- await muteFn({ rid, username: user.username });
- closeModal();
- dispatchToastMessage({
- type: 'success',
- message: t(isMuted ? 'User__username__unmuted_in_room__roomName__' : 'User__username__muted_in_room__roomName__', {
- username: user.username,
- roomName,
- }),
- });
- } catch (error) {
- dispatchToastMessage({ type: 'error', message: error });
- }
- };
-
- if (isMuted) {
- return onConfirm();
- }
-
- setModal(
- ,
- );
- };
-
- return (
- roomCanMute &&
- userCanMute && {
- label: t(isMuted ? 'Unmute_user' : 'Mute_user'),
- icon: isMuted ? 'mic' : 'mic-off',
- action,
- }
- );
- }, [closeModal, dispatchToastMessage, isMuted, muteFn, rid, roomCanMute, roomName, setModal, t, user.username, userCanMute]);
-
- const removeFromTeam = useEndpointActionExperimental('POST', 'teams.removeMember', t('User_has_been_removed_from_team'));
-
- const removeUserAction = useEndpointActionExperimental('POST', `${endpointPrefix}.kick`, t('User_has_been_removed_from_s', roomName));
- const removeUserOptionAction = useMutableCallback(() => {
- if (room.teamMain && room.teamId) {
- return setModal(
- {
- const roomKeys = Object.keys(rooms);
- await removeFromTeam({
- teamId: room.teamId,
- userId: uid,
- ...(roomKeys.length && { rooms: roomKeys }),
- });
- closeModal();
- reload && reload();
- }}
- />,
- );
- }
-
- setModal(
- {
- await removeUserAction({ roomId: rid, userId: uid });
- closeModal();
- reload && reload();
- }}
- />,
- );
- });
-
- const removeUserOption = useMemo(
- () =>
- roomCanRemove &&
- userCanRemove && {
- label: {room.teamMain ? t('Remove_from_team') : t('Remove_from_room')},
- icon: 'sign-out',
- action: removeUserOptionAction,
- },
- [room, roomCanRemove, userCanRemove, removeUserOptionAction, t],
- );
-
- return useMemo(
- () => ({
- ...(openDirectMessageOption && { openDirectMessage: openDirectMessageOption }),
- ...(videoCallOption && { video: videoCallOption }),
- ...(audioCallOption && { audio: audioCallOption }),
- ...(changeOwnerOption && { changeOwner: changeOwnerOption }),
- ...(changeLeaderOption && { changeLeader: changeLeaderOption }),
- ...(changeModeratorOption && { changeModerator: changeModeratorOption }),
- ...(ignoreUserOption && { ignoreUser: ignoreUserOption }),
- ...(muteUserOption && { muteUser: muteUserOption }),
- ...(removeUserOption && { removeUser: removeUserOption }),
- ...(toggleBlockUserOption && { toggleBlock: toggleBlockUserOption }),
- }),
- [
- audioCallOption,
- changeLeaderOption,
- changeModeratorOption,
- changeOwnerOption,
- ignoreUserOption,
- muteUserOption,
- openDirectMessageOption,
- removeUserOption,
- videoCallOption,
- toggleBlockUserOption,
- ],
- );
-};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useAudioCallAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useAudioCallAction.ts
new file mode 100644
index 000000000000..db92ce2f6895
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useAudioCallAction.ts
@@ -0,0 +1,33 @@
+import { IRoom } from '@rocket.chat/core-typings';
+import { useTranslation } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { Action } from '../../../../hooks/useActionSpread';
+import { useWebRTC } from '../../useWebRTC';
+
+export const useAudioCallAction = (rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const { shouldAllowCalls, callInProgress, joinCall, startCall } = useWebRTC(rid);
+
+ const audioCallOption = useMemo(() => {
+ const handleJoinCall = (): void => {
+ joinCall({ audio: true, video: false });
+ };
+
+ const handleStartCall = (): void => {
+ startCall({ audio: true, video: false });
+ };
+
+ const action = callInProgress ? handleJoinCall : handleStartCall;
+
+ return shouldAllowCalls
+ ? {
+ label: t(callInProgress ? 'Join_audio_call' : 'Start_audio_call'),
+ icon: 'mic',
+ action,
+ }
+ : undefined;
+ }, [callInProgress, shouldAllowCalls, t, joinCall, startCall]);
+
+ return audioCallOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useBlockUserAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useBlockUserAction.ts
new file mode 100644
index 000000000000..0a0574d6b762
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useBlockUserAction.ts
@@ -0,0 +1,51 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { useTranslation, useMethod, useToastMessageDispatch, useUserId, useUserSubscription, useUserRoom } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { Action } from '../../../../hooks/useActionSpread';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+
+export const useBlockUserAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+ const currentSubscription = useUserSubscription(rid);
+ const ownUserId = useUserId();
+ const { _id: uid } = user;
+ const room = useUserRoom(rid);
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const { roomCanBlock } = getRoomDirectives(room);
+
+ const isUserBlocked = currentSubscription?.blocker;
+ const toggleBlock = useMethod(isUserBlocked ? 'unblockUser' : 'blockUser');
+
+ const toggleBlockUserAction = useMutableCallback(async () => {
+ try {
+ await toggleBlock({ rid, blocked: uid });
+ dispatchToastMessage({
+ type: 'success',
+ message: t(isUserBlocked ? 'User_is_unblocked' : 'User_is_blocked'),
+ });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ });
+
+ const toggleBlockUserOption = useMemo(
+ () =>
+ roomCanBlock && uid !== ownUserId
+ ? {
+ label: t(isUserBlocked ? 'Unblock' : 'Block'),
+ icon: 'ban',
+ action: toggleBlockUserAction,
+ }
+ : undefined,
+ [isUserBlocked, ownUserId, roomCanBlock, t, toggleBlockUserAction, uid],
+ );
+
+ return toggleBlockUserOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts
new file mode 100644
index 000000000000..dddfc28397e3
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts
@@ -0,0 +1,53 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { escapeHTML } from '@rocket.chat/string-helpers';
+import { useTranslation, usePermission, useUserRoom } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { useEndpointActionExperimental } from '../../../../../hooks/useEndpointActionExperimental';
+import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
+import { Action } from '../../../../hooks/useActionSpread';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+import { useUserHasRoomRole } from '../../useUserHasRoomRole';
+
+// TODO: Remove endpoint concatenation
+export const useChangeLeaderAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const room = useUserRoom(rid);
+ const { _id: uid } = user;
+ const userCanSetLeader = usePermission('set-leader', rid);
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels';
+ const { roomCanSetLeader } = getRoomDirectives(room);
+ const isLeader = useUserHasRoomRole(uid, rid, 'leader');
+ const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room));
+
+ const changeLeaderEndpoint = isLeader ? 'removeLeader' : 'addLeader';
+ const changeLeaderMessage = isLeader
+ ? 'User__username__removed_from__room_name__leaders'
+ : 'User__username__is_now_a_leader_of__room_name_';
+ const changeLeader = useEndpointActionExperimental(
+ 'POST',
+ `${endpointPrefix}.${changeLeaderEndpoint}`,
+ // eslint-disable-next-line @typescript-eslint/camelcase
+ t(changeLeaderMessage, { username: user.username, room_name: roomName }),
+ );
+ const changeLeaderAction = useMutableCallback(() => changeLeader({ roomId: rid, userId: uid }));
+ const changeLeaderOption = useMemo(
+ () =>
+ roomCanSetLeader && userCanSetLeader
+ ? {
+ label: t(isLeader ? 'Remove_as_leader' : 'Set_as_leader'),
+ icon: 'shield-alt',
+ action: changeLeaderAction,
+ }
+ : undefined,
+ [isLeader, roomCanSetLeader, t, userCanSetLeader, changeLeaderAction],
+ );
+
+ return changeLeaderOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.ts
new file mode 100644
index 000000000000..0b32928789aa
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.ts
@@ -0,0 +1,54 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { escapeHTML } from '@rocket.chat/string-helpers';
+import { useTranslation, usePermission, useUserRoom } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { useEndpointActionExperimental } from '../../../../../hooks/useEndpointActionExperimental';
+import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
+import { Action } from '../../../../hooks/useActionSpread';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+import { useUserHasRoomRole } from '../../useUserHasRoomRole';
+
+// TODO: Remove endpoint concatenation
+export const useChangeModeratorAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const room = useUserRoom(rid);
+ const { _id: uid } = user;
+
+ const userCanSetModerator = usePermission('set-moderator', rid);
+ const isModerator = useUserHasRoomRole(uid, rid, 'moderator');
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels';
+ const { roomCanSetModerator } = getRoomDirectives(room);
+ const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room));
+
+ const changeModeratorEndpoint = isModerator ? 'removeModerator' : 'addModerator';
+ const changeModeratorMessage = isModerator
+ ? 'User__username__removed_from__room_name__moderators'
+ : 'User__username__is_now_a_moderator_of__room_name_';
+ const changeModerator = useEndpointActionExperimental(
+ 'POST',
+ `${endpointPrefix}.${changeModeratorEndpoint}`,
+ // eslint-disable-next-line @typescript-eslint/camelcase
+ t(changeModeratorMessage, { username: user.username, room_name: roomName }),
+ );
+ const changeModeratorAction = useMutableCallback(() => changeModerator({ roomId: rid, userId: uid }));
+ const changeModeratorOption = useMemo(
+ () =>
+ roomCanSetModerator && userCanSetModerator
+ ? {
+ label: t(isModerator ? 'Remove_as_moderator' : 'Set_as_moderator'),
+ icon: 'shield-blank',
+ action: changeModeratorAction,
+ }
+ : undefined,
+ [changeModeratorAction, isModerator, roomCanSetModerator, t, userCanSetModerator],
+ );
+
+ return changeModeratorOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx
new file mode 100644
index 000000000000..4e027c0c150d
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx
@@ -0,0 +1,53 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { escapeHTML } from '@rocket.chat/string-helpers';
+import { useTranslation, usePermission, useUserRoom } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { useEndpointActionExperimental } from '../../../../../hooks/useEndpointActionExperimental';
+import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
+import { Action } from '../../../../hooks/useActionSpread';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+import { useUserHasRoomRole } from '../../useUserHasRoomRole';
+
+// TODO: Remove endpoint concatenation
+export const useChangeOwnerAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const room = useUserRoom(rid);
+ const { _id: uid } = user;
+ const userCanSetOwner = usePermission('set-owner', rid);
+ const isOwner = useUserHasRoomRole(uid, rid, 'owner');
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels';
+ const { roomCanSetOwner } = getRoomDirectives(room);
+ const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room));
+
+ const changeOwnerEndpoint = isOwner ? 'removeOwner' : 'addOwner';
+ const changeOwnerMessage = isOwner ? 'User__username__removed_from__room_name__owners' : 'User__username__is_now_a_owner_of__room_name_';
+
+ const changeOwner = useEndpointActionExperimental(
+ 'POST',
+ `${endpointPrefix}.${changeOwnerEndpoint}`,
+ // eslint-disable-next-line @typescript-eslint/camelcase
+ t(changeOwnerMessage, { username: user.username, room_name: roomName }),
+ );
+
+ const changeOwnerAction = useMutableCallback(async () => changeOwner({ roomId: rid, userId: uid }));
+ const changeOwnerOption = useMemo(
+ () =>
+ roomCanSetOwner && userCanSetOwner
+ ? {
+ label: t(isOwner ? 'Remove_as_owner' : 'Set_as_owner'),
+ icon: 'shield-check',
+ action: changeOwnerAction,
+ }
+ : undefined,
+ [changeOwnerAction, roomCanSetOwner, userCanSetOwner, isOwner, t],
+ );
+
+ return changeOwnerOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useDirectMessageAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useDirectMessageAction.ts
new file mode 100644
index 000000000000..07f07a78d328
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useDirectMessageAction.ts
@@ -0,0 +1,54 @@
+import { IRoom, IUser, ISubscription } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { useTranslation, usePermission, useRoute, useUserSubscription, useUserSubscriptionByName } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { Action } from '../../../../hooks/useActionSpread';
+
+const getShouldOpenDirectMessage = (
+ currentSubscription?: ISubscription,
+ usernameSubscription?: ISubscription,
+ canOpenDirectMessage?: boolean,
+ username?: IUser['username'],
+): boolean => {
+ const canOpenDm = canOpenDirectMessage || usernameSubscription;
+ const directMessageIsNotAlreadyOpen = currentSubscription && currentSubscription.name !== username;
+ return (canOpenDm && directMessageIsNotAlreadyOpen) ?? false;
+};
+
+export const useDirectMessageAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const usernameSubscription = useUserSubscriptionByName(user.username ?? '');
+ const currentSubscription = useUserSubscription(rid);
+ const canOpenDirectMessage = usePermission('create-d');
+ const directRoute = useRoute('direct');
+
+ const shouldOpenDirectMessage = getShouldOpenDirectMessage(
+ currentSubscription,
+ usernameSubscription,
+ canOpenDirectMessage,
+ user.username,
+ );
+
+ const openDirectMessage = useMutableCallback(
+ () =>
+ user.username &&
+ directRoute.push({
+ rid: user.username,
+ }),
+ );
+
+ const openDirectMessageOption = useMemo(
+ () =>
+ shouldOpenDirectMessage
+ ? {
+ label: t('Direct_Message'),
+ icon: 'balloon',
+ action: openDirectMessage,
+ }
+ : undefined,
+ [openDirectMessage, shouldOpenDirectMessage, t],
+ );
+
+ return openDirectMessageOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useIgnoreUserAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useIgnoreUserAction.ts
new file mode 100644
index 000000000000..8147c461ef8d
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useIgnoreUserAction.ts
@@ -0,0 +1,52 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { useTranslation, useMethod, useUserSubscription, useUserRoom, useUserId, useToastMessageDispatch } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { Action } from '../../../../hooks/useActionSpread';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+
+export const useIgnoreUserAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const room = useUserRoom(rid);
+ const { _id: uid } = user;
+ const ownUserId = useUserId();
+ const dispatchToastMessage = useToastMessageDispatch();
+ const currentSubscription = useUserSubscription(rid);
+ const ignoreUser = useMethod('ignoreUser');
+
+ const isIgnored = currentSubscription?.ignored && currentSubscription.ignored.indexOf(uid) > -1;
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const { roomCanIgnore } = getRoomDirectives(room);
+
+ const ignoreUserAction = useMutableCallback(async () => {
+ try {
+ await ignoreUser({ rid, userId: uid, ignore: !isIgnored });
+ if (isIgnored) {
+ dispatchToastMessage({ type: 'success', message: t('User_has_been_unignored') });
+ } else {
+ dispatchToastMessage({ type: 'success', message: t('User_has_been_ignored') });
+ }
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ });
+
+ const ignoreUserOption = useMemo(
+ () =>
+ roomCanIgnore && uid !== ownUserId
+ ? {
+ label: t(isIgnored ? 'Unignore' : 'Ignore'),
+ icon: 'ban',
+ action: ignoreUserAction,
+ }
+ : undefined,
+ [ignoreUserAction, isIgnored, ownUserId, roomCanIgnore, t, uid],
+ );
+
+ return ignoreUserOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useMuteUserAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useMuteUserAction.tsx
new file mode 100644
index 000000000000..b3fc6fe926b4
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useMuteUserAction.tsx
@@ -0,0 +1,119 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { escapeHTML } from '@rocket.chat/string-helpers';
+import {
+ useAllPermissions,
+ usePermission,
+ useSetModal,
+ useMethod,
+ useToastMessageDispatch,
+ useTranslation,
+ useUserRoom,
+} from '@rocket.chat/ui-contexts';
+import React, { useMemo } from 'react';
+
+import GenericModal from '../../../../../components/GenericModal';
+import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
+import { Action } from '../../../../hooks/useActionSpread';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+
+const getUserIsMuted = (
+ user: Pick,
+ room: IRoom | undefined,
+ userCanPostReadonly: boolean,
+): boolean | undefined => {
+ if (room?.ro) {
+ if (Array.isArray(room.unmuted) && room.unmuted.indexOf(user.username ?? '') !== -1) {
+ return false;
+ }
+
+ if (userCanPostReadonly) {
+ return Array.isArray(room.muted) && room.muted.indexOf(user.username ?? '') !== -1;
+ }
+
+ return true;
+ }
+
+ return room && Array.isArray(room.muted) && room.muted.indexOf(user.username ?? '') > -1;
+};
+
+export const useMuteUserAction = (user: Pick, rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const room = useUserRoom(rid);
+ const userCanMute = usePermission('mute-user', rid);
+ const dispatchToastMessage = useToastMessageDispatch();
+ const setModal = useSetModal();
+ const closeModal = useMutableCallback(() => setModal(null));
+ const otherUserCanPostReadonly = useAllPermissions(
+ useMemo(() => ['post-readonly'], []),
+ rid,
+ );
+
+ const isMuted = getUserIsMuted(user, room, otherUserCanPostReadonly);
+ const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room));
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const { roomCanMute } = getRoomDirectives(room);
+
+ const mutedMessage = isMuted ? 'User__username__unmuted_in_room__roomName__' : 'User__username__muted_in_room__roomName__';
+
+ const muteUser = useMethod(isMuted ? 'unmuteUserInRoom' : 'muteUserInRoom');
+
+ const muteUserOption = useMemo(() => {
+ const action = (): Promise | void => {
+ const onConfirm = async (): Promise => {
+ try {
+ await muteUser({ rid, username: user.username });
+
+ return dispatchToastMessage({
+ type: 'success',
+ message: t(mutedMessage, {
+ username: user.username,
+ roomName,
+ }),
+ });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ closeModal();
+ }
+ };
+
+ if (isMuted) {
+ return onConfirm();
+ }
+
+ return setModal(
+
+ {t('The_user_wont_be_able_to_type_in_s', roomName)}
+ ,
+ );
+ };
+
+ return roomCanMute && userCanMute
+ ? {
+ label: t(isMuted ? 'Unmute_user' : 'Mute_user'),
+ icon: isMuted ? 'mic' : 'mic-off',
+ action,
+ }
+ : undefined;
+ }, [
+ closeModal,
+ mutedMessage,
+ dispatchToastMessage,
+ isMuted,
+ muteUser,
+ rid,
+ roomCanMute,
+ roomName,
+ setModal,
+ t,
+ user.username,
+ userCanMute,
+ ]);
+
+ return muteUserOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
new file mode 100644
index 000000000000..6bc41e267db0
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
@@ -0,0 +1,92 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { Box, Icon } from '@rocket.chat/fuselage';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { escapeHTML } from '@rocket.chat/string-helpers';
+import { usePermission, useSetModal, useTranslation, useUserRoom } from '@rocket.chat/ui-contexts';
+import React, { useMemo } from 'react';
+
+import GenericModal from '../../../../../components/GenericModal';
+import { useEndpointActionExperimental } from '../../../../../hooks/useEndpointActionExperimental';
+import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
+import { Action } from '../../../../hooks/useActionSpread';
+import RemoveUsersModal from '../../../../teams/contextualBar/members/RemoveUsersModal';
+import { getRoomDirectives } from '../../../lib/getRoomDirectives';
+
+// TODO: Remove endpoint concatenation
+export const useRemoveUserAction = (user: Pick, rid: IRoom['_id'], reload?: () => void): Action | undefined => {
+ const t = useTranslation();
+ const room = useUserRoom(rid);
+ const { _id: uid } = user;
+
+ const userCanRemove = usePermission('remove-user', rid);
+ const setModal = useSetModal();
+ const closeModal = useMutableCallback(() => setModal(null));
+ const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room));
+
+ if (!room) {
+ throw Error('Room not provided');
+ }
+
+ const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels';
+ const { roomCanRemove } = getRoomDirectives(room);
+
+ const removeFromTeam = useEndpointActionExperimental('POST', '/v1/teams.removeMember', t('User_has_been_removed_from_team'));
+ const removeFromRoom = useEndpointActionExperimental('POST', `${endpointPrefix}.kick`, t('User_has_been_removed_from_s', roomName));
+
+ const removeUserOptionAction = useMutableCallback(() => {
+ const handleRemoveFromTeam = async (rooms: IRoom[]): Promise => {
+ if (room.teamId) {
+ const roomKeys = Object.keys(rooms);
+ await removeFromTeam({
+ teamId: room.teamId,
+ userId: uid,
+ ...(roomKeys.length && { rooms: roomKeys }),
+ });
+ closeModal();
+ reload?.();
+ }
+ };
+
+ const handleRemoveFromRoom = async (rid: IRoom['_id'], uid: IUser['_id']): Promise => {
+ await removeFromRoom({ roomId: rid, userId: uid });
+ closeModal();
+ reload?.();
+ };
+
+ if (room.teamMain && room.teamId) {
+ return setModal(
+ ,
+ );
+ }
+
+ setModal(
+ => handleRemoveFromRoom(rid, uid)}
+ >
+ {t('The_user_will_be_removed_from_s', roomName)}
+ ,
+ );
+ });
+
+ const removeUserOption = useMemo(
+ () =>
+ roomCanRemove && userCanRemove
+ ? {
+ label: (
+
+
+ {room?.teamMain ? t('Remove_from_team') : t('Remove_from_room')}
+
+ ),
+ action: removeUserOptionAction,
+ }
+ : undefined,
+ [room, roomCanRemove, userCanRemove, removeUserOptionAction, t],
+ );
+
+ return removeUserOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx
new file mode 100644
index 000000000000..6a3fafcd8dbe
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx
@@ -0,0 +1,33 @@
+import { IRoom } from '@rocket.chat/core-typings';
+import { useTranslation } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+import { Action } from '../../../../hooks/useActionSpread';
+import { useWebRTC } from '../../useWebRTC';
+
+export const useVideoCallAction = (rid: IRoom['_id']): Action | undefined => {
+ const t = useTranslation();
+ const { shouldAllowCalls, callInProgress, joinCall, startCall } = useWebRTC(rid);
+
+ const videoCallOption = useMemo(() => {
+ const handleJoinCall = (): void => {
+ joinCall({ audio: true, video: true });
+ };
+
+ const handleStartCall = (): void => {
+ startCall({ audio: true, video: true });
+ };
+
+ const action = callInProgress ? handleJoinCall : handleStartCall;
+
+ return shouldAllowCalls
+ ? {
+ label: t(callInProgress ? 'Join_video_call' : 'Start_video_call'),
+ icon: 'video',
+ action,
+ }
+ : undefined;
+ }, [callInProgress, shouldAllowCalls, t, joinCall, startCall]);
+
+ return videoCallOption;
+};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/index.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/index.ts
new file mode 100644
index 000000000000..44ce5ef6c2da
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/index.ts
@@ -0,0 +1 @@
+export { useUserInfoActions } from './useUserInfoActions';
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts
new file mode 100644
index 000000000000..f54586c0a18e
--- /dev/null
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts
@@ -0,0 +1,60 @@
+import { IRoom, IUser } from '@rocket.chat/core-typings';
+import { useMemo } from 'react';
+
+import { Action } from '../../../hooks/useActionSpread';
+import { useAudioCallAction } from './actions/useAudioCallAction';
+import { useBlockUserAction } from './actions/useBlockUserAction';
+import { useChangeLeaderAction } from './actions/useChangeLeaderAction';
+import { useChangeModeratorAction } from './actions/useChangeModeratorAction';
+import { useChangeOwnerAction } from './actions/useChangeOwnerAction';
+import { useDirectMessageAction } from './actions/useDirectMessageAction';
+import { useIgnoreUserAction } from './actions/useIgnoreUserAction';
+import { useMuteUserAction } from './actions/useMuteUserAction';
+import { useRemoveUserAction } from './actions/useRemoveUserAction';
+import { useVideoCallAction } from './actions/useVideoCallAction';
+
+export const useUserInfoActions = (
+ user: Pick,
+ rid: IRoom['_id'],
+ reload?: () => void,
+): {
+ [key: string]: Action;
+} => {
+ const audioCallOption = useAudioCallAction(rid);
+ const blockUserOption = useBlockUserAction(user, rid);
+ const changeLeaderOption = useChangeLeaderAction(user, rid);
+ const changeModeratorOption = useChangeModeratorAction(user, rid);
+ const changeOwnerOption = useChangeOwnerAction(user, rid);
+ const openDirectMessageOption = useDirectMessageAction(user, rid);
+ const ignoreUserOption = useIgnoreUserAction(user, rid);
+ const muteUserOption = useMuteUserAction(user, rid);
+ const removeUserOption = useRemoveUserAction(user, rid, reload);
+ const videoCallOption = useVideoCallAction(rid);
+
+ return useMemo(
+ () => ({
+ ...(openDirectMessageOption && { openDirectMessage: openDirectMessageOption }),
+ ...(videoCallOption && { video: videoCallOption }),
+ ...(audioCallOption && { audio: audioCallOption }),
+ ...(changeOwnerOption && { changeOwner: changeOwnerOption }),
+ ...(changeLeaderOption && { changeLeader: changeLeaderOption }),
+ ...(changeModeratorOption && { changeModerator: changeModeratorOption }),
+ ...(ignoreUserOption && { ignoreUser: ignoreUserOption }),
+ ...(muteUserOption && { muteUser: muteUserOption }),
+ ...(blockUserOption && { toggleBlock: blockUserOption }),
+ ...(removeUserOption && { removeUser: removeUserOption }),
+ }),
+ [
+ audioCallOption,
+ changeLeaderOption,
+ changeModeratorOption,
+ changeOwnerOption,
+ ignoreUserOption,
+ muteUserOption,
+ openDirectMessageOption,
+ removeUserOption,
+ videoCallOption,
+ blockUserOption,
+ ],
+ );
+};
diff --git a/apps/meteor/client/views/room/lib/getRoomDirectives.ts b/apps/meteor/client/views/room/lib/getRoomDirectives.ts
new file mode 100644
index 000000000000..2f5602f02d2b
--- /dev/null
+++ b/apps/meteor/client/views/room/lib/getRoomDirectives.ts
@@ -0,0 +1,33 @@
+import { IRoom } from '@rocket.chat/core-typings';
+
+import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig';
+import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
+
+type getRoomDirectiesType = {
+ roomCanSetOwner: boolean;
+ roomCanSetLeader: boolean;
+ roomCanSetModerator: boolean;
+ roomCanIgnore: boolean;
+ roomCanBlock: boolean;
+ roomCanMute: boolean;
+ roomCanRemove: boolean;
+};
+
+export const getRoomDirectives = (room: IRoom): getRoomDirectiesType => {
+ const roomDirectives = room?.t && roomCoordinator.getRoomDirectives(room.t);
+
+ const [roomCanSetOwner, roomCanSetLeader, roomCanSetModerator, roomCanIgnore, roomCanBlock, roomCanMute, roomCanRemove] = [
+ ...((roomDirectives && [
+ roomDirectives.allowMemberAction(room, RoomMemberActions.SET_AS_OWNER),
+ roomDirectives.allowMemberAction(room, RoomMemberActions.SET_AS_LEADER),
+ roomDirectives.allowMemberAction(room, RoomMemberActions.SET_AS_MODERATOR),
+ roomDirectives.allowMemberAction(room, RoomMemberActions.IGNORE),
+ roomDirectives.allowMemberAction(room, RoomMemberActions.BLOCK),
+ roomDirectives.allowMemberAction(room, RoomMemberActions.MUTE),
+ roomDirectives.allowMemberAction(room, RoomMemberActions.REMOVE_USER),
+ ]) ??
+ []),
+ ];
+
+ return { roomCanSetOwner, roomCanSetLeader, roomCanSetModerator, roomCanIgnore, roomCanBlock, roomCanMute, roomCanRemove };
+};
diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
index 98cb59cc79fa..7396656497f0 100644
--- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -670,6 +670,7 @@
"Better": "Better",
"Bio": "Bio",
"Bio_Placeholder": "Bio Placeholder",
+ "Block": "Block",
"Block_Multiple_Failed_Logins_Attempts_Until_Block_By_Ip": "How many failed attempts until block by IP",
"Block_Multiple_Failed_Logins_Attempts_Until_Block_by_User": "How many failed attempts until block by User",
"Block_Multiple_Failed_Logins_By_Ip": "Block failed login attempts by IP",
@@ -4547,6 +4548,7 @@
"unarchive-room_description": "Permission to unarchive channels",
"Unassigned": "Unassigned",
"Unavailable": "Unavailable",
+ "Unblock": "Unblock",
"Unblock_User": "Unblock User",
"Uncheck_All": "Uncheck All",
"Uncollapse": "Uncollapse",
diff --git a/packages/core-typings/src/ISubscription.ts b/packages/core-typings/src/ISubscription.ts
index 3dcdccd2fa95..7b140a775e3c 100644
--- a/packages/core-typings/src/ISubscription.ts
+++ b/packages/core-typings/src/ISubscription.ts
@@ -56,7 +56,7 @@ export interface ISubscription extends IRocketChatRecord {
autoTranslateLanguage?: string;
disableNotifications?: boolean;
muteGroupMentions?: boolean;
- ignored?: unknown;
+ ignored?: IUser['_id'][];
department?: unknown;
diff --git a/packages/rest-typings/src/v1/groups.ts b/packages/rest-typings/src/v1/groups.ts
index 7fc73cbe7440..9ab08be043d4 100644
--- a/packages/rest-typings/src/v1/groups.ts
+++ b/packages/rest-typings/src/v1/groups.ts
@@ -381,4 +381,22 @@ export type GroupsEndpoints = {
messages: IMessage[];
}>;
};
+ '/v1/groups.addModerator': {
+ POST: (params: { roomId: string; userId: string }) => {};
+ };
+ '/v1/groups.removeModerator': {
+ POST: (params: { roomId: string; userId: string }) => {};
+ };
+ '/v1/groups.addOwner': {
+ POST: (params: { roomId: string; userId: string }) => {};
+ };
+ '/v1/groups.removeOwner': {
+ POST: (params: { roomId: string; userId: string }) => {};
+ };
+ '/v1/groups.addLeader': {
+ POST: (params: { roomId: string; userId: string }) => {};
+ };
+ '/v1/groups.removeLeader': {
+ POST: (params: { roomId: string; userId: string }) => {};
+ };
};
diff --git a/packages/ui-contexts/src/hooks/useUserSubscriptionByName.ts b/packages/ui-contexts/src/hooks/useUserSubscriptionByName.ts
index 8ec78f325c56..716ae5fb1108 100644
--- a/packages/ui-contexts/src/hooks/useUserSubscriptionByName.ts
+++ b/packages/ui-contexts/src/hooks/useUserSubscriptionByName.ts
@@ -4,7 +4,7 @@ import { useSubscription } from 'use-subscription';
import { Fields, Sort, UserContext } from '../UserContext';
-export const useUserSubscriptionByName = (name: string, fields: Fields, sort?: Sort): ISubscription | undefined => {
+export const useUserSubscriptionByName = (name: string, fields?: Fields, sort?: Sort): ISubscription | undefined => {
const { querySubscription } = useContext(UserContext);
const subscription = useMemo(() => querySubscription({ name }, fields, sort), [querySubscription, name, fields, sort]);
return useSubscription(subscription);