From c857a534ec81879d9ae92f26d9a5b8b1c9f5dc09 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Mon, 3 Aug 2026 18:45:01 +0300 Subject: [PATCH] migrate the post social queries and feed api construction sites Co-Authored-By: Claude Fable 5 --- src/state/queries/explore-feed-previews.tsx | 9 ++-- src/state/queries/post-feed.ts | 47 ++++++++++++++------- src/state/queries/post-liked-by.ts | 30 +++++++------ src/state/queries/post-quotes.ts | 21 ++++----- src/state/queries/post-reposted-by.ts | 23 +++++----- 5 files changed, 75 insertions(+), 55 deletions(-) diff --git a/src/state/queries/explore-feed-previews.tsx b/src/state/queries/explore-feed-previews.tsx index acbc4ee7e1..c340078603 100644 --- a/src/state/queries/explore-feed-previews.tsx +++ b/src/state/queries/explore-feed-previews.tsx @@ -5,6 +5,7 @@ import { AtUri, moderatePost, } from '@atproto/api' +import {type AtUriString} from '@atproto/syntax' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import { @@ -28,7 +29,7 @@ import { embedViewRecordToPostView, getEmbeddedPost, } from '#/state/queries/util' -import {useAgent} from '#/state/session' +import {useAppviewClient} from '#/state/session' const RQKEY_ROOT = 'feed-previews' const RQKEY = (feeds: string[]) => [RQKEY_ROOT, feeds] @@ -121,7 +122,7 @@ export function useFeedPreviews( const uris = feeds.map(feed => feed.uri) const {_} = useLingui() - const agent = useAgent() + const client = useAppviewClient() const {data: preferences} = usePreferencesQuery() const userInterests = aggregateUserInterests(preferences) const moderationOpts = useModerationOpts() @@ -143,8 +144,8 @@ export function useFeedPreviews( queryFn: async ({pageParam}) => { const feed = feeds[pageParam] const api = new CustomFeedAPI({ - agent, - feedParams: {feed: feed.uri}, + client, + feedParams: {feed: feed.uri as AtUriString}, userInterests, }) const data = await api.fetch({cursor: undefined, limit: LIMIT}) diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index 959ed81c28..0d5b48acde 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -4,12 +4,13 @@ import { type AppBskyActorDefs, AppBskyFeedDefs, type AppBskyFeedPost, - type AtpAgent, AtUri, moderatePost, type ModerationDecision, type ModerationPrefs, } from '@atproto/api' +import {type Client} from '@atproto/lex' +import {type AtIdentifierString, type AtUriString} from '@atproto/syntax' import { type InfiniteData, type QueryClient, @@ -33,7 +34,7 @@ import {DISCOVER_FEED_URI} from '#/lib/constants' import {logger} from '#/logger' import {STALE} from '#/state/queries' import {DEFAULT_LOGGED_OUT_PREFERENCES} from '#/state/queries/preferences/const' -import {useAgent} from '#/state/session' +import {useAgent, useAppviewClient} from '#/state/session' import * as userActionHistory from '#/state/userActionHistory' import {KnownError} from '#/view/com/posts/PostFeedErrorMessage' import {useFeedTuners} from '../preferences/feed-tuners' @@ -149,6 +150,7 @@ export function usePostFeedQuery( ) ?? -1 const enableFollowingToDiscoverFallback = followingPinnedIndex === 0 const agent = useAgent() + const client = useAppviewClient() const lastRun = useRef<{ data: InfiniteData args: typeof selectArgs @@ -193,7 +195,7 @@ export function usePostFeedQuery( feedDesc, feedParams: params || {}, feedTuners, - agent, + client, // Not in the query key because they don't change: userInterests, // Not in the query key. Reacting to it switching isn't important: @@ -443,55 +445,68 @@ function createApi({ feedParams, feedTuners, userInterests, - agent, + client, enableFollowingToDiscoverFallback, }: { feedDesc: FeedDescriptor feedParams: FeedParams feedTuners: FeedTunerFn[] userInterests?: string - agent: AtpAgent + client: Client enableFollowingToDiscoverFallback: boolean }) { if (feedDesc === 'following') { if (feedParams.mergeFeedEnabled) { return new MergeFeedAPI({ - agent, + client, feedParams, feedTuners, userInterests, }) } else { if (enableFollowingToDiscoverFallback) { - return new HomeFeedAPI({agent, userInterests}) + return new HomeFeedAPI({client, userInterests}) } else { - return new FollowingFeedAPI({agent}) + return new FollowingFeedAPI({client}) } } } else if (feedDesc.startsWith('author')) { const [__, actor, filter] = feedDesc.split('|') - return new AuthorFeedAPI({agent, feedParams: {actor, filter}}) + /* + * The descriptor is split out of an internally-built string, so neither the + * actor identifier nor the filter token is narrowed by the compiler here. + */ + return new AuthorFeedAPI({ + client, + feedParams: {actor: actor as AtIdentifierString, filter}, + }) } else if (feedDesc.startsWith('likes')) { const [__, actor] = feedDesc.split('|') - return new LikesFeedAPI({agent, feedParams: {actor}}) + return new LikesFeedAPI({ + client, + feedParams: {actor: actor as AtIdentifierString}, + }) } else if (feedDesc.startsWith('feedgen')) { const [__, feed] = feedDesc.split('|') return new CustomFeedAPI({ - agent, - feedParams: {feed}, + client, + feedParams: {feed: feed as AtUriString}, userInterests, }) } else if (feedDesc.startsWith('list')) { const [__, list] = feedDesc.split('|') - return new ListFeedAPI({agent, feedParams: {list}}) + return new ListFeedAPI({client, feedParams: {list: list as AtUriString}}) } else if (feedDesc.startsWith('posts')) { const [__, uriList] = feedDesc.split('|') - return new PostListFeedAPI({agent, feedParams: {uris: uriList.split(',')}}) + return new PostListFeedAPI({ + client, + feedParams: {uris: uriList.split(',') as AtUriString[]}, + }) } else if (feedDesc === 'demo') { - return new DemoFeedAPI({agent}) + return new DemoFeedAPI({client}) } else { // shouldnt happen - return new FollowingFeedAPI({agent}) + return new FollowingFeedAPI({client}) } } diff --git a/src/state/queries/post-liked-by.ts b/src/state/queries/post-liked-by.ts index e4f37c14ca..cc18a46426 100644 --- a/src/state/queries/post-liked-by.ts +++ b/src/state/queries/post-liked-by.ts @@ -1,4 +1,5 @@ -import {type AppBskyActorDefs, type AppBskyFeedGetLikes} from '@atproto/api' +import {type AppBskyActorDefs} from '@atproto/api' +import {type AtUriString} from '@atproto/syntax' import { type InfiniteData, type QueryClient, @@ -9,7 +10,8 @@ import { import {STALE} from '#/state/queries' import {createQueryKey} from '#/state/queries/util' -import {useAgent} from '#/state/session' +import {useAppviewClient} from '#/state/session' +import {app} from '#/lexicons' const PAGE_SIZE = 30 type RQPageParam = string | undefined @@ -19,22 +21,22 @@ const RQKEY_ROOT = 'liked-by' export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri] export function useLikedByQuery(resolvedUri: string | undefined) { - const agent = useAgent() + const client = useAppviewClient() return useInfiniteQuery< - AppBskyFeedGetLikes.OutputSchema, + app.bsky.feed.getLikes.$OutputBody, Error, - InfiniteData, + InfiniteData, QueryKey, RQPageParam >({ queryKey: RQKEY(resolvedUri || ''), async queryFn({pageParam}: {pageParam: RQPageParam}) { - const res = await agent.getLikes({ - uri: resolvedUri || '', + return await client.call(app.bsky.feed.getLikes, { + // the enabled flag prevents this from running until resolvedUri is set + uri: (resolvedUri || '') as AtUriString, limit: PAGE_SIZE, cursor: pageParam, }) - return res.data }, initialPageParam: undefined, getNextPageParam: lastPage => lastPage.cursor, @@ -59,12 +61,14 @@ export const createLikedBySampleQueryKey = (args: {uri: string}) => * perturb the liked-by screen's pagination. */ export function useLikedBySampleQuery({uri}: {uri: string | undefined}) { - const agent = useAgent() + const client = useAppviewClient() return useQuery({ queryKey: createLikedBySampleQueryKey({uri: uri ?? ''}), queryFn: async () => { - const res = await agent.getLikes({uri: uri ?? '', limit: SAMPLE_SIZE}) - return res.data + return await client.call(app.bsky.feed.getLikes, { + uri: (uri ?? '') as AtUriString, + limit: SAMPLE_SIZE, + }) }, staleTime: STALE.MINUTES.FIVE, enabled: !!uri, @@ -81,7 +85,7 @@ export function* findAllProfilesInQueryData( did: string, ): Generator { const queryDatas = queryClient.getQueriesData< - InfiniteData + InfiniteData >({ queryKey: [RQKEY_ROOT], }) @@ -98,7 +102,7 @@ export function* findAllProfilesInQueryData( } } const sampleQueryDatas = - queryClient.getQueriesData({ + queryClient.getQueriesData({ queryKey: [likedBySampleQueryKeyRoot], }) for (const [_queryKey, queryData] of sampleQueryDatas) { diff --git a/src/state/queries/post-quotes.ts b/src/state/queries/post-quotes.ts index 1d0fa07e8e..77b5dabcf8 100644 --- a/src/state/queries/post-quotes.ts +++ b/src/state/queries/post-quotes.ts @@ -2,9 +2,9 @@ import { type AppBskyActorDefs, AppBskyEmbedRecord, type AppBskyFeedDefs, - type AppBskyFeedGetQuotes, AtUri, } from '@atproto/api' +import {type AtUriString} from '@atproto/syntax' import { type InfiniteData, type QueryClient, @@ -12,7 +12,8 @@ import { useInfiniteQuery, } from '@tanstack/react-query' -import {useAgent} from '#/state/session' +import {useAppviewClient} from '#/state/session' +import {app} from '#/lexicons' import { didOrHandleUriMatches, embedViewRecordToPostView, @@ -26,22 +27,22 @@ const RQKEY_ROOT = 'post-quotes' export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri] export function usePostQuotesQuery(resolvedUri: string | undefined) { - const agent = useAgent() + const client = useAppviewClient() return useInfiniteQuery< - AppBskyFeedGetQuotes.OutputSchema, + app.bsky.feed.getQuotes.$OutputBody, Error, - InfiniteData, + InfiniteData, QueryKey, RQPageParam >({ queryKey: RQKEY(resolvedUri || ''), async queryFn({pageParam}: {pageParam: RQPageParam}) { - const res = await agent.api.app.bsky.feed.getQuotes({ - uri: resolvedUri || '', + return await client.call(app.bsky.feed.getQuotes, { + // the enabled flag prevents this from running until resolvedUri is set + uri: (resolvedUri || '') as AtUriString, limit: PAGE_SIZE, cursor: pageParam, }) - return res.data }, initialPageParam: undefined, getNextPageParam: lastPage => lastPage.cursor, @@ -72,7 +73,7 @@ export function* findAllProfilesInQueryData( did: string, ): Generator { const queryDatas = queryClient.getQueriesData< - InfiniteData + InfiniteData >({ queryKey: [RQKEY_ROOT], }) @@ -99,7 +100,7 @@ export function* findAllPostsInQueryData( uri: string, ): Generator { const queryDatas = queryClient.getQueriesData< - InfiniteData + InfiniteData >({ queryKey: [RQKEY_ROOT], }) diff --git a/src/state/queries/post-reposted-by.ts b/src/state/queries/post-reposted-by.ts index 814a815aae..d2c5e62385 100644 --- a/src/state/queries/post-reposted-by.ts +++ b/src/state/queries/post-reposted-by.ts @@ -1,7 +1,5 @@ -import { - type AppBskyActorDefs, - type AppBskyFeedGetRepostedBy, -} from '@atproto/api' +import {type AppBskyActorDefs} from '@atproto/api' +import {type AtUriString} from '@atproto/syntax' import { type InfiniteData, type QueryClient, @@ -9,7 +7,8 @@ import { useInfiniteQuery, } from '@tanstack/react-query' -import {useAgent} from '#/state/session' +import {useAppviewClient} from '#/state/session' +import {app} from '#/lexicons' const PAGE_SIZE = 30 type RQPageParam = string | undefined @@ -19,22 +18,22 @@ const RQKEY_ROOT = 'post-reposted-by' export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri] export function usePostRepostedByQuery(resolvedUri: string | undefined) { - const agent = useAgent() + const client = useAppviewClient() return useInfiniteQuery< - AppBskyFeedGetRepostedBy.OutputSchema, + app.bsky.feed.getRepostedBy.$OutputBody, Error, - InfiniteData, + InfiniteData, QueryKey, RQPageParam >({ queryKey: RQKEY(resolvedUri || ''), async queryFn({pageParam}: {pageParam: RQPageParam}) { - const res = await agent.getRepostedBy({ - uri: resolvedUri || '', + return await client.call(app.bsky.feed.getRepostedBy, { + // the enabled flag prevents this from running until resolvedUri is set + uri: (resolvedUri || '') as AtUriString, limit: PAGE_SIZE, cursor: pageParam, }) - return res.data }, initialPageParam: undefined, getNextPageParam: lastPage => lastPage.cursor, @@ -47,7 +46,7 @@ export function* findAllProfilesInQueryData( did: string, ): Generator { const queryDatas = queryClient.getQueriesData< - InfiniteData + InfiniteData >({ queryKey: [RQKEY_ROOT], })