From bdb9082e7e4b91815437f20d1a6b5809156a3a71 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Mon, 6 Nov 2023 13:31:03 -0600 Subject: [PATCH] Refinement --- src/storage/__tests__/storage.test.ts | 16 +++---- src/storage/index.ts | 41 ++++++++--------- src/storage/schema.ts | 64 ++------------------------- 3 files changed, 33 insertions(+), 88 deletions(-) diff --git a/src/storage/__tests__/storage.test.ts b/src/storage/__tests__/storage.test.ts index ec4692ffe9..b15194678e 100644 --- a/src/storage/__tests__/storage.test.ts +++ b/src/storage/__tests__/storage.test.ts @@ -14,26 +14,26 @@ afterEach(() => { }) test(`gets and sets data synchronously`, async () => { - storage.set('shell.colorMode', 'light') + storage.set('shell', {colorMode: 'light'}) expect(AsyncStorage.setItem).toHaveBeenCalledWith( storage.STORAGE_ROOT_KEY, JSON.stringify({shell: {colorMode: 'light'}}), ) - expect(storage.get('shell.colorMode')).toBe('light') + storage.set('shell', {colorMode: 'light'}) expect(AsyncStorage.getItem).not.toHaveBeenCalled() - storage.set('shell.colorMode', 'dark') - expect(storage.get('shell.colorMode')).toBe('dark') + storage.set('shell', {colorMode: 'dark'}) + expect(storage.get('shell').colorMode).toBe('dark') }) -test(`set rolls back on error`, async () => { - storage.set('shell.colorMode', 'light') +test(`set ignores error and continues in memory`, async () => { + storage.set('shell', {colorMode: 'light'}) jest .mocked(AsyncStorage.setItem) .mockImplementationOnce(() => Promise.reject(new Error('test error'))) - await storage.set('shell.colorMode', 'system') + await storage.set('shell', {colorMode: 'system'}) - expect(storage.get('shell.colorMode')).toBe('light') + expect(storage.get('shell').colorMode).toBe('system') }) diff --git a/src/storage/index.ts b/src/storage/index.ts index be401497c6..b87050b348 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,45 +1,46 @@ import AsyncStorage from '@react-native-async-storage/async-storage' import {logger} from '#/logger' -import {Schema, PropertyMap} from '#/storage/schema' +import {Schema, defaultData} from '#/storage/schema' export const STORAGE_ROOT_KEY = 'root' /** * In memory cache of local storage data. Never export or reference this * directly. + * + * We set it to a default value so that if AsyncStorage entirely fails, the app + * will continue to function entirely in memory. */ -let data: Schema | undefined +let data: Schema = defaultData export async function init() { - const raw = await AsyncStorage.getItem(STORAGE_ROOT_KEY) - logger.debug(`storage initialized`) - // TODO: Add try catch here etc, same in lib/storage.ts - data = (raw ? JSON.parse(raw) : {}) as Schema + logger.debug(`storage initializing`) + + try { + const raw = await AsyncStorage.getItem(STORAGE_ROOT_KEY) + data = (raw ? JSON.parse(raw) : {}) as Schema + } catch (e) { + logger.error(`storage init() failed`) + } } -export function get(key: T): PropertyMap[T] { +export function get(key: T): Schema[T] { logger.debug(`storage get(${key})`) - if (!data) { - throw new Error(`Data is not initialized. Did you forget to call init()`) - } return data[key] } -export async function set( - key: T, - value: PropertyMap[T], -) { +export async function set(key: T, value: Schema[T]) { logger.debug(`storage set(${key}, value)`) - if (!data) { - throw new Error(`Data is not initialized. Did you forget to call init()`) - } - data[ley] = value - // TODO: actual writing should probably be debounced + data[key] = value + try { + // TODO maybe debounce this in the future await AsyncStorage.setItem(STORAGE_ROOT_KEY, JSON.stringify(data)) + return true } catch (err) { - logger.error(`storage set(${keypath}, value) failed`) + logger.error(`storage set(${key}, value) failed`) + return false } } diff --git a/src/storage/schema.ts b/src/storage/schema.ts index 32958dc994..3eb3c5499d 100644 --- a/src/storage/schema.ts +++ b/src/storage/schema.ts @@ -1,5 +1,3 @@ -// import {LabelPreference} from '@atproto/api' - /** * The shape of the object we store in local storage */ @@ -7,64 +5,10 @@ export type Schema = { shell: { colorMode: 'system' | 'light' | 'dark' } - // session: { - // data: { - // service: string - // did: `did:plc:${string}` - // } - // accounts: { - // service: string - // did: `did:plc:${string}` - // refreshJwt: string - // accessJwt: string - // handle: string - // email: string - // displayName: string - // aviUrl: string - // emailConfirmed: boolean - // }[] - // } - // me: { - // did: `did:plc:${string}` - // handle: string - // displayName: string - // description: string - // avatar: string - // } - // onboarding: { - // step: string - // } - // preferences: { - // primaryLanguage: string - // contentLanguages: string[] - // postLanguage: string - // postLanguageHistory: string[] - // contentLabels: { - // nsfw: LabelPreference - // nudity: LabelPreference - // suggestive: LabelPreference - // gore: LabelPreference - // hate: LabelPreference - // spam: LabelPreference - // impersonation: LabelPreference - // } - // savedFeeds: string[] - // pinnedFeeds: string[] - // requireAltTextEnabled: boolean - // } - // invitedUsers: { - // seenDids: string[] - // copiedInvites: string[] - // } - // mutedThreads: {uris: string[]} - // reminders: {lastEmailConfirm: string} } -/** - * A map of all the properties that can be stored in the storage, and their - * values. This mapping must be updated in order to get/set values by their - * keypaths. - */ -export type PropertyMap = { - 'shell.colorMode': Schema['shell']['colorMode'] +export const defaultData: Schema = { + shell: { + colorMode: 'system', + }, }