sanity 3.72.1 → 3.72.2-use-live-content-api.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/_chunks-cjs/LiveQueries.js +236 -163
- package/lib/_chunks-cjs/LiveQueries.js.map +1 -1
- package/lib/_chunks-cjs/PresentationToolGrantsCheck.js +3 -7
- package/lib/_chunks-cjs/PresentationToolGrantsCheck.js.map +1 -1
- package/lib/_chunks-cjs/buildAction.js +3 -0
- package/lib/_chunks-cjs/buildAction.js.map +1 -1
- package/lib/_chunks-cjs/presentation.js +3 -4
- package/lib/_chunks-cjs/presentation.js.map +1 -1
- package/lib/_chunks-cjs/version.js +1 -1
- package/lib/_chunks-es/LiveQueries.mjs +235 -162
- package/lib/_chunks-es/LiveQueries.mjs.map +1 -1
- package/lib/_chunks-es/PresentationToolGrantsCheck.mjs +2 -4
- package/lib/_chunks-es/PresentationToolGrantsCheck.mjs.map +1 -1
- package/lib/_chunks-es/presentation.mjs +3 -3
- package/lib/_chunks-es/presentation.mjs.map +1 -1
- package/lib/_chunks-es/version.mjs +1 -1
- package/lib/_internal/cli/threads/validateDocuments.js +1 -1
- package/lib/_internal/cli/threads/validateDocuments.js.map +1 -1
- package/lib/_legacy/LiveQueries.esm.js +235 -162
- package/lib/_legacy/LiveQueries.esm.js.map +1 -1
- package/lib/_legacy/PresentationToolGrantsCheck.esm.js +2 -4
- package/lib/_legacy/PresentationToolGrantsCheck.esm.js.map +1 -1
- package/lib/_legacy/presentation.esm.js +3 -3
- package/lib/_legacy/presentation.esm.js.map +1 -1
- package/lib/_legacy/version.esm.js +1 -1
- package/package.json +13 -14
- package/src/_internal/cli/server/buildVendorDependencies.ts +1 -0
- package/src/_internal/cli/threads/validateDocuments.ts +2 -2
- package/src/presentation/PresentationTool.tsx +10 -21
- package/src/presentation/constants.ts +4 -16
- package/src/presentation/loader/LiveQueries.tsx +79 -234
- package/src/presentation/loader/useLiveEvents.ts +75 -0
- package/src/presentation/loader/useLiveQueries.ts +127 -0
- package/src/presentation/loader/utils.ts +2 -125
- package/src/presentation/types.ts +1 -18
- package/lib/_chunks-cjs/LoaderQueries.js +0 -281
- package/lib/_chunks-cjs/LoaderQueries.js.map +0 -1
- package/lib/_chunks-cjs/utils.js +0 -70
- package/lib/_chunks-cjs/utils.js.map +0 -1
- package/lib/_chunks-es/LoaderQueries.mjs +0 -288
- package/lib/_chunks-es/LoaderQueries.mjs.map +0 -1
- package/lib/_chunks-es/utils.mjs +0 -74
- package/lib/_chunks-es/utils.mjs.map +0 -1
- package/lib/_legacy/LoaderQueries.esm.js +0 -288
- package/lib/_legacy/LoaderQueries.esm.js.map +0 -1
- package/lib/_legacy/utils.esm.js +0 -74
- package/lib/_legacy/utils.esm.js.map +0 -1
- package/src/presentation/loader/LoaderQueries.tsx +0 -564
@@ -1,564 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
type ClientConfig,
|
3
|
-
type ClientPerspective,
|
4
|
-
type ContentSourceMap,
|
5
|
-
type QueryParams,
|
6
|
-
type SyncTag,
|
7
|
-
} from '@sanity/client'
|
8
|
-
import {applySourceDocuments, getPublishedId} from '@sanity/client/csm'
|
9
|
-
import {
|
10
|
-
type ChannelInstance,
|
11
|
-
type Controller,
|
12
|
-
createConnectionMachine,
|
13
|
-
type StatusEvent,
|
14
|
-
} from '@sanity/comlink'
|
15
|
-
import {
|
16
|
-
createCompatibilityActors,
|
17
|
-
type LoaderControllerMsg,
|
18
|
-
type LoaderNodeMsg,
|
19
|
-
} from '@sanity/presentation-comlink'
|
20
|
-
import {applyPatch} from 'mendoza'
|
21
|
-
import LRUCache from 'mnemonist/lru-cache-with-delete'
|
22
|
-
import {memo, useEffect, useMemo, useState} from 'react'
|
23
|
-
import {
|
24
|
-
type SanityClient,
|
25
|
-
type SanityDocument,
|
26
|
-
useClient,
|
27
|
-
// useCurrentUser,
|
28
|
-
useDataset,
|
29
|
-
useProjectId,
|
30
|
-
} from 'sanity'
|
31
|
-
|
32
|
-
import {
|
33
|
-
LIVE_QUERY_CACHE_BATCH_SIZE,
|
34
|
-
LIVE_QUERY_CACHE_SIZE,
|
35
|
-
MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL,
|
36
|
-
} from '../constants'
|
37
|
-
import {
|
38
|
-
type LiveQueriesState,
|
39
|
-
type LiveQueriesStateValue,
|
40
|
-
type LoaderConnection,
|
41
|
-
type PresentationPerspective,
|
42
|
-
} from '../types'
|
43
|
-
import {type DocumentOnPage} from '../useDocumentsOnPage'
|
44
|
-
import {mapChangedValue, useQueryParams, useRevalidate} from './utils'
|
45
|
-
|
46
|
-
export interface LoaderQueriesProps {
|
47
|
-
liveDocument: Partial<SanityDocument> | null | undefined
|
48
|
-
controller: Controller | undefined
|
49
|
-
perspective: ClientPerspective
|
50
|
-
documentsOnPage: {_id: string; _type: string}[]
|
51
|
-
onLoadersConnection: (event: StatusEvent) => void
|
52
|
-
onDocumentsOnPage: (
|
53
|
-
key: string,
|
54
|
-
perspective: PresentationPerspective,
|
55
|
-
state: DocumentOnPage[],
|
56
|
-
) => void
|
57
|
-
}
|
58
|
-
|
59
|
-
export default function LoaderQueries(props: LoaderQueriesProps): React.JSX.Element {
|
60
|
-
const {
|
61
|
-
liveDocument,
|
62
|
-
controller,
|
63
|
-
perspective: activePerspective,
|
64
|
-
documentsOnPage,
|
65
|
-
onLoadersConnection,
|
66
|
-
onDocumentsOnPage,
|
67
|
-
} = props
|
68
|
-
|
69
|
-
const [comlink, setComlink] = useState<ChannelInstance<LoaderControllerMsg, LoaderNodeMsg>>()
|
70
|
-
const [liveQueries, setLiveQueries] = useState<LiveQueriesState>({})
|
71
|
-
|
72
|
-
const projectId = useProjectId()
|
73
|
-
const dataset = useDataset()
|
74
|
-
|
75
|
-
useEffect(() => {
|
76
|
-
const interval = setInterval(
|
77
|
-
() =>
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
79
|
-
setLiveQueries((liveQueries) => {
|
80
|
-
if (Object.keys(liveQueries).length < 1) {
|
81
|
-
return liveQueries
|
82
|
-
}
|
83
|
-
|
84
|
-
const now = Date.now()
|
85
|
-
const hasAnyExpired = Object.values(liveQueries).some(
|
86
|
-
// eslint-disable-next-line max-nested-callbacks
|
87
|
-
(liveQuery) =>
|
88
|
-
liveQuery.heartbeat !== false && now > liveQuery.receivedAt + liveQuery.heartbeat,
|
89
|
-
)
|
90
|
-
if (!hasAnyExpired) {
|
91
|
-
return liveQueries
|
92
|
-
}
|
93
|
-
const next = {} as LiveQueriesState
|
94
|
-
for (const [key, value] of Object.entries(liveQueries)) {
|
95
|
-
if (value.heartbeat !== false && now > value.receivedAt + value.heartbeat) {
|
96
|
-
continue
|
97
|
-
}
|
98
|
-
next[key] = value
|
99
|
-
}
|
100
|
-
return next
|
101
|
-
}),
|
102
|
-
MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL,
|
103
|
-
)
|
104
|
-
return () => clearInterval(interval)
|
105
|
-
}, [])
|
106
|
-
|
107
|
-
useEffect(() => {
|
108
|
-
if (controller) {
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
110
|
-
const comlink = controller.createChannel<LoaderControllerMsg, LoaderNodeMsg>(
|
111
|
-
{
|
112
|
-
name: 'presentation',
|
113
|
-
connectTo: 'loaders',
|
114
|
-
heartbeat: true,
|
115
|
-
},
|
116
|
-
createConnectionMachine<LoaderControllerMsg, LoaderNodeMsg>().provide({
|
117
|
-
actors: createCompatibilityActors<LoaderControllerMsg>(),
|
118
|
-
}),
|
119
|
-
)
|
120
|
-
setComlink(comlink)
|
121
|
-
|
122
|
-
comlink.onStatus(onLoadersConnection)
|
123
|
-
|
124
|
-
comlink.on('loader/documents', (data) => {
|
125
|
-
if (data.projectId === projectId && data.dataset === dataset) {
|
126
|
-
onDocumentsOnPage(
|
127
|
-
'loaders',
|
128
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
129
|
-
data.perspective as unknown as any,
|
130
|
-
data.documents,
|
131
|
-
)
|
132
|
-
}
|
133
|
-
})
|
134
|
-
|
135
|
-
comlink.on('loader/query-listen', (data) => {
|
136
|
-
if (data.projectId === projectId && data.dataset === dataset) {
|
137
|
-
if (
|
138
|
-
typeof data.heartbeat === 'number' &&
|
139
|
-
data.heartbeat < MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL
|
140
|
-
) {
|
141
|
-
throw new Error(
|
142
|
-
`Loader query listen heartbeat interval must be at least ${MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL}ms`,
|
143
|
-
)
|
144
|
-
}
|
145
|
-
setLiveQueries((prev) => ({
|
146
|
-
...prev,
|
147
|
-
[getQueryCacheKey(data.query, data.params)]: {
|
148
|
-
perspective: data.perspective,
|
149
|
-
query: data.query,
|
150
|
-
params: data.params,
|
151
|
-
receivedAt: Date.now(),
|
152
|
-
heartbeat: data.heartbeat ?? false,
|
153
|
-
} satisfies LiveQueriesStateValue,
|
154
|
-
}))
|
155
|
-
}
|
156
|
-
})
|
157
|
-
|
158
|
-
return comlink.start()
|
159
|
-
}
|
160
|
-
return undefined
|
161
|
-
}, [controller, dataset, onDocumentsOnPage, onLoadersConnection, projectId])
|
162
|
-
|
163
|
-
const [cache] = useState(() => new LRUCache<string, SanityDocument>(LIVE_QUERY_CACHE_SIZE))
|
164
|
-
const studioClient = useClient({apiVersion: '2023-10-16'})
|
165
|
-
const clientConfig = useMemo(() => studioClient.config(), [studioClient])
|
166
|
-
const client = useMemo(
|
167
|
-
() =>
|
168
|
-
studioClient.withConfig({
|
169
|
-
resultSourceMap: 'withKeyArraySelector',
|
170
|
-
}),
|
171
|
-
[studioClient],
|
172
|
-
)
|
173
|
-
useEffect(() => {
|
174
|
-
if (comlink) {
|
175
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
176
|
-
const {projectId, dataset} = clientConfig
|
177
|
-
comlink.post('loader/perspective', {
|
178
|
-
projectId: projectId!,
|
179
|
-
dataset: dataset!,
|
180
|
-
perspective: activePerspective,
|
181
|
-
})
|
182
|
-
}
|
183
|
-
}, [comlink, clientConfig, activePerspective])
|
184
|
-
|
185
|
-
const turboIds = useMemo(() => {
|
186
|
-
const documentsActuallyInUse = documentsOnPage.map(({_id}) => _id)
|
187
|
-
const set = new Set(documentsActuallyInUse)
|
188
|
-
const ids = [...set]
|
189
|
-
const max = cache.capacity
|
190
|
-
if (ids.length >= max) {
|
191
|
-
ids.length = max
|
192
|
-
}
|
193
|
-
return ids
|
194
|
-
}, [cache.capacity, documentsOnPage])
|
195
|
-
|
196
|
-
const [documentsCacheLastUpdated, setDocumentsCacheLastUpdated] = useState(0)
|
197
|
-
|
198
|
-
return (
|
199
|
-
<>
|
200
|
-
<Turbo
|
201
|
-
cache={cache}
|
202
|
-
client={client}
|
203
|
-
turboIds={turboIds}
|
204
|
-
setDocumentsCacheLastUpdated={setDocumentsCacheLastUpdated}
|
205
|
-
/>
|
206
|
-
{Object.entries(liveQueries).map(([key, {query, params, perspective}]) => (
|
207
|
-
<QuerySubscription
|
208
|
-
key={`${key}${perspective}`}
|
209
|
-
cache={cache}
|
210
|
-
projectId={clientConfig.projectId!}
|
211
|
-
dataset={clientConfig.dataset!}
|
212
|
-
perspective={perspective}
|
213
|
-
query={query}
|
214
|
-
params={params}
|
215
|
-
comlink={comlink}
|
216
|
-
client={client}
|
217
|
-
refreshInterval={activePerspective ? 2000 : 0}
|
218
|
-
liveDocument={liveDocument}
|
219
|
-
documentsCacheLastUpdated={documentsCacheLastUpdated}
|
220
|
-
/>
|
221
|
-
))}
|
222
|
-
</>
|
223
|
-
)
|
224
|
-
}
|
225
|
-
|
226
|
-
interface SharedProps {
|
227
|
-
/**
|
228
|
-
* The Sanity client to use for fetching data and listening to mutations.
|
229
|
-
*/
|
230
|
-
client: SanityClient
|
231
|
-
/**
|
232
|
-
* How frequently queries should be refetched in the background to refresh the parts of queries that can't be source mapped.
|
233
|
-
* Setting it to `0` will disable background refresh.
|
234
|
-
* @defaultValue 10000
|
235
|
-
*/
|
236
|
-
refreshInterval?: number
|
237
|
-
/**
|
238
|
-
* The documents cache to use for turbo-charging queries.
|
239
|
-
*/
|
240
|
-
cache: LRUCache<string, SanityDocument>
|
241
|
-
}
|
242
|
-
|
243
|
-
interface TurboProps extends Pick<SharedProps, 'client' | 'cache'> {
|
244
|
-
turboIds: string[]
|
245
|
-
setDocumentsCacheLastUpdated: (timestamp: number) => void
|
246
|
-
}
|
247
|
-
/**
|
248
|
-
* A turbo-charged mutation observer that uses Content Source Maps to apply mendoza patches on your queries
|
249
|
-
*/
|
250
|
-
const Turbo = memo(function Turbo(props: TurboProps) {
|
251
|
-
const {cache, client, turboIds, setDocumentsCacheLastUpdated} = props
|
252
|
-
// Figure out which documents are missing from the cache
|
253
|
-
const [batch, setBatch] = useState<string[][]>([])
|
254
|
-
useEffect(() => {
|
255
|
-
const batchSet = new Set(batch.flat())
|
256
|
-
const nextBatch = new Set<string>()
|
257
|
-
for (const turboId of turboIds) {
|
258
|
-
if (!batchSet.has(turboId) && !cache.has(turboId)) {
|
259
|
-
nextBatch.add(turboId)
|
260
|
-
}
|
261
|
-
}
|
262
|
-
const nextBatchSlice = [...nextBatch].slice(0, LIVE_QUERY_CACHE_BATCH_SIZE)
|
263
|
-
if (nextBatchSlice.length === 0) return undefined
|
264
|
-
const raf = requestAnimationFrame(() =>
|
265
|
-
// eslint-disable-next-line max-nested-callbacks
|
266
|
-
setBatch((prevBatch) => [...prevBatch.slice(-LIVE_QUERY_CACHE_BATCH_SIZE), nextBatchSlice]),
|
267
|
-
)
|
268
|
-
return () => cancelAnimationFrame(raf)
|
269
|
-
}, [batch, cache, turboIds])
|
270
|
-
|
271
|
-
// Use the same listen instance and patch documents as they come in
|
272
|
-
useEffect(() => {
|
273
|
-
const subscription = client
|
274
|
-
.listen(
|
275
|
-
'*',
|
276
|
-
{},
|
277
|
-
{
|
278
|
-
events: ['mutation'],
|
279
|
-
effectFormat: 'mendoza',
|
280
|
-
includePreviousRevision: false,
|
281
|
-
includeResult: false,
|
282
|
-
tag: 'presentation-loader',
|
283
|
-
},
|
284
|
-
)
|
285
|
-
.subscribe((update) => {
|
286
|
-
if (update.type === 'mutation' && update.transition === 'disappear') {
|
287
|
-
if (cache.delete(update.documentId)) {
|
288
|
-
setDocumentsCacheLastUpdated(Date.now())
|
289
|
-
}
|
290
|
-
}
|
291
|
-
|
292
|
-
if (update.type !== 'mutation' || !update.effects?.apply?.length) return
|
293
|
-
// Schedule a reach state update with the ID of the document that were mutated
|
294
|
-
// This react handler will apply the document to related source map snapshots
|
295
|
-
const cachedDocument = cache.peek(update.documentId)
|
296
|
-
if (cachedDocument as SanityDocument) {
|
297
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
298
|
-
const patchDoc = {...cachedDocument} as any
|
299
|
-
delete patchDoc._rev
|
300
|
-
const patchedDocument = applyPatch(patchDoc, update.effects.apply)
|
301
|
-
cache.set(update.documentId, patchedDocument)
|
302
|
-
setDocumentsCacheLastUpdated(Date.now())
|
303
|
-
}
|
304
|
-
})
|
305
|
-
return () => subscription.unsubscribe()
|
306
|
-
}, [cache, client, setDocumentsCacheLastUpdated])
|
307
|
-
|
308
|
-
return (
|
309
|
-
<>
|
310
|
-
{batch.map((ids) => (
|
311
|
-
<GetDocuments
|
312
|
-
key={JSON.stringify(ids)}
|
313
|
-
cache={cache}
|
314
|
-
client={client}
|
315
|
-
ids={ids}
|
316
|
-
setDocumentsCacheLastUpdated={setDocumentsCacheLastUpdated}
|
317
|
-
/>
|
318
|
-
))}
|
319
|
-
</>
|
320
|
-
)
|
321
|
-
})
|
322
|
-
|
323
|
-
interface GetDocumentsProps extends Pick<SharedProps, 'client' | 'cache'> {
|
324
|
-
ids: string[]
|
325
|
-
setDocumentsCacheLastUpdated: (timestamp: number) => void
|
326
|
-
}
|
327
|
-
const GetDocuments = memo(function GetDocuments(props: GetDocumentsProps) {
|
328
|
-
const {client, cache, ids, setDocumentsCacheLastUpdated} = props
|
329
|
-
|
330
|
-
useEffect(() => {
|
331
|
-
const missingIds = ids.filter((id) => !cache.has(id))
|
332
|
-
if (missingIds.length === 0) return
|
333
|
-
client.getDocuments(missingIds).then((documents) => {
|
334
|
-
for (const doc of documents) {
|
335
|
-
if (doc && doc?._id) {
|
336
|
-
cache.set(doc._id, doc)
|
337
|
-
setDocumentsCacheLastUpdated(Date.now())
|
338
|
-
}
|
339
|
-
}
|
340
|
-
// eslint-disable-next-line no-console
|
341
|
-
}, console.error)
|
342
|
-
}, [cache, client, ids, setDocumentsCacheLastUpdated])
|
343
|
-
|
344
|
-
return null
|
345
|
-
})
|
346
|
-
GetDocuments.displayName = 'GetDocuments'
|
347
|
-
|
348
|
-
interface QuerySubscriptionProps
|
349
|
-
extends Pick<
|
350
|
-
UseQuerySubscriptionProps,
|
351
|
-
'client' | 'cache' | 'refreshInterval' | 'liveDocument' | 'documentsCacheLastUpdated'
|
352
|
-
> {
|
353
|
-
projectId: string
|
354
|
-
dataset: string
|
355
|
-
perspective: ClientPerspective
|
356
|
-
query: string
|
357
|
-
params: QueryParams
|
358
|
-
comlink: LoaderConnection | undefined
|
359
|
-
}
|
360
|
-
function QuerySubscription(props: QuerySubscriptionProps) {
|
361
|
-
const {
|
362
|
-
cache,
|
363
|
-
projectId,
|
364
|
-
dataset,
|
365
|
-
perspective,
|
366
|
-
query,
|
367
|
-
client,
|
368
|
-
refreshInterval,
|
369
|
-
liveDocument,
|
370
|
-
comlink,
|
371
|
-
documentsCacheLastUpdated,
|
372
|
-
} = props
|
373
|
-
|
374
|
-
const params = useQueryParams(props.params)
|
375
|
-
const data = useQuerySubscription({
|
376
|
-
cache,
|
377
|
-
client,
|
378
|
-
liveDocument,
|
379
|
-
params,
|
380
|
-
perspective,
|
381
|
-
query,
|
382
|
-
refreshInterval,
|
383
|
-
documentsCacheLastUpdated,
|
384
|
-
})
|
385
|
-
const result = data?.result
|
386
|
-
const resultSourceMap = data?.resultSourceMap
|
387
|
-
const tags = data?.tags
|
388
|
-
|
389
|
-
useEffect(() => {
|
390
|
-
if (resultSourceMap) {
|
391
|
-
comlink?.post('loader/query-change', {
|
392
|
-
projectId,
|
393
|
-
dataset,
|
394
|
-
perspective,
|
395
|
-
query,
|
396
|
-
params,
|
397
|
-
result,
|
398
|
-
resultSourceMap,
|
399
|
-
tags,
|
400
|
-
})
|
401
|
-
}
|
402
|
-
}, [comlink, dataset, params, perspective, projectId, query, result, resultSourceMap, tags])
|
403
|
-
|
404
|
-
return null
|
405
|
-
}
|
406
|
-
|
407
|
-
interface UseQuerySubscriptionProps
|
408
|
-
extends Required<Pick<SharedProps, 'client' | 'refreshInterval' | 'cache'>> {
|
409
|
-
liveDocument: Partial<SanityDocument> | null | undefined
|
410
|
-
query: string
|
411
|
-
params: QueryParams
|
412
|
-
perspective: ClientPerspective
|
413
|
-
documentsCacheLastUpdated: number
|
414
|
-
}
|
415
|
-
function useQuerySubscription(props: UseQuerySubscriptionProps) {
|
416
|
-
const {
|
417
|
-
cache,
|
418
|
-
liveDocument,
|
419
|
-
client,
|
420
|
-
refreshInterval,
|
421
|
-
query,
|
422
|
-
params,
|
423
|
-
perspective,
|
424
|
-
documentsCacheLastUpdated,
|
425
|
-
} = props
|
426
|
-
const [snapshot, setSnapshot] = useState<{
|
427
|
-
result: unknown
|
428
|
-
resultSourceMap?: ContentSourceMap
|
429
|
-
tags?: SyncTag[]
|
430
|
-
} | null>(null)
|
431
|
-
const {projectId, dataset} = useMemo(() => {
|
432
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
433
|
-
const {projectId, dataset} = client.config()
|
434
|
-
return {projectId, dataset} as Required<Pick<ClientConfig, 'projectId' | 'dataset'>>
|
435
|
-
}, [client])
|
436
|
-
|
437
|
-
// Make sure any async errors bubble up to the nearest error boundary
|
438
|
-
const [error, setError] = useState<unknown>(null)
|
439
|
-
if (error) throw error
|
440
|
-
|
441
|
-
const [revalidate, startRefresh] = useRevalidate({refreshInterval})
|
442
|
-
const shouldRefetch = revalidate === 'refresh' || revalidate === 'inflight'
|
443
|
-
useEffect(() => {
|
444
|
-
if (!shouldRefetch) {
|
445
|
-
return undefined
|
446
|
-
}
|
447
|
-
|
448
|
-
let fulfilled = false
|
449
|
-
let fetching = false
|
450
|
-
const controller = new AbortController()
|
451
|
-
// eslint-disable-next-line no-inner-declarations
|
452
|
-
async function effect() {
|
453
|
-
const {signal} = controller
|
454
|
-
fetching = true
|
455
|
-
const {result, resultSourceMap, syncTags} = await client.fetch(query, params, {
|
456
|
-
tag: 'presentation-loader',
|
457
|
-
signal,
|
458
|
-
perspective,
|
459
|
-
filterResponse: false,
|
460
|
-
})
|
461
|
-
fetching = false
|
462
|
-
|
463
|
-
if (!signal.aborted) {
|
464
|
-
setSnapshot({result, resultSourceMap, tags: syncTags})
|
465
|
-
|
466
|
-
fulfilled = true
|
467
|
-
}
|
468
|
-
}
|
469
|
-
const onFinally = startRefresh()
|
470
|
-
effect()
|
471
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
472
|
-
.catch((error) => {
|
473
|
-
fetching = false
|
474
|
-
if (error.name !== 'AbortError') {
|
475
|
-
setError(error)
|
476
|
-
}
|
477
|
-
})
|
478
|
-
.finally(onFinally)
|
479
|
-
return () => {
|
480
|
-
if (!fulfilled && !fetching) {
|
481
|
-
controller.abort()
|
482
|
-
}
|
483
|
-
}
|
484
|
-
}, [
|
485
|
-
client,
|
486
|
-
dataset,
|
487
|
-
liveDocument,
|
488
|
-
params,
|
489
|
-
perspective,
|
490
|
-
projectId,
|
491
|
-
query,
|
492
|
-
shouldRefetch,
|
493
|
-
startRefresh,
|
494
|
-
])
|
495
|
-
|
496
|
-
return useMemo(() => {
|
497
|
-
if (documentsCacheLastUpdated && snapshot?.resultSourceMap) {
|
498
|
-
return {
|
499
|
-
result: turboChargeResultIfSourceMap(
|
500
|
-
cache,
|
501
|
-
liveDocument,
|
502
|
-
snapshot.result,
|
503
|
-
perspective,
|
504
|
-
snapshot.resultSourceMap,
|
505
|
-
),
|
506
|
-
resultSourceMap: snapshot.resultSourceMap,
|
507
|
-
}
|
508
|
-
}
|
509
|
-
return snapshot
|
510
|
-
}, [cache, documentsCacheLastUpdated, liveDocument, perspective, snapshot])
|
511
|
-
}
|
512
|
-
|
513
|
-
let warnedAboutCrossDatasetReference = false
|
514
|
-
export function turboChargeResultIfSourceMap<T = unknown>(
|
515
|
-
cache: SharedProps['cache'],
|
516
|
-
liveDocument: Partial<SanityDocument> | null | undefined,
|
517
|
-
result: T,
|
518
|
-
perspective: ClientPerspective,
|
519
|
-
resultSourceMap?: ContentSourceMap,
|
520
|
-
): T {
|
521
|
-
if (perspective === 'raw') {
|
522
|
-
throw new Error('turboChargeResultIfSourceMap does not support raw perspective')
|
523
|
-
}
|
524
|
-
return applySourceDocuments(
|
525
|
-
result,
|
526
|
-
resultSourceMap,
|
527
|
-
(sourceDocument) => {
|
528
|
-
if (sourceDocument._projectId) {
|
529
|
-
// @TODO Handle cross dataset references
|
530
|
-
if (!warnedAboutCrossDatasetReference) {
|
531
|
-
// eslint-disable-next-line no-console
|
532
|
-
console.warn(
|
533
|
-
'Cross dataset references are not supported yet, ignoring source document',
|
534
|
-
sourceDocument,
|
535
|
-
)
|
536
|
-
warnedAboutCrossDatasetReference = true
|
537
|
-
}
|
538
|
-
return undefined
|
539
|
-
}
|
540
|
-
// If there's a displayed document, always prefer it
|
541
|
-
if (
|
542
|
-
liveDocument?._id &&
|
543
|
-
getPublishedId(liveDocument._id) === getPublishedId(sourceDocument._id)
|
544
|
-
) {
|
545
|
-
if (typeof liveDocument._id === 'string' && typeof sourceDocument._type === 'string') {
|
546
|
-
return liveDocument as unknown as Required<Pick<SanityDocument, '_id' | '_type'>>
|
547
|
-
}
|
548
|
-
return {
|
549
|
-
...liveDocument,
|
550
|
-
_id: liveDocument._id || sourceDocument._id,
|
551
|
-
_type: liveDocument._type || sourceDocument._type,
|
552
|
-
}
|
553
|
-
}
|
554
|
-
// Fallback to general documents cache
|
555
|
-
return cache.get(sourceDocument._id)
|
556
|
-
},
|
557
|
-
mapChangedValue,
|
558
|
-
perspective,
|
559
|
-
)
|
560
|
-
}
|
561
|
-
|
562
|
-
function getQueryCacheKey(query: string, params: QueryParams | string): `${string}-${string}` {
|
563
|
-
return `${query}-${typeof params === 'string' ? params : JSON.stringify(params)}`
|
564
|
-
}
|