Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

memoize each module individually (#8111)

authored by

Samuel Newman and committed by
GitHub
898065d0 faa5f40c

+220 -224
+220 -224
src/screens/Search/Explore.tsx
··· 325 325 fetchNextPageFeedPreviews, 326 326 ]) 327 327 328 - const items = useMemo<ExploreScreenItems[]>(() => { 328 + const topBorder = useMemo( 329 + () => ({type: 'topBorder', key: 'top-border'} as const), 330 + [], 331 + ) 332 + const trendingTopicsModule = useMemo( 333 + () => ({type: 'trendingTopics', key: 'trending-topics'} as const), 334 + [], 335 + ) 336 + const suggestedFollowsModule = useMemo(() => { 329 337 const i: ExploreScreenItems[] = [] 330 - 331 - const addTopBorder = () => { 332 - i.push({type: 'topBorder', key: 'top-border'}) 333 - } 338 + i.push({ 339 + type: 'tabbedHeader', 340 + key: 'suggested-accounts-header', 341 + title: _(msg`Suggested Accounts`), 342 + icon: Person, 343 + searchButton: { 344 + label: _(msg`Search for more accounts`), 345 + metricsTag: 'suggestedAccounts', 346 + tab: 'user', 347 + }, 348 + }) 334 349 335 - const addTrendingTopicsModule = () => { 350 + if (!canShowSuggestedProfiles) { 351 + i.push({type: 'profilePlaceholder', key: 'profilePlaceholder'}) 352 + } else if (profilesError) { 336 353 i.push({ 337 - type: 'trendingTopics', 338 - key: `trending-topics`, 354 + type: 'error', 355 + key: 'profilesError', 356 + message: _(msg`Failed to load suggested follows`), 357 + error: cleanError(profilesError), 339 358 }) 340 - 341 - // temp - disable trending videos 342 - // if (isNative) { 343 - // i.push({ 344 - // type: 'trendingVideos', 345 - // key: `trending-videos`, 346 - // }) 347 - // } 348 - } 349 - 350 - const addSuggestedFollowsModule = () => { 351 - i.push({ 352 - type: 'tabbedHeader', 353 - key: 'suggested-accounts-header', 354 - title: _(msg`Suggested Accounts`), 355 - icon: Person, 356 - searchButton: { 357 - label: _(msg`Search for more accounts`), 358 - metricsTag: 'suggestedAccounts', 359 - tab: 'user', 360 - }, 361 - }) 362 - 363 - if (!canShowSuggestedProfiles) { 364 - i.push({type: 'profilePlaceholder', key: 'profilePlaceholder'}) 365 - } else if (profilesError) { 366 - i.push({ 367 - type: 'error', 368 - key: 'profilesError', 369 - message: _(msg`Failed to load suggested follows`), 370 - error: cleanError(profilesError), 371 - }) 372 - } else { 373 - if (profiles !== undefined) { 374 - if (profiles.pages.length > 0 && moderationOpts) { 375 - // Currently the responses contain duplicate items. 376 - // Needs to be fixed on backend, but let's dedupe to be safe. 377 - let seen = new Set() 378 - const profileItems: ExploreScreenItems[] = [] 379 - for (const page of profiles.pages) { 380 - for (const actor of page.actors) { 381 - if (!seen.has(actor.did) && !actor.viewer?.following) { 382 - seen.add(actor.did) 383 - profileItems.push({ 384 - type: 'profile', 385 - key: actor.did, 386 - profile: actor, 387 - recId: page.recId, 388 - }) 389 - } 359 + } else { 360 + if (profiles !== undefined) { 361 + if (profiles.pages.length > 0 && moderationOpts) { 362 + // Currently the responses contain duplicate items. 363 + // Needs to be fixed on backend, but let's dedupe to be safe. 364 + let seen = new Set() 365 + const profileItems: ExploreScreenItems[] = [] 366 + for (const page of profiles.pages) { 367 + for (const actor of page.actors) { 368 + if (!seen.has(actor.did) && !actor.viewer?.following) { 369 + seen.add(actor.did) 370 + profileItems.push({ 371 + type: 'profile', 372 + key: actor.did, 373 + profile: actor, 374 + recId: page.recId, 375 + }) 390 376 } 391 377 } 378 + } 392 379 393 - if (profileItems.length === 0) { 394 - if (!hasNextProfilesPage) { 395 - // no items! remove the header 396 - i.pop() 397 - } 398 - } else { 399 - i.push(...profileItems) 400 - } 401 - if (hasNextProfilesPage) { 402 - i.push({ 403 - type: 'loadMore', 404 - key: 'loadMoreProfiles', 405 - message: _(msg`Load more suggested accounts`), 406 - isLoadingMore: isLoadingMoreProfiles, 407 - onLoadMore: onLoadMoreProfiles, 408 - }) 380 + if (profileItems.length === 0) { 381 + if (!hasNextProfilesPage) { 382 + // no items! remove the header 383 + i.pop() 409 384 } 410 385 } else { 411 - console.log('no pages') 386 + i.push(...profileItems) 387 + } 388 + if (hasNextProfilesPage) { 389 + i.push({ 390 + type: 'loadMore', 391 + key: 'loadMoreProfiles', 392 + message: _(msg`Load more suggested accounts`), 393 + isLoadingMore: isLoadingMoreProfiles, 394 + onLoadMore: onLoadMoreProfiles, 395 + }) 412 396 } 413 397 } else { 414 - i.push({type: 'profilePlaceholder', key: 'profilePlaceholder'}) 398 + console.log('no pages') 415 399 } 400 + } else { 401 + i.push({type: 'profilePlaceholder', key: 'profilePlaceholder'}) 416 402 } 417 403 } 404 + return i 405 + }, [ 406 + profiles, 407 + _, 408 + canShowSuggestedProfiles, 409 + hasNextProfilesPage, 410 + isLoadingMoreProfiles, 411 + moderationOpts, 412 + onLoadMoreProfiles, 413 + profilesError, 414 + ]) 415 + const suggestedFeedsModule = useMemo(() => { 416 + const i: ExploreScreenItems[] = [] 417 + i.push({ 418 + type: 'header', 419 + key: 'suggested-feeds-header', 420 + title: _(msg`Discover Feeds`), 421 + icon: ListSparkle, 422 + searchButton: { 423 + label: _(msg`Search for more feeds`), 424 + metricsTag: 'suggestedFeeds', 425 + tab: 'feed', 426 + }, 427 + }) 418 428 419 - const addSuggestedFeedsModule = () => { 420 - i.push({ 421 - type: 'header', 422 - key: 'suggested-feeds-header', 423 - title: _(msg`Discover Feeds`), 424 - icon: ListSparkle, 425 - searchButton: { 426 - label: _(msg`Search for more feeds`), 427 - metricsTag: 'suggestedFeeds', 428 - tab: 'feed', 429 - }, 430 - }) 431 - 432 - if (feeds && preferences) { 433 - // Currently the responses contain duplicate items. 434 - // Needs to be fixed on backend, but let's dedupe to be safe. 435 - let seen = new Set() 436 - const feedItems: ExploreScreenItems[] = [] 437 - for (const page of feeds.pages) { 438 - for (const feed of page.feeds) { 439 - if (!seen.has(feed.uri)) { 440 - seen.add(feed.uri) 441 - feedItems.push({ 442 - type: 'feed', 443 - key: feed.uri, 444 - feed, 445 - }) 446 - } 429 + if (feeds && preferences) { 430 + // Currently the responses contain duplicate items. 431 + // Needs to be fixed on backend, but let's dedupe to be safe. 432 + let seen = new Set() 433 + const feedItems: ExploreScreenItems[] = [] 434 + for (const page of feeds.pages) { 435 + for (const feed of page.feeds) { 436 + if (!seen.has(feed.uri)) { 437 + seen.add(feed.uri) 438 + feedItems.push({ 439 + type: 'feed', 440 + key: feed.uri, 441 + feed, 442 + }) 447 443 } 448 444 } 445 + } 449 446 450 - // feeds errors can occur during pagination, so feeds is truthy 451 - if (feedsError) { 452 - i.push({ 453 - type: 'error', 454 - key: 'feedsError', 455 - message: _(msg`Failed to load suggested feeds`), 456 - error: cleanError(feedsError), 457 - }) 458 - } else if (preferencesError) { 459 - i.push({ 460 - type: 'error', 461 - key: 'preferencesError', 462 - message: _(msg`Failed to load feeds preferences`), 463 - error: cleanError(preferencesError), 464 - }) 447 + // feeds errors can occur during pagination, so feeds is truthy 448 + if (feedsError) { 449 + i.push({ 450 + type: 'error', 451 + key: 'feedsError', 452 + message: _(msg`Failed to load suggested feeds`), 453 + error: cleanError(feedsError), 454 + }) 455 + } else if (preferencesError) { 456 + i.push({ 457 + type: 'error', 458 + key: 'preferencesError', 459 + message: _(msg`Failed to load feeds preferences`), 460 + error: cleanError(preferencesError), 461 + }) 462 + } else { 463 + if (feedItems.length === 0) { 464 + if (!hasNextFeedsPage) { 465 + i.pop() 466 + } 465 467 } else { 466 - if (feedItems.length === 0) { 467 - if (!hasNextFeedsPage) { 468 - i.pop() 469 - } 468 + // This query doesn't follow the limit very well, so the first press of the 469 + // load more button just unslices the array back to ~10 items 470 + if (!hasPressedLoadMoreFeeds) { 471 + i.push(...feedItems.slice(0, 3)) 470 472 } else { 471 - // This query doesn't follow the limit very well, so the first press of the 472 - // load more button just unslices the array back to ~10 items 473 - if (!hasPressedLoadMoreFeeds) { 474 - i.push(...feedItems.slice(0, 3)) 475 - } else { 476 - i.push(...feedItems) 477 - } 478 - } 479 - if (hasNextFeedsPage) { 480 - i.push({ 481 - type: 'loadMore', 482 - key: 'loadMoreFeeds', 483 - message: _(msg`Load more suggested feeds`), 484 - isLoadingMore: isLoadingMoreFeeds, 485 - onLoadMore: onLoadMoreFeeds, 486 - }) 473 + i.push(...feedItems) 487 474 } 488 475 } 489 - } else { 490 - if (feedsError) { 476 + if (hasNextFeedsPage) { 491 477 i.push({ 492 - type: 'error', 493 - key: 'feedsError', 494 - message: _(msg`Failed to load suggested feeds`), 495 - error: cleanError(feedsError), 478 + type: 'loadMore', 479 + key: 'loadMoreFeeds', 480 + message: _(msg`Load more suggested feeds`), 481 + isLoadingMore: isLoadingMoreFeeds, 482 + onLoadMore: onLoadMoreFeeds, 496 483 }) 497 - } else if (preferencesError) { 498 - i.push({ 499 - type: 'error', 500 - key: 'preferencesError', 501 - message: _(msg`Failed to load feeds preferences`), 502 - error: cleanError(preferencesError), 503 - }) 504 - } else { 505 - i.push({type: 'feedPlaceholder', key: 'feedPlaceholder'}) 506 484 } 507 485 } 508 - } 509 - 510 - const addSuggestedStarterPacksModule = () => { 511 - i.push({ 512 - type: 'header', 513 - key: 'suggested-starterPacks-header', 514 - title: _(msg`Starter Packs`), 515 - icon: StarterPack, 516 - }) 517 - 518 - if (isLoadingSuggestedSPs) { 519 - Array.from({length: 3}).forEach((_, index) => 520 - i.push({ 521 - type: 'starterPackSkeleton', 522 - key: `starterPackSkeleton-${index}`, 523 - }), 524 - ) 525 - } else if (suggestedSPsError || !suggestedSPs) { 526 - // just get rid of the section 527 - i.pop() 486 + } else { 487 + if (feedsError) { 488 + i.push({ 489 + type: 'error', 490 + key: 'feedsError', 491 + message: _(msg`Failed to load suggested feeds`), 492 + error: cleanError(feedsError), 493 + }) 494 + } else if (preferencesError) { 495 + i.push({ 496 + type: 'error', 497 + key: 'preferencesError', 498 + message: _(msg`Failed to load feeds preferences`), 499 + error: cleanError(preferencesError), 500 + }) 528 501 } else { 529 - suggestedSPs.starterPacks.map(s => { 530 - i.push({ 531 - type: 'starterPack', 532 - key: s.uri, 533 - view: s, 534 - }) 535 - }) 502 + i.push({type: 'feedPlaceholder', key: 'feedPlaceholder'}) 536 503 } 537 504 } 505 + return i 506 + }, [ 507 + feeds, 508 + _, 509 + feedsError, 510 + hasNextFeedsPage, 511 + hasPressedLoadMoreFeeds, 512 + isLoadingMoreFeeds, 513 + onLoadMoreFeeds, 514 + preferences, 515 + preferencesError, 516 + ]) 517 + const suggestedStarterPacksModule = useMemo(() => { 518 + const i: ExploreScreenItems[] = [] 519 + i.push({ 520 + type: 'header', 521 + key: 'suggested-starterPacks-header', 522 + title: _(msg`Starter Packs`), 523 + icon: StarterPack, 524 + }) 538 525 539 - const addFeedPreviews = () => { 540 - i.push(...feedPreviewSlices) 541 - if (isFetchingNextPageFeedPreviews) { 526 + if (isLoadingSuggestedSPs) { 527 + Array.from({length: 3}).forEach((__, index) => 542 528 i.push({ 543 - type: 'preview:loading', 544 - key: 'preview-loading-more', 529 + type: 'starterPackSkeleton', 530 + key: `starterPackSkeleton-${index}`, 531 + }), 532 + ) 533 + } else if (suggestedSPsError || !suggestedSPs) { 534 + // just get rid of the section 535 + i.pop() 536 + } else { 537 + suggestedSPs.starterPacks.map(s => { 538 + i.push({ 539 + type: 'starterPack', 540 + key: s.uri, 541 + view: s, 545 542 }) 546 - } 543 + }) 544 + } 545 + return i 546 + }, [suggestedSPs, _, isLoadingSuggestedSPs, suggestedSPsError]) 547 + const feedPreviewsModule = useMemo(() => { 548 + const i: ExploreScreenItems[] = [] 549 + i.push(...feedPreviewSlices) 550 + if (isFetchingNextPageFeedPreviews) { 551 + i.push({ 552 + type: 'preview:loading', 553 + key: 'preview-loading-more', 554 + }) 547 555 } 556 + return i 557 + }, [feedPreviewSlices, isFetchingNextPageFeedPreviews]) 548 558 549 - // Dynamic module ordering 559 + const isNewUser = guide?.guide === 'follow-10' && !guide.isComplete 560 + const items = useMemo<ExploreScreenItems[]>(() => { 561 + const i: ExploreScreenItems[] = [] 550 562 551 - addTopBorder() 563 + // Dynamic module ordering 552 564 553 - if (guide?.guide === 'follow-10' && !guide.isComplete) { 554 - addSuggestedFollowsModule() 555 - addSuggestedStarterPacksModule() 556 - addTrendingTopicsModule() 565 + i.push(topBorder) 566 + if (isNewUser) { 567 + i.push(...suggestedFollowsModule) 568 + i.push(...suggestedStarterPacksModule) 569 + i.push(trendingTopicsModule) 557 570 } else { 558 - addTrendingTopicsModule() 559 - addSuggestedFollowsModule() 560 - addSuggestedStarterPacksModule() 571 + i.push(trendingTopicsModule) 572 + i.push(...suggestedFollowsModule) 573 + i.push(...suggestedStarterPacksModule) 561 574 } 562 - 563 575 if (gate('explore_show_suggested_feeds')) { 564 - addSuggestedFeedsModule() 576 + i.push(...suggestedFeedsModule) 565 577 } 566 - 567 - addFeedPreviews() 578 + i.push(...feedPreviewsModule) 568 579 569 580 return i 570 581 }, [ 571 - _, 572 - profiles, 573 - feeds, 574 - preferences, 575 - onLoadMoreFeeds, 576 - onLoadMoreProfiles, 577 - isLoadingMoreProfiles, 578 - isLoadingMoreFeeds, 579 - profilesError, 580 - feedsError, 581 - preferencesError, 582 - hasNextProfilesPage, 583 - hasNextFeedsPage, 584 - guide, 582 + topBorder, 583 + isNewUser, 584 + suggestedFollowsModule, 585 + suggestedStarterPacksModule, 586 + suggestedFeedsModule, 587 + trendingTopicsModule, 588 + feedPreviewsModule, 585 589 gate, 586 - moderationOpts, 587 - hasPressedLoadMoreFeeds, 588 - suggestedSPs, 589 - isLoadingSuggestedSPs, 590 - suggestedSPsError, 591 - feedPreviewSlices, 592 - isFetchingNextPageFeedPreviews, 593 - canShowSuggestedProfiles, 594 590 ]) 595 591 596 592 const renderItem = useCallback( ··· 709 705 case 'profilePlaceholder': { 710 706 return ( 711 707 <> 712 - {Array.from({length: 3}).map((_, index) => ( 708 + {Array.from({length: 3}).map((__, i) => ( 713 709 <View 714 710 style={[ 715 711 a.px_lg, ··· 717 713 a.border_t, 718 714 t.atoms.border_contrast_low, 719 715 ]} 720 - key={index}> 716 + key={i}> 721 717 <ProfileCard.Outer> 722 718 <ProfileCard.Header> 723 719 <ProfileCard.AvatarPlaceholder />