feat(web/m7-364): queue mutations + drawer state on player store

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-03 20:29:48 -04:00
parent 8053704afa
commit 6f9b5d0059
2 changed files with 161 additions and 2 deletions
+67 -1
View File
@@ -27,6 +27,7 @@ let _volume = $state(readStoredVolume());
let _shuffle = $state(false);
let _repeat = $state<RepeatMode>('off');
let _error = $state<string | null>(null);
let _queueDrawerOpen = $state(false);
// M4c: track when the queue was seeded by a radio call so we can
// auto-refresh at 80% consumption. Cleared when the user enqueues
@@ -47,7 +48,8 @@ export const player = {
get volume() { return _volume; },
get shuffle() { return _shuffle; },
get repeat() { return _repeat; },
get error() { return _error; }
get error() { return _error; },
get queueDrawerOpen() { return _queueDrawerOpen; }
};
export function registerAudioEl(el: HTMLAudioElement | null): void {
@@ -243,6 +245,70 @@ export async function playRadio(seedTrackId: string): Promise<void> {
_radioSeedId = seedTrackId; // M4c: set AFTER playQueue (which clears it)
}
export function moveQueueItem(from: number, to: number): void {
if (from === to) return;
if (from < 0 || from >= _queue.length) return;
if (to < 0 || to >= _queue.length) return;
const playingId = _queue[_index]?.id;
const next = _queue.slice();
const [moved] = next.splice(from, 1);
next.splice(to, 0, moved);
_queue = next;
if (playingId) {
const found = next.findIndex((x) => x.id === playingId);
if (found >= 0) _index = found;
}
}
export function removeFromQueue(idx: number): void {
if (idx < 0 || idx >= _queue.length) return;
const next = _queue.slice();
next.splice(idx, 1);
if (idx < _index) {
_queue = next;
_index = _index - 1;
return;
}
if (idx > _index) {
_queue = next;
return;
}
// idx === _index — removing the currently-playing track.
_queue = next;
if (next.length === 0) {
_index = 0;
_state = 'idle';
_position = 0;
_duration = 0;
_error = null;
return;
}
// _index stays — now points at what was the next track.
if (_index >= next.length) _index = next.length - 1;
_position = 0;
_duration = 0;
_state = 'loading';
_error = null;
}
export function playFromQueueIndex(idx: number): void {
if (idx < 0 || idx >= _queue.length) return;
_radioSeedId = null;
_index = idx;
_position = 0;
_duration = 0;
_state = 'loading';
_error = null;
}
export function toggleQueueDrawer(): void {
_queueDrawerOpen = !_queueDrawerOpen;
}
export function closeQueueDrawer(): void {
_queueDrawerOpen = false;
}
// M4c: auto-refresh radio queue when 80% consumed.
// Fires whenever the consumption ratio crosses 80% AND the queue was
// seeded by playRadio() AND no refresh is currently in-flight.