# T10 — Trips list screen **Phase** 4 · **Depends on** T02, T08 · **Status** Done ## Goal A scrollable list of completed rides, each showing enough to identify it, tapping through to detail. Done when past rides are browsable and the list updates reactively as rides complete. ## Context There is currently no way to see a past ride at all — v1 has one screen showing lifetime totals. This is the first place trips become visible as objects. `TripDao.observeCompletedTrips()` from T02 provides the data, ordered `startedAt DESC`. All displayed values are already persisted on the `Trip` row by T07, so **this screen runs no computation** — no loading points, no statistics. That keeps it fast with hundreds of rides. ## Design One row per trip: ``` Sat 9 Aug · 14:32 47.2 km 1h 12m moving · max 118 km/h ▸ ``` - Name if set, otherwise a date-derived label ("Sat 9 Aug · 14:32") - Distance as the right-aligned prominent figure - Moving time and max speed as the secondary line - Monospace with `tabular-nums` for the figures so columns align down the list **Empty state** matters here — a new install has no trips, and an empty screen with no explanation reads as broken. Show a short line pointing at the Record tab. **An in-progress trip is excluded** (`endedAt IS NULL`). A half-finished ride in the history list with a growing distance would be confusing; the Record screen owns that. Grouping by month is tempting but premature. Revisit when there are enough rides to justify it. ## Implementation 1. `TripsViewModel` exposing `observeCompletedTrips()` as state. 2. `LazyColumn` of `TripRow`. 3. Date formatting via `java.time` with the device locale and zone — **not** hardcoded patterns. Ride timestamps come from `Location.time`, which is UTC epoch millis. 4. Reuse `Telemetry.formatDuration`. 5. Distance formatting helper — km with one decimal; metres below 1 km. 6. Tap navigates to `trip/{tripId}`. 7. Empty state. ## Acceptance criteria - [x] Completed trips listed newest first - [x] Active trip excluded - [x] Empty state shown on a fresh install - [x] Tap opens the right trip - [ ] List updates when a ride completes, without manual refresh - [x] No point-table reads — the screen uses only `Trip` rows > **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: - Empty state on no data - Rows render in the right order - Tap emits the right navigation event - Active trip absent from the list Instrumented DAO test for ordering and the `endedAt IS NULL` exclusion. ## Risks / gotchas - **Timezone.** `Location.time` is UTC. Format in the device zone or a ride at 21:00 local shows as tomorrow. - **Do not compute stats here.** If a value is missing from the `Trip` row, fix T07 rather than loading points in the list — that path leads to a screen that janks after fifty rides. ## Out of scope Rename/delete/merge (T12); detail content (T11).