From 8877f532599011ca02f267863189d4ffe6755955 Mon Sep 17 00:00:00 2001 From: Bobby Date: Wed, 22 Apr 2026 09:47:33 +0530 Subject: Wire the begin-state detector through Dexie via world and people repositories --- loading/session.ts | 25 ++++++++++++++++++++----- persistence/client.ts | 16 ++++++++++++++++ persistence/people.ts | 14 ++++++++++++++ persistence/worlds.ts | 21 +++++++++++++++++++++ 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 persistence/client.ts create mode 100644 persistence/people.ts create mode 100644 persistence/worlds.ts diff --git a/loading/session.ts b/loading/session.ts index a1a685b..788f0e6 100644 --- a/loading/session.ts +++ b/loading/session.ts @@ -1,3 +1,6 @@ +import { getPerson } from '@hollowdark/persistence/people' +import { getCurrentWorld } from '@hollowdark/persistence/worlds' + /** * The three states the Begin screen can render. * @@ -16,11 +19,23 @@ export type BeginState = | { readonly kind: 'returning-no-active' } /** - * Inspect device-local state and decide which Begin variant to show. The - * real implementation queries IndexedDB for worlds and the currently - * active player character; while persistence is still being wired up this - * returns `first-ever` unconditionally. + * Inspect device-local state and decide which Begin variant to show. Reads + * the single world record (design invariant) and, when a current player + * character is set, reads their name for the continue prompt. */ export async function detectBeginState(): Promise { - return { kind: 'first-ever' } + const world = await getCurrentWorld() + if (world === null) return { kind: 'first-ever' } + + const activeId = world.currentPlayerCharacterId + if (activeId === null) return { kind: 'returning-no-active' } + + const active = await getPerson(activeId) + if (active === null) return { kind: 'returning-no-active' } + + return { kind: 'returning-active', characterName: displayName(active.name) } +} + +function displayName(name: { given: string; preferredName: string | null }): string { + return name.preferredName ?? name.given } diff --git a/persistence/client.ts b/persistence/client.ts new file mode 100644 index 0000000..7e2468b --- /dev/null +++ b/persistence/client.ts @@ -0,0 +1,16 @@ +import { HollowdarkUserData } from '@hollowdark/persistence/db' + +let userData: HollowdarkUserData | null = null + +/** + * The shared `HollowdarkUserData` Dexie client. Lazily constructed on + * first access so pure unit tests that never touch persistence do not + * open an IndexedDB connection. Repositories should import this helper + * rather than instantiating `HollowdarkUserData` themselves. + */ +export function userDataDb(): HollowdarkUserData { + if (userData === null) { + userData = new HollowdarkUserData() + } + return userData +} diff --git a/persistence/people.ts b/persistence/people.ts new file mode 100644 index 0000000..c26819c --- /dev/null +++ b/persistence/people.ts @@ -0,0 +1,14 @@ +import { userDataDb } from '@hollowdark/persistence/client' +import type { PersonId } from '@hollowdark/engine/entities/base' +import type { Person } from '@hollowdark/engine/entities/person' + +/** Fetch a single person by id, or `null` when the row is missing. */ +export async function getPerson(id: PersonId): Promise { + const row = await userDataDb().people.get(id) + return row ?? null +} + +/** Persist a person record. Replaces any existing row with the same id. */ +export async function savePerson(person: Person): Promise { + await userDataDb().people.put(person) +} diff --git a/persistence/worlds.ts b/persistence/worlds.ts new file mode 100644 index 0000000..0938a84 --- /dev/null +++ b/persistence/worlds.ts @@ -0,0 +1,21 @@ +import { userDataDb } from '@hollowdark/persistence/client' +import type { World } from '@hollowdark/engine/entities/world' + +/** + * The single world a player has on this device, or `null` if they have + * never begun one. Design invariant: one continuous world per player. + * Should the table ever hold more than one row, the earliest-created + * row wins — newer rows would only arise from explicit import. + */ +export async function getCurrentWorld(): Promise { + const rows = await userDataDb().worlds.toArray() + if (rows.length === 0) return null + return rows.reduce((earliest, candidate) => + candidate.createdAt < earliest.createdAt ? candidate : earliest + ) +} + +/** Persist a world record. Replaces any existing row with the same id. */ +export async function saveWorld(world: World): Promise { + await userDataDb().worlds.put(world) +} -- cgit v1.2.3