Use BskyAgent as the source of truth
This commit is contained in:
+161
-177
@@ -19,6 +19,9 @@ import {useCloseAllActiveElements} from '#/state/util'
|
|||||||
import {emitSessionDropped} from '../events'
|
import {emitSessionDropped} from '../events'
|
||||||
import {readLabelers} from './agent-config'
|
import {readLabelers} from './agent-config'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use `agent` from `useSession` instead
|
||||||
|
*/
|
||||||
let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
|
let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +29,8 @@ let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
|
|||||||
* Never hold on to the object returned by this function.
|
* Never hold on to the object returned by this function.
|
||||||
* Call `getAgent()` at the time of invocation to ensure
|
* Call `getAgent()` at the time of invocation to ensure
|
||||||
* that you never have a stale agent.
|
* that you never have a stale agent.
|
||||||
|
*
|
||||||
|
* @deprecated use `agent` from `useSession` instead
|
||||||
*/
|
*/
|
||||||
export function getAgent() {
|
export function getAgent() {
|
||||||
return __globalAgent
|
return __globalAgent
|
||||||
@@ -33,15 +38,14 @@ export function getAgent() {
|
|||||||
|
|
||||||
export type SessionAccount = persisted.PersistedAccount
|
export type SessionAccount = persisted.PersistedAccount
|
||||||
|
|
||||||
export type SessionState = {
|
export type StateContext = {
|
||||||
|
agent: BskyAgent
|
||||||
isInitialLoad: boolean
|
isInitialLoad: boolean
|
||||||
isSwitchingAccounts: boolean
|
isSwitchingAccounts: boolean
|
||||||
|
hasSession: boolean
|
||||||
accounts: SessionAccount[]
|
accounts: SessionAccount[]
|
||||||
currentAccount: SessionAccount | undefined
|
currentAccount: SessionAccount | undefined
|
||||||
}
|
}
|
||||||
export type StateContext = SessionState & {
|
|
||||||
hasSession: boolean
|
|
||||||
}
|
|
||||||
export type ApiContext = {
|
export type ApiContext = {
|
||||||
createAccount: (props: {
|
createAccount: (props: {
|
||||||
service: string
|
service: string
|
||||||
@@ -83,14 +87,14 @@ export type ApiContext = {
|
|||||||
account: SessionAccount,
|
account: SessionAccount,
|
||||||
logContext: LogEvents['account:loggedIn']['logContext'],
|
logContext: LogEvents['account:loggedIn']['logContext'],
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
updateCurrentAccount: (
|
/**
|
||||||
account: Partial<
|
* Refreshes the BskyAgent's session and derive a fresh `currentAccount`
|
||||||
Pick<SessionAccount, 'handle' | 'email' | 'emailConfirmed'>
|
*/
|
||||||
>,
|
refreshSession: () => void
|
||||||
) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const StateContext = React.createContext<StateContext>({
|
const StateContext = React.createContext<StateContext>({
|
||||||
|
agent: PUBLIC_BSKY_AGENT,
|
||||||
isInitialLoad: true,
|
isInitialLoad: true,
|
||||||
isSwitchingAccounts: false,
|
isSwitchingAccounts: false,
|
||||||
accounts: [],
|
accounts: [],
|
||||||
@@ -106,10 +110,43 @@ const ApiContext = React.createContext<ApiContext>({
|
|||||||
resumeSession: async () => {},
|
resumeSession: async () => {},
|
||||||
removeAccount: () => {},
|
removeAccount: () => {},
|
||||||
selectAccount: async () => {},
|
selectAccount: async () => {},
|
||||||
updateCurrentAccount: () => {},
|
refreshSession: () => {},
|
||||||
clearCurrentAccount: () => {},
|
clearCurrentAccount: () => {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function agentToSessionAccount(agent: BskyAgent): SessionAccount | undefined {
|
||||||
|
if (!agent.session) return undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
service: agent.service.toString(),
|
||||||
|
did: agent.session.did,
|
||||||
|
handle: agent.session.handle,
|
||||||
|
email: agent.session.email,
|
||||||
|
emailConfirmed: agent.session.emailConfirmed,
|
||||||
|
deactivated: isSessionDeactivated(agent.session.accessJwt),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tokens are undefined if the session expires, or if creation fails for
|
||||||
|
* any reason e.g. tokens are invalid, network error, etc.
|
||||||
|
*/
|
||||||
|
refreshJwt: agent.session.refreshJwt,
|
||||||
|
accessJwt: agent.session.accessJwt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sessionAccountToAgentSession(
|
||||||
|
account: SessionAccount,
|
||||||
|
): BskyAgent['session'] {
|
||||||
|
return {
|
||||||
|
did: account.did,
|
||||||
|
handle: account.handle,
|
||||||
|
email: account.email,
|
||||||
|
emailConfirmed: account.emailConfirmed,
|
||||||
|
accessJwt: account.accessJwt || '',
|
||||||
|
refreshJwt: account.refreshJwt || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createPersistSessionHandler(
|
function createPersistSessionHandler(
|
||||||
account: SessionAccount,
|
account: SessionAccount,
|
||||||
persistSessionCallback: (props: {
|
persistSessionCallback: (props: {
|
||||||
@@ -176,43 +213,40 @@ function createPersistSessionHandler(
|
|||||||
|
|
||||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
const isDirty = React.useRef(false)
|
const isDirty = React.useRef(false)
|
||||||
const [state, setState] = React.useState<SessionState>({
|
const [agent, setAgent] = React.useState<BskyAgent>(PUBLIC_BSKY_AGENT)
|
||||||
isInitialLoad: true,
|
const [accounts, setAccounts] = React.useState<SessionAccount[]>(
|
||||||
isSwitchingAccounts: false,
|
persisted.get('session').accounts,
|
||||||
accounts: persisted.get('session').accounts,
|
)
|
||||||
currentAccount: undefined, // assume logged out to start
|
const [isInitialLoad, setIsInitialLoad] = React.useState(true)
|
||||||
})
|
const [isSwitchingAccounts, setIsSwitchingAccounts] = React.useState(false)
|
||||||
|
const currentAccount = React.useMemo(
|
||||||
const setStateAndPersist = React.useCallback(
|
() => agentToSessionAccount(agent),
|
||||||
(fn: (prev: SessionState) => SessionState) => {
|
[agent],
|
||||||
isDirty.current = true
|
|
||||||
setState(fn)
|
|
||||||
},
|
|
||||||
[setState],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const setCurrentAccount = React.useCallback(
|
const persistNextUpdate = React.useCallback(
|
||||||
|
() => (isDirty.current = true),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
const upsertAccount = React.useCallback(
|
||||||
(account: SessionAccount) => {
|
(account: SessionAccount) => {
|
||||||
setStateAndPersist(s => {
|
persistNextUpdate()
|
||||||
return {
|
setAccounts(accounts => [
|
||||||
...s,
|
account,
|
||||||
currentAccount: account,
|
...accounts.filter(a => a.did !== account.did),
|
||||||
accounts: [account, ...s.accounts.filter(a => a.did !== account.did)],
|
])
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
[setStateAndPersist],
|
[setAccounts, persistNextUpdate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const clearCurrentAccount = React.useCallback(() => {
|
const clearCurrentAccount = React.useCallback(() => {
|
||||||
logger.warn(`session: clear current account`)
|
logger.warn(`session: clear current account`)
|
||||||
__globalAgent = PUBLIC_BSKY_AGENT
|
|
||||||
|
persistNextUpdate()
|
||||||
|
setAgent(PUBLIC_BSKY_AGENT)
|
||||||
BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
|
BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
|
||||||
setStateAndPersist(s => ({
|
}, [persistNextUpdate, setAgent])
|
||||||
...s,
|
|
||||||
currentAccount: undefined,
|
|
||||||
}))
|
|
||||||
}, [setStateAndPersist])
|
|
||||||
|
|
||||||
const createAccount = React.useCallback<ApiContext['createAccount']>(
|
const createAccount = React.useCallback<ApiContext['createAccount']>(
|
||||||
async ({
|
async ({
|
||||||
@@ -258,16 +292,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const account: SessionAccount = {
|
const account = agentToSessionAccount(agent)!
|
||||||
service: agent.service.toString(),
|
|
||||||
did: agent.session.did,
|
|
||||||
handle: agent.session.handle,
|
|
||||||
email: agent.session.email!, // TODO this is always defined?
|
|
||||||
emailConfirmed: false,
|
|
||||||
refreshJwt: agent.session.refreshJwt,
|
|
||||||
accessJwt: agent.session.accessJwt,
|
|
||||||
deactivated,
|
|
||||||
}
|
|
||||||
|
|
||||||
await configureModeration(agent, account)
|
await configureModeration(agent, account)
|
||||||
|
|
||||||
@@ -275,24 +300,21 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
createPersistSessionHandler(
|
createPersistSessionHandler(
|
||||||
account,
|
account,
|
||||||
({expired, refreshedAccount}) => {
|
({expired, refreshedAccount}) => {
|
||||||
if (expired) {
|
upsertAccount(refreshedAccount)
|
||||||
clearCurrentAccount()
|
if (expired) clearCurrentAccount()
|
||||||
} else {
|
|
||||||
setCurrentAccount(refreshedAccount)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{networkErrorCallback: clearCurrentAccount},
|
{networkErrorCallback: clearCurrentAccount},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
__globalAgent = agent
|
setAgent(agent)
|
||||||
setCurrentAccount(account)
|
upsertAccount(account)
|
||||||
|
|
||||||
logger.debug(`session: created account`, {}, logger.DebugContext.session)
|
logger.debug(`session: created account`, {}, logger.DebugContext.session)
|
||||||
track('Create Account')
|
track('Create Account')
|
||||||
logEvent('account:create:success', {})
|
logEvent('account:create:success', {})
|
||||||
},
|
},
|
||||||
[setCurrentAccount, clearCurrentAccount],
|
[upsertAccount, clearCurrentAccount],
|
||||||
)
|
)
|
||||||
|
|
||||||
const login = React.useCallback<ApiContext['login']>(
|
const login = React.useCallback<ApiContext['login']>(
|
||||||
@@ -300,68 +322,54 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
logger.debug(`session: login`, {}, logger.DebugContext.session)
|
logger.debug(`session: login`, {}, logger.DebugContext.session)
|
||||||
|
|
||||||
const agent = new BskyAgent({service})
|
const agent = new BskyAgent({service})
|
||||||
|
|
||||||
await agent.login({identifier, password})
|
await agent.login({identifier, password})
|
||||||
|
|
||||||
if (!agent.session) {
|
if (!agent.session) {
|
||||||
throw new Error(`session: login failed to establish a session`)
|
throw new Error(`session: login failed to establish a session`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const account: SessionAccount = {
|
const account = agentToSessionAccount(agent)!
|
||||||
service: agent.service.toString(),
|
|
||||||
did: agent.session.did,
|
|
||||||
handle: agent.session.handle,
|
|
||||||
email: agent.session.email,
|
|
||||||
emailConfirmed: agent.session.emailConfirmed || false,
|
|
||||||
refreshJwt: agent.session.refreshJwt,
|
|
||||||
accessJwt: agent.session.accessJwt,
|
|
||||||
deactivated: isSessionDeactivated(agent.session.accessJwt),
|
|
||||||
}
|
|
||||||
|
|
||||||
await configureModeration(agent, account)
|
await configureModeration(agent, account)
|
||||||
|
|
||||||
agent.setPersistSessionHandler(
|
agent.setPersistSessionHandler(
|
||||||
createPersistSessionHandler(
|
createPersistSessionHandler(
|
||||||
account,
|
account,
|
||||||
({expired, refreshedAccount}) => {
|
({expired, refreshedAccount}) => {
|
||||||
if (expired) {
|
upsertAccount(refreshedAccount)
|
||||||
clearCurrentAccount()
|
if (expired) clearCurrentAccount()
|
||||||
} else {
|
|
||||||
setCurrentAccount(refreshedAccount)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{networkErrorCallback: clearCurrentAccount},
|
{networkErrorCallback: clearCurrentAccount},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
__globalAgent = agent
|
setAgent(agent)
|
||||||
setCurrentAccount(account)
|
upsertAccount(account)
|
||||||
|
|
||||||
logger.debug(`session: logged in`, {}, logger.DebugContext.session)
|
logger.debug(`session: logged in`, {}, logger.DebugContext.session)
|
||||||
|
|
||||||
track('Sign In', {resumedSession: false})
|
track('Sign In', {resumedSession: false})
|
||||||
logEvent('account:loggedIn', {logContext, withPassword: true})
|
logEvent('account:loggedIn', {logContext, withPassword: true})
|
||||||
},
|
},
|
||||||
[setCurrentAccount, clearCurrentAccount],
|
[upsertAccount, clearCurrentAccount],
|
||||||
)
|
)
|
||||||
|
|
||||||
const logout = React.useCallback<ApiContext['logout']>(
|
const logout = React.useCallback<ApiContext['logout']>(
|
||||||
async logContext => {
|
async logContext => {
|
||||||
logger.debug(`session: logout`)
|
logger.debug(`session: logout`)
|
||||||
|
|
||||||
clearCurrentAccount()
|
clearCurrentAccount()
|
||||||
setStateAndPersist(s => {
|
persistNextUpdate()
|
||||||
return {
|
setAccounts(accounts =>
|
||||||
...s,
|
accounts.map(a => ({
|
||||||
accounts: s.accounts.map(a => ({
|
|
||||||
...a,
|
...a,
|
||||||
refreshJwt: undefined,
|
|
||||||
accessJwt: undefined,
|
accessJwt: undefined,
|
||||||
|
refreshJwt: undefined,
|
||||||
})),
|
})),
|
||||||
}
|
)
|
||||||
})
|
|
||||||
logEvent('account:loggedOut', {logContext})
|
logEvent('account:loggedOut', {logContext})
|
||||||
},
|
},
|
||||||
[clearCurrentAccount, setStateAndPersist],
|
[clearCurrentAccount, persistNextUpdate, setAccounts],
|
||||||
)
|
)
|
||||||
|
|
||||||
const initSession = React.useCallback<ApiContext['initSession']>(
|
const initSession = React.useCallback<ApiContext['initSession']>(
|
||||||
@@ -373,24 +381,17 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
persistSession: createPersistSessionHandler(
|
persistSession: createPersistSessionHandler(
|
||||||
account,
|
account,
|
||||||
({expired, refreshedAccount}) => {
|
({expired, refreshedAccount}) => {
|
||||||
if (expired) {
|
upsertAccount(refreshedAccount)
|
||||||
clearCurrentAccount()
|
if (expired) clearCurrentAccount()
|
||||||
} else {
|
|
||||||
__globalAgent = agent
|
|
||||||
setCurrentAccount(refreshedAccount)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{networkErrorCallback: clearCurrentAccount},
|
{networkErrorCallback: clearCurrentAccount},
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
const prevSession = {
|
const prevSession = {
|
||||||
|
...account,
|
||||||
accessJwt: account.accessJwt || '',
|
accessJwt: account.accessJwt || '',
|
||||||
refreshJwt: account.refreshJwt || '',
|
refreshJwt: account.refreshJwt || '',
|
||||||
did: account.did,
|
|
||||||
handle: account.handle,
|
|
||||||
deactivated:
|
|
||||||
isSessionDeactivated(account.accessJwt) || account.deactivated,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let canReusePrevSession = false
|
let canReusePrevSession = false
|
||||||
@@ -418,8 +419,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
logger.DebugContext.session,
|
logger.DebugContext.session,
|
||||||
)
|
)
|
||||||
agent.session = prevSession
|
agent.session = prevSession
|
||||||
__globalAgent = agent
|
setAgent(agent)
|
||||||
setCurrentAccount(account)
|
upsertAccount(account)
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`session: attempting to resumeSession using previous session`,
|
`session: attempting to resumeSession using previous session`,
|
||||||
@@ -429,13 +430,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
try {
|
try {
|
||||||
// will call `persistSession` on `BskyAgent` instance above if success
|
// will call `persistSession` on `BskyAgent` instance above if success
|
||||||
await networkRetry(1, () => agent.resumeSession(prevSession))
|
await networkRetry(1, () => agent.resumeSession(prevSession))
|
||||||
|
setAgent(agent)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`session: resumeSession failed`, {message: e})
|
logger.error(`session: resumeSession failed`, {message: e})
|
||||||
clearCurrentAccount()
|
clearCurrentAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setCurrentAccount, clearCurrentAccount],
|
[upsertAccount, clearCurrentAccount],
|
||||||
)
|
)
|
||||||
|
|
||||||
const resumeSession = React.useCallback<ApiContext['resumeSession']>(
|
const resumeSession = React.useCallback<ApiContext['resumeSession']>(
|
||||||
@@ -447,119 +449,92 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(`session: resumeSession failed`, {message: e})
|
logger.error(`session: resumeSession failed`, {message: e})
|
||||||
} finally {
|
} finally {
|
||||||
setState(s => ({
|
setIsInitialLoad(false)
|
||||||
...s,
|
|
||||||
isInitialLoad: false,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[initSession],
|
[initSession, setIsInitialLoad],
|
||||||
)
|
)
|
||||||
|
|
||||||
const removeAccount = React.useCallback<ApiContext['removeAccount']>(
|
const removeAccount = React.useCallback<ApiContext['removeAccount']>(
|
||||||
account => {
|
account => {
|
||||||
setStateAndPersist(s => {
|
persistNextUpdate()
|
||||||
return {
|
setAccounts(accounts => accounts.filter(a => a.did !== account.did))
|
||||||
...s,
|
|
||||||
accounts: s.accounts.filter(a => a.did !== account.did),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
[setStateAndPersist],
|
[setAccounts, persistNextUpdate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateCurrentAccount = React.useCallback<
|
const refreshSession = React.useCallback<
|
||||||
ApiContext['updateCurrentAccount']
|
ApiContext['refreshSession']
|
||||||
>(
|
>(async () => {
|
||||||
account => {
|
await agent.refreshSession()
|
||||||
setStateAndPersist(s => {
|
persistNextUpdate()
|
||||||
const currentAccount = s.currentAccount
|
upsertAccount(agentToSessionAccount(agent)!)
|
||||||
|
setAgent(agent.clone())
|
||||||
// ignore, should never happen
|
}, [agent, setAgent, persistNextUpdate, upsertAccount])
|
||||||
if (!currentAccount) return s
|
|
||||||
|
|
||||||
const updatedAccount = {
|
|
||||||
...currentAccount,
|
|
||||||
handle: account.handle || currentAccount.handle,
|
|
||||||
email: account.email || currentAccount.email,
|
|
||||||
emailConfirmed:
|
|
||||||
account.emailConfirmed !== undefined
|
|
||||||
? account.emailConfirmed
|
|
||||||
: currentAccount.emailConfirmed,
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...s,
|
|
||||||
currentAccount: updatedAccount,
|
|
||||||
accounts: [
|
|
||||||
updatedAccount,
|
|
||||||
...s.accounts.filter(a => a.did !== currentAccount.did),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[setStateAndPersist],
|
|
||||||
)
|
|
||||||
|
|
||||||
const selectAccount = React.useCallback<ApiContext['selectAccount']>(
|
const selectAccount = React.useCallback<ApiContext['selectAccount']>(
|
||||||
async (account, logContext) => {
|
async (account, logContext) => {
|
||||||
setState(s => ({...s, isSwitchingAccounts: true}))
|
setIsSwitchingAccounts(true)
|
||||||
try {
|
try {
|
||||||
await initSession(account)
|
await initSession(account)
|
||||||
setState(s => ({...s, isSwitchingAccounts: false}))
|
setIsSwitchingAccounts(false)
|
||||||
logEvent('account:loggedIn', {logContext, withPassword: false})
|
logEvent('account:loggedIn', {logContext, withPassword: false})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// reset this in case of error
|
// reset this in case of error
|
||||||
setState(s => ({...s, isSwitchingAccounts: false}))
|
setIsSwitchingAccounts(false)
|
||||||
// but other listeners need a throw
|
// but other listeners need a throw
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setState, initSession],
|
[setIsSwitchingAccounts, initSession],
|
||||||
)
|
)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isDirty.current) {
|
if (isDirty.current) {
|
||||||
isDirty.current = false
|
isDirty.current = false
|
||||||
persisted.write('session', {
|
persisted.write('session', {
|
||||||
accounts: state.accounts,
|
accounts,
|
||||||
currentAccount: state.currentAccount,
|
currentAccount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [state])
|
}, [accounts, currentAccount])
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
return persisted.onUpdate(async () => {
|
return persisted.onUpdate(async () => {
|
||||||
const session = persisted.get('session')
|
const persistedSession = persisted.get('session')
|
||||||
|
|
||||||
logger.debug(`session: persisted onUpdate`, {})
|
logger.debug(`session: persisted onUpdate`, {
|
||||||
|
persistedCurrentAccount: persistedSession.currentAccount,
|
||||||
|
currentAccount,
|
||||||
|
})
|
||||||
|
|
||||||
if (session.currentAccount && session.currentAccount.refreshJwt) {
|
setAccounts(persistedSession.accounts)
|
||||||
if (session.currentAccount?.did !== state.currentAccount?.did) {
|
|
||||||
|
if (
|
||||||
|
persistedSession.currentAccount &&
|
||||||
|
persistedSession.currentAccount.refreshJwt
|
||||||
|
) {
|
||||||
|
if (persistedSession.currentAccount?.did !== currentAccount?.did) {
|
||||||
logger.debug(`session: persisted onUpdate, switching accounts`, {
|
logger.debug(`session: persisted onUpdate, switching accounts`, {
|
||||||
from: {
|
from: {
|
||||||
did: state.currentAccount?.did,
|
did: currentAccount?.did,
|
||||||
handle: state.currentAccount?.handle,
|
handle: currentAccount?.handle,
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
did: session.currentAccount.did,
|
did: persistedSession.currentAccount.did,
|
||||||
handle: session.currentAccount.handle,
|
handle: persistedSession.currentAccount.handle,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await initSession(session.currentAccount)
|
await initSession(persistedSession.currentAccount)
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`session: persisted onUpdate, updating session`, {})
|
logger.debug(`session: persisted onUpdate, updating session`, {})
|
||||||
|
agent.session = sessionAccountToAgentSession(
|
||||||
/*
|
persistedSession.currentAccount,
|
||||||
* Use updated session in this tab's agent. Do not call
|
)
|
||||||
* setCurrentAccount, since that will only persist the session that's
|
setAgent(agent.clone())
|
||||||
* already persisted, and we'll get a loop between tabs.
|
|
||||||
*/
|
|
||||||
// @ts-ignore we checked for `refreshJwt` above
|
|
||||||
__globalAgent.session = session.currentAccount
|
|
||||||
}
|
}
|
||||||
} else if (!session.currentAccount && state.currentAccount) {
|
} else if (!persistedSession.currentAccount && currentAccount) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`session: persisted onUpdate, logging out`,
|
`session: persisted onUpdate, logging out`,
|
||||||
{},
|
{},
|
||||||
@@ -574,20 +549,26 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
*/
|
*/
|
||||||
clearCurrentAccount()
|
clearCurrentAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(s => ({
|
|
||||||
...s,
|
|
||||||
accounts: session.accounts,
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}, [state, setState, clearCurrentAccount, initSession])
|
}, [
|
||||||
|
currentAccount,
|
||||||
|
setAccounts,
|
||||||
|
clearCurrentAccount,
|
||||||
|
initSession,
|
||||||
|
agent,
|
||||||
|
setAgent,
|
||||||
|
])
|
||||||
|
|
||||||
const stateContext = React.useMemo(
|
const stateContext = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...state,
|
agent,
|
||||||
hasSession: !!state.currentAccount,
|
isInitialLoad,
|
||||||
|
isSwitchingAccounts,
|
||||||
|
currentAccount,
|
||||||
|
accounts,
|
||||||
|
hasSession: Boolean(currentAccount),
|
||||||
}),
|
}),
|
||||||
[state],
|
[agent, isInitialLoad, isSwitchingAccounts, accounts, currentAccount],
|
||||||
)
|
)
|
||||||
|
|
||||||
const api = React.useMemo(
|
const api = React.useMemo(
|
||||||
@@ -599,7 +580,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
resumeSession,
|
resumeSession,
|
||||||
removeAccount,
|
removeAccount,
|
||||||
selectAccount,
|
selectAccount,
|
||||||
updateCurrentAccount,
|
refreshSession,
|
||||||
clearCurrentAccount,
|
clearCurrentAccount,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
@@ -610,11 +591,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
resumeSession,
|
resumeSession,
|
||||||
removeAccount,
|
removeAccount,
|
||||||
selectAccount,
|
selectAccount,
|
||||||
updateCurrentAccount,
|
refreshSession,
|
||||||
clearCurrentAccount,
|
clearCurrentAccount,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// as we migrate, continue to keep this updated
|
||||||
|
__globalAgent = agent
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StateContext.Provider value={stateContext}>
|
<StateContext.Provider value={stateContext}>
|
||||||
<ApiContext.Provider value={api}>{children}</ApiContext.Provider>
|
<ApiContext.Provider value={api}>{children}</ApiContext.Provider>
|
||||||
|
|||||||
Reference in New Issue
Block a user