An ATproto social media client -- with an independent Appview.
1import {
2 type AppBskyFeedGetActorFeeds,
3 moderateFeedGenerator,
4} from '@atproto/api'
5import {
6 type InfiniteData,
7 type QueryKey,
8 useInfiniteQuery,
9} from '@tanstack/react-query'
10
11import {useAgent} from '#/state/session'
12import {useModerationOpts} from '../preferences/moderation-opts'
13
14const PAGE_SIZE = 50
15type RQPageParam = string | undefined
16
17// TODO refactor invalidate on mutate?
18export const RQKEY_ROOT = 'profile-feedgens'
19export const RQKEY = (did: string) => [RQKEY_ROOT, did]
20
21export function useProfileFeedgensQuery(
22 did: string,
23 opts?: {enabled?: boolean},
24) {
25 const moderationOpts = useModerationOpts()
26 const enabled = opts?.enabled !== false && Boolean(moderationOpts)
27 const agent = useAgent()
28 return useInfiniteQuery<
29 AppBskyFeedGetActorFeeds.OutputSchema,
30 Error,
31 InfiniteData<AppBskyFeedGetActorFeeds.OutputSchema>,
32 QueryKey,
33 RQPageParam
34 >({
35 queryKey: RQKEY(did),
36 async queryFn({pageParam}: {pageParam: RQPageParam}) {
37 const res = await agent.app.bsky.feed.getActorFeeds({
38 actor: did,
39 limit: PAGE_SIZE,
40 cursor: pageParam,
41 })
42 res.data.feeds.sort((a, b) => {
43 return (b.likeCount || 0) - (a.likeCount || 0)
44 })
45 return res.data
46 },
47 initialPageParam: undefined,
48 getNextPageParam: lastPage => lastPage.cursor,
49 enabled,
50 select(data) {
51 return {
52 ...data,
53 pages: data.pages.map(page => {
54 return {
55 ...page,
56 feeds: page.feeds
57 // filter by labels
58 .filter(list => {
59 const decision = moderateFeedGenerator(list, moderationOpts!)
60 return !decision.ui('contentList').filter
61 }),
62 }
63 }),
64 }
65 },
66 })
67}