migrate the remaining agent reads outside the session layer
The last non-session `useAgent` consumers, all thin aliases over calls the clients already make: - `handle.ts`: `getProfile` / `resolveHandle` to the appview client. - `post.ts`: the three post readers share one `fetchPost` helper on the appview client. Their consumers still want `@atproto/api` views, so the generated view is asserted across at that single boundary rather than at each call site. - `post-feed.ts`: `agent.session` gated the logged-out "did any post survive moderation" assertion, which is a question about the session, not the transport - it reads `hasSession` from the session context now. - `ChangeHandleDialog`: `agent.serviceUrl` becomes `currentAccount.service`. - `TestCtrls.e2e`: drops the `configureProxy` call. The appview client reads `BLUESKY_PROXY_HEADER` when the bundle builds it, and the gate around this input means no bundle exists yet, so setting the constant is sufficient. `preferences/index.ts` still writes labeler subscriptions to the agent; that one moves with the bundle rework, which is what gives it a client to write to. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,7 @@ import {useFetchDid, useUpdateHandleMutation} from '#/state/queries/handle'
|
|||||||
import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
|
import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
|
||||||
import {useServiceQuery} from '#/state/queries/service'
|
import {useServiceQuery} from '#/state/queries/service'
|
||||||
import {useCurrentAccountProfile} from '#/state/queries/useCurrentAccountProfile'
|
import {useCurrentAccountProfile} from '#/state/queries/useCurrentAccountProfile'
|
||||||
import {useAgent, useSession, useSessionApi} from '#/state/session'
|
import {useSession, useSessionApi} from '#/state/session'
|
||||||
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
|
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
|
||||||
import {atoms as a, native, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, native, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {Admonition} from '#/components/Admonition'
|
import {Admonition} from '#/components/Admonition'
|
||||||
@@ -63,12 +63,12 @@ export function ChangeHandleDialog({
|
|||||||
function ChangeHandleDialogInner() {
|
function ChangeHandleDialogInner() {
|
||||||
const control = Dialog.useDialogContext()
|
const control = Dialog.useDialogContext()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const agent = useAgent()
|
const {currentAccount} = useSession()
|
||||||
const {
|
const {
|
||||||
data: serviceInfo,
|
data: serviceInfo,
|
||||||
error: serviceInfoError,
|
error: serviceInfoError,
|
||||||
refetch,
|
refetch,
|
||||||
} = useServiceQuery(agent.serviceUrl.toString())
|
} = useServiceQuery(currentAccount?.service ?? '')
|
||||||
|
|
||||||
const [page, setPage] = useState<'provided-handle' | 'own-handle'>(
|
const [page, setPage] = useState<'provided-handle' | 'own-handle'>(
|
||||||
'provided-handle',
|
'provided-handle',
|
||||||
|
|||||||
+17
-13
@@ -1,10 +1,10 @@
|
|||||||
import {useCallback} from 'react'
|
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 {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {useAgent, usePdsClient} from '#/state/session'
|
import {useAppviewClient, usePdsClient} from '#/state/session'
|
||||||
import {com} from '#/lexicons'
|
import {app, com} from '#/lexicons'
|
||||||
|
|
||||||
const handleQueryKeyRoot = 'handle'
|
const handleQueryKeyRoot = 'handle'
|
||||||
const fetchHandleQueryKey = (handleOrDid: string) => [
|
const fetchHandleQueryKey = (handleOrDid: string) => [
|
||||||
@@ -16,21 +16,24 @@ const fetchDidQueryKey = (handleOrDid: string) => [didQueryKeyRoot, handleOrDid]
|
|||||||
|
|
||||||
export function useFetchHandle() {
|
export function useFetchHandle() {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
async (handleOrDid: string) => {
|
async (handleOrDid: string) => {
|
||||||
if (handleOrDid.startsWith('did:')) {
|
if (handleOrDid.startsWith('did:')) {
|
||||||
const res = await queryClient.fetchQuery({
|
const data = await queryClient.fetchQuery({
|
||||||
staleTime: STALE.MINUTES.FIVE,
|
staleTime: STALE.MINUTES.FIVE,
|
||||||
queryKey: fetchHandleQueryKey(handleOrDid),
|
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
|
return handleOrDid
|
||||||
},
|
},
|
||||||
[queryClient, agent],
|
[queryClient, client],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +45,6 @@ export function useUpdateHandleMutation(opts?: {
|
|||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({handle}: {handle: string}) => {
|
mutationFn: async ({handle}: {handle: string}) => {
|
||||||
// `agent.updateHandle` was a pure alias for this method
|
|
||||||
await client.call(com.atproto.identity.updateHandle, {
|
await client.call(com.atproto.identity.updateHandle, {
|
||||||
// callers validate the handle before submitting
|
// callers validate the handle before submitting
|
||||||
handle: handle as HandleString,
|
handle: handle as HandleString,
|
||||||
@@ -59,7 +61,7 @@ export function useUpdateHandleMutation(opts?: {
|
|||||||
|
|
||||||
export function useFetchDid() {
|
export function useFetchDid() {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
async (handleOrDid: string) => {
|
async (handleOrDid: string) => {
|
||||||
@@ -69,13 +71,15 @@ export function useFetchDid() {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
let identifier = handleOrDid
|
let identifier = handleOrDid
|
||||||
if (!identifier.startsWith('did:')) {
|
if (!identifier.startsWith('did:')) {
|
||||||
const res = await agent.resolveHandle({handle: identifier})
|
const data = await client.call(com.atproto.identity.resolveHandle, {
|
||||||
identifier = res.data.did
|
handle: identifier as HandleString,
|
||||||
|
})
|
||||||
|
identifier = data.did
|
||||||
}
|
}
|
||||||
return identifier
|
return identifier
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[queryClient, agent],
|
[queryClient, client],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {moderatePost} from '#/lib/moderation/subjects'
|
|||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
import {DEFAULT_LOGGED_OUT_PREFERENCES} from '#/state/queries/preferences/const'
|
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 * as userActionHistory from '#/state/userActionHistory'
|
||||||
import {KnownError} from '#/view/com/posts/PostFeedErrorMessage'
|
import {KnownError} from '#/view/com/posts/PostFeedErrorMessage'
|
||||||
import {useFeedTuners} from '../preferences/feed-tuners'
|
import {useFeedTuners} from '../preferences/feed-tuners'
|
||||||
@@ -151,7 +151,7 @@ export function usePostFeedQuery(
|
|||||||
f => f.pinned && f.value === 'following',
|
f => f.pinned && f.value === 'following',
|
||||||
) ?? -1
|
) ?? -1
|
||||||
const enableFollowingToDiscoverFallback = followingPinnedIndex === 0
|
const enableFollowingToDiscoverFallback = followingPinnedIndex === 0
|
||||||
const agent = useAgent()
|
const {hasSession} = useSession()
|
||||||
const client = useAppviewClient()
|
const client = useAppviewClient()
|
||||||
const lastRun = useRef<{
|
const lastRun = useRef<{
|
||||||
data: InfiniteData<FeedPageUnselected>
|
data: InfiniteData<FeedPageUnselected>
|
||||||
@@ -214,7 +214,7 @@ export function usePostFeedQuery(
|
|||||||
* moderations happen later, which results in some posts being shown and
|
* moderations happen later, which results in some posts being shown and
|
||||||
* some not.
|
* some not.
|
||||||
*/
|
*/
|
||||||
if (!agent.session) {
|
if (!hasSession) {
|
||||||
assertSomePostsPassModeration(
|
assertSomePostsPassModeration(
|
||||||
res.feed,
|
res.feed,
|
||||||
preferences?.moderationPrefs ||
|
preferences?.moderationPrefs ||
|
||||||
|
|||||||
+46
-50
@@ -1,6 +1,7 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import {type AppBskyActorDefs, type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
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 {deleteLike, deletePost, deleteRepost, like, repost} from '@bsky.app/sdk'
|
||||||
import {
|
import {
|
||||||
type QueryClient,
|
type QueryClient,
|
||||||
@@ -12,16 +13,11 @@ import {
|
|||||||
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
|
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
|
||||||
import {updatePostShadow} from '#/state/cache/post-shadow'
|
import {updatePostShadow} from '#/state/cache/post-shadow'
|
||||||
import {type Shadow} from '#/state/cache/types'
|
import {type Shadow} from '#/state/cache/types'
|
||||||
import {
|
import {useAppviewClient, usePdsClient, useSession} from '#/state/session'
|
||||||
useAgent,
|
|
||||||
useAppviewClient,
|
|
||||||
usePdsClient,
|
|
||||||
useSession,
|
|
||||||
} from '#/state/session'
|
|
||||||
import * as userActionHistory from '#/state/userActionHistory'
|
import * as userActionHistory from '#/state/userActionHistory'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {type Metrics, toClout} from '#/analytics/metrics'
|
import {type Metrics, toClout} from '#/analytics/metrics'
|
||||||
import {app} from '#/lexicons'
|
import {app, com} from '#/lexicons'
|
||||||
import {useIsThreadMuted, useSetThreadMute} from '../cache/thread-mutes'
|
import {useIsThreadMuted, useSetThreadMute} from '../cache/thread-mutes'
|
||||||
import {findProfileQueryData} from './profile'
|
import {findProfileQueryData} from './profile'
|
||||||
|
|
||||||
@@ -29,25 +25,15 @@ const RQKEY_ROOT = 'post'
|
|||||||
export const RQKEY = (postUri: string) => [RQKEY_ROOT, postUri]
|
export const RQKEY = (postUri: string) => [RQKEY_ROOT, postUri]
|
||||||
|
|
||||||
export function usePostQuery(uri: string | undefined) {
|
export function usePostQuery(uri: string | undefined) {
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
return useQuery<AppBskyFeedDefs.PostView>({
|
return useQuery<AppBskyFeedDefs.PostView>({
|
||||||
queryKey: RQKEY(uri || ''),
|
queryKey: RQKEY(uri || ''),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!uri) throw new Error('[unreachable] No URI provided')
|
if (!uri) throw new Error('[unreachable] No URI provided')
|
||||||
|
|
||||||
const urip = new AtUri(uri)
|
const post = await fetchPost(client, uri)
|
||||||
|
if (post) {
|
||||||
if (!urip.host.startsWith('did:')) {
|
return post
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('No data')
|
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(
|
export function precachePost(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
uri: string,
|
uri: string,
|
||||||
@@ -66,59 +79,42 @@ export function precachePost(
|
|||||||
|
|
||||||
export function useGetPost() {
|
export function useGetPost() {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
return useCallback(
|
return useCallback(
|
||||||
async ({uri}: {uri: string}) => {
|
async ({uri}: {uri: string}) => {
|
||||||
return queryClient.fetchQuery({
|
return queryClient.fetchQuery({
|
||||||
queryKey: RQKEY(uri || ''),
|
queryKey: RQKEY(uri || ''),
|
||||||
async queryFn() {
|
async queryFn() {
|
||||||
const urip = new AtUri(uri)
|
const post = await fetchPost(client, uri)
|
||||||
|
if (post) {
|
||||||
if (!urip.host.startsWith('did:')) {
|
return post
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('useGetPost: post not found')
|
throw new Error('useGetPost: post not found')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[queryClient, agent],
|
[queryClient, client],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGetPosts() {
|
export function useGetPosts() {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
return useCallback(
|
return useCallback(
|
||||||
async ({uris}: {uris: string[]}) => {
|
async ({uris}: {uris: string[]}) => {
|
||||||
return queryClient.fetchQuery({
|
return queryClient.fetchQuery({
|
||||||
queryKey: RQKEY(uris.join(',') || ''),
|
queryKey: RQKEY(uris.join(',') || ''),
|
||||||
async queryFn() {
|
async queryFn() {
|
||||||
const res = await agent.getPosts({
|
const data = await client.call(app.bsky.feed.getPosts, {
|
||||||
uris,
|
uris: uris as AtUriString[],
|
||||||
})
|
})
|
||||||
|
// See the note on `fetchPost` about the view shapes.
|
||||||
if (res.success) {
|
return data.posts as AppBskyFeedDefs.PostView[]
|
||||||
return res.data.posts
|
|
||||||
} else {
|
|
||||||
throw new Error('useGetPosts failed')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[queryClient, agent],
|
[queryClient, client],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {LogBox, Pressable, TextInput, View} from 'react-native'
|
|||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {BLUESKY_PROXY_HEADER} from '#/lib/constants'
|
import {BLUESKY_PROXY_HEADER} from '#/lib/constants'
|
||||||
import {useAgent, useSessionApi} from '#/state/session'
|
import {useSessionApi} from '#/state/session'
|
||||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||||
import {useOnboardingDispatch} from '#/state/shell/onboarding'
|
import {useOnboardingDispatch} from '#/state/shell/onboarding'
|
||||||
import {navigate} from '../../../Navigation'
|
import {navigate} from '../../../Navigation'
|
||||||
@@ -31,7 +31,6 @@ const BTN = {height: 1, width: 1, backgroundColor: 'red'}
|
|||||||
let hasConfiguredProxy = false
|
let hasConfiguredProxy = false
|
||||||
|
|
||||||
export function TestCtrls() {
|
export function TestCtrls() {
|
||||||
const agent = useAgent()
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {logoutEveryAccount, login} = useSessionApi()
|
const {logoutEveryAccount, login} = useSessionApi()
|
||||||
const onboardingDispatch = useOnboardingDispatch()
|
const onboardingDispatch = useOnboardingDispatch()
|
||||||
@@ -74,8 +73,13 @@ export function TestCtrls() {
|
|||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
const header = `${proxyHeader}#bsky_appview`
|
const header = `${proxyHeader}#bsky_appview`
|
||||||
|
/*
|
||||||
|
* The appview client reads `BLUESKY_PROXY_HEADER.get()` when the
|
||||||
|
* bundle builds it (see clients.ts), so setting the mutable constant
|
||||||
|
* retargets the proxy for the sign-ins below without reconfiguring
|
||||||
|
* anything: the gate above means no bundle exists yet.
|
||||||
|
*/
|
||||||
BLUESKY_PROXY_HEADER.set(header)
|
BLUESKY_PROXY_HEADER.set(header)
|
||||||
agent.configureProxy(header as any)
|
|
||||||
hasConfiguredProxy = true
|
hasConfiguredProxy = true
|
||||||
setIsProxyConfigured(true)
|
setIsProxyConfigured(true)
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user