Spaces:
Running
Running
| const DEFAULTS = () => ({ liked: {}, hfLiked: {}, saved: {}, seen: [], round: null }); | |
| export class Store { | |
| constructor({ key = "bsr:v1", backend } = {}) { | |
| this.key = key; | |
| this.backend = backend || globalThis.localStorage; | |
| this.data = this._read(); | |
| } | |
| _read() { | |
| try { | |
| const raw = this.backend?.getItem(this.key); | |
| if (!raw) return DEFAULTS(); | |
| return { ...DEFAULTS(), ...JSON.parse(raw) }; | |
| } catch { | |
| return DEFAULTS(); | |
| } | |
| } | |
| _write() { | |
| try { | |
| this.backend?.setItem(this.key, JSON.stringify(this.data)); | |
| } catch { | |
| /* storage full / blocked — ignore */ | |
| } | |
| } | |
| // saves | |
| isSaved(id) { return !!this.data.saved[id]; } | |
| toggleSaved(id) { | |
| if (this.data.saved[id]) { delete this.data.saved[id]; this._write(); return false; } | |
| this.data.saved[id] = Date.now(); this._write(); return true; | |
| } | |
| savedIds() { | |
| return Object.keys(this.data.saved).sort((a, b) => this.data.saved[b] - this.data.saved[a]); | |
| } | |
| // local-like fallback | |
| isLocalLiked(id) { return !!this.data.liked[id]; } | |
| setLocalLiked(id, on) { | |
| if (on) this.data.liked[id] = Date.now(); else delete this.data.liked[id]; | |
| this._write(); | |
| } | |
| // confirmed real HF like (this session/device) | |
| isHfLiked(id) { return !!this.data.hfLiked[id]; } | |
| setHfLiked(id, on) { | |
| if (on) this.data.hfLiked[id] = true; else delete this.data.hfLiked[id]; | |
| this._write(); | |
| } | |
| // any like (real or local) — for button fill state | |
| isLiked(id) { return this.isHfLiked(id) || this.isLocalLiked(id); } | |
| // seen / progress | |
| markSeen(id) { | |
| if (!this.data.seen.includes(id)) { this.data.seen.push(id); this._write(); } | |
| } | |
| // resume round | |
| saveRound(round) { this.data.round = round; this._write(); } | |
| loadRound() { return this.data.round; } | |
| } | |