This commit is contained in:
Nate Kelley 2025-02-12 14:45:34 -07:00
parent e91e0d9f86
commit 04a884da50
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 87 additions and 50 deletions

View File

@ -1,7 +1,7 @@
import { useBusterWebSocket } from '../BusterWebSocket'; import { useBusterWebSocket } from '../BusterWebSocket';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { BusterUserFavorite, ShareAssetType } from '@/api/asset_interfaces'; 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 { useQueryClient } from '@tanstack/react-query';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
@ -19,6 +19,13 @@ export const useFavoriteProvider = () => {
{ route: '/users/favorites/list:listFavorites' } { route: '/users/favorites/list:listFavorites' }
); );
const { mutate: addItemToFavorite } = useSocketQueryMutation(
{ route: '/users/favorites/post' },
{
route: '/users/favorites/post:createFavorite'
}
);
const setUserFavorites = useMemoizedFn( const setUserFavorites = useMemoizedFn(
(updater: (v: BusterUserFavorite[]) => BusterUserFavorite[]) => { (updater: (v: BusterUserFavorite[]) => BusterUserFavorite[]) => {
queryClient.setQueryData(favoritesQueryKey, (v: BusterUserFavorite[] | undefined) => { queryClient.setQueryData(favoritesQueryKey, (v: BusterUserFavorite[] | undefined) => {
@ -27,7 +34,7 @@ export const useFavoriteProvider = () => {
} }
); );
const addItemToFavorite = useMemoizedFn( const aXddItemToFavorite = useMemoizedFn(
async ({ async ({
id, id,
asset_type, asset_type,

View File

@ -4,5 +4,6 @@ import { BusterSocketResponseConfig } from './types';
export * from './useSocketQueryEmitAndOnce'; export * from './useSocketQueryEmitAndOnce';
export * from './useSocketQueryEmitOn'; export * from './useSocketQueryEmitOn';
export * from './useSocketQueryOn'; export * from './useSocketQueryOn';
export * from './useSocketQueryMutation';
export { createQueryKey, type BusterSocketResponseConfig }; export { createQueryKey, type BusterSocketResponseConfig };

View File

@ -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'; import { UseQueryResult } from '@tanstack/react-query';
/** /**
@ -20,3 +24,17 @@ export type BusterSocketResponseConfig<TRoute extends BusterSocketResponseRoute>
}; };
export type UseBusterSocketQueryResult<TData, TError = unknown> = UseQueryResult<TData, TError>; export type UseBusterSocketQueryResult<TData, TError = unknown> = UseQueryResult<TData, TError>;
/**
* Extract the route type from BusterSocketRequest
*/
export type BusterSocketRequestRoute = BusterSocketRequest['route'];
export type BusterSocketRequestConfig<TRoute extends BusterSocketRequestRoute> = {
route: TRoute;
};
export type InferBusterSocketRequestPayload<TRoute extends BusterSocketRequestRoute> = Extract<
BusterSocketRequest,
{ route: TRoute }
>['payload'];

View File

@ -35,7 +35,6 @@ export const useSocketQueryEmitOn = <TRoute extends BusterSocketResponseRoute, T
> >
): UseBusterSocketQueryResult<InferBusterSocketResponseData<TRoute>, TError> => { ): UseBusterSocketQueryResult<InferBusterSocketResponseData<TRoute>, TError> => {
const busterSocket = useBusterWebSocket(); const busterSocket = useBusterWebSocket();
const queryClient = useQueryClient();
const queryKey = createQueryKey(socketResponse, socketRequest); const queryKey = createQueryKey(socketResponse, socketRequest);

View File

@ -6,7 +6,13 @@ import {
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'; import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query';
import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useBusterWebSocket } from '@/context/BusterWebSocket';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { BusterSocketResponseConfig, InferBusterSocketResponseData } from './types'; import {
BusterSocketResponseConfig,
InferBusterSocketResponseData,
InferBusterSocketRequestPayload,
BusterSocketRequestConfig,
BusterSocketRequestRoute
} from './types';
import { ShareAssetType } from '@/api/asset_interfaces'; import { ShareAssetType } from '@/api/asset_interfaces';
import { createQueryKey } from './helpers'; import { createQueryKey } from './helpers';
@ -25,11 +31,12 @@ import { createQueryKey } from './helpers';
* @returns A React Query mutation result for handling socket requests * @returns A React Query mutation result for handling socket requests
*/ */
export const useSocketQueryMutation = < export const useSocketQueryMutation = <
TRequestRoute extends BusterSocketRequestRoute,
TRoute extends BusterSocketResponseRoute, TRoute extends BusterSocketResponseRoute,
TVariables = void, TError = unknown,
TError = unknown TVariables = InferBusterSocketRequestPayload<TRequestRoute>
>( >(
socketRequest: BusterSocketRequest, socketRequest: BusterSocketRequestConfig<TRequestRoute>,
socketResponse: BusterSocketResponseConfig<TRoute> & { socketResponse: BusterSocketResponseConfig<TRoute> & {
callback?: (d: unknown) => InferBusterSocketResponseData<TRoute>; callback?: (d: unknown) => InferBusterSocketResponseData<TRoute>;
}, },
@ -49,7 +56,11 @@ export const useSocketQueryMutation = <
const { preSetQueryData, queryDataStrategy = 'ignore', ...options } = optionsProps || {}; const { preSetQueryData, queryDataStrategy = 'ignore', ...options } = optionsProps || {};
const mutationFn = useMemoizedFn(async (variables: TVariables) => { 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) { if (preSetQueryData) {
await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>>(queryKey, (d) => { await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>>(queryKey, (d) => {
@ -58,7 +69,7 @@ export const useSocketQueryMutation = <
} }
const res = await busterSocket.emitAndOnce({ const res = await busterSocket.emitAndOnce({
emitEvent: socketRequest, emitEvent: compiledSocketRequest,
responseEvent: { responseEvent: {
...socketResponse, ...socketResponse,
callback: (d: unknown) => { callback: (d: unknown) => {
@ -70,61 +81,62 @@ export const useSocketQueryMutation = <
} as BusterSocketResponse } as BusterSocketResponse
}); });
if (queryDataStrategy === 'replace') { if (res !== undefined) {
await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>>(queryKey, () => { if (queryDataStrategy === 'replace') {
return res as InferBusterSocketResponseData<TRoute>; await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>>(queryKey, () => {
}); return res as InferBusterSocketResponseData<TRoute>;
} else if (queryDataStrategy === 'append') { });
await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>[]>(queryKey, (d) => { } else if (queryDataStrategy === 'append') {
return [...(Array.isArray(d) ? d : []), res as InferBusterSocketResponseData<TRoute>]; await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>[]>(queryKey, (d) => {
}); return [...(Array.isArray(d) ? d : []), res as InferBusterSocketResponseData<TRoute>];
} else if (queryDataStrategy === 'prepend') { });
await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>[]>(queryKey, (d) => { } else if (queryDataStrategy === 'prepend') {
return [res as InferBusterSocketResponseData<TRoute>, ...(Array.isArray(d) ? d : [])]; await queryClient.setQueryData<InferBusterSocketResponseData<TRoute>[]>(queryKey, (d) => {
}); return [res as InferBusterSocketResponseData<TRoute>, ...(Array.isArray(d) ? d : [])];
} else if (queryDataStrategy === 'merge') { });
await queryClient.setQueryData<Record<string, InferBusterSocketResponseData<TRoute>>>( } else if (queryDataStrategy === 'merge') {
queryKey, await queryClient.setQueryData<Record<string, InferBusterSocketResponseData<TRoute>>>(
(d) => { queryKey,
if (typeof res === 'object' && res !== null && 'id' in res) { (d) => {
const typedRes = res as InferBusterSocketResponseData<TRoute> & { id: string }; if (typeof res === 'object' && res !== null && 'id' in res) {
return { const typedRes = res as InferBusterSocketResponseData<TRoute> & { id: string };
...(d || {}), return {
[typedRes.id]: typedRes ...(d || {}),
}; [typedRes.id]: typedRes
} else { };
console.warn('response is not an object with an id', res); } else {
console.warn('response is not an object with an id', res);
}
return d;
} }
return d; );
} }
);
} }
return res as InferBusterSocketResponseData<TRoute>; return res as InferBusterSocketResponseData<TRoute>;
}); });
return useMutation<InferBusterSocketResponseData<TRoute>, TError, TVariables>({ return useMutation({
...options, ...options,
mutationFn mutationFn: mutationFn
}); });
}; };
const Example = () => { const Example = () => {
const { mutate, data, ...rest } = useSocketQueryMutation( // Example 1: Favorites mutation
const { mutate, data } = useSocketQueryMutation(
{ {
route: '/users/favorites/post', route: '/users/favorites/post'
payload: {
id: '1',
asset_type: ShareAssetType.DASHBOARD
}
}, },
{ {
route: '/users/favorites/post:createFavorite' route: '/users/favorites/post:createFavorite'
},
{
preSetQueryData: (d) => {
return d || [];
}
} }
); );
mutate({
id: 'some-asset-id',
asset_type: ShareAssetType.DASHBOARD
});
return null;
}; };