diff --git a/src/state/index.ts b/src/state/index.ts index 55dcae6d6f..c1b0da9588 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -11,7 +11,7 @@ export const LOCAL_DEV_SERVICE = export const STAGING_SERVICE = 'https://staging.bsky.dev' export const PROD_SERVICE = 'https://bsky.social' export const DEFAULT_SERVICE = PROD_SERVICE -const ROOT_STATE_STORAGE_KEY = 'root' +export const ROOT_STATE_STORAGE_KEY = 'root' const STATE_FETCH_INTERVAL = 15e3 export async function setupState(serviceUri = DEFAULT_SERVICE) { diff --git a/src/storage/__tests__/storage.test.ts b/src/storage/__tests__/storage.test.ts index b15194678e..08ffdb73a7 100644 --- a/src/storage/__tests__/storage.test.ts +++ b/src/storage/__tests__/storage.test.ts @@ -16,13 +16,14 @@ afterEach(() => { test(`gets and sets data synchronously`, async () => { storage.set('shell', {colorMode: 'light'}) expect(AsyncStorage.setItem).toHaveBeenCalledWith( - storage.STORAGE_ROOT_KEY, + storage.ROOT_STATE_STORAGE_KEY, JSON.stringify({shell: {colorMode: 'light'}}), ) storage.set('shell', {colorMode: 'light'}) expect(AsyncStorage.getItem).not.toHaveBeenCalled() storage.set('shell', {colorMode: 'dark'}) + // @ts-expect-error expect(storage.get('shell').colorMode).toBe('dark') }) @@ -35,5 +36,6 @@ test(`set ignores error and continues in memory`, async () => { await storage.set('shell', {colorMode: 'system'}) + // @ts-expect-error expect(storage.get('shell').colorMode).toBe('system') }) diff --git a/src/storage/index.ts b/src/storage/index.ts index b87050b348..b297e3b8da 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -3,7 +3,13 @@ import AsyncStorage from '@react-native-async-storage/async-storage' import {logger} from '#/logger' import {Schema, defaultData} from '#/storage/schema' -export const STORAGE_ROOT_KEY = 'root' +/** + * The key we use to store our data in local storage. + * + * This value exists in `src/state/index.ts` also, but that file loads every mobx + * store too, so it's duplicated here so that tests run faster. + */ +export const ROOT_STATE_STORAGE_KEY = 'root' /** * In memory cache of local storage data. Never export or reference this @@ -14,22 +20,34 @@ export const STORAGE_ROOT_KEY = 'root' */ let data: Schema = defaultData +/** + * Loads data from local storage into memory for synchronous access. This + * should be called once at the root of the application. + */ export async function init() { logger.debug(`storage initializing`) try { - const raw = await AsyncStorage.getItem(STORAGE_ROOT_KEY) + const raw = await AsyncStorage.getItem(ROOT_STATE_STORAGE_KEY) data = (raw ? JSON.parse(raw) : {}) as Schema } catch (e) { logger.error(`storage init() failed`) } } -export function get(key: T): Schema[T] { +/** + * Get a value from local storage. Returns `unknown` type to force us to + * validate and cast to the types we expect. + */ +export function get(key: T): unknown { logger.debug(`storage get(${key})`) return data[key] } +/** + * Set a value on local storage. When setting objects, you need to manually + * merge in new values. + */ export async function set(key: T, value: Schema[T]) { logger.debug(`storage set(${key}, value)`) @@ -37,7 +55,7 @@ export async function set(key: T, value: Schema[T]) { try { // TODO maybe debounce this in the future - await AsyncStorage.setItem(STORAGE_ROOT_KEY, JSON.stringify(data)) + await AsyncStorage.setItem(ROOT_STATE_STORAGE_KEY, JSON.stringify(data)) return true } catch (err) { logger.error(`storage set(${key}, value) failed`) diff --git a/src/storage/schema.ts b/src/storage/schema.ts index 3eb3c5499d..f6e96c488e 100644 --- a/src/storage/schema.ts +++ b/src/storage/schema.ts @@ -1,5 +1,5 @@ /** - * The shape of the object we store in local storage + * The shape of the object we store in local storage. */ export type Schema = { shell: { @@ -7,6 +7,11 @@ export type Schema = { } } +/** + * The default values for the schema. This is used to initialize the store, and + * is used in case AsyncStorage is unavailable. This should be kept in sync + * with the Schema type above. + */ export const defaultData: Schema = { shell: { colorMode: 'system',