import { ApolloError, ApolloQueryResult, LazyQueryHookOptions as BaseLazyQueryHookOptions, OperationVariables, QueryHookOptions as BaseQueryHookOptions, QueryResult, useQuery as useBaseQuery, } from "@apollo/client"; import { handleQueryAuthError, useUser } from "@saleor/auth"; import { PrefixedPermissions } from "@saleor/graphql/extendedTypes"; import { PermissionEnum, UserPermissionFragment, } from "@saleor/graphql/types.generated"; import { RequireAtLeastOne } from "@saleor/misc"; import { DocumentNode } from "graphql"; import { useEffect } from "react"; import { useIntl } from "react-intl"; import useAppState from "./useAppState"; import useNotifier from "./useNotifier"; export { useLazyQuery } from "@apollo/client"; const getPermissionKey = (permission: string) => `PERMISSION_${permission}` as PrefixedPermissions; export const allPermissions: Record = Object.keys( PermissionEnum, ).reduce( (prev, code) => ({ ...prev, [getPermissionKey(code)]: false, }), {} as Record, ); const getUserPermissions = ( userPermissions: UserPermissionFragment[], ): Record => userPermissions.reduce( (prev, permission) => ({ ...prev, [getPermissionKey(permission.code)]: true, }), {} as Record, ); export interface LoadMore { loadMore: ( mergeFunc: (prev: TData, next: TData) => TData, extraVariables: Partial, ) => Promise>; } export type LazyQueryHookOptions< TData = any, TVariables = OperationVariables > = BaseLazyQueryHookOptions; export type UseQueryResult = QueryResult & LoadMore; export type QueryHookOptions = Partial< Omit, "variables"> & { displayLoader: boolean; handleError?: (error: ApolloError) => void | undefined; variables?: Omit; } >; type UseQueryHook = ( opts?: QueryHookOptions>, ) => UseQueryResult; export function useQuery( query: DocumentNode, { displayLoader, skip, variables, fetchPolicy, handleError, ...opts }: QueryHookOptions = {}, ): UseQueryResult { const notify = useNotifier(); const intl = useIntl(); const [, dispatchAppState] = useAppState(); const user = useUser(); const userPermissions = getUserPermissions(user.user?.userPermissions || []); const variablesWithPermissions = { ...variables, ...allPermissions, ...userPermissions, } as TVariables & Record; const queryData = useBaseQuery(query, { ...opts, context: { useBatching: true, }, errorPolicy: "all", fetchPolicy: fetchPolicy ?? "cache-and-network", onError: error => { if (!!handleError) { handleError(error); } else { handleQueryAuthError(error, notify, user.logout, intl); } }, skip, variables: variablesWithPermissions, }); useEffect(() => { if (displayLoader) { dispatchAppState({ payload: { value: queryData.loading, }, type: "displayLoader", }); } }, [queryData.loading]); const loadMore = ( mergeFunc: (previousResults: TData, fetchMoreResult: TData) => TData, extraVariables: RequireAtLeastOne, ) => queryData.fetchMore({ query, updateQuery: (previousResults, { fetchMoreResult }) => { if (!fetchMoreResult) { return previousResults; } return mergeFunc(previousResults, fetchMoreResult); }, variables: { ...variablesWithPermissions, ...extraVariables }, }); return { ...queryData, loadMore, }; } function makeQuery( query: DocumentNode, ): UseQueryHook { return (opts: QueryHookOptions) => useQuery(query, opts); } export default makeQuery;