From a27a9c4cb7bf919c6e501fb883184c2d132c38e9 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:36:48 +0530 Subject: [PATCH] chore: Remove toggleFavorite client meteor method (#33734) Co-authored-by: Tasso <tasso.evangelista@rocket.chat> --- apps/meteor/client/hooks/useReactiveQuery.ts | 9 ++-- .../meteor/client/lib/mutationEffects/room.ts | 19 ++++++++ apps/meteor/client/lib/queryKeys.ts | 5 ++ apps/meteor/client/methods/index.ts | 1 - apps/meteor/client/methods/toggleFavorite.ts | 24 ---------- .../hooks/usePutChatOnHoldMutation.ts | 4 +- .../hooks/useReturnChatToQueueMutation.ts | 8 ++-- .../views/room/Header/icons/Favorite.tsx | 20 ++------ .../hooks/usePutChatOnHoldMutation.ts | 4 +- .../hooks/useReturnChatToQueueMutation.ts | 8 ++-- .../views/room/HeaderV2/icons/Favorite.tsx | 20 ++------ .../hooks/useResumeChatOnHoldMutation.ts | 4 +- .../hooks/useToggleFavoriteMutation.spec.tsx | 43 +++++++++++++++++ .../room/hooks/useToggleFavoriteMutation.ts | 48 +++++++++++++++++++ .../views/room/providers/RoomProvider.tsx | 3 +- apps/meteor/tests/mocks/client/meteor.ts | 3 ++ 16 files changed, 154 insertions(+), 69 deletions(-) create mode 100644 apps/meteor/client/lib/mutationEffects/room.ts delete mode 100644 apps/meteor/client/methods/toggleFavorite.ts create mode 100644 apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.spec.tsx create mode 100644 apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.ts diff --git a/apps/meteor/client/hooks/useReactiveQuery.ts b/apps/meteor/client/hooks/useReactiveQuery.ts index 0083fce17ba..61bffde48c2 100644 --- a/apps/meteor/client/hooks/useReactiveQuery.ts +++ b/apps/meteor/client/hooks/useReactiveQuery.ts @@ -11,9 +11,9 @@ export const useReactiveQuery = <TQueryFnData, TData = TQueryFnData, TQueryKey e ): UseQueryResult<TData, Error> => { const queryClient = useQueryClient(); - return useQuery( + return useQuery({ queryKey, - (): Promise<TQueryFnData> => + queryFn: (): Promise<TQueryFnData> => new Promise((resolve, reject) => { queueMicrotask(() => { Tracker.autorun((c) => { @@ -33,6 +33,7 @@ export const useReactiveQuery = <TQueryFnData, TData = TQueryFnData, TQueryKey e }); }); }), - { staleTime: Infinity, ...options }, - ); + staleTime: Infinity, + ...options, + }); }; diff --git a/apps/meteor/client/lib/mutationEffects/room.ts b/apps/meteor/client/lib/mutationEffects/room.ts new file mode 100644 index 00000000000..cba7005cf73 --- /dev/null +++ b/apps/meteor/client/lib/mutationEffects/room.ts @@ -0,0 +1,19 @@ +import { Meteor } from 'meteor/meteor'; + +import { Subscriptions } from '../../../app/models/client'; + +export const toggleFavoriteRoom = (roomId: string, favorite: boolean) => { + const userId = Meteor.userId()!; + + Subscriptions.update( + { + 'rid': roomId, + 'u._id': userId, + }, + { + $set: { + f: favorite, + }, + }, + ); +}; diff --git a/apps/meteor/client/lib/queryKeys.ts b/apps/meteor/client/lib/queryKeys.ts index 112e1dbcb61..57a2dc52493 100644 --- a/apps/meteor/client/lib/queryKeys.ts +++ b/apps/meteor/client/lib/queryKeys.ts @@ -10,3 +10,8 @@ export const roomsQueryKeys = { messageActionsWithParameters: (rid: IRoom['_id'], message: IMessage | Serialized<IMessage>) => [...roomsQueryKeys.messageActions(rid, message._id), message] as const, }; + +export const subscriptionsQueryKeys = { + all: ['subscriptions'] as const, + subscription: (rid: IRoom['_id']) => [...subscriptionsQueryKeys.all, { rid }] as const, +}; diff --git a/apps/meteor/client/methods/index.ts b/apps/meteor/client/methods/index.ts index 338b669ae63..7be75a2707f 100644 --- a/apps/meteor/client/methods/index.ts +++ b/apps/meteor/client/methods/index.ts @@ -1,6 +1,5 @@ import './hideRoom'; import './openRoom'; import './pinMessage'; -import './toggleFavorite'; import './unpinMessage'; import './updateMessage'; diff --git a/apps/meteor/client/methods/toggleFavorite.ts b/apps/meteor/client/methods/toggleFavorite.ts deleted file mode 100644 index a6deb281fea..00000000000 --- a/apps/meteor/client/methods/toggleFavorite.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Meteor } from 'meteor/meteor'; - -import { Subscriptions } from '../../app/models/client'; - -Meteor.methods<ServerMethods>({ - async toggleFavorite(rid, f) { - if (!Meteor.userId()) { - return 0; - } - - return Subscriptions.update( - { - rid, - 'u._id': Meteor.userId(), - }, - { - $set: { - f, - }, - }, - ); - }, -}); diff --git a/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts b/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts index 0c9dbc76795..4cfbbe0aed7 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts +++ b/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts @@ -3,6 +3,8 @@ import { useEndpoint } from '@rocket.chat/ui-contexts'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { subscriptionsQueryKeys } from '../../../../../../lib/queryKeys'; + export const usePutChatOnHoldMutation = ( options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>, ): UseMutationResult<void, Error, IRoom['_id']> => { @@ -19,7 +21,7 @@ export const usePutChatOnHoldMutation = ( onSuccess: async (data, rid, context) => { await queryClient.invalidateQueries(['current-chats']); await queryClient.invalidateQueries(['rooms', rid]); - await queryClient.invalidateQueries(['subscriptions', { rid }]); + await queryClient.invalidateQueries(subscriptionsQueryKeys.subscription(rid)); return options?.onSuccess?.(data, rid, context); }, }, diff --git a/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts b/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts index c037f200514..c44afc8a2d0 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts +++ b/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts @@ -3,6 +3,8 @@ import { useMethod } from '@rocket.chat/ui-contexts'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { subscriptionsQueryKeys } from '../../../../../../lib/queryKeys'; + export const useReturnChatToQueueMutation = ( options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>, ): UseMutationResult<void, Error, IRoom['_id']> => { @@ -18,9 +20,9 @@ export const useReturnChatToQueueMutation = ( ...options, onSuccess: async (data, rid, context) => { await queryClient.invalidateQueries(['current-chats']); - await queryClient.removeQueries(['rooms', rid]); - await queryClient.removeQueries(['/v1/rooms.info', rid]); - await queryClient.removeQueries(['subscriptions', { rid }]); + queryClient.removeQueries(['rooms', rid]); + queryClient.removeQueries(['/v1/rooms.info', rid]); + queryClient.removeQueries(subscriptionsQueryKeys.subscription(rid)); return options?.onSuccess?.(data, rid, context); }, }, diff --git a/apps/meteor/client/views/room/Header/icons/Favorite.tsx b/apps/meteor/client/views/room/Header/icons/Favorite.tsx index f6d17cb0e7b..bf58a748697 100644 --- a/apps/meteor/client/views/room/Header/icons/Favorite.tsx +++ b/apps/meteor/client/views/room/Header/icons/Favorite.tsx @@ -1,35 +1,25 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; -import { useSetting, useMethod, useTranslation, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import { HeaderState } from '../../../../components/Header'; import { useUserIsSubscribed } from '../../contexts/RoomContext'; +import { useToggleFavoriteMutation } from '../../hooks/useToggleFavoriteMutation'; const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: IRoom & { f?: ISubscription['f'] } }) => { const t = useTranslation(); const subscribed = useUserIsSubscribed(); - const dispatchToastMessage = useToastMessageDispatch(); - const isFavoritesEnabled = useSetting('Favorite_Rooms') && ['c', 'p', 'd', 't'].includes(type); - const toggleFavorite = useMethod('toggleFavorite'); + const isFavoritesEnabled = useSetting('Favorite_Rooms', true) && ['c', 'p', 'd', 't'].includes(type); + const { mutate: toggleFavorite } = useToggleFavoriteMutation(); const handleFavoriteClick = useEffectEvent(() => { if (!isFavoritesEnabled) { return; } - try { - toggleFavorite(_id, !favorite); - dispatchToastMessage({ - type: 'success', - message: !favorite - ? t('__roomName__was_added_to_favorites', { roomName: name }) - : t('__roomName__was_removed_from_favorites', { roomName: name }), - }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } + toggleFavorite({ roomId: _id, favorite: !favorite, roomName: name || '' }); }); const favoriteLabel = favorite ? `${t('Unfavorite')} ${name}` : `${t('Favorite')} ${name}`; diff --git a/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts b/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts index 0c9dbc76795..4cfbbe0aed7 100644 --- a/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts +++ b/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/usePutChatOnHoldMutation.ts @@ -3,6 +3,8 @@ import { useEndpoint } from '@rocket.chat/ui-contexts'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { subscriptionsQueryKeys } from '../../../../../../lib/queryKeys'; + export const usePutChatOnHoldMutation = ( options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>, ): UseMutationResult<void, Error, IRoom['_id']> => { @@ -19,7 +21,7 @@ export const usePutChatOnHoldMutation = ( onSuccess: async (data, rid, context) => { await queryClient.invalidateQueries(['current-chats']); await queryClient.invalidateQueries(['rooms', rid]); - await queryClient.invalidateQueries(['subscriptions', { rid }]); + await queryClient.invalidateQueries(subscriptionsQueryKeys.subscription(rid)); return options?.onSuccess?.(data, rid, context); }, }, diff --git a/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts b/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts index c037f200514..c44afc8a2d0 100644 --- a/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts +++ b/apps/meteor/client/views/room/HeaderV2/Omnichannel/QuickActions/hooks/useReturnChatToQueueMutation.ts @@ -3,6 +3,8 @@ import { useMethod } from '@rocket.chat/ui-contexts'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { subscriptionsQueryKeys } from '../../../../../../lib/queryKeys'; + export const useReturnChatToQueueMutation = ( options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>, ): UseMutationResult<void, Error, IRoom['_id']> => { @@ -18,9 +20,9 @@ export const useReturnChatToQueueMutation = ( ...options, onSuccess: async (data, rid, context) => { await queryClient.invalidateQueries(['current-chats']); - await queryClient.removeQueries(['rooms', rid]); - await queryClient.removeQueries(['/v1/rooms.info', rid]); - await queryClient.removeQueries(['subscriptions', { rid }]); + queryClient.removeQueries(['rooms', rid]); + queryClient.removeQueries(['/v1/rooms.info', rid]); + queryClient.removeQueries(subscriptionsQueryKeys.subscription(rid)); return options?.onSuccess?.(data, rid, context); }, }, diff --git a/apps/meteor/client/views/room/HeaderV2/icons/Favorite.tsx b/apps/meteor/client/views/room/HeaderV2/icons/Favorite.tsx index f6d17cb0e7b..bf58a748697 100644 --- a/apps/meteor/client/views/room/HeaderV2/icons/Favorite.tsx +++ b/apps/meteor/client/views/room/HeaderV2/icons/Favorite.tsx @@ -1,35 +1,25 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; -import { useSetting, useMethod, useTranslation, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import { HeaderState } from '../../../../components/Header'; import { useUserIsSubscribed } from '../../contexts/RoomContext'; +import { useToggleFavoriteMutation } from '../../hooks/useToggleFavoriteMutation'; const Favorite = ({ room: { _id, f: favorite = false, t: type, name } }: { room: IRoom & { f?: ISubscription['f'] } }) => { const t = useTranslation(); const subscribed = useUserIsSubscribed(); - const dispatchToastMessage = useToastMessageDispatch(); - const isFavoritesEnabled = useSetting('Favorite_Rooms') && ['c', 'p', 'd', 't'].includes(type); - const toggleFavorite = useMethod('toggleFavorite'); + const isFavoritesEnabled = useSetting('Favorite_Rooms', true) && ['c', 'p', 'd', 't'].includes(type); + const { mutate: toggleFavorite } = useToggleFavoriteMutation(); const handleFavoriteClick = useEffectEvent(() => { if (!isFavoritesEnabled) { return; } - try { - toggleFavorite(_id, !favorite); - dispatchToastMessage({ - type: 'success', - message: !favorite - ? t('__roomName__was_added_to_favorites', { roomName: name }) - : t('__roomName__was_removed_from_favorites', { roomName: name }), - }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } + toggleFavorite({ roomId: _id, favorite: !favorite, roomName: name || '' }); }); const favoriteLabel = favorite ? `${t('Unfavorite')} ${name}` : `${t('Favorite')} ${name}`; diff --git a/apps/meteor/client/views/room/composer/ComposerOmnichannel/hooks/useResumeChatOnHoldMutation.ts b/apps/meteor/client/views/room/composer/ComposerOmnichannel/hooks/useResumeChatOnHoldMutation.ts index 9992be96975..e6bf592b156 100644 --- a/apps/meteor/client/views/room/composer/ComposerOmnichannel/hooks/useResumeChatOnHoldMutation.ts +++ b/apps/meteor/client/views/room/composer/ComposerOmnichannel/hooks/useResumeChatOnHoldMutation.ts @@ -3,6 +3,8 @@ import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { subscriptionsQueryKeys } from '../../../../../lib/queryKeys'; + export const useResumeChatOnHoldMutation = ( options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>, ): UseMutationResult<void, Error, IRoom['_id']> => { @@ -21,7 +23,7 @@ export const useResumeChatOnHoldMutation = ( onSuccess: async (data, rid, context) => { await queryClient.invalidateQueries(['current-chats']); await queryClient.invalidateQueries(['rooms', rid]); - await queryClient.invalidateQueries(['subscriptions', { rid }]); + await queryClient.invalidateQueries(subscriptionsQueryKeys.subscription(rid)); return options?.onSuccess?.(data, rid, context); }, onError: (error) => { diff --git a/apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.spec.tsx b/apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.spec.tsx new file mode 100644 index 00000000000..f5c625c8ae4 --- /dev/null +++ b/apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.spec.tsx @@ -0,0 +1,43 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { useToggleFavoriteMutation } from './useToggleFavoriteMutation'; +import { subscriptionsQueryKeys } from '../../../lib/queryKeys'; + +it('should work', async () => { + const endpointHandler = jest.fn(() => null); + + const { result } = renderHook(() => useToggleFavoriteMutation(), { + legacyRoot: true, + wrapper: mockAppRoot().withEndpoint('POST', '/v1/rooms.favorite', endpointHandler).build(), + }); + + result.current.mutate({ roomId: 'general', favorite: true, roomName: 'general' }); + + await waitFor(() => expect(result.current.status).toBe('success')); + expect(endpointHandler).toHaveBeenCalledWith({ + roomId: 'general', + favorite: true, + }); +}); + +it('should invalidate any subscription queries', async () => { + const queryClient = new QueryClient(); + jest.spyOn(queryClient, 'invalidateQueries'); + + const { result } = renderHook(() => useToggleFavoriteMutation(), { + legacyRoot: true, + wrapper: mockAppRoot() + .withEndpoint('POST', '/v1/rooms.favorite', async () => null) + .wrap((children) => <QueryClientProvider client={queryClient} children={children} />) + .build(), + }); + + result.current.mutate({ roomId: 'general', favorite: true, roomName: 'general' }); + + await waitFor(() => expect(result.current.status).toBe('success')); + + expect(queryClient.invalidateQueries).toHaveBeenCalledWith(subscriptionsQueryKeys.subscription('general')); +}); diff --git a/apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.ts b/apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.ts new file mode 100644 index 00000000000..90806d68f90 --- /dev/null +++ b/apps/meteor/client/views/room/hooks/useToggleFavoriteMutation.ts @@ -0,0 +1,48 @@ +import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useTranslation } from 'react-i18next'; + +import { toggleFavoriteRoom } from '../../../lib/mutationEffects/room'; +import { subscriptionsQueryKeys } from '../../../lib/queryKeys'; + +export const useToggleFavoriteMutation = () => { + const { t } = useTranslation(); + const dispatchToastMessage = useToastMessageDispatch(); + + const toggleFavorite = useEndpoint('POST', '/v1/rooms.favorite'); + const queryClient = useQueryClient(); + + return useMutation( + async ({ roomId, favorite }: { roomId: string; favorite: boolean; roomName: string }) => { + await toggleFavorite({ roomId, favorite }); + }, + { + onMutate: ({ roomId, favorite }) => { + queryClient.setQueryData<SubscriptionWithRoom | null>(subscriptionsQueryKeys.subscription(roomId), (subscription) => + subscription + ? { + ...subscription, + f: favorite, + } + : undefined, + ); + }, + onSuccess: (_data, { roomId, favorite, roomName }) => { + toggleFavoriteRoom(roomId, favorite); + dispatchToastMessage({ + type: 'success', + message: favorite + ? t('__roomName__was_added_to_favorites', { roomName }) + : t('__roomName__was_removed_from_favorites', { roomName }), + }); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + onSettled: (_data, _error, { roomId }) => { + queryClient.invalidateQueries(subscriptionsQueryKeys.subscription(roomId)); + }, + }, + ); +}; diff --git a/apps/meteor/client/views/room/providers/RoomProvider.tsx b/apps/meteor/client/views/room/providers/RoomProvider.tsx index 7cf08bcbff2..0ac4e81d54d 100644 --- a/apps/meteor/client/views/room/providers/RoomProvider.tsx +++ b/apps/meteor/client/views/room/providers/RoomProvider.tsx @@ -17,6 +17,7 @@ import { useReactiveValue } from '../../../hooks/useReactiveValue'; import { useRoomInfoEndpoint } from '../../../hooks/useRoomInfoEndpoint'; import { useSidePanelNavigation } from '../../../hooks/useSidePanelNavigation'; import { RoomManager } from '../../../lib/RoomManager'; +import { subscriptionsQueryKeys } from '../../../lib/queryKeys'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import ImageGalleryProvider from '../../../providers/ImageGalleryProvider'; import RoomNotFound from '../RoomNotFound'; @@ -45,7 +46,7 @@ const RoomProvider = ({ rid, children }: RoomProviderProps): ReactElement => { } }, [resultFromLocal.data, resultFromLocal.isSuccess, resultFromServer, router]); - const subscriptionQuery = useReactiveQuery(['subscriptions', { rid }], () => Subscriptions.findOne({ rid }) ?? null); + const subscriptionQuery = useReactiveQuery(subscriptionsQueryKeys.subscription(rid), () => Subscriptions.findOne({ rid }) ?? null); useRedirectOnSettingsChanged(subscriptionQuery.data); diff --git a/apps/meteor/tests/mocks/client/meteor.ts b/apps/meteor/tests/mocks/client/meteor.ts index aa55781bf67..08be68865f9 100644 --- a/apps/meteor/tests/mocks/client/meteor.ts +++ b/apps/meteor/tests/mocks/client/meteor.ts @@ -10,11 +10,14 @@ export const Meteor = { setItem: jest.fn(), }, users: {}, + userId: () => 'uid', }; export const Mongo = { Collection: class Collection { findOne = jest.fn(); + + update = jest.fn(); }, }; -- GitLab