[SDK] Delete the bridge agent and rework the session bundle onto lex clients (#11385)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+17
-13
@@ -1,10 +1,10 @@
|
||||
import {useCallback} from 'react'
|
||||
import {type HandleString} from '@atproto/syntax'
|
||||
import {type DidString, type HandleString} from '@atproto/syntax'
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useAgent, usePdsClient} from '#/state/session'
|
||||
import {com} from '#/lexicons'
|
||||
import {useAppviewClient, usePdsClient} from '#/state/session'
|
||||
import {app, com} from '#/lexicons'
|
||||
|
||||
const handleQueryKeyRoot = 'handle'
|
||||
const fetchHandleQueryKey = (handleOrDid: string) => [
|
||||
@@ -16,21 +16,24 @@ const fetchDidQueryKey = (handleOrDid: string) => [didQueryKeyRoot, handleOrDid]
|
||||
|
||||
export function useFetchHandle() {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useCallback(
|
||||
async (handleOrDid: string) => {
|
||||
if (handleOrDid.startsWith('did:')) {
|
||||
const res = await queryClient.fetchQuery({
|
||||
const data = await queryClient.fetchQuery({
|
||||
staleTime: STALE.MINUTES.FIVE,
|
||||
queryKey: fetchHandleQueryKey(handleOrDid),
|
||||
queryFn: () => agent.getProfile({actor: handleOrDid}),
|
||||
queryFn: () =>
|
||||
client.call(app.bsky.actor.getProfile, {
|
||||
actor: handleOrDid as DidString,
|
||||
}),
|
||||
})
|
||||
return res.data.handle
|
||||
return data.handle
|
||||
}
|
||||
return handleOrDid
|
||||
},
|
||||
[queryClient, agent],
|
||||
[queryClient, client],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,7 +45,6 @@ export function useUpdateHandleMutation(opts?: {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({handle}: {handle: string}) => {
|
||||
// `agent.updateHandle` was a pure alias for this method
|
||||
await client.call(com.atproto.identity.updateHandle, {
|
||||
// callers validate the handle before submitting
|
||||
handle: handle as HandleString,
|
||||
@@ -59,7 +61,7 @@ export function useUpdateHandleMutation(opts?: {
|
||||
|
||||
export function useFetchDid() {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useCallback(
|
||||
async (handleOrDid: string) => {
|
||||
@@ -69,13 +71,15 @@ export function useFetchDid() {
|
||||
queryFn: async () => {
|
||||
let identifier = handleOrDid
|
||||
if (!identifier.startsWith('did:')) {
|
||||
const res = await agent.resolveHandle({handle: identifier})
|
||||
identifier = res.data.did
|
||||
const data = await client.call(com.atproto.identity.resolveHandle, {
|
||||
handle: identifier as HandleString,
|
||||
})
|
||||
identifier = data.did
|
||||
}
|
||||
return identifier
|
||||
},
|
||||
})
|
||||
},
|
||||
[queryClient, agent],
|
||||
[queryClient, client],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import {moderatePost} from '#/lib/moderation/subjects'
|
||||
import {logger} from '#/logger'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {DEFAULT_LOGGED_OUT_PREFERENCES} from '#/state/queries/preferences/const'
|
||||
import {useAgent, useAppviewClient} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import * as userActionHistory from '#/state/userActionHistory'
|
||||
import {KnownError} from '#/view/com/posts/PostFeedErrorMessage'
|
||||
import {useFeedTuners} from '../preferences/feed-tuners'
|
||||
@@ -151,7 +151,7 @@ export function usePostFeedQuery(
|
||||
f => f.pinned && f.value === 'following',
|
||||
) ?? -1
|
||||
const enableFollowingToDiscoverFallback = followingPinnedIndex === 0
|
||||
const agent = useAgent()
|
||||
const {hasSession} = useSession()
|
||||
const client = useAppviewClient()
|
||||
const lastRun = useRef<{
|
||||
data: InfiniteData<FeedPageUnselected>
|
||||
@@ -214,7 +214,7 @@ export function usePostFeedQuery(
|
||||
* moderations happen later, which results in some posts being shown and
|
||||
* some not.
|
||||
*/
|
||||
if (!agent.session) {
|
||||
if (!hasSession) {
|
||||
assertSomePostsPassModeration(
|
||||
res.feed,
|
||||
preferences?.moderationPrefs ||
|
||||
|
||||
+46
-50
@@ -1,6 +1,7 @@
|
||||
import {useCallback} from 'react'
|
||||
import {type AppBskyActorDefs, type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {type Client} from '@atproto/lex'
|
||||
import {type AtUriString, type HandleString} from '@atproto/syntax'
|
||||
import {deleteLike, deletePost, deleteRepost, like, repost} from '@bsky.app/sdk'
|
||||
import {
|
||||
type QueryClient,
|
||||
@@ -12,16 +13,11 @@ import {
|
||||
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
|
||||
import {updatePostShadow} from '#/state/cache/post-shadow'
|
||||
import {type Shadow} from '#/state/cache/types'
|
||||
import {
|
||||
useAgent,
|
||||
useAppviewClient,
|
||||
usePdsClient,
|
||||
useSession,
|
||||
} from '#/state/session'
|
||||
import {useAppviewClient, usePdsClient, useSession} from '#/state/session'
|
||||
import * as userActionHistory from '#/state/userActionHistory'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {type Metrics, toClout} from '#/analytics/metrics'
|
||||
import {app} from '#/lexicons'
|
||||
import {app, com} from '#/lexicons'
|
||||
import {useIsThreadMuted, useSetThreadMute} from '../cache/thread-mutes'
|
||||
import {findProfileQueryData} from './profile'
|
||||
|
||||
@@ -29,25 +25,15 @@ const RQKEY_ROOT = 'post'
|
||||
export const RQKEY = (postUri: string) => [RQKEY_ROOT, postUri]
|
||||
|
||||
export function usePostQuery(uri: string | undefined) {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
return useQuery<AppBskyFeedDefs.PostView>({
|
||||
queryKey: RQKEY(uri || ''),
|
||||
queryFn: async () => {
|
||||
if (!uri) throw new Error('[unreachable] No URI provided')
|
||||
|
||||
const urip = new AtUri(uri)
|
||||
|
||||
if (!urip.host.startsWith('did:')) {
|
||||
const res = await agent.resolveHandle({
|
||||
handle: urip.host,
|
||||
})
|
||||
// @ts-expect-error TODO new-sdk-migration
|
||||
urip.host = res.data.did
|
||||
}
|
||||
|
||||
const res = await agent.getPosts({uris: [urip.toString()]})
|
||||
if (res.success && res.data.posts[0]) {
|
||||
return res.data.posts[0]
|
||||
const post = await fetchPost(client, uri)
|
||||
if (post) {
|
||||
return post
|
||||
}
|
||||
|
||||
throw new Error('No data')
|
||||
@@ -56,6 +42,33 @@ export function usePostQuery(uri: string | undefined) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one post by AT-URI, resolving a handle authority first when the URI
|
||||
* carries one.
|
||||
*
|
||||
* The appview still answers with `@atproto/api`-shaped views for the callers of
|
||||
* these hooks, so the generated view is asserted across at this single
|
||||
* boundary rather than at every consumer.
|
||||
*/
|
||||
async function fetchPost(
|
||||
client: Client,
|
||||
uri: string,
|
||||
): Promise<AppBskyFeedDefs.PostView | undefined> {
|
||||
const urip = new AtUri(uri)
|
||||
|
||||
if (!urip.host.startsWith('did:')) {
|
||||
const data = await client.call(com.atproto.identity.resolveHandle, {
|
||||
handle: urip.host as HandleString,
|
||||
})
|
||||
urip.host = data.did
|
||||
}
|
||||
|
||||
const data = await client.call(app.bsky.feed.getPosts, {
|
||||
uris: [urip.toString()],
|
||||
})
|
||||
return data.posts[0]
|
||||
}
|
||||
|
||||
export function precachePost(
|
||||
queryClient: QueryClient,
|
||||
uri: string,
|
||||
@@ -66,59 +79,42 @@ export function precachePost(
|
||||
|
||||
export function useGetPost() {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
return useCallback(
|
||||
async ({uri}: {uri: string}) => {
|
||||
return queryClient.fetchQuery({
|
||||
queryKey: RQKEY(uri || ''),
|
||||
async queryFn() {
|
||||
const urip = new AtUri(uri)
|
||||
|
||||
if (!urip.host.startsWith('did:')) {
|
||||
const res = await agent.resolveHandle({
|
||||
handle: urip.host,
|
||||
})
|
||||
// @ts-expect-error TODO new-sdk-migration
|
||||
urip.host = res.data.did
|
||||
}
|
||||
|
||||
const res = await agent.getPosts({
|
||||
uris: [urip.toString()],
|
||||
})
|
||||
|
||||
if (res.success && res.data.posts[0]) {
|
||||
return res.data.posts[0]
|
||||
const post = await fetchPost(client, uri)
|
||||
if (post) {
|
||||
return post
|
||||
}
|
||||
|
||||
throw new Error('useGetPost: post not found')
|
||||
},
|
||||
})
|
||||
},
|
||||
[queryClient, agent],
|
||||
[queryClient, client],
|
||||
)
|
||||
}
|
||||
|
||||
export function useGetPosts() {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
return useCallback(
|
||||
async ({uris}: {uris: string[]}) => {
|
||||
return queryClient.fetchQuery({
|
||||
queryKey: RQKEY(uris.join(',') || ''),
|
||||
async queryFn() {
|
||||
const res = await agent.getPosts({
|
||||
uris,
|
||||
const data = await client.call(app.bsky.feed.getPosts, {
|
||||
uris: uris as AtUriString[],
|
||||
})
|
||||
|
||||
if (res.success) {
|
||||
return res.data.posts
|
||||
} else {
|
||||
throw new Error('useGetPosts failed')
|
||||
}
|
||||
// See the note on `fetchPost` about the view shapes.
|
||||
return data.posts as AppBskyFeedDefs.PostView[]
|
||||
},
|
||||
})
|
||||
},
|
||||
[queryClient, agent],
|
||||
[queryClient, client],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
type UsePreferencesQueryResponse,
|
||||
} from '#/state/queries/preferences/types'
|
||||
import {createQueryKey} from '#/state/queries/util'
|
||||
import {useAgent, usePdsClient} from '#/state/session'
|
||||
import {useAppviewClient, usePdsClient} from '#/state/session'
|
||||
import {applyLabelersToClient, saveLabelers} from '#/state/session/moderation'
|
||||
import {useAgeAssurance} from '#/ageAssurance'
|
||||
import {makeAgeRestrictedModerationPrefs} from '#/ageAssurance/util'
|
||||
@@ -58,7 +58,7 @@ export const preferencesQueryKey = createQueryKey(
|
||||
|
||||
export function usePreferencesQuery() {
|
||||
const client = usePdsClient()
|
||||
const agent = useAgent()
|
||||
const appviewClient = useAppviewClient()
|
||||
const aa = useAgeAssurance()
|
||||
|
||||
const query = useQuery({
|
||||
@@ -86,12 +86,12 @@ export function usePreferencesQuery() {
|
||||
* from a labeler would not affect server-attached labels until the
|
||||
* session bundle was rebuilt.
|
||||
*
|
||||
* `applyLabelersToClient` writes to the agent, which is what stamps the
|
||||
* header on the requests the wrapping appview client issues, and it
|
||||
* drops the Bluesky moderation DID so the globally redacted authority is
|
||||
* not also listed unredacted.
|
||||
* The subscriptions go on the appview client, which is what stamps
|
||||
* `atproto-accept-labelers` on its own requests. The Bluesky moderation
|
||||
* DID is dropped so the globally redacted authority is not also listed
|
||||
* unredacted.
|
||||
*/
|
||||
applyLabelersToClient(agent, labelerDids)
|
||||
applyLabelersToClient(appviewClient, labelerDids)
|
||||
|
||||
/*
|
||||
* `BskyPreferences` is now the sdk's own type, so the assembled
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useMemo} from 'react'
|
||||
import {AtpAgent} from '@atproto/api'
|
||||
import {Client} from '@atproto/lex'
|
||||
import {interpretLabelValueDefinitions} from '@bsky.app/sdk/moderation'
|
||||
|
||||
import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities'
|
||||
@@ -14,7 +14,7 @@ export function useMyLabelersQuery({
|
||||
const prefs = usePreferencesQuery()
|
||||
let dids = Array.from(
|
||||
new Set(
|
||||
AtpAgent.appLabelers.concat(
|
||||
(Client.appLabelers as readonly string[]).concat(
|
||||
prefs.data?.moderationPrefs.labelers.map(l => l.did) || [],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import {type AtpAgent} from '@atproto/api'
|
||||
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {type ResolvedLink, resolveGif, resolveLink} from '#/lib/api/resolve'
|
||||
import {
|
||||
type LinkResolvers,
|
||||
type ResolvedLink,
|
||||
resolveGif,
|
||||
resolveLink,
|
||||
} from '#/lib/api/resolve'
|
||||
import {STALE} from '#/state/queries/index'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient, useChatClient} from '#/state/session'
|
||||
import {type Gif} from '#/features/gifPicker/types'
|
||||
|
||||
export const RQKEY_LINK_ROOT = 'resolve-link'
|
||||
@@ -12,24 +16,25 @@ export const RQKEY_LINK = (url: string) => [RQKEY_LINK_ROOT, url]
|
||||
export const RQKEY_GIF_ROOT = 'resolve-gif'
|
||||
export const RQKEY_GIF = (url: string) => [RQKEY_GIF_ROOT, url]
|
||||
|
||||
export function resolveLinkQueryOptions(agent: AtpAgent, url: string) {
|
||||
export function resolveLinkQueryOptions(clients: LinkResolvers, url: string) {
|
||||
return queryOptions({
|
||||
staleTime: STALE.HOURS.ONE,
|
||||
queryKey: RQKEY_LINK(url),
|
||||
queryFn: () => resolveLink(agent, url),
|
||||
queryFn: () => resolveLink(clients, url),
|
||||
})
|
||||
}
|
||||
|
||||
export function useResolveLinkQuery(url: string) {
|
||||
const agent = useAgent()
|
||||
return useQuery(resolveLinkQueryOptions(agent, url))
|
||||
const appviewClient = useAppviewClient()
|
||||
const chatClient = useChatClient()
|
||||
return useQuery(resolveLinkQueryOptions({appviewClient, chatClient}, url))
|
||||
}
|
||||
export function fetchResolveLinkQuery(
|
||||
queryClient: QueryClient,
|
||||
agent: AtpAgent,
|
||||
clients: LinkResolvers,
|
||||
url: string,
|
||||
) {
|
||||
return queryClient.fetchQuery(resolveLinkQueryOptions(agent, url))
|
||||
return queryClient.fetchQuery(resolveLinkQueryOptions(clients, url))
|
||||
}
|
||||
export function precacheResolveLinkQuery(
|
||||
queryClient: QueryClient,
|
||||
@@ -39,26 +44,25 @@ export function precacheResolveLinkQuery(
|
||||
queryClient.setQueryData(RQKEY_LINK(url), resolvedLink)
|
||||
}
|
||||
|
||||
/*
|
||||
* GIF resolution is pure metadata work on a URL the picker already returned -
|
||||
* it makes no atproto request - so it takes no client.
|
||||
*/
|
||||
export function useResolveGifQuery(gif: Gif) {
|
||||
const agent = useAgent()
|
||||
return useQuery({
|
||||
staleTime: STALE.HOURS.ONE,
|
||||
queryKey: RQKEY_GIF(gif.url),
|
||||
queryFn: async () => {
|
||||
return await resolveGif(agent, gif)
|
||||
return await resolveGif(gif)
|
||||
},
|
||||
})
|
||||
}
|
||||
export function fetchResolveGifQuery(
|
||||
queryClient: QueryClient,
|
||||
agent: AtpAgent,
|
||||
gif: Gif,
|
||||
) {
|
||||
export function fetchResolveGifQuery(queryClient: QueryClient, gif: Gif) {
|
||||
return queryClient.fetchQuery({
|
||||
staleTime: STALE.HOURS.ONE,
|
||||
queryKey: RQKEY_GIF(gif.url),
|
||||
queryFn: async () => {
|
||||
return await resolveGif(agent, gif)
|
||||
return await resolveGif(gif)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user