%PDF- %PDF-
Direktori : /var/www/projetos/takthua.com.br/wp-content/plugins/ml-slider/extendify-sdk/src/pages/ |
Current File : /var/www/projetos/takthua.com.br/wp-content/plugins/ml-slider/extendify-sdk/src/pages/GridView.js |
import { Spinner, Button } from '@wordpress/components' import { useEffect, useState, useCallback, useRef, memo, } from '@wordpress/element' import { __, sprintf } from '@wordpress/i18n' import { cloneDeep } from 'lodash' import { useInView } from 'react-intersection-observer' import Masonry from 'react-masonry-css' import { Templates as TemplatesApi } from '@extendify/api/Templates' import { ImportTemplateBlock } from '@extendify/components/ImportTemplateBlock' import { useIsMounted } from '@extendify/hooks/helpers' import { useTestGroup } from '@extendify/hooks/useTestGroup' import { useGlobalStore } from '@extendify/state/GlobalState' import { useTaxonomyStore } from '@extendify/state/Taxonomies' import { useTemplatesStore } from '@extendify/state/Templates' export const GridView = memo(function GridView() { const isMounted = useIsMounted() const templates = useTemplatesStore((state) => state.templates) const appendTemplates = useTemplatesStore((state) => state.appendTemplates) const [serverError, setServerError] = useState('') const retryOnce = useRef(false) const [nothingFound, setNothingFound] = useState(false) const [loading, setLoading] = useState(false) const [loadMoreRef, inView] = useInView() const searchParamsRaw = useTemplatesStore((state) => state.searchParams) const currentType = useGlobalStore((state) => state.currentType) const resetTemplates = useTemplatesStore((state) => state.resetTemplates) const open = useGlobalStore((state) => state.open) const taxonomies = useTaxonomyStore((state) => state.taxonomies) const updateType = useTemplatesStore((state) => state.updateType) const updateTaxonomies = useTemplatesStore( (state) => state.updateTaxonomies, ) // 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] const defaultOrAlt = useTestGroup('default-or-alt-sitetype', ['A', 'B']) // Subscribing to the store will keep these values updates synchronously useEffect(() => { return useTemplatesStore.subscribe( (state) => state.nextPage, (n) => (nextPage.current = n), ) }, []) useEffect(() => { return useTemplatesStore.subscribe( (state) => state.searchParams, (s) => (searchParams.current = s), ) }, []) // Fetch the templates then add them to the current state const fetchTemplates = useCallback(() => { if (!defaultOrAlt) { return } setServerError('') setNothingFound(false) const defaultError = __( 'Unknown error occured. Check browser console or contact support.', 'extendify', ) const args = { offset: nextPage.current } // AB test the default or defaultAlt site type const defaultSiteType = defaultOrAlt === 'A' ? { slug: 'default' } : { slug: 'defaultAlt' } const siteType = searchParams.current.taxonomies?.siteType?.slug?.length ? searchParams.current.taxonomies.siteType : defaultSiteType // End AB test - otherwise use { slug: 'default' } when empty const params = cloneDeep(searchParams.current) params.taxonomies.siteType = siteType TemplatesApi.get(params, 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, defaultOrAlt]) useEffect(() => { if (templates?.length === 0) { setLoading(true) return } }, [templates?.length, searchParamsRaw]) useEffect(() => { // If there's a server error, retry the request // This is temporary until we upgrade the bckend and add // a tool like react query to handle this automatically if (!retryOnce.current && serverError.length) { retryOnce.current = true fetchTemplates() } }, [serverError, fetchTemplates]) useEffect(() => { // This will check the URL for a pattern type and set that and remove it // TODO: possibly refactor this if we exapnd it to support layouts if (!open || !taxonomies?.patternType?.length) return const search = new URLSearchParams(window.location.search) if (!search.has('ext-patternType')) return const term = search.get('ext-patternType') // Delete it right away search.delete('ext-patternType') window.history.replaceState( null, null, window.location.pathname + '?' + search.toString(), ) // Search the slug in patternTypes const tax = taxonomies.patternType.find((t) => t.slug === term) if (!tax) return updateTaxonomies({ patternType: tax }) updateType('pattern') }, [open, taxonomies, updateType, updateTaxonomies]) // 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() return () => resetTemplates() }, [fetchTemplates, searchParams, resetTemplates]) // Fetches when the load more is in view useEffect(() => { nextPage.current && inView && fetchTemplates() }, [inView, fetchTemplates, templates]) if (serverError.length && retryOnce.current) { return ( <div className="text-left"> <h2 className="text-left">{__('Server error', 'extendify')}</h2> <code className="mb-4 block max-w-xl p-4" style={{ minHeight: '10rem' }}> {serverError} </code> <Button isTertiary onClick={() => { retryOnce.current = false fetchTemplates() }}> {__('Press here to reload')} </Button> </div> ) } if (nothingFound) { return ( <div className="-mt-2 flex h-full w-full items-center justify-center sm:mt-0"> <h2 className="text-sm font-normal text-extendify-gray"> {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, )} </h2> </div> ) } return ( <> {loading && ( <div className="-mt-2 flex h-full w-full items-center justify-center sm:mt-0"> <Spinner /> </div> )} <Grid type={currentType} templates={templates}> {templates.map((template) => { return ( <ImportTemplateBlock maxHeight={ currentType === 'template' ? 520 : 'none' } key={template.id} template={template} /> ) })} </Grid> {nextPage.current && ( <> <div className="my-20"> <Spinner /> </div> {/* This is a large div that, when in view, will trigger more patterns to load */} <div className="relative flex -translate-y-full transform flex-col items-end justify-end" ref={loadMoreRef} style={{ zIndex: -1, marginBottom: '-100%', height: currentType === 'template' ? '150vh' : '75vh', }} /> </> )} </> ) }) const Grid = ({ type, children }) => { const sharedClasses = 'relative min-h-screen z-10 pb-40 pt-0.5' switch (type) { case 'template': return ( <div className={`grid gap-6 md:gap-8 lg:grid-cols-2 ${sharedClasses}`}> {children} </div> ) } const breakpointColumnsObj = { default: 3, 1600: 2, 860: 1, 599: 2, 400: 1, } return ( <Masonry breakpointCols={breakpointColumnsObj} className={`-ml-6 flex w-auto px-0.5 md:-ml-8 ${sharedClasses}`} columnClassName="pl-6 md:pl-8 bg-clip-padding space-y-6 md:space-y-8"> {children} </Masonry> ) }