diff options
Diffstat (limited to 'src/state/persisted')
-rw-r--r-- | src/state/persisted/broadcast/index.ts | 6 | ||||
-rw-r--r-- | src/state/persisted/broadcast/index.web.ts | 1 | ||||
-rw-r--r-- | src/state/persisted/index.ts | 15 | ||||
-rw-r--r-- | src/state/persisted/legacy.ts | 84 | ||||
-rw-r--r-- | src/state/persisted/schema.ts | 20 | ||||
-rw-r--r-- | src/state/persisted/store.ts | 9 |
6 files changed, 94 insertions, 41 deletions
diff --git a/src/state/persisted/broadcast/index.ts b/src/state/persisted/broadcast/index.ts deleted file mode 100644 index e0e7f724b..000000000 --- a/src/state/persisted/broadcast/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default class BroadcastChannel { - constructor(public name: string) {} - postMessage(_data: any) {} - close() {} - onmessage: (event: MessageEvent) => void = () => {} -} diff --git a/src/state/persisted/broadcast/index.web.ts b/src/state/persisted/broadcast/index.web.ts deleted file mode 100644 index 33b3548ad..000000000 --- a/src/state/persisted/broadcast/index.web.ts +++ /dev/null @@ -1 +0,0 @@ -export default BroadcastChannel diff --git a/src/state/persisted/index.ts b/src/state/persisted/index.ts index 67fac6b65..545fdc0e1 100644 --- a/src/state/persisted/index.ts +++ b/src/state/persisted/index.ts @@ -3,10 +3,10 @@ import {logger} from '#/logger' import {defaults, Schema} from '#/state/persisted/schema' import {migrate} from '#/state/persisted/legacy' import * as store from '#/state/persisted/store' -import BroadcastChannel from '#/state/persisted/broadcast' +import BroadcastChannel from '#/lib/broadcast' -export type {Schema} from '#/state/persisted/schema' -export {defaults as schema} from '#/state/persisted/schema' +export type {Schema, PersistedAccount} from '#/state/persisted/schema' +export {defaults} from '#/state/persisted/schema' const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL') const UPDATE_EVENT = 'BSKY_UPDATE' @@ -19,7 +19,7 @@ const _emitter = new EventEmitter() * the Provider. */ export async function init() { - logger.debug('persisted state: initializing') + logger.info('persisted state: initializing') broadcast.onmessage = onBroadcastMessage @@ -28,11 +28,12 @@ export async function init() { const stored = await store.read() // check for new store if (!stored) await store.write(defaults) // opt: init new store _state = stored || defaults // return new store + logger.log('persisted state: initialized') } catch (e) { logger.error('persisted state: failed to load root state from storage', { error: e, }) - // AsyncStorage failured, but we can still continue in memory + // AsyncStorage failure, but we can still continue in memory return defaults } } @@ -50,7 +51,9 @@ export async function write<K extends keyof Schema>( await store.write(_state) // must happen on next tick, otherwise the tab will read stale storage data setTimeout(() => broadcast.postMessage({event: UPDATE_EVENT}), 0) - logger.debug(`persisted state: wrote root state to storage`) + logger.debug(`persisted state: wrote root state to storage`, { + updatedKey: key, + }) } catch (e) { logger.error(`persisted state: failed writing root state to storage`, { error: e, diff --git a/src/state/persisted/legacy.ts b/src/state/persisted/legacy.ts index 67eef81a0..025877529 100644 --- a/src/state/persisted/legacy.ts +++ b/src/state/persisted/legacy.ts @@ -66,45 +66,47 @@ type LegacySchema = { const DEPRECATED_ROOT_STATE_STORAGE_KEY = 'root' -export function transform(legacy: LegacySchema): Schema { +// TODO remove, assume that partial data may be here during our refactor +export function transform(legacy: Partial<LegacySchema>): Schema { return { colorMode: legacy.shell?.colorMode || defaults.colorMode, session: { - accounts: legacy.session.accounts || defaults.session.accounts, + accounts: legacy.session?.accounts || defaults.session.accounts, currentAccount: - legacy.session.accounts.find(a => a.did === legacy.session.data.did) || - defaults.session.currentAccount, + legacy.session?.accounts?.find( + a => a.did === legacy.session?.data?.did, + ) || defaults.session.currentAccount, }, reminders: { lastEmailConfirm: - legacy.reminders.lastEmailConfirm || + legacy.reminders?.lastEmailConfirm || defaults.reminders.lastEmailConfirm, }, languagePrefs: { primaryLanguage: - legacy.preferences.primaryLanguage || + legacy.preferences?.primaryLanguage || defaults.languagePrefs.primaryLanguage, contentLanguages: - legacy.preferences.contentLanguages || + legacy.preferences?.contentLanguages || defaults.languagePrefs.contentLanguages, postLanguage: - legacy.preferences.postLanguage || defaults.languagePrefs.postLanguage, + legacy.preferences?.postLanguage || defaults.languagePrefs.postLanguage, postLanguageHistory: - legacy.preferences.postLanguageHistory || + legacy.preferences?.postLanguageHistory || defaults.languagePrefs.postLanguageHistory, + appLanguage: + legacy.preferences?.postLanguage || defaults.languagePrefs.appLanguage, }, requireAltTextEnabled: - legacy.preferences.requireAltTextEnabled || + legacy.preferences?.requireAltTextEnabled || defaults.requireAltTextEnabled, - mutedThreads: legacy.mutedThreads.uris || defaults.mutedThreads, - invitedUsers: { - seenDids: legacy.invitedUsers.seenDids || defaults.invitedUsers.seenDids, + mutedThreads: legacy.mutedThreads?.uris || defaults.mutedThreads, + invites: { copiedInvites: - legacy.invitedUsers.copiedInvites || - defaults.invitedUsers.copiedInvites, + legacy.invitedUsers?.copiedInvites || defaults.invites.copiedInvites, }, onboarding: { - step: legacy.onboarding.step || defaults.onboarding.step, + step: legacy.onboarding?.step || defaults.onboarding.step, }, } } @@ -114,20 +116,52 @@ export function transform(legacy: LegacySchema): Schema { * local storage AND old storage exists. */ export async function migrate() { - logger.debug('persisted state: migrate') + logger.info('persisted state: migrate') try { const rawLegacyData = await AsyncStorage.getItem( DEPRECATED_ROOT_STATE_STORAGE_KEY, ) - const alreadyMigrated = Boolean(await read()) + const newData = await read() + const alreadyMigrated = Boolean(newData) + + try { + if (rawLegacyData) { + const legacy = JSON.parse(rawLegacyData) as Partial<LegacySchema> + logger.info(`persisted state: debug legacy data`, { + hasExistingLoggedInAccount: Boolean(legacy?.session?.data), + numberOfExistingAccounts: legacy?.session?.accounts?.length, + foundExistingCurrentAccount: Boolean( + legacy.session?.accounts?.find( + a => a.did === legacy.session?.data?.did, + ), + ), + }) + logger.info(`persisted state: debug new data`, { + hasExistingLoggedInAccount: Boolean(newData?.session?.currentAccount), + numberOfExistingAccounts: newData?.session?.accounts?.length, + existingAccountMatchesLegacy: Boolean( + newData?.session?.currentAccount?.did === + legacy?.session?.data?.did, + ), + }) + } else { + logger.info(`persisted state: no legacy to debug, fresh install`) + } + } catch (e) { + logger.error(`persisted state: legacy debugging failed`, {error: e}) + } if (!alreadyMigrated && rawLegacyData) { - logger.debug('persisted state: migrating legacy storage') + logger.info('persisted state: migrating legacy storage') const legacyData = JSON.parse(rawLegacyData) const newData = transform(legacyData) await write(newData) - logger.debug('persisted state: migrated legacy storage') + // track successful migrations + logger.log('persisted state: migrated legacy storage') + } else { + // track successful migrations + logger.log('persisted state: no migration needed') } } catch (e) { logger.error('persisted state: error migrating legacy storage', { @@ -135,3 +169,13 @@ export async function migrate() { }) } } + +export async function clearLegacyStorage() { + try { + await AsyncStorage.removeItem(DEPRECATED_ROOT_STATE_STORAGE_KEY) + } catch (e: any) { + logger.error(`persisted legacy store: failed to clear`, { + error: e.toString(), + }) + } +} diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts index c00ee500a..71f9bd545 100644 --- a/src/state/persisted/schema.ts +++ b/src/state/persisted/schema.ts @@ -2,15 +2,19 @@ import {z} from 'zod' import {deviceLocales} from '#/platform/detection' // only data needed for rendering account page +// TODO agent.resumeSession requires the following fields const accountSchema = z.object({ service: z.string(), did: z.string(), - refreshJwt: z.string().optional(), - accessJwt: z.string().optional(), handle: z.string(), - displayName: z.string(), - aviUrl: z.string(), + email: z.string(), + emailConfirmed: z.boolean(), + refreshJwt: z.string().optional(), // optional because it can expire + accessJwt: z.string().optional(), // optional because it can expire + // displayName: z.string().optional(), + // aviUrl: z.string().optional(), }) +export type PersistedAccount = z.infer<typeof accountSchema> export const schema = z.object({ colorMode: z.enum(['system', 'light', 'dark']), @@ -26,11 +30,11 @@ export const schema = z.object({ contentLanguages: z.array(z.string()), // should move to server postLanguage: z.string(), // should move to server postLanguageHistory: z.array(z.string()), + appLanguage: z.string(), }), requireAltTextEnabled: z.boolean(), // should move to server mutedThreads: z.array(z.string()), // should move to server - invitedUsers: z.object({ - seenDids: z.array(z.string()), + invites: z.object({ copiedInvites: z.array(z.string()), }), onboarding: z.object({ @@ -55,11 +59,11 @@ export const defaults: Schema = { postLanguageHistory: (deviceLocales || []) .concat(['en', 'ja', 'pt', 'de']) .slice(0, 6), + appLanguage: deviceLocales[0] || 'en', }, requireAltTextEnabled: false, mutedThreads: [], - invitedUsers: { - seenDids: [], + invites: { copiedInvites: [], }, onboarding: { diff --git a/src/state/persisted/store.ts b/src/state/persisted/store.ts index 2b03bec20..04858fe5b 100644 --- a/src/state/persisted/store.ts +++ b/src/state/persisted/store.ts @@ -1,6 +1,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage' import {Schema, schema} from '#/state/persisted/schema' +import {logger} from '#/logger' const BSKY_STORAGE = 'BSKY_STORAGE' @@ -16,3 +17,11 @@ export async function read(): Promise<Schema | undefined> { return objData } } + +export async function clear() { + try { + await AsyncStorage.removeItem(BSKY_STORAGE) + } catch (e: any) { + logger.error(`persisted store: failed to clear`, {error: e.toString()}) + } +} |