aboutsummaryrefslogtreecommitdiff
path: root/loading
diff options
context:
space:
mode:
Diffstat (limited to 'loading')
-rw-r--r--loading/progress.ts30
-rw-r--r--loading/session.ts26
-rw-r--r--loading/stub.ts42
3 files changed, 98 insertions, 0 deletions
diff --git a/loading/progress.ts b/loading/progress.ts
new file mode 100644
index 0000000..f631757
--- /dev/null
+++ b/loading/progress.ts
@@ -0,0 +1,30 @@
+import { writable, type Writable } from 'svelte/store'
+
+/**
+ * Phase the loading pipeline is in.
+ * `idle` nothing running yet; percentage is 0.
+ * `loading` a load is in flight; percentage advances 0 → 1.
+ * `complete` load finished; percentage is 1.
+ */
+export type LoadingPhase = 'idle' | 'loading' | 'complete'
+
+/** Reactive snapshot of the loading pipeline. */
+export interface LoadingProgress {
+ readonly percentage: number
+ readonly currentMessage: string
+ readonly phase: LoadingPhase
+}
+
+const DEFAULT: LoadingProgress = {
+ percentage: 0,
+ currentMessage: 'Preparing your reading space',
+ phase: 'idle'
+}
+
+/** Global store of the initial-load progress. Components subscribe via `$`. */
+export const loadingProgress: Writable<LoadingProgress> = writable(DEFAULT)
+
+/** Reset the store to its initial state. Useful between sessions in dev. */
+export function resetLoadingProgress(): void {
+ loadingProgress.set(DEFAULT)
+}
diff --git a/loading/session.ts b/loading/session.ts
new file mode 100644
index 0000000..a1a685b
--- /dev/null
+++ b/loading/session.ts
@@ -0,0 +1,26 @@
+/**
+ * The three states the Begin screen can render.
+ *
+ * `first-ever` no world exists on device yet. Only option is
+ * to begin (create the first world + character).
+ * `returning-active` a world exists with a currently-active player
+ * character. Primary option is to continue them.
+ * `returning-no-active` a world exists but the current character has
+ * died (or been ended) without a successor yet.
+ * Primary option is to pick a successor via the
+ * continuation flow.
+ */
+export type BeginState =
+ | { readonly kind: 'first-ever' }
+ | { readonly kind: 'returning-active'; readonly characterName: string }
+ | { 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.
+ */
+export async function detectBeginState(): Promise<BeginState> {
+ return { kind: 'first-ever' }
+}
diff --git a/loading/stub.ts b/loading/stub.ts
new file mode 100644
index 0000000..99bbd61
--- /dev/null
+++ b/loading/stub.ts
@@ -0,0 +1,42 @@
+import { loadingProgress } from '@hollowdark/loading/progress'
+
+const STUB_DURATION_MS = 3000
+
+/**
+ * Stand-in for the real content-manifest fetch and chunk download. Animates
+ * the loading progress store from 0 to 1 over three seconds of wall-clock
+ * time, then resolves. Replace with the real pipeline when content loading
+ * comes online.
+ */
+export function runStubInitialLoad(): Promise<void> {
+ loadingProgress.set({
+ percentage: 0,
+ currentMessage: 'Preparing your reading space',
+ phase: 'loading'
+ })
+
+ return new Promise((resolve) => {
+ const startedAt = performance.now()
+
+ const tick = (): void => {
+ const elapsed = performance.now() - startedAt
+ const pct = Math.min(1, elapsed / STUB_DURATION_MS)
+ const done = pct >= 1
+
+ loadingProgress.set({
+ percentage: pct,
+ currentMessage: 'Preparing your reading space',
+ phase: done ? 'complete' : 'loading'
+ })
+
+ if (done) {
+ resolve()
+ return
+ }
+
+ requestAnimationFrame(tick)
+ }
+
+ requestAnimationFrame(tick)
+ })
+}