Skip to content

Drop legacy queue + player slices: single playback slice#14186

Merged
dylanjeffers merged 2 commits into
mainfrom
fully-drop-legacy-queue
Apr 28, 2026
Merged

Drop legacy queue + player slices: single playback slice#14186
dylanjeffers merged 2 commits into
mainfrom
fully-drop-legacy-queue

Conversation

@dylanjeffers
Copy link
Copy Markdown
Contributor

Summary

  • Deletes the legacy queue and player slices and folds everything into a single playback slice
  • Combines the old web/common/store/queue/sagas.ts (494 lines) + web/common/store/player/sagas.ts (~570 lines) into one web/common/store/playback/sagas.ts
  • Rewires ~70 consumers from queueActions/queueSelectors/playerActions/playerSelectorsplaybackActions/playbackSelectors

The previous PR (#14178) introduced playback that shadowed queue and player for compat. This finishes the job — playback becomes 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.ts
  • packages/web/src/common/store/player/sagas.ts + errorSagas.ts
  • 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, play({uid})-style, addToQueue/removeByUid/reorder, setPlayingState, playSucceeded, reset/resetSucceeded, incrementCounter, error, queueAutoplay.
  • selectors.tsgetCurrentPlaybackTrack, 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).

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 recommendation append), PLAYBACK_PLAY analytics, cache subscribe on enqueue, and pagination via querySource.queryKey.

Migration

~70 consumer files updated. Action-name renames the new shape requires:

Legacy New
queueActions.add({entries}) playbackActions.addToQueue({tracks: PlaybackTrack[]})
queueActions.clear playbackActions.clearQueue
queueActions.remove({uid}) playbackActions.removeByUid({uid})
queueActions.repeat/shuffle playbackActions.setRepeat/setShuffle
queueActions.updateIndex playbackActions.setIndex
playerActions.seek playbackActions.seekTo
playerActions.incrementCount playbackActions.incrementCounter
playerSelectors.getUid playbackSelectors.getUid
queueSelectors.getSource playbackSelectors.getCurrentSource
queueSelectors.getPlayerBehavior playbackSelectors.getCurrentPlayerBehavior

PlaybackTrack.legacyUid field 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

  • packages/common, packages/web, packages/mobile typecheck clean
  • 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.
  • 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. Pause freezes position; resume advances. 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

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>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 28, 2026

⚠️ No Changeset found

Latest commit: 06c8f4d

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

…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>
@github-actions
Copy link
Copy Markdown
Contributor

🌐 Web preview ready

Preview URL: https://audius-web-preview-pr-14186.audius.workers.dev

Unique preview for this PR (deployed from this branch).
Workflow run

@dylanjeffers dylanjeffers merged commit f8cbf13 into main Apr 28, 2026
14 checks passed
@dylanjeffers dylanjeffers deleted the fully-drop-legacy-queue branch April 28, 2026 19:47
@dylanjeffers dylanjeffers mentioned this pull request Apr 29, 2026
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>
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