feat: the redesigned Event Clock
The new reader app replaces the old UI: Checklist with a rail (focus pills, Next to expire, today's dailies, pinned routines), Timeline in a new and a classic look, Routines, Archive and a Settings area, in a Structured (default) and a Relaxed interface. It follows the approved Round 6 B and Round 7 mockups, corrected screen by screen with the owner. Reader data moves to a v2 store (gacha-tracker:v2:*). Existing v1 data is migrated on first load, a recovery copy is written first, and the v1 keys are never removed, so a rollback finds them unchanged. Redeem codes for Genshin arrive as their own feed. Also: live's urgency ramp, editable per theme; completion and section-complete fireworks; a returning-user tour; the design mockups, specs, plans, reviews and handoff record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ddd9673f86
commit
50c6e91b00
@@ -0,0 +1,49 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { migrateLegacy } from "../src/client/state/reader/migration.ts";
|
||||
import {
|
||||
buildReaderExport,
|
||||
parseReaderExport,
|
||||
} from "../src/client/state/reader/export.ts";
|
||||
const at = "2026-10-16T12:00:00.000Z";
|
||||
test("full reader export round-trips unknown fields without including backup history", () => {
|
||||
const reader = {
|
||||
...migrateLegacy({}, ["genshin"], at, () => "account:one"),
|
||||
future: { keep: true },
|
||||
};
|
||||
const value = buildReaderExport(
|
||||
reader,
|
||||
{
|
||||
"gacha-tracker:v1:tasks": '{"unreadable":true}',
|
||||
"gacha-tracker:v2:backups": "do-not-recurse",
|
||||
},
|
||||
at,
|
||||
);
|
||||
expect(parseReaderExport(value)).toEqual(reader);
|
||||
expect(JSON.stringify(value)).not.toContain("do-not-recurse");
|
||||
});
|
||||
test("unknown versions and invalid known fields cannot be restored", () => {
|
||||
const value = buildReaderExport(
|
||||
migrateLegacy({}, [], at, () => "account:one"),
|
||||
{},
|
||||
at,
|
||||
);
|
||||
expect(parseReaderExport({ ...value, version: 999 })).toBeNull();
|
||||
expect(
|
||||
parseReaderExport({ ...value, reader: { version: 2, accounts: [] } }),
|
||||
).toBeNull();
|
||||
});
|
||||
test("existing version-one exports remain convertible", () => {
|
||||
const reader = parseReaderExport({
|
||||
format: "gacha-tracker-export",
|
||||
version: 1,
|
||||
exportedAt: at,
|
||||
prefs: { region: "asia" },
|
||||
progress: { "genshin:a:2026-01-01": { status: "done", at } },
|
||||
daily: {},
|
||||
ignored: {},
|
||||
customGames: {},
|
||||
customEvents: {},
|
||||
});
|
||||
expect(reader?.version).toBe(2);
|
||||
expect(Object.values(reader!.accounts)[0]?.region).toBe("asia");
|
||||
});
|
||||
Reference in New Issue
Block a user