create socket query

This commit is contained in:
Nate Kelley 2025-02-12 12:34:49 -07:00
parent 51291d44e0
commit ad02a021ac
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
13 changed files with 88 additions and 100 deletions

View File

@ -15,15 +15,22 @@ import { useBusterNotifications } from '@/context/BusterNotifications';
import { RustApiError } from './buster_rest/errors';
import { useMemoizedFn } from 'ahooks';
interface CreateQueryProps<T> extends UseQueryOptions {
interface CreateQueryProps<TData, TError = unknown> {
queryKey: QueryKey;
queryFn: () => Promise<TData>;
isUseSession?: boolean;
useErrorNotification?: boolean;
enabled?: boolean;
initialData?: TData;
refetchOnWindowFocus?: boolean;
refetchOnMount?: boolean;
staleTime?: number;
options?: Omit<UseQueryOptions<TData, TError, TData>, 'queryKey' | 'queryFn'>;
}
export const PREFETCH_STALE_TIME = 1000 * 10;
export const useCreateReactQuery = <T>({
export const useCreateReactQuery = <TData, TError = unknown>({
queryKey,
queryFn,
isUseSession = true,
@ -33,13 +40,13 @@ export const useCreateReactQuery = <T>({
refetchOnMount = true,
useErrorNotification = true,
staleTime,
...rest
}: CreateQueryProps<T>) => {
options = {}
}: CreateQueryProps<TData, TError>) => {
const { openErrorNotification } = useBusterNotifications();
const accessToken = useSupabaseContext((state) => state.accessToken);
const baseEnabled = isUseSession ? !!accessToken : true;
const q = useQuery({
const q = useQuery<TData, TError>({
queryKey: queryKey,
queryFn,
enabled: baseEnabled && !!enabled,
@ -48,7 +55,7 @@ export const useCreateReactQuery = <T>({
refetchOnWindowFocus,
refetchOnMount,
staleTime,
...rest
...options
});
useEffect(() => {

View File

@ -35,7 +35,7 @@ interface BusterSocket {
}) => Promise<Parameters<T['callback']>[0]>;
}
export const useBusterWebSocketHook = ({
const useBusterWebSocketHook = ({
socketURL,
accessToken,
checkTokenValidity
@ -195,13 +195,13 @@ export const BusterWebSocketProvider: React.FC<{
const accessToken = useSupabaseContext((state) => state.accessToken);
const checkTokenValidity = useSupabaseContext((state) => state.checkTokenValidity);
const value = useBusterWebSocketHook({
const busterSocketHook = useBusterWebSocketHook({
socketURL: BUSTER_WS_URL,
accessToken,
checkTokenValidity
});
return <BusterWebSocket.Provider value={value}>{children}</BusterWebSocket.Provider>;
return <BusterWebSocket.Provider value={busterSocketHook}>{children}</BusterWebSocket.Provider>;
});
BusterWebSocketProvider.displayName = 'BusterWebSocketProvider';
@ -212,7 +212,3 @@ const useBusterWebSocketSelector = <T,>(
export const useBusterWebSocket = () => {
return useBusterWebSocketSelector((state) => state.busterSocket);
};
export const useBusterWebSocketConnectionStatus = () => {
return useBusterWebSocketSelector((state) => state.connectionStatus);
};

View File

@ -1,8 +0,0 @@
import { UseBusterSocketQueryOptions } from './types';
export const DEFAULT_OPTIONS: Partial<UseBusterSocketQueryOptions<unknown, unknown>> = {
refetchOnWindowFocus: false,
refetchOnMount: true,
retry: 0,
staleTime: 0
};

View File

@ -1,12 +0,0 @@
import { BusterSocketRequest, BusterSocketResponse } from '@/api/buster_socket';
export const isSocketError = (error: unknown): error is Error => {
return error instanceof Error;
};
export const transformError = (error: unknown): Error => {
if (isSocketError(error)) {
return error;
}
return new Error('Unknown WebSocket error occurred');
};

View File

@ -1,3 +0,0 @@
export * from './types';
export * from './useBusterWebSocketQuery';
export * from './helpers';

View File

@ -1,25 +0,0 @@
import type { BusterSocketResponse, BusterSocketResponseRoute } from '@/api/buster_socket';
import { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
/**
* Infers the response data type from a BusterSocket route
*/
export type InferBusterSocketResponseData<TRoute extends BusterSocketResponseRoute> = Extract<
BusterSocketResponse,
{ route: TRoute }
>['callback'] extends (d: infer D) => void
? D
: never;
/**
* Socket response configuration with optional error handler
*/
export type BusterSocketResponseConfig<TRoute extends BusterSocketResponseRoute> = {
route: TRoute;
onError?: (d: unknown) => void;
};
export interface UseBusterSocketQueryOptions<TData, TError = unknown>
extends Omit<UseQueryOptions<TData, TError>, 'queryKey' | 'queryFn'> {}
export type UseBusterSocketQueryResult<TData, TError = unknown> = UseQueryResult<TData, TError>;

View File

@ -203,7 +203,7 @@ export const useUpdateMetricConfig = ({
route: '/metrics/update:updateMetricState',
callback: onInitializeMetric
}
}) as Promise<[BusterMetric]>;
});
}
);

View File

@ -1,3 +1,4 @@
export * from './react';
export * from './dom';
export * from './useDebounceSearch';
export * from './useBusterWebSocketQuery';

View File

@ -1,8 +0,0 @@
import { UseQueryOptions } from '@tanstack/react-query';
export const DEFAULT_OPTIONS: Partial<UseQueryOptions> = {
refetchOnWindowFocus: false,
refetchOnMount: true,
retry: 0,
staleTime: 0
};

View File

@ -0,0 +1 @@
export * from './useBusterWebSocketQuery';

View File

@ -1,13 +1,22 @@
import { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import {
BusterSocketRequestBase,
BusterSocketResponseBase
} from '@/api/buster_socket/base_interfaces';
import type { BusterSocketResponse, BusterSocketResponseRoute } from '@/api/buster_socket';
import { UseQueryResult } from '@tanstack/react-query';
export interface UseBusterSocketQueryOptions<TData, TError = unknown>
extends Omit<UseQueryOptions<TData, TError>, 'queryKey' | 'queryFn'> {
socketRequest: BusterSocketRequestBase;
socketResponse: Omit<BusterSocketResponseBase, 'callback' | 'onError'>;
}
/**
* Infers the response data type from a BusterSocket route
*/
export type InferBusterSocketResponseData<TRoute extends BusterSocketResponseRoute> = Extract<
BusterSocketResponse,
{ route: TRoute }
>['callback'] extends (d: infer D) => void
? D
: never;
/**
* Socket response configuration with optional error handler
*/
export type BusterSocketResponseConfig<TRoute extends BusterSocketResponseRoute> = {
route: TRoute;
onError?: (d: unknown) => void;
};
export type UseBusterSocketQueryResult<TData, TError = unknown> = UseQueryResult<TData, TError>;

View File

@ -0,0 +1,40 @@
'use client';
import { QueryKey, useQuery, useQueryClient } from '@tanstack/react-query';
import type { BusterSocketResponse, BusterSocketResponseRoute } from '@/api/buster_socket';
import { useBusterWebSocket } from '@/context/BusterWebSocket';
import type {
UseBusterSocketQueryResult,
InferBusterSocketResponseData,
BusterSocketResponseConfig
} from './types';
import { useMount } from 'ahooks';
export const useBusterWebSocketOn = <TRoute extends BusterSocketResponseRoute, TError = unknown>(
queryKey: QueryKey,
socketResponse: BusterSocketResponseConfig<TRoute>
): UseBusterSocketQueryResult<InferBusterSocketResponseData<TRoute>, TError> => {
const busterSocket = useBusterWebSocket();
const queryClient = useQueryClient();
useMount(() => {
busterSocket.on({
route: socketResponse.route,
onError: socketResponse.onError,
callback: (d: unknown) => {
queryClient.setQueryData(queryKey, d as InferBusterSocketResponseData<TRoute>);
queryClient.invalidateQueries({ queryKey });
}
} as BusterSocketResponse);
});
return useQuery({
queryKey
});
};
const ExampleUsage = () => {
const { data, isFetched } = useBusterWebSocketOn(['chats', 'get', '123'], {
route: '/chats/get:getChat'
});
};

View File

@ -1,13 +1,11 @@
import { useQuery, QueryKey, UseQueryOptions } from '@tanstack/react-query';
import { QueryKey, UseQueryOptions } from '@tanstack/react-query';
import type {
BusterSocketRequest,
BusterSocketResponse,
BusterSocketResponseRoute
} from '@/api/buster_socket';
import { useBusterWebSocket } from '../useBusterWebSocket';
import { transformError } from './helpers';
import { useBusterWebSocket } from '@/context/BusterWebSocket';
import type {
UseBusterSocketQueryOptions,
UseBusterSocketQueryResult,
InferBusterSocketResponseData,
BusterSocketResponseConfig
@ -18,7 +16,10 @@ export function useBusterWebSocketQuery<TRoute extends BusterSocketResponseRoute
queryKey: QueryKey,
socketRequest: BusterSocketRequest,
socketResponse: BusterSocketResponseConfig<TRoute>,
options?: UseQueryOptions<InferBusterSocketResponseData<TRoute>, TError>
options?: Omit<
UseQueryOptions<InferBusterSocketResponseData<TRoute>, TError>,
'queryKey' | 'queryFn'
>
): UseBusterSocketQueryResult<InferBusterSocketResponseData<TRoute>, TError> {
const busterSocket = useBusterWebSocket();
@ -39,30 +40,19 @@ export function useBusterWebSocketQuery<TRoute extends BusterSocketResponseRoute
}
};
// return useCreateReactQuery<InferBusterSocketResponseData<TRoute>>({
// queryKey,
// queryFn,
// isUseSession: false
// });
return useQuery<
InferBusterSocketResponseData<TRoute>,
TError,
InferBusterSocketResponseData<TRoute>
>({
return useCreateReactQuery<InferBusterSocketResponseData<TRoute>, TError>({
queryKey,
queryFn,
...options
isUseSession: false,
options
});
}
// Example usage with automatic type inference
export const ExampleUsage = () => {
const ExampleUsage = () => {
const { data, isLoading, error } = useBusterWebSocketQuery(
['chats', 'get', '123'],
{ route: '/chats/get', payload: { id: '123' } },
{ route: '/chats/get:getChat' }
);
useCreateReactQuery;
};