Skip to content

fix(mobile): rich CollectionTile in lineups (large artwork + track list + duration)#14428

Merged
dylanjeffers merged 2 commits into
mainfrom
feat/mobile-collection-tile-rich
May 30, 2026
Merged

fix(mobile): rich CollectionTile in lineups (large artwork + track list + duration)#14428
dylanjeffers merged 2 commits into
mainfrom
feat/mobile-collection-tile-rich

Conversation

@dylanjeffers
Copy link
Copy Markdown
Contributor

@dylanjeffers dylanjeffers commented May 30, 2026

Note: The first commit on this branch (131320e35d) took a UID-based approach. After review, that was the wrong shape — web's mobile CollectionTile doesn't use UIDs in the read path. The follow-up commit ac487f236d rewrites the data flow to mirror web exactly. Read the latest as the canonical change.

Mobile collection tiles in the feed lineup were rendering as a compact row (small 72×72 thumbnail beside title/artist, no track list, no duration) — the wrong shape compared to web's mobile view, which shows a card layout (large artwork, label, title, duration, 5-row numbered track list with "by ", "+N Tracks" overflow).

How web does it (the path we now mirror)

packages/web/src/components/track/mobile/CollectionTile.tsx:

const { data: collectionWithoutFallback } = useCollection(id)
const collection = getCollectionWithFallback(collectionWithoutFallback)
const tracks = useOrderedCollectionTracks(collectionWithoutFallback)

And the per-row TrackItem fetches its artist name separately:

const { data: trackOwnerName } = useUser(track?.owner_id, {
  select: (user) => user?.name
})

No UIDs anywhere — useOrderedCollectionTracks takes the collection and pulls tracks via useTracks(collection.trackIds) internally; it returns plain CollectionTrack[] (aka TrackMetadata[]), no user join. Mobile now does the same.

Root cause

After #14411 wired the new collection branch into the mobile TrackLineup, the existing useEnhancedCollectionTracks(uid) hook returned [] because the lineup didn't supply a UID. That left tracks.length === 0, so the sum duration was 0 (hiding behind a duration > 0 guard) and CollectionTileTrackList rendered an empty body. Compounded by LineupTileMetadata being a row-layout with a hardcoded 72×72 LineupTileArt.

Changes

packages/mobile/src/components/lineup-tile/CollectionTile.tsx

  • Drop the uid prop / destructure entirely.
  • Drop the custom useCollection(id, { select }) (web reads the full collection — mirror).
  • Replace useEnhancedCollectionTracks(uid ?? '') with useOrderedCollectionTracks(cachedCollection).
  • Bypass LineupTileMetadata for the collection variant; inline a card-style header: full-width square CollectionImage at SquareSizes.SIZE_480_BY_480, then PLAYLIST/ALBUM label, title row with duration on the right via formatLineupTileDuration(_, false, true) (renders "15m 32s"), artist link.
  • Pass isLoading={tracks.length === 0 && trackCount > 0} so CollectionTileTrackList renders skeleton rows during the fetch.
  • handlePress passes only id to togglePlay — the new playback slice's togglePlay already ignores uid and queues by id.

packages/mobile/src/components/lineup-tile/CollectionTileTrackList.tsx

  • Switch the track type from LineupTrack to CollectionTrack (TrackMetadata).
  • Mirror web's TrackItem exactly: fetch the artist name per row via useUser(track?.owner_id, { select: u => u?.name }) and render \by ${trackOwnerName ?? ''}`instead of reading the (no-longer-present)track.user?.name` join.

packages/mobile/src/components/lineup/TrackLineup.tsx

  • No UID needed for collections. Reverted the collectionUidFor callback, the uid field on CollectionEntry, and the uid prop on <CollectionTile>.

Non-lineup surfaces

The two other CollectionTile callers — chat-unfurled ChatMessagePlaylist.tsx and CollectionLineupCarousel — both pass collection and tracks explicitly or sit inside 343-wide columns. The card layout fits in both contexts; nothing else changed.

Verification

  • tsc --noEmit clean in packages/mobile and packages/common
  • Mobile manual: open Feed → For You, scroll until a playlist tile appears. Expect large artwork, PLAYLIST label, title, duration, numbered track rows (up to 5) with "by ", +N Tracks overflow, action buttons. Track-tile entries unchanged.
  • Mobile manual: same on the Latest (following feed) tab and on Profile → Reposts.
  • Mobile manual: chat-unfurled playlist in a DM still renders correctly.
  • Mobile manual: explore screen's collection carousels still render correctly.

🤖 Generated with Claude Code

…st + duration)

In the feed lineup, mobile's CollectionTile rendered as a compact row
(small 72×72 thumbnail beside title/artist, no track list, no duration)
— missing the rich card layout the web's mobile view shows for the same
data (large artwork, "PLAYLIST"/"ALBUM" label, title, duration, 5-row
numbered track list with "by <artist>", "+N Tracks" overflow).

Two root causes, both fixed:

1. **No UID was passed in**. After #14411 wired CollectionTile into
   TrackLineup, the entry was constructed with only the collection ID.
   `useEnhancedCollectionTracks` requires a UID to derive per-row track
   UIDs; called with `''`, it returns `[]`. With zero tracks, the sum
   `duration` is `0` (so the formatted text was hidden) and the
   `CollectionTileTrackList` body rendered empty.
   - TrackLineup: add a `Kind.COLLECTIONS` `uidFor` and include
     `uid: collectionUidFor(item.id)` on `CollectionEntry`; pass `uid`
     to `<CollectionTile>`.

2. **Wrong layout for the lineup context.** `LineupTileMetadata` is a
   row-layout (image beside title) with `LineupTileArt` hardcoded at
   72×72, and `LineupTileTopRight` hardcodes `isCollection={false}` (so
   duration is formatted with the wrong shape). Bypassed it for the
   collection variant and inlined a card header: full-width square
   480×480 `CollectionImage`, then `PLAYLIST`/`ALBUM` label, title row
   with duration on the right (`formatLineupTileDuration(_, false,
   true)` → "15m 32s"), then the artist link. Stats / track list /
   action buttons unchanged.

3. **Skeleton during fetch.** Pass
   `isLoading={tracks.length === 0 && expectedTrackCount > 0}` so
   `CollectionTileTrackList` renders skeleton rows while tracks are
   loading, instead of an empty block that looks like the old compact
   tile.

Chat unfurled collections and explore-screen carousel both already pass
`collection` and `tracks` (or use sufficiently wide layouts), so the
richer card sits inside their existing constraints fine.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

changeset-bot Bot commented May 30, 2026

⚠️ No Changeset found

Latest commit: ac487f2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

…tracks

Previous commit on this branch added a UID indirection to fetch the
collection's tracks via the existing `useEnhancedCollectionTracks(uid)`
hook. That was the wrong shape: web's mobile CollectionTile uses
`useOrderedCollectionTracks(collection)` driven directly by the
collection ID, with no UID anywhere in the read path. Mirror that exactly.

- `CollectionTile.tsx`: drop the `uid` prop / destructure. Drop the
  custom `useCollection(id, {select})` (web reads the full collection).
  Replace `useEnhancedCollectionTracks(uid ?? '')` with
  `useOrderedCollectionTracks(cachedCollection)`. Update `handlePress` to
  pass only `id` to `togglePlay` — there is no per-row UID to forward
  (the new playback slice in TrackLineup ignores `uid` anyway and queues
  by `id`).
- `CollectionTileTrackList.tsx`: switch the track type from
  `LineupTrack` to `CollectionTrack` (TrackMetadata). Mirror web's
  `TrackItem` exactly: fetch the artist name per row via
  `useUser(track?.owner_id, { select: u => u?.name })` instead of
  reading the (no-longer-present) `track.user?.name` join.
- `TrackLineup.tsx`: revert the previous commit's `collectionUidFor`
  and the `uid` field on `CollectionEntry` / on the `<CollectionTile>`
  call site. Pure ID flow now.

Same visual outcome as before (large 480 artwork, label, title +
duration row, artist link, 5-row track list with "by <artist>", "+N
Tracks" overflow, skeleton during fetch), but the data path now
matches web exactly. No UIDs anywhere in CollectionTile.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dylanjeffers dylanjeffers merged commit 17a4971 into main May 30, 2026
3 checks passed
@dylanjeffers dylanjeffers deleted the feat/mobile-collection-tile-rich branch May 30, 2026 00:59
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