diff options
| author | Bobby <[email protected]> | 2026-04-22 08:03:06 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-04-22 08:03:06 +0530 |
| commit | e72162b34bddbf04998eea934335e9496b8649f8 (patch) | |
| tree | 121f6442c797fe7a3581f98c631810431a0bcae5 /loading | |
| parent | 4938bd9e1454769f5cc338ba9fab437eaa7e8f58 (diff) | |
| download | hollowdark-e72162b34bddbf04998eea934335e9496b8649f8.tar.xz hollowdark-e72162b34bddbf04998eea934335e9496b8649f8.zip | |
Implement initial load + Begin screens with stub 3s loading pipeline
Diffstat (limited to 'loading')
| -rw-r--r-- | loading/progress.ts | 30 | ||||
| -rw-r--r-- | loading/session.ts | 26 | ||||
| -rw-r--r-- | loading/stub.ts | 42 |
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) + }) +} |
