Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Session] Call persist handler directly #3828

Merged
merged 1 commit into from
May 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 63 additions & 96 deletions src/state/session/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import {AtpPersistSessionHandler, BskyAgent} from '@atproto/api'
import {AtpSessionData, AtpSessionEvent, BskyAgent} from '@atproto/api'

import {track} from '#/lib/analytics/analytics'
import {networkRetry} from '#/lib/async/retry'
Expand Down Expand Up @@ -93,77 +93,65 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
}))
}, [setState])

const createPersistSessionHandler = React.useCallback(
const onAgentSessionChange = React.useCallback(
(
agent: BskyAgent,
account: SessionAccount,
persistSessionCallback: (props: {
expired: boolean
refreshedAccount: SessionAccount
}) => void,
{
networkErrorCallback,
}: {
networkErrorCallback?: () => void
} = {},
): AtpPersistSessionHandler => {
return function persistSession(event, session) {
const expired = event === 'expired' || event === 'create-failed'

if (event === 'network-error') {
logger.warn(
`session: persistSessionHandler received network-error event`,
)
networkErrorCallback?.()
return
}

// TODO: use agentToSessionAccount for this too.
const refreshedAccount: SessionAccount = {
service: account.service,
did: session?.did || account.did,
handle: session?.handle || account.handle,
email: session?.email || account.email,
emailConfirmed: session?.emailConfirmed || account.emailConfirmed,
emailAuthFactor: session?.emailAuthFactor || account.emailAuthFactor,
deactivated: isSessionDeactivated(session?.accessJwt),
pdsUrl: agent.pdsUrl?.toString(),

/*
* 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(`session: persistSession`, {
event,
deactivated: refreshedAccount.deactivated,
})
event: AtpSessionEvent,
session: AtpSessionData | undefined,
) => {
const expired = event === 'expired' || event === 'create-failed'

if (event === 'network-error') {
logger.warn(
`session: persistSessionHandler received network-error event`,
)
clearCurrentAccount()
return
}

if (expired) {
logger.warn(`session: expired`)
emitSessionDropped()
}
// TODO: use agentToSessionAccount for this too.
const refreshedAccount: SessionAccount = {
service: account.service,
did: session?.did || account.did,
handle: session?.handle || account.handle,
email: session?.email || account.email,
emailConfirmed: session?.emailConfirmed || account.emailConfirmed,
emailAuthFactor: session?.emailAuthFactor || account.emailAuthFactor,
deactivated: isSessionDeactivated(session?.accessJwt),
pdsUrl: agent.pdsUrl?.toString(),

/*
* 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.
* Tokens are undefined if the session expires, or if creation fails for
* any reason e.g. tokens are invalid, network error, etc.
*/
persistSessionCallback({
expired,
refreshedAccount,
})
refreshJwt: session?.refreshJwt,
accessJwt: session?.accessJwt,
}

logger.debug(`session: persistSession`, {
event,
deactivated: refreshedAccount.deactivated,
})

if (expired) {
logger.warn(`session: expired`)
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.
*/
upsertAccount(refreshedAccount, expired)
},
[],
[clearCurrentAccount, upsertAccount],
)

const createAccount = React.useCallback<SessionApiContext['createAccount']>(
Expand Down Expand Up @@ -191,16 +179,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
},
)

agent.setPersistSessionHandler(
createPersistSessionHandler(
agent,
account,
({expired, refreshedAccount}) => {
upsertAccount(refreshedAccount, expired)
},
{networkErrorCallback: clearCurrentAccount},
),
)
agent.setPersistSessionHandler((event, session) => {
onAgentSessionChange(agent, account, event, session)
})

__globalAgent = agent
await fetchingGates
Expand All @@ -210,7 +191,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
track('Create Account')
logEvent('account:create:success', {})
},
[upsertAccount, clearCurrentAccount, createPersistSessionHandler],
[upsertAccount, onAgentSessionChange],
)

const login = React.useCallback<SessionApiContext['login']>(
Expand All @@ -223,16 +204,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
authFactorToken,
})

agent.setPersistSessionHandler(
createPersistSessionHandler(
agent,
account,
({expired, refreshedAccount}) => {
upsertAccount(refreshedAccount, expired)
},
{networkErrorCallback: clearCurrentAccount},
),
)
agent.setPersistSessionHandler((event, session) => {
onAgentSessionChange(agent, account, event, session)
})

__globalAgent = agent
// @ts-ignore
Expand All @@ -245,7 +219,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
track('Sign In', {resumedSession: false})
logEvent('account:loggedIn', {logContext, withPassword: true})
},
[upsertAccount, clearCurrentAccount, createPersistSessionHandler],
[upsertAccount, onAgentSessionChange],
)

const logout = React.useCallback<SessionApiContext['logout']>(
Expand Down Expand Up @@ -280,16 +254,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
agent.pdsUrl = agent.api.xrpc.uri = new URL(account.pdsUrl)
}

agent.setPersistSessionHandler(
createPersistSessionHandler(
agent,
account,
({expired, refreshedAccount}) => {
upsertAccount(refreshedAccount, expired)
},
{networkErrorCallback: clearCurrentAccount},
),
)
agent.setPersistSessionHandler((event, session) => {
onAgentSessionChange(agent, account, event, session)
})

// @ts-ignore
if (IS_DEV && isWeb) window.agent = agent
Expand Down Expand Up @@ -380,7 +347,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
return sessionAccount
}
},
[upsertAccount, clearCurrentAccount, createPersistSessionHandler],
[upsertAccount, onAgentSessionChange],
)

const removeAccount = React.useCallback<SessionApiContext['removeAccount']>(
Expand Down
Loading