mirror of https://github.com/buster-so/buster.git
mutation update for collections
This commit is contained in:
parent
5d283d49d3
commit
a9f7cf23cc
|
@ -1,6 +1,12 @@
|
|||
'use client';
|
||||
|
||||
import { type UseQueryOptions, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
MutationFunction,
|
||||
QueryKey,
|
||||
type UseQueryOptions,
|
||||
useMutation,
|
||||
useQueryClient
|
||||
} from '@tanstack/react-query';
|
||||
import type {
|
||||
BusterSocketRequest,
|
||||
BusterSocketResponse,
|
||||
|
@ -16,6 +22,62 @@ import type {
|
|||
InferBusterSocketResponseData
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* A custom hook that combines WebSocket communication with React Query's mutation capabilities.
|
||||
* This hook allows you to emit socket events and handle their responses while integrating with React Query's state management.
|
||||
*
|
||||
* @template TRequestRoute - The type of socket request route
|
||||
* @template TRoute - The type of socket response route
|
||||
* @template TError - The type of error that might occur during the mutation
|
||||
* @template TData - The type of data returned by the socket response
|
||||
* @template TPayload - The type of payload sent in the socket request
|
||||
* @template TQueryData - The type of data stored in the React Query cache
|
||||
*
|
||||
* @param socketRequest - Configuration object for the socket request
|
||||
* @param socketResponse - Configuration object for handling the socket response
|
||||
* @param options - React Query options for the mutation
|
||||
* @param preCallback - Optional callback function executed before the socket request, allowing manipulation of cached data
|
||||
* @param callback - Optional callback function executed after receiving the socket response, allowing transformation of the response data
|
||||
*
|
||||
* @returns A mutation object from React Query that can be used to trigger the socket request
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Example usage in a component
|
||||
* function ChatComponent() {
|
||||
* const mutation = useSocketQueryMutation(
|
||||
* { route: 'chat:send' },
|
||||
* { route: 'chat:message' },
|
||||
* {
|
||||
* queryKey: ['chat', 'messages']
|
||||
* },
|
||||
* // Pre-callback: Optimistically update the UI
|
||||
* (currentMessages, newMessage) => {
|
||||
* return [...currentMessages, { pending: true, ...newMessage }];
|
||||
* },
|
||||
* // Post-callback: Update with the actual response
|
||||
* (socketResponse, currentMessages) => {
|
||||
* return currentMessages.map(msg =>
|
||||
* msg.pending ? socketResponse : msg
|
||||
* );
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* const sendMessage = (message: string) => {
|
||||
* mutation.mutate({ content: message });
|
||||
* };
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* onClick={() => sendMessage('Hello!')}
|
||||
* disabled={mutation.isPending}
|
||||
* >
|
||||
* Send Message
|
||||
* </button>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function useSocketQueryMutation<
|
||||
TRequestRoute extends BusterSocketRequestRoute,
|
||||
TRoute extends BusterSocketResponseRoute,
|
||||
|
@ -26,7 +88,7 @@ export function useSocketQueryMutation<
|
|||
>(
|
||||
socketRequest: BusterSocketRequestConfig<TRequestRoute>,
|
||||
socketResponse: BusterSocketResponseConfig<TRoute>,
|
||||
options?: UseQueryOptions<TQueryData, any, TQueryData, any>,
|
||||
options?: UseQueryOptions<TQueryData, any, TQueryData, any> | null,
|
||||
preCallback?: (
|
||||
currentData: TQueryData | null,
|
||||
variables: TPayload
|
||||
|
@ -39,41 +101,47 @@ export function useSocketQueryMutation<
|
|||
const busterSocket = useBusterWebSocket();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutationFn = useMemoizedFn(async (variables: TPayload): Promise<TData> => {
|
||||
const queryKey = options?.queryKey;
|
||||
const mutationFn: MutationFunction<TData, TPayload> = useMemoizedFn(
|
||||
async (variables: TPayload): Promise<TData> => {
|
||||
const queryKey: QueryKey = options?.queryKey;
|
||||
|
||||
if (queryKey && preCallback) {
|
||||
const currentData = queryClient.getQueryData<TQueryData>(queryKey) ?? null;
|
||||
const transformedData = await preCallback(currentData, variables);
|
||||
queryClient.setQueryData(queryKey, transformedData);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await busterSocket.emitAndOnce({
|
||||
emitEvent: {
|
||||
route: socketRequest.route,
|
||||
payload: variables
|
||||
} as BusterSocketRequest,
|
||||
responseEvent: {
|
||||
route: socketResponse.route,
|
||||
onError: socketResponse.onError,
|
||||
callback: (d: unknown) => d
|
||||
} as BusterSocketResponse
|
||||
});
|
||||
|
||||
if (queryKey && callback) {
|
||||
const socketData = result as InferBusterSocketResponseData<TRoute>;
|
||||
const currentData = queryClient.getQueryData<TQueryData>(queryKey) ?? null;
|
||||
const transformedData = callback(socketData, currentData);
|
||||
queryClient.setQueryData(queryKey, transformedData);
|
||||
return result as TData;
|
||||
if (preCallback) {
|
||||
const currentData = queryKey
|
||||
? (queryClient.getQueryData<TQueryData>(queryKey) ?? null)
|
||||
: null;
|
||||
const transformedData = await preCallback(currentData, variables);
|
||||
if (queryKey) queryClient.setQueryData(queryKey, transformedData);
|
||||
}
|
||||
|
||||
return result as TData;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
try {
|
||||
const result = await busterSocket.emitAndOnce({
|
||||
emitEvent: {
|
||||
route: socketRequest.route,
|
||||
payload: variables
|
||||
} as BusterSocketRequest,
|
||||
responseEvent: {
|
||||
route: socketResponse.route,
|
||||
onError: socketResponse.onError,
|
||||
callback: (d: unknown) => d
|
||||
} as BusterSocketResponse
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
const socketData = result as InferBusterSocketResponseData<TRoute>;
|
||||
const currentData = queryKey
|
||||
? (queryClient.getQueryData<TQueryData>(queryKey) ?? null)
|
||||
: null;
|
||||
const transformedData = callback(socketData, currentData);
|
||||
if (queryKey) queryClient.setQueryData(queryKey, transformedData);
|
||||
return result as TData;
|
||||
}
|
||||
|
||||
return result as TData;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
return useMutation<TData, TError, TPayload>({
|
||||
mutationFn
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useBusterWebSocket } from '../../BusterWebSocket';
|
||||
import { useSocketQueryEmitAndOnce, useSocketQueryMutation } from '@/api/buster_socket_query';
|
||||
import { useSocketQueryMutation } from '@/api/buster_socket_query';
|
||||
import { queryKeys } from '@/api/asset_interfaces';
|
||||
|
||||
const getChatsListOptions = queryKeys['/chats/list:getChatsList']();
|
||||
|
||||
export const useChatAssosciations = () => {
|
||||
const busterSocket = useBusterWebSocket();
|
||||
const x = useSocketQueryMutation('');
|
||||
const { mutate: deleteChat } = useSocketQueryMutation(
|
||||
{ route: '/chats/delete' },
|
||||
{ route: '/chats/delete:deleteChat' },
|
||||
getChatsListOptions,
|
||||
(currentData, deleteDataIds) => {
|
||||
//TODO: maybe use query client to remove all the chats from the query cache?
|
||||
const allDeleteDataIds = deleteDataIds.map((d) => d.id);
|
||||
return currentData?.filter((chat) => !allDeleteDataIds.includes(chat.id)) || [];
|
||||
}
|
||||
);
|
||||
|
||||
const onDeleteChat = useMemoizedFn(async (chatId: string) => {
|
||||
//
|
||||
// await busterSocket.emit({
|
||||
// route: '/chats/delete',
|
||||
// payload: { id: chatId }
|
||||
// });
|
||||
deleteChat([{ id: chatId }]);
|
||||
});
|
||||
|
||||
const onDeleteChats = useMemoizedFn(async (chatIds: string[]) => {
|
||||
deleteChat(chatIds.map((id) => ({ id })));
|
||||
});
|
||||
|
||||
return {
|
||||
onDeleteChat
|
||||
onDeleteChat,
|
||||
onDeleteChats
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,35 +1,37 @@
|
|||
import type { BusterCollection } from '@/api/asset_interfaces';
|
||||
import type { CollectionUpdateCollection } from '@/api/buster_socket/collections';
|
||||
import { useSocketQueryMutation } from '@/hooks';
|
||||
import { useSocketQueryMutation } from '@/api/buster_socket_query';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { BusterCollection, BusterCollectionListItem, queryKeys } from '@/api/asset_interfaces';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const useCollectionUpdate = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutateAsync: updateCollection, isPending: isUpdatingCollection } = useSocketQueryMutation(
|
||||
{ route: '/collections/update' },
|
||||
{ route: '/collections/update:collectionState' },
|
||||
{
|
||||
preSetQueryData: [
|
||||
{
|
||||
responseRoute: '/collections/get:collectionState',
|
||||
callback: (data, _variables) => {
|
||||
const variables = _variables as Partial<BusterCollection>;
|
||||
const newObject: BusterCollection = { ...data!, ...variables };
|
||||
return newObject;
|
||||
}
|
||||
},
|
||||
{
|
||||
responseRoute: '/collections/list:listCollections',
|
||||
callback: (data, _variables) => {
|
||||
const existingData = data || [];
|
||||
const variables = _variables as Partial<BusterCollection>;
|
||||
return existingData.map((collection) =>
|
||||
collection.id === variables.id
|
||||
? { ...collection, name: variables.name || collection.name }
|
||||
: collection
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
null,
|
||||
(_, variables) => {
|
||||
const collectionId = variables.id!;
|
||||
const collectionOptions = queryKeys['/collections/get:collectionState'](collectionId);
|
||||
const queryKey = collectionOptions.queryKey;
|
||||
const collection = queryClient.getQueryData(queryKey);
|
||||
if (collection) {
|
||||
const newCollection: BusterCollection = {
|
||||
...collection!,
|
||||
...(variables as Partial<BusterCollection>)
|
||||
};
|
||||
queryClient.setQueryData(queryKey, newCollection);
|
||||
}
|
||||
|
||||
const collectionListOptions = queryKeys['/collections/list:getCollectionsList']();
|
||||
const collectionList = queryClient.getQueryData(collectionListOptions.queryKey);
|
||||
if (collectionList && variables.name) {
|
||||
const newCollectionList: BusterCollectionListItem[] = collectionList.map((collection) =>
|
||||
collection.id === collectionId ? { ...collection, name: variables.name! } : collection
|
||||
);
|
||||
queryClient.setQueryData(collectionListOptions.queryKey, newCollectionList);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,38 +1,25 @@
|
|||
import React, { PropsWithChildren, useLayoutEffect, useState } from 'react';
|
||||
import { useMemoizedFn, useUnmount } from 'ahooks';
|
||||
import React, { PropsWithChildren, useState } from 'react';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import {
|
||||
useContextSelector,
|
||||
createContext,
|
||||
ContextSelector
|
||||
} from '@fluentui/react-context-selector';
|
||||
import { BusterDashboardResponse } from '@/api/asset_interfaces';
|
||||
import { queryKeys } from '@/api/asset_interfaces';
|
||||
import { useDashboardAssosciations } from './useDashboardAssosciations';
|
||||
import { useDashboardCreate } from './useDashboardCreate';
|
||||
import { useDashboardUpdateConfig } from './useDashboardUpdateConfig';
|
||||
import { createQueryKey } from '@/hooks';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const useBusterDashboards = () => {
|
||||
const [openAddContentModal, setOpenAddContentModal] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const getAssetPassword = useBusterAssetsContextSelector((state) => state.getAssetPassword);
|
||||
|
||||
const getDashboard = useQuery({
|
||||
queryKey: ['/dashboards/get:getDashboardState', { id: '1' }],
|
||||
queryFn: () => {
|
||||
return { id: '1' };
|
||||
},
|
||||
enabled: false
|
||||
});
|
||||
|
||||
const getDashboardMemoized = useMemoizedFn((dashboardId: string) => {
|
||||
const { password } = getAssetPassword(dashboardId);
|
||||
const queryKey = createQueryKey(
|
||||
{ route: '/dashboards/get:getDashboardState' },
|
||||
{ route: '/dashboards/get', payload: { id: dashboardId, password } }
|
||||
);
|
||||
return queryClient.getQueryData<BusterDashboardResponse>(queryKey);
|
||||
const options = queryKeys['/dashboards/get:getDashboardState'](dashboardId);
|
||||
const queryKey = options.queryKey;
|
||||
const data = queryClient.getQueryData(queryKey);
|
||||
return data;
|
||||
});
|
||||
|
||||
const dashboardUpdateConfig = useDashboardUpdateConfig({ getDashboardMemoized });
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import type { BusterDashboardResponse } from '@/api/asset_interfaces';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useBusterWebSocket } from '@/context/BusterWebSocket';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useBusterDashboardListContextSelector } from '../DashboardListProvider/DashboardListProvider';
|
||||
import { useSocketQueryMutation } from '@/hooks';
|
||||
|
||||
export const useDashboardAssosciations = () => {
|
||||
const busterSocket = useBusterWebSocket();
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import type {
|
||||
BusterDashboard,
|
||||
BusterDashboardResponse,
|
||||
VerificationStatus
|
||||
import {
|
||||
queryKeys,
|
||||
type BusterDashboard,
|
||||
type BusterDashboardResponse,
|
||||
type VerificationStatus
|
||||
} from '@/api/asset_interfaces';
|
||||
import { DashboardUpdate } from '@/api/buster_socket/dashboards';
|
||||
import { useBusterWebSocket } from '@/context/BusterWebSocket';
|
||||
import { useSocketQueryMutation } from '@/hooks';
|
||||
import { useSocketQueryMutation } from '@/api/buster_socket_query';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { create } from 'mutative';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const useDashboardUpdateConfig = ({
|
||||
getDashboardMemoized
|
||||
|
@ -15,24 +17,25 @@ export const useDashboardUpdateConfig = ({
|
|||
getDashboardMemoized: (dashboardId: string) => BusterDashboardResponse | undefined;
|
||||
}) => {
|
||||
const busterSocket = useBusterWebSocket();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutateAsync: updateDashboard, isPending: isUpdatingDashboard } = useSocketQueryMutation(
|
||||
{ route: '/dashboards/update' },
|
||||
{ route: '/dashboards/update:updateDashboard' },
|
||||
{
|
||||
preSetQueryData: [
|
||||
{
|
||||
responseRoute: '/dashboards/get:getDashboardState',
|
||||
callback: (data, variables) => {
|
||||
const newObject: BusterDashboardResponse = create(data!, (draft) => {
|
||||
Object.assign(draft.dashboard, variables, {
|
||||
config: { ...draft.dashboard.config, ...variables.config }
|
||||
});
|
||||
});
|
||||
return newObject;
|
||||
}
|
||||
}
|
||||
]
|
||||
null,
|
||||
(_, variables) => {
|
||||
const options = queryKeys['/dashboards/get:getDashboardState'](variables.id);
|
||||
const queryKey = options.queryKey;
|
||||
const currentData = queryClient.getQueryData(queryKey);
|
||||
if (currentData) {
|
||||
const newObject: BusterDashboardResponse = create(currentData, (draft) => {
|
||||
Object.assign(draft.dashboard, variables, {
|
||||
config: { ...draft.dashboard.config, ...variables.config }
|
||||
});
|
||||
});
|
||||
queryClient.setQueryData(queryKey, newObject);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -93,6 +96,7 @@ export const useDashboardUpdateConfig = ({
|
|||
});
|
||||
|
||||
return {
|
||||
isUpdatingDashboard,
|
||||
onShareDashboard,
|
||||
onUpdateDashboardConfig,
|
||||
onUpdateDashboard,
|
||||
|
|
Loading…
Reference in New Issue