perf(player): debounce localStorage persistence

Wrap save() in a 300ms debounce to prevent rapid serialization
when multiple state changes fire in quick succession (e.g. removing
several items or adjusting volume).
This commit is contained in:
2026-02-11 22:57:25 -08:00
parent 31414c5874
commit cd443b974b

View File

@@ -26,6 +26,9 @@ class PlayerStore {
isMuted = $state(false); isMuted = $state(false);
uiOpen = $state(false); // Mobile UI state uiOpen = $state(false); // Mobile UI state
// Debounce timer for save()
private _saveTimer: ReturnType<typeof setTimeout> | null = null;
// O(1) index: track.id → index in queue // O(1) index: track.id → index in queue
private idToIndex = $derived.by(() => { private idToIndex = $derived.by(() => {
const map = new Map<number, number>(); const map = new Map<number, number>();
@@ -99,6 +102,7 @@ class PlayerStore {
} }
save() { save() {
// Read snapshots synchronously so $effect tracks reactive deps
const data: PlayerState = { const data: PlayerState = {
queue: $state.snapshot(this.queue), queue: $state.snapshot(this.queue),
currentId: $state.snapshot(this.currentId), currentId: $state.snapshot(this.currentId),
@@ -109,7 +113,11 @@ class PlayerStore {
volume: $state.snapshot(this.volume), volume: $state.snapshot(this.volume),
isMuted: $state.snapshot(this.isMuted), isMuted: $state.snapshot(this.isMuted),
}; };
// Debounce only the serialization + write
if (this._saveTimer) clearTimeout(this._saveTimer);
this._saveTimer = setTimeout(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}, 300);
} }
// Actions // Actions