diff --git a/.changeset/healthy-coins-scream.md b/.changeset/healthy-coins-scream.md new file mode 100644 index 0000000000000000000000000000000000000000..ede00272b49c9678f5ce1c8796cbe0ca58a8930f --- /dev/null +++ b/.changeset/healthy-coins-scream.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue where changing user role in a room displays the wrong message diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts index f97ae7dab10b153c3800a535aa667dfed5790c0e..feee1e61832f332f442788e5a092e0cfe9e644bf 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeLeaderAction.ts @@ -1,19 +1,34 @@ import type { IRoom, IUser } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useTranslation, usePermission, useUserRoom, useUserSubscription } from '@rocket.chat/ui-contexts'; -import { useMemo } from 'react'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { + useTranslation, + usePermission, + useUserRoom, + useUserSubscription, + useEndpoint, + useToastMessageDispatch, +} from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; -import { useEndpointAction } from '../../../../../hooks/useEndpointAction'; import { getRoomDirectives } from '../../../lib/getRoomDirectives'; import { useUserHasRoomRole } from '../../useUserHasRoomRole'; import type { UserInfoAction, UserInfoActionType } from '../useUserInfoActions'; +const getEndpoint = (roomType: string, isLeader: boolean) => { + if (roomType === 'p') { + return isLeader ? '/v1/groups.removeLeader' : '/v1/groups.addLeader'; + } + return isLeader ? '/v1/channels.removeLeader' : '/v1/channels.addLeader'; +}; + export const useChangeLeaderAction = (user: Pick<IUser, '_id' | 'username'>, rid: IRoom['_id']): UserInfoAction | undefined => { const t = useTranslation(); const room = useUserRoom(rid); - const { _id: uid } = user; + const { _id: uid, username } = user; const userCanSetLeader = usePermission('set-leader', rid); const userSubscription = useUserSubscription(rid); + const dispatchToastMessage = useToastMessageDispatch(); if (!room) { throw Error('Room not provided'); @@ -22,13 +37,27 @@ export const useChangeLeaderAction = (user: Pick<IUser, '_id' | 'username'>, rid const { roomCanSetLeader } = getRoomDirectives({ room, showingUserId: uid, userSubscription }); const isLeader = useUserHasRoomRole(uid, rid, 'leader'); - const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels'; - const changeLeaderEndpoint = isLeader ? 'removeLeader' : 'addLeader'; - const changeLeaderMessage = isLeader ? 'removed__username__as__role_' : 'set__username__as__role_'; - const changeLeader = useEndpointAction('POST', `${endpointPrefix}.${changeLeaderEndpoint}`, { - successMessage: t(changeLeaderMessage, { username: user.username, role: 'leader' }), + const toggleLeaderEndpoint = useEndpoint('POST', getEndpoint(room.t, isLeader)); + + const toggleOwnerMutation = useMutation({ + mutationFn: useCallback( + async ({ roomId, userId }) => { + await toggleLeaderEndpoint({ roomId, userId }); + + return t(isLeader ? 'removed__username__as__role_' : 'set__username__as__role_', { username, role: 'leader' }); + }, + [t, isLeader, toggleLeaderEndpoint, username], + ), + onSuccess: (message) => { + dispatchToastMessage({ type: 'success', message }); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, }); - const changeLeaderAction = useMutableCallback(() => changeLeader({ roomId: rid, userId: uid })); + + const changeLeaderAction = useEffectEvent(async () => toggleOwnerMutation.mutateAsync({ roomId: rid, userId: uid })); + const changeLeaderOption = useMemo( () => roomCanSetLeader && userCanSetLeader diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.tsx index 095695b160c1cb909f98ce56bd624a94595b7531..9a50a63567fe15721c78e74aa2fc05add90c88aa 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.tsx +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeModeratorAction.tsx @@ -1,13 +1,22 @@ import type { IRoom, IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { useTranslation, usePermission, useUserRoom, useUserSubscription, useUser, useSetModal } from '@rocket.chat/ui-contexts'; +import { + useTranslation, + usePermission, + useUserRoom, + useUserSubscription, + useUser, + useSetModal, + useEndpoint, + useToastMessageDispatch, +} from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { useCallback, useMemo } from 'react'; import GenericModal from '../../../../../components/GenericModal'; -import { useEndpointAction } from '../../../../../hooks/useEndpointAction'; import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator'; import { getRoomDirectives } from '../../../lib/getRoomDirectives'; import { useUserHasRoomRole } from '../../useUserHasRoomRole'; @@ -32,6 +41,13 @@ const getWarningModalForFederatedRooms = ( </GenericModal> ); +const getEndpoint = (roomType: string, isModerator: boolean) => { + if (roomType === 'p') { + return isModerator ? '/v1/groups.removeModerator' : '/v1/groups.addModerator'; + } + return isModerator ? '/v1/channels.removeModerator' : '/v1/channels.addModerator'; +}; + export const useChangeModeratorAction = (user: Pick<IUser, '_id' | 'username'>, rid: IRoom['_id']): UserInfoAction | undefined => { const t = useTranslation(); const room = useUserRoom(rid); @@ -44,7 +60,7 @@ export const useChangeModeratorAction = (user: Pick<IUser, '_id' | 'username'>, const loggedUserIsModerator = useUserHasRoomRole(loggedUserId, rid, 'moderator'); const loggedUserIsOwner = useUserHasRoomRole(loggedUserId, rid, 'owner'); const setModal = useSetModal(); - const closeModal = useCallback(() => setModal(null), [setModal]); + const dispatchToastMessage = useToastMessageDispatch(); if (!room) { throw Error('Room not provided'); @@ -53,27 +69,42 @@ export const useChangeModeratorAction = (user: Pick<IUser, '_id' | 'username'>, const { roomCanSetModerator } = getRoomDirectives({ room, showingUserId: uid, userSubscription }); const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room)); - const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels'; - 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 = useEndpointAction('POST', `${endpointPrefix}.${changeModeratorEndpoint}`, { - successMessage: t(changeModeratorMessage, { username: user.username, room_name: roomName }), + const toggleModeratorEndpoint = useEndpoint('POST', getEndpoint(room.t, isModerator)); + const toggleModerator = useMutation({ + mutationFn: useCallback( + async ({ roomId, userId }) => { + await toggleModeratorEndpoint({ roomId, userId }); + + return t( + isModerator ? 'User__username__removed_from__room_name__moderators' : 'User__username__is_now_a_moderator_of__room_name_', + { + username: user.username, + room_name: roomName, + }, + ); + }, + [toggleModeratorEndpoint, t, isModerator, user.username, roomName], + ), + onSuccess: (message) => { + dispatchToastMessage({ type: 'success', message }); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, }); - const handleConfirm = useCallback(() => { - changeModerator({ roomId: rid, userId: uid }); - closeModal(); - }, [changeModerator, rid, uid, closeModal]); - const handleChangeModerator = useCallback( ({ userId }) => { if (!isRoomFederated(room)) { - return changeModerator({ roomId: rid, userId: uid }); + return toggleModerator.mutateAsync({ roomId: rid, userId: uid }); } + const closeModal = () => setModal(null); + const handleConfirm = async () => { + await toggleModerator.mutateAsync({ roomId: rid, userId: uid }); + closeModal(); + }; + const changingOwnRole = userId === loggedUserId; if (changingOwnRole && loggedUserIsModerator) { return setModal(() => @@ -111,12 +142,13 @@ export const useChangeModeratorAction = (user: Pick<IUser, '_id' | 'username'>, ); } - changeModerator({ roomId: rid, userId: uid }); + toggleModerator.mutateAsync({ roomId: rid, userId: uid }); }, - [setModal, loggedUserId, loggedUserIsModerator, loggedUserIsOwner, t, rid, uid, changeModerator, closeModal, handleConfirm, room], + [setModal, loggedUserId, loggedUserIsModerator, loggedUserIsOwner, t, rid, uid, toggleModerator, room], ); - const changeModeratorAction = useMutableCallback(() => handleChangeModerator({ roomId: rid, userId: uid })); + const changeModeratorAction = useEffectEvent(() => handleChangeModerator({ roomId: rid, userId: uid })); + const changeModeratorOption = useMemo( () => (isRoomFederated(room) && roomCanSetModerator) || (!isRoomFederated(room) && roomCanSetModerator && userCanSetModerator) diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx index 5894138d87f446ee2d2dcacd0330c036ac575ed6..d550a43efd1fbf51c73454e224fbb285460a46eb 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useChangeOwnerAction.tsx @@ -1,13 +1,22 @@ import type { IRoom, IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { useTranslation, usePermission, useUserRoom, useUserSubscription, useSetModal, useUser } from '@rocket.chat/ui-contexts'; +import { + useTranslation, + usePermission, + useUserRoom, + useUserSubscription, + useSetModal, + useUser, + useToastMessageDispatch, + useEndpoint, +} from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { useCallback, useMemo } from 'react'; import GenericModal from '../../../../../components/GenericModal'; -import { useEndpointAction } from '../../../../../hooks/useEndpointAction'; import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator'; import { getRoomDirectives } from '../../../lib/getRoomDirectives'; import { useUserHasRoomRole } from '../../useUserHasRoomRole'; @@ -32,17 +41,24 @@ const getWarningModalForFederatedRooms = ( </GenericModal> ); +const getEndpoint = (roomType: string, isOwner: boolean) => { + if (roomType === 'p') { + return isOwner ? '/v1/groups.removeOwner' : '/v1/groups.addOwner'; + } + return isOwner ? '/v1/channels.removeOwner' : '/v1/channels.addOwner'; +}; + export const useChangeOwnerAction = (user: Pick<IUser, '_id' | 'username'>, rid: IRoom['_id']): UserInfoAction | undefined => { const t = useTranslation(); const room = useUserRoom(rid); - const { _id: uid } = user; + const { _id: uid, username } = user; const userCanSetOwner = usePermission('set-owner', rid); const isOwner = useUserHasRoomRole(uid, rid, 'owner'); const userSubscription = useUserSubscription(rid); const setModal = useSetModal(); const { _id: loggedUserId = '' } = useUser() || {}; const loggedUserIsOwner = useUserHasRoomRole(loggedUserId, rid, 'owner'); - const closeModal = useCallback(() => setModal(null), [setModal]); + const dispatchToastMessage = useToastMessageDispatch(); if (!room) { throw Error('Room not provided'); @@ -51,55 +67,73 @@ export const useChangeOwnerAction = (user: Pick<IUser, '_id' | 'username'>, rid: const { roomCanSetOwner } = getRoomDirectives({ room, showingUserId: uid, userSubscription }); const roomName = room?.t && escapeHTML(roomCoordinator.getRoomName(room.t, room)); - const endpointPrefix = room.t === 'p' ? '/v1/groups' : '/v1/channels'; - const changeOwnerEndpoint = isOwner ? 'removeOwner' : 'addOwner'; - const changeOwnerMessage = isOwner ? 'User__username__removed_from__room_name__owners' : 'User__username__is_now_an_owner_of__room_name_'; - const changeOwner = useEndpointAction('POST', `${endpointPrefix}.${changeOwnerEndpoint}`, { - successMessage: t(changeOwnerMessage, { username: user.username, room_name: roomName }), - }); + const toggleOwnerEndpoint = useEndpoint('POST', getEndpoint(room.t, isOwner)); - const handleConfirm = useCallback(() => { - changeOwner({ roomId: rid, userId: uid }); - closeModal(); - }, [changeOwner, rid, uid, closeModal]); - - const handleChangeOwner = useCallback( - ({ userId }) => { - if (!isRoomFederated(room)) { - return changeOwner({ roomId: rid, userId: uid }); - } - const changingOwnRole = userId === loggedUserId; - - if (changingOwnRole && loggedUserIsOwner) { - return setModal(() => - getWarningModalForFederatedRooms( - closeModal, - handleConfirm, - t('Federation_Matrix_losing_privileges'), - t('Yes_continue'), - t('Federation_Matrix_losing_privileges_warning'), - ), - ); - } - - if (!changingOwnRole && loggedUserIsOwner) { - return setModal(() => - getWarningModalForFederatedRooms( - closeModal, - handleConfirm, - t('Warning'), - t('Yes_continue'), - t('Federation_Matrix_giving_same_permission_warning'), - ), - ); - } - - changeOwner({ roomId: rid, userId: uid }); + const toggleOwnerMutation = useMutation({ + mutationFn: useCallback( + async ({ roomId, userId }) => { + await toggleOwnerEndpoint({ roomId, userId }); + + return t(isOwner ? 'User__username__removed_from__room_name__owners' : 'User__username__is_now_an_owner_of__room_name_', { + username, + room_name: roomName, + }); + }, + [toggleOwnerEndpoint, t, isOwner, username, roomName], + ), + onSuccess: (message) => { + dispatchToastMessage({ type: 'success', message }); }, - [setModal, loggedUserId, loggedUserIsOwner, t, rid, uid, changeOwner, closeModal, handleConfirm, room], - ); + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + }); + + const handleChangeOwner = useCallback(() => { + if (!isRoomFederated(room)) { + return toggleOwnerMutation.mutateAsync({ roomId: rid, userId: uid }); + } + + const changingOwnRole = uid === loggedUserId; + + const closeModal = () => { + setModal(null); + }; + + const handleConfirm = () => { + toggleOwnerMutation.mutateAsync({ roomId: rid, userId: uid }); + closeModal(); + }; + + if (changingOwnRole && loggedUserIsOwner) { + return setModal(() => + getWarningModalForFederatedRooms( + closeModal, + handleConfirm, + t('Federation_Matrix_losing_privileges'), + t('Yes_continue'), + t('Federation_Matrix_losing_privileges_warning'), + ), + ); + } + + if (!changingOwnRole && loggedUserIsOwner) { + return setModal(() => + getWarningModalForFederatedRooms( + closeModal, + handleConfirm, + t('Warning'), + t('Yes_continue'), + t('Federation_Matrix_giving_same_permission_warning'), + ), + ); + } + + toggleOwnerMutation.mutateAsync({ roomId: rid, userId: uid }); + }, [room, loggedUserId, loggedUserIsOwner, toggleOwnerMutation, rid, uid, t, setModal]); + + const changeOwnerAction = useEffectEvent(async () => handleChangeOwner()); - const changeOwnerAction = useMutableCallback(async () => handleChangeOwner({ roomId: rid, userId: uid })); const changeOwnerOption = useMemo( () => (isRoomFederated(room) && roomCanSetOwner) || (!isRoomFederated(room) && roomCanSetOwner && userCanSetOwner)