diff --git a/packages/sanity/src/core/store/key-value/localStorageSWR.ts b/packages/sanity/src/core/store/key-value/localStorageSWR.ts index 366e428e926..557d46ac00c 100644 --- a/packages/sanity/src/core/store/key-value/localStorageSWR.ts +++ b/packages/sanity/src/core/store/key-value/localStorageSWR.ts @@ -1,16 +1,26 @@ import {isEqual} from 'lodash' -import {merge, of} from 'rxjs' -import {distinctUntilChanged, tap} from 'rxjs/operators' +import {EMPTY, fromEvent, merge, of} from 'rxjs' +import {distinctUntilChanged, filter, map, share, tap} from 'rxjs/operators' import {localStoreStorage} from './storage/localStoreStorage' -import {type KeyValueStore, type KeyValueStoreValue} from './types' +import {type GetKeyOptions, type KeyValueStore, type KeyValueStoreValue} from './types' + +const storageEvent = + typeof window === 'undefined' ? EMPTY : fromEvent(window, 'storage').pipe(share()) /** * Wraps a KeyValueStore and adds Stale-While-Revalidate (SWR) behavior to it */ export function withLocalStorageSWR(wrappedStore: KeyValueStore): KeyValueStore { - function getKey(key: string) { - return merge(of(localStoreStorage.getKey(key)), wrappedStore.getKey(key)).pipe( + function getKey(key: string, options?: GetKeyOptions) { + const lsUpdates = options?.live + ? storageEvent.pipe( + filter((event) => event.key === key), + map(() => localStoreStorage.getKey(key)), + ) + : EMPTY + + return merge(of(localStoreStorage.getKey(key)), lsUpdates, wrappedStore.getKey(key)).pipe( distinctUntilChanged(isEqual), tap((value) => { localStoreStorage.setKey(key, value) diff --git a/packages/sanity/src/core/store/key-value/types.ts b/packages/sanity/src/core/store/key-value/types.ts index c3c60917899..e2f908d0f5e 100644 --- a/packages/sanity/src/core/store/key-value/types.ts +++ b/packages/sanity/src/core/store/key-value/types.ts @@ -6,11 +6,19 @@ type JsonObject = {[Key in string]: KeyValueStoreValue} & { type JsonArray = KeyValueStoreValue[] | readonly KeyValueStoreValue[] type JsonPrimitive = string | number | boolean | null +export interface GetKeyOptions { + /** + * if `true` this setting will sync instantly (on a best-effort basis) if changed elsewhere + * Note: this only syncs across tabs within the same browser for now + * in the future we might include the semantics of `live` to mean that it's also live updated if changed remotely + */ + live?: boolean +} /** @internal */ export type KeyValueStoreValue = JsonPrimitive | JsonObject | JsonArray /** @internal */ export interface KeyValueStore { - getKey(key: string): Observable + getKey(key: string, options?: {live?: boolean}): Observable setKey(key: string, value: KeyValueStoreValue): Promise }