migrate the list and feed generator queries to the appview client

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-03 15:15:40 +03:00
parent 8d738b891f
commit a8db57e00d
5 changed files with 70 additions and 63 deletions
+13 -9
View File
@@ -1,7 +1,6 @@
import {
type AppBskyActorDefs,
type AppBskyGraphDefs,
type AppBskyGraphGetList,
type AtpAgent,
} from '@atproto/api'
import {
@@ -13,7 +12,8 @@ import {
} from '@tanstack/react-query'
import {STALE} from '#/state/queries'
import {useAgent} from '#/state/session'
import {useAgent, useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
@@ -24,23 +24,23 @@ export const RQKEY = (uri: string) => [RQKEY_ROOT, uri]
export const RQKEY_ALL = (uri: string) => [RQKEY_ROOT_ALL, uri]
export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) {
const agent = useAgent()
const client = useAppviewClient()
return useInfiniteQuery<
AppBskyGraphGetList.OutputSchema,
app.bsky.graph.getList.$OutputBody,
Error,
InfiniteData<AppBskyGraphGetList.OutputSchema>,
InfiniteData<app.bsky.graph.getList.$OutputBody>,
QueryKey,
RQPageParam
>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(uri ?? ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getList({
list: uri!, // the enabled flag will prevent this from running until uri is set
return await client.call(app.bsky.graph.getList, {
// the enabled flag will prevent this from running until uri is set
list: uri! as app.bsky.graph.getList.$Params['list'],
limit,
cursor: pageParam,
})
return res.data
},
initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor,
@@ -60,6 +60,10 @@ export function useAllListMembersQuery(uri?: string) {
})
}
/*
* Still on the legacy agent: four call sites outside this cluster pass their
* own `AtpAgent`, so the parameter type is migrated with those consumers.
*/
export async function getAllListMembers(agent: AtpAgent, uri: string) {
let hasMore = true
let cursor: string | undefined
@@ -95,7 +99,7 @@ export function* findAllProfilesInQueryData(
did: string,
): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetList.OutputSchema>
InfiniteData<app.bsky.graph.getList.$OutputBody>
>({
queryKey: [RQKEY_ROOT],
})
+19 -15
View File
@@ -1,7 +1,4 @@
import {
type AppBskyActorDefs,
type AppBskyGraphGetListsWithMembership,
} from '@atproto/api'
import {type AppBskyActorDefs} from '@atproto/api'
import {
type InfiniteData,
type QueryClient,
@@ -10,10 +7,11 @@ import {
} from '@tanstack/react-query'
import {createQueryKey} from '#/state/queries/util'
import {useAgent} from '#/state/session'
import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
export type ListWithMembership =
AppBskyGraphGetListsWithMembership.ListWithMembership
app.bsky.graph.getListsWithMembership.ListWithMembership
const listsWithMembershipQueryKeyRoot = 'lists-with-membership'
export const createListsWithMembershipQueryKey = (args: {actor: string}) =>
@@ -26,23 +24,23 @@ export function useListsWithMembershipQuery({
actor: string | undefined
enabled?: boolean
}) {
const agent = useAgent()
const client = useAppviewClient()
return useInfiniteQuery<
AppBskyGraphGetListsWithMembership.OutputSchema,
app.bsky.graph.getListsWithMembership.$OutputBody,
Error,
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>,
InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>,
QueryKey,
string | undefined
>({
queryKey: createListsWithMembershipQueryKey({actor: actor ?? ''}),
queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getListsWithMembership({
actor: actor!, // the enabled flag prevents this from running until actor is set
return await client.call(app.bsky.graph.getListsWithMembership, {
// the enabled flag prevents this from running until actor is set
actor: actor! as app.bsky.graph.getListsWithMembership.$Params['actor'],
limit: 50,
cursor: pageParam,
})
return res.data
},
enabled: Boolean(actor) && enabled,
initialPageParam: undefined,
@@ -64,7 +62,7 @@ export function updateListMembershipOptimistically({
subject: AppBskyActorDefs.ProfileView
}) {
queryClient.setQueryData<
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>
InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>
>(createListsWithMembershipQueryKey({actor}), old => {
if (!old) return old
@@ -74,12 +72,18 @@ export function updateListMembershipOptimistically({
...page,
listsWithMembership: page.listsWithMembership.map(lwm => {
if (lwm.list.uri === listUri) {
/*
* Callers hand over a plain at-uri and an old-world ProfileView,
* whose `uri`/`did` are unbranded strings. They are runtime
* identical to the generated branded forms, so the synthesised
* listItem is asserted rather than re-validated.
*/
return {
...lwm,
listItem: {
uri: membershipUri,
subject,
},
} as app.bsky.graph.defs.ListItemView,
}
}
return lwm
@@ -99,7 +103,7 @@ export function removeListMembershipOptimistically({
listUri: string
}) {
queryClient.setQueryData<
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>
InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>
>(createListsWithMembershipQueryKey({actor}), old => {
if (!old) return old
+20 -18
View File
@@ -3,7 +3,8 @@ import {type QueryClient, useQuery} from '@tanstack/react-query'
import {accumulate} from '#/lib/async/accumulate'
import {STALE} from '#/state/queries'
import {useAgent, useSession} from '#/state/session'
import {useAppviewClient, useSession} from '#/state/session'
import {app} from '#/lexicons'
export type MyListsFilter =
| 'all'
@@ -16,7 +17,7 @@ export const RQKEY = (filter: MyListsFilter) => [RQKEY_ROOT, filter]
export function useMyListsQuery(filter: MyListsFilter) {
const {currentAccount} = useSession()
const agent = useAgent()
const client = useAppviewClient()
return useQuery<AppBskyGraphDefs.ListView[]>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(filter),
@@ -24,42 +25,43 @@ export function useMyListsQuery(filter: MyListsFilter) {
let lists: AppBskyGraphDefs.ListView[] = []
const promises = [
accumulate(cursor =>
agent.app.bsky.graph
.getLists({
actor: currentAccount!.did,
client
.call(app.bsky.graph.getLists, {
actor: currentAccount!
.did as app.bsky.graph.getLists.$Params['actor'],
cursor,
limit: 50,
})
.then(res => ({
cursor: res.data.cursor,
items: res.data.lists,
.then(data => ({
cursor: data.cursor,
items: data.lists,
})),
),
]
if (filter === 'all-including-subscribed' || filter === 'mod') {
promises.push(
accumulate(cursor =>
agent.app.bsky.graph
.getListMutes({
client
.call(app.bsky.graph.getListMutes, {
cursor,
limit: 50,
})
.then(res => ({
cursor: res.data.cursor,
items: res.data.lists,
.then(data => ({
cursor: data.cursor,
items: data.lists,
})),
),
)
promises.push(
accumulate(cursor =>
agent.app.bsky.graph
.getListBlocks({
client
.call(app.bsky.graph.getListBlocks, {
cursor,
limit: 50,
})
.then(res => ({
cursor: res.data.cursor,
items: res.data.lists,
.then(data => ({
cursor: data.cursor,
items: data.lists,
})),
),
)
+10 -12
View File
@@ -1,14 +1,12 @@
import {
type AppBskyFeedGetActorFeeds,
moderateFeedGenerator,
} from '@atproto/api'
import {moderateFeedGenerator} from '@atproto/api'
import {
type InfiniteData,
type QueryKey,
useInfiniteQuery,
} from '@tanstack/react-query'
import {useAgent} from '#/state/session'
import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
import {useModerationOpts} from '../preferences/moderation-opts'
const PAGE_SIZE = 50
@@ -24,25 +22,25 @@ export function useProfileFeedgensQuery(
) {
const moderationOpts = useModerationOpts()
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
const agent = useAgent()
const client = useAppviewClient()
return useInfiniteQuery<
AppBskyFeedGetActorFeeds.OutputSchema,
app.bsky.feed.getActorFeeds.$OutputBody,
Error,
InfiniteData<AppBskyFeedGetActorFeeds.OutputSchema>,
InfiniteData<app.bsky.feed.getActorFeeds.$OutputBody>,
QueryKey,
RQPageParam
>({
queryKey: RQKEY(did),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.feed.getActorFeeds({
actor: did,
const data = await client.call(app.bsky.feed.getActorFeeds, {
actor: did as app.bsky.feed.getActorFeeds.$Params['actor'],
limit: PAGE_SIZE,
cursor: pageParam,
})
res.data.feeds.sort((a, b) => {
data.feeds.sort((a, b) => {
return (b.likeCount || 0) - (a.likeCount || 0)
})
return res.data
return data
},
initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor,
+8 -9
View File
@@ -1,11 +1,12 @@
import {type AppBskyGraphGetLists, moderateUserList} from '@atproto/api'
import {moderateUserList} from '@atproto/api'
import {
type InfiniteData,
type QueryKey,
useInfiniteQuery,
} from '@tanstack/react-query'
import {useAgent} from '#/state/session'
import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
import {useModerationOpts} from '../preferences/moderation-opts'
const PAGE_SIZE = 30
@@ -17,23 +18,21 @@ export const RQKEY = (did: string) => [RQKEY_ROOT, did]
export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
const moderationOpts = useModerationOpts()
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
const agent = useAgent()
const client = useAppviewClient()
return useInfiniteQuery<
AppBskyGraphGetLists.OutputSchema,
app.bsky.graph.getLists.$OutputBody,
Error,
InfiniteData<AppBskyGraphGetLists.OutputSchema>,
InfiniteData<app.bsky.graph.getLists.$OutputBody>,
QueryKey,
RQPageParam
>({
queryKey: RQKEY(did),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getLists({
actor: did,
return await client.call(app.bsky.graph.getLists, {
actor: did as app.bsky.graph.getLists.$Params['actor'],
limit: PAGE_SIZE,
cursor: pageParam,
})
return res.data
},
initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor,