Skip to content

Commit

Permalink
feat: added storage invalidation
Browse files Browse the repository at this point in the history
  • Loading branch information
mbret committed Mar 14, 2024
1 parent 3948708 commit a5ec015
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 55 deletions.
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export * from "./lib/binding/useBehaviorSubject"
export * from "./lib/state/signal"
export * from "./lib/state/useSignalValue"
export * from "./lib/state/constants"
export * from "./lib/state/persistance/adapters/createSharedStoreAdapter"
export * from "./lib/state/persistance/adapters/createLocalforageAdapter"
export * from "./lib/state/persistance/adapters/createLocalStorageAdapter"
export * from "./lib/state/persistance/usePersistSignals"
Expand Down
66 changes: 59 additions & 7 deletions src/lib/state/persistance/adapters/createLocalStorageAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,65 @@
export const createLocalStorageAdapter = () => ({
getItem: async (key: string) => {
const serializedValue = localStorage.getItem(key)
import { type Adapter } from "../types"

if (!serializedValue) return undefined
const normalizeStore = (store: unknown) => {
if (!store || typeof store !== "object") {
return undefined
}

return store
}

/**
* Create an adapter which use one unique store entry to store all
* state. When using many signals it can help with maintenance to keep things
* tidy.
*/
const createSharedStoreAdapter = ({
adapter,
key
}: {
adapter: Adapter
key: string
}): Adapter => ({
getItem: async (itemKey: string) => {
const unsafeStore = await adapter.getItem(key)
const store = normalizeStore(unsafeStore) ?? {}

return JSON.parse(serializedValue)
if (itemKey in store) {
return store[itemKey as keyof typeof store]
}

return undefined
},

setItem: async (key: string, value: unknown) => {
localStorage.setItem(key, JSON.stringify(value))
setItem: async (itemKey: string, value: unknown) => {
const unsafeStore = await adapter.getItem(key)
const store = normalizeStore(unsafeStore) ?? {}

await adapter.setItem(key, { ...store, [itemKey]: value })
}
})

export const createLocalStorageAdapter = ({
key
}: { key?: string } = {}): Adapter => {
if (key) {
return createSharedStoreAdapter({
adapter: createLocalStorageAdapter(),
key
})
}

return {
getItem: async (key: string) => {
const serializedValue = localStorage.getItem(key)

if (!serializedValue) return undefined

return JSON.parse(serializedValue)
},

setItem: async (key: string, value: unknown) => {
localStorage.setItem(key, JSON.stringify(value))
}
}
}
40 changes: 0 additions & 40 deletions src/lib/state/persistance/adapters/createSharedStoreAdapter.ts

This file was deleted.

67 changes: 60 additions & 7 deletions src/lib/state/persistance/usePersistSignals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
from,
map,
merge,
mergeMap,
of,
switchMap,
tap,
Expand All @@ -20,7 +21,7 @@ import type { Signal } from "../signal"
import { getNormalizedPersistanceValue } from "./getNormalizedPersistanceValue"
import { IDENTIFIER_PERSISTANCE_KEY } from "./constants"

const persistValue = async ({
const persistValue = ({
adapter,
signal,
version
Expand All @@ -37,7 +38,13 @@ const persistValue = async ({
migrationVersion: version
} satisfies PersistanceEntry

await adapter.setItem(signal.config.key, value)
return from(adapter.setItem(signal.config.key, value)).pipe(
catchError((e) => {
console.error(e)

return of(null)
})
)
}

const hydrateValueToSignal = ({
Expand Down Expand Up @@ -69,14 +76,50 @@ const hydrateValueToSignal = ({
)
}

const useInvalidateStorage = ({
adapter,
entries,
storageKey
}: {
entries: {
current: Array<{ version: number; signal: Signal<any, any, string> }>
}
adapter: {
current: Adapter
}
storageKey?: string
}) => {
useSubscribe(
() =>
!storageKey
? EMPTY
: merge(
...entries.current.map(({ signal, version }) =>
persistValue({
adapter: adapter.current,
signal,
version
})
)
),
[storageKey]
)
}

export const usePersistSignals = ({
entries = [],
onReady,
adapter = createLocalStorageAdapter()
adapter = createLocalStorageAdapter(),
storageKey
}: {
entries?: Array<{ version: number; signal: Signal<any, any, string> }>
onReady?: () => void
adapter?: Adapter
/**
* Can be used to invalidate current storage
* resulting on a re-persist of all values.
*/
storageKey?: string
}) => {
const entriesRef = useLiveRef(entries)
const onReadyRef = useLiveRef(onReady)
Expand All @@ -95,7 +138,15 @@ export const usePersistSignals = ({
adapter: adapterRef.current,
signal,
version
})
}).pipe(
mergeMap(() =>
persistValue({
adapter: adapterRef.current,
signal,
version
})
)
)
)
).pipe(map(() => true))

Expand Down Expand Up @@ -123,15 +174,17 @@ export const usePersistSignals = ({
throttleTime(500, asyncScheduler, {
trailing: true
}),
switchMap(() =>
from(
switchMap(() => {
return from(
persistValue({ adapter: adapterRef.current, signal, version })
)
)
})
)
)
)
}, [isHydrated, adapterRef])

useInvalidateStorage({ adapter: adapterRef, entries: entriesRef, storageKey })

return { isHydrated }
}

0 comments on commit a5ec015

Please sign in to comment.