PluginProbe
Extendify / 0.2.0
Extendify v0.2.0
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / pages / GridView.js

GridView.js in Extendify 0.2.0, at src/pages/GridView.js

211 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import Masonry from 'react-masonry-css'
2 import { useEffect, useState, useCallback, useRef } from '@wordpress/element'
3 import { Spinner, Button } from '@wordpress/components'
4 import { __, sprintf } from '@wordpress/i18n'
5 import { useTemplatesStore } from '../state/Templates'
6 import { Templates as TemplatesApi } from '../api/Templates'
7 import { useInView } from 'react-intersection-observer'
8 import { useIsMounted } from '../hooks/helpers'
9 import { ImportTemplateBlock } from '../components/ImportTemplateBlock'
10
11 export default function GridView() {
12 const isMounted = useIsMounted()
13 const templates = useTemplatesStore((state) => state.templates)
14 const appendTemplates = useTemplatesStore((state) => state.appendTemplates)
15 const [serverError, setServerError] = useState('')
16 const [nothingFound, setNothingFound] = useState(false)
17 const [loading, setLoading] = useState(false)
18 const [loadMoreRef, inView] = useInView()
19 const updateSearchParams = useTemplatesStore(
20 (state) => state.updateSearchParams,
21 )
22 const searchParamsRaw = useTemplatesStore((state) => state.searchParams)
23
24 // Store the next page in case we have pagination
25 const nextPage = useRef(useTemplatesStore.getState().nextPage)
26 const searchParams = useRef(useTemplatesStore.getState().searchParams)
27 const taxonomyType =
28 searchParams.current.type === 'pattern' ? 'patternType' : 'layoutType'
29 const currentTax = searchParams.current.taxonomies[taxonomyType]
30
31 // Connect to the store on mount, disconnect on unmount, catch state-changes in a reference
32 useEffect(
33 () =>
34 useTemplatesStore.subscribe(
35 (n) => (nextPage.current = n),
36 (state) => state.nextPage,
37 ),
38 [],
39 )
40 useEffect(
41 () =>
42 useTemplatesStore.subscribe(
43 (s) => (searchParams.current = s),
44 (state) => state.searchParams,
45 ),
46 [],
47 )
48
49 // Fetch the templates then add them to the current state
50 // TODO: This works, but it's not really doing what it's intended to do
51 // as it has a side effect in there, and isn't pure.
52 // It could be extracted to a hook
53 const fetchTemplates = useCallback(async () => {
54 setServerError('')
55 setNothingFound(false)
56 const response = await TemplatesApi.get(searchParams.current, {
57 offset: nextPage.current,
58 }).catch((error) => {
59 console.error(error)
60 setServerError(
61 error && error.message
62 ? error.message
63 : __(
64 'Unknown error occured. Check browser console or contact support.',
65 'extendify',
66 ),
67 )
68 })
69 if (!isMounted.current) {
70 return
71 }
72 if (response?.error?.length) {
73 setServerError(response?.error)
74 }
75 if (response?.records && searchParamsRaw === searchParams.current) {
76 useTemplatesStore.setState({
77 nextPage: response.offset,
78 })
79 appendTemplates(response.records)
80 setNothingFound(response.records.length <= 0)
81 setLoading(false)
82 }
83 }, [searchParamsRaw, appendTemplates, isMounted])
84
85 useEffect(() => {
86 if (templates.length === 0) {
87 setLoading(true)
88 }
89 }, [templates.length, searchParamsRaw])
90
91 // This is the main driver for loading templates
92 // This loads the initial batch of templates. But if we don't yet have taxonomies.
93 // There's also an option to skip loading on first mount
94 useEffect(() => {
95 if (!Object.keys(searchParams.current.taxonomies).length) {
96 return
97 }
98
99 if (useTemplatesStore.getState().skipNextFetch) {
100 // This is useful if the templates are fetched already and
101 // the library moves to/from another state that re-renders
102 // The point is to keep the logic close to the list. That may change someday
103 useTemplatesStore.setState({
104 skipNextFetch: false,
105 })
106 return
107 }
108 fetchTemplates()
109 }, [fetchTemplates, searchParams])
110
111 // Fetches when the load more is in view
112 useEffect(() => {
113 nextPage.current && inView && fetchTemplates()
114 }, [inView, fetchTemplates, templates])
115
116 if (serverError.length) {
117 return (
118 <div className="text-left">
119 <h2 className="text-left">{__('Server error', 'extendify')}</h2>
120 <code
121 className="block max-w-xl p-4 mb-4"
122 style={{
123 minHeight: '10rem',
124 }}>
125 {serverError}
126 </code>
127 <Button
128 isTertiary
129 onClick={() => {
130 updateSearchParams({
131 taxonomies: {},
132 search: '',
133 })
134 fetchTemplates()
135 }}>
136 {__('Press here to reload experience')}
137 </Button>
138 </div>
139 )
140 }
141
142 if (nothingFound) {
143 return (
144 <div className="flex h-full items-center justify-center w-full -mt-2 sm:mt-0">
145 <h2 className="text-sm text-extendify-gray font-normal">
146 {sprintf(
147 searchParams.current.type === 'template'
148 ? __(
149 'We couldn\'t find any layouts in the "%s" category.',
150 'extendify',
151 )
152 : __(
153 'We couldn\'t find any patterns in the "%s" category.',
154 'extendify',
155 ),
156 currentTax?.title ?? currentTax.slug,
157 )}
158 </h2>
159 </div>
160 )
161 }
162
163 const breakpointColumnsObj = {
164 default: 2,
165 1320: 2,
166 860: 1,
167 599: 2,
168 400: 1,
169 }
170 return (
171 <>
172 {loading && (
173 <div className="flex h-full items-center justify-center w-full -mt-2 sm:mt-0">
174 <Spinner />
175 </div>
176 )}
177 <Masonry
178 breakpointCols={breakpointColumnsObj}
179 className="flex -ml-6 md:-ml-8 w-auto pb-40 pt-0.5 px-0.5 relative z-10"
180 columnClassName="pl-6 md:pl-8 bg-clip-padding min-h-screen">
181 {templates.map((template) => {
182 return (
183 <ImportTemplateBlock
184 key={template.id}
185 template={template}
186 />
187 )
188 })}
189 </Masonry>
190
191 {nextPage.current && (
192 <>
193 <div className="my-20">
194 <Spinner />
195 </div>
196 {/* This is a large div that, when in view, will trigger more patterns to load */}
197 <div
198 className="-translate-y-full flex flex-col items-end justify-end relative transform"
199 ref={loadMoreRef}
200 style={{
201 zIndex: -1,
202 marginBottom: '-200vh',
203 height: '200vh',
204 }}
205 />
206 </>
207 )}
208 </>
209 )
210 }
211