feat: add local-only completion and preference state
Completions and prefs live in localStorage and never reach the server — there is no account and nothing to log into. Reads never throw, so a corrupt value costs a preference rather than the whole screen. Import merges and never removes: a completion present on either side stays completed, because nothing else holds a copy to restore from. The feed client refuses a schemaVersion it does not know rather than guessing at unfamiliar fields. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
49d52e3587
commit
ac165f0df7
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* localStorage access.
|
||||
*
|
||||
* Everything here stays on the device — there is no account, no session, and
|
||||
* the server never learns what a user has completed. The `v1` segment in every
|
||||
* key is the migration hook: read old versions forward, and never delete an old
|
||||
* key until the migration has shipped and run, because a user who has not
|
||||
* opened the app in six months still has their data under it.
|
||||
*/
|
||||
|
||||
const NS = "gacha-tracker:v1";
|
||||
|
||||
export const KEYS = {
|
||||
completions: `${NS}:completions`,
|
||||
prefs: `${NS}:prefs`,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Reads never throw. A corrupt or foreign value falls back to the default
|
||||
* rather than taking the app down — losing a preference is recoverable, a blank
|
||||
* screen is not.
|
||||
*/
|
||||
export function readJson<T>(key: string, fallback: T): T {
|
||||
try {
|
||||
const raw = localStorage.getItem(key);
|
||||
if (raw === null) return fallback;
|
||||
return JSON.parse(raw) as T;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
export function writeJson(key: string, value: unknown): void {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch {
|
||||
// Quota exceeded or storage disabled (private mode). The UI keeps working
|
||||
// from in-memory state; only persistence is lost.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user