[SDK] Migrate search, bookmarks, contacts and feed queries to the lex clients (#11360)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+22
-13
@@ -8,6 +8,7 @@ import {
|
||||
} from 'react'
|
||||
import {AppState, type AppStateStatus} from 'react-native'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {type AtUriString, type DidString} from '@atproto/syntax'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
import {PROD_FEEDS, STAGING_FEEDS} from '#/lib/constants'
|
||||
@@ -22,7 +23,8 @@ import {
|
||||
} from '#/state/queries/post-feed'
|
||||
import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {useAgent} from './session'
|
||||
import {app} from '#/lexicons'
|
||||
import {useAppviewClient} from './session'
|
||||
|
||||
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
|
||||
|
||||
@@ -65,7 +67,7 @@ export function useFeedFeedback(
|
||||
) {
|
||||
const ax = useAnalytics()
|
||||
const logger = ax.logger.useChild(ax.logger.Context.FeedFeedback)
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
const feed =
|
||||
!!feedSourceInfo && isFeedSourceFeedInfo(feedSourceInfo)
|
||||
@@ -150,16 +152,19 @@ export function useFeedFeedback(
|
||||
return
|
||||
}
|
||||
|
||||
// Send to the feed
|
||||
agent.app.bsky.feed
|
||||
.sendInteractions(
|
||||
{interactions: interactionsToSend, feed: feed?.uri},
|
||||
/*
|
||||
* Send to the feed. Interactions go to the feed generator rather than the
|
||||
* appview, which the agent did by setting `atproto-proxy` by hand; the
|
||||
* client's per-call `service` option writes that same header.
|
||||
*/
|
||||
client
|
||||
.call(
|
||||
app.bsky.feed.sendInteractions,
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: {
|
||||
'atproto-proxy': `${proxyDid}#bsky_fg`,
|
||||
},
|
||||
interactions: interactionsToSend,
|
||||
feed: feed?.uri as AtUriString | undefined,
|
||||
},
|
||||
{service: `${proxyDid as DidString}#bsky_fg`},
|
||||
)
|
||||
.catch(() => {}) // ignore upstream errors
|
||||
|
||||
@@ -172,7 +177,7 @@ export function useFeedFeedback(
|
||||
)
|
||||
throttledFlushAggregatedStats()
|
||||
logger.debug('flushed')
|
||||
}, [agent, throttledFlushAggregatedStats, proxyDid, enabled, feed])
|
||||
}, [client, throttledFlushAggregatedStats, proxyDid, enabled, feed])
|
||||
|
||||
const sendToFeed = useMemo(
|
||||
() =>
|
||||
@@ -283,9 +288,13 @@ function toString(interaction: AppBskyFeedDefs.Interaction): string {
|
||||
}|${interaction.reqId || ''}`
|
||||
}
|
||||
|
||||
function toInteraction(str: string): AppBskyFeedDefs.Interaction {
|
||||
function toInteraction(str: string): app.bsky.feed.defs.Interaction {
|
||||
const [item, event, feedContext, reqId] = str.split('|')
|
||||
return {item, event, feedContext, reqId}
|
||||
/*
|
||||
* The fields come from splitting an internally-built key, so neither the
|
||||
* at-uri nor the event token is narrowed by the compiler here.
|
||||
*/
|
||||
return {item, event, feedContext, reqId} as app.bsky.feed.defs.Interaction
|
||||
}
|
||||
|
||||
type AggregatedStats = {
|
||||
|
||||
@@ -9,7 +9,8 @@ import {keepPreviousData, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {isJustAMute, moduiContainsHideableOffense} from '#/lib/moderation'
|
||||
import {logger} from '#/logger'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {app} from '#/lexicons'
|
||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
||||
import {DEFAULT_LOGGED_OUT_PREFERENCES} from './preferences'
|
||||
|
||||
@@ -27,7 +28,7 @@ export function useActorAutocompleteQuery(
|
||||
limit?: number,
|
||||
) {
|
||||
const moderationOpts = useModerationOpts()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
prefix = prefix.toLowerCase().trim()
|
||||
if (prefix.endsWith('.')) {
|
||||
@@ -39,13 +40,13 @@ export function useActorAutocompleteQuery(
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(prefix || ''),
|
||||
async queryFn() {
|
||||
const res = prefix
|
||||
? await agent.searchActorsTypeahead({
|
||||
const data = prefix
|
||||
? await client.call(app.bsky.actor.searchActorsTypeahead, {
|
||||
q: prefix,
|
||||
limit: limit || 8,
|
||||
})
|
||||
: undefined
|
||||
return res?.data.actors || []
|
||||
return data?.actors || []
|
||||
},
|
||||
select: useCallback(
|
||||
(data: AppBskyActorDefs.ProfileViewBasic[]) => {
|
||||
@@ -65,7 +66,7 @@ export type ActorAutocompleteFn = ReturnType<typeof useActorAutocompleteFn>
|
||||
export function useActorAutocompleteFn() {
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useCallback(
|
||||
async ({query, limit = 8}: {query: string; limit?: number}) => {
|
||||
@@ -77,7 +78,7 @@ export function useActorAutocompleteFn() {
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(query || ''),
|
||||
queryFn: () =>
|
||||
agent.searchActorsTypeahead({
|
||||
client.call(app.bsky.actor.searchActorsTypeahead, {
|
||||
q: query,
|
||||
limit,
|
||||
}),
|
||||
@@ -91,11 +92,11 @@ export function useActorAutocompleteFn() {
|
||||
|
||||
return computeSuggestions({
|
||||
q: query,
|
||||
searched: res?.data.actors,
|
||||
searched: res?.actors,
|
||||
moderationOpts: moderationOpts || DEFAULT_MOD_OPTS,
|
||||
})
|
||||
},
|
||||
[queryClient, moderationOpts, agent],
|
||||
[queryClient, moderationOpts, client],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {type AppBskyActorSearchActors} from '@atproto/api'
|
||||
import {
|
||||
type InfiniteData,
|
||||
keepPreviousData,
|
||||
@@ -8,7 +7,8 @@ import {
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {app} from '#/lexicons'
|
||||
|
||||
export const RQKEY_ROOT = 'actor-search'
|
||||
export const RQKEY = (query: string, limit?: number) => [
|
||||
@@ -28,23 +28,22 @@ export function useActorSearch({
|
||||
maintainData?: boolean
|
||||
limit?: number
|
||||
}) {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
return useInfiniteQuery<
|
||||
AppBskyActorSearchActors.OutputSchema,
|
||||
app.bsky.actor.searchActors.$OutputBody,
|
||||
Error,
|
||||
InfiniteData<AppBskyActorSearchActors.OutputSchema>,
|
||||
InfiniteData<app.bsky.actor.searchActors.$OutputBody>,
|
||||
QueryKey,
|
||||
string | undefined
|
||||
>({
|
||||
staleTime: STALE.MINUTES.FIVE,
|
||||
queryKey: RQKEY(query, limit),
|
||||
queryFn: async ({pageParam}) => {
|
||||
const res = await agent.searchActors({
|
||||
return await client.call(app.bsky.actor.searchActors, {
|
||||
q: query,
|
||||
limit,
|
||||
cursor: pageParam,
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
enabled: enabled && !!query,
|
||||
initialPageParam: undefined,
|
||||
@@ -54,7 +53,7 @@ export function useActorSearch({
|
||||
})
|
||||
}
|
||||
|
||||
function select(data: InfiniteData<AppBskyActorSearchActors.OutputSchema>) {
|
||||
function select(data: InfiniteData<app.bsky.actor.searchActors.$OutputBody>) {
|
||||
// enforce uniqueness
|
||||
const dids = new Set()
|
||||
|
||||
@@ -77,7 +76,7 @@ export function* findAllProfilesInQueryData(
|
||||
did: string,
|
||||
) {
|
||||
const queryDatas = queryClient.getQueriesData<
|
||||
InfiniteData<AppBskyActorSearchActors.OutputSchema>
|
||||
InfiniteData<app.bsky.actor.searchActors.$OutputBody>
|
||||
>({
|
||||
queryKey: [RQKEY_ROOT],
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {isNetworkError} from '#/lib/strings/errors'
|
||||
@@ -8,7 +9,8 @@ import {
|
||||
optimisticallyDeleteBookmark,
|
||||
optimisticallySaveBookmark,
|
||||
} from '#/state/queries/bookmarks/useBookmarksQuery'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {app} from '#/lexicons'
|
||||
|
||||
type MutationArgs =
|
||||
| {action: 'create'; post: AppBskyFeedDefs.PostView}
|
||||
@@ -23,20 +25,20 @@ type MutationArgs =
|
||||
|
||||
export function useBookmarkMutation() {
|
||||
const qc = useQueryClient()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useMutation({
|
||||
async mutationFn(args: MutationArgs) {
|
||||
if (args.action === 'create') {
|
||||
updatePostShadow(qc, args.post.uri, {bookmarked: true})
|
||||
await agent.app.bsky.bookmark.createBookmark({
|
||||
uri: args.post.uri,
|
||||
await client.call(app.bsky.bookmark.createBookmark, {
|
||||
uri: args.post.uri as AtUriString,
|
||||
cid: args.post.cid,
|
||||
})
|
||||
} else if (args.action === 'delete') {
|
||||
updatePostShadow(qc, args.uri, {bookmarked: false})
|
||||
await agent.app.bsky.bookmark.deleteBookmark({
|
||||
uri: args.uri,
|
||||
await client.call(app.bsky.bookmark.deleteBookmark, {
|
||||
uri: args.uri as AtUriString,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
type $Typed,
|
||||
type AppBskyBookmarkGetBookmarks,
|
||||
AppBskyFeedDefs,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
import {type $Typed, AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||
import {
|
||||
type InfiniteData,
|
||||
type QueryClient,
|
||||
@@ -16,28 +11,28 @@ import {
|
||||
embedViewRecordToPostView,
|
||||
getEmbeddedPost,
|
||||
} from '#/state/queries/util'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export const bookmarksQueryKeyRoot = 'bookmarks'
|
||||
export const createBookmarksQueryKey = () => [bookmarksQueryKeyRoot]
|
||||
|
||||
export function useBookmarksQuery() {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useInfiniteQuery<
|
||||
AppBskyBookmarkGetBookmarks.OutputSchema,
|
||||
app.bsky.bookmark.getBookmarks.$OutputBody,
|
||||
Error,
|
||||
InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>,
|
||||
InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>,
|
||||
QueryKey,
|
||||
string | undefined
|
||||
>({
|
||||
queryKey: createBookmarksQueryKey(),
|
||||
async queryFn({pageParam}) {
|
||||
const res = await agent.app.bsky.bookmark.getBookmarks({
|
||||
return await client.call(app.bsky.bookmark.getBookmarks, {
|
||||
cursor: pageParam,
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
initialPageParam: undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
@@ -45,7 +40,7 @@ export function useBookmarksQuery() {
|
||||
}
|
||||
|
||||
export async function truncateAndInvalidate(qc: QueryClient) {
|
||||
qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
|
||||
qc.setQueriesData<InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>>(
|
||||
{queryKey: [bookmarksQueryKeyRoot]},
|
||||
data => {
|
||||
if (data) {
|
||||
@@ -64,7 +59,7 @@ export async function optimisticallySaveBookmark(
|
||||
qc: QueryClient,
|
||||
post: AppBskyFeedDefs.PostView,
|
||||
) {
|
||||
qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
|
||||
qc.setQueriesData<InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>>(
|
||||
{
|
||||
queryKey: [bookmarksQueryKeyRoot],
|
||||
},
|
||||
@@ -75,19 +70,22 @@ export async function optimisticallySaveBookmark(
|
||||
pages: data.pages.map((page, index) => {
|
||||
if (index === 0) {
|
||||
post.$type = 'app.bsky.feed.defs#postView'
|
||||
/*
|
||||
* The optimistic entry is synthesized from an `@atproto/api`
|
||||
* `PostView`, whose string fields are unbranded, so it is asserted
|
||||
* to the vendored view type the query data is now keyed on.
|
||||
*/
|
||||
const bookmark = {
|
||||
createdAt: new Date().toISOString(),
|
||||
subject: {
|
||||
uri: post.uri,
|
||||
cid: post.cid,
|
||||
},
|
||||
item: post as $Typed<AppBskyFeedDefs.PostView>,
|
||||
} as unknown as app.bsky.bookmark.defs.BookmarkView
|
||||
return {
|
||||
...page,
|
||||
bookmarks: [
|
||||
{
|
||||
createdAt: new Date().toISOString(),
|
||||
subject: {
|
||||
uri: post.uri,
|
||||
cid: post.cid,
|
||||
},
|
||||
item: post as $Typed<AppBskyFeedDefs.PostView>,
|
||||
},
|
||||
...page.bookmarks,
|
||||
],
|
||||
bookmarks: [bookmark, ...page.bookmarks],
|
||||
}
|
||||
}
|
||||
return page
|
||||
@@ -101,7 +99,7 @@ export async function optimisticallyDeleteBookmark(
|
||||
qc: QueryClient,
|
||||
{uri}: {uri: string},
|
||||
) {
|
||||
qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
|
||||
qc.setQueriesData<InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>>(
|
||||
{
|
||||
queryKey: [bookmarksQueryKeyRoot],
|
||||
},
|
||||
@@ -125,7 +123,7 @@ export function* findAllPostsInQueryData(
|
||||
uri: string,
|
||||
): Generator<AppBskyFeedDefs.PostView, undefined> {
|
||||
const queryDatas = queryClient.getQueriesData<
|
||||
InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>
|
||||
InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>
|
||||
>({
|
||||
queryKey: [bookmarksQueryKeyRoot],
|
||||
})
|
||||
|
||||
+68
-56
@@ -3,11 +3,11 @@ import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyGraphDefs,
|
||||
type AppBskyUnspeccedGetPopularFeedGenerators,
|
||||
AtUri,
|
||||
moderateFeedGenerator,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {t} from '@lingui/core/macro'
|
||||
import {
|
||||
type InfiniteData,
|
||||
@@ -26,7 +26,8 @@ import {GCTIME, STALE} from '#/state/queries'
|
||||
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {createQueryKey} from '#/state/queries/util'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import {app} from '#/lexicons'
|
||||
import {router} from '#/routes'
|
||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
||||
import {type FeedDescriptor} from './post-feed'
|
||||
@@ -184,7 +185,7 @@ export function getAvatarTypeFromUri(uri: string) {
|
||||
|
||||
export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
||||
const type = getFeedTypeFromUri(uri)
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useQuery({
|
||||
staleTime: STALE.INFINITY,
|
||||
@@ -193,14 +194,16 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
||||
let view: FeedSourceInfo
|
||||
|
||||
if (type === 'feed') {
|
||||
const res = await agent.app.bsky.feed.getFeedGenerator({feed: uri})
|
||||
view = hydrateFeedGenerator(res.data.view)
|
||||
const data = await client.call(app.bsky.feed.getFeedGenerator, {
|
||||
feed: uri as AtUriString,
|
||||
})
|
||||
view = hydrateFeedGenerator(data.view)
|
||||
} else {
|
||||
const res = await agent.app.bsky.graph.getList({
|
||||
list: uri,
|
||||
const data = await client.call(app.bsky.graph.getList, {
|
||||
list: uri as AtUriString,
|
||||
limit: 1,
|
||||
})
|
||||
view = hydrateList(res.data.list)
|
||||
view = hydrateList(data.list)
|
||||
}
|
||||
|
||||
return view
|
||||
@@ -234,7 +237,7 @@ export function createGetPopularFeedsQueryKey(
|
||||
|
||||
export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
||||
const {hasSession} = useSession()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const limit = options?.limit || 10
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -255,24 +258,27 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
||||
enabled: Boolean(moderationOpts) && options?.enabled !== false,
|
||||
queryKey: createGetPopularFeedsQueryKey(options),
|
||||
queryFn: async ({pageParam}) => {
|
||||
const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({
|
||||
limit,
|
||||
cursor: pageParam,
|
||||
})
|
||||
const data = await client.call(
|
||||
app.bsky.unspecced.getPopularFeedGenerators,
|
||||
{
|
||||
limit,
|
||||
cursor: pageParam,
|
||||
},
|
||||
)
|
||||
|
||||
// precache feeds
|
||||
for (const feed of res.data.feeds) {
|
||||
for (const feed of data.feeds) {
|
||||
const hydratedFeed = hydrateFeedGenerator(feed)
|
||||
precacheFeed(queryClient, hydratedFeed)
|
||||
}
|
||||
|
||||
return res.data
|
||||
return data
|
||||
},
|
||||
initialPageParam: undefined as string | undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
select: useCallback(
|
||||
(
|
||||
data: InfiniteData<AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema>,
|
||||
data: InfiniteData<app.bsky.unspecced.getPopularFeedGenerators.$OutputBody>,
|
||||
) => {
|
||||
const {
|
||||
savedFeeds,
|
||||
@@ -336,24 +342,27 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
||||
}
|
||||
|
||||
export function useSearchPopularFeedsMutation() {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (query: string) => {
|
||||
const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({
|
||||
limit: 10,
|
||||
query: query,
|
||||
})
|
||||
const data = await client.call(
|
||||
app.bsky.unspecced.getPopularFeedGenerators,
|
||||
{
|
||||
limit: 10,
|
||||
query: query,
|
||||
},
|
||||
)
|
||||
|
||||
if (moderationOpts) {
|
||||
return res.data.feeds.filter(feed => {
|
||||
return data.feeds.filter(feed => {
|
||||
const decision = moderateFeedGenerator(feed, moderationOpts)
|
||||
return !decision.ui('contentMedia').blur
|
||||
})
|
||||
}
|
||||
|
||||
return res.data.feeds
|
||||
return data.feeds
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -371,7 +380,7 @@ export function usePopularFeedsSearch({
|
||||
query: string
|
||||
enabled?: boolean
|
||||
}) {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const enabledInner = enabled ?? Boolean(moderationOpts)
|
||||
|
||||
@@ -379,12 +388,15 @@ export function usePopularFeedsSearch({
|
||||
enabled: enabledInner,
|
||||
queryKey: createPopularFeedsSearchQueryKey(query),
|
||||
queryFn: async () => {
|
||||
const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({
|
||||
limit: 15,
|
||||
query: query,
|
||||
})
|
||||
const data = await client.call(
|
||||
app.bsky.unspecced.getPopularFeedGenerators,
|
||||
{
|
||||
limit: 15,
|
||||
query: query,
|
||||
},
|
||||
)
|
||||
|
||||
return res.data.feeds
|
||||
return data.feeds
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
select(data) {
|
||||
@@ -444,7 +456,7 @@ const createPinnedFeedInfosQueryKey = (
|
||||
|
||||
export function usePinnedFeedsInfos() {
|
||||
const {hasSession} = useSession()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
||||
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
|
||||
|
||||
@@ -467,13 +479,13 @@ export function usePinnedFeedsInfos() {
|
||||
const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed')
|
||||
let feedsPromise = Promise.resolve()
|
||||
if (pinnedFeeds.length > 0) {
|
||||
feedsPromise = agent.app.bsky.feed
|
||||
.getFeedGenerators({
|
||||
feeds: pinnedFeeds.map(f => f.value),
|
||||
feedsPromise = client
|
||||
.call(app.bsky.feed.getFeedGenerators, {
|
||||
feeds: pinnedFeeds.map(f => f.value as AtUriString),
|
||||
})
|
||||
.then(res => {
|
||||
for (let i = 0; i < res.data.feeds.length; i++) {
|
||||
const feedView = res.data.feeds[i]
|
||||
.then(data => {
|
||||
for (let i = 0; i < data.feeds.length; i++) {
|
||||
const feedView = data.feeds[i]
|
||||
resolved.set(feedView.uri, hydrateFeedGenerator(feedView))
|
||||
}
|
||||
})
|
||||
@@ -482,13 +494,13 @@ export function usePinnedFeedsInfos() {
|
||||
// Get all lists. This currently has to be done individually.
|
||||
const pinnedLists = pinnedItems.filter(feed => feed.type === 'list')
|
||||
const listsPromises = pinnedLists.map(list =>
|
||||
agent.app.bsky.graph
|
||||
.getList({
|
||||
list: list.value,
|
||||
client
|
||||
.call(app.bsky.graph.getList, {
|
||||
list: list.value as AtUriString,
|
||||
limit: 1,
|
||||
})
|
||||
.then(res => {
|
||||
const listView = res.data.list
|
||||
.then(data => {
|
||||
const listView = data.list
|
||||
resolved.set(listView.uri, hydrateList(listView))
|
||||
}),
|
||||
)
|
||||
@@ -551,7 +563,7 @@ export type SavedFeedItem =
|
||||
}
|
||||
|
||||
export function useSavedFeeds() {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
||||
const savedItems = preferences?.savedFeeds ?? []
|
||||
const queryClient = useQueryClient()
|
||||
@@ -582,25 +594,25 @@ export function useSavedFeeds() {
|
||||
|
||||
let feedsPromise = Promise.resolve()
|
||||
if (savedFeeds.length > 0) {
|
||||
feedsPromise = agent.app.bsky.feed
|
||||
.getFeedGenerators({
|
||||
feeds: savedFeeds.map(f => f.value),
|
||||
feedsPromise = client
|
||||
.call(app.bsky.feed.getFeedGenerators, {
|
||||
feeds: savedFeeds.map(f => f.value as AtUriString),
|
||||
})
|
||||
.then(res => {
|
||||
res.data.feeds.forEach(f => {
|
||||
.then(data => {
|
||||
data.feeds.forEach(f => {
|
||||
resolvedFeeds.set(f.uri, f)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const listsPromises = savedLists.map(list =>
|
||||
agent.app.bsky.graph
|
||||
.getList({
|
||||
list: list.value,
|
||||
client
|
||||
.call(app.bsky.graph.getList, {
|
||||
list: list.value as AtUriString,
|
||||
limit: 1,
|
||||
})
|
||||
.then(res => {
|
||||
const listView = res.data.list
|
||||
.then(data => {
|
||||
const listView = data.list
|
||||
resolvedLists.set(listView.uri, listView)
|
||||
}),
|
||||
)
|
||||
@@ -656,7 +668,7 @@ export function useSavedFeeds() {
|
||||
const feedInfoQueryKeyRoot = 'feedInfo'
|
||||
|
||||
export function useFeedInfo(feedUri: string | undefined) {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useQuery({
|
||||
staleTime: STALE.INFINITY,
|
||||
@@ -666,11 +678,11 @@ export function useFeedInfo(feedUri: string | undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
const res = await agent.app.bsky.feed.getFeedGenerator({
|
||||
feed: feedUri,
|
||||
const data = await client.call(app.bsky.feed.getFeedGenerator, {
|
||||
feed: feedUri as AtUriString,
|
||||
})
|
||||
|
||||
const feedSourceInfo = hydrateFeedGenerator(res.data.view)
|
||||
const feedSourceInfo = hydrateFeedGenerator(data.view)
|
||||
return feedSourceInfo
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {type AppBskyContactGetMatches} from '@atproto/api'
|
||||
import {
|
||||
type InfiniteData,
|
||||
type QueryClient,
|
||||
@@ -6,8 +5,9 @@ import {
|
||||
useQuery,
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {type Match} from '#/components/contacts/state'
|
||||
import {app} from '#/lexicons'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {STALE} from '.'
|
||||
|
||||
@@ -15,13 +15,12 @@ const RQ_KEY_ROOT = 'find-contacts'
|
||||
export const findContactsStatusQueryKey = [RQ_KEY_ROOT, 'sync-status']
|
||||
|
||||
export function useContactsSyncStatusQuery() {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useQuery({
|
||||
queryKey: findContactsStatusQueryKey,
|
||||
queryFn: async () => {
|
||||
const status = await agent.app.bsky.contact.getSyncStatus()
|
||||
return status.data
|
||||
return await client.call(app.bsky.contact.getSyncStatus, {})
|
||||
},
|
||||
staleTime: STALE.SECONDS.THIRTY,
|
||||
})
|
||||
@@ -30,15 +29,14 @@ export function useContactsSyncStatusQuery() {
|
||||
export const findContactsGetMatchesQueryKey = [RQ_KEY_ROOT, 'matches']
|
||||
|
||||
export function useContactsMatchesQuery() {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
|
||||
return useInfiniteQuery({
|
||||
queryKey: findContactsGetMatchesQueryKey,
|
||||
queryFn: async ({pageParam}) => {
|
||||
const matches = await agent.app.bsky.contact.getMatches({
|
||||
return await client.call(app.bsky.contact.getMatches, {
|
||||
cursor: pageParam,
|
||||
})
|
||||
return matches.data
|
||||
},
|
||||
initialPageParam: undefined as string | undefined,
|
||||
getNextPageParam: lastPage => lastPage.cursor,
|
||||
@@ -47,20 +45,19 @@ export function useContactsMatchesQuery() {
|
||||
}
|
||||
|
||||
export function optimisticRemoveMatch(queryClient: QueryClient, did: string) {
|
||||
queryClient.setQueryData<InfiniteData<AppBskyContactGetMatches.OutputSchema>>(
|
||||
findContactsGetMatchesQueryKey,
|
||||
old => {
|
||||
if (!old) return old
|
||||
queryClient.setQueryData<
|
||||
InfiniteData<app.bsky.contact.getMatches.$OutputBody>
|
||||
>(findContactsGetMatchesQueryKey, old => {
|
||||
if (!old) return old
|
||||
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => ({
|
||||
...page,
|
||||
matches: page.matches.filter(match => match.did !== did),
|
||||
})),
|
||||
}
|
||||
},
|
||||
)
|
||||
return {
|
||||
...old,
|
||||
pages: old.pages.map(page => ({
|
||||
...page,
|
||||
matches: page.matches.filter(match => match.did !== did),
|
||||
})),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const findContactsMatchesPassthroughQueryKey = (dids: string[]) => [
|
||||
@@ -95,7 +92,7 @@ export function* findAllProfilesInQueryData(
|
||||
did: string,
|
||||
): Generator<bsky.profile.AnyProfileView, void> {
|
||||
const queryDatas = queryClient.getQueriesData<
|
||||
InfiniteData<AppBskyContactGetMatches.OutputSchema>
|
||||
InfiniteData<app.bsky.contact.getMatches.$OutputBody>
|
||||
>({
|
||||
queryKey: findContactsGetMatchesQueryKey,
|
||||
})
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import {useState} from 'react'
|
||||
import {type DidDocument, getPdsEndpoint} from '@atproto/common-web'
|
||||
import {type HandleString} from '@atproto/syntax'
|
||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {DEFAULT_SERVICE, PUBLIC_BSKY_SERVICE} from '#/lib/constants'
|
||||
import {DEFAULT_SERVICE} from '#/lib/constants'
|
||||
import {useDebouncedValue} from '#/lib/hooks/useDebouncedValue'
|
||||
import {isNetworkError} from '#/lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {Agent} from '#/state/session/agent'
|
||||
import {getPublicAppviewClient} from '#/state/session/clients'
|
||||
import {com} from '#/lexicons'
|
||||
|
||||
const RQKEY_ROOT = 'pds-detection'
|
||||
export const RQKEY = (identifier: string) => [RQKEY_ROOT, identifier]
|
||||
@@ -147,16 +149,25 @@ export async function resolvePdsForIdentifier(
|
||||
identifier: string,
|
||||
): Promise<{did: string; pdsUrl: string | null} | null> {
|
||||
const norm = normalizeIdentifier(identifier)
|
||||
const agent = new Agent(null, {service: PUBLIC_BSKY_SERVICE})
|
||||
/*
|
||||
* Resolution runs without a session, so this uses the public appview client
|
||||
* rather than a session-scoped one. It matches the unauthenticated,
|
||||
* unproxied public agent this previously constructed by hand.
|
||||
*/
|
||||
const client = getPublicAppviewClient()
|
||||
try {
|
||||
let did: string
|
||||
if (norm.startsWith('did:')) {
|
||||
did = norm
|
||||
} else {
|
||||
const res = await withResolveTimeout(signal =>
|
||||
agent.resolveHandle({handle: norm}, {signal}),
|
||||
const data = await withResolveTimeout(signal =>
|
||||
client.call(
|
||||
com.atproto.identity.resolveHandle,
|
||||
{handle: norm as HandleString},
|
||||
{signal},
|
||||
),
|
||||
)
|
||||
did = res.data.did
|
||||
did = data.did
|
||||
}
|
||||
logger.debug('pds-detection: resolved identifier to DID', {
|
||||
identifier: norm,
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import {type AtpAgent, AtUri} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {type Client} from '@atproto/lex'
|
||||
import {type HandleString} from '@atproto/syntax'
|
||||
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {com} from '#/lexicons'
|
||||
import {useUnstableProfileViewCache} from './profile'
|
||||
|
||||
const RQKEY_ROOT = 'resolved-did'
|
||||
export const RQKEY = (didOrHandle: string) => [RQKEY_ROOT, didOrHandle]
|
||||
|
||||
const resolvedDidQueryOptions = (
|
||||
agent: AtpAgent,
|
||||
client: Client,
|
||||
getUnstableProfile: (did: string) => {did: string} | undefined,
|
||||
didOrHandle: string | undefined,
|
||||
) =>
|
||||
@@ -21,8 +24,15 @@ const resolvedDidQueryOptions = (
|
||||
// Just return the did if it's already one
|
||||
if (didOrHandle.startsWith('did:')) return didOrHandle
|
||||
|
||||
const res = await agent.resolveHandle({handle: didOrHandle})
|
||||
return res.data.did
|
||||
/*
|
||||
* Resolution stays on the appview client: the old agent call was proxied
|
||||
* to the appview, and the PDS implementation is not equivalent for
|
||||
* handles hosted elsewhere.
|
||||
*/
|
||||
const data = await client.call(com.atproto.identity.resolveHandle, {
|
||||
handle: didOrHandle as HandleString,
|
||||
})
|
||||
return data.did
|
||||
},
|
||||
initialData: () => {
|
||||
// Return undefined if no did or handle
|
||||
@@ -37,11 +47,11 @@ export function useResolveUriQuery(uri: string | undefined) {
|
||||
const urip = new AtUri(uri || '')
|
||||
const host = urip.host
|
||||
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const {getUnstableProfile} = useUnstableProfileViewCache()
|
||||
|
||||
return useQuery({
|
||||
...resolvedDidQueryOptions(agent, getUnstableProfile, host),
|
||||
...resolvedDidQueryOptions(client, getUnstableProfile, host),
|
||||
select: did => ({
|
||||
did,
|
||||
uri: AtUri.make(did, urip.collection, urip.rkey).toString(),
|
||||
@@ -50,11 +60,11 @@ export function useResolveUriQuery(uri: string | undefined) {
|
||||
}
|
||||
|
||||
export function useResolveDidQuery(didOrHandle: string | undefined) {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const {getUnstableProfile} = useUnstableProfileViewCache()
|
||||
|
||||
return useQuery(
|
||||
resolvedDidQueryOptions(agent, getUnstableProfile, didOrHandle),
|
||||
resolvedDidQueryOptions(client, getUnstableProfile, didOrHandle),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user