Adds ambientPositionProvider (never calls LocationSource.stop(), only the
idempotent start()) and wires it into ShellScaffold whenever no trip is
active, so the persistent background map centers and follows the device's
live GPS position at street-level zoom instead of sitting at (0,0)/zoom 2
until a recording starts. RideMap gains an ambientPosition param that the
existing chase-camera/pan-cancel/location-marker logic falls back to
whenever there are no recorded points, with recorded points always taking
priority. Also brings in the docs/feedback ticket set (FB-01..FB-05, README,
FEEDBACK.md) that this worktree's branch point predated.
Adds 10 tests (374 -> 384): RideMap-level ambient centering/chase/pan-cancel/
marker coverage in test/ride_map_test.dart, plus shell-wiring and
provider-level (never-calls-stop, mapEnabledProvider-off) coverage in
test/widget_test.dart. flutter analyze remains clean.
Adds MapConnectivityState, a shared tracker of tile-fetch outcomes (cache
miss + network failure) that flips every map into an animated skeleton
after 3 consecutive failures and recovers on a single success -- either an
ordinary fetch succeeding, or (once TileLayer has been fully unmounted in
skeleton mode) a periodic single-tile probe every 15s. Detected at the
fetch level rather than via an OS connectivity API, since a captive portal
or degraded connection can report "online" while every real fetch times
out.
SkeletonMapLayer reuses the Stitch exports' 40px grid-overlay treatment
with a shimmer sweep, replacing TileLayer entirely (never fetching
underneath its own placeholder) while markers/polylines keep rendering
since they come from local data. RideMap and the route planner's
independent FlutterMap both wire this in via a plain skeletonMode bool.
Moved the tile-source constants into a new tiles/tile_config.dart so the
connectivity probe (in the app-layer composition root) doesn't need to
import from ui/ to build its request URL.
Verified end-to-end on a real emulator: cut network, cleared the tile
cache, confirmed the skeleton renders after real fetch failures, then
confirmed automatic recovery within one probe interval once network
returned -- not just via the widget/unit tests that also cover this.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
flutter_map with the same OSM tiles and no-API-key reasoning that chose osmdroid.
All four load-bearing behaviours ported and, unlike the native app's map, tested:
one polyline per segment so a pause is a real gap, render-only decimation, the
zoom clamp at OSM's max tile zoom with a short-ride fallback, and a real user
agent. Speed colouring is bucketed per run rather than per-vertex, since neither
osmdroid nor flutter_map makes per-vertex paint reasonable.
Export via share_plus, which also handles the iPad popover anchor a naive port
forgets. It passes the raw stored points, never the map's decimated path.
164 tests passing, analyze clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>