Files
samplez/rippr-src/docs/v2/08-nav-shell.md
uhryniuk 280fd7f988 Add Rippr source snapshot and full-history bundle
Two copies for two jobs. rippr-src/ is a browsable git archive export of
the tracked tree at 2f76983 - no build outputs, no local.properties, no
nested .git - which is convenient to read in gitea but carries no history
and will drift.

rippr-full-history.bundle is the real backup: all 18 commits, verified as
"records a complete history" and test-cloned before committing. This
matters because ~/dojo/rippr has no git remote and otherwise exists only
on one machine.

rippr-src/SNAPSHOT.md explains the difference and how to restore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 08:30:25 -05:00

3.8 KiB
Raw Permalink Blame History

T08 — Navigation shell + ViewModels

Phase 4 · Depends on T01 · Status Done

Goal

Three navigable destinations, theme extracted out of the Activity, and state moved into ViewModels. Done when Record, Trips, and Trip Detail are reachable with a working back stack and MainActivity no longer owns screen state.

Context

app/src/main/java/com/rippr/MainActivity.kt (216 lines) currently does everything: permission launcher, service intents, theme definition, screen composition, and state. That was proportionate for one screen and will not survive three.

Existing pieces to preserve rather than rewrite:

  • The permission flow (RequestMultiplePermissions, with POST_NOTIFICATIONS added only on TIRAMISU+) works and should move largely as-is.
  • The battery-optimisation exemption prompt is genuinely useful for an app that must survive a multi-hour ride; keep it on the Record screen.
  • RipprTheme is currently a private composable applying darkColorScheme(). Dylan called the UI "simple and clean" and explicitly likes it — do not redesign it. Extract, do not restyle.
  • StatRow and BigStat are reusable; move them into a shared ui/components/.

Design

ui/
  theme/       Theme.kt, Color.kt
  components/  StatRow.kt, BigStat.kt, ConfirmDialog.kt
  record/      RecordScreen.kt, RecordViewModel.kt
  trips/       TripsScreen.kt, TripsViewModel.kt
  detail/      TripDetailScreen.kt, TripDetailViewModel.kt
  RipprNavHost.kt

Routes:

Route Screen
record Start destination
trips Trip list
trip/{tripId} Detail, tripId: Long argument

navigation-compose rather than hand-rolled state switching: the back stack from detail → list is worth not writing by hand, and it gives correct behaviour on the system back gesture for free.

ViewModels get the repository through a simple factory — the app already uses a @Volatile singleton for AppDatabase and T03 adds one for TripRepository. Hilt would be more ceremony than a three-screen app earns.

Navigation from Record → Trips goes in a top bar action, keeping the Record screen's centre free for the numeric readout.

Implementation

  1. Extract ui/theme/ from MainActivity.
  2. Move StatRow / BigStat into ui/components/, unchanged.
  3. Create RipprNavHost with the three routes.
  4. Add a ViewModel per screen with a shared factory.
  5. Reduce MainActivity to: permissions, battery exemption, setContent { RipprTheme { RipprNavHost() } }.
  6. Move service-intent dispatch into RecordViewModel.

Acceptance criteria

  • All three destinations reachable; system back works from detail
  • MainActivity holds no screen state
  • Visual appearance of the Record screen is unchanged
  • Permission flow and battery-optimisation prompt still function
  • Config change (rotation) does not lose state

Not verified. The unchecked items above, and the Compose UI tests below, were not done. Verification for T08–T11 was manual on the emulator (screenshots through the full flow) plus the existing unit and instrumented suites. Compose UI tests are outstanding — tracked in T18.

Tests

Instrumented Compose UI tests:

  • Navigate record → trips → detail → back → back
  • Rotation preserves screen state
  • Existing suites stay green

Risks / gotchas

  • Do not restyle. The brief was "simple and clean, I like that". This task is structural only.
  • tripId argument type. Room ids are Long; declare NavType.LongType explicitly or it silently arrives as a String.
  • The v1 UI reads state via collectAsStateWithLifecycle already — keep that, it is the correct choice over collectAsState for lifecycle-aware collection.

Out of scope

Screen content beyond wiring (T09, T10, T11); any map (T13).