Files
samplez/rippr-src/docs/v2/11-trip-detail.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.7 KiB
Raw Blame History

T11 — Trip detail screen (stats only)

Phase 4 · Depends on T05, T08 · Status Done

Goal

A full breakdown of a single ride — summary figures, elevation profile, speed distribution — with a deliberate placeholder where the map will go. Done when every statistic renders correctly and the screen is verifiable before osmdroid lands.

Context

Splitting this from the map is intentional. osmdroid brings tile caching, MapView lifecycle, and coordinate projection — three sources of subtle failure. Building the screen first means that when the map misbehaves in T13, the surrounding screen is already known-good and the bug has nowhere to hide.

Data comes from RideStatistics.compute() (T05) over pointsForTrip(id) and segmentsForTrip(id). Unlike the list screen, this one does load points — a few thousand rows for one trip is fine, and the derived series need them.

Design

Sections top to bottom:

  1. Header — name (inline-editable in T12) and start date/time
  2. Map placeholder — a bordered box captioned "Map coming in T13", replaced wholesale by T13/T14. Reserving the space now means the layout does not shift later.
  3. Summary grid — distance, moving time, elapsed time, max speed, average moving speed, elevation gain
  4. Elevation profile — line chart over distance-along-path
  5. Speed distribution — histogram in 10 km/h buckets
  6. Segments — listed only when there is more than one, showing where the ride paused

Charts are drawn with Compose Canvas, not a charting library. Two simple series do not justify a dependency, and hand-drawn keeps them consistent with the existing visual language.

Both charts need: an explicit empty/insufficient-data state, axis labels with units, and tabular-nums on any numeric label.

Point loading happens off the main thread in the ViewModel, with a loading state. A three-hour ride is ~21,600 rows; loading that synchronously would jank the transition into the screen.

Implementation

  1. TripDetailViewModel loading trip, points, and segments on Dispatchers.IO, exposing Loading | Ready(summary, profile, histogram, segments) | NotFound.
  2. Summary grid from RideSummary.
  3. ElevationChart composable — Canvas, path stroke, min/max labels.
  4. SpeedHistogram composable — Canvas, bars, bucket labels.
  5. Segment list, shown conditionally.
  6. Map placeholder box.
  7. NotFound state for a deleted trip.

Acceptance criteria

  • All summary figures match RideStatistics.compute()
  • Charts render for a real ride and degrade gracefully with <2 points
  • Segments section appears only when the ride was paused
  • Loading state shown while points load; no main-thread I/O
  • Deleted trip shows NotFound rather than crashing
  • Map placeholder occupies the space the real map will take

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

Compose UI tests: loading, ready, not-found, single-segment vs multi-segment. Unit tests for the chart data-preparation helpers (scaling, bucketing) — the drawing itself is verified by screenshot on the emulator.

Risks / gotchas

  • Main-thread point loading is the obvious trap here. Keep it in the ViewModel on IO.
  • Chart division by zero when all altitudes or speeds are identical — a flat ride gives a zero-height range. Guard the scaling.
  • Do not start integrating the map in this task. The separation is the point.

Out of scope

The map (T13, T14), rename/delete/merge (T12), export (T16).