Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions packages/web/src/components/lineup/TrackLineup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
// Approximate rendered heights of a TrackTile in different variants — used to
// compute how many skeletons to render so the bottom-of-list "loading window"
// fills the threshold area instead of leaving the user staring at a frozen
// last entry while the next page is in flight.
const APPROX_TILE_HEIGHT_LARGE = 124
const APPROX_TILE_HEIGHT_SMALL = 80
// last entry while the next page is in flight. Large tile = 144px body +
// 24px mb='l' + 16px parent gap='m' ≈ 184. Small tile is rendered in a row
// layout, so a smaller estimate is fine.
const APPROX_TILE_HEIGHT_LARGE = 184
const APPROX_TILE_HEIGHT_SMALL = 96

const { getPlaying: getPlayerPlaying } = playbackSelectors
const { makeGetCurrent } = playbackSelectors
Expand Down Expand Up @@ -254,7 +256,11 @@
return () => observer.disconnect()
}, [externalScrollParent])

const effectiveLoadMoreThreshold = scrollParentHeight
// Base threshold = the "remaining content" buffer we want before the bottom
// of *loaded tiles*. We add the persistent skeleton area to this below so
// react-infinite-scroller's bottom-of-content check stays anchored to the
// loaded tiles rather than the skeleton padding.
const baseLoadMoreThreshold = scrollParentHeight
? scrollParentHeight * LOAD_MORE_VIEWPORTS
: loadMoreThreshold

Expand Down Expand Up @@ -359,19 +365,28 @@
const isEmpty =
tiles.length === 0 && !isFetching && !isInitialLoad && !isLoadMoreTriggered

// While a page is in flight we render skeletons below the loaded tiles. They
// need to fill ~one threshold's worth of vertical space so the bottom of the
// list feels populated even when the user scrolls into the trigger area
// faster than the network can return. `pageSize` is too small on its own
// (e.g. trending uses 4) so we floor by a viewport-derived count.
// Persistent skeletons render below the loaded tiles whenever more pages are
// available. They fill ~one threshold's worth of vertical space so the bottom
// of the list feels populated even when the user scrolls into the trigger
// area faster than the network can return. `pageSize` is too small on its own
// (e.g. trending uses 4) so we floor by a viewport-derived count. Keeping
// them mounted across fetch cycles (rather than gating on isFetching) means
// scrollHeight only ever grows monotonically, which keeps the scroll thumb
// stable instead of snapping wider/narrower each page.
const approxTileHeight = isSmallTrackTile
? APPROX_TILE_HEIGHT_SMALL
: APPROX_TILE_HEIGHT_LARGE
const fillCount = Math.ceil(effectiveLoadMoreThreshold / approxTileHeight)
const fillCount = Math.ceil(baseLoadMoreThreshold / approxTileHeight)
const loadingSkeletonCount = Math.min(
Math.max(0, maxEntries - tiles.length),
Math.max(pageSize, fillCount)
)
// The skeleton block adds to scrollHeight; compensate the threshold so the
// trigger fires when the user is within `LOAD_MORE_VIEWPORTS` of the bottom
// of *loaded tiles*, not the bottom of the skeleton padding.
const skeletonAreaHeight =
hasNextPage && tiles.length > 0 ? loadingSkeletonCount * approxTileHeight : 0

Check failure on line 388 in packages/web/src/components/lineup/TrackLineup.tsx

View workflow job for this annotation

GitHub Actions / Web Lint & Stylelint

Replace `·?·loadingSkeletonCount·*·approxTileHeight` with `⏎······?·loadingSkeletonCount·*·approxTileHeight⏎·····`
const effectiveLoadMoreThreshold = baseLoadMoreThreshold + skeletonAreaHeight

return (
<div
Expand Down
Loading