First pass at a session handler (#1850)

* First pass at a session handler

* TODOs

* Fix recursion

* Couple more things

* Add back resume session concept

* Handle ready

* Cleanup of initial loading states

* Handle init failure

* Cleanup

* Remove account

* Add updateCurrentAccount

* Remove log

* Cleanup

* Integrate removeAccount

* Add hasSession

* Add to App.native, harden migration

* Use effect to persist data
This commit is contained in:
Eric Bailey 2023-11-09 17:14:51 -06:00 committed by GitHub
parent 664e7a91a9
commit 625cbc435f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 488 additions and 56 deletions

View file

@ -5,7 +5,7 @@ import {migrate} from '#/state/persisted/legacy'
import * as store from '#/state/persisted/store'
import BroadcastChannel from '#/state/persisted/broadcast'
export type {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')
@ -50,7 +50,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,

View file

@ -66,43 +66,45 @@ 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,
},
requireAltTextEnabled:
legacy.preferences.requireAltTextEnabled ||
legacy.preferences?.requireAltTextEnabled ||
defaults.requireAltTextEnabled,
mutedThreads: legacy.mutedThreads.uris || defaults.mutedThreads,
mutedThreads: legacy.mutedThreads?.uris || defaults.mutedThreads,
invites: {
copiedInvites:
legacy.invitedUsers.copiedInvites || defaults.invites.copiedInvites,
legacy.invitedUsers?.copiedInvites || defaults.invites.copiedInvites,
},
onboarding: {
step: legacy.onboarding.step || defaults.onboarding.step,
step: legacy.onboarding?.step || defaults.onboarding.step,
},
}
}

View file

@ -2,15 +2,17 @@ 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().optional(),
displayName: z.string().optional(),
aviUrl: z.string().optional(),
handle: z.string(),
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']),