mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {
2 AppBskyActorDefs,
3 AppBskyActorGetSuggestions,
4 AppBskyGraphGetSuggestedFollowsByActor,
5 moderateProfile,
6} from '@atproto/api'
7import {
8 InfiniteData,
9 QueryClient,
10 QueryKey,
11 useInfiniteQuery,
12 useQuery,
13} from '@tanstack/react-query'
14
15import {
16 aggregateUserInterests,
17 createBskyTopicsHeader,
18} from '#/lib/api/feed/utils'
19import {getContentLanguages} from '#/state/preferences/languages'
20import {STALE} from '#/state/queries'
21import {usePreferencesQuery} from '#/state/queries/preferences'
22import {useAgent, useSession} from '#/state/session'
23import {useModerationOpts} from '../preferences/moderation-opts'
24
25const suggestedFollowsQueryKeyRoot = 'suggested-follows'
26const suggestedFollowsQueryKey = (options?: SuggestedFollowsOptions) => [
27 suggestedFollowsQueryKeyRoot,
28 options,
29]
30
31const suggestedFollowsByActorQueryKeyRoot = 'suggested-follows-by-actor'
32const suggestedFollowsByActorQueryKey = (did: string) => [
33 suggestedFollowsByActorQueryKeyRoot,
34 did,
35]
36
37type SuggestedFollowsOptions = {limit?: number; subsequentPageLimit?: number}
38
39export function useSuggestedFollowsQuery(options?: SuggestedFollowsOptions) {
40 const {currentAccount} = useSession()
41 const agent = useAgent()
42 const moderationOpts = useModerationOpts()
43 const {data: preferences} = usePreferencesQuery()
44 const limit = options?.limit || 25
45
46 return useInfiniteQuery<
47 AppBskyActorGetSuggestions.OutputSchema,
48 Error,
49 InfiniteData<AppBskyActorGetSuggestions.OutputSchema>,
50 QueryKey,
51 string | undefined
52 >({
53 enabled: !!moderationOpts && !!preferences,
54 staleTime: STALE.HOURS.ONE,
55 queryKey: suggestedFollowsQueryKey(options),
56 queryFn: async ({pageParam}) => {
57 const contentLangs = getContentLanguages().join(',')
58 const maybeDifferentLimit =
59 options?.subsequentPageLimit && pageParam
60 ? options.subsequentPageLimit
61 : limit
62 const res = await agent.app.bsky.actor.getSuggestions(
63 {
64 limit: maybeDifferentLimit,
65 cursor: pageParam,
66 },
67 {
68 headers: {
69 ...createBskyTopicsHeader(aggregateUserInterests(preferences)),
70 'Accept-Language': contentLangs,
71 },
72 },
73 )
74
75 res.data.actors = res.data.actors
76 .filter(
77 actor =>
78 !moderateProfile(actor, moderationOpts!).ui('profileList').filter,
79 )
80 .filter(actor => {
81 const viewer = actor.viewer
82 if (viewer) {
83 if (
84 viewer.following ||
85 viewer.muted ||
86 viewer.mutedByList ||
87 viewer.blockedBy ||
88 viewer.blocking
89 ) {
90 return false
91 }
92 }
93 if (actor.did === currentAccount?.did) {
94 return false
95 }
96 return true
97 })
98
99 return res.data
100 },
101 initialPageParam: undefined,
102 getNextPageParam: lastPage => lastPage.cursor,
103 })
104}
105
106export function useSuggestedFollowsByActorQuery({
107 did,
108 enabled,
109}: {
110 did: string
111 enabled?: boolean
112}) {
113 const agent = useAgent()
114 return useQuery({
115 queryKey: suggestedFollowsByActorQueryKey(did),
116 queryFn: async () => {
117 const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({
118 actor: did,
119 })
120 const suggestions = res.data.isFallback
121 ? []
122 : res.data.suggestions.filter(profile => !profile.viewer?.following)
123 return {suggestions, recId: res.data.recId}
124 },
125 enabled,
126 })
127}
128
129export function* findAllProfilesInQueryData(
130 queryClient: QueryClient,
131 did: string,
132): Generator<AppBskyActorDefs.ProfileView, void> {
133 yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did)
134 yield* findAllProfilesInSuggestedFollowsByActorQueryData(queryClient, did)
135}
136
137function* findAllProfilesInSuggestedFollowsQueryData(
138 queryClient: QueryClient,
139 did: string,
140) {
141 const queryDatas = queryClient.getQueriesData<
142 InfiniteData<AppBskyActorGetSuggestions.OutputSchema>
143 >({
144 queryKey: [suggestedFollowsQueryKeyRoot],
145 })
146 for (const [_queryKey, queryData] of queryDatas) {
147 if (!queryData?.pages) {
148 continue
149 }
150 for (const page of queryData?.pages) {
151 for (const actor of page.actors) {
152 if (actor.did === did) {
153 yield actor
154 }
155 }
156 }
157 }
158}
159
160function* findAllProfilesInSuggestedFollowsByActorQueryData(
161 queryClient: QueryClient,
162 did: string,
163) {
164 const queryDatas =
165 queryClient.getQueriesData<AppBskyGraphGetSuggestedFollowsByActor.OutputSchema>(
166 {
167 queryKey: [suggestedFollowsByActorQueryKeyRoot],
168 },
169 )
170 for (const [_queryKey, queryData] of queryDatas) {
171 if (!queryData) {
172 continue
173 }
174 for (const suggestion of queryData.suggestions) {
175 if (suggestion.did === did) {
176 yield suggestion
177 }
178 }
179 }
180}