mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
132 lines
3.1 KiB
TypeScript
132 lines
3.1 KiB
TypeScript
import {
|
|
gqlFetcherFactory,
|
|
type GraphQLQuery,
|
|
type QueryOptions,
|
|
type QueryResponse,
|
|
} from '@affine/graphql';
|
|
import type { GraphQLError } from 'graphql';
|
|
import { useCallback, useMemo } from 'react';
|
|
import type { SWRConfiguration, SWRResponse } from 'swr';
|
|
import useSWR from 'swr';
|
|
import useSWRImmutable from 'swr/immutable';
|
|
import useSWRInfinite from 'swr/infinite';
|
|
|
|
/**
|
|
* A `useSWR` wrapper for sending graphql queries
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* import { someQuery, someQueryWithNoVars } from '@affine/graphql'
|
|
*
|
|
* const swrResponse1 = useQuery({
|
|
* query: workspaceByIdQuery,
|
|
* variables: { id: '1' }
|
|
* })
|
|
*
|
|
* const swrResponse2 = useQuery({
|
|
* query: someQueryWithNoVars
|
|
* })
|
|
* ```
|
|
*/
|
|
type useQueryFn = <Query extends GraphQLQuery>(
|
|
options?: QueryOptions<Query>,
|
|
config?: Omit<
|
|
SWRConfiguration<
|
|
QueryResponse<Query>,
|
|
GraphQLError,
|
|
(options: QueryOptions<Query>) => Promise<QueryResponse<Query>>
|
|
>,
|
|
'fetcher'
|
|
>
|
|
) => SWRResponse<
|
|
QueryResponse<Query>,
|
|
GraphQLError,
|
|
{
|
|
suspense: true;
|
|
}
|
|
>;
|
|
|
|
const createUseQuery =
|
|
(immutable: boolean): useQueryFn =>
|
|
(options, config) => {
|
|
const configWithSuspense: SWRConfiguration = useMemo(
|
|
() => ({
|
|
suspense: true,
|
|
...config,
|
|
}),
|
|
[config]
|
|
);
|
|
|
|
const useSWRFn = immutable ? useSWRImmutable : useSWR;
|
|
return useSWRFn(
|
|
options ? () => ['cloud', options.query.id, options.variables] : null,
|
|
options ? () => gqlFetcher(options) : null,
|
|
configWithSuspense
|
|
);
|
|
};
|
|
|
|
export const useQuery = createUseQuery(false);
|
|
export const useQueryImmutable = createUseQuery(true);
|
|
|
|
export const gqlFetcher = gqlFetcherFactory('/graphql', window.fetch);
|
|
|
|
export function useQueryInfinite<Query extends GraphQLQuery>(
|
|
options: Omit<QueryOptions<Query>, 'variables'> & {
|
|
getVariables: (
|
|
pageIndex: number,
|
|
previousPageData: QueryResponse<Query>
|
|
) => QueryOptions<Query>['variables'];
|
|
},
|
|
config?: Omit<
|
|
SWRConfiguration<
|
|
QueryResponse<Query>,
|
|
GraphQLError | GraphQLError[],
|
|
(options: QueryOptions<Query>) => Promise<QueryResponse<Query>>
|
|
>,
|
|
'fetcher'
|
|
>
|
|
) {
|
|
const configWithSuspense: SWRConfiguration = useMemo(
|
|
() => ({
|
|
suspense: true,
|
|
...config,
|
|
}),
|
|
[config]
|
|
);
|
|
|
|
const { data, setSize, size, error } = useSWRInfinite<
|
|
QueryResponse<Query>,
|
|
GraphQLError | GraphQLError[]
|
|
>(
|
|
(pageIndex: number, previousPageData: QueryResponse<Query>) => [
|
|
'cloud',
|
|
options.query.id,
|
|
options.getVariables(pageIndex, previousPageData),
|
|
],
|
|
async ([_, __, variables]) => {
|
|
const params = { ...options, variables } as QueryOptions<Query>;
|
|
return gqlFetcher(params);
|
|
},
|
|
configWithSuspense
|
|
);
|
|
|
|
const loadingMore = size > 0 && data && !data[size - 1];
|
|
|
|
// TODO(@Peng): find a generic way to know whether or not there are more items to load
|
|
const loadMore = useCallback(() => {
|
|
if (loadingMore) {
|
|
return;
|
|
}
|
|
setSize(size => size + 1).catch(err => {
|
|
console.error(err);
|
|
});
|
|
}, [loadingMore, setSize]);
|
|
return {
|
|
data,
|
|
error,
|
|
loadingMore,
|
|
loadMore,
|
|
};
|
|
}
|