import type {SerializableObject} from '@sanity/presentation-comlink' import {useEffect, useMemo, type FunctionComponent, type PropsWithChildren} from 'react' import type {VisualEditingNode} from '../../types' import {SharedStateContext} from './SharedStateContext' const createStore = (initialState: SerializableObject) => { let state = initialState const getState = () => state const listeners = new Set<() => void>() const setState = (fn: (state: SerializableObject) => SerializableObject) => { state = fn(state) listeners.forEach((l) => l()) } const subscribe = (listener: () => void) => { listeners.add(listener) return () => listeners.delete(listener) } return {getState, setState, subscribe} } const store = createStore({}) export const SharedStateProvider: FunctionComponent< PropsWithChildren<{ comlink?: VisualEditingNode }> > = (props) => { const {comlink, children} = props useEffect(() => { return comlink?.on('presentation/shared-state', (data) => { if ('value' in data) { store.setState((prev) => ({...prev, [data.key]: data.value})) } else { store.setState((prev) => { return Object.fromEntries(Object.entries(prev).filter(([key]) => key !== data.key)) }) } }) }, [comlink]) useEffect(() => { async function fetch() { const value = await comlink?.fetch('visual-editing/shared-state', undefined, { suppressWarnings: true, }) if (value) { store.setState(() => value.state) } } fetch().catch((reason) => { // eslint-disable-next-line no-console console.debug(reason) // eslint-disable-next-line no-console console.warn( '[@sanity/visual-editing]: Failed to fetch shared state. Check your version of `sanity` is up-to-date', ) }) }, [comlink]) const value = useMemo(() => ({comlink, store}), [comlink]) return {children} }