Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions .changeset/prefetch-track-comments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@audius/common': patch
---

Add `usePrefetchTrackComments` and `usePrefetchTrackPageLineup`, hooks that warm a track's comment list and "more by / remixes / you might also like" lineup as early as possible (e.g. on track screen mount) so those sections render from cache instead of starting their own fetch only once they mount. Each keeps a live observer so the warmed data isn't evicted before the section mounts. `usePrefetchTrackComments` can fire from a bare trackId; `usePrefetchTrackPageLineup` still depends on the hero track + owner handle but starts the instant those resolve rather than waiting for the mobile screen-ready/animation gate.
41 changes: 37 additions & 4 deletions packages/common/src/api/tan-query/comments/useTrackComments.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useEffect } from 'react'

import { Id } from '@audius/sdk'
import {
GetTrackCommentsSortMethodEnum as CommentSortMethod,
Id
} from '@audius/sdk'
import {
useInfiniteQuery,
useIsMutating,
Expand Down Expand Up @@ -28,7 +31,13 @@ export type GetCommentsByTrackArgs = {
pageSize?: number
}

export const useTrackComments = (
/**
* Base infinite query for a track's root comment IDs. Keeps a live observer on
* the comment-list query (which uses `gcTime: 0`, so the data is evicted the
* moment no observer is mounted) and primes individual comment data into the
* cache. Shared by `useTrackComments` and `usePrefetchTrackComments`.
*/
const useTrackCommentsQuery = (
{
trackId,
sortMethod,
Expand All @@ -39,10 +48,9 @@ export const useTrackComments = (
const { audiusSdk } = useQueryContext()
const isMutating = useIsMutating()
const queryClient = useQueryClient()
const dispatch = useDispatch()
const { data: currentUserId } = useCurrentUserId()

const queryRes = useInfiniteQuery({
return useInfiniteQuery({
initialPageParam: 0,
getNextPageParam: (lastPage: ID[], pages) => {
if (lastPage?.length < pageSize) return undefined
Expand Down Expand Up @@ -78,6 +86,31 @@ export const useTrackComments = (
...options,
enabled: isMutating === 0 && options?.enabled !== false && !!trackId
})
}

/**
* Warms the track comment list as early as possible (e.g. on track screen
* mount, in parallel with the track fetch) so the comment section renders with
* data already in cache instead of starting its fetch only once it mounts.
*
* Uses the default `Top` sort and page size to match what the comment section
* requests by default, ensuring a cache hit. Keeps a live observer mounted so
* the `gcTime: 0` query isn't evicted before the comment section mounts.
*/
export const usePrefetchTrackComments = (trackId: ID | null | undefined) => {
useTrackCommentsQuery(
{ trackId: trackId as ID, sortMethod: CommentSortMethod.Top },
{ enabled: !!trackId }
)
}

export const useTrackComments = (
args: GetCommentsByTrackArgs,
options?: QueryOptions
) => {
const dispatch = useDispatch()

const queryRes = useTrackCommentsQuery(args, options)

const { error, data: commentIds } = queryRes

Expand Down
20 changes: 20 additions & 0 deletions packages/common/src/api/tan-query/lineups/useTrackPageLineup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,23 @@ export const useTrackPageLineup = (
queryKey
}
}

/**
* Warms the track page lineup ("more by", remixes, "you might also like") as
* early as possible so the lineup renders from cache instead of starting its
* fetch only once `TrackScreenLineup` mounts — which on mobile is gated behind
* the screen-ready / nav-animation delay in `ScreenSecondaryContent`.
*
* Unlike comments, this query genuinely depends on the hero track and its
* owner's handle (see the `enabled` guard in `useTrackPageLineup`), so it can't
* fire from a bare trackId before the track resolves. But hoisting it above the
* secondary content gate lets it fire the instant those deps are ready —
* overlapping the push transition instead of waiting for it to finish. Keeps a
* live observer so the warmed data is shared with `TrackScreenLineup`'s own
* query.
*/
export const usePrefetchTrackPageLineup = (
trackId: ID | null | undefined
) => {
useTrackPageLineup({ trackId })
}
23 changes: 22 additions & 1 deletion packages/mobile/src/screens/track-screen/TrackScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { useRef } from 'react'

import { useTrackByParams, useUser } from '@audius/common/api'
import {
useTrackByParams,
usePrefetchTrackComments,
usePrefetchTrackPageLineup,
useUser
} from '@audius/common/api'
import { Kind } from '@audius/common/models'
import { reachabilitySelectors } from '@audius/common/store'
import { makeStableUid } from '@audius/common/utils'
Expand Down Expand Up @@ -34,6 +39,22 @@ export const TrackScreen = () => {
const { data: fetchedTrack } = useTrackByParams(restParams)
const track = fetchedTrack ?? searchTrack

// Kick off the comments fetch as early as possible — on mount, in parallel
// with the track/user fetch — so the comment section renders from cache
// instead of starting its own fetch only once it mounts (gated behind the
// user fetch and the secondary-content gate below). Uses the trackId from
// route params when available so it can fire before the track resolves.
const paramTrackId =
'trackId' in restParams ? restParams.trackId : undefined
const trackId = paramTrackId ?? track?.track_id
usePrefetchTrackComments(track?.comments_disabled ? null : trackId)

// Warm the "more by / remixes / you might also like" lineup too. Unlike
// comments it can't fire from the bare trackId (it needs the hero track +
// owner handle), but hoisting it here lets it start as soon as those resolve
// instead of waiting for the ScreenSecondaryContent screen-ready gate.
usePrefetchTrackPageLineup(trackId)

const { data: user } = useUser(track?.owner_id)

if (!track || !user) {
Expand Down
Loading