import { useMemo, useCallback } from "react" import { QueryKey, useSuspenseInfiniteQuery, UseSuspenseInfiniteQueryResult, UseSuspenseInfiniteQueryOptions, InfiniteData, } from "@tanstack/react-query" import { FunctionName, Reactor, TransformKey, ReactorArgs, ReactorReturnOk, ReactorReturnErr, } from "@ic-reactor/core" import { CallConfig } from "@icp-sdk/core/agent" /** * Parameters for useActorSuspenseInfiniteQuery hook. * Extends react-query's UseSuspenseInfiniteQueryOptions with custom reactor params. */ export interface UseActorSuspenseInfiniteQueryParameters< A, M extends FunctionName, T extends TransformKey = "candid", TPageParam = unknown, TSelected = InfiniteData, TPageParam>, > extends Omit< UseSuspenseInfiniteQueryOptions< ReactorReturnOk, ReactorReturnErr, TSelected, QueryKey, TPageParam >, "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam" > { /** The reactor instance to use for method calls */ reactor: Reactor /** The method name to call on the canister */ functionName: M /** Function to get args from page parameter */ getArgs: (pageParam: TPageParam) => ReactorArgs /** Agent call configuration (effectiveCanisterId, etc.) */ callConfig?: CallConfig /** Custom query key (auto-generated if not provided) */ queryKey?: QueryKey /** Initial page parameter */ initialPageParam: TPageParam /** Function to determine next page parameter */ getNextPageParam: ( lastPage: ReactorReturnOk, allPages: ReactorReturnOk[], lastPageParam: TPageParam, allPageParams: TPageParam[] ) => TPageParam | undefined | null } export type UseActorSuspenseInfiniteQueryConfig< A, M extends FunctionName, T extends TransformKey = "candid", TPageParam = unknown, > = Omit< UseActorSuspenseInfiniteQueryParameters, "reactor" > export type UseActorSuspenseInfiniteQueryResult< A, M extends FunctionName, T extends TransformKey = "candid", TPageParam = unknown, > = UseSuspenseInfiniteQueryResult< InfiniteData, TPageParam>, ReactorReturnErr > /** * Hook for executing suspense-enabled infinite/paginated query calls on a canister. * * @example * const { data, fetchNextPage, hasNextPage } = useActorSuspenseInfiniteQuery({ * reactor, * functionName: "getItems", * getArgs: (pageParam) => [{ offset: pageParam, limit: 10 }], * initialPageParam: 0, * getNextPageParam: (lastPage) => lastPage.nextOffset, * }) */ export const useActorSuspenseInfiniteQuery = < A, M extends FunctionName, T extends TransformKey = "candid", TPageParam = unknown, >({ reactor, functionName, getArgs, callConfig, queryKey, ...options }: UseActorSuspenseInfiniteQueryParameters< A, M, T, TPageParam >): UseActorSuspenseInfiniteQueryResult => { // Always pass queryKey through generateQueryKey so it is merged with the // reactor/function identity. Using the custom key verbatim would cause cache // collisions if two different actors or methods share the same key string. const baseQueryKey = useMemo( () => reactor.generateQueryKey({ functionName, queryKey }, callConfig), [queryKey, reactor, functionName, callConfig] ) // Memoize queryFn to prevent recreation on every render const queryFn = useCallback( async ({ pageParam }: { pageParam: TPageParam }) => { const args = getArgs(pageParam) return reactor.callMethod({ functionName, args, callConfig, }) }, [reactor, functionName, getArgs, callConfig] ) return useSuspenseInfiniteQuery( { queryKey: baseQueryKey, queryFn, ...options, } as any, reactor.queryClient ) as UseActorSuspenseInfiniteQueryResult }