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>
This commit is contained in:
2026-08-11 08:30:25 -05:00
parent 4debdfa518
commit 280fd7f988
106 changed files with 10377 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
# 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
- [x] All summary figures match `RideStatistics.compute()`
- [ ] Charts render for a real ride and degrade gracefully with <2 points
- [x] Segments section appears only when the ride was paused
- [x] Loading state shown while points load; no main-thread I/O
- [ ] Deleted trip shows `NotFound` rather than crashing
- [x] 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).