Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Reuse overfetching for popular feeds, add in existing filtering (#4501)

authored by

Eric Bailey and committed by
GitHub
4c0f0378 bdeac28d

+101 -45
+95 -5
src/state/queries/feed.ts
··· 1 + import {useCallback, useEffect, useMemo, useRef} from 'react' 1 2 import { 2 3 AppBskyActorDefs, 3 4 AppBskyFeedDefs, ··· 171 172 }) 172 173 } 173 174 174 - export const useGetPopularFeedsQueryKey = ['getPopularFeeds'] 175 + // HACK 176 + // the protocol doesn't yet tell us which feeds are personalized 177 + // this list is used to filter out feed recommendations from logged out users 178 + // for the ones we know need it 179 + // -prf 180 + export const KNOWN_AUTHED_ONLY_FEEDS = [ 181 + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app 182 + 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed 183 + 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed 184 + 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow 185 + 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz 186 + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky 187 + 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz 188 + 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why 189 + ] 175 190 176 - export function useGetPopularFeedsQuery() { 191 + type GetPopularFeedsOptions = {limit?: number} 192 + 193 + export function createGetPopularFeedsQueryKey(...args: any[]) { 194 + return ['getPopularFeeds', ...args] 195 + } 196 + 197 + export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) { 198 + const {hasSession} = useSession() 177 199 const agent = useAgent() 178 - return useInfiniteQuery< 200 + const limit = options?.limit || 10 201 + const {data: preferences} = usePreferencesQuery() 202 + 203 + // Make sure this doesn't invalidate unless really needed. 204 + const selectArgs = useMemo( 205 + () => ({ 206 + hasSession, 207 + savedFeeds: preferences?.savedFeeds || [], 208 + }), 209 + [hasSession, preferences?.savedFeeds], 210 + ) 211 + const lastPageCountRef = useRef(0) 212 + 213 + const query = useInfiniteQuery< 179 214 AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema, 180 215 Error, 181 216 InfiniteData<AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema>, 182 217 QueryKey, 183 218 string | undefined 184 219 >({ 185 - queryKey: useGetPopularFeedsQueryKey, 220 + queryKey: createGetPopularFeedsQueryKey(options), 186 221 queryFn: async ({pageParam}) => { 187 222 const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({ 188 - limit: 10, 223 + limit, 189 224 cursor: pageParam, 190 225 }) 191 226 return res.data 192 227 }, 193 228 initialPageParam: undefined, 194 229 getNextPageParam: lastPage => lastPage.cursor, 230 + select: useCallback( 231 + ( 232 + data: InfiniteData<AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema>, 233 + ) => { 234 + const {savedFeeds, hasSession: hasSessionInner} = selectArgs 235 + data?.pages.map(page => { 236 + page.feeds = page.feeds.filter(feed => { 237 + if ( 238 + !hasSessionInner && 239 + KNOWN_AUTHED_ONLY_FEEDS.includes(feed.uri) 240 + ) { 241 + return false 242 + } 243 + const alreadySaved = Boolean( 244 + savedFeeds?.find(f => { 245 + return f.value === feed.uri 246 + }), 247 + ) 248 + return !alreadySaved 249 + }) 250 + 251 + return page 252 + }) 253 + 254 + return data 255 + }, 256 + [selectArgs /* Don't change. Everything needs to go into selectArgs. */], 257 + ), 195 258 }) 259 + 260 + useEffect(() => { 261 + const {isFetching, hasNextPage, data} = query 262 + if (isFetching || !hasNextPage) { 263 + return 264 + } 265 + 266 + // avoid double-fires of fetchNextPage() 267 + if ( 268 + lastPageCountRef.current !== 0 && 269 + lastPageCountRef.current === data?.pages?.length 270 + ) { 271 + return 272 + } 273 + 274 + // fetch next page if we haven't gotten a full page of content 275 + let count = 0 276 + for (const page of data?.pages || []) { 277 + count += page.feeds.length 278 + } 279 + if (count < limit && (data?.pages.length || 0) < 6) { 280 + query.fetchNextPage() 281 + lastPageCountRef.current = data?.pages?.length || 0 282 + } 283 + }, [query, limit]) 284 + 285 + return query 196 286 } 197 287 198 288 export function useSearchPopularFeedsMutation() {
+6 -40
src/view/screens/Feeds.tsx
··· 104 104 key: string 105 105 } 106 106 107 - // HACK 108 - // the protocol doesn't yet tell us which feeds are personalized 109 - // this list is used to filter out feed recommendations from logged out users 110 - // for the ones we know need it 111 - // -prf 112 - const KNOWN_AUTHED_ONLY_FEEDS = [ 113 - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app 114 - 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed 115 - 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed 116 - 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow 117 - 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz 118 - 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky 119 - 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz 120 - 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why 121 - ] 122 - 123 107 export function FeedsScreen(_props: Props) { 124 108 const pal = usePalette('default') 125 109 const {openComposer} = useComposerControls() ··· 327 311 type: 'popularFeedsLoading', 328 312 }) 329 313 } else { 330 - if ( 331 - !popularFeeds?.pages || 332 - popularFeeds?.pages[0]?.feeds?.length === 0 333 - ) { 314 + if (!popularFeeds?.pages) { 334 315 slices.push({ 335 316 key: 'popularFeedsNoResults', 336 317 type: 'popularFeedsNoResults', ··· 338 319 } else { 339 320 for (const page of popularFeeds.pages || []) { 340 321 slices = slices.concat( 341 - page.feeds 342 - .filter(feed => { 343 - if ( 344 - !hasSession && 345 - KNOWN_AUTHED_ONLY_FEEDS.includes(feed.uri) 346 - ) { 347 - return false 348 - } 349 - const alreadySaved = Boolean( 350 - preferences?.savedFeeds?.find(f => { 351 - return f.value === feed.uri 352 - }), 353 - ) 354 - return !alreadySaved 355 - }) 356 - .map(feed => ({ 357 - key: `popularFeed:${feed.uri}`, 358 - type: 'popularFeed', 359 - feedUri: feed.uri, 360 - })), 322 + page.feeds.map(feed => ({ 323 + key: `popularFeed:${feed.uri}`, 324 + type: 'popularFeed', 325 + feedUri: feed.uri, 326 + })), 361 327 ) 362 328 } 363 329