More session improvements (#2129)
* More session improvements * Drop resume session retries from 3 to 1 --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com>zio/stable
parent
a0b9fd799b
commit
261a935747
|
@ -39,7 +39,7 @@ SplashScreen.preventAutoHideAsync()
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const {isInitialLoad, currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {resumeSession} = useSessionApi()
|
const {resumeSession} = useSessionApi()
|
||||||
|
|
||||||
// init
|
// init
|
||||||
|
@ -53,16 +53,6 @@ function InnerApp() {
|
||||||
resumeSession(account)
|
resumeSession(account)
|
||||||
}, [resumeSession])
|
}, [resumeSession])
|
||||||
|
|
||||||
// show nothing prior to init
|
|
||||||
if (isInitialLoad) {
|
|
||||||
// TODO add a loading state
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Session and initial state should be loaded prior to rendering below.
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment
|
<React.Fragment
|
||||||
// Resets the entire tree below when it changes:
|
// Resets the entire tree below when it changes:
|
||||||
|
|
|
@ -30,7 +30,7 @@ import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unre
|
||||||
import * as persisted from '#/state/persisted'
|
import * as persisted from '#/state/persisted'
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const {isInitialLoad, currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {resumeSession} = useSessionApi()
|
const {resumeSession} = useSessionApi()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
|
||||||
|
@ -40,16 +40,6 @@ function InnerApp() {
|
||||||
resumeSession(account)
|
resumeSession(account)
|
||||||
}, [resumeSession])
|
}, [resumeSession])
|
||||||
|
|
||||||
// show nothing prior to init
|
|
||||||
if (isInitialLoad) {
|
|
||||||
// TODO add a loading state
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Session and initial state should be loaded prior to rendering below.
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment
|
<React.Fragment
|
||||||
// Resets the entire tree below when it changes:
|
// Resets the entire tree below when it changes:
|
||||||
|
|
|
@ -28,7 +28,6 @@ export function getAgent() {
|
||||||
export type SessionAccount = persisted.PersistedAccount
|
export type SessionAccount = persisted.PersistedAccount
|
||||||
|
|
||||||
export type SessionState = {
|
export type SessionState = {
|
||||||
isInitialLoad: boolean
|
|
||||||
isSwitchingAccounts: boolean
|
isSwitchingAccounts: boolean
|
||||||
accounts: SessionAccount[]
|
accounts: SessionAccount[]
|
||||||
currentAccount: SessionAccount | undefined
|
currentAccount: SessionAccount | undefined
|
||||||
|
@ -76,7 +75,6 @@ export type ApiContext = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const StateContext = React.createContext<StateContext>({
|
const StateContext = React.createContext<StateContext>({
|
||||||
isInitialLoad: true,
|
|
||||||
isSwitchingAccounts: false,
|
isSwitchingAccounts: false,
|
||||||
accounts: [],
|
accounts: [],
|
||||||
currentAccount: undefined,
|
currentAccount: undefined,
|
||||||
|
@ -104,31 +102,43 @@ function createPersistSessionHandler(
|
||||||
}) => void,
|
}) => void,
|
||||||
): AtpPersistSessionHandler {
|
): AtpPersistSessionHandler {
|
||||||
return function persistSession(event, session) {
|
return function persistSession(event, session) {
|
||||||
const expired = !(event === 'create' || event === 'update')
|
const expired = event === 'expired' || event === 'create-failed'
|
||||||
|
|
||||||
const refreshedAccount: SessionAccount = {
|
const refreshedAccount: SessionAccount = {
|
||||||
service: account.service,
|
service: account.service,
|
||||||
did: session?.did || account.did,
|
did: session?.did || account.did,
|
||||||
handle: session?.handle || account.handle,
|
handle: session?.handle || account.handle,
|
||||||
email: session?.email || account.email,
|
email: session?.email || account.email,
|
||||||
emailConfirmed: session?.emailConfirmed || account.emailConfirmed,
|
emailConfirmed: session?.emailConfirmed || account.emailConfirmed,
|
||||||
refreshJwt: session?.refreshJwt, // undefined when expired or creation fails
|
|
||||||
accessJwt: session?.accessJwt, // undefined when expired or creation fails
|
/*
|
||||||
|
* Tokens are undefined if the session expires, or if creation fails for
|
||||||
|
* any reason e.g. tokens are invalid, network error, etc.
|
||||||
|
*/
|
||||||
|
refreshJwt: session?.refreshJwt,
|
||||||
|
accessJwt: session?.accessJwt,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(
|
logger.info(`session: persistSession`, {
|
||||||
`session: BskyAgent.persistSession`,
|
event,
|
||||||
{
|
did: refreshedAccount.did,
|
||||||
expired,
|
handle: refreshedAccount.handle,
|
||||||
did: refreshedAccount.did,
|
})
|
||||||
handle: refreshedAccount.handle,
|
|
||||||
},
|
|
||||||
logger.DebugContext.session,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (expired) {
|
if (expired) {
|
||||||
emitSessionDropped()
|
emitSessionDropped()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the session expired, or it was successfully created/updated, we want
|
||||||
|
* to update/persist the data.
|
||||||
|
*
|
||||||
|
* If the session creation failed, it could be a network error, or it could
|
||||||
|
* be more serious like an invalid token(s). We can't differentiate, so in
|
||||||
|
* order to allow the user to get a fresh token (if they need it), we need
|
||||||
|
* to persist this data and wipe their tokens, effectively logging them
|
||||||
|
* out.
|
||||||
|
*/
|
||||||
persistSessionCallback({
|
persistSessionCallback({
|
||||||
expired,
|
expired,
|
||||||
refreshedAccount,
|
refreshedAccount,
|
||||||
|
@ -140,7 +150,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const isDirty = React.useRef(false)
|
const isDirty = React.useRef(false)
|
||||||
const [state, setState] = React.useState<SessionState>({
|
const [state, setState] = React.useState<SessionState>({
|
||||||
isInitialLoad: true, // try to resume the session first
|
|
||||||
isSwitchingAccounts: false,
|
isSwitchingAccounts: false,
|
||||||
accounts: persisted.get('session').accounts,
|
accounts: persisted.get('session').accounts,
|
||||||
currentAccount: undefined, // assume logged out to start
|
currentAccount: undefined, // assume logged out to start
|
||||||
|
@ -338,7 +347,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Could not decode jwt.')
|
logger.error(`session: could not decode jwt`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const prevSession = {
|
const prevSession = {
|
||||||
|
@ -355,23 +364,53 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
upsertAccount(account)
|
upsertAccount(account)
|
||||||
|
|
||||||
// Intentionally not awaited to unblock the UI:
|
// Intentionally not awaited to unblock the UI:
|
||||||
resumeSessionWithFreshAccount().then(freshAccount => {
|
resumeSessionWithFreshAccount()
|
||||||
if (JSON.stringify(account) !== JSON.stringify(freshAccount)) {
|
.then(freshAccount => {
|
||||||
upsertAccount(freshAccount)
|
if (JSON.stringify(account) !== JSON.stringify(freshAccount)) {
|
||||||
}
|
upsertAccount(freshAccount)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
/*
|
||||||
|
* Note: `agent.persistSession` is also called when this fails, and
|
||||||
|
* we handle that failure via `createPersistSessionHandler`
|
||||||
|
*/
|
||||||
|
logger.info(`session: resumeSessionWithFreshAccount failed`, {
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
|
||||||
|
__globalAgent = PUBLIC_BSKY_AGENT
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
const freshAccount = await resumeSessionWithFreshAccount()
|
try {
|
||||||
__globalAgent = agent
|
const freshAccount = await resumeSessionWithFreshAccount()
|
||||||
queryClient.clear()
|
__globalAgent = agent
|
||||||
upsertAccount(freshAccount)
|
queryClient.clear()
|
||||||
|
upsertAccount(freshAccount)
|
||||||
|
} catch (e) {
|
||||||
|
/*
|
||||||
|
* Note: `agent.persistSession` is also called when this fails, and
|
||||||
|
* we handle that failure via `createPersistSessionHandler`
|
||||||
|
*/
|
||||||
|
logger.info(`session: resumeSessionWithFreshAccount failed`, {
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
|
||||||
|
__globalAgent = PUBLIC_BSKY_AGENT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resumeSessionWithFreshAccount(): Promise<SessionAccount> {
|
async function resumeSessionWithFreshAccount(): Promise<SessionAccount> {
|
||||||
await networkRetry(3, () => agent.resumeSession(prevSession))
|
await networkRetry(1, () => agent.resumeSession(prevSession))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If `agent.resumeSession` fails above, it'll throw. This is just to
|
||||||
|
* make TypeScript happy.
|
||||||
|
*/
|
||||||
if (!agent.session) {
|
if (!agent.session) {
|
||||||
throw new Error(`session: initSession failed to establish a session`)
|
throw new Error(`session: initSession failed to establish a session`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure changes in handle/email etc are captured on reload
|
// ensure changes in handle/email etc are captured on reload
|
||||||
return {
|
return {
|
||||||
service: agent.service.toString(),
|
service: agent.service.toString(),
|
||||||
|
@ -395,11 +434,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`session: resumeSession failed`, {error: e})
|
logger.error(`session: resumeSession failed`, {error: e})
|
||||||
} finally {
|
|
||||||
setState(s => ({
|
|
||||||
...s,
|
|
||||||
isInitialLoad: false,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[initSession],
|
[initSession],
|
||||||
|
|
Loading…
Reference in New Issue