import { debounce } from '@graphcommerce/ecommerce-ui' import type { ApolloClient } from '@graphcommerce/graphql' import { getPrivateQueryContext, usePrivateQuery, useQuery } from '@graphcommerce/graphql' import { StoreConfigDocument } from '@graphcommerce/magento-store' import { showPageLoadIndicator } from '@graphcommerce/next-ui' import { useEventCallback } from '@mui/material' import type { FilterFormProviderProps, ProductFiltersQuery, ProductFiltersQueryVariables, } from '../components' import { hasUserFilterActive, ProductFiltersDocument } from '../components' import type { ProductListQuery, ProductListQueryVariables, } from '../components/ProductList/ProductList.gql' import { ProductListDocument } from '../components/ProductList/ProductList.gql' import type { CategoryDefaultFragment } from '../components/ProductListItems/CategoryDefault.gql' import { useRouterFilterParams } from '../components/ProductListItems/filteredProductList' import type { ProductListParams } from '../components/ProductListItems/filterTypes' import { toProductListParams } from '../components/ProductListItems/filterTypes' import { categoryDefaultsToProductListFilters, productListApplyCategoryDefaults, useProductListApplyCategoryDefaults, } from '../components/ProductListItems/productListApplyCategoryDefaults' const productListQueries: Array> = [] type Next = Parameters>[1] export const prefetchProductList = debounce( async ( variables: ProductListQueryVariables, filtersVariables: ProductFiltersQueryVariables, next: Next, client: ApolloClient, shallow: boolean, ) => { if (!shallow) return next(shallow) showPageLoadIndicator.set(true) const context = getPrivateQueryContext(client) const productList = client.query({ query: ProductListDocument, variables: { ...variables, context }, }) // const productFilters = client.query({ // query: ProductFiltersDocument, // variables: { // ...filtersVariables, // context, // }, // }) const both = Promise.all([productList]) // Push the query to the queue array. productListQueries.push(both) // Since we're waiting here the form will be submitting for longer. await both const includes = productListQueries.includes(both) // Remove all requests that are before the current request const index = productListQueries.indexOf(both) if (index > -1) { // eslint-disable-next-line @typescript-eslint/no-floating-promises productListQueries.splice(0, index + 1) } if (productListQueries.length === 0) showPageLoadIndicator.set(false) if (includes) { // todo: When navigating a category, it should now be a shallow route // If the resolved request is still in the array, it may be rendered (URL may be updated) await next(shallow) } return undefined }, 200, // the maxWait is now set to a somewhat shorter time than the average query time. { leading: true, maxWait: 700, trailing: true }, ) /** * - Handles shallow routing requests * - Handles customer specific product list queries */ export function useProductList< T extends ProductListQuery & ProductFiltersQuery & { params?: ProductListParams category?: CategoryDefaultFragment | null | undefined }, >(props: T) { const { category } = props const { params, shallow } = useRouterFilterParams(props) const variables = useProductListApplyCategoryDefaults(params, category) const result = usePrivateQuery(ProductListDocument, { variables, skip: !shallow }, props) const filters = usePrivateQuery( ProductFiltersDocument, { variables: categoryDefaultsToProductListFilters(variables), skip: !shallow || !hasUserFilterActive(params), }, props, ) const storeConfig = useQuery(StoreConfigDocument).data const handleSubmit: NonNullable = useEventCallback( async (formValues, next) => { if (!storeConfig) return const vars = await productListApplyCategoryDefaults( toProductListParams(formValues), storeConfig, category, ) const shallowNow = hasUserFilterActive(params) === hasUserFilterActive(vars) await prefetchProductList( vars, categoryDefaultsToProductListFilters(vars), next, result.client, shallowNow, ) }, ) return { ...props, filters: filters.data.filters, ...result.data, params, mask: result.mask, handleSubmit, } }