[SDK] Migrate profile and graph read queries to the appview client (#11356)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-13 22:26:15 +03:00
committed by GitHub
parent 855457c9fa
commit 0912119c77
14 changed files with 180 additions and 148 deletions
+11 -9
View File
@@ -1,6 +1,8 @@
import {type DidString} from '@atproto/syntax'
import {type QueryClient, useInfiniteQuery} from '@tanstack/react-query' import {type QueryClient, useInfiniteQuery} from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
export const RQKEY_ROOT = 'actor-starter-packs' export const RQKEY_ROOT = 'actor-starter-packs'
export const RQKEY_WITH_MEMBERSHIP_ROOT = 'actor-starter-packs-with-membership' export const RQKEY_WITH_MEMBERSHIP_ROOT = 'actor-starter-packs-with-membership'
@@ -17,17 +19,17 @@ export function useActorStarterPacksQuery({
did?: string did?: string
enabled?: boolean enabled?: boolean
}) { }) {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery({ return useInfiniteQuery({
queryKey: RQKEY(did), queryKey: RQKEY(did),
queryFn: async ({pageParam}: {pageParam?: string}) => { queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getActorStarterPacks({ return await client.call(app.bsky.graph.getActorStarterPacks, {
actor: did!, // the enabled flag prevents this from running until did is set
actor: did! as DidString,
limit: 10, limit: 10,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
enabled: Boolean(did) && enabled, enabled: Boolean(did) && enabled,
initialPageParam: undefined, initialPageParam: undefined,
@@ -42,17 +44,17 @@ export function useActorStarterPacksWithMembershipsQuery({
did?: string did?: string
enabled?: boolean enabled?: boolean
}) { }) {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery({ return useInfiniteQuery({
queryKey: RQKEY_WITH_MEMBERSHIP(did), queryKey: RQKEY_WITH_MEMBERSHIP(did),
queryFn: async ({pageParam}: {pageParam?: string}) => { queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getStarterPacksWithMembership({ return await client.call(app.bsky.graph.getStarterPacksWithMembership, {
actor: did!, // the enabled flag prevents this from running until did is set
actor: did! as DidString,
limit: 10, limit: 10,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
enabled: Boolean(did) && enabled, enabled: Boolean(did) && enabled,
initialPageParam: undefined, initialPageParam: undefined,
+11 -12
View File
@@ -1,7 +1,5 @@
import { import {type AppBskyActorDefs} from '@atproto/api'
type AppBskyActorDefs, import {type DidString} from '@atproto/syntax'
type AppBskyGraphGetKnownFollowers,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -9,7 +7,8 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
const PAGE_SIZE = 50 const PAGE_SIZE = 50
type RQPageParam = string | undefined type RQPageParam = string | undefined
@@ -18,22 +17,22 @@ const RQKEY_ROOT = 'profile-known-followers'
export const RQKEY = (did: string) => [RQKEY_ROOT, did] export const RQKEY = (did: string) => [RQKEY_ROOT, did]
export function useProfileKnownFollowersQuery(did: string | undefined) { export function useProfileKnownFollowersQuery(did: string | undefined) {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetKnownFollowers.OutputSchema, app.bsky.graph.getKnownFollowers.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetKnownFollowers.OutputSchema>, InfiniteData<app.bsky.graph.getKnownFollowers.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
queryKey: RQKEY(did || ''), queryKey: RQKEY(did || ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getKnownFollowers({ return await client.call(app.bsky.graph.getKnownFollowers, {
actor: did!, // the enabled flag prevents this from running until did is set
actor: did! as DidString,
limit: PAGE_SIZE, limit: PAGE_SIZE,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -46,7 +45,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetKnownFollowers.OutputSchema> InfiniteData<app.bsky.graph.getKnownFollowers.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
+14 -9
View File
@@ -1,9 +1,9 @@
import { import {
type AppBskyActorDefs, type AppBskyActorDefs,
type AppBskyGraphDefs, type AppBskyGraphDefs,
type AppBskyGraphGetList,
type AtpAgent, type AtpAgent,
} from '@atproto/api' } from '@atproto/api'
import {type AtUriString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -13,7 +13,8 @@ import {
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useAgent} from '#/state/session' import {useAgent, useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
const PAGE_SIZE = 30 const PAGE_SIZE = 30
type RQPageParam = string | undefined type RQPageParam = string | undefined
@@ -24,23 +25,23 @@ export const RQKEY = (uri: string) => [RQKEY_ROOT, uri]
export const RQKEY_ALL = (uri: string) => [RQKEY_ROOT_ALL, uri] export const RQKEY_ALL = (uri: string) => [RQKEY_ROOT_ALL, uri]
export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) { export function useListMembersQuery(uri?: string, limit: number = PAGE_SIZE) {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetList.OutputSchema, app.bsky.graph.getList.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetList.OutputSchema>, InfiniteData<app.bsky.graph.getList.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(uri ?? ''), queryKey: RQKEY(uri ?? ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getList({ return await client.call(app.bsky.graph.getList, {
list: uri!, // the enabled flag will prevent this from running until uri is set // the enabled flag will prevent this from running until uri is set
list: uri! as AtUriString,
limit, limit,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -60,6 +61,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) { export async function getAllListMembers(agent: AtpAgent, uri: string) {
let hasMore = true let hasMore = true
let cursor: string | undefined let cursor: string | undefined
@@ -95,7 +100,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetList.OutputSchema> InfiniteData<app.bsky.graph.getList.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
+20 -15
View File
@@ -1,7 +1,5 @@
import { import {type AppBskyActorDefs} from '@atproto/api'
type AppBskyActorDefs, import {type AtIdentifierString} from '@atproto/syntax'
type AppBskyGraphGetListsWithMembership,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -10,10 +8,11 @@ import {
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {createQueryKey} from '#/state/queries/util' import {createQueryKey} from '#/state/queries/util'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
export type ListWithMembership = export type ListWithMembership =
AppBskyGraphGetListsWithMembership.ListWithMembership app.bsky.graph.getListsWithMembership.ListWithMembership
const listsWithMembershipQueryKeyRoot = 'lists-with-membership' const listsWithMembershipQueryKeyRoot = 'lists-with-membership'
export const createListsWithMembershipQueryKey = (args: {actor: string}) => export const createListsWithMembershipQueryKey = (args: {actor: string}) =>
@@ -26,23 +25,23 @@ export function useListsWithMembershipQuery({
actor: string | undefined actor: string | undefined
enabled?: boolean enabled?: boolean
}) { }) {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetListsWithMembership.OutputSchema, app.bsky.graph.getListsWithMembership.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>, InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>,
QueryKey, QueryKey,
string | undefined string | undefined
>({ >({
queryKey: createListsWithMembershipQueryKey({actor: actor ?? ''}), queryKey: createListsWithMembershipQueryKey({actor: actor ?? ''}),
queryFn: async ({pageParam}: {pageParam?: string}) => { queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getListsWithMembership({ return await client.call(app.bsky.graph.getListsWithMembership, {
actor: actor!, // the enabled flag prevents this from running until actor is set // the enabled flag prevents this from running until actor is set
actor: actor! as AtIdentifierString,
limit: 50, limit: 50,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
enabled: Boolean(actor) && enabled, enabled: Boolean(actor) && enabled,
initialPageParam: undefined, initialPageParam: undefined,
@@ -64,7 +63,7 @@ export function updateListMembershipOptimistically({
subject: AppBskyActorDefs.ProfileView subject: AppBskyActorDefs.ProfileView
}) { }) {
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema> InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>
>(createListsWithMembershipQueryKey({actor}), old => { >(createListsWithMembershipQueryKey({actor}), old => {
if (!old) return old if (!old) return old
@@ -74,12 +73,18 @@ export function updateListMembershipOptimistically({
...page, ...page,
listsWithMembership: page.listsWithMembership.map(lwm => { listsWithMembership: page.listsWithMembership.map(lwm => {
if (lwm.list.uri === listUri) { 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 { return {
...lwm, ...lwm,
listItem: { listItem: {
uri: membershipUri, uri: membershipUri,
subject, subject,
}, } as app.bsky.graph.defs.ListItemView,
} }
} }
return lwm return lwm
@@ -99,7 +104,7 @@ export function removeListMembershipOptimistically({
listUri: string listUri: string
}) { }) {
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema> InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>
>(createListsWithMembershipQueryKey({actor}), old => { >(createListsWithMembershipQueryKey({actor}), old => {
if (!old) return old if (!old) return old
+8 -8
View File
@@ -1,4 +1,4 @@
import {type AppBskyActorDefs, type AppBskyGraphGetBlocks} from '@atproto/api' import {type AppBskyActorDefs} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -6,28 +6,28 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
const RQKEY_ROOT = 'my-blocked-accounts' const RQKEY_ROOT = 'my-blocked-accounts'
export const RQKEY = () => [RQKEY_ROOT] export const RQKEY = () => [RQKEY_ROOT]
type RQPageParam = string | undefined type RQPageParam = string | undefined
export function useMyBlockedAccountsQuery() { export function useMyBlockedAccountsQuery() {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetBlocks.OutputSchema, app.bsky.graph.getBlocks.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetBlocks.OutputSchema>, InfiniteData<app.bsky.graph.getBlocks.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
queryKey: RQKEY(), queryKey: RQKEY(),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getBlocks({ return await client.call(app.bsky.graph.getBlocks, {
limit: 30, limit: 30,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -39,7 +39,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetBlocks.OutputSchema> InfiniteData<app.bsky.graph.getBlocks.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
+20 -18
View File
@@ -1,9 +1,11 @@
import {type AppBskyGraphDefs} from '@atproto/api' import {type AppBskyGraphDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax'
import {type QueryClient, useQuery} from '@tanstack/react-query' import {type QueryClient, useQuery} from '@tanstack/react-query'
import {accumulate} from '#/lib/async/accumulate' import {accumulate} from '#/lib/async/accumulate'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useAgent, useSession} from '#/state/session' import {useAppviewClient, useSession} from '#/state/session'
import {app} from '#/lexicons'
export type MyListsFilter = export type MyListsFilter =
| 'all' | 'all'
@@ -16,7 +18,7 @@ export const RQKEY = (filter: MyListsFilter) => [RQKEY_ROOT, filter]
export function useMyListsQuery(filter: MyListsFilter) { export function useMyListsQuery(filter: MyListsFilter) {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const agent = useAgent() const client = useAppviewClient()
return useQuery<AppBskyGraphDefs.ListView[]>({ return useQuery<AppBskyGraphDefs.ListView[]>({
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(filter), queryKey: RQKEY(filter),
@@ -24,42 +26,42 @@ export function useMyListsQuery(filter: MyListsFilter) {
let lists: AppBskyGraphDefs.ListView[] = [] let lists: AppBskyGraphDefs.ListView[] = []
const promises = [ const promises = [
accumulate(cursor => accumulate(cursor =>
agent.app.bsky.graph client
.getLists({ .call(app.bsky.graph.getLists, {
actor: currentAccount!.did, actor: currentAccount!.did as DidString,
cursor, cursor,
limit: 50, limit: 50,
}) })
.then(res => ({ .then(data => ({
cursor: res.data.cursor, cursor: data.cursor,
items: res.data.lists, items: data.lists,
})), })),
), ),
] ]
if (filter === 'all-including-subscribed' || filter === 'mod') { if (filter === 'all-including-subscribed' || filter === 'mod') {
promises.push( promises.push(
accumulate(cursor => accumulate(cursor =>
agent.app.bsky.graph client
.getListMutes({ .call(app.bsky.graph.getListMutes, {
cursor, cursor,
limit: 50, limit: 50,
}) })
.then(res => ({ .then(data => ({
cursor: res.data.cursor, cursor: data.cursor,
items: res.data.lists, items: data.lists,
})), })),
), ),
) )
promises.push( promises.push(
accumulate(cursor => accumulate(cursor =>
agent.app.bsky.graph client
.getListBlocks({ .call(app.bsky.graph.getListBlocks, {
cursor, cursor,
limit: 50, limit: 50,
}) })
.then(res => ({ .then(data => ({
cursor: res.data.cursor, cursor: data.cursor,
items: res.data.lists, items: data.lists,
})), })),
), ),
) )
+8 -8
View File
@@ -1,4 +1,4 @@
import {type AppBskyActorDefs, type AppBskyGraphGetMutes} from '@atproto/api' import {type AppBskyActorDefs} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -6,28 +6,28 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
const RQKEY_ROOT = 'my-muted-accounts' const RQKEY_ROOT = 'my-muted-accounts'
export const RQKEY = () => [RQKEY_ROOT] export const RQKEY = () => [RQKEY_ROOT]
type RQPageParam = string | undefined type RQPageParam = string | undefined
export function useMyMutedAccountsQuery() { export function useMyMutedAccountsQuery() {
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetMutes.OutputSchema, app.bsky.graph.getMutes.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetMutes.OutputSchema>, InfiniteData<app.bsky.graph.getMutes.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
queryKey: RQKEY(), queryKey: RQKEY(),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getMutes({ return await client.call(app.bsky.graph.getMutes, {
limit: 30, limit: 30,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -39,7 +39,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetMutes.OutputSchema> InfiniteData<app.bsky.graph.getMutes.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
+6 -4
View File
@@ -1,3 +1,4 @@
import {type DidString} from '@atproto/syntax'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
@@ -5,14 +6,15 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {app} from '#/lexicons'
import {updatePostShadow} from '../cache/post-shadow' import {updatePostShadow} from '../cache/post-shadow'
import {useAgent, useSession} from '../session' import {useAppviewClient, useSession} from '../session'
import {useProfileUpdateMutation} from './profile' import {useProfileUpdateMutation} from './profile'
export function usePinnedPostMutation() { export function usePinnedPostMutation() {
const {_} = useLingui() const {_} = useLingui()
const {currentAccount} = useSession() const {currentAccount} = useSession()
const agent = useAgent() const client = useAppviewClient()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const {mutateAsync: profileUpdateMutate} = useProfileUpdateMutation() const {mutateAsync: profileUpdateMutate} = useProfileUpdateMutation()
@@ -33,8 +35,8 @@ export function usePinnedPostMutation() {
// get the currently pinned post so we can optimistically remove the pin from it // get the currently pinned post so we can optimistically remove the pin from it
if (!currentAccount) throw new Error('Not signed in') if (!currentAccount) throw new Error('Not signed in')
const {data: profile} = await agent.getProfile({ const profile = await client.call(app.bsky.actor.getProfile, {
actor: currentAccount.did, actor: currentAccount.did as DidString,
}) })
prevPinnedPost = profile.pinnedPost?.uri prevPinnedPost = profile.pinnedPost?.uri
if (prevPinnedPost && prevPinnedPost !== postUri) { if (prevPinnedPost && prevPinnedPost !== postUri) {
+11 -12
View File
@@ -1,14 +1,13 @@
import { import {moderateFeedGenerator} from '@atproto/api'
type AppBskyFeedGetActorFeeds, import {type DidString} from '@atproto/syntax'
moderateFeedGenerator,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryKey, type QueryKey,
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
import {useModerationOpts} from '../preferences/moderation-opts' import {useModerationOpts} from '../preferences/moderation-opts'
const PAGE_SIZE = 50 const PAGE_SIZE = 50
@@ -24,25 +23,25 @@ export function useProfileFeedgensQuery(
) { ) {
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const enabled = opts?.enabled !== false && Boolean(moderationOpts) const enabled = opts?.enabled !== false && Boolean(moderationOpts)
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyFeedGetActorFeeds.OutputSchema, app.bsky.feed.getActorFeeds.$OutputBody,
Error, Error,
InfiniteData<AppBskyFeedGetActorFeeds.OutputSchema>, InfiniteData<app.bsky.feed.getActorFeeds.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
queryKey: RQKEY(did), queryKey: RQKEY(did),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.feed.getActorFeeds({ const data = await client.call(app.bsky.feed.getActorFeeds, {
actor: did, actor: did as DidString,
limit: PAGE_SIZE, limit: PAGE_SIZE,
cursor: pageParam, cursor: pageParam,
}) })
res.data.feeds.sort((a, b) => { data.feeds.sort((a, b) => {
return (b.likeCount || 0) - (a.likeCount || 0) return (b.likeCount || 0) - (a.likeCount || 0)
}) })
return res.data return data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
+18 -14
View File
@@ -1,7 +1,5 @@
import { import {type AppBskyActorDefs} from '@atproto/api'
type AppBskyActorDefs, import {type DidString} from '@atproto/syntax'
type AppBskyGraphGetFollowers,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -9,8 +7,9 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {app} from '#/lexicons'
const DEFAULT_SORT = 'latest' const DEFAULT_SORT = 'latest'
const PAGE_SIZE = 30 const PAGE_SIZE = 30
@@ -33,26 +32,31 @@ export function useProfileFollowersQuery(
) { ) {
const ax = useAnalytics() const ax = useAnalytics()
const isSortEnabled = ax.features.enabled(ax.features.FollowSortEnable) const isSortEnabled = ax.features.enabled(ax.features.FollowSortEnable)
const agent = useAgent() const client = useAppviewClient()
const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetFollowers.OutputSchema, app.bsky.graph.getFollowers.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetFollowers.OutputSchema>, InfiniteData<app.bsky.graph.getFollowers.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
queryKey: RQKEY(did || '', sortParam), queryKey: RQKEY(did || '', sortParam),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getFollowers({ /*
actor: did || '', * The vendored lexicon does not declare `sort`, so it is spread in only
* when set and the whole params object is asserted. lex forwards
* undeclared params verbatim but rejects an undeclared key whose value
* is `undefined`, hence the conditional spread.
*/
return await client.call(app.bsky.graph.getFollowers, {
actor: (did || '') as DidString,
limit: PAGE_SIZE, limit: PAGE_SIZE,
cursor: pageParam, cursor: pageParam,
sort: sortParam, ...(sortParam ? {sort: sortParam} : {}),
}) } as app.bsky.graph.getFollowers.$Params)
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -65,7 +69,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetFollowers.OutputSchema> InfiniteData<app.bsky.graph.getFollowers.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
+18 -11
View File
@@ -1,4 +1,5 @@
import {type AppBskyActorDefs, type AppBskyGraphGetFollows} from '@atproto/api' import {type AppBskyActorDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -7,8 +8,9 @@ import {
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {app} from '#/lexicons'
const DEFAULT_SORT = 'latest' const DEFAULT_SORT = 'latest'
const PAGE_SIZE = 30 const PAGE_SIZE = 30
@@ -34,27 +36,32 @@ export function useProfileFollowsQuery(
) { ) {
const ax = useAnalytics() const ax = useAnalytics()
const isSortEnabled = ax.features.enabled(ax.features.FollowSortEnable) const isSortEnabled = ax.features.enabled(ax.features.FollowSortEnable)
const agent = useAgent() const client = useAppviewClient()
const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined const sortParam = isSortEnabled ? sort || DEFAULT_SORT : undefined
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetFollows.OutputSchema, app.bsky.graph.getFollows.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetFollows.OutputSchema>, InfiniteData<app.bsky.graph.getFollows.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(did || '', sortParam), queryKey: RQKEY(did || '', sortParam),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getFollows({ /*
actor: did || '', * The vendored lexicon does not declare `sort`, so it is spread in only
* when set and the whole params object is asserted. lex forwards
* undeclared params verbatim but rejects an undeclared key whose value
* is `undefined`, hence the conditional spread.
*/
return await client.call(app.bsky.graph.getFollows, {
actor: (did || '') as DidString,
limit: limit || PAGE_SIZE, limit: limit || PAGE_SIZE,
cursor: pageParam, cursor: pageParam,
sort: sortParam, ...(sortParam ? {sort: sortParam} : {}),
}) } as app.bsky.graph.getFollows.$Params)
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
@@ -67,7 +74,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<AppBskyActorDefs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyGraphGetFollows.OutputSchema> InfiniteData<app.bsky.graph.getFollows.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
+9 -9
View File
@@ -1,11 +1,13 @@
import {type AppBskyGraphGetLists, moderateUserList} from '@atproto/api' import {moderateUserList} from '@atproto/api'
import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
type QueryKey, type QueryKey,
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
import {useModerationOpts} from '../preferences/moderation-opts' import {useModerationOpts} from '../preferences/moderation-opts'
const PAGE_SIZE = 30 const PAGE_SIZE = 30
@@ -17,23 +19,21 @@ export const RQKEY = (did: string) => [RQKEY_ROOT, did]
export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) { export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const enabled = opts?.enabled !== false && Boolean(moderationOpts) const enabled = opts?.enabled !== false && Boolean(moderationOpts)
const agent = useAgent() const client = useAppviewClient()
return useInfiniteQuery< return useInfiniteQuery<
AppBskyGraphGetLists.OutputSchema, app.bsky.graph.getLists.$OutputBody,
Error, Error,
InfiniteData<AppBskyGraphGetLists.OutputSchema>, InfiniteData<app.bsky.graph.getLists.$OutputBody>,
QueryKey, QueryKey,
RQPageParam RQPageParam
>({ >({
queryKey: RQKEY(did), queryKey: RQKEY(did),
async queryFn({pageParam}: {pageParam: RQPageParam}) { async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getLists({ return await client.call(app.bsky.graph.getLists, {
actor: did, actor: did as DidString,
limit: PAGE_SIZE, limit: PAGE_SIZE,
cursor: pageParam, cursor: pageParam,
}) })
return res.data
}, },
initialPageParam: undefined, initialPageParam: undefined,
getNextPageParam: lastPage => lastPage.cursor, getNextPageParam: lastPage => lastPage.cursor,
+15 -14
View File
@@ -1,9 +1,6 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import { import {type AppBskyActorDefs} from '@atproto/api'
type AppBskyActorDefs, import {type DidString} from '@atproto/syntax'
type AppBskyActorGetSuggestions,
type AppBskyGraphGetSuggestedFollowsByActor,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -12,7 +9,8 @@ import {
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
const suggestedFollowsQueryKeyRoot = 'suggested-follows' const suggestedFollowsQueryKeyRoot = 'suggested-follows'
@@ -32,18 +30,21 @@ export function useSuggestedFollowsByActorQuery({
enabled?: boolean enabled?: boolean
staleTime?: number staleTime?: number
}) { }) {
const agent = useAgent() const client = useAppviewClient()
return useQuery({ return useQuery({
staleTime, staleTime,
queryKey: suggestedFollowsByActorQueryKey(did), queryKey: suggestedFollowsByActorQueryKey(did),
queryFn: async () => { queryFn: async () => {
const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({ const data = await client.call(
actor: did, app.bsky.graph.getSuggestedFollowsByActor,
}) {
const suggestions = res.data.suggestions.filter( actor: did as DidString,
},
)
const suggestions = data.suggestions.filter(
profile => !profile.viewer?.following, profile => !profile.viewer?.following,
) )
return {suggestions, recId: res.data.recIdStr} return {suggestions, recId: data.recIdStr}
}, },
enabled, enabled,
}) })
@@ -112,7 +113,7 @@ function* findAllProfilesInSuggestedFollowsQueryData(
did: string, did: string,
) { ) {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<AppBskyActorGetSuggestions.OutputSchema> InfiniteData<app.bsky.actor.getSuggestions.$OutputBody>
>({ >({
queryKey: [suggestedFollowsQueryKeyRoot], queryKey: [suggestedFollowsQueryKeyRoot],
}) })
@@ -135,7 +136,7 @@ function* findAllProfilesInSuggestedFollowsByActorQueryData(
did: string, did: string,
) { ) {
const queryDatas = const queryDatas =
queryClient.getQueriesData<AppBskyGraphGetSuggestedFollowsByActor.OutputSchema>( queryClient.getQueriesData<app.bsky.graph.getSuggestedFollowsByActor.$OutputBody>(
{ {
queryKey: [suggestedFollowsByActorQueryKeyRoot], queryKey: [suggestedFollowsByActorQueryKeyRoot],
}, },
@@ -1,9 +1,11 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {type DidString} from '@atproto/syntax'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
import {updateProfileShadow} from '#/state/cache/profile-shadow' import {updateProfileShadow} from '#/state/cache/profile-shadow'
import {useAgent} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
/** /**
@@ -13,13 +15,17 @@ import type * as bsky from '#/types/bsky'
*/ */
export function useUpdateProfileVerificationCache() { export function useUpdateProfileVerificationCache() {
const qc = useQueryClient() const qc = useQueryClient()
const agent = useAgent() const client = useAppviewClient()
return useCallback( return useCallback(
async ({profile}: {profile: bsky.profile.AnyProfileView}) => { async ({profile}: {profile: bsky.profile.AnyProfileView}) => {
try { try {
const {data: updated} = await agent.getProfile({ if (!profile.did) {
actor: profile.did ?? '', logger.warn(`useUpdateProfileVerificationCache: no did`, {profile})
return
}
const updated = await client.call(app.bsky.actor.getProfile, {
actor: profile.did as DidString,
}) })
updateProfileShadow(qc, profile.did, { updateProfileShadow(qc, profile.did, {
verification: updated.verification, verification: updated.verification,
@@ -30,6 +36,6 @@ export function useUpdateProfileVerificationCache() {
}) })
} }
}, },
[agent, qc], [client, qc],
) )
} }