diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/frontend/app/components/StopMapModal.tsx | 34 | ||||
| -rw-r--r-- | src/frontend/app/components/StopMapSheet.tsx | 75 |
2 files changed, 61 insertions, 48 deletions
diff --git a/src/frontend/app/components/StopMapModal.tsx b/src/frontend/app/components/StopMapModal.tsx index a5a87fd..74f20d9 100644 --- a/src/frontend/app/components/StopMapModal.tsx +++ b/src/frontend/app/components/StopMapModal.tsx @@ -1,10 +1,10 @@ import maplibregl from "maplibre-gl"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import Map, { - Layer, - Marker, - Source, - type MapRef + Layer, + Marker, + Source, + type MapRef } from "react-map-gl/maplibre"; import { Sheet } from "react-modal-sheet"; import { useApp } from "~/AppContext"; @@ -55,6 +55,7 @@ export const StopMapModal: React.FC<StopMapModalProps> = ({ const [styleSpec, setStyleSpec] = useState<any | null>(null); const mapRef = useRef<MapRef | null>(null); const hasFitBounds = useRef(false); + const userInteracted = useRef(false); const [shapeData, setShapeData] = useState<any | null>(null); const [previousShapeData, setPreviousShapeData] = useState<any | null>(null); @@ -95,6 +96,8 @@ export const StopMapModal: React.FC<StopMapModalProps> = ({ const handleCenter = useCallback(() => { if (!mapRef.current) return; + if (userInteracted.current) return; + const points: { lat: number; lon: number }[] = []; const addShapePoints = (data: any) => { @@ -230,6 +233,7 @@ export const StopMapModal: React.FC<StopMapModalProps> = ({ useEffect(() => { if (!isOpen) { hasFitBounds.current = false; + userInteracted.current = false; setShapeData(null); setPreviousShapeData(null); } @@ -361,6 +365,23 @@ export const StopMapModal: React.FC<StopMapModalProps> = ({ attributionControl={{compact: false, customAttribution: "Concello de Vigo & Viguesa de Transportes SL"}} ref={mapRef} interactive={true} + onMove={(e) => { + if (e.originalEvent) { + userInteracted.current = true; + } + }} + onDragStart={() => { + userInteracted.current = true; + }} + onZoomStart={() => { + userInteracted.current = true; + }} + onRotateStart={() => { + userInteracted.current = true; + }} + onPitchStart={() => { + userInteracted.current = true; + }} > {/* Previous Shape Layer */} {previousShapeData && selectedBus && ( @@ -531,7 +552,10 @@ export const StopMapModal: React.FC<StopMapModalProps> = ({ type="button" aria-label="Center" className="center-btn" - onClick={handleCenter} + onClick={() => { + userInteracted.current = false; + handleCenter(); + }} title="Center view" > <svg diff --git a/src/frontend/app/components/StopMapSheet.tsx b/src/frontend/app/components/StopMapSheet.tsx index d70fcb6..afad1c3 100644 --- a/src/frontend/app/components/StopMapSheet.tsx +++ b/src/frontend/app/components/StopMapSheet.tsx @@ -39,14 +39,33 @@ export const StopMap: React.FC<StopMapProps> = ({ const { theme } = useApp(); const [styleSpec, setStyleSpec] = useState<any | null>(null); const mapRef = useRef<MapRef | null>(null); - const hasFitBounds = useRef(false); const [userPosition, setUserPosition] = useState<{ latitude: number; longitude: number; accuracy?: number; } | null>(null); const geoWatchId = useRef<number | null>(null); - const [zoom, setZoom] = useState<number>(16); + const [viewState, setViewState] = useState(() => { + let latitude = 42.2406; + let longitude = -8.7207; + if (stop.latitude && stop.longitude) { + latitude = stop.latitude; + longitude = stop.longitude; + } else { + const pos = circulations.find((c) => c.currentPosition)?.currentPosition; + if (pos) { + latitude = pos.latitude; + longitude = pos.longitude; + } + } + return { + latitude, + longitude, + zoom: 16, + }; + }); + const { zoom } = viewState; + const hasFitted = useRef(false); const [moveTick, setMoveTick] = useState<number>(0); const [showAttribution, setShowAttribution] = useState(false); const [shapes, setShapes] = useState<Record<string, any>>({}); @@ -200,28 +219,16 @@ export const StopMap: React.FC<StopMapProps> = ({ }; }, []); - const center = useMemo(() => { - if (stop.latitude && stop.longitude) { - return { latitude: stop.latitude, longitude: stop.longitude }; - } - // fallback to first available bus position - const pos = circulations.find((c) => c.currentPosition)?.currentPosition; - return pos - ? { latitude: pos.latitude, longitude: pos.longitude } - : { latitude: 42.2406, longitude: -8.7207 }; // Vigo approx fallback - }, [stop.latitude, stop.longitude, circulations]); - const busPositions = useMemo( () => circulations.filter((c) => !!c.currentPosition), [circulations] ); - // Fit bounds to stop + buses, with ~1km padding each side, with a modest animation - // Only fit bounds on the first load, not on subsequent updates - useEffect(() => { - if (!styleSpec || !mapRef.current || hasFitBounds.current) return; + const handleMapLoad = (e: any) => { + if (hasFitted.current) return; + hasFitted.current = true; - const map = mapRef.current.getMap(); + const map = e.target; // Handle missing sprite images to suppress console warnings const handleStyleImageMissing = (e: any) => { @@ -237,10 +244,7 @@ export const StopMap: React.FC<StopMapProps> = ({ map.on("styleimagemissing", handleStyleImageMissing); const points = computeFocusPoints(); - if (points.length === 0) { - map.off("styleimagemissing", handleStyleImageMissing); - return; - } + if (points.length === 0) return; let minLat = points[0].lat, maxLat = points[0].lat, @@ -266,28 +270,16 @@ export const StopMap: React.FC<StopMapProps> = ({ try { if (points.length === 1) { const only = points[0]; - mapRef.current - .getMap() - .jumpTo({ center: [only.lon, only.lat], zoom: 16 }); + map.jumpTo({ center: [only.lon, only.lat], zoom: 16 }); } else { - mapRef.current.fitBounds(bounds, { + map.fitBounds(bounds, { padding: padding as any, duration: 700, maxZoom: 17, } as any); } - hasFitBounds.current = true; } catch {} - - return () => { - if (mapRef.current) { - const map = mapRef.current.getMap(); - if (map) { - map.off("styleimagemissing", handleStyleImageMissing); - } - } - }; - }, [styleSpec, stop.latitude, stop.longitude, busPositions, userPosition]); + }; const handleCenter = () => { if (!mapRef.current) return; @@ -334,17 +326,14 @@ export const StopMap: React.FC<StopMapProps> = ({ {styleSpec && ( <Map mapLib={maplibregl as any} - initialViewState={{ - latitude: center.latitude, - longitude: center.longitude, - zoom: 16, - }} + {...viewState} style={{ width: "100%", height: "100%" }} mapStyle={styleSpec} attributionControl={false} ref={mapRef} + onLoad={handleMapLoad} onMove={(e) => { - setZoom(e.viewState.zoom); + setViewState(e.viewState); setMoveTick((t) => (t + 1) % 1000000); }} > |
