[Web] Smooth out lineup infinite-scroll on Trending and Feed#14286
Open
raymondjacobson wants to merge 6 commits intomainfrom
Open
[Web] Smooth out lineup infinite-scroll on Trending and Feed#14286raymondjacobson wants to merge 6 commits intomainfrom
raymondjacobson wants to merge 6 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: 43dc8e6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
Contributor
🌐 Web preview readyPreview URL: https://audius-web-preview-pr-14286.audius.workers.dev Unique preview for this PR (deployed from this branch). |
Mirror the recent mobile fix (#14272) on the web TrackLineup. Two issues stacked at the bottom of tanquery-driven lineups: - The `react-infinite-scroller` threshold was a fixed 500px, so on tall monitors the user could blow past it before paint. - Skeletons rendered only once tanquery's `isFetching` flipped to true — a multi-tick round-trip after the scroll handler fires, long enough for the user to land on the literal bottom of the list with nothing there. This change: - Sizes the threshold to ~one viewport (clientHeight of the scroll parent, observed via ResizeObserver), matching mobile's `onEndReachedThreshold = 1`. - Adds a local `isLoadMoreTriggered` flag set the same tick the scroll handler fires, so skeletons render on the very next frame instead of waiting for `isFetching` to propagate. Cleared once new entries arrive or the parent finishes fetching. - Routes InfiniteScroll's `loadMore` through a single `handleLoadMore` guarded by the flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The first pass at smoothing infinite-scroll wasn't enough on desktop because two more things stacked on top of the late-skeleton problem: - A 1-viewport threshold isn't enough buffer for fast desktop scrolls (mouse fling, trackpad, PgDn). Bumped to 2 viewports so the next page request fires while there's still meaningful content below. - The skeleton count was `pageSize`, which is only 4 on Trending / Feed. At ~124px per desktop tile that's ~480px of skeletons — half a viewport — so the user could blow right through them and land on the literal bottom of the list while waiting for the network. Skeleton count is now `max(pageSize, ceil(threshold / approxTileHeight))` so the loading window always fills the threshold area. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups to the smoother infinite-scroll work, both visible while scrolling Trending: 1. Skeleton tiles in ordered lineups now reserve the order column. The desktop TrackTile previously hid the rank number and crown when `isLoading`, so the order column collapsed to ~0 width on a skeleton and the artwork shifted left when real data arrived. With the skeleton now rendering the index it's been told (`tiles.length + i`), the number / crown stay visible and the layout doesn't jump on load. 2. Skeleton tail is now constant while `hasNextPage`. Before, skeletons were gated on `isFetching || isLoadMoreTriggered`, so the page ballooned by ~one threshold's worth of skeletons when fetching, then shrank back when the page resolved. On a fast scroll deep into that shrinking region, the browser had to clamp `scrollTop`, which felt like the page "bouncing" mid-scroll. Keeping a constant skeleton tail until the end of the lineup means the scroll height only ever grows (smoothly, by `pageSize` tile-heights per resolved page) until `hasNextPage` flips to false at end-of-lineup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shared default (`TRENDING_LOAD_MORE_PAGE_SIZE` / `FEED_LOAD_MORE_PAGE_SIZE = 4`) is tuned for mobile viewports and slower touch scrolling. On desktop a fast trackpad / wheel scroll covers ~2 viewports per second, but each 4-track page only adds ~480px of real content — so successive load-mores can't keep up and the lineup visibly lags behind the user even with the synchronous skeleton tail. Override at the desktop call sites only, leaving the shared constant (and mobile's behavior) untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9899823 to
bf5237c
Compare
…y swap
Two follow-ups:
1. Skeleton keys are now `skeleton-${absolutePosition}` instead of
`skeleton-${localIndex}`. Before, the same DOM node (e.g. the first
skeleton, key `skeleton-0`) was reused across each page-load
transition with a different `index` prop — so its rendered order
number changed (e.g. 11 → 21 → 31), which read as the numbers
visibly jumping during scroll. Keying by absolute position lets a
skeleton DOM node keep displaying the same number; pages mount new
skeletons at the new tail and unmount the ones that were replaced
by real tiles.
2. Switching Trending category (Tracks / Underground / Winners) now
resets the scroll parent to top. The outer `mainContent` scroller
keeps its position by default, so swapping lineups underneath would
leave the new category mid-scroll. Wrap `setCategory` to call
`containerRef.current.scrollTo(0, 0)` first.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Web port of the recent mobile fix in apps#14272. The tanquery-driven
TrackLineup(Trending, Feed, Profile Tracks/Reposts, Search Tracks, Listening History, Remixes, Contest Submissions) felt rough at the bottom of a page: scrolling fast, you'd hit the literal end of the list with nothing visible, then late skeletons would appear, then tracks. Two things stacked:react-infinite-scroller. On tall monitors the user could blow past it before the next page even started loading.isFetching— they only painted after the scroll handler →loadNextPage→fetchNextPage→ tanquery state round-trip. That's a multi-tick gap after the user already reached the bottom.This PR:
clientHeight, tracked with aResizeObserver), matching mobile'sonEndReachedThreshold = 1. Falls back to 800px until the parent is measured.isLoadMoreTriggeredflag set the same tick the scroll handler fires, so skeletons render on the very next frame instead of waiting forisFetchingto flip. Cleared when entries arrive or the parent finishes fetching.loadMorethrough a singlehandleLoadMoreguarded by the flag, so we never double-trigger.Single load-more entry point now:
Skeleton render conditions consult both the parent state and the local trigger:
Test plan
useWindowpath) — same smooth behavior.ResizeObserver.TrackLineupconsumers still paginate correctly (Profile Tracks / Reposts, Search Tracks, Track remixes, Listening History, Contest Submissions).🤖 Generated with Claude Code