Skip to content

Commit

Permalink
refactor: simplify get and set and move default value handling to ind…
Browse files Browse the repository at this point in the history
…ividual hooks
  • Loading branch information
cngonzalez committed Mar 7, 2024
1 parent b94decd commit 11e76dd
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 35 deletions.
31 changes: 24 additions & 7 deletions packages/sanity/src/core/store/key-value/KeyValueStore.ts
@@ -1,5 +1,5 @@
import {type SanityClient} from '@sanity/client'
import {merge, type Observable, Subject} from 'rxjs'
import {merge, Observable, Subject} from 'rxjs'
import {filter, map, switchMap} from 'rxjs/operators'

import {serverBackend} from './backends/server'
Expand All @@ -22,21 +22,38 @@ export function createKeyValueStore({client}: {client: SanityClient}): KeyValueS
),
)

const getKey = (
key: string,
defaultValue: KeyValueStoreValue,
): Observable<KeyValueStoreValue> => {
const getKey = (key: string): Observable<KeyValueStoreValue> => {
return merge(
storageBackend.getKey(key, defaultValue),
storageBackend.getKey(key),
updates$.pipe(
filter((update) => update.key === key),
map((update) => update.value),
),
) as Observable<KeyValueStoreValue>
}

const setKey = (key: string, value: KeyValueStoreValue) => {
const setKey = (key: string, value: KeyValueStoreValue): Observable<KeyValueStoreValue> => {
/*
* The backend returns the result of the set operation, so we can just pass that along.
* Most utils do not use it (they will take advantage of local state first) but it reflects the
* backend function and could be useful for debugging.
*/
const response = new Observable<KeyValueStoreValue>((subscriber) => {
const subscription = storageBackend.setKey(key, value).subscribe({
next: (nextValue) => {
subscriber.next(nextValue as KeyValueStoreValue)
subscriber.complete()
},
//storageBackend should handle its own errors, we're just passing along the result.
error: (err) => {
subscriber.error(err)
},
})
return () => subscription.unsubscribe()
})

setKey$.next({key, value})
return response
}

return {getKey, setKey}
Expand Down
Expand Up @@ -2,20 +2,20 @@ import {type Observable, of as observableOf} from 'rxjs'

import {type Backend, type KeyValuePair} from './types'

const tryParse = (val: string, defValue: unknown) => {
const tryParse = (val: string) => {
try {
return JSON.parse(val)
} catch (err) {
// eslint-disable-next-line no-console
console.warn(`Failed to parse settings: ${err.message}`)
return defValue
return null
}
}

const getKey = (key: string, defaultValue: unknown): Observable<unknown> => {
const getKey = (key: string): Observable<unknown> => {
const val = localStorage.getItem(key)

return observableOf(val === null ? defaultValue : tryParse(val, defaultValue))
return observableOf(val === null ? null : tryParse(val))
}

const setKey = (key: string, nextValue: unknown): Observable<unknown> => {
Expand All @@ -30,10 +30,10 @@ const setKey = (key: string, nextValue: unknown): Observable<unknown> => {
return observableOf(nextValue)
}

const getKeys = (keys: string[], defaultValues: unknown[]): Observable<unknown[]> => {
const getKeys = (keys: string[]): Observable<unknown[]> => {
const values = keys.map((key, i) => {
const val = localStorage.getItem(key)
return val === null ? defaultValues[i] : tryParse(val, defaultValues[i])
return val === null ? null : tryParse(val)
})

return observableOf(values)
Expand Down
7 changes: 3 additions & 4 deletions packages/sanity/src/core/store/key-value/backends/memory.ts
Expand Up @@ -4,16 +4,15 @@ import {type Backend, type KeyValuePair} from './types'

const DB = Object.create(null)

const getKey = (key: string, defaultValue: unknown): Observable<unknown> =>
observableOf(key in DB ? DB[key] : defaultValue)
const getKey = (key: string): Observable<unknown> => observableOf(key in DB ? DB[key] : null)

const setKey = (key: string, nextValue: unknown): Observable<unknown> => {
DB[key] = nextValue
return observableOf(nextValue)
}

const getKeys = (keys: string[], defaultValues: unknown[]): Observable<unknown[]> => {
return observableOf(keys.map((key, i) => (key in DB ? DB[key] : defaultValues[i])))
const getKeys = (keys: string[]): Observable<unknown[]> => {
return observableOf(keys.map((key, i) => (key in DB ? DB[key] : null)))
}

const setKeys = (keyValuePairs: KeyValuePair[]): Observable<unknown[]> => {
Expand Down
33 changes: 23 additions & 10 deletions packages/sanity/src/core/store/key-value/backends/server.ts
Expand Up @@ -20,7 +20,7 @@ export function serverBackend({client: _client}: ServerBackendOptions): Backend
const client = _client.withConfig({...DEFAULT_STUDIO_CLIENT_OPTIONS, apiVersion: 'vX'})

const keyValueLoader = new DataLoader<string, KeyValueStoreValue | null>(async (keys) => {
const keyValuePairs = await client
const value = await client
.request<KeyValuePair[]>({
uri: `/users/me/keyvalue/${keys.join(',')}`,
withCredentials: true,
Expand All @@ -30,7 +30,18 @@ export function serverBackend({client: _client}: ServerBackendOptions): Backend
return Array(keys.length).fill(null)
})

return keyValuePairs.map((pair) => pair.value)
const keyValuePairs = value.reduce(
(acc, next) => {
if (next?.key) {
acc[next.key] = next.value
}
return acc
},
{} as Record<string, KeyValueStoreValue | null>,
)

const result = keys.map((key) => keyValuePairs[key] || null)
return result
})

const getKeys = (keys: string[]) => {
Expand Down Expand Up @@ -60,15 +71,17 @@ export function serverBackend({client: _client}: ServerBackendOptions): Backend
)
}

const getKey = (key: string) => {
return getKeys([key]).pipe(map((values) => values[0]))
}

const setKey = (key: string, nextValue: unknown) => {
return setKeys([{key, value: nextValue as KeyValueStoreValue}]).pipe(map((values) => values[0]))
}

return {
getKey: (key: string) => {
return getKeys([key]).pipe(map((values) => values[0]))
},
setKey: (key: string, nextValue: unknown) => {
return setKeys([{key, value: nextValue as KeyValueStoreValue}]).pipe(
map((values) => values[0]),
)
},
getKey,
setKey,
getKeys,
setKeys,
}
Expand Down
4 changes: 2 additions & 2 deletions packages/sanity/src/core/store/key-value/backends/types.ts
Expand Up @@ -8,8 +8,8 @@ export interface KeyValuePair {
}

export interface Backend {
getKey: (key: string, defValue: unknown) => Observable<unknown>
getKey: (key: string) => Observable<unknown>
setKey: (key: string, nextValue: unknown) => Observable<unknown>
getKeys: (keys: string[], defValues: unknown[]) => Observable<unknown[]>
getKeys: (keys: string[]) => Observable<unknown[]>
setKeys: (keyValuePairs: KeyValuePair[]) => Observable<unknown[]>
}
4 changes: 2 additions & 2 deletions packages/sanity/src/core/store/key-value/types.ts
Expand Up @@ -11,6 +11,6 @@ export type KeyValueStoreValue = JsonPrimitive | JsonObject | JsonArray

/** @internal */
export interface KeyValueStore {
getKey(key: string, defaultValue?: KeyValueStoreValue): Observable<KeyValueStoreValue | undefined>
setKey(key: string, value: KeyValueStoreValue): void
getKey(key: string): Observable<KeyValueStoreValue | null>
setKey(key: string, value: KeyValueStoreValue): Observable<KeyValueStoreValue>
}
16 changes: 12 additions & 4 deletions packages/sanity/src/structure/useStructureToolSetting.ts
@@ -1,8 +1,9 @@
import {useCallback, useEffect, useMemo, useState} from 'react'
import {startWith} from 'rxjs/operators'
import {map, startWith} from 'rxjs/operators'
import {useKeyValueStore} from 'sanity'

const STRUCTURE_TOOL_NAMESPACE = 'studio.structure-tool'

/**
* @internal
*/
Expand All @@ -21,9 +22,16 @@ export function useStructureToolSetting<ValueType>(
}, [keyValueStore, keyValueStoreKey])

useEffect(() => {
const sub = settings.pipe(startWith(defaultValue)).subscribe({
next: setValue as any,
})
const sub = settings
.pipe(
startWith(defaultValue),
map((fetchedValue) => {
return fetchedValue === null ? defaultValue : fetchedValue
}),
)
.subscribe({
next: setValue as any,
})

return () => sub?.unsubscribe()
}, [defaultValue, keyValueStoreKey, settings])
Expand Down

0 comments on commit 11e76dd

Please sign in to comment.