diff --git a/web/src/context/Users/useFavoriteProvider.tsx b/web/src/context/Users/useFavoriteProvider.tsx index 1a7f429be..82d62cfcf 100644 --- a/web/src/context/Users/useFavoriteProvider.tsx +++ b/web/src/context/Users/useFavoriteProvider.tsx @@ -1,7 +1,7 @@ import { useBusterWebSocket } from '../BusterWebSocket'; import { useMemoizedFn } from 'ahooks'; import { BusterUserFavorite, ShareAssetType } from '@/api/asset_interfaces'; -import { createQueryKey, useSocketQueryEmitOn } from '@/hooks'; +import { createQueryKey, useSocketQueryEmitOn, useSocketQueryMutation } from '@/hooks'; import { useQueryClient } from '@tanstack/react-query'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -19,6 +19,13 @@ export const useFavoriteProvider = () => { { route: '/users/favorites/list:listFavorites' } ); + const { mutate: addItemToFavorite } = useSocketQueryMutation( + { route: '/users/favorites/post' }, + { + route: '/users/favorites/post:createFavorite' + } + ); + const setUserFavorites = useMemoizedFn( (updater: (v: BusterUserFavorite[]) => BusterUserFavorite[]) => { queryClient.setQueryData(favoritesQueryKey, (v: BusterUserFavorite[] | undefined) => { @@ -27,7 +34,7 @@ export const useFavoriteProvider = () => { } ); - const addItemToFavorite = useMemoizedFn( + const aXddItemToFavorite = useMemoizedFn( async ({ id, asset_type, diff --git a/web/src/hooks/useSocketQuery/index.ts b/web/src/hooks/useSocketQuery/index.ts index d1b575736..6c90e103b 100644 --- a/web/src/hooks/useSocketQuery/index.ts +++ b/web/src/hooks/useSocketQuery/index.ts @@ -4,5 +4,6 @@ import { BusterSocketResponseConfig } from './types'; export * from './useSocketQueryEmitAndOnce'; export * from './useSocketQueryEmitOn'; export * from './useSocketQueryOn'; +export * from './useSocketQueryMutation'; export { createQueryKey, type BusterSocketResponseConfig }; diff --git a/web/src/hooks/useSocketQuery/types.ts b/web/src/hooks/useSocketQuery/types.ts index 706b04a4d..a8f9eca71 100644 --- a/web/src/hooks/useSocketQuery/types.ts +++ b/web/src/hooks/useSocketQuery/types.ts @@ -1,4 +1,8 @@ -import type { BusterSocketResponse, BusterSocketResponseRoute } from '@/api/buster_socket'; +import type { + BusterSocketRequest, + BusterSocketResponse, + BusterSocketResponseRoute +} from '@/api/buster_socket'; import { UseQueryResult } from '@tanstack/react-query'; /** @@ -20,3 +24,17 @@ export type BusterSocketResponseConfig }; export type UseBusterSocketQueryResult = UseQueryResult; + +/** + * Extract the route type from BusterSocketRequest + */ +export type BusterSocketRequestRoute = BusterSocketRequest['route']; + +export type BusterSocketRequestConfig = { + route: TRoute; +}; + +export type InferBusterSocketRequestPayload = Extract< + BusterSocketRequest, + { route: TRoute } +>['payload']; diff --git a/web/src/hooks/useSocketQuery/useSocketQueryEmitOn.tsx b/web/src/hooks/useSocketQuery/useSocketQueryEmitOn.tsx index a253a5b26..bc4ec3ca4 100644 --- a/web/src/hooks/useSocketQuery/useSocketQueryEmitOn.tsx +++ b/web/src/hooks/useSocketQuery/useSocketQueryEmitOn.tsx @@ -35,7 +35,6 @@ export const useSocketQueryEmitOn = ): UseBusterSocketQueryResult, TError> => { const busterSocket = useBusterWebSocket(); - const queryClient = useQueryClient(); const queryKey = createQueryKey(socketResponse, socketRequest); diff --git a/web/src/hooks/useSocketQuery/useSocketQueryMutation.tsx b/web/src/hooks/useSocketQuery/useSocketQueryMutation.tsx index f890227c7..cff314ddc 100644 --- a/web/src/hooks/useSocketQuery/useSocketQueryMutation.tsx +++ b/web/src/hooks/useSocketQuery/useSocketQueryMutation.tsx @@ -6,7 +6,13 @@ import { import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'; import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useMemoizedFn } from 'ahooks'; -import { BusterSocketResponseConfig, InferBusterSocketResponseData } from './types'; +import { + BusterSocketResponseConfig, + InferBusterSocketResponseData, + InferBusterSocketRequestPayload, + BusterSocketRequestConfig, + BusterSocketRequestRoute +} from './types'; import { ShareAssetType } from '@/api/asset_interfaces'; import { createQueryKey } from './helpers'; @@ -25,11 +31,12 @@ import { createQueryKey } from './helpers'; * @returns A React Query mutation result for handling socket requests */ export const useSocketQueryMutation = < + TRequestRoute extends BusterSocketRequestRoute, TRoute extends BusterSocketResponseRoute, - TVariables = void, - TError = unknown + TError = unknown, + TVariables = InferBusterSocketRequestPayload >( - socketRequest: BusterSocketRequest, + socketRequest: BusterSocketRequestConfig, socketResponse: BusterSocketResponseConfig & { callback?: (d: unknown) => InferBusterSocketResponseData; }, @@ -49,7 +56,11 @@ export const useSocketQueryMutation = < const { preSetQueryData, queryDataStrategy = 'ignore', ...options } = optionsProps || {}; const mutationFn = useMemoizedFn(async (variables: TVariables) => { - const queryKey = createQueryKey(socketResponse, socketRequest); + const compiledSocketRequest = { + ...socketRequest, + payload: variables + } as unknown as BusterSocketRequest; + const queryKey = createQueryKey(socketResponse, compiledSocketRequest); if (preSetQueryData) { await queryClient.setQueryData>(queryKey, (d) => { @@ -58,7 +69,7 @@ export const useSocketQueryMutation = < } const res = await busterSocket.emitAndOnce({ - emitEvent: socketRequest, + emitEvent: compiledSocketRequest, responseEvent: { ...socketResponse, callback: (d: unknown) => { @@ -70,61 +81,62 @@ export const useSocketQueryMutation = < } as BusterSocketResponse }); - if (queryDataStrategy === 'replace') { - await queryClient.setQueryData>(queryKey, () => { - return res as InferBusterSocketResponseData; - }); - } else if (queryDataStrategy === 'append') { - await queryClient.setQueryData[]>(queryKey, (d) => { - return [...(Array.isArray(d) ? d : []), res as InferBusterSocketResponseData]; - }); - } else if (queryDataStrategy === 'prepend') { - await queryClient.setQueryData[]>(queryKey, (d) => { - return [res as InferBusterSocketResponseData, ...(Array.isArray(d) ? d : [])]; - }); - } else if (queryDataStrategy === 'merge') { - await queryClient.setQueryData>>( - queryKey, - (d) => { - if (typeof res === 'object' && res !== null && 'id' in res) { - const typedRes = res as InferBusterSocketResponseData & { id: string }; - return { - ...(d || {}), - [typedRes.id]: typedRes - }; - } else { - console.warn('response is not an object with an id', res); + if (res !== undefined) { + if (queryDataStrategy === 'replace') { + await queryClient.setQueryData>(queryKey, () => { + return res as InferBusterSocketResponseData; + }); + } else if (queryDataStrategy === 'append') { + await queryClient.setQueryData[]>(queryKey, (d) => { + return [...(Array.isArray(d) ? d : []), res as InferBusterSocketResponseData]; + }); + } else if (queryDataStrategy === 'prepend') { + await queryClient.setQueryData[]>(queryKey, (d) => { + return [res as InferBusterSocketResponseData, ...(Array.isArray(d) ? d : [])]; + }); + } else if (queryDataStrategy === 'merge') { + await queryClient.setQueryData>>( + queryKey, + (d) => { + if (typeof res === 'object' && res !== null && 'id' in res) { + const typedRes = res as InferBusterSocketResponseData & { id: string }; + return { + ...(d || {}), + [typedRes.id]: typedRes + }; + } else { + console.warn('response is not an object with an id', res); + } + return d; } - return d; - } - ); + ); + } } return res as InferBusterSocketResponseData; }); - return useMutation, TError, TVariables>({ + return useMutation({ ...options, - mutationFn + mutationFn: mutationFn }); }; const Example = () => { - const { mutate, data, ...rest } = useSocketQueryMutation( + // Example 1: Favorites mutation + const { mutate, data } = useSocketQueryMutation( { - route: '/users/favorites/post', - payload: { - id: '1', - asset_type: ShareAssetType.DASHBOARD - } + route: '/users/favorites/post' }, { route: '/users/favorites/post:createFavorite' - }, - { - preSetQueryData: (d) => { - return d || []; - } } ); + + mutate({ + id: 'some-asset-id', + asset_type: ShareAssetType.DASHBOARD + }); + + return null; };