UI redesign: 8 tickets from the Stitch export analysis
Same shape as docs/v3/: Goal/Context/Design/Implementation/Acceptance/Tests/Risks/Out-of-scope, written before implementing. UI-01: persistent 4-tab shell (StatefulShellRoute) replacing the current push/pop stack rooted at Record, with a single shared background map dimmed behind every tab and no header anywhere. UI-02: animated skeleton map for the no-cache/no-network case, since the map is now a permanent background presence rather than opt-in per screen. UI-03: shared GlassPanel/FloatingPill/PulsingLocationMarker component kit. UI-04: draggable, resizable HUD telemetry widgets with a Settings visibility toggle -- split out from the screen redesign itself since it's a genuinely separate piece of engineering (a small widget-layout system, not a reskin). UI-05: Map HUD/Record screen redesign -- the design north star every other screen ticket restyles toward. UI-06: Plan & Route Planning -- correct in principle, explicitly restyled toward UI-05 rather than copying the Stitch export's drifted styling. UI-07: Rides History -- live dimmed map background (not the mockup's static image), search/filter, real per-ride map thumbnails, and real distinct summary stats (fixing the export's 4-identical-cards generation artifact). UI-08: theme migration to Modern Professional Dark, superseding V3-16's safety-orange direction. README documents the dependency order, and two categories worth keeping separate: what was named explicitly by the person commissioning this (map-on-every-tab, no header, offline skeleton, customizable HUD) versus what the mockups themselves got inconsistent (nav icon count, duplicate summary cards, wrong active-tab highlight) and should not be copied as intentional.
This commit is contained in:
82
docs/ui-redesign/UI-02-offline-skeleton-map.md
Normal file
82
docs/ui-redesign/UI-02-offline-skeleton-map.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# UI-02 — Offline / no-connection skeleton map
|
||||
|
||||
**Depends on** UI-01 · **Size** S · **Status** Not started
|
||||
|
||||
## Goal
|
||||
When the map has no tiles to show — no cached tiles for the current view and no network
|
||||
to fetch fresh ones — show an animated skeleton in place of a broken or blank map, on
|
||||
every tab, since UI-01 makes the map a permanent background presence rather than
|
||||
something a rider opts into per-screen.
|
||||
|
||||
## Context
|
||||
By explicit instruction: *"If we do not have connection for the map, we should just show
|
||||
an animated skeleton map so it doesn't look weird."* Once UI-01 ships, the map is always
|
||||
on screen somewhere — a blank grey rectangle or a grid of broken-image icons behind every
|
||||
tab, all the time, on a phone with no signal, is a much worse look than it was when the
|
||||
map only appeared on the one screen a rider explicitly opened.
|
||||
|
||||
This composes with existing infrastructure rather than duplicating it:
|
||||
`CachedTileProvider` (V3-11) already tries the offline tile cache before the network, so
|
||||
"no connection" in practice means *both* the cache and the network failed for the tiles
|
||||
currently in view.
|
||||
|
||||
## Design
|
||||
Detect at the `CachedTileProvider`/`TileLayer` level, not by asking the OS for
|
||||
connectivity state directly — a connectivity API can report "online" while the actual
|
||||
tile fetch still times out (captive portal, degraded connection), and the tile fetch's
|
||||
own success/failure is the only thing that actually matters here.
|
||||
|
||||
- Track a rolling failure count for in-flight tile fetches (cache miss **and** network
|
||||
fetch failed) over a short window. Crossing a small threshold (e.g. 3 consecutive
|
||||
failures) flips the map into skeleton mode; a single successful fetch flips it back.
|
||||
- Skeleton mode replaces the `TileLayer` with a static, non-fetching placeholder — never
|
||||
a widget that itself keeps trying and failing in a loop.
|
||||
- Placeholder look: a shimmer/gradient-sweep animation over a neutral grid pattern
|
||||
(reuse the "technical grid overlay" treatment already present in the Stitch exports'
|
||||
`map-grid-overlay` CSS — 40px faint grid lines — as the static base under the shimmer),
|
||||
not a spinner. A map-shaped thing that is clearly still loading, not an error state.
|
||||
- Recheck periodically (not on every frame) so the map recovers automatically the moment
|
||||
connectivity returns, without the rider having to do anything.
|
||||
|
||||
## Implementation
|
||||
1. Wrap `CachedTileProvider` (or add a sibling) that reports fetch outcomes to a small
|
||||
`MapConnectivityState` — rolling window, threshold, debounced flip.
|
||||
2. `SkeletonMapLayer` widget: the grid + shimmer, sized to fill the same space a
|
||||
`TileLayer` would.
|
||||
3. `RideMap`/`PersistentMapBackground` (from UI-01) watches `MapConnectivityState` and
|
||||
swaps `TileLayer` for `SkeletonMapLayer` when in skeleton mode — markers/polylines
|
||||
(the rider's own path, planned route) keep rendering on top either way, since those
|
||||
come from local data, not tiles.
|
||||
4. A cap on retry frequency while in skeleton mode — this must not turn into a tile-
|
||||
fetch retry loop that itself violates OSM's usage policy the way V3-11's design
|
||||
explicitly guards against.
|
||||
|
||||
## Acceptance criteria
|
||||
- [ ] With no cached tiles and no network, the map area shows an animated skeleton, not
|
||||
blank space or broken-image icons
|
||||
- [ ] Recovery is automatic: once tiles become fetchable again, the skeleton is replaced
|
||||
without user action
|
||||
- [ ] The rider's live position/path and any planned route still render on top of the
|
||||
skeleton (only the base tiles are missing)
|
||||
- [ ] Skeleton mode does not itself hammer the tile server with retries
|
||||
- [ ] Behaves correctly across all four tabs (UI-01's shared background map), not just
|
||||
the Map tab
|
||||
|
||||
## Tests
|
||||
- Unit: the failure-count/threshold/debounce logic that decides skeleton vs. live,
|
||||
exercised with fakes (no real network) — mirrors V3-11's `tile_downloader_test.dart`
|
||||
pattern of injecting fake fetch outcomes
|
||||
- Widget: `SkeletonMapLayer` renders when connectivity state says offline; `TileLayer`
|
||||
renders when it says online
|
||||
- Widget: markers/polylines still render in skeleton mode
|
||||
|
||||
## Risks
|
||||
- Flapping between skeleton and live on a marginal connection would be worse than
|
||||
either state alone — the debounce window is what prevents this; tune it based on real
|
||||
behavior, not a guess (echoes V3-13's "measure, don't tune blind" discipline).
|
||||
- Must not let skeleton-mode retries become the kind of bulk/rapid tile request V3-11's
|
||||
design explicitly exists to avoid.
|
||||
|
||||
## Out of scope
|
||||
General offline-mode UX beyond the map itself (e.g. graying out map-dependent buttons).
|
||||
Manual retry controls — automatic recovery is the whole point.
|
||||
Reference in New Issue
Block a user