Skip to content

Commit db2c216

Browse files
authored
Merge pull request #139 from Programcode/native-ios/07a-offline-read
Native iOS app: offline reading
2 parents bb36239 + 95313b1 commit db2c216

42 files changed

Lines changed: 1461 additions & 119 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/mobile/AGENTS.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The iOS app (`@debatelab/mobile`): Expo SDK 57, React Native 0.86, React 19, Exp
3030
- Sparring is the website's typed mode: `sparring.ts` sends the opponent's turns and the judge as `generateSparringTurn` and `judgeSparringSession` in `src/lib/ai/client.ts` do (the Sparring model settings, a transcript-only debate context), and the round's moves (turn order, the deadline clock, judging, saved rows) are `packages/core/src/sparring/session.ts`, shared with the website's sparring store. The server saves the judged round to `sparring_sessions` when it scores it; the app then files it in its debate with `attach_to_debate` and the student's side, as the store does. The side is asked when the round starts. The unfinished round is a session slot (`sparringDraft`), so leaving the screen keeps it, and a request cut off by leaving is offered again. The flow button opens the Flowchart on the Current Debate's map, above the round, which keeps running. Practice lists the Current Debate's judged rounds and the Debate tab lists them by side; both open `sparring/[id]`, which can delete them. Voice rounds need the development build, so the voice switch shows disabled.
3131
- Flowchart mirrors the website's `FlowchartPage.tsx` and the session store's `saveFlowchart` and `deleteSavedFlowchart` without React Flow: the saved `flowcharts` row keeps the website's React Flow node and edge JSON, and `packages/core/src/flowchart.ts` reads and edits it (starter map, edge kinds from the label or stroke the website writes, role columns, the tree, and edits that keep every field they do not touch). `flowcharts.ts` reads and writes the rows; Save over an opened map updates it, Save as inserts a row as the website's Save does. `flowchart-drafts.ts` holds each debate's open map and unsaved edits in session memory, shared by the screen and its sheets. Maps follow `activeDebateId`: one-off maps are saved with no debate, as on the website. The website's PNG export is a PDF of the Map tree here (`flowchartExportHtml`).
3232
- One-off mode is session-only and never written, as on the website; only maps saved in it are kept, with no debate, as the website keeps them. The switcher turns it on with or without a Current Debate, and `OneOffNotice` shows it on the Debate tab, Prepare, Ask, Library and the Generate, Attack, Brainstorm, Factoids, Crossfire and Sparring forms (through `WorkingIn`) and the Flowchart. The Debate tab, Prepare and Library still list the Current Debate's work in one-off mode, as the website's home does. Students prepare both sides of a motion, so a debate has no side: the Debate tab's In favor / Against toggle only chooses which side's work it shows, using the website's rule (`'both'` counts for either side, neutral and side-less work for neither). It starts on In favor and is never stored.
33+
- Offline reading: `offline-cache.ts` keeps a copy of every read in `debates.ts`, `sources.ts`, `results.ts`, `flowcharts.ts`, `chats.ts` and `sparring.ts` (the debates list, counts and recent work, Library sources with their text, saved results, maps, chats and judged Sparring rounds) in AsyncStorage under the user id. A read goes to the network first and saves its answer; when no answer comes back (supabase-js status 0: no connection, DNS, timeout), or `network-status.ts` (expo-network) says the device is offline, the copy answers and `OfflineNotice` shows at the top of `Screen`, `DocumentScreen` and the Flowchart until each copy shown is read from the server again or the screen showing it closes, moves on or leaves view (a screen passes `useCopyHolder().mark()` with its reads). A server error (an expired session, a policy, a 5xx) is thrown, never answered from a copy; a read made of several queries runs them through `readAll` (or `pickFailure`), so one query's refusal wins over another's missing answer. A slower answer never replaces a newer copy, each group keeps its most recently fetched copies only (`LIMITS`), and the auth provider calls `setCacheOwner` before it adopts a session, so another account's copies are removed before its first read. Nothing is written offline: AI and saving fail with their network errors, and there is no write queue. A read added to those modules goes through `cachedRead`, takes an optional `CopyMark`, and throws `readFailed(error, status)`. Every write that succeeds calls `afterWrite` with the copies it changed (`debateListScopes` names a debate's counts and lists; a delete uses `deletedListScopes`, which drops every debate's when no row came back), which drops them and stops older reads from saving over them, or `updateCopy` when it knows the new value.
3334
- `src/theme/`: colours from the mocks and the website's dark palette, Inter fonts.
3435
- `scripts/screenshots.ts`: web-export screenshots. `scripts/render-tab-icons.ts` redraws the tab bar PNGs.
3536

@@ -41,6 +42,7 @@ Root `npm run lint` and `npm run typecheck` cover the app. From the repo root:
4142
npm run -w @debatelab/mobile typecheck
4243
npm run -w @debatelab/mobile export:web
4344
PORT=<free port> npx tsx apps/mobile/scripts/screenshots.ts
45+
ONLY=offline PORT=18080 STAGING_DEMO_CODE=... npx tsx apps/mobile/scripts/screenshots.ts # offline reading, signed in
4446
```
4547

4648
Screenshots go to `.verify/mobile-shots/`. For signed-in screens, see the header of `scripts/screenshots.ts`. To load the app on a phone, see [docs/development.md](../../docs/development.md#mobile-app).

‎apps/mobile/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"expo-linear-gradient": "~57.0.2",
3030
"expo-linking": "~57.0.12",
3131
"expo-localization": "~57.0.2",
32+
"expo-network": "~57.0.2",
3233
"expo-print": "~57.0.2",
3334
"expo-router": "~57.0.25",
3435
"expo-secure-store": "~57.0.4",

0 commit comments

Comments
 (0)