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>
3.7 KiB
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:
- Header — name (inline-editable in T12) and start date/time
- 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.
- Summary grid — distance, moving time, elapsed time, max speed, average moving speed, elevation gain
- Elevation profile — line chart over distance-along-path
- Speed distribution — histogram in 10 km/h buckets
- 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
TripDetailViewModelloading trip, points, and segments onDispatchers.IO, exposingLoading | Ready(summary, profile, histogram, segments) | NotFound.- Summary grid from
RideSummary. ElevationChartcomposable —Canvas, path stroke, min/max labels.SpeedHistogramcomposable —Canvas, bars, bucket labels.- Segment list, shown conditionally.
- Map placeholder box.
NotFoundstate 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
NotFoundrather 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).