diff --git a/package-lock.json b/package-lock.json index 8ca6f1d4a..e631fd46b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@rxfork/r2wc-react-to-web-component": "^2.4.0", "@seamapi/fake-devicedb": "^1.6.1", "@seamapi/fake-seam-connect": "^1.76.0", - "@seamapi/http": "^1.40.0", + "@seamapi/http": "^1.40.1", "@seamapi/types": "^1.395.3", "@storybook/addon-designs": "^7.0.1", "@storybook/addon-essentials": "^7.0.2", @@ -5993,9 +5993,9 @@ } }, "node_modules/@seamapi/http": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@seamapi/http/-/http-1.40.0.tgz", - "integrity": "sha512-Ae7l3ZVD6s4wkwDD/csB+c8dQQLq24dLThRCH3iIcMhKSlPmyFJieCVuVa+4v1dIKvRbA6FHXAwokzHh1OQjtA==", + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@seamapi/http/-/http-1.40.1.tgz", + "integrity": "sha512-in/Mx88HTvoCYVrRP7J9Vztfzp/EHgu2LuQgyeRz7a9oya7Wxxcc1Ya5l3AijGUzpjvvDxfHJLHVgQMYDzZwJA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6cf740156..19c8b0485 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,7 @@ "@rxfork/r2wc-react-to-web-component": "^2.4.0", "@seamapi/fake-devicedb": "^1.6.1", "@seamapi/fake-seam-connect": "^1.76.0", - "@seamapi/http": "^1.40.0", + "@seamapi/http": "^1.40.1", "@seamapi/types": "^1.395.3", "@storybook/addon-designs": "^7.0.1", "@storybook/addon-essentials": "^7.0.2", diff --git a/src/lib/seam/index.ts b/src/lib/seam/index.ts index 097759f7e..8eab32f0c 100644 --- a/src/lib/seam/index.ts +++ b/src/lib/seam/index.ts @@ -12,6 +12,7 @@ export * from './devices/use-device-providers.js' export * from './devices/use-devices.js' export * from './SeamProvider.js' export * from './use-seam-client.js' +export * from './use-seam-infinite-query.js' export * from './use-seam-mutation.js' export * from './use-seam-mutation-without-workspace.js' export * from './use-seam-query.js' diff --git a/src/lib/seam/use-seam-infinite-query.ts b/src/lib/seam/use-seam-infinite-query.ts new file mode 100644 index 000000000..ed8eac9d9 --- /dev/null +++ b/src/lib/seam/use-seam-infinite-query.ts @@ -0,0 +1,81 @@ +import type { + SeamHttpApiError, + SeamHttpEndpointPaginatedQueryPaths, + SeamHttpEndpoints, + SeamHttpRequest, + SeamPageCursor, +} from '@seamapi/http/connect' +import { + useInfiniteQuery, + type UseInfiniteQueryOptions, + type UseInfiniteQueryResult, +} from '@tanstack/react-query' + +import { useSeamClient } from 'lib/seam/use-seam-client.js' + +export type UseSeamInfiniteQueryParameters< + T extends SeamHttpEndpointPaginatedQueryPaths, +> = Parameters[0] + +export type UseSeamInfiniteQueryResult< + T extends SeamHttpEndpointPaginatedQueryPaths, +> = UseInfiniteQueryResult, SeamHttpApiError> + +export function useSeamInfiniteQuery< + T extends SeamHttpEndpointPaginatedQueryPaths, +>( + endpointPath: T, + parameters?: UseSeamInfiniteQueryParameters, + options: Parameters[1] & + QueryOptions, SeamHttpApiError> = {} +): UseSeamInfiniteQueryResult { + const { endpointClient: client, queryKeyPrefixes } = useSeamClient() + return useInfiniteQuery({ + enabled: client != null, + ...options, + queryKey: [ + ...queryKeyPrefixes, + ...endpointPath.split('/').filter((v) => v !== ''), + parameters, + ], + initialPageParam: null, + getNextPageParam: (lastPage) => lastPage.nextPageCursor, + queryFn: async ({ pageParam }) => { + if (client == null) + return { + data: [] as Awaited>, + nextPageCursor: null, + } + // Using @ts-expect-error over any is preferred, but not possible here because TypeScript will run out of memory. + // Type assertion is needed here for performance reasons. The types are correct at runtime. + const endpoint = client[endpointPath] as (...args: any) => any + const request = endpoint(parameters, options) + const pages = client.createPaginator(request as SeamHttpRequest) + if (pageParam == null) { + const [data, { nextPageCursor }] = await pages.firstPage() + return { + data: data as Awaited>, + nextPageCursor, + } + } + // Type assertion is needed for pageParam since the Seam API expects a branded PageCursor type. + const [data, { nextPageCursor }] = await pages.nextPage( + pageParam as SeamPageCursor + ) + return { + data: data as Awaited>, + nextPageCursor, + } + }, + }) +} + +interface QueryData { + data: Awaited> + nextPageCursor: SeamPageCursor | null +} + +type QueryOptions = Omit< + UseInfiniteQueryOptions, + 'queryKey' | 'queryFn' | 'initialPageParam' | 'getNextPageParam' +> diff --git a/src/lib/seam/use-seam-mutation-without-workspace.ts b/src/lib/seam/use-seam-mutation-without-workspace.ts index e1890d6b6..6bbae1441 100644 --- a/src/lib/seam/use-seam-mutation-without-workspace.ts +++ b/src/lib/seam/use-seam-mutation-without-workspace.ts @@ -23,7 +23,7 @@ export type UseSeamMutationWithoutWorkspaceResult< UseSeamMutationWithoutWorkspaceVariables > -export function UseSeamMutationWithoutWorkspace< +export function useSeamMutationWithoutWorkspace< T extends SeamHttpEndpointWithoutWorkspaceMutationPaths, >( endpointPath: T,