From 4b706c9519c548d53529876bdf815836ec71bdd4 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 7 Feb 2025 11:18:51 +0000 Subject: Per-user search history (#7588) * per-user search history * move to mmkv with cool new hook * revert accidental changes This reverts commit 27c89fa645eff0acb7a8fd852203ff1ea3725c69. * restore limits --- src/storage/index.ts | 72 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/storage/schema.ts | 1 + 2 files changed, 70 insertions(+), 3 deletions(-) (limited to 'src/storage') diff --git a/src/storage/index.ts b/src/storage/index.ts index 8ec09eefa..9b39e1c1a 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,5 +1,5 @@ +import {useCallback, useEffect, useState} from 'react' import {MMKV} from 'react-native-mmkv' -import {Did} from '@atproto/api' import {Account, Device} from '#/storage/schema' @@ -65,6 +65,72 @@ export class Storage { removeMany(scopes: [...Scopes], keys: Key[]) { keys.forEach(key => this.remove([...scopes, key])) } + + /** + * Fires a callback when the storage associated with a given key changes + * + * @returns Listener - call `remove()` to stop listening + */ + addOnValueChangedListener( + scopes: [...Scopes, Key], + callback: () => void, + ) { + return this.store.addOnValueChangedListener(key => { + if (key === scopes.join(this.sep)) { + callback() + } + }) + } +} + +type StorageSchema> = T extends Storage< + any, + infer U +> + ? U + : never +type StorageScopes> = T extends Storage< + infer S, + any +> + ? S + : never + +/** + * Hook to use a storage instance. Acts like a useState hook, but persists the + * value in storage. + */ +export function useStorage< + Store extends Storage, + Key extends keyof StorageSchema, +>( + storage: Store, + scopes: [...StorageScopes, Key], +): [ + StorageSchema[Key] | undefined, + (data: StorageSchema[Key]) => void, +] { + type Schema = StorageSchema + const [value, setValue] = useState(() => + storage.get(scopes), + ) + + useEffect(() => { + const sub = storage.addOnValueChangedListener(scopes, () => { + setValue(storage.get(scopes)) + }) + return () => sub.remove() + }, [storage, scopes]) + + const setter = useCallback( + (data: Schema[Key]) => { + setValue(data) + storage.set(scopes, data) + }, + [storage, scopes], + ) + + return [value, setter] as const } /** @@ -77,10 +143,10 @@ export const device = new Storage<[], Device>({id: 'bsky_device'}) /** * Account data that's specific to the account on this device */ -export const account = new Storage<[Did], Account>({id: 'bsky_account'}) +export const account = new Storage<[string], Account>({id: 'bsky_account'}) if (__DEV__ && typeof window !== 'undefined') { - // @ts-ignore + // @ts-expect-error - dev global window.bsky_storage = { device, account, diff --git a/src/storage/schema.ts b/src/storage/schema.ts index d8b9874e4..667b43208 100644 --- a/src/storage/schema.ts +++ b/src/storage/schema.ts @@ -13,4 +13,5 @@ export type Device = { export type Account = { searchTermHistory?: string[] + searchAccountHistory?: string[] } -- cgit 1.4.1