From d73093eaddb5baf3616c7cb2b7043b58dc28ee1a Mon Sep 17 00:00:00 2001 From: juliajforesti Date: Thu, 30 Apr 2026 17:52:30 -0300 Subject: [PATCH 1/4] fix: current device logout logic to use useMutation to avoid error Co-authored-by: Copilot --- apps/meteor/client/hooks/useDeviceLogout.tsx | 59 +++++++++++++------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/apps/meteor/client/hooks/useDeviceLogout.tsx b/apps/meteor/client/hooks/useDeviceLogout.tsx index c13528d4d4c80..265f5f59a3901 100644 --- a/apps/meteor/client/hooks/useDeviceLogout.tsx +++ b/apps/meteor/client/hooks/useDeviceLogout.tsx @@ -1,10 +1,17 @@ import { GenericModal } from '@rocket.chat/ui-client'; -import { useSetModal, useToastMessageDispatch, useRoute, useRouteParameter } from '@rocket.chat/ui-contexts'; -import { useQueryClient } from '@tanstack/react-query'; -import { useCallback } from 'react'; +import { + useSetModal, + useToastMessageDispatch, + useRoute, + useRouteParameter, + useEndpoint, + useSessionDispatch, + UserContext, +} from '@rocket.chat/ui-contexts'; +import { useQueryClient, useMutation } from '@tanstack/react-query'; +import { useCallback, useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useEndpointMutation } from './useEndpointMutation'; import { deviceManagementQueryKeys } from '../lib/queryKeys'; export const useDeviceLogout = (sessionId: string, endpoint: '/v1/sessions/logout' | '/v1/sessions/logout.me'): (() => void) => { @@ -13,29 +20,40 @@ export const useDeviceLogout = (sessionId: string, endpoint: '/v1/sessions/logou const dispatchToastMessage = useToastMessageDispatch(); const deviceManagementRouter = useRoute('device-management'); const routeId = useRouteParameter('id'); + const setForceLogout = useSessionDispatch('forceLogout'); + const { logout } = useContext(UserContext); const queryClient = useQueryClient(); + const logoutEndpoint = useEndpoint('POST', endpoint); - const { mutateAsync: logoutDevice } = useEndpointMutation('POST', endpoint, { - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: deviceManagementQueryKeys.all }); - isContextualBarOpen && handleCloseContextualBar(); - dispatchToastMessage({ type: 'success', message: t('Device_Logged_Out') }); - }, - onSettled: () => { - setModal(null); - }, - }); + const isOwnSessionLogout = endpoint === '/v1/sessions/logout.me'; const handleCloseContextualBar = useCallback(() => deviceManagementRouter.push({}), [deviceManagementRouter]); - const isContextualBarOpen = routeId === sessionId; + const { mutate: logoutDevice } = useMutation({ + mutationFn: logoutEndpoint, + onSettled: () => { + if (isOwnSessionLogout) { + setModal(null); + logout(); + } else { + queryClient.invalidateQueries({ queryKey: deviceManagementQueryKeys.all }); + if (isContextualBarOpen) { + handleCloseContextualBar(); + } + dispatchToastMessage({ type: 'success', message: t('Device_Logged_Out') }); + setModal(null); + } + }, + throwOnError: false, + }); + return useCallback(() => { const closeModal = () => setModal(null); - const handleLogoutDevice = async () => { - await logoutDevice({ sessionId }); + const handleLogoutDevice = () => { + logoutDevice({ sessionId }); }; setModal( @@ -44,12 +62,15 @@ export const useDeviceLogout = (sessionId: string, endpoint: '/v1/sessions/logou variant='danger' confirmText={t('Logout_Device')} cancelText={t('Cancel')} - onConfirm={handleLogoutDevice} + onConfirm={() => { + setForceLogout(true); + handleLogoutDevice(); + }} onCancel={closeModal} onClose={closeModal} > {t('Device_Logout_Text')} , ); - }, [setModal, t, logoutDevice, sessionId]); + }, [setModal, t, logoutDevice, sessionId, setForceLogout]); }; From 9ec0f2879d65fcbbfc3609cfc311c744b4997f43 Mon Sep 17 00:00:00 2001 From: juliajforesti Date: Thu, 30 Apr 2026 17:53:33 -0300 Subject: [PATCH 2/4] chore: changeset --- .changeset/forty-stars-bathe.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/forty-stars-bathe.md diff --git a/.changeset/forty-stars-bathe.md b/.changeset/forty-stars-bathe.md new file mode 100644 index 0000000000000..00f21597c8a50 --- /dev/null +++ b/.changeset/forty-stars-bathe.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes error message being shown when logging out current device via Device Management despite successful logout. From 734181ced60473d16bf900eb0a0e7422420f53d2 Mon Sep 17 00:00:00 2001 From: juliajforesti Date: Thu, 7 May 2026 15:45:18 -0300 Subject: [PATCH 3/4] chore: add currentLoginToken to `aggregateSessionsByUserId` --- apps/meteor/ee/server/api/sessions.ts | 9 ++++++++- packages/model-typings/src/models/ISessionsModel.ts | 2 ++ packages/models/src/models/Sessions.ts | 11 +++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/meteor/ee/server/api/sessions.ts b/apps/meteor/ee/server/api/sessions.ts index 5c091864f3d5d..f4ea149c18a8c 100644 --- a/apps/meteor/ee/server/api/sessions.ts +++ b/apps/meteor/ee/server/api/sessions.ts @@ -95,7 +95,14 @@ API.v1.addRoute( return API.v1.failure('error-invalid-sort-keys'); } - const sessions = await Sessions.aggregateSessionsByUserId({ uid: this.userId, search, sort, offset, count }); + const sessions = await Sessions.aggregateSessionsByUserId({ + uid: this.userId, + search, + sort, + offset, + count, + currentLoginToken: this.token, + }); return API.v1.success(sessions); }, }, diff --git a/packages/model-typings/src/models/ISessionsModel.ts b/packages/model-typings/src/models/ISessionsModel.ts index 654658a209b26..1a068da3da6c9 100644 --- a/packages/model-typings/src/models/ISessionsModel.ts +++ b/packages/model-typings/src/models/ISessionsModel.ts @@ -43,12 +43,14 @@ export interface ISessionsModel extends IBaseModel { search, offset, count, + currentLoginToken, }: { uid: string; sort?: Record; search?: string | null; offset?: number; count?: number; + currentLoginToken?: string; }): Promise<{ sessions: Array; count: number; offset: number; total: number }>; getActiveUsersBetweenDates({ start, end }: DestructuredRange): Promise; diff --git a/packages/models/src/models/Sessions.ts b/packages/models/src/models/Sessions.ts index 3763bf38b457a..52bce4ac135cb 100644 --- a/packages/models/src/models/Sessions.ts +++ b/packages/models/src/models/Sessions.ts @@ -748,12 +748,14 @@ export class SessionsRaw extends BaseRaw implements ISessionsModel { search, offset = 0, count = 10, + currentLoginToken, }: { uid: string; sort?: Record; search?: string | null; offset?: number; count?: number; + currentLoginToken?: string; }): Promise> { const searchQuery = search ? [{ searchTerm: { $regex: search, $options: 'i' } }] : []; @@ -824,6 +826,15 @@ export class SessionsRaw extends BaseRaw implements ISessionsModel { host: 1, ip: 1, loginAt: 1, + ...(currentLoginToken && { + current: { + $cond: { + if: { $eq: ['$_id', currentLoginToken] }, + then: true, + else: false, + }, + }, + }), }, }; From f18c08cae36dc4b5bd31def09a10ea6b05bb9d9a Mon Sep 17 00:00:00 2001 From: juliajforesti Date: Thu, 7 May 2026 15:46:36 -0300 Subject: [PATCH 4/4] chore: add boolean `current` to `useDeviceLogout` to conditionally logout --- apps/meteor/client/hooks/useDeviceLogout.tsx | 10 ++++++---- .../DeviceManagementAccountRow.tsx | 5 +++-- .../DeviceManagementAccountTable.tsx | 1 + packages/core-typings/src/ISession.ts | 4 +++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/meteor/client/hooks/useDeviceLogout.tsx b/apps/meteor/client/hooks/useDeviceLogout.tsx index 265f5f59a3901..215ee191c5ea0 100644 --- a/apps/meteor/client/hooks/useDeviceLogout.tsx +++ b/apps/meteor/client/hooks/useDeviceLogout.tsx @@ -14,7 +14,11 @@ import { useTranslation } from 'react-i18next'; import { deviceManagementQueryKeys } from '../lib/queryKeys'; -export const useDeviceLogout = (sessionId: string, endpoint: '/v1/sessions/logout' | '/v1/sessions/logout.me'): (() => void) => { +export const useDeviceLogout = ( + sessionId: string, + endpoint: '/v1/sessions/logout' | '/v1/sessions/logout.me', + isCurrentSession?: boolean, +): (() => void) => { const { t } = useTranslation(); const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); @@ -26,15 +30,13 @@ export const useDeviceLogout = (sessionId: string, endpoint: '/v1/sessions/logou const queryClient = useQueryClient(); const logoutEndpoint = useEndpoint('POST', endpoint); - const isOwnSessionLogout = endpoint === '/v1/sessions/logout.me'; - const handleCloseContextualBar = useCallback(() => deviceManagementRouter.push({}), [deviceManagementRouter]); const isContextualBarOpen = routeId === sessionId; const { mutate: logoutDevice } = useMutation({ mutationFn: logoutEndpoint, onSettled: () => { - if (isOwnSessionLogout) { + if (isCurrentSession) { setModal(null); logout(); } else { diff --git a/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountRow.tsx b/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountRow.tsx index 892c750af1dfd..653f38ddd7748 100644 --- a/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountRow.tsx +++ b/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountRow.tsx @@ -13,14 +13,15 @@ type DevicesRowProps = { deviceType?: string; deviceOSName?: string; loginAt: string; + current?: boolean; }; -const DeviceManagementAccountRow = ({ _id, deviceName, deviceType = 'browser', deviceOSName, loginAt }: DevicesRowProps) => { +const DeviceManagementAccountRow = ({ _id, deviceName, deviceType = 'browser', deviceOSName, loginAt, current }: DevicesRowProps) => { const { t } = useTranslation(); const formatDateAndTime = useFormatDateAndTime(); const mediaQuery = useMediaQuery('(min-width: 1024px)'); - const handleDeviceLogout = useDeviceLogout(_id, '/v1/sessions/logout.me'); + const handleDeviceLogout = useDeviceLogout(_id, '/v1/sessions/logout.me', current); return ( diff --git a/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx b/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx index 9c8757b45edbd..5eaa5560824ca 100644 --- a/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx +++ b/apps/meteor/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx @@ -66,6 +66,7 @@ const DeviceManagementAccountTable = () => { deviceType={session.device?.type} deviceOSName={session.device?.os.name} loginAt={session.loginAt} + current={session.current} /> )} current={current} diff --git a/packages/core-typings/src/ISession.ts b/packages/core-typings/src/ISession.ts index f280b4fe817b0..f0a57f9fac5be 100644 --- a/packages/core-typings/src/ISession.ts +++ b/packages/core-typings/src/ISession.ts @@ -75,7 +75,9 @@ export type OSSessionAggregation = Pick & { time: number; }; -export type DeviceManagementSession = Pick; +export type DeviceManagementSession = Pick & { + current?: boolean; +}; export type DeviceManagementPopulatedSession = DeviceManagementSession & { _user: Pick;