mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

[Explore] Add shadow cache (#8119)

* enable shadowcache for feed previews

* extract keyextractor

* dedupe feeds

authored by samuel.fm and committed by

GitHub a0c644ac a15658cb

+194 -41
+9 -5
src/screens/Search/Explore.tsx
··· 15 15 import {type MetricEvents} from '#/logger/metrics' 16 16 import {useModerationOpts} from '#/state/preferences/moderation-opts' 17 17 import {useActorSearchPaginated} from '#/state/queries/actor-search' 18 + import { 19 + type FeedPreviewItem, 20 + useFeedPreviews, 21 + } from '#/state/queries/explore-feed-previews' 18 22 import {useGetPopularFeedsQuery} from '#/state/queries/feed' 19 23 import {usePreferencesQuery} from '#/state/queries/preferences' 20 24 import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows' ··· 47 51 import * as ProfileCard from '#/components/ProfileCard' 48 52 import {Text} from '#/components/Typography' 49 53 import * as ModuleHeader from './components/ModuleHeader' 50 - import { 51 - type FeedPreviewItem, 52 - useFeedPreviews, 53 - } from './modules/ExploreFeedPreviews' 54 54 import { 55 55 SuggestedAccountsTabBar, 56 56 SuggestedProfileCard, ··· 900 900 <List 901 901 data={items} 902 902 renderItem={renderItem} 903 - keyExtractor={item => item.key} 903 + keyExtractor={keyExtractor} 904 904 desktopFixedHeight 905 905 contentContainerStyle={{paddingBottom: 100}} 906 906 keyboardShouldPersistTaps="handled" ··· 912 912 onEndReachedThreshold={2} 913 913 /> 914 914 ) 915 + } 916 + 917 + function keyExtractor(item: FeedPreviewItem) { 918 + return item.key 915 919 } 916 920 917 921 const viewabilityConfig: ViewabilityConfig = {
+127 -3
src/screens/Search/modules/ExploreFeedPreviews.tsx src/state/queries/explore-feed-previews.tsx
··· 1 1 import {useMemo} from 'react' 2 - import {type AppBskyFeedDefs, moderatePost} from '@atproto/api' 2 + import { 3 + type AppBskyActorDefs, 4 + AppBskyFeedDefs, 5 + AtUri, 6 + moderatePost, 7 + } from '@atproto/api' 3 8 import {msg} from '@lingui/macro' 4 9 import {useLingui} from '@lingui/react' 5 - import {useInfiniteQuery} from '@tanstack/react-query' 10 + import { 11 + type InfiniteData, 12 + type QueryClient, 13 + useInfiniteQuery, 14 + } from '@tanstack/react-query' 6 15 7 16 import {CustomFeedAPI} from '#/lib/api/feed/custom' 8 17 import {aggregateUserInterests} from '#/lib/api/feed/utils' ··· 14 23 type FeedPostSliceItem, 15 24 } from '#/state/queries/post-feed' 16 25 import {usePreferencesQuery} from '#/state/queries/preferences' 26 + import { 27 + didOrHandleUriMatches, 28 + embedViewRecordToPostView, 29 + getEmbeddedPost, 30 + } from '#/state/queries/util' 17 31 import {useAgent} from '#/state/session' 18 32 19 33 const RQKEY_ROOT = 'feed-previews' ··· 68 82 uri: string 69 83 } 70 84 71 - export function useFeedPreviews(feeds: AppBskyFeedDefs.GeneratorView[]) { 85 + export function useFeedPreviews( 86 + feedsMaybeWithDuplicates: AppBskyFeedDefs.GeneratorView[], 87 + ) { 88 + const feeds = useMemo( 89 + () => 90 + feedsMaybeWithDuplicates.filter( 91 + (f, i, a) => i === a.findIndex(f2 => f.uri === f2.uri), 92 + ), 93 + [feedsMaybeWithDuplicates], 94 + ) 95 + 72 96 const uris = feeds.map(feed => feed.uri) 73 97 const {_} = useLingui() 74 98 const agent = useAgent() ··· 262 286 ]), 263 287 } 264 288 } 289 + 290 + export function* findAllPostsInQueryData( 291 + queryClient: QueryClient, 292 + uri: string, 293 + ): Generator<AppBskyFeedDefs.PostView, undefined> { 294 + const atUri = new AtUri(uri) 295 + 296 + const queryDatas = queryClient.getQueriesData< 297 + InfiniteData<{ 298 + feed: AppBskyFeedDefs.GeneratorView 299 + posts: AppBskyFeedDefs.FeedViewPost[] 300 + }> 301 + >({ 302 + queryKey: [RQKEY_ROOT], 303 + }) 304 + for (const [_queryKey, queryData] of queryDatas) { 305 + if (!queryData?.pages) { 306 + continue 307 + } 308 + for (const page of queryData?.pages) { 309 + for (const item of page.posts) { 310 + if (didOrHandleUriMatches(atUri, item.post)) { 311 + yield item.post 312 + } 313 + 314 + const quotedPost = getEmbeddedPost(item.post.embed) 315 + if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { 316 + yield embedViewRecordToPostView(quotedPost) 317 + } 318 + 319 + if (AppBskyFeedDefs.isPostView(item.reply?.parent)) { 320 + if (didOrHandleUriMatches(atUri, item.reply.parent)) { 321 + yield item.reply.parent 322 + } 323 + 324 + const parentQuotedPost = getEmbeddedPost(item.reply.parent.embed) 325 + if ( 326 + parentQuotedPost && 327 + didOrHandleUriMatches(atUri, parentQuotedPost) 328 + ) { 329 + yield embedViewRecordToPostView(parentQuotedPost) 330 + } 331 + } 332 + 333 + if (AppBskyFeedDefs.isPostView(item.reply?.root)) { 334 + if (didOrHandleUriMatches(atUri, item.reply.root)) { 335 + yield item.reply.root 336 + } 337 + 338 + const rootQuotedPost = getEmbeddedPost(item.reply.root.embed) 339 + if (rootQuotedPost && didOrHandleUriMatches(atUri, rootQuotedPost)) { 340 + yield embedViewRecordToPostView(rootQuotedPost) 341 + } 342 + } 343 + } 344 + } 345 + } 346 + } 347 + 348 + export function* findAllProfilesInQueryData( 349 + queryClient: QueryClient, 350 + did: string, 351 + ): Generator<AppBskyActorDefs.ProfileViewBasic, undefined> { 352 + const queryDatas = queryClient.getQueriesData< 353 + InfiniteData<{ 354 + feed: AppBskyFeedDefs.GeneratorView 355 + posts: AppBskyFeedDefs.FeedViewPost[] 356 + }> 357 + >({ 358 + queryKey: [RQKEY_ROOT], 359 + }) 360 + for (const [_queryKey, queryData] of queryDatas) { 361 + if (!queryData?.pages) { 362 + continue 363 + } 364 + for (const page of queryData?.pages) { 365 + for (const item of page.posts) { 366 + if (item.post.author.did === did) { 367 + yield item.post.author 368 + } 369 + const quotedPost = getEmbeddedPost(item.post.embed) 370 + if (quotedPost?.author.did === did) { 371 + yield quotedPost.author 372 + } 373 + if ( 374 + AppBskyFeedDefs.isPostView(item.reply?.parent) && 375 + item.reply?.parent?.author.did === did 376 + ) { 377 + yield item.reply.parent.author 378 + } 379 + if ( 380 + AppBskyFeedDefs.isPostView(item.reply?.root) && 381 + item.reply?.root?.author.did === did 382 + ) { 383 + yield item.reply.root.author 384 + } 385 + } 386 + } 387 + } 388 + }
+15 -8
src/state/cache/post-shadow.ts
··· 2 2 import { 3 3 AppBskyEmbedRecord, 4 4 AppBskyEmbedRecordWithMedia, 5 - AppBskyFeedDefs, 5 + type AppBskyFeedDefs, 6 6 } from '@atproto/api' 7 - import {QueryClient} from '@tanstack/react-query' 7 + import {type QueryClient} from '@tanstack/react-query' 8 8 import EventEmitter from 'eventemitter3' 9 9 10 10 import {batchedUpdates} from '#/lib/batchedUpdates' 11 - import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '../queries/notifications/feed' 12 - import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '../queries/post-feed' 13 - import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '../queries/post-quotes' 14 - import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '../queries/post-thread' 15 - import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '../queries/search-posts' 16 - import {castAsShadow, Shadow} from './types' 11 + import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews' 12 + import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed' 13 + import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '#/state/queries/post-feed' 14 + import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/queries/post-quotes' 15 + import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '#/state/queries/post-thread' 16 + import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/queries/search-posts' 17 + import {castAsShadow, type Shadow} from './types' 17 18 export type {Shadow} from './types' 18 19 19 20 export interface PostShadow { ··· 152 153 yield post 153 154 } 154 155 for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) { 156 + yield post 157 + } 158 + for (let post of findAllPostsInExploreFeedPreviewsQueryData( 159 + queryClient, 160 + uri, 161 + )) { 155 162 yield post 156 163 } 157 164 }
+20 -18
src/state/cache/profile-shadow.ts
··· 1 1 import {useEffect, useMemo, useState} from 'react' 2 - import {QueryClient} from '@tanstack/react-query' 2 + import {type QueryClient} from '@tanstack/react-query' 3 3 import EventEmitter from 'eventemitter3' 4 4 5 5 import {batchedUpdates} from '#/lib/batchedUpdates' 6 - import * as bsky from '#/types/bsky' 7 - import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '../queries/actor-search' 8 - import {findAllProfilesInQueryData as findAllProfilesInKnownFollowersQueryData} from '../queries/known-followers' 9 - import {findAllProfilesInQueryData as findAllProfilesInListMembersQueryData} from '../queries/list-members' 10 - import {findAllProfilesInQueryData as findAllProfilesInListConvosQueryData} from '../queries/messages/list-conversations' 11 - import {findAllProfilesInQueryData as findAllProfilesInMyBlockedAccountsQueryData} from '../queries/my-blocked-accounts' 12 - import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '../queries/my-muted-accounts' 13 - import {findAllProfilesInQueryData as findAllProfilesInFeedsQueryData} from '../queries/post-feed' 14 - import {findAllProfilesInQueryData as findAllProfilesInPostLikedByQueryData} from '../queries/post-liked-by' 15 - import {findAllProfilesInQueryData as findAllProfilesInPostQuotesQueryData} from '../queries/post-quotes' 16 - import {findAllProfilesInQueryData as findAllProfilesInPostRepostedByQueryData} from '../queries/post-reposted-by' 17 - import {findAllProfilesInQueryData as findAllProfilesInPostThreadQueryData} from '../queries/post-thread' 18 - import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '../queries/profile' 19 - import {findAllProfilesInQueryData as findAllProfilesInProfileFollowersQueryData} from '../queries/profile-followers' 20 - import {findAllProfilesInQueryData as findAllProfilesInProfileFollowsQueryData} from '../queries/profile-follows' 21 - import {findAllProfilesInQueryData as findAllProfilesInSuggestedFollowsQueryData} from '../queries/suggested-follows' 22 - import {castAsShadow, Shadow} from './types' 6 + import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '#/state/queries/actor-search' 7 + import {findAllProfilesInQueryData as findAllProfilesInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews' 8 + import {findAllProfilesInQueryData as findAllProfilesInKnownFollowersQueryData} from '#/state/queries/known-followers' 9 + import {findAllProfilesInQueryData as findAllProfilesInListMembersQueryData} from '#/state/queries/list-members' 10 + import {findAllProfilesInQueryData as findAllProfilesInListConvosQueryData} from '#/state/queries/messages/list-conversations' 11 + import {findAllProfilesInQueryData as findAllProfilesInMyBlockedAccountsQueryData} from '#/state/queries/my-blocked-accounts' 12 + import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '#/state/queries/my-muted-accounts' 13 + import {findAllProfilesInQueryData as findAllProfilesInFeedsQueryData} from '#/state/queries/post-feed' 14 + import {findAllProfilesInQueryData as findAllProfilesInPostLikedByQueryData} from '#/state/queries/post-liked-by' 15 + import {findAllProfilesInQueryData as findAllProfilesInPostQuotesQueryData} from '#/state/queries/post-quotes' 16 + import {findAllProfilesInQueryData as findAllProfilesInPostRepostedByQueryData} from '#/state/queries/post-reposted-by' 17 + import {findAllProfilesInQueryData as findAllProfilesInPostThreadQueryData} from '#/state/queries/post-thread' 18 + import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '#/state/queries/profile' 19 + import {findAllProfilesInQueryData as findAllProfilesInProfileFollowersQueryData} from '#/state/queries/profile-followers' 20 + import {findAllProfilesInQueryData as findAllProfilesInProfileFollowsQueryData} from '#/state/queries/profile-follows' 21 + import {findAllProfilesInQueryData as findAllProfilesInSuggestedFollowsQueryData} from '#/state/queries/suggested-follows' 22 + import type * as bsky from '#/types/bsky' 23 + import {castAsShadow, type Shadow} from './types' 23 24 24 25 export type {Shadow} from './types' 25 26 ··· 154 155 yield* findAllProfilesInFeedsQueryData(queryClient, did) 155 156 yield* findAllProfilesInPostThreadQueryData(queryClient, did) 156 157 yield* findAllProfilesInKnownFollowersQueryData(queryClient, did) 158 + yield* findAllProfilesInExploreFeedPreviewsQueryData(queryClient, did) 157 159 }
+23 -7
src/state/queries/post-thread.ts
··· 1 1 import { 2 - AppBskyActorDefs, 3 - AppBskyEmbedRecord, 2 + type AppBskyActorDefs, 3 + type AppBskyEmbedRecord, 4 4 AppBskyFeedDefs, 5 - AppBskyFeedGetPostThread, 5 + type AppBskyFeedGetPostThread, 6 6 AppBskyFeedPost, 7 7 AtUri, 8 8 moderatePost, 9 - ModerationDecision, 10 - ModerationOpts, 9 + type ModerationDecision, 10 + type ModerationOpts, 11 11 } from '@atproto/api' 12 - import {QueryClient, useQuery, useQueryClient} from '@tanstack/react-query' 12 + import {type QueryClient, useQuery, useQueryClient} from '@tanstack/react-query' 13 13 14 + import { 15 + findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData, 16 + findAllProfilesInQueryData as findAllProfilesInExploreFeedPreviewsQueryData, 17 + } from '#/state/queries/explore-feed-previews' 14 18 import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/queries/post-quotes' 15 - import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types' 19 + import {type UsePreferencesQueryResponse} from '#/state/queries/preferences/types' 16 20 import { 17 21 findAllPostsInQueryData as findAllPostsInSearchQueryData, 18 22 findAllProfilesInQueryData as findAllProfilesInSearchQueryData, ··· 495 499 for (let post of findAllPostsInSearchQueryData(queryClient, uri)) { 496 500 yield postViewToPlaceholderThread(post) 497 501 } 502 + for (let post of findAllPostsInExploreFeedPreviewsQueryData( 503 + queryClient, 504 + uri, 505 + )) { 506 + yield postViewToPlaceholderThread(post) 507 + } 498 508 } 499 509 500 510 export function* findAllProfilesInQueryData( ··· 527 537 yield profile 528 538 } 529 539 for (let profile of findAllProfilesInSearchQueryData(queryClient, did)) { 540 + yield profile 541 + } 542 + for (let profile of findAllProfilesInExploreFeedPreviewsQueryData( 543 + queryClient, 544 + did, 545 + )) { 530 546 yield profile 531 547 } 532 548 }