balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

Grid.js (5849B)


      1 import {
      2     useEffect, useState, useCallback, useRef,
      3 } from '@wordpress/element'
      4 import { useTemplatesStore } from '../../state/Templates'
      5 import { Templates as TemplatesApi } from '../../api/Templates'
      6 import { useInView } from 'react-intersection-observer'
      7 import { Spinner, Button } from '@wordpress/components'
      8 import { __, sprintf } from '@wordpress/i18n'
      9 import { useIsMounted } from '../../hooks/helpers'
     10 import TemplateButton from '../../components/TemplateButton'
     11 
     12 export default function TemplatesList() {
     13     const isMounted = useIsMounted()
     14     const templates = useTemplatesStore(state => state.templates)
     15     const setActiveTemplate = useTemplatesStore(state => state.setActive)
     16     const appendTemplates = useTemplatesStore(state => state.appendTemplates)
     17     const [serverError, setServerError] = useState('')
     18     const [nothingFound, setNothingFound] = useState(false)
     19     // const [imagesLoaded, setImagesLoaded] = useState([])
     20     const [loadMoreRef, inView] = useInView()
     21 
     22     const updateSearchParams = useTemplatesStore(state => state.updateSearchParams)
     23     const searchParamsRaw = useTemplatesStore(state => state.searchParams)
     24 
     25     // Store the next page in case we have pagination
     26     const nextPage = useRef(useTemplatesStore.getState().nextPage)
     27     const searchParams = useRef(useTemplatesStore.getState().searchParams)
     28     // Connect to the store on mount, disconnect on unmount, catch state-changes in a reference
     29     useEffect(() => useTemplatesStore.subscribe(n => (nextPage.current = n),
     30         state => state.nextPage), [])
     31     useEffect(() => useTemplatesStore.subscribe(s => (searchParams.current = s),
     32         state => state.searchParams), [])
     33 
     34     // Fetch the templates then add them to the current state
     35     // TODO: This works, but it's not really doing what it's intended to do
     36     // as it has a side effect in there, and isn't pure.
     37     // It could be extracted to a hook
     38     const fetchTemplates = useCallback(async () => {
     39         setServerError('')
     40         setNothingFound(false)
     41         const response = await TemplatesApi.get(searchParams.current, { offset: nextPage.current })
     42             .catch((error) => {
     43                 console.error(error)
     44                 setServerError(error && error.message
     45                     ? error.message
     46                     : __('Unknown error occured. Check browser console or contact support.', 'extendify-sdk'))
     47             })
     48         if (!isMounted.current) {
     49             return
     50         }
     51         if (response?.error?.length) {
     52             setServerError(response?.error)
     53         }
     54         if (response?.records && searchParamsRaw === searchParams.current) {
     55             useTemplatesStore.setState({
     56                 nextPage: response.offset,
     57             })
     58             appendTemplates(response.records)
     59             setNothingFound(response.records.length <= 0)
     60         }
     61     }, [searchParamsRaw, appendTemplates, isMounted])
     62 
     63     // This is the main driver for loading templates
     64     // This loads the initial batch of templates. But if we don't yet have taxonomies.
     65     // There's also an option to skip loading on first mount
     66     useEffect(() => {
     67         if (!Object.keys(searchParams.current.taxonomies).length) {
     68             return
     69         }
     70 
     71         if (useTemplatesStore.getState().skipNextFetch) {
     72             // This is useful if the templates are fetched already and
     73             // the library moves to/from another state that re-renders
     74             // The point is to keep the logic close to the list. That may change someday
     75             useTemplatesStore.setState({
     76                 skipNextFetch: false,
     77             })
     78             return
     79         }
     80         // setImagesLoaded([])
     81         fetchTemplates()
     82     }, [fetchTemplates, searchParams])
     83 
     84     // Fetches when the load more is in view
     85     useEffect(() => {
     86         inView && fetchTemplates()
     87     }, [inView, fetchTemplates])
     88 
     89     if (serverError.length) {
     90         return <div className="text-left">
     91             <h2 className="text-left">{__('Server error', 'extendify-sdk')}</h2>
     92             <code className="block max-w-xl p-4 mb-4" style={{
     93                 minHeight: '10rem',
     94             }}>{serverError}</code>
     95             <Button isTertiary onClick={() => {
     96                 // setImagesLoaded([])
     97                 updateSearchParams({
     98                     taxonomies: {},
     99                     search: '',
    100                 })
    101                 fetchTemplates()
    102             }}>{ __('Press here to reload experience')}</Button>
    103         </div>
    104     }
    105 
    106     if (nothingFound) {
    107         if (searchParamsRaw?.search.length) {
    108             return <h2 className="text-left">
    109                 {sprintf(__('No results for %s.', 'extendify-sdk'), searchParamsRaw?.search)}
    110             </h2>
    111         }
    112         return <h2 className="text-left">{__('No results found.', 'extendify-sdk')}</h2>
    113     }
    114 
    115     if (!templates.length) {
    116         return <div className="flex items-center justify-center w-full sm:mt-64">
    117             <Spinner/>
    118         </div>
    119     }
    120 
    121     return <>
    122         <ul className="flex-grow gap-6 grid xl:grid-cols-2 2xl:grid-cols-3 pb-32 m-0">
    123             {templates.map((template) => {
    124                 return <li key={template.id}>
    125                     <TemplateButton
    126                         template={template}
    127                         setActiveTemplate={() => setActiveTemplate(template)}
    128                         imageLoaded={() => {}}
    129                     />
    130                 </li>
    131             })}
    132         </ul>
    133         {useTemplatesStore.getState().nextPage && <>
    134             <div
    135                 className="-translate-y-full flex flex-col h-80 items-end justify-end my-2 relative transform z-0 text"
    136                 ref={loadMoreRef}
    137                 style={{ zIndex: -1 }}>
    138             </div>
    139             <div className="my-4">
    140                 <Spinner/>
    141             </div>
    142         </>}
    143     </>
    144 }