Drop legacy queue + player slices: single playback slice#14186
Merged
Conversation
The previous PR (#14178) introduced a `playback` slice that shadowed the legacy `queue` and `player` slices for compat. This commit finishes the job — `queue` and `player` are deleted, and `playback` becomes the single source of truth for the queue, transport, and audio-engine state. ## Why Two slices for one concern is exactly the legacy complexity we want gone. `queue` owned ordering + intent, `player` owned the audio engine, and the old design needed a saga whose only job was keeping them in sync. `playback` already duplicated most of the player state (`playing`, `buffering`, `seek`, `playbackRate`, `previewing`); folding the rest in removes the shadow saga and cuts the playback surface roughly in half. ## What's gone - `packages/common/src/store/queue/` (slice + selectors + types + tests) - `packages/common/src/store/player/` (slice + selectors + types + utils + sagas) - `packages/web/src/common/store/queue/sagas.ts` (494 lines) - `packages/web/src/common/store/player/sagas.ts` + `errorSagas.ts` (~570 lines) - `state.queue` and `state.player` from the root reducer + types ## What's new — `packages/common/src/store/playback/` - `slice.ts` — owns the queue (`PlaybackTrack[]`), index, `playingUid`/ `playingTrackId`, `playing`/`buffering`/`previewing`, `seek`/ `seekCounter`/`counter`, `playbackRate`, `repeat`/`shuffle`/ `shuffleOrder`/`shuffleIndex`, `querySource`, `retries`, `overshot`/`undershot`. New actions: `set`, `setIndex`, `playByUid`- style `play({uid})`, `addToQueue`/`removeByUid`/`reorder`, `setPlayingState`, `playSucceeded`, `reset`/`resetSucceeded`, `incrementCounter`, `error`, `queueAutoplay`. - `selectors.ts` — full surface: `getCurrentPlaybackTrack`, `getCurrentTrackId`, `getCurrentSource`, `getCurrentEntryUid`, `getCurrentPlayerBehavior`, `getCollectionId`, `makeGetCurrent`, `getOrder` (Queueable adapter for the mobile `AudioPlayer`), `getUid`/`getTrackId`/`getPlaying`/`getBuffering`/`getCounter`/ `getPlaybackRate`/`getSeek`/etc. - `types.ts` — owns `PlaybackTrack`, `PlaybackQuerySource`, `PlaybackState`, plus the formerly-queue/player types (`QueueSource`, `Queueable`, `QueueItem`, `RepeatMode`, `PlayerBehavior`, `PlaybackRate`, `PLAYBACK_RATE_LS_KEY`, `playbackRateValueMap`). - `sagas.ts` (common) — initial-rate restore + persist-rate to LS. - `utils.ts` — `calculatePlayerBehavior`. ## Web-side engine — `packages/web/src/common/store/playback/sagas.ts` Combines the old player + queue sagas into one. Handles audio loading via the `audioPlayer` service (HTML on web, RNTrackPlayer shim on mobile), cascading mirror retries, audio-element listener bridge, buffering / error channels, listen counting, queue-driven `playCurrent` on `playFrom`/`playTrackAt`/`next`/`previous`/`togglePlay`, skip-deleted/owner-deactivated/locked tracks, `queueAutoplay` (genre- based recommendations append), `PLAYBACK_PLAY` analytics, cache subscribe on enqueue, and pagination via `querySource.queryKey`. ## Migration ~70 consumer files across web, mobile, and common — `queueActions/ queueSelectors/playerActions/playerSelectors` → `playbackActions/ playbackSelectors` with the action-name renames the new shape requires (`add` → `addToQueue` with `tracks: PlaybackTrack[]`, `clear` → `clearQueue`, `remove` → `removeByUid`, `repeat`/`shuffle` → `setRepeat`/`setShuffle`, `seek` → `seekTo`, `updateIndex` → `setIndex`, `incrementCount` → `incrementCounter`, `playerSelectors.getUid` → `playbackSelectors.getUid`, etc.). The `legacyUid` field on `PlaybackTrack` is renamed to `uid`. ## Compat preserved `PlaybackTrack.uid` matches what the rendered tile uses, so tile- highlight comparisons (`playingUid === entry.uid`) and the desktop `PlaylistLibrary` "currently playing source" highlight keep working. The mobile `AudioPlayer` reads from a `getOrder` Queueable adapter on top of `playback.queue`, so RNTrackPlayer keeps driving exactly as before. ## Test plan - [x] `packages/common`, `packages/web`, `packages/mobile` typecheck clean - [x] **Web (localhost:3002, @DuranDylan)**: Trending tile play → audio loads, PlayBar populates, tile speaker icon lights up. Next button advances, previous button returns. Library / SAVED_TRACKS row click loads + plays with the correct source. Pause / resume flips engine state. No console errors. - [x] **iOS Simulator (iPhone 17 Pro)**: Trending tile play → mini- player + NowPlayingDrawer populate, position counter advances (0:13 → 0:37 → 1:01 with real audio). Next loads new track (0:13 immediately). Pause freezes position; resume advances (1:56 → 2:14). Shuffle button toggled to active (purple). Repeat button cycled state. - [ ] Reviewer: smoke test on your own stage/prod to confirm end-to-end playback and that nothing else regressed. ## Scope Touched: 100 files, +4014 / -3280. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
…removal Eslint pickup after the slice merge — 7 unused-import errors in `web/common/store/playback/sagas.ts` (`queryCollection`, `queryTrackByUid`, `ID`, `PlayerBehavior`, `getCurrentEntryUid`) and `pages/pick-winners-page/PickWinnersPage.tsx` (`clear`, `playAction` were left over from the pre-`playFrom` refactor of `handlePlay`). Plus prettier auto-collapsed a bunch of single-symbol multi-line imports that the dedupe pass had left expanded. No behavior changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
🌐 Web preview readyPreview URL: https://audius-web-preview-pr-14186.audius.workers.dev Unique preview for this PR (deployed from this branch). |
7 tasks
dylanjeffers
added a commit
that referenced
this pull request
Apr 30, 2026
## Summary
Strips the per-queue-entry **UID** concept from the playback module
entirely, replacing it with `(queue index, trackId)`. Duplicates in the
queue are disambiguated by index; tile-highlight comparisons fall back
to trackId-only, with `collectionId` / `source` as the disambiguator for
the same track in different rendering contexts.
This is the foundation pass. About half the consumer fixups are also in
this PR — the rest (~15 files, mechanical rewrites) are listed under
**Remaining Work** below. **The build will not typecheck until those are
done.**
## What changed (foundation — complete)
### Playback module (`packages/common/src/store/playback/`)
- **`types.ts`** — drop `uid?: UID` from `PlaybackTrack` and
`Queueable`; add `collectionId?: ID` for analytics.
- **`slice.ts`** — replace `playingUid` state with `playingIndex`; drop
`removeByUid` reducer; change `reorder` to take an index array
(`orderedIndices`) instead of a uid array; drop `uid` from `play` /
`set` / `playSucceeded` payloads; `set` now requires `index`.
- **`selectors.ts`** — drop `getUid`, `getCurrentEntryUid`,
`getUidInQueue`, `getOrder`; add `getPlayingIndex`; `getCollectionId`
now reads `PlaybackTrack.collectionId` instead of parsing it out of a
uid string; `makeGetCurrent()` returns `{ trackId, index, source }`.
### Web saga (`packages/web/src/common/store/playback/sagas.ts`)
- Drop `makeUid` import + usage in `playCurrent` and `queueAutoplay`.
- Plumb `index` (from `getPlaybackIndex`) through `playSucceeded`
instead of `uid`.
- Drop `getUid` from the reset-after-autoplay branch — only `getTrackId`
is needed.
### Mobile AudioPlayer
(`packages/mobile/src/components/audio/AudioPlayer.tsx`)
- Switch the RNTrackPlayer diffing loop from `queueTrackUids: string[]`
(compared with `isEqual`) to `queueTrackIds: ID[]` — duplicates are
still distinguished position-by-position.
- Replace `previousUid && !uid` stop-detection with
`previousPlayingTrackId && !playingTrackId`.
- `updatePlayerInfo({ trackId, uid })` → `updatePlayerInfo({ trackId,
index })`.
### Chat playback (`packages/common/src/hooks/chats/useTrackPlayer.ts`)
- `useToggleTrack` and `usePlayTrack` no longer take a uid; the "is this
tile playing" check is now `(currentQueueItem.trackId === id &&
currentQueueItem.source === source)`.
- **Behavior change:** if the same trackId appears in two messages of
the same chat thread, both tiles will highlight together when that track
plays. (Today they're disambiguated by uid.) Acceptable for chat UX.
### Data layer
- `useCollectionTracksWithUid` → `useOrderedCollectionTracks`;
`CollectionTrackWithUid` → plain `TrackMetadata` re-exported as
`CollectionTrack`. The hook no longer rebuilds per-collection uids; it
just returns the tracks in playlist order.
### Consumers also fixed up
- `TrackListItem` (desktop), `TrackTile` (desktop),
`components/track/types.ts` — `togglePlay(uid, trackId)` →
`togglePlay(trackId, index?)`; `isActive = uid === playingUid` →
`isActive = id === playingTrackId`.
- `PlayBarProvider`, `PlayBar` (desktop + mobile), `SocialActions` —
drop uid selectors and props.
- `CookieBanner`, `usePlaylistPlayingStatus` — switch to `getHasTrack` /
`collectionId === id`.
- `feed-page/types.ts`, `trending-page/types.ts` — drop `UID` from prop
signatures.
## Remaining Work (not in this PR — build will not typecheck until done)
These files still reference removed selectors (`getUid`, `getOrder`,
`getCurrentEntryUid`, `getUidInQueue`), removed types
(`CollectionTrackWithUid`), or build queue entries with `.uid` fields.
Each needs the same shape of rewrite — replace uid comparisons with
`(trackId, source, collectionId)` and pass `(trackId, index)` through
`togglePlay` chains. The legacy queue removal (#14186) is the model.
**Web:**
- `packages/web/src/pages/library-page/hooks/useLibraryPage.ts` (~887
lines, deeply uid-integrated)
-
`packages/web/src/pages/library-page/components/mobile/LibraryPage.tsx`
- `packages/web/src/pages/collection-page/useCollectionPage.ts`
-
`packages/web/src/pages/collection-page/components/mobile/CollectionPage.tsx`
- `packages/web/src/pages/pick-winners-page/PickWinnersPage.tsx`
-
`packages/web/src/pages/fan-club-detail-page/components/FanClubFeedSection.tsx`
- `packages/web/src/pages/visualizer/VisualizerProvider.tsx`
-
`packages/web/src/pages/search-explore-page/components/desktop/TileHelpers.tsx`
- `packages/web/src/components/now-playing/NowPlaying.tsx`
- `packages/web/src/components/lineup/TrackLineup.tsx`
-
`packages/web/src/components/track/{desktop,mobile}/CollectionTile.tsx`
-
`packages/web/src/components/track/mobile/{TrackTile,TrackListItem,TrackList}.tsx`
**Mobile:**
-
`packages/mobile/src/components/lineup-tile/{TrackTile,CollectionTile,CollectionTileTrackList}.tsx`
-
`packages/mobile/src/components/track-list/{TrackList,TrackListItem}.tsx`
-
`packages/mobile/src/components/now-playing-drawer/NowPlayingDrawer.tsx`
- `packages/mobile/src/components/lineup/TrackLineup.tsx`
- `packages/mobile/src/screens/chat-screen/ChatMessagePlaylist.tsx`
- `packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx`
-
`packages/mobile/src/screens/explore-screen/components/TrackTileCarousel.tsx`
## Test plan
- [ ] `npm run verify` clean once consumer fixups land
- [ ] Click-to-play on trending / feed / profile / collection / library
/ search pages
- [ ] Queue reorder via NowPlaying drawer (`reorder` now takes index
array)
- [ ] Same-track-in-two-messages chat playback (acceptable highlight
behavior change)
- [ ] Mobile RNTrackPlayer queue diffing on rapid next/prev presses
- [ ] PlaylistLibrary "currently playing" indicator
- [ ] Listen analytics + recommendation autoplay near end of queue
https://claude.ai/code/session_01GMw8KhWviP9LhvCMKB4nFu
---
_Generated by [Claude
Code](https://claude.ai/code/session_01GMw8KhWviP9LhvCMKB4nFu)_
---------
Co-authored-by: Claude <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
queueandplayerslices and folds everything into a singleplaybacksliceweb/common/store/queue/sagas.ts(494 lines) +web/common/store/player/sagas.ts(~570 lines) into oneweb/common/store/playback/sagas.tsqueueActions/queueSelectors/playerActions/playerSelectors→playbackActions/playbackSelectorsThe previous PR (#14178) introduced
playbackthat shadowedqueueandplayerfor compat. This finishes the job —playbackbecomes the single source of truth for queue, transport, and audio-engine state. Two slices for one concern was exactly the legacy complexity we wanted gone; the new design replaces a saga whose only job was keeping them in sync.What's gone
packages/common/src/store/queue/(slice + selectors + types + tests)packages/common/src/store/player/(slice + selectors + types + utils + sagas)packages/web/src/common/store/queue/sagas.tspackages/web/src/common/store/player/sagas.ts+errorSagas.tsstate.queueandstate.playerfrom the root reducer + typesWhat's new —
packages/common/src/store/playback/slice.ts— owns the queue (PlaybackTrack[]), index,playingUid/playingTrackId,playing/buffering/previewing,seek/seekCounter/counter,playbackRate,repeat/shuffle/shuffleOrder/shuffleIndex,querySource,retries,overshot/undershot. New actions:set,setIndex,play({uid})-style,addToQueue/removeByUid/reorder,setPlayingState,playSucceeded,reset/resetSucceeded,incrementCounter,error,queueAutoplay.selectors.ts—getCurrentPlaybackTrack,getCurrentTrackId,getCurrentSource,getCurrentEntryUid,getCurrentPlayerBehavior,getCollectionId,makeGetCurrent,getOrder(Queueable adapter for the mobileAudioPlayer),getUid/getTrackId/getPlaying/getBuffering/getCounter/getPlaybackRate/getSeek/etc.types.ts— ownsPlaybackTrack,PlaybackQuerySource,PlaybackState, plus the formerly-queue/player types (QueueSource,Queueable,QueueItem,RepeatMode,PlayerBehavior,PlaybackRate).Web-side engine —
packages/web/src/common/store/playback/sagas.tsCombines the old player + queue sagas into one. Handles audio loading via the
audioPlayerservice (HTML on web, RNTrackPlayer shim on mobile), cascading mirror retries, audio-element listener bridge, buffering / error channels, listen counting, queue-drivenplayCurrentonplayFrom/playTrackAt/next/previous/togglePlay, skip-deleted/owner-deactivated/locked tracks,queueAutoplay(genre-based recommendation append),PLAYBACK_PLAYanalytics, cache subscribe on enqueue, and pagination viaquerySource.queryKey.Migration
~70 consumer files updated. Action-name renames the new shape requires:
queueActions.add({entries})playbackActions.addToQueue({tracks: PlaybackTrack[]})queueActions.clearplaybackActions.clearQueuequeueActions.remove({uid})playbackActions.removeByUid({uid})queueActions.repeat/shuffleplaybackActions.setRepeat/setShufflequeueActions.updateIndexplaybackActions.setIndexplayerActions.seekplaybackActions.seekToplayerActions.incrementCountplaybackActions.incrementCounterplayerSelectors.getUidplaybackSelectors.getUidqueueSelectors.getSourceplaybackSelectors.getCurrentSourcequeueSelectors.getPlayerBehaviorplaybackSelectors.getCurrentPlayerBehaviorPlaybackTrack.legacyUidfield renamed touid.Compat preserved
PlaybackTrack.uidmatches what the rendered tile uses, so tile-highlight comparisons (playingUid === entry.uid) and the desktopPlaylistLibrary"currently playing source" highlight keep working. The mobileAudioPlayerreads from agetOrderQueueable adapter on top ofplayback.queue, so RNTrackPlayer keeps driving exactly as before.Test plan
packages/common,packages/web,packages/mobiletypecheck cleanScope
Touched: 100 files, +4014 / −3280.
🤖 Generated with Claude Code