feat(m1): Connect & browse — first-run flow, public content, contact #6
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/m1-connect-browse"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
M1 — Connect & browse (functional Kotlin pass)
Implements
docs/android/PLAN.mdM1 (§9): every public screen wired to its endpoint and working end-to-end with functional (not yet designed) Compose UI. No auth yet — that's M3. The M5 design pass restyles these screens without touching the architecture, data flow, or contracts established here (§2.1).What's here
GET /public/status, confirms it's a Runic Gateway backend (version identity), and persists it to DataStore. HTTPS required in release; HTTP allowed only in debug for local dev. Settings → Server switch does a hard reset back to this screen.HostSelectionInterceptorretargets every relative call onto the selected site, preserving any base-path prefix. AUserAgentInterceptorsends an identifiable UA so the site's scanner guard (§8) doesn't reject the app.screen → ViewModel → repository → PublicApi (Retrofit) → DTO. Repositories return a typedApiResult(Ok/HttpError/NetworkError) so the UI degrades gracefully (§7) — clean loading / error+retry / empty states, no crash on a down backend.brand.accentin/public/settings; site-relative asset paths resolved against the base URL.heading / rich_text / image / quote / cta / divider / two_column), and the Contact form (validation, mailto-fallback, 429/502 handling). One shared, declarative navigation drawer (§5).ApiResult/UiStatemapping, and brand-color parsing.Note: hand-written vs generated API client
PLAN §2 anticipates generating DTOs from
swagger-output.json. In practice the committed spec is produced by swagger-autogen and its component schemas are meta-descriptive (nested{type, example}wrappers), soopenapi-generatorwould emit unusable DTOs from it. I therefore hand-wrote spec-aligned DTOs + the Retrofit interface — the "checked-in generated module" the plan explicitly allows (§2) — matching shapes against the website controllers/models. Every DTO ignores unknown keys so additive backend fields never break decoding. If we want true codegen later, the spec's component schemas need to be authored as real OpenAPI models first.Verification
./gradlew lint test assembleDebuggreen locally (JDK 17 via the Android Studio JBR + platform-35 SDK). CI runs the same gate.Out of scope (later milestones)
Public shard widgets + SSE (M2), auth/token handling (M3), player self-service (M4), the design pass (M5).
AI-assisted contribution disclosure: authored with Claude Code (Claude Opus). AI-authored commit carries a
Co-Authored-By: Claudetrailer. I've reviewed the changes for correctness.🤖 Generated with Claude Code
Implements M1 (functional Kotlin pass, docs/android/PLAN.md §9): the first-run base-URL connect flow, brand-seeded Material 3 theming from /public/settings, a Retrofit/OkHttp/kotlinx-serialization client with a runtime host-selection interceptor (the base URL is not compiled in), the layered repository stack returning a typed ApiResult for graceful degradation, and functional Compose screens for Home/Status, News (+ post detail), Wiki (+ detail), CMS pages (block renderer), and the contact form. One shared, declarative navigation drawer. No auth yet (M3). DTOs + the Retrofit interface are hand-written and spec-aligned rather than openapi-generated: the committed swagger-output.json is produced by swagger-autogen and its component schemas are meta-descriptive (nested {type, example} wrappers), not codegen-clean, so a hand-authored client module is the pragmatic "checked-in generated module" the plan allows (§2). Shapes were matched against the website controllers/models. JVM unit tests cover URL normalization, host rewriting, ApiResult/UiState mapping, and brand-color parsing. `lint test assembleDebug` green locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr