Skip to content

Commit

Permalink
First pass at a session handler (#1850)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
estrattonbailey authored Nov 9, 2023
1 parent 664e7a9 commit 625cbc4
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 56 deletions.
41 changes: 29 additions & 12 deletions src/App.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ import {Provider as ModalStateProvider} from 'state/modals'
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
import {Provider as PrefsStateProvider} from 'state/preferences'
import {
Provider as SessionProvider,
useSession,
useSessionApi,
} from 'state/session'
import * as persisted from '#/state/persisted'
import {i18n} from '@lingui/core'
import {I18nProvider} from '@lingui/react'
import {messages} from './locale/locales/en/messages'
Expand All @@ -36,6 +42,8 @@ SplashScreen.preventAutoHideAsync()

const InnerApp = observer(function AppImpl() {
const colorMode = useColorMode()
const {isInitialLoad} = useSession()
const {resumeSession} = useSessionApi()
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
undefined,
)
Expand All @@ -52,10 +60,17 @@ const InnerApp = observer(function AppImpl() {
})
}, [])

useEffect(() => {
const account = persisted.get('session').currentAccount
resumeSession(account)
}, [resumeSession])

// show nothing prior to init
if (!rootStore) {
if (!rootStore || isInitialLoad) {
// TODO add a loading state
return null
}

return (
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={colorMode}>
Expand Down Expand Up @@ -88,17 +103,19 @@ function App() {
}

return (
<ShellStateProvider>
<PrefsStateProvider>
<MutedThreadsProvider>
<InvitesStateProvider>
<ModalStateProvider>
<InnerApp />
</ModalStateProvider>
</InvitesStateProvider>
</MutedThreadsProvider>
</PrefsStateProvider>
</ShellStateProvider>
<SessionProvider>
<ShellStateProvider>
<PrefsStateProvider>
<MutedThreadsProvider>
<InvitesStateProvider>
<ModalStateProvider>
<InnerApp />
</ModalStateProvider>
</InvitesStateProvider>
</MutedThreadsProvider>
</PrefsStateProvider>
</ShellStateProvider>
</SessionProvider>
)
}

Expand Down
42 changes: 29 additions & 13 deletions src/App.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,16 @@ import {Provider as ModalStateProvider} from 'state/modals'
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
import {Provider as PrefsStateProvider} from 'state/preferences'
import {
Provider as SessionProvider,
useSession,
useSessionApi,
} from 'state/session'
import * as persisted from '#/state/persisted'

const InnerApp = observer(function AppImpl() {
const {isInitialLoad} = useSession()
const {resumeSession} = useSessionApi()
const colorMode = useColorMode()
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
undefined,
Expand All @@ -38,10 +46,16 @@ const InnerApp = observer(function AppImpl() {
analytics.init(store)
})
dynamicActivate(defaultLocale) // async import of locale data
}, [])
}, [resumeSession])

useEffect(() => {
const account = persisted.get('session').currentAccount
resumeSession(account)
}, [resumeSession])

// show nothing prior to init
if (!rootStore) {
if (!rootStore || isInitialLoad) {
// TODO add a loading state
return null
}

Expand Down Expand Up @@ -77,17 +91,19 @@ function App() {
}

return (
<ShellStateProvider>
<PrefsStateProvider>
<MutedThreadsProvider>
<InvitesStateProvider>
<ModalStateProvider>
<InnerApp />
</ModalStateProvider>
</InvitesStateProvider>
</MutedThreadsProvider>
</PrefsStateProvider>
</ShellStateProvider>
<SessionProvider>
<ShellStateProvider>
<PrefsStateProvider>
<MutedThreadsProvider>
<InvitesStateProvider>
<ModalStateProvider>
<InnerApp />
</ModalStateProvider>
</InvitesStateProvider>
</MutedThreadsProvider>
</PrefsStateProvider>
</ShellStateProvider>
</SessionProvider>
)
}

Expand Down
6 changes: 4 additions & 2 deletions src/state/persisted/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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,
Expand Down
28 changes: 15 additions & 13 deletions src/state/persisted/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
}
Expand Down
12 changes: 7 additions & 5 deletions src/state/persisted/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']),
Expand Down
Loading

0 comments on commit 625cbc4

Please sign in to comment.