Skip to content

Commit

Permalink
feat(core): add support for user setting sync between tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Sep 30, 2024
1 parent 9d3e3cd commit 55d548e
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 6 deletions.
20 changes: 15 additions & 5 deletions packages/sanity/src/core/store/key-value/localStorageSWR.ts
Original file line number Diff line number Diff line change
@@ -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<StorageEvent>(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)
Expand Down
10 changes: 9 additions & 1 deletion packages/sanity/src/core/store/key-value/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<KeyValueStoreValue | null>
getKey(key: string, options?: {live?: boolean}): Observable<KeyValueStoreValue | null>
setKey(key: string, value: KeyValueStoreValue): Promise<KeyValueStoreValue>
}

0 comments on commit 55d548e

Please sign in to comment.