Files
rippr/docs/v3/V3-06-notification-stats.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.2 KiB

V3-06 — Live stats in the notification

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

Goal

Distance and duration readable from the notification shade without unlocking.

Context

The ongoing notification currently says "Rippr is recording / Tracking your ride" — static text. During a pocketed ride that is a wasted surface.

Constraint that shapes this whole ticket: the notification is owned by geolocator's ForegroundNotificationConfig, which takes fixed strings at stream-subscription time and offers no update path and no actions. That is the parity gap recorded in ../port/PARITY-AUDIT.md.

Design

Two honest options:

A. Restart the position stream with new text. Cheap to write, but it tears down and re-establishes location updates every time — unacceptable during recording.

B. Take the notification back with flutter_local_notifications, and let geolocator raise a silent minimal one. More moving parts, but it also restores Pause/Resume actions — closing the one capability lost in the port.

Recommend B, precisely because it buys back the parity gap as well.

Implementation

  1. Add flutter_local_notifications
  2. Own a notification on the same channel, updated on each writer flush (~2 s), not per fix
  3. Pause/Resume actions routed back into RecordingEngine
  4. Android 13+ notification permission is already requested

Acceptance criteria

  • Distance and elapsed update while riding, without unlocking
  • Pause and Resume work from the shade
  • Location updates are not interrupted when the notification changes
  • The notification cannot be swiped away mid-ride

Tests

  • Unit: notification text formatting from a trip
  • Integration on a device: start, confirm the text advances, pause from the shade, confirm the engine actually paused (check the database, do not trust the UI)

Risks

  • Two notification sources fighting is the obvious failure. Verify only one is visible.
  • An update per fix would be a battery and jank problem; drive it from the flush.

Out of scope

iOS. There is no equivalent live notification surface; a Live Activity is a much larger piece of work and belongs in its own ticket.