UNPKG

@sanity/sdk

Version:
107 lines (100 loc) 3.36 kB
import { debounceTime, defer, distinctUntilChanged, EMPTY, filter, from, map, Observable, pairwise, startWith, Subscription, switchMap, tap, } from 'rxjs' import {getQueryState, resolveQuery} from '../query/queryStore' import {type StoreContext} from '../store/defineStore' import {createPreviewQuery, processPreviewQuery} from './previewQuery' import {type PreviewQueryResult, type PreviewStoreState} from './previewStore' import {PREVIEW_PERSPECTIVE, PREVIEW_TAG} from './util' const BATCH_DEBOUNCE_TIME = 50 const isSetEqual = <T>(a: Set<T>, b: Set<T>) => a.size === b.size && Array.from(a).every((i) => b.has(i)) export const subscribeToStateAndFetchBatches = ({ state, instance, }: StoreContext<PreviewStoreState>): Subscription => { const newSubscriberIds$ = state.observable.pipe( map(({subscriptions}) => new Set(Object.keys(subscriptions))), distinctUntilChanged(isSetEqual), debounceTime(BATCH_DEBOUNCE_TIME), startWith(new Set<string>()), pairwise(), tap(([prevIds, currIds]) => { // for all new subscriptions, set their values to pending const newIds = [...currIds].filter((element) => !prevIds.has(element)) state.set('updatingPending', (prev) => { const pendingValues = newIds.reduce<PreviewStoreState['values']>((acc, id) => { const prevValue = prev.values[id] const value = prevValue?.data ? prevValue.data : null acc[id] = {data: value, isPending: true} return acc }, {}) return {values: {...prev.values, ...pendingValues}} }) }), map(([, ids]) => ids), distinctUntilChanged(isSetEqual), ) return newSubscriberIds$ .pipe( switchMap((ids) => { if (!ids.size) return EMPTY const {query, params} = createPreviewQuery(ids) const controller = new AbortController() return new Observable<PreviewQueryResult[]>((observer) => { const {getCurrent, observable} = getQueryState<PreviewQueryResult[]>(instance, { query, params, tag: PREVIEW_TAG, perspective: PREVIEW_PERSPECTIVE, }) const source$ = defer(() => { if (getCurrent() === undefined) { return from( resolveQuery<PreviewQueryResult[]>(instance, { query, params, tag: PREVIEW_TAG, perspective: PREVIEW_PERSPECTIVE, signal: controller.signal, }), ).pipe(switchMap(() => observable)) } return observable }).pipe(filter((result) => result !== undefined)) const subscription = source$.subscribe(observer) return () => { if (!controller.signal.aborted) { controller.abort() } subscription.unsubscribe() } }).pipe(map((data) => ({data, ids}))) }), map(({ids, data}) => ({ values: processPreviewQuery({ projectId: instance.config.projectId!, dataset: instance.config.dataset!, ids, results: data, }), })), ) .subscribe({ next: ({values}) => { state.set('updateResult', (prev) => ({values: {...prev.values, ...values}})) }, }) }