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
|
|
@ -197,36 +197,6 @@ const MapView = forwardRef(function MapView(_, ref) {
|
|||
|
||||
map.addControl(new maplibregl.NavigationControl(), 'top-right')
|
||||
|
||||
// GPS tracking — creates chevron or dot marker
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
const { latitude, longitude } = pos.coords
|
||||
if (useStore.getState().stops.length === 0) {
|
||||
map.flyTo({ center: [longitude, latitude], zoom: 12, duration: 1500 })
|
||||
}
|
||||
useStore.getState().setUserLocation({ lat: latitude, lon: longitude })
|
||||
useStore.getState().setGeoPermission('granted')
|
||||
createOrUpdateGpsMarker(map, latitude, longitude, null)
|
||||
},
|
||||
() => {
|
||||
useStore.getState().setGeoPermission('denied')
|
||||
},
|
||||
{ enableHighAccuracy: false, timeout: 8000, maximumAge: 300000 }
|
||||
)
|
||||
|
||||
// Watch for heading changes
|
||||
watchIdRef.current = navigator.geolocation.watchPosition(
|
||||
(pos) => {
|
||||
const { latitude, longitude, heading } = pos.coords
|
||||
useStore.getState().setUserLocation({ lat: latitude, lon: longitude })
|
||||
createOrUpdateGpsMarker(map, latitude, longitude, heading)
|
||||
},
|
||||
() => {},
|
||||
{ enableHighAccuracy: true, maximumAge: 5000 }
|
||||
)
|
||||
}
|
||||
|
||||
// Map click — drop pin and reverse geocode
|
||||
map.on('click', (e) => {
|
||||
// If a stop pin was just clicked, skip the pin-drop
|
||||
|
|
@ -335,7 +305,34 @@ const MapView = forwardRef(function MapView(_, ref) {
|
|||
}
|
||||
}
|
||||
|
||||
// Swap map theme when store.theme changes
|
||||
// React to permission changes from LocateButton (when user grants after initial denial)
|
||||
useEffect(() => {
|
||||
const map = mapInstance.current
|
||||
if (!map || geoPermission !== 'granted') return
|
||||
|
||||
// If marker already exists, watchPosition is already running — nothing to do
|
||||
if (gpsMarkerRef.current) return
|
||||
|
||||
// Permission was just granted (likely from LocateButton) — create marker + start tracking
|
||||
const loc = useStore.getState().userLocation
|
||||
if (loc) {
|
||||
createOrUpdateGpsMarker(map, loc.lat, loc.lon, null)
|
||||
}
|
||||
|
||||
if (!watchIdRef.current) {
|
||||
watchIdRef.current = navigator.geolocation.watchPosition(
|
||||
(pos) => {
|
||||
const { latitude, longitude, heading } = pos.coords
|
||||
useStore.getState().setUserLocation({ lat: latitude, lon: longitude })
|
||||
createOrUpdateGpsMarker(map, latitude, longitude, heading)
|
||||
},
|
||||
() => {},
|
||||
{ enableHighAccuracy: true, maximumAge: 5000 }
|
||||
)
|
||||
}
|
||||
}, [geoPermission])
|
||||
|
||||
// Swap map theme when store.theme changes
|
||||
useEffect(() => {
|
||||
const map = mapInstance.current
|
||||
if (!map || currentThemeRef.current === theme) return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue