aeharding / aeharding/wingover
Plan map comes back on the continental default after a flight (arrival framing races the pin load)
- Langage dominant
- TypeScript
- Étoiles
- 0
- Forks
- 0
- Merge moyen
- 4 h 2 min
- PR mergées (30 j)
- 6
Description
Land a flight, tap **Plan**, and the plan map can come back on MapKit's
construction camera — the whole of North America — with the pilot's route
loaded but off in another continent. The route pill in the corner reads the
correct distance the entire time, so the pins are there; only the camera is
wrong. A long-press in that state drops a pin in Kansas.
Found while debugging #151 on a live iPhone 11 simulator (iOS 26.5). The
drill long-pressed the plan map right after a flight and the pin it created
landed at `{"latitude":39.79999999999999,"longitude":-98.5}` — MapKit JS's
construction default, the geographic centre of the contiguous US. A
screenshot of the same moment shows North America under a "Route: 7.38 mi"
pill.
## Mechanism
A flight replaces the entire tab shell:
```tsx
// src/ui/App.tsx:95
if (inFlight) return ;
```
so `PlanPage` unmounts at flight start and mounts fresh at flight end. On
that fresh mount two async things race:
- `loadPlan()` → `listPins()` → PouchDB (`src/ui/pages/PlanPage.tsx:93`,
kicked off by the mount effect at :104 and `useIonViewWillEnter` at :99);
- `createBackend()` → the MapKit JS map (`src/ui/map/MapCanvas.tsx`).
The arrival framing runs exactly once, when the map wins:
```tsx
// src/ui/pages/PlanPage.tsx:246-266
// Initial camera, once — reads whatever pins exist when the map arrives.
const frameInitial = useEffectEvent((next: MapView) => {
if (pins.length === 1) { … } else if (pins.length > 1) { … fitBounds … }
});
useEffect(() => {
if (!map) return;
if (skipArrivalFrameRef.current) { skipArrivalFrameRef.current = false; return; }
frameInitial(map);
}, [map]);
```
Straight after a flight MapKit is warm and the map arrives before PouchDB
answers, so `frameInitial` reads `pins.length === 0`, frames nothing, and
nobody frames again when the pins land a moment later. On a cold launch the
DB usually wins, which is why this reads as intermittent.
`skipArrivalFrameRef` does not cover it: that flag is for a MapCanvas
re-create that carried its predecessor's camera over
(`next.restoredCamera`), and a post-flight mount is a brand new instance
with nothing to restore.
## Fix sketch (not attempted here)
Frame on the FIRST resolved `listPins()` rather than on map arrival — i.e.
track "the plan has loaded at least once" and let the arrival framing wait
for both that and the map, still exactly once per map instance. It must not
become "frame whenever pins change", or dropping your first pin on an empty
plan would yank the camera to it.
## Not the cause of #151
Called out because #151's fix had to work around it (the drill re-taps
Center-on-me after its calibration flight for exactly this reason), but the
announcement drill never returned to the Plan tab before, so this is not
what turned it red.
Guide de contribution
Aucun guide de contribution indexé pour ce dépôt
Évaluation
Cette issue n'a pas encore été évaluée.