import { useQuery, useInfiniteQuery, InfiniteData, QueryKey, useMutation, } from '@tanstack/react-query' import { AtUri, RichText, AppBskyFeedDefs, AppBskyGraphDefs, AppBskyUnspeccedGetPopularFeedGenerators, } from '@atproto/api' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {useSession} from '#/state/session' type FeedSourceInfo = | { type: 'feed' uri: string cid: string href: string avatar: string | undefined displayName: string description: RichText creatorDid: string creatorHandle: string likeCount: number | undefined likeUri: string | undefined } | { type: 'list' uri: string cid: string href: string avatar: string | undefined displayName: string description: RichText creatorDid: string creatorHandle: string } export const useFeedSourceInfoQueryKey = ({uri}: {uri: string}) => [ 'getFeedSourceInfo', uri, ] const feedSourceNSIDs = { feed: 'app.bsky.feed.generator', list: 'app.bsky.graph.list', } function hydrateFeedGenerator( view: AppBskyFeedDefs.GeneratorView, ): FeedSourceInfo { const urip = new AtUri(view.uri) const collection = urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists' const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}` return { type: 'feed', uri: view.uri, cid: view.cid, href, avatar: view.avatar, displayName: view.displayName ? sanitizeDisplayName(view.displayName) : `Feed by ${sanitizeHandle(view.creator.handle, '@')}`, description: new RichText({ text: view.description || '', facets: (view.descriptionFacets || [])?.slice(), }), creatorDid: view.creator.did, creatorHandle: view.creator.handle, likeCount: view.likeCount, likeUri: view.viewer?.like, } } function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo { const urip = new AtUri(view.uri) const collection = urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists' const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}` return { type: 'list', uri: view.uri, cid: view.cid, href, avatar: view.avatar, description: new RichText({ text: view.description || '', facets: (view.descriptionFacets || [])?.slice(), }), creatorDid: view.creator.did, creatorHandle: view.creator.handle, displayName: view.name ? sanitizeDisplayName(view.name) : `User List by ${sanitizeHandle(view.creator.handle, '@')}`, } } export function useFeedSourceInfoQuery({uri}: {uri: string}) { const {agent} = useSession() const {pathname} = new AtUri(uri) const type = pathname.includes(feedSourceNSIDs.feed) ? 'feed' : 'list' return useQuery({ queryKey: useFeedSourceInfoQueryKey({uri}), queryFn: async () => { let view: FeedSourceInfo if (type === 'feed') { const res = await agent.app.bsky.feed.getFeedGenerator({feed: uri}) view = hydrateFeedGenerator(res.data.view) } else { const res = await agent.app.bsky.graph.getList({ list: uri, limit: 1, }) view = hydrateList(res.data.list) } return view }, }) } export const useGetPopularFeedsQueryKey = ['getPopularFeeds'] export function useGetPopularFeedsQuery() { const {agent} = useSession() return useInfiniteQuery< AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema, Error, InfiniteData, QueryKey, string | undefined >({ queryKey: useGetPopularFeedsQueryKey, queryFn: async ({pageParam}) => { const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({ limit: 10, cursor: pageParam, }) return res.data }, initialPageParam: undefined, getNextPageParam: lastPage => lastPage.cursor, }) } export function useSearchPopularFeedsMutation() { const {agent} = useSession() return useMutation({ mutationFn: async (query: string) => { const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({ limit: 10, query: query, }) return res.data.feeds }, }) }