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