UNPKG

@sanity/visual-editing

Version:

[![npm stat](https://img.shields.io/npm/dm/@sanity/visual-editing.svg?style=flat-square)](https://npm-stat.com/charts.html?package=@sanity/visual-editing) [![npm version](https://img.shields.io/npm/v/@sanity/visual-editing.svg?style=flat-square)](https://

71 lines (70 loc) 3.26 kB
import { getPublishedId } from '@sanity/client/csm'; import { isEmptyActor, } from '@sanity/visual-editing/optimistic'; import { onMount } from 'svelte'; import { derived, get, writable } from 'svelte/store'; import { optimisticActor } from './optimisticActor'; export function useOptimistic(initial, reducer) { // The current passthrough state, either the initial value passed to the // function call or set via update const passthrough = writable(initial); // The last action event that was received, if this is defined, we are in a // "dirty" state const lastEvent = writable(null); // The optimistic state that is returned if we are in a "dirty" state const optimistic = writable(initial); const reduceStateFromAction = (action, prevState) => { const reducers = Array.isArray(reducer) ? reducer : [reducer]; return reducers.reduce((acc, reducer) => reducer(acc, { document: action.document, id: getPublishedId(action.id), originalId: action.id, type: action.type, }), prevState); }; let pristineTimeout; onMount(() => optimisticActor.subscribe((actor) => { // If the actor hasn't been set yet, we don't need to subscribe to mutations if (isEmptyActor(actor)) { return; } // When a rebased event is received, apply it to the current optimistic // state, and update the last action to signal we are in a "dirty" state actor.on('rebased.local', (event) => { const action = { document: event.document, id: event.id, originalId: getPublishedId(event.id), type: 'mutate', }; optimistic.update((prev) => reduceStateFromAction(action, prev)); lastEvent.set(action); clearTimeout(pristineTimeout); }); // If no rebased events were received in the 15 seconds after a pristine // event, reset to a "pristine" state by removing the last event, and // align the optimistic state with the passthrough state actor.on('pristine', () => { pristineTimeout = setTimeout(() => { lastEvent.set(null); optimistic.set(get(passthrough)); }, 15000); }); })); const updatePassthrough = (newPassthrough) => { // If we are in a dirty state (i.e. have a last event to apply) when the // passthrough is updated, apply it to the passthrough as optimistic state const $lastEvent = get(lastEvent); if ($lastEvent) { optimistic.set(reduceStateFromAction($lastEvent, newPassthrough)); } // Also always update the passthrough state passthrough.set(newPassthrough); }; // If we are in a "dirty" state (have an event to apply), return the // optimistic state, otherwise we are in the "pristine" state, so return the // passthrough state const optimisticValue = derived([passthrough, optimistic, lastEvent], ([$passthrough, $optimistic, $lastEvent]) => { return $lastEvent ? $optimistic : $passthrough; }); return { update: updatePassthrough, value: optimisticValue }; }