feat(custom): model reader-authored games and events
The data layer for PRD F13, with no UI and no behaviour change yet.
src/shared/custom.ts defines the two key spaces, their schemas, and the
projection into what the views read. Two properties are the point of it:
- A reader's event id is random, not derived from their title. They can type a
scraped event's exact name and date, which under ${game}:${slug}:${date} is a
byte-identical key — one completion mark and one streak silently shared by two
events. Randomness also means renaming their own event never moves its id.
- A reader's event carries no sourceUrl, so a hand-entered date can never be
attributed to a source, and claims no region split, because they entered one
instant and inventing three would fabricate two of them.
The rest is widening what was GameId-shaped into a lane that may be one of
theirs: clockFor takes the boundary fields structurally so their events run on
the identical countdown rather than a second one, day keys fall back to the
regional default for a lane with no server map, and gameMeta becomes a context
resolver so metaFor stays pure and total — a lane can outlive its game when an
import carries an event whose game did not come with it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4029885833
commit
3702ee7a4c
+4
-3
@@ -26,8 +26,8 @@ import {
|
||||
} from "./state/lens.ts";
|
||||
import { clockFor, DAY, formatRemaining } from "../shared/time.ts";
|
||||
import { dailySummary, isDaily, resolveDaily } from "../shared/daily.ts";
|
||||
import { gameMeta } from "../shared/games.ts";
|
||||
import type { GameId } from "../shared/schema.ts";
|
||||
import { useGameMeta } from "./state/gameMeta.tsx";
|
||||
import type { LaneId } from "../shared/custom.ts";
|
||||
|
||||
type View = "soon" | "calendar";
|
||||
|
||||
@@ -70,6 +70,7 @@ export function App() {
|
||||
// The event most recently ignored, so it can be put back without hunting for
|
||||
// a row that just disappeared.
|
||||
const [lastIgnored, setLastIgnored] = useState<{ id: string; title: string } | null>(null);
|
||||
const gameMeta = useGameMeta();
|
||||
const now = useNow();
|
||||
const online = useOnline();
|
||||
const { prefs, update, toggleGame } = usePrefs();
|
||||
@@ -151,7 +152,7 @@ export function App() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state, prefs.region, Math.floor(now / 60_000)]);
|
||||
|
||||
const games = useMemo<GameId[]>(
|
||||
const games = useMemo<LaneId[]>(
|
||||
() => [...new Set(allRows.map((r) => r.event.game))],
|
||||
[allRows],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user