Bluesky app fork with some witchin' additions 馃挮
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 183 lines 5.0 kB view raw
1import { 2 type AppBskyActorDefs, 3 type AppBskyActorGetSuggestions, 4 type AppBskyGraphGetSuggestedFollowsByActor, 5 moderateProfile, 6} from '@atproto/api' 7import { 8 type InfiniteData, 9 type QueryClient, 10 type 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 staleTime = STALE.MINUTES.FIVE, 110}: { 111 did: string 112 enabled?: boolean 113 staleTime?: number 114}) { 115 const agent = useAgent() 116 return useQuery({ 117 staleTime, 118 queryKey: suggestedFollowsByActorQueryKey(did), 119 queryFn: async () => { 120 const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({ 121 actor: did, 122 }) 123 const suggestions = res.data.isFallback 124 ? [] 125 : res.data.suggestions.filter(profile => !profile.viewer?.following) 126 return {suggestions, recId: res.data.recId} 127 }, 128 enabled, 129 }) 130} 131 132export function* findAllProfilesInQueryData( 133 queryClient: QueryClient, 134 did: string, 135): Generator<AppBskyActorDefs.ProfileView, void> { 136 yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did) 137 yield* findAllProfilesInSuggestedFollowsByActorQueryData(queryClient, did) 138} 139 140function* findAllProfilesInSuggestedFollowsQueryData( 141 queryClient: QueryClient, 142 did: string, 143) { 144 const queryDatas = queryClient.getQueriesData< 145 InfiniteData<AppBskyActorGetSuggestions.OutputSchema> 146 >({ 147 queryKey: [suggestedFollowsQueryKeyRoot], 148 }) 149 for (const [_queryKey, queryData] of queryDatas) { 150 if (!queryData?.pages) { 151 continue 152 } 153 for (const page of queryData?.pages) { 154 for (const actor of page.actors) { 155 if (actor.did === did) { 156 yield actor 157 } 158 } 159 } 160 } 161} 162 163function* findAllProfilesInSuggestedFollowsByActorQueryData( 164 queryClient: QueryClient, 165 did: string, 166) { 167 const queryDatas = 168 queryClient.getQueriesData<AppBskyGraphGetSuggestedFollowsByActor.OutputSchema>( 169 { 170 queryKey: [suggestedFollowsByActorQueryKeyRoot], 171 }, 172 ) 173 for (const [_queryKey, queryData] of queryDatas) { 174 if (!queryData) { 175 continue 176 } 177 for (const suggestion of queryData.suggestions) { 178 if (suggestion.did === did) { 179 yield suggestion 180 } 181 } 182 } 183}