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,93 @@
|
||||
import type { Region } from "../../../shared/schema.ts";
|
||||
import type { View } from "../usePrefs.ts";
|
||||
import { routinesFor, offered } from "../../../shared/routines.ts";
|
||||
import { createAccount, changeAccountRegion } from "./accounts.ts";
|
||||
import type { AccountId, ReaderState, TrackingIntent } from "./schema.ts";
|
||||
export interface SetupDraft {
|
||||
region: Region;
|
||||
interface: "relaxed" | "structured";
|
||||
theme: "dark" | "light" | "system";
|
||||
scheme: string;
|
||||
timezone: string | null;
|
||||
games: string[];
|
||||
tracking: Record<string, TrackingIntent>;
|
||||
showUpcoming: boolean;
|
||||
view: View;
|
||||
/** Asked only when the reader picks Timeline; absent keeps the default look. */
|
||||
timelineLook?: "new" | "classic";
|
||||
}
|
||||
export function setupReader(
|
||||
draft: SetupDraft,
|
||||
existing: ReaderState,
|
||||
at: string,
|
||||
mint: () => AccountId,
|
||||
): ReaderState {
|
||||
const accounts = { ...existing.accounts };
|
||||
for (const game of draft.games) {
|
||||
let a =
|
||||
Object.values(accounts).find((a) => a.game === game && !a.retiredAt) ??
|
||||
createAccount(game, draft.region, at, mint());
|
||||
if (a.region !== draft.region)
|
||||
a = changeAccountRegion(a, draft.region, at, `setup-${at}`);
|
||||
const intent = draft.tracking[game] ?? {
|
||||
events: true,
|
||||
dailies: false,
|
||||
weeklyMonthly: false,
|
||||
cycles: false,
|
||||
};
|
||||
const routines = {
|
||||
...a.routines,
|
||||
[`dailies:${game}`]: {
|
||||
followed: intent.dailies,
|
||||
checklist: intent.dailies,
|
||||
timeline: intent.dailies,
|
||||
},
|
||||
};
|
||||
for (const r of routinesFor(game)) {
|
||||
const followed =
|
||||
offered(r, existing.prefs.standardPulls === true) &&
|
||||
(r.period.unit === "cycle" ? intent.cycles : intent.weeklyMonthly);
|
||||
routines[r.id] = { followed, checklist: followed, timeline: followed };
|
||||
}
|
||||
accounts[a.id] = { ...a, intent, routines };
|
||||
}
|
||||
for (const [id, a] of Object.entries(accounts))
|
||||
if (!draft.games.includes(a.game))
|
||||
accounts[id] = {
|
||||
...a,
|
||||
intent: {
|
||||
events: false,
|
||||
dailies: false,
|
||||
weeklyMonthly: false,
|
||||
cycles: false,
|
||||
},
|
||||
routines: Object.fromEntries(
|
||||
Object.entries(a.routines).map(([id, p]) => [
|
||||
id,
|
||||
{ ...p, followed: false },
|
||||
]),
|
||||
),
|
||||
};
|
||||
return {
|
||||
...existing,
|
||||
accounts,
|
||||
prefs: {
|
||||
...existing.prefs,
|
||||
region: draft.region,
|
||||
interface: draft.interface,
|
||||
theme: draft.theme,
|
||||
scheme: draft.scheme,
|
||||
timezone: draft.timezone,
|
||||
showUpcoming: draft.showUpcoming,
|
||||
view: draft.view,
|
||||
...(draft.view === "timeline" && draft.timelineLook
|
||||
? { timelineLook: draft.timelineLook }
|
||||
: {}),
|
||||
onboarded: true,
|
||||
redesignTourSeen: "2026-10-redesign",
|
||||
hiddenGames: Object.values(accounts)
|
||||
.map((a) => a.game)
|
||||
.filter((g) => !draft.games.includes(g)),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user