fix(queue): pin queueServerId on auto-add paths so infinite + radio top-up refs resolve (#930)

* fix(queue): extend server-pin contract to auto-add paths

The infinite-queue top-up and radio top-up paths in nextAction.ts
read state.queueServerId directly inside their set callbacks. When
the queue was populated without a queue-replacing playTrack (single-
track enqueue from a SongRow + button, AdvancedSearch row, etc),
queueServerId stayed null, seedQueueResolver skipped its store-write
under the if (serverId) guard, and the auto-added refs landed with
an empty server key. Every auto-added row rendered as the resolver
placeholder (… / 0:00) until the next time something happened to
bind the server. Same symptom PR #892 fixed for the manual enqueue
surface, just on the auto-add paths.

Extract ensureQueueServerPinned() from the private helper in
queueMutationActions.ts into playbackServer.ts so it can be shared.
Call it before every set callback that appends or splices refs in
nextAction.ts — appendTracksAndPlayFirst, proactive infinite top-up,
proactive radio top-up. Helper returns the pinned canonical key so
the caller does not need a second store read.

Regression coverage in ensureQueueServerPinned.test.ts: pin on null
+ active server, idempotent on already-bound, empty-string fallback
when no active server, canonical-key return value matches what
toQueueItemRefs expects (not the raw auth uuid). Existing
b1QueueServerIdentity.test.ts continues to cover the manual
enqueue surface unchanged.

* docs(release): CHANGELOG for queue auto-top-up placeholder fix (PR #930)
This commit is contained in:
Frank Stellmacher
2026-05-30 23:04:14 +02:00
committed by GitHub
parent ae1572f370
commit 2a88ca3248
5 changed files with 139 additions and 19 deletions
+21
View File
@@ -57,6 +57,27 @@ export function bindQueueServerForPlayback(): void {
usePlayerStore.setState({ queueServerId: canonical });
}
/**
* Bind `queueServerId` via {@link bindQueueServerForPlayback} when it is still
* null, then return the (now-bound) server identifier. Call this synchronously
* before any state mutation that adds new tracks to the queue.
*
* Without the pin, refs land with an empty server key, {@link seedQueueResolver}
* skips its store-write, and queue rows render as the resolver placeholder
* (`…` / 0:00) until something else binds the server (see PR #892). Affects
* both the manual enqueue mutations and the auto-add paths (infinite-queue
* top-up, radio top-up).
*
* Idempotent: no-op when already pinned. Returns `''` when no active server is
* available to pin (e.g. unit tests without an authed store).
*/
export function ensureQueueServerPinned(): string {
if (usePlayerStore.getState().queueServerId == null) {
bindQueueServerForPlayback();
}
return usePlayerStore.getState().queueServerId ?? '';
}
export function clearQueueServerForPlayback(): void {
usePlayerStore.setState({ queueServerId: null });
}