refactor(web): safeLocalStorage helper for store persistence (#375)

Three nearly-identical try/catch wrappers across theme + player stores
collapse to read()/write()/remove() in lib/util/safeLocalStorage.ts.
Sets the pattern for future stores. Caller still does parse/serialize
since the existing call sites store strings (theme preference, volume
number) — no JSON wrapper needed yet.

persisted.ts left alone — its JSON-payload + per-key-suffix shape is
distinct enough to keep self-contained.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 15:36:45 -04:00
parent 2357df83be
commit 633406c05b
3 changed files with 44 additions and 20 deletions
+7 -10
View File
@@ -3,6 +3,7 @@ import { untrack } from 'svelte';
import { api } from '$lib/api/client';
import { user } from '$lib/auth/user.svelte';
import { sessionEnd } from '$lib/auth/sessionEnd.svelte';
import * as storage from '$lib/util/safeLocalStorage';
import { readPersistedQueue, writePersistedQueue, clearPersistedQueue } from './persisted';
export type PlayerState = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
@@ -11,15 +12,11 @@ export type RepeatMode = 'off' | 'all' | 'one';
const VOLUME_KEY = 'minstrel.volume';
export function readStoredVolume(): number {
try {
const raw = localStorage.getItem(VOLUME_KEY);
if (raw == null) return 1;
const n = Number(raw);
if (Number.isFinite(n) && n >= 0 && n <= 1) return n;
return 1;
} catch {
return 1;
}
const raw = storage.read(VOLUME_KEY);
if (raw == null) return 1;
const n = Number(raw);
if (Number.isFinite(n) && n >= 0 && n <= 1) return n;
return 1;
}
let _queue = $state<TrackRef[]>([]);
@@ -135,7 +132,7 @@ export function seekTo(sec: number): void {
export function setVolume(v: number): void {
const clamped = Math.max(0, Math.min(1, v));
_volume = clamped;
try { localStorage.setItem(VOLUME_KEY, String(clamped)); } catch { /* ignore */ }
storage.write(VOLUME_KEY, String(clamped));
}
export function reportTimeUpdate(sec: number): void {