Skip to content

[Mobile] Smooth out lineup infinite-scroll on Trending and Feed#14272

Merged
dylanjeffers merged 1 commit intomainfrom
dgg/mobile-lineup-scroll-smoothness
May 7, 2026
Merged

[Mobile] Smooth out lineup infinite-scroll on Trending and Feed#14272
dylanjeffers merged 1 commit intomainfrom
dgg/mobile-lineup-scroll-smoothness

Conversation

@dylanjeffers
Copy link
Copy Markdown
Contributor

Summary

The mobile TrackLineup infinite-scroll on Trending and Feed felt rough: scrolling to the bottom looked frozen for a beat, then late skeletons appeared, then tracks loaded. Three things were stacked on top of each other:

  • 100ms debounce on loadNextPage (useDebouncedCallback(..., 100)) — every load trigger waited a full debounce window before dispatching.
  • Skeletons gated on the parent's isFetching — they only rendered after loadNextPage() ran, RTK Query started the request, and isFetching propagated back through the parent. That's a multi-tick round-trip after the user already hit the bottom.
  • Duplicate scroll handlers — both a custom onScroll and onEndReached called the same debounced load.

This PR makes the scroll feedback synchronous:

  • Bump onEndReachedThreshold from 0.5 to 1 so the next page request fires when one full viewport of content remains below.
  • Drop the debounce and the duplicate onScroll handler — onEndReached already fires at the same threshold.
  • Add a local isLoadMoreTriggered flag set the same tick the scroll handler fires, so skeletons render on the very next frame instead of waiting for the parent's tanquery isFetching to flip. Cleared once new entries arrive or the parent finishes fetching.

Single load-more entry point now:

const handleLoadMore = useCallback(() => {
  if (!hasNextPage || isFetching || isLoadMoreTriggered) return
  if (!loadNextPage) return
  setIsLoadMoreTriggered(true)
  loadNextPage()
}, [hasNextPage, isFetching, isLoadMoreTriggered, loadNextPage])

Skeleton count consults both the parent state and the local trigger:

if (isFetching || isLoadMoreTriggered) {
  return Math.min(maxEntries, pageSize)
}

Test plan

  • Open Trending (Week / Month / All-Time) on iOS, scroll quickly — skeletons appear before reaching the bottom and the next page loads in.
  • Same on Android.
  • Open Feed, scroll fast through several pages — no perceptible "frozen at bottom" pause.
  • Verify pull-to-refresh still works on Trending / Feed.
  • Verify other TrackLineup consumers still paginate correctly (Profile Tracks/Reposts, Search Tracks, Track remixes, Listening History, Contest Submissions).
  • Verify the empty state still renders when a tab has no tracks.

🤖 Generated with Claude Code

The mobile `TrackLineup` previously triggered `loadNextPage` through a 100ms
debounce, then deferred skeleton rendering until the parent's tanquery
`isFetching` propagated back — so scrolling to the bottom looked frozen for
a beat before any feedback appeared. Bumps `onEndReachedThreshold` from 0.5
to a full viewport ahead, drops the debounce and the duplicate `onScroll`
handler, and adds a synchronous local "load triggered" flag so skeletons
flip on in the same tick the scroll handler fires.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

🦋 Changeset detected

Latest commit: a407633

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@audius/mobile Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dylanjeffers dylanjeffers merged commit 5a5a93f into main May 7, 2026
3 checks passed
@dylanjeffers dylanjeffers deleted the dgg/mobile-lineup-scroll-smoothness branch May 7, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant