diff --git a/android/PLAN.md b/android/PLAN.md index fee7275..b015519 100644 --- a/android/PLAN.md +++ b/android/PLAN.md @@ -82,6 +82,17 @@ runs `openapi-generator` against the committed spec. Endpoints that return service (Retrofit) → DTO`. Repositories expose `Result`-like sealed types so the UI degrades gracefully (see §7). +### 2.1 Build workflow: Kotlin first, then design-led UI +The app is built in two passes. **First**, the functional Kotlin is written — the layering above with +placeholder/functional Compose screens: navigation, ViewModels, repositories, the generated API +client, auth/token handling, and every screen wired to its endpoints and working end-to-end. **Then**, +once that Kotlin code is done, **Claude Design produces the front-end design** for the app, and +**Claude Code implements the final UI (Compose screens, theming, components) according to that +design.** The design pass restyles and refines the already-working screens; it does not change the +architecture, data flow, or endpoint contracts established in the first pass. Keeping strings +externalized and branding data-driven (§2, §3) from the start is what lets the design pass reskin +freely without touching logic. + --- ## 3. Base URL: first-run + settings @@ -294,7 +305,7 @@ maintenance cost. Reserve v2 for a real breaking re-shape if one ever arises. stay website-handled and the app hands off to them (§4.2). This is a deliberate scope reduction. 3. **Push notifications** — see §11. Additive v1 endpoints under `/auth/me/devices*` and `/auth/me/notifications*`, plus a **self-hosted `ntfy` service added to `website/docker-compose.yml`** - with fully declarative, zero-interaction config. Not required for the first release (M6, not M1–M5). + with fully declarative, zero-interaction config. Not required for the first release (M7, not M1–M6). 4. **Version/health surfacing** — ensure `/public/status` (or a light `/public/version`) exposes enough for the app's first-run probe and version-mismatch guard. 5. **Docs** — update `docs/website/BACKEND_DESIGN.md` for any new/changed endpoint; keep this file and @@ -309,25 +320,41 @@ No `link/` or `servuo-plugins/` changes are expected — the app is downstream o ## 9. Milestones +**Two passes (§2.1).** M0–M4 are the **functional Kotlin pass** — every screen wired to its endpoints +and working end-to-end with placeholder/functional Compose UI, no design investment yet. **M5 is the +design pass**: once the functional Kotlin is done, Claude Design produces the front-end design and +Claude Code implements the final UI to it. Polish/release, push, and Play (M6–M8) follow the designed +app. + 1. **M0 — Repo scaffold**: Gradle + Compose + Hilt skeleton, CI (build + lint + unit test), license headers (GPL-3.0-or-later), CONTRIBUTING/AI-disclosure parity with the other repos. -2. **M1 — Connect & browse**: first-run base-URL flow, `/public/status`+`/public/settings` theming, - generated API client, public content (news/wiki/pages) + contact. No auth yet. -3. **M2 — Public shard**: shard widgets + SSE live stream with reconnect/degradation. -4. **M3 — Auth (§4)**: native password+TOTP login (429 handling), token storage, refresh interceptor, - logout, `/auth/me` role re-validation, the access-level menu. Custom-Tab **hand-offs** to the website - for register / invite / password-reset / SSO (no native screens for those). Optional biometric - app-lock. *Prerequisite:* the website password-reset flow (§8) is already built. -5. **M4 — Player self-service & game data**: account management (via `/auth/me/*`), game-account - linking, own roster/characters/vendors/houses/sales — **text-only** presentation (§6.3). -6. **M5 — Polish & first release**: settings (server switch = hard reset), version-mismatch guard, +2. **M1 — Connect & browse** *(functional pass)*: first-run base-URL flow, + `/public/status`+`/public/settings` theming, generated API client, public content + (news/wiki/pages) + contact. No auth yet. +3. **M2 — Public shard** *(functional pass)*: shard widgets + SSE live stream with + reconnect/degradation. +4. **M3 — Auth (§4)** *(functional pass)*: native password+TOTP login (429 handling), token storage, + refresh interceptor, logout, `/auth/me` role re-validation, the access-level menu. Custom-Tab + **hand-offs** to the website for register / invite / password-reset / SSO (no native screens for + those). Optional biometric app-lock. *Prerequisite:* the website password-reset flow (§8) is + already built. +5. **M4 — Player self-service & game data** *(functional pass)*: account management (via + `/auth/me/*`), game-account linking, own roster/characters/vendors/houses/sales — **text-only** + presentation (§6.3). +6. **M5 — Design pass & final UI (§2.1)**: with the functional Kotlin from M1–M4 working end-to-end, + **Claude Design produces the front-end design** for the app, then **Claude Code implements the final + UI to it** — Compose screens, Material 3 theming from the per-shard branding (§3), reusable + components, loading/error/empty states, the designed access-level menu. Restyles the existing + screens only; no changes to architecture, data flow, or endpoint contracts. Text-only game data + (§6.3) still holds — this is visual design of the data screens, not paperdoll art. +7. **M6 — Polish & first release**: settings (server switch = hard reset), version-mismatch guard, release build hardening (HTTPS-only, no token logging). No offline cache in v1 (§7). **Ship v1 as a signed APK attached to a Gitea release** (see §10). -7. **M6 — Push notifications** (post-v1): add the self-hosted `ntfy` service to +8. **M7 — Push notifications** (post-v1): add the self-hosted `ntfy` service to `website/docker-compose.yml` (declarative, zero-interaction config), UnifiedPush integration in the app, device registration, the subscriptions UI, and the content-free-tickle backend fan-out (see §11). The app is built with room for this from M0 but it does not gate the first release. -8. **M7 — Google Play**: Play Console listing, signing/upload key, and (optionally) an FCM build flavor +9. **M8 — Google Play**: Play Console listing, signing/upload key, and (optionally) an FCM build flavor — after the direct-APK release is stable. --- @@ -344,7 +371,7 @@ No `link/` or `servuo-plugins/` changes are expected — the app is downstream o ## 11. Push notifications (built-for, shipped post-v1) -The app is architected from M0 to accommodate push, but push itself ships in M6 — it does not block the +The app is architected from M0 to accommodate push, but push itself ships in M7 — it does not block the first release. Users **opt in per stream**: nothing is pushed unless subscribed. ### Transport — UnifiedPush via self-hosted ntfy (decided) @@ -422,7 +449,7 @@ repos use), on a bare `ubuntu:latest` container. frequent: run the job under a prebuilt Android-SDK `container:` image so nothing installs per-run.) - **PR gate** (`.gitea/workflows/pr-checks.yml`, on PR → `main`): `./gradlew lint test assembleDebug`. Debug builds are auto-signed, so the gate needs no secrets. Mirrors `website/`'s pre-merge gate. -- **Release** (`.gitea/workflows/release.yml`, M5+): build a **signed release APK** and attach it to a +- **Release** (`.gitea/workflows/release.yml`, M6+): build a **signed release APK** and attach it to a Gitea release (mirrors `link/`'s release job). The **keystore is a base64 Gitea Actions secret** decoded in CI; store/key passwords are secrets. The keystore never lives in the repo. Keep the signing identity stable from the first release (Play later requires consistency). @@ -445,7 +472,7 @@ ntfy / UnifiedPush (§11). own verified app links — confirm whether any deep-link-back is wanted at all for v1. - ntfy: exact upstream image + pinned tag, its reverse-proxy hostname/path, and whether to add a backend publish token (optional hardening — the content-free-tickle design does not require one). -- FCM flavor: build it for the Play release or ship Play on UnifiedPush too? Decide at M7. +- FCM flavor: build it for the Play release or ship Play on UnifiedPush too? Decide at M8. - Deep-link / share targets for wiki pages, posts, and notification taps. - iOS: none planned (this is the Android-only choice); revisit only if cross-platform is later required (would change §2 — and push, which would then favor a cross-platform transport).