import { ApolloError, ApolloQueryResult, QueryResult, useQuery as useBaseQuery, WatchQueryFetchPolicy } from "@apollo/client"; import { handleQueryAuthError } from "@saleor/auth"; import { useUser } from "@saleor/auth"; import { RequireAtLeastOne } from "@saleor/misc"; import { ServerErrorWithName } from "@saleor/types"; import { DocumentNode } from "graphql"; import { getOperationAST } from "graphql"; import { useEffect } from "react"; import { useIntl } from "react-intl"; import { User_userPermissions } from "../fragments/types/User"; import { PrefixedPermissions } from "../types/extendedTypes"; import { PermissionEnum } from "../types/globalTypes"; import useAppState from "./useAppState"; import useNotifier from "./useNotifier"; const getPermissionKey = (permission: string) => `PERMISSION_${permission}` as PrefixedPermissions; const allPermissions = Object.keys(PermissionEnum).reduce( (prev, code) => ({ ...prev, [getPermissionKey(code)]: false }), {} as Record ); const getUserPermissions = (userPermissions: User_userPermissions[]) => 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 UseQueryResult = QueryResult & LoadMore; export type UseQueryOpts = Partial<{ displayLoader: boolean; skip: boolean; variables: TVariables; fetchPolicy: WatchQueryFetchPolicy; handleError?: (error: ApolloError) => void | undefined; }>; type UseQueryHook = ( opts?: UseQueryOpts> ) => UseQueryResult; function makeQuery( query: DocumentNode ): UseQueryHook { function useQuery({ displayLoader, skip, variables, fetchPolicy, handleError, ...rest }: UseQueryOpts = {}): UseQueryResult { const notify = useNotifier(); const intl = useIntl(); const [, dispatchAppState] = useAppState(); const user = useUser(); const userPermissions = getUserPermissions( user.user?.userPermissions || [] ); const variablesWithPermissions = { ...variables, ...allPermissions, ...userPermissions }; const queryData = useBaseQuery(query, { context: { useBatching: true }, errorPolicy: "all", fetchPolicy: fetchPolicy ?? "cache-and-network", onError: error => { // TO-INVESTIGATE-BATCHING if ( (error.networkError as ServerErrorWithName).operationName === getOperationAST(query).name.value ) { if (!!handleError) { handleError(error); } else { handleQueryAuthError(error, notify, user.logout, intl); } } }, skip, variables: variablesWithPermissions, ...rest }); 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 }; } return useQuery; } export default makeQuery;