Move global "Sign out" out of the current account row (#4941)
* Rename logout to logoutEveryAccount * Add logoutCurrentAccount() * Make all "Log out" buttons refer to current account Each of these usages is completely contextual and refers to a specific account. * Add Sign out of all accounts to Settings * Move single account Sign Out below as well * Prompt on account removal * Add Other Accounts header to reduce ambiguity * Spacing fix --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com>
This commit is contained in:
parent
f3b57dd456
commit
b6e515c664
11 changed files with 247 additions and 77 deletions
|
@ -76,7 +76,7 @@ describe('session', () => {
|
|||
|
||||
state = run(state, [
|
||||
{
|
||||
type: 'logged-out',
|
||||
type: 'logged-out-every-account',
|
||||
},
|
||||
])
|
||||
// Should keep the account but clear out the tokens.
|
||||
|
@ -372,7 +372,7 @@ describe('session', () => {
|
|||
state = run(state, [
|
||||
{
|
||||
// Log everyone out.
|
||||
type: 'logged-out',
|
||||
type: 'logged-out-every-account',
|
||||
},
|
||||
])
|
||||
expect(state.accounts.length).toBe(3)
|
||||
|
@ -466,7 +466,7 @@ describe('session', () => {
|
|||
|
||||
state = run(state, [
|
||||
{
|
||||
type: 'logged-out',
|
||||
type: 'logged-out-every-account',
|
||||
},
|
||||
])
|
||||
expect(state.accounts.length).toBe(1)
|
||||
|
@ -674,6 +674,103 @@ describe('session', () => {
|
|||
expect(state.currentAgentState.did).toBe(undefined)
|
||||
})
|
||||
|
||||
it('can log out of the current account', () => {
|
||||
let state = getInitialState([])
|
||||
|
||||
const agent1 = new BskyAgent({service: 'https://alice.com'})
|
||||
agent1.sessionManager.session = {
|
||||
active: true,
|
||||
did: 'alice-did',
|
||||
handle: 'alice.test',
|
||||
accessJwt: 'alice-access-jwt-1',
|
||||
refreshJwt: 'alice-refresh-jwt-1',
|
||||
}
|
||||
state = run(state, [
|
||||
{
|
||||
type: 'switched-to-account',
|
||||
newAgent: agent1,
|
||||
newAccount: agentToSessionAccountOrThrow(agent1),
|
||||
},
|
||||
])
|
||||
expect(state.accounts.length).toBe(1)
|
||||
expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
|
||||
expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1')
|
||||
expect(state.currentAgentState.did).toBe('alice-did')
|
||||
|
||||
const agent2 = new BskyAgent({service: 'https://bob.com'})
|
||||
agent2.sessionManager.session = {
|
||||
active: true,
|
||||
did: 'bob-did',
|
||||
handle: 'bob.test',
|
||||
accessJwt: 'bob-access-jwt-1',
|
||||
refreshJwt: 'bob-refresh-jwt-1',
|
||||
}
|
||||
state = run(state, [
|
||||
{
|
||||
type: 'switched-to-account',
|
||||
newAgent: agent2,
|
||||
newAccount: agentToSessionAccountOrThrow(agent2),
|
||||
},
|
||||
])
|
||||
expect(state.accounts.length).toBe(2)
|
||||
expect(state.accounts[0].accessJwt).toBe('bob-access-jwt-1')
|
||||
expect(state.accounts[0].refreshJwt).toBe('bob-refresh-jwt-1')
|
||||
expect(state.currentAgentState.did).toBe('bob-did')
|
||||
|
||||
state = run(state, [
|
||||
{
|
||||
type: 'logged-out-current-account',
|
||||
},
|
||||
])
|
||||
expect(state.accounts.length).toBe(2)
|
||||
expect(state.accounts[0].accessJwt).toBe(undefined)
|
||||
expect(state.accounts[0].refreshJwt).toBe(undefined)
|
||||
expect(state.accounts[1].accessJwt).toBe('alice-access-jwt-1')
|
||||
expect(state.accounts[1].refreshJwt).toBe('alice-refresh-jwt-1')
|
||||
expect(state.currentAgentState.did).toBe(undefined)
|
||||
expect(printState(state)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"accounts": [
|
||||
{
|
||||
"accessJwt": undefined,
|
||||
"active": true,
|
||||
"did": "bob-did",
|
||||
"email": undefined,
|
||||
"emailAuthFactor": false,
|
||||
"emailConfirmed": false,
|
||||
"handle": "bob.test",
|
||||
"pdsUrl": undefined,
|
||||
"refreshJwt": undefined,
|
||||
"service": "https://bob.com/",
|
||||
"signupQueued": false,
|
||||
"status": undefined,
|
||||
},
|
||||
{
|
||||
"accessJwt": "alice-access-jwt-1",
|
||||
"active": true,
|
||||
"did": "alice-did",
|
||||
"email": undefined,
|
||||
"emailAuthFactor": false,
|
||||
"emailConfirmed": false,
|
||||
"handle": "alice.test",
|
||||
"pdsUrl": undefined,
|
||||
"refreshJwt": "alice-refresh-jwt-1",
|
||||
"service": "https://alice.com/",
|
||||
"signupQueued": false,
|
||||
"status": undefined,
|
||||
},
|
||||
],
|
||||
"currentAgentState": {
|
||||
"agent": {
|
||||
"service": "https://public.api.bsky.app/",
|
||||
},
|
||||
"did": undefined,
|
||||
},
|
||||
"needsPersist": true,
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
it('updates stored account with refreshed tokens', () => {
|
||||
let state = getInitialState([])
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ const AgentContext = React.createContext<BskyAgent | null>(null)
|
|||
const ApiContext = React.createContext<SessionApiContext>({
|
||||
createAccount: async () => {},
|
||||
login: async () => {},
|
||||
logout: async () => {},
|
||||
logoutCurrentAccount: async () => {},
|
||||
logoutEveryAccount: async () => {},
|
||||
resumeSession: async () => {},
|
||||
removeAccount: () => {},
|
||||
})
|
||||
|
@ -115,14 +116,31 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||
[onAgentSessionChange, cancelPendingTask],
|
||||
)
|
||||
|
||||
const logout = React.useCallback<SessionApiContext['logout']>(
|
||||
const logoutCurrentAccount = React.useCallback<
|
||||
SessionApiContext['logoutEveryAccount']
|
||||
>(
|
||||
logContext => {
|
||||
addSessionDebugLog({type: 'method:start', method: 'logout'})
|
||||
cancelPendingTask()
|
||||
dispatch({
|
||||
type: 'logged-out',
|
||||
type: 'logged-out-current-account',
|
||||
})
|
||||
logEvent('account:loggedOut', {logContext})
|
||||
logEvent('account:loggedOut', {logContext, scope: 'current'})
|
||||
addSessionDebugLog({type: 'method:end', method: 'logout'})
|
||||
},
|
||||
[cancelPendingTask],
|
||||
)
|
||||
|
||||
const logoutEveryAccount = React.useCallback<
|
||||
SessionApiContext['logoutEveryAccount']
|
||||
>(
|
||||
logContext => {
|
||||
addSessionDebugLog({type: 'method:start', method: 'logout'})
|
||||
cancelPendingTask()
|
||||
dispatch({
|
||||
type: 'logged-out-every-account',
|
||||
})
|
||||
logEvent('account:loggedOut', {logContext, scope: 'every'})
|
||||
addSessionDebugLog({type: 'method:end', method: 'logout'})
|
||||
},
|
||||
[cancelPendingTask],
|
||||
|
@ -230,11 +248,19 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||
() => ({
|
||||
createAccount,
|
||||
login,
|
||||
logout,
|
||||
logoutCurrentAccount,
|
||||
logoutEveryAccount,
|
||||
resumeSession,
|
||||
removeAccount,
|
||||
}),
|
||||
[createAccount, login, logout, resumeSession, removeAccount],
|
||||
[
|
||||
createAccount,
|
||||
login,
|
||||
logoutCurrentAccount,
|
||||
logoutEveryAccount,
|
||||
resumeSession,
|
||||
removeAccount,
|
||||
],
|
||||
)
|
||||
|
||||
// @ts-ignore
|
||||
|
|
|
@ -42,7 +42,10 @@ export type Action =
|
|||
accountDid: string
|
||||
}
|
||||
| {
|
||||
type: 'logged-out'
|
||||
type: 'logged-out-current-account'
|
||||
}
|
||||
| {
|
||||
type: 'logged-out-every-account'
|
||||
}
|
||||
| {
|
||||
type: 'synced-accounts'
|
||||
|
@ -138,7 +141,23 @@ let reducer = (state: State, action: Action): State => {
|
|||
needsPersist: true,
|
||||
}
|
||||
}
|
||||
case 'logged-out': {
|
||||
case 'logged-out-current-account': {
|
||||
const {currentAgentState} = state
|
||||
return {
|
||||
accounts: state.accounts.map(a =>
|
||||
a.did === currentAgentState.did
|
||||
? {
|
||||
...a,
|
||||
refreshJwt: undefined,
|
||||
accessJwt: undefined,
|
||||
}
|
||||
: a,
|
||||
),
|
||||
currentAgentState: createPublicAgentState(),
|
||||
needsPersist: true,
|
||||
}
|
||||
}
|
||||
case 'logged-out-every-account': {
|
||||
return {
|
||||
accounts: state.accounts.map(a => ({
|
||||
...a,
|
||||
|
|
|
@ -29,12 +29,12 @@ export type SessionApiContext = {
|
|||
},
|
||||
logContext: LogEvents['account:loggedIn']['logContext'],
|
||||
) => Promise<void>
|
||||
/**
|
||||
* A full logout. Clears the `currentAccount` from session, AND removes
|
||||
* access tokens from all accounts, so that returning as any user will
|
||||
* require a full login.
|
||||
*/
|
||||
logout: (logContext: LogEvents['account:loggedOut']['logContext']) => void
|
||||
logoutCurrentAccount: (
|
||||
logContext: LogEvents['account:loggedOut']['logContext'],
|
||||
) => void
|
||||
logoutEveryAccount: (
|
||||
logContext: LogEvents['account:loggedOut']['logContext'],
|
||||
) => void
|
||||
resumeSession: (account: SessionAccount) => Promise<void>
|
||||
removeAccount: (account: SessionAccount) => void
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue