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
20
src/App.jsx
20
src/App.jsx
|
|
@ -7,6 +7,7 @@ import MapView from './components/MapView'
|
|||
import Panel from './components/Panel'
|
||||
import PlaceDetail from './components/PlaceDetail'
|
||||
import LayerControl from './components/LayerControl'
|
||||
import LocateButton from './components/LocateButton'
|
||||
|
||||
export default function App() {
|
||||
const mapViewRef = useRef(null)
|
||||
|
|
@ -24,24 +25,6 @@ export default function App() {
|
|||
const setRouteLoading = useStore((s) => s.setRouteLoading)
|
||||
const setRouteError = useStore((s) => s.setRouteError)
|
||||
const clearRoute = useStore((s) => s.clearRoute)
|
||||
const setUserLocation = useStore((s) => s.setUserLocation)
|
||||
const setGeoPermission = useStore((s) => s.setGeoPermission)
|
||||
|
||||
// Proactive geolocation request on mount
|
||||
useEffect(() => {
|
||||
if (!navigator.geolocation) {
|
||||
setGeoPermission('denied')
|
||||
return
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
setUserLocation({ lat: pos.coords.latitude, lon: pos.coords.longitude })
|
||||
setGeoPermission('granted')
|
||||
},
|
||||
() => setGeoPermission('denied'),
|
||||
{ enableHighAccuracy: false, timeout: 8000, maximumAge: 300000 }
|
||||
)
|
||||
}, [setUserLocation, setGeoPermission])
|
||||
|
||||
// Fetch route when stops, mode, gpsOrigin, or geoPermission change (debounced 500ms)
|
||||
// NOTE: userLocation is NOT a dep — read from store inside the callback to avoid re-routing on every GPS update
|
||||
|
|
@ -108,6 +91,7 @@ export default function App() {
|
|||
<Panel onManeuverClick={handleManeuverClick} />
|
||||
<PlaceDetail />
|
||||
<LayerControl mapRef={mapViewRef} />
|
||||
<LocateButton mapRef={mapViewRef} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue