import Masonry from 'react-masonry-css' import { useEffect, useState, useCallback, useRef, memo, } from '@wordpress/element' import { Spinner, Button } from '@wordpress/components' import { __, sprintf } from '@wordpress/i18n' import { useTemplatesStore } from '../state/Templates' import { Templates as TemplatesApi } from '../api/Templates' import { useInView } from 'react-intersection-observer' import { useIsMounted } from '../hooks/helpers' import { ImportTemplateBlock } from '../components/ImportTemplateBlock' export const GridView = memo(() => { const isMounted = useIsMounted() const templates = useTemplatesStore((state) => state.templates) const appendTemplates = useTemplatesStore((state) => state.appendTemplates) const [serverError, setServerError] = useState('') const [nothingFound, setNothingFound] = useState(false) const [loading, setLoading] = useState(false) const [loadMoreRef, inView] = useInView() const searchParamsRaw = useTemplatesStore((state) => state.searchParams) const resetTemplates = useTemplatesStore((state) => state.resetTemplates) // Store the next page in case we have pagination const nextPage = useRef(useTemplatesStore.getState().nextPage) const searchParams = useRef(useTemplatesStore.getState().searchParams) const taxonomyType = searchParams.current.type === 'pattern' ? 'patternType' : 'layoutType' const currentTax = searchParams.current.taxonomies[taxonomyType] // Subscribing to the store will keep these values updates synchronously useEffect(() => { return useTemplatesStore.subscribe( (n) => (nextPage.current = n), (state) => state.nextPage, ) }, []) useEffect(() => { return useTemplatesStore.subscribe( (s) => (searchParams.current = s), (state) => state.searchParams, ) }, []) // Fetch the templates then add them to the current state const fetchTemplates = useCallback(() => { setServerError('') setNothingFound(false) const defaultError = __( 'Unknown error occured. Check browser console or contact support.', 'extendify', ) const args = { offset: nextPage.current } TemplatesApi.get(searchParams.current, args) .then((response) => { if (!isMounted.current) return if (response?.error?.length) { setServerError(response?.error) return } if (response?.records?.length <= 0) { setNothingFound(true) return } if ( searchParamsRaw === searchParams.current && response?.records.length ) { useTemplatesStore.setState({ nextPage: response?.offset ?? '', }) appendTemplates(response.records) setLoading(false) } }) .catch((error) => { if (!isMounted.current) return console.error(error) setServerError(defaultError) }) }, [appendTemplates, isMounted, searchParamsRaw]) useEffect(() => { if (templates.length === 0) { setLoading(true) return } }, [templates.length, searchParamsRaw]) // This is the main driver for loading templates // This loads the initial batch of templates. But if we don't yet have taxonomies. // There's also an option to skip loading on first mount useEffect(() => { if (!Object.keys(searchParams.current.taxonomies).length) { return } if (useTemplatesStore.getState().skipNextFetch) { // This is useful if the templates are fetched already and // the library moves to/from another state that re-renders // The point is to keep the logic close to the list. That may change someday useTemplatesStore.setState({ skipNextFetch: false, }) return } fetchTemplates() }, [fetchTemplates, searchParams]) // Fetches when the load more is in view useEffect(() => { nextPage.current && inView && fetchTemplates() }, [inView, fetchTemplates, templates]) if (serverError.length) { return (

{__('Server error', 'extendify')}

{serverError}
) } if (nothingFound) { return (

{sprintf( searchParams.current.type === 'template' ? __( 'We couldn\'t find any layouts in the "%s" category.', 'extendify', ) : __( 'We couldn\'t find any patterns in the "%s" category.', 'extendify', ), currentTax?.title ?? currentTax.slug, )}

) } const breakpointColumnsObj = { default: 2, 1320: 2, 860: 1, 599: 2, 400: 1, } return ( <> {loading && (
)} {templates.map((template) => { return ( ) })} {nextPage.current && ( <>
{/* This is a large div that, when in view, will trigger more patterns to load */}
)} ) })