diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index f93c40f..7e7ddf9 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -570,6 +570,7 @@ export function App() { ); const clearRouteOutcome = useCallback(() => { + courseAssistant.stop(); routeRequestId.current += 1; routeWeatherRequestId.current += 1; setRoute(null); @@ -580,7 +581,20 @@ export function App() { setRouteWeatherError(null); setRouteWeatherLoading(false); setRouteLoading(false); - }, []); + }, [courseAssistant.stop]); + + const editRoute = useCallback(() => { + clearRouteOutcome(); + setPickMode(null); + }, [clearRouteOutcome]); + + const cancelRoute = useCallback(() => { + setRouteStart(null); + setDestination(null); + setWaypoints([]); + clearRouteOutcome(); + setPickMode(null); + }, [clearRouteOutcome]); const saveCurrentBoat = useCallback((profile: BoatProfile) => { const stored = persistBoatProfile(profile); @@ -675,11 +689,12 @@ export function App() { return; } + courseAssistant.stop(); setRoute(selected); if (activeVesselProfile) { loadRouteWeather(selected, activeVesselProfile); } - }, [activeVesselProfile, loadRouteWeather, routeOptions]); + }, [activeVesselProfile, courseAssistant.stop, loadRouteWeather, routeOptions]); const clearStart = useCallback(() => { setRouteStart(null); @@ -716,6 +731,7 @@ export function App() { }, [clearRouteOutcome]); const loadOfflineVoyage = useCallback((voyage: OfflineVoyage) => { + courseAssistant.stop(); routeRequestId.current += 1; routeWeatherRequestId.current += 1; setRouteStart(voyage.plan.start); @@ -737,7 +753,7 @@ export function App() { if (voyage.plan.vesselProfile && navigator.onLine) { loadRouteWeather(offlineRoute, voyage.plan.vesselProfile); } - }, [loadRouteWeather]); + }, [courseAssistant.stop, loadRouteWeather]); const uiMode = anchorWatch.phase !== "idle" ? "anchor" @@ -1022,6 +1038,8 @@ export function App() { onClearDestination={clearDestination} onUseGpsAsStart={useGpsAsStart} onSelectRoute={selectRouteOption} + onEditRoute={editRoute} + onCancelRoute={cancelRoute} guidanceActive={courseAssistant.active} onStartGuidance={startCourseAssistant} onEditBoat={() => { diff --git a/apps/web/src/components/MapView.tsx b/apps/web/src/components/MapView.tsx index abe84ac..f798869 100644 --- a/apps/web/src/components/MapView.tsx +++ b/apps/web/src/components/MapView.tsx @@ -417,8 +417,8 @@ export function MapView({ map.addSource("route-guidance", { type: "geojson", data: guidanceFeatures( - guidanceActiveRef.current ? positionRef.current : null, - guidanceActiveRef.current ? guidanceTargetRef.current : null + routeRef.current && guidanceActiveRef.current ? positionRef.current : null, + routeRef.current && guidanceActiveRef.current ? guidanceTargetRef.current : null ) }); map.addSource("anchor-watch", { @@ -950,13 +950,18 @@ export function MapView({ useEffect(() => { const map = mapRef.current; - if (!map?.isStyleLoaded()) { + const source = map?.getSource("route-guidance") as GeoJSONSource | undefined; + if (!source) { return; } - const source = map.getSource("route-guidance") as GeoJSONSource | undefined; - source?.setData(guidanceFeatures(guidanceActive ? position : null, guidanceActive ? guidanceTarget : null)); - }, [guidanceActive, guidanceTarget, position]); + source.setData( + guidanceFeatures( + route && guidanceActive ? position : null, + route && guidanceActive ? guidanceTarget : null + ) + ); + }, [guidanceActive, guidanceTarget, position, route]); useEffect(() => { const map = mapRef.current; @@ -987,42 +992,45 @@ export function MapView({ useEffect(() => { const map = mapRef.current; - if (!map?.isStyleLoaded()) { + const source = map?.getSource("start-point") as GeoJSONSource | undefined; + if (!source) { return; } - const source = map.getSource("start-point") as GeoJSONSource | undefined; - source?.setData(startPoint ? pointFeature(startPoint) : emptyPoint()); + source.setData(startPoint ? pointFeature(startPoint) : emptyPoint()); }, [startPoint]); useEffect(() => { const map = mapRef.current; - if (!map?.isStyleLoaded()) { + const source = map?.getSource("destination") as GeoJSONSource | undefined; + if (!source) { return; } - const source = map.getSource("destination") as GeoJSONSource | undefined; - source?.setData(destination ? pointFeature(destination) : emptyPoint()); + source.setData(destination ? pointFeature(destination) : emptyPoint()); }, [destination]); useEffect(() => { const map = mapRef.current; - if (!map?.isStyleLoaded()) { + const source = map?.getSource("waypoints") as GeoJSONSource | undefined; + if (!source) { return; } - const source = map.getSource("waypoints") as GeoJSONSource | undefined; - source?.setData(waypointFeatures(waypoints)); + source.setData(waypointFeatures(waypoints)); }, [waypoints]); useEffect(() => { const map = mapRef.current; - if (!map?.isStyleLoaded()) { + if (!map) { + return; + } + const source = map.getSource("route") as GeoJSONSource | undefined; + if (!source) { return; } - const source = map.getSource("route") as GeoJSONSource | undefined; - source?.setData(route ? routeFeature(route) : emptyLine()); + source.setData(route ? routeFeature(route) : emptyLine()); fitRouteOnMap(map, route); }, [route]); diff --git a/apps/web/src/components/RoutePlanner.tsx b/apps/web/src/components/RoutePlanner.tsx index 3a6afa4..a56700d 100644 --- a/apps/web/src/components/RoutePlanner.tsx +++ b/apps/web/src/components/RoutePlanner.tsx @@ -93,6 +93,8 @@ type RoutePlannerProps = { onClearDestination: () => void; onUseGpsAsStart: () => void; onSelectRoute: (routeId: string) => void; + onEditRoute?: () => void; + onCancelRoute?: () => void; guidanceActive?: boolean; onStartGuidance?: () => void; onEditBoat?: () => void; @@ -148,6 +150,8 @@ export function RoutePlanner({ onClearDestination, onUseGpsAsStart, onSelectRoute, + onEditRoute = () => undefined, + onCancelRoute = () => undefined, guidanceActive = false, onStartGuidance = () => undefined, onEditBoat = () => undefined, @@ -612,13 +616,29 @@ export function RoutePlanner({ )} - +