route appview state and facet resolution off the pds client
Audit of usePdsClient call sites: the PDS client is for com.atproto.* account/repo operations. Appview-private state (actor/list/thread mutes, notification seen-state) and mention/facet resolution now use the appview-routed client. useRichText also regains logged-out mention resolution via useLexClient's public fallback (usePdsClient throws when logged out, silently leaving mentions unresolved on StarterPackLanding and web ProfileHoverCard). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
useListCreateMutation,
|
||||
useListMetadataMutation,
|
||||
} from '#/state/queries/list'
|
||||
import {usePdsClient} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||
import {EditableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
@@ -134,7 +134,7 @@ function DialogInner({
|
||||
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
const control = Dialog.useDialogContext()
|
||||
const {
|
||||
mutateAsync: createListMutation,
|
||||
@@ -228,7 +228,7 @@ function DialogInner({
|
||||
{cleanNewlines: true},
|
||||
)
|
||||
|
||||
await richText.detectFacets(pdsClient)
|
||||
await richText.detectFacets(appviewClient)
|
||||
richText = shortenLinks(richText)
|
||||
richText = stripInvalidMentions(richText)
|
||||
|
||||
@@ -276,7 +276,7 @@ function DialogInner({
|
||||
setImageError,
|
||||
activePurpose,
|
||||
isCurateList,
|
||||
pdsClient,
|
||||
appviewClient,
|
||||
_,
|
||||
])
|
||||
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {usePdsClient} from '#/state/session'
|
||||
import {useLexClient} from '#/state/session'
|
||||
|
||||
export function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
const [prevText, setPrevText] = useState(text)
|
||||
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
|
||||
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
|
||||
/*
|
||||
* Facet detection resolves handles via `com.atproto.identity.resolveHandle`,
|
||||
* which the account (PDS) client serves. We standardize on the account client
|
||||
* where a session is in scope (design section B). Logged out, this is the
|
||||
* throwing client; `detectFacets` will reject, and the raw (unresolved)
|
||||
* RichText is returned in the meantime.
|
||||
* Facet/mention resolution is an appview job - it resolves handles via
|
||||
* `com.atproto.identity.resolveHandle` through the appview. `useLexClient`
|
||||
* falls back to the public client when logged out, so mentions still resolve
|
||||
* on logged-out surfaces (StarterPackLandingScreen, web ProfileHoverCard).
|
||||
*/
|
||||
const client = usePdsClient()
|
||||
const client = useLexClient()
|
||||
if (text !== prevText) {
|
||||
setPrevText(text)
|
||||
setRawRT(new RichTextAPI({text}))
|
||||
|
||||
@@ -45,7 +45,7 @@ import {type ConvoState, ConvoStatus} from '#/state/messages/convo/types'
|
||||
import {useGetJoinLinkPreview} from '#/state/queries/join-links'
|
||||
import {useGetPost} from '#/state/queries/post'
|
||||
import {createEmbedViewRecordFromPost} from '#/state/queries/postgate/util'
|
||||
import {usePdsClient, useSession} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import {List, type ListMethods} from '#/view/com/util/List'
|
||||
import {MessageComposer} from '#/screens/Messages/components/MessageComposer'
|
||||
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
||||
@@ -141,10 +141,9 @@ export function MessagesList({
|
||||
const convoState = useConvoActive()
|
||||
/*
|
||||
* Facet detection resolves handles via `com.atproto.identity.resolveHandle`,
|
||||
* which the account (PDS) client serves - chat requires a session, so the
|
||||
* client is always live here.
|
||||
* which the appview client serves (design section B).
|
||||
*/
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
const getPost = useGetPost()
|
||||
const getJoinLinkPreview = useGetJoinLinkPreview()
|
||||
@@ -618,7 +617,7 @@ export function MessagesList({
|
||||
replyTo = {messageId: reply.id}
|
||||
}
|
||||
|
||||
await rt.detectFacets(pdsClient)
|
||||
await rt.detectFacets(appviewClient)
|
||||
|
||||
rt = shortenLinks(rt)
|
||||
rt = stripInvalidMentions(rt)
|
||||
@@ -671,7 +670,7 @@ export function MessagesList({
|
||||
}
|
||||
},
|
||||
[
|
||||
pdsClient,
|
||||
appviewClient,
|
||||
convoState,
|
||||
getPost,
|
||||
getJoinLinkPreview,
|
||||
|
||||
Vendored
+4
-4
@@ -9,7 +9,7 @@ import {type AtUriString} from '@atproto/syntax'
|
||||
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {app} from '#/lexicons'
|
||||
import {usePdsClient, useSession} from '../session'
|
||||
import {useAppviewClient, useSession} from '../session'
|
||||
|
||||
type StateContext = Map<string, boolean>
|
||||
type SetStateContext = (uri: string, value: boolean) => void
|
||||
@@ -58,7 +58,7 @@ export function useSetThreadMute() {
|
||||
}
|
||||
|
||||
function useMigrateMutes(setThreadMute: SetStateContext) {
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
useEffect(() => {
|
||||
@@ -89,7 +89,7 @@ function useMigrateMutes(setThreadMute: SetStateContext) {
|
||||
|
||||
setThreadMute(root, true)
|
||||
|
||||
await pdsClient
|
||||
await appviewClient
|
||||
.call(app.bsky.graph.muteThread, {root: root as AtUriString})
|
||||
// not a big deal if this fails, since the post might have been deleted
|
||||
.catch(console.error)
|
||||
@@ -102,5 +102,5 @@ function useMigrateMutes(setThreadMute: SetStateContext) {
|
||||
cancelled = true
|
||||
}
|
||||
}
|
||||
}, [pdsClient, currentAccount, setThreadMute])
|
||||
}, [appviewClient, currentAccount, setThreadMute])
|
||||
}
|
||||
|
||||
@@ -253,13 +253,12 @@ export function useListDeleteMutation() {
|
||||
export function useListMuteMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
const pdsClient = usePdsClient()
|
||||
return useMutation<void, Error, {uri: string; mute: boolean}>({
|
||||
mutationFn: async ({uri, mute}) => {
|
||||
if (mute) {
|
||||
await pdsClient.call(muteActorList, {list: uri as AtUriString})
|
||||
await appviewClient.call(muteActorList, {list: uri as AtUriString})
|
||||
} else {
|
||||
await pdsClient.call(unmuteActorList, {list: uri as AtUriString})
|
||||
await appviewClient.call(unmuteActorList, {list: uri as AtUriString})
|
||||
}
|
||||
|
||||
await whenAppViewReady(appviewClient, uri, v => {
|
||||
|
||||
@@ -20,7 +20,7 @@ import BroadcastChannel from '#/lib/broadcast'
|
||||
import {resetBadgeCount} from '#/lib/notifications/notifications'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useAppviewClient, usePdsClient, useSession} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import {RQKEY as RQKEY_NOTIFS} from './feed'
|
||||
import {type CachedFeedPage, type FeedPage} from './types'
|
||||
import {fetchPage} from './util'
|
||||
@@ -55,7 +55,6 @@ apiContext.displayName = 'NotificationsUnreadApiContext'
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const {hasSession} = useSession()
|
||||
const appviewClient = useAppviewClient()
|
||||
const pdsClient = usePdsClient()
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
|
||||
@@ -123,7 +122,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
return {
|
||||
async markAllRead() {
|
||||
// update server
|
||||
await pdsClient.call(
|
||||
await appviewClient.call(
|
||||
updateSeenNotifications,
|
||||
// toISOString() always yields a valid datetime string
|
||||
cacheRef.current.syncedAt.toISOString() as DatetimeString,
|
||||
@@ -212,14 +211,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
},
|
||||
}
|
||||
}, [
|
||||
setNumUnread,
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
appviewClient,
|
||||
pdsClient,
|
||||
hasSession,
|
||||
])
|
||||
}, [setNumUnread, queryClient, moderationOpts, appviewClient, hasSession])
|
||||
checkUnreadRef.current = api.checkUnread
|
||||
|
||||
return (
|
||||
|
||||
@@ -414,14 +414,14 @@ export function useThreadMuteMutationQueue(
|
||||
}
|
||||
|
||||
function useThreadMuteMutation() {
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
return useMutation<
|
||||
{},
|
||||
Error,
|
||||
{uri: string} // the root post's uri
|
||||
>({
|
||||
mutationFn: async ({uri}) => {
|
||||
await pdsClient.call(app.bsky.graph.muteThread, {
|
||||
await appviewClient.call(app.bsky.graph.muteThread, {
|
||||
root: uri as AtUriString,
|
||||
})
|
||||
return {}
|
||||
@@ -430,10 +430,10 @@ function useThreadMuteMutation() {
|
||||
}
|
||||
|
||||
function useThreadUnmuteMutation() {
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
return useMutation<{}, Error, {uri: string}>({
|
||||
mutationFn: async ({uri}) => {
|
||||
await pdsClient.call(app.bsky.graph.unmuteThread, {
|
||||
await appviewClient.call(app.bsky.graph.unmuteThread, {
|
||||
root: uri as AtUriString,
|
||||
})
|
||||
return {}
|
||||
|
||||
@@ -474,10 +474,10 @@ export function useProfileMuteMutationQueue(
|
||||
|
||||
function useProfileMuteMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
return useMutation<void, Error, {did: string}>({
|
||||
mutationFn: async ({did}) => {
|
||||
await pdsClient.call(muteActor, {actor: did as AtIdentifierString})
|
||||
await appviewClient.call(muteActor, {actor: did as AtIdentifierString})
|
||||
},
|
||||
onSuccess() {
|
||||
void queryClient.invalidateQueries({queryKey: RQKEY_MY_MUTED()})
|
||||
@@ -487,10 +487,10 @@ function useProfileMuteMutation() {
|
||||
|
||||
function useProfileUnmuteMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
return useMutation<void, Error, {did: string}>({
|
||||
mutationFn: async ({did}) => {
|
||||
await pdsClient.call(unmuteActor, {actor: did as AtIdentifierString})
|
||||
await appviewClient.call(unmuteActor, {actor: did as AtIdentifierString})
|
||||
},
|
||||
onSuccess() {
|
||||
void queryClient.invalidateQueries({queryKey: RQKEY_MY_MUTED()})
|
||||
|
||||
@@ -110,7 +110,7 @@ export function useCreateStarterPackMutation({
|
||||
let descriptionFacets: app.bsky.richtext.facet.Main[] | undefined
|
||||
if (description) {
|
||||
const rt = new RichText({text: description})
|
||||
await rt.detectFacets(pdsClient)
|
||||
await rt.detectFacets(appviewClient)
|
||||
descriptionFacets = rt.facets
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ export function useEditStarterPackMutation({
|
||||
let descriptionFacets: app.bsky.richtext.facet.Main[] | undefined
|
||||
if (description) {
|
||||
const rt = new RichText({text: description})
|
||||
await rt.detectFacets(pdsClient)
|
||||
await rt.detectFacets(appviewClient)
|
||||
descriptionFacets = rt.facets
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import {useLabelerInfoQuery} from '#/state/queries/labeler'
|
||||
import {resetProfilePostsQueries} from '#/state/queries/post-feed'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||
import {usePdsClient, useSession} from '#/state/session'
|
||||
import {useLexClient, useSession} from '#/state/session'
|
||||
import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens'
|
||||
import {ProfileLists} from '#/view/com/lists/ProfileLists'
|
||||
import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
|
||||
@@ -610,7 +610,7 @@ function ProfileScreenLoaded({
|
||||
}
|
||||
|
||||
function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
const client = usePdsClient()
|
||||
const client = useLexClient()
|
||||
const [prevText, setPrevText] = useState(text)
|
||||
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
|
||||
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
|
||||
|
||||
Reference in New Issue
Block a user