Files
rippr/docs/v3/V3-04-live-map.md
Dylan 342a8f8382 Break v3 into 16 tickets
One file per feature, in the v2 shape that worked: goal, context, design,
implementation, acceptance criteria, tests, risks, out of scope -- written before
implementing so the risks are on paper rather than walked into.

Three carry more weight than their size suggests. V3-01 ships the port's first
real migration, and since the destructive fallback is gone, getting addColumn and
a v1-database test right matters more than the feature. V3-08 forces a
routing-engine decision with ongoing cost, so it sits behind a RoutingService
interface mirroring what LocationSource did for GPS. V3-13 is not code at all --
it answers the three questions open since v2, and V3-15 may close unbuilt as a
result, which is a legitimate outcome.

Several tickets record constraints that are easy to lose: no activity picker in
front of Start, because the founding premise is press-and-go with gloves; the
Route-to-Trip foreign key must not cascade, or deleting an old plan deletes the
ride; V3-14 will deliberately break the parity harness by adding GPX <type>, and
that expectation should be updated rather than the check dropped; and V3-16 must
not regress the explicit text colours that exist because of the black-on-black bug.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 10:41:35 -05:00

2.7 KiB

V3-04 — Live map on the recording screen

Phase Live map · Depends on nothing · Size M · Status Not started

Goal

While recording, show the path as it is drawn, on the recording screen.

Context

v2 shipped without one deliberately: the phone rides in a pocket, so a live map would burn battery for something nobody is looking at. That decision is reversed — Dylan wants it for visual appeal, and because a mounted phone is now a real use case (V3-05).

The map component already exists (RideMap) and already handles per-segment polylines, render-only decimation and the zoom clamp. This ticket is mostly about lifecycle, not drawing.

Design

Two constraints survive the reversal and are non-negotiable:

  • RecordingEngine must never reference a map. Rendering belongs to a visible screen's widget lifecycle. The engine already exposes everything needed.
  • No tile fetch or redraw while backgrounded. A pocketed phone must cost exactly what it costs today.

Feed the map from a stream of the current trip's points. watchTripStats exists but returns aggregates; this needs the points themselves — add a watchPointsForTrip Drift stream, which updates naturally on each writer flush (~2 s), not per fix.

Follow the rider: keep the latest point centred, with a manual-pan override that stops auto-follow until re-enabled.

Behind the existing map toggle, off by default while it is unproven on battery.

Implementation

  1. watchPointsForTrip(tripId) in AppDatabase
  2. Hoist the map above the stats card on the record screen, behind the toggle
  3. WidgetsBindingObserver — on AppLifecycleState.paused, stop tile fetching; resume on resumed. This is the load-bearing part.
  4. Auto-follow with a pan override
  5. Keep the numeric readout visible; the map must not push SPEED off screen (the record screen already scrolls — see the overflow fix in port/PROGRESS.md)

Acceptance criteria

  • The path appears and extends while recording
  • Backgrounding the app stops all tile activity, verified in a network log
  • RecordingEngine still has no map import — grep it
  • With the toggle off, no map widget is constructed at all
  • Speed and elapsed remain visible without scrolling on a common phone size

Tests

  • Widget: map appears only when recording and the toggle is on
  • Widget: lifecycle transition to paused stops the tile layer
  • The existing map tests still pass

Risks

  • Battery. This is the whole reason v2 said no. Measure before defaulting it on (V3-13).
  • Redrawing per fix rather than per flush would be wasteful; drive from the database stream, which is already batched.

Out of scope

Mounted mode (V3-05). Other riders on the map (v4).