import { atom } from 'jotai' ; import { selectAtom , useAtomValue } from 'jotai/utils' ; import { useRouter } from 'next/router' ; import { useCallback , useEffect , useMemo } from 'react' ; import type { SearchState } from 'react-instantsearch-core' ; import { searchStateToUrl , urlToSearchState , createURL , } from '@instantsearch/utils/url' ; import { autocompleteAtom } from '@/components/@autocomplete/_default/autocomplete' ; import { autocompleteStateAtom } from '@/components/@autocomplete/basic/autocomplete-basic' ; import { configAtom } from '@/config/config' ; import { useDebouncedCallback } from '@/hooks/useDebouncedCallback' ; import { useDeepCompareEffect } from '@/hooks/useDeepCompareEffect' ; import { useDeepUpdateAtom } from '@/hooks/useDeepUpdateAtom' ; import { isomorphicWindow } from '@/utils/browser' ; import { scrollToTop } from '@/utils/scrollToTop' ; export const searchStateAtom = atom < SearchState > ( urlToSearchState ( isomorphicWindow ?. location . href ) ) ; export const searchQueryAtom = selectAtom < SearchState , string | undefined > ( searchStateAtom , ( { query } ) => query ) ; export function useUrlSync ( ) { const router = useRouter ( ) ; const isCatalogPage = useMemo ( ( ) => router ?. pathname === '/catalog/[[...slugs]]' , [ router ?. pathname ] ) ; const [ searchState , _setSearchState ] = useDeepUpdateAtom ( searchStateAtom ) ; const setSearchState = useCallback ( ( nextSearchState ) => _setSearchState ( ( currentSearchState ) => ( { ... currentSearchState , ... nextSearchState , } ) ) , [ _setSearchState ] ) ; const pushRoute = useCallback ( ( nextSearchState ) => { if ( ! isCatalogPage ) return ; const newRoute = `/catalog${ searchStateToUrl ( nextSearchState ) }` ; if ( router . asPath !== newRoute ) { router . push ( newRoute , newRoute , { shallow : true } ) ; } } , [ router ?. asPath ] ) ; const { url : urlConfig } = useAtomValue ( configAtom ) ; const debouncedPushRoute = useDebouncedCallback ( pushRoute , urlConfig . debouncing ) ; const autocomplete = useAtomValue ( autocompleteAtom ) ; const autocompleteState = useAtomValue ( autocompleteStateAtom ) ; useEffect ( ( ) => { const handleRouteChange = ( ) => { const newSearchState = urlToSearchState ( window . location . href ) ; setSearchState ( newSearchState ) ; if ( ! newSearchState . query ) { autocomplete ?. setQuery ( '' ) ; } } ; router . events . on ( 'routeChangeComplete' , handleRouteChange ) ; return ( ) => { router . events . off ( 'routeChangeComplete' , handleRouteChange ) ; } ; } , [ router ?. events , setSearchState , autocomplete , autocompleteState ?. query ] ) ; const onSearchStateChange = useCallback ( ( nextSearchState ) => { setSearchState ( nextSearchState ) ; debouncedPushRoute ( nextSearchState ) ; } , [ setSearchState , debouncedPushRoute ] ) ; useDeepCompareEffect ( ( ) => { debouncedPushRoute ( searchState ) ; } , [ debouncedPushRoute , searchState ] ) ; useEffect ( ( ) => { if ( isCatalogPage ) { scrollToTop ( 'smooth' ) ; } } , [ isCatalogPage , searchState . query ] ) ; return { searchState , onSearchStateChange , createURL , } ; }