mirror of
https://github.com/zvx-echo6/navi.git
synced 2026-05-20 22:54:42 +02:00
fix: mobile UX — GPS permission flow, traffic toggle, overflow
GPS permission:
- Remove silent mount-time getCurrentPosition calls that cause iOS Safari
to cache a "denied" state without ever prompting the user
- LocateButton always retries getCurrentPosition on tap (user gesture)
- Only show "denied" toast on PERMISSION_DENIED (code 1), not timeout
- MapView watchPosition now starts only after confirmed grant, not unconditionally
Traffic toggle:
- Fix isStyleLoaded() race in LayerControl — if style not loaded when
toggle fires, defer to map.once("style.load") instead of silently bailing
- Change outside-click handler from mousedown to pointerdown for mobile
Mobile UX (from prior session):
- Add LocateButton component (crosshair GPS locate/re-center)
- Reposition layer control + locate button to top-right on mobile
(below MapLibre nav controls, above bottom sheet)
- ModeSelector: add min-w-0 to prevent flex overflow at 390px
- StopItem: remove button visible on touch (60% opacity vs hover-only)
- Panel: overflow-x-hidden + safe-area-inset-bottom on mobile sheet
- Body overflow-x guard on mobile viewports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4020d5ae0a
commit
03e9780834
8 changed files with 216 additions and 72 deletions
|
|
@ -40,27 +40,49 @@ export default function LayerControl({ mapRef }) {
|
|||
|
||||
// Apply layers when prefs change
|
||||
useEffect(() => {
|
||||
const map = mapRef?.current?.getMap?.()
|
||||
if (!map || !map.isStyleLoaded()) return
|
||||
const mapView = mapRef?.current
|
||||
if (!mapView) return
|
||||
const map = mapView.getMap?.()
|
||||
if (!map) return
|
||||
|
||||
if (hillshade && hasFeature('has_hillshade')) {
|
||||
mapRef.current.addHillshadeLayer?.()
|
||||
const apply = () => {
|
||||
if (hillshade && hasFeature('has_hillshade')) {
|
||||
mapView.addHillshadeLayer?.()
|
||||
} else {
|
||||
mapView.removeHillshadeLayer?.()
|
||||
}
|
||||
}
|
||||
|
||||
if (map.isStyleLoaded()) {
|
||||
apply()
|
||||
} else {
|
||||
mapRef.current.removeHillshadeLayer?.()
|
||||
map.once('style.load', apply)
|
||||
}
|
||||
savePrefs({ hillshade, traffic })
|
||||
return () => map.off('style.load', apply)
|
||||
}, [hillshade, mapRef])
|
||||
|
||||
useEffect(() => {
|
||||
const map = mapRef?.current?.getMap?.()
|
||||
if (!map || !map.isStyleLoaded()) return
|
||||
const mapView = mapRef?.current
|
||||
if (!mapView) return
|
||||
const map = mapView.getMap?.()
|
||||
if (!map) return
|
||||
|
||||
if (traffic && hasFeature('has_traffic_overlay')) {
|
||||
mapRef.current.addTrafficLayer?.()
|
||||
const apply = () => {
|
||||
if (traffic && hasFeature('has_traffic_overlay')) {
|
||||
mapView.addTrafficLayer?.()
|
||||
} else {
|
||||
mapView.removeTrafficLayer?.()
|
||||
}
|
||||
}
|
||||
|
||||
if (map.isStyleLoaded()) {
|
||||
apply()
|
||||
} else {
|
||||
mapRef.current.removeTrafficLayer?.()
|
||||
map.once('style.load', apply)
|
||||
}
|
||||
savePrefs({ hillshade, traffic })
|
||||
return () => map.off('style.load', apply)
|
||||
}, [traffic, mapRef])
|
||||
|
||||
// Close on outside click
|
||||
|
|
@ -71,8 +93,8 @@ export default function LayerControl({ mapRef }) {
|
|||
setOpen(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', handleClick)
|
||||
return () => document.removeEventListener('mousedown', handleClick)
|
||||
document.addEventListener('pointerdown', handleClick)
|
||||
return () => document.removeEventListener('pointerdown', handleClick)
|
||||
}, [open])
|
||||
|
||||
const showHillshade = hasFeature('has_hillshade')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue