Cleanup, feedback

This commit is contained in:
Eric Bailey
2024-04-11 16:25:00 -05:00
parent fd085fd437
commit 9721bbaeb3
5 changed files with 92 additions and 70 deletions
+1
View File
@@ -4,6 +4,7 @@ export const LOCAL_DEV_SERVICE =
Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583'
export const STAGING_SERVICE = 'https://staging.bsky.dev'
export const BSKY_SERVICE = 'https://bsky.social'
export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'
export const DEFAULT_SERVICE = BSKY_SERVICE
const HELP_DESK_LANG = 'en-us'
export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
+1 -5
View File
@@ -6,11 +6,7 @@ import {migrate} from '#/state/persisted/legacy'
import {defaults, Schema} from '#/state/persisted/schema'
import * as store from '#/state/persisted/store'
export type {
PersistedAccount,
PersistedCurrentAccount,
Schema,
} from '#/state/persisted/schema'
export type {PersistedAccount, Schema} from '#/state/persisted/schema'
export {defaults} from '#/state/persisted/schema'
const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL')
+16 -5
View File
@@ -4,7 +4,10 @@ import {deviceLocales} from '#/platform/detection'
const externalEmbedOptions = ['show', 'hide'] as const
// only data needed for rendering account page
/**
* A account persisted to storage. Stored in the `accounts[]` array. Contains
* base account info and access tokens.
*/
const accountSchema = z.object({
service: z.string(),
did: z.string(),
@@ -17,17 +20,25 @@ const accountSchema = z.object({
})
export type PersistedAccount = z.infer<typeof accountSchema>
const currentAccountSchema = z.object({
did: z.string(),
/**
* The current account. Stored in the `currentAccount` field.
*
* In previous versions, this included tokens and other info. Now, it's used
* only to reference the `did` field, and all other fields are marked as
* optional. They should be considered deprecated and not used, but are kept
* here for backwards compat.
*/
const currentAccountScheme = accountSchema.extend({
service: z.string().optional(),
handle: z.string().optional(),
})
export type PersistedCurrentAccount = z.infer<typeof currentAccountSchema>
export const schema = z.object({
colorMode: z.enum(['system', 'light', 'dark']),
darkTheme: z.enum(['dim', 'dark']).optional(),
session: z.object({
accounts: z.array(accountSchema),
currentAccount: currentAccountSchema.optional(),
currentAccount: currentAccountScheme.optional(),
}),
reminders: z.object({
lastEmailConfirm: z.string().optional(),
+3 -1
View File
@@ -1,7 +1,9 @@
import {BskyAgent} from '@atproto/api'
import {PUBLIC_BSKY_SERVICE} from '#/lib/constants'
export const PUBLIC_BSKY_AGENT = new BskyAgent({
service: 'https://public.api.bsky.app',
service: PUBLIC_BSKY_SERVICE,
})
export const STALE = {
+71 -59
View File
@@ -5,12 +5,12 @@ import {jwtDecode} from 'jwt-decode'
import {track} from '#/lib/analytics/analytics'
import {networkRetry} from '#/lib/async/retry'
import {IS_TEST_USER} from '#/lib/constants'
import {PUBLIC_BSKY_SERVICE} from '#/lib/constants'
import {logEvent, LogEvents} from '#/lib/statsig/statsig'
import {hasProp} from '#/lib/type-guards'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import * as persisted from '#/state/persisted'
import {PUBLIC_BSKY_AGENT} from '#/state/queries'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util'
import * as Toast from '#/view/com/util/Toast'
@@ -18,10 +18,16 @@ import {IS_DEV} from '#/env'
import {emitSessionDropped} from '../events'
import {readLabelers} from './agent-config'
/**
* Only used for the initial agent values in state and context. Replaced
* immediately, and should not be reused.
*/
const INITIAL_AGENT = new BskyAgent({service: PUBLIC_BSKY_SERVICE})
/**
* @deprecated use `agent` from `useSession` instead
*/
let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
let __globalAgent: BskyAgent = INITIAL_AGENT
/**
* NOTE
@@ -36,6 +42,7 @@ export function getAgent() {
}
export type SessionAccount = persisted.PersistedAccount
export type CurrentAccount = Omit<SessionAccount, 'accessJwt' | 'refreshJwt'>
export type StateContext = {
currentAgent: BskyAgent
@@ -44,10 +51,10 @@ export type StateContext = {
hasSession: boolean
accounts: SessionAccount[]
/**
* This value is derived from `BskyAgent.session` and should contain the full
* account object persisted to storage, minus the access tokens.
* Contains the full account object persisted to storage, minus access
* tokens.
*/
currentAccount: Omit<SessionAccount, 'accessJwt' | 'refreshJwt'> | undefined
currentAccount: CurrentAccount | undefined
}
export type ApiContext = {
@@ -98,7 +105,7 @@ export type ApiContext = {
}
const StateContext = React.createContext<StateContext>({
currentAgent: PUBLIC_BSKY_AGENT,
currentAgent: INITIAL_AGENT,
isInitialLoad: true,
isSwitchingAccounts: false,
accounts: [],
@@ -133,15 +140,6 @@ function agentToSessionAccount(agent: BskyAgent): SessionAccount | undefined {
}
}
function agentToCurrentAccount(
agent: BskyAgent,
): StateContext['currentAccount'] {
const sessionAccount = agentToSessionAccount(agent)
delete sessionAccount?.accessJwt
delete sessionAccount?.refreshJwt
return sessionAccount
}
function sessionAccountToAgentSession(
account: SessionAccount,
): BskyAgent['session'] {
@@ -158,16 +156,20 @@ function sessionAccountToAgentSession(
export function Provider({children}: React.PropsWithChildren<{}>) {
const isDirty = React.useRef(false)
const [currentAgent, setCurrentAgent] =
React.useState<BskyAgent>(PUBLIC_BSKY_AGENT)
React.useState<BskyAgent>(INITIAL_AGENT)
const [accounts, setAccounts] = React.useState<SessionAccount[]>(
persisted.get('session').accounts,
)
const [isInitialLoad, setIsInitialLoad] = React.useState(true)
const [isSwitchingAccounts, setIsSwitchingAccounts] = React.useState(false)
const currentAccount = React.useMemo(
() => agentToCurrentAccount(currentAgent),
const currentAccountDid = React.useMemo(
() => currentAgent.session?.did,
[currentAgent],
)
const currentAccount = React.useMemo(
() => accounts.find(a => a.did === currentAccountDid),
[accounts, currentAccountDid],
)
const persistNextUpdate = React.useCallback(
() => (isDirty.current = true),
@@ -187,10 +189,16 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
const clearCurrentAccount = React.useCallback(() => {
logger.warn(`session: clear current account`)
// immediate clear this so any pending requests don't use it
currentAgent.setPersistSessionHandler(() => {})
persistNextUpdate()
setCurrentAgent(PUBLIC_BSKY_AGENT)
BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
}, [persistNextUpdate, setCurrentAgent])
const newAgent = new BskyAgent({service: PUBLIC_BSKY_SERVICE})
setCurrentAgent(newAgent)
configureModeration(newAgent)
}, [currentAgent, persistNextUpdate, setCurrentAgent])
React.useMemo(() => {
currentAgent.setPersistSessionHandler(event => {
@@ -436,17 +444,20 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
>(async () => {
const {accounts: persistedAccounts} = persisted.get('session')
const selectedAccount = persistedAccounts.find(
a => a.did === currentAccount?.did,
a => a.did === currentAccountDid,
)
if (!selectedAccount) return
await currentAgent.resumeSession(
sessionAccountToAgentSession(selectedAccount)!,
)
// update and swap agent to trigger render refresh
const newAgent = currentAgent.clone()
await newAgent.resumeSession(sessionAccountToAgentSession(selectedAccount)!)
const refreshedAccount = agentToSessionAccount(newAgent)
persistNextUpdate()
upsertAndPersistAccount(agentToSessionAccount(currentAgent)!)
setCurrentAgent(currentAgent.clone())
upsertAndPersistAccount(refreshedAccount!)
setCurrentAgent(newAgent)
configureModeration(newAgent, refreshedAccount)
}, [
currentAccount,
currentAccountDid,
currentAgent,
setCurrentAgent,
persistNextUpdate,
@@ -475,11 +486,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
isDirty.current = false
persisted.write('session', {
accounts,
currentAccount: currentAccount
? {
did: currentAccount.did,
}
: undefined,
currentAccount,
})
}
}, [accounts, currentAccount])
@@ -502,17 +509,15 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
if (selectedAccount && selectedAccount.refreshJwt) {
if (selectedAccount?.did !== currentAccount?.did) {
if (selectedAccount?.did !== currentAccountDid) {
logger.debug(
`session: persisted onUpdate, switching accounts`,
{
from: {
did: currentAccount?.did,
handle: currentAccount?.handle,
did: currentAccountDid,
},
to: {
did: selectedAccount.did,
handle: selectedAccount.handle,
},
},
logger.DebugContext.session,
@@ -526,12 +531,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
logger.DebugContext.session,
)
// console.log('UPDATE', { refreshJwt: selectedAccount.refreshJwt.slice(-10) })
// updates silently, all subsequent calls will use the new session
currentAgent.session = sessionAccountToAgentSession(selectedAccount)
// replace agent to re-derive currentAccount and trigger rerender with fresh data
setCurrentAgent(currentAgent.clone())
const newAgent = currentAgent.clone()
newAgent.session = sessionAccountToAgentSession(selectedAccount)
configureModeration(newAgent, selectedAccount)
setCurrentAgent(newAgent)
}
} else if (!selectedAccount && currentAccount) {
} else if (!selectedAccount && currentAccountDid) {
logger.debug(
`session: persisted onUpdate, logging out`,
{},
@@ -548,7 +553,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
}
})
}, [
currentAccount,
currentAccountDid,
setAccounts,
clearCurrentAccount,
initSession,
@@ -614,25 +619,32 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
}
async function configureModeration(agent: BskyAgent, account: SessionAccount) {
if (IS_TEST_USER(account.handle)) {
const did = (
await agent
.resolveHandle({handle: 'mod-authority.test'})
.catch(_ => undefined)
)?.data.did
if (did) {
console.warn('USING TEST ENV MODERATION')
BskyAgent.configure({appLabelers: [did]})
async function configureModeration(agent: BskyAgent, account?: SessionAccount) {
if (account) {
if (IS_TEST_USER(account.handle)) {
const did = (
await agent
.resolveHandle({handle: 'mod-authority.test'})
.catch(_ => undefined)
)?.data.did
if (did) {
console.warn('USING TEST ENV MODERATION')
BskyAgent.configure({appLabelers: [did]})
}
} else {
BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
if (account) {
const labelerDids = await readLabelers(account.did).catch(_ => {})
if (labelerDids) {
agent.configureLabelersHeader(
labelerDids.filter(did => did !== BSKY_LABELER_DID),
)
}
}
}
} else {
BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
const labelerDids = await readLabelers(account.did).catch(_ => {})
if (labelerDids) {
agent.configureLabelersHeader(
labelerDids.filter(did => did !== BSKY_LABELER_DID),
)
}
}
}