From ef57bf4cdea86ee1f35ae546ec8357e8a2f2e73f Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sat, 9 Aug 2025 19:57:04 +0200 Subject: [PATCH] feat(frontend): implement via mode for setting routes with waypoints --- frontend/src/components/MapNode.tsx | 14 ++-- frontend/src/components/RegionMap.tsx | 103 ++++++++++++-------------- 2 files changed, 51 insertions(+), 66 deletions(-) diff --git a/frontend/src/components/MapNode.tsx b/frontend/src/components/MapNode.tsx index 052e465..90cf4f7 100644 --- a/frontend/src/components/MapNode.tsx +++ b/frontend/src/components/MapNode.tsx @@ -67,14 +67,11 @@ export const MapNode: React.FC = ({ onDragEnd?.(e); }; - const nodeColor = security !== undefined - ? getSecurityColor(security) - : '#a855f7'; // fallback purple color + const nodeColor = security !== undefined ? getSecurityColor(security) : '#a855f7'; if (type === 'region') { - // Further reduce region size to prevent overlap - made even smaller - const pillWidth = Math.max(name.length * 5, 40); // Reduced from 8 to 5, min from 60 to 40 - const pillHeight = 18; // Reduced from 24 to 18 + const pillWidth = Math.max(name.length * 5, 40); + const pillHeight = 18; return ( = ({ ); } else { - // Render system as a dot with external label const nodeSize = 6; - const textOffset = 20; // Position text below the dot - moved down more + const textOffset = 20; return ( = ({ onMouseDown={handleMouseDown} onClick={(e) => { e.stopPropagation(); - if (!disableNavigate) onClick(); + onClick(); }} onDoubleClick={(e) => { e.stopPropagation(); diff --git a/frontend/src/components/RegionMap.tsx b/frontend/src/components/RegionMap.tsx index e1a94f4..39dfba6 100644 --- a/frontend/src/components/RegionMap.tsx +++ b/frontend/src/components/RegionMap.tsx @@ -70,14 +70,34 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho const svgRef = useRef(null); const [viaMode, setViaMode] = useState(false); + const [viaDest, setViaDest] = useState(null); + const [viaQueue, setViaQueue] = useState([]); useEffect(() => { - const onKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape') setViaMode(false); + const onKeyDown = async (e: KeyboardEvent) => { + if (e.key === 'Escape' && viaMode) { + // Commit the route: post destination then queued waypoints + try { + if (!(await ensureAnyLoggedIn())) return; + if (viaDest) { + await SetDestinationForAll(viaDest, true, false); + for (const name of viaQueue) { + await AddWaypointForAllByName(name, false); + } + toast({ title: 'Route set', description: `${viaDest}${viaQueue.length ? ' via ' + viaQueue.join(', ') : ''}` }); + } + } catch (err: any) { + toast({ title: 'Failed to set route', description: String(err), variant: 'destructive' }); + } finally { + setViaMode(false); + setViaDest(null); + setViaQueue([]); + } + } }; window.addEventListener('keydown', onKeyDown); return () => window.removeEventListener('keydown', onKeyDown); - }, []); + }, [viaMode, viaDest, viaQueue]); const { data: rsystems, isLoading, error } = useRegionData(regionName); useEffect(() => { @@ -85,18 +105,14 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho setSystems(rsystems); }, [rsystems, isLoading, error]); - // Process connections when systems or nodePositions change useEffect(() => { if (!systems || systems.size === 0) return; - console.log("Computing node positions"); const positions = computeNodePositions(systems); setPositions(positions); - console.log("Computing node connections"); const connections = computeNodeConnections(systems); setConnections(connections); }, [systems]); - // Load wormhole systems on mount if in wormhole region useEffect(() => { if (isWormholeRegion) { loadWormholeSystems().then(wormholeSystems => { @@ -121,14 +137,13 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho const handleSystemClick = async (systemName: string) => { if (viaMode) { - try { - if (!(await ensureAnyLoggedIn())) return; - await AddWaypointForAllByName(systemName, false); - toast({ title: 'Waypoint added', description: systemName }); - } catch (e: any) { - console.error('Append waypoint failed:', e); - toast({ title: 'Failed to add waypoint', description: String(e), variant: 'destructive' }); - } + setViaQueue(prev => { + if (prev.includes(systemName)) return prev; + const next = [...prev, systemName]; + return next; + }); + console.log('Queued waypoint:', systemName); + toast({ title: 'Waypoint queued', description: systemName }); return; } if (focusSystem === systemName) return; @@ -268,36 +283,20 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho if (!svgRef.current) return; setIsPanning(true); const rect = svgRef.current.getBoundingClientRect(); - setLastPanPoint({ - x: e.clientX - rect.left, - y: e.clientY - rect.top - }); + setLastPanPoint({ x: e.clientX - rect.left, y: e.clientY - rect.top }); }, []); const handleMouseMove = useCallback((e: React.MouseEvent) => { if (!isPanning || !svgRef.current) return; - const rect = svgRef.current.getBoundingClientRect(); - const currentPoint = { - x: e.clientX - rect.left, - y: e.clientY - rect.top - }; - + const currentPoint = { x: e.clientX - rect.left, y: e.clientY - rect.top }; const deltaX = (lastPanPoint.x - currentPoint.x) * (viewBox.width / rect.width); const deltaY = (lastPanPoint.y - currentPoint.y) * (viewBox.height / rect.height); - - setViewBox(prev => ({ - ...prev, - x: prev.x + deltaX, - y: prev.y + deltaY - })); - + setViewBox(prev => ({ ...prev, x: prev.x + deltaX, y: prev.y + deltaY })); setLastPanPoint(currentPoint); }, [isPanning, lastPanPoint, viewBox.width, viewBox.height]); - const handleMouseUp = useCallback(() => { - setIsPanning(false); - }, []); + const handleMouseUp = useCallback(() => { setIsPanning(false); }, []); const handleWheel = useCallback((e: React.WheelEvent) => { e.preventDefault(); @@ -403,16 +402,18 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho const onSetDestination = async (systemName: string, wantVia: boolean) => { try { if (!(await ensureAnyLoggedIn())) return; - if (!viaMode) { + if (wantVia) { + setViaDest(systemName); + setViaQueue([]); + setViaMode(true); + console.log('Via mode start, dest:', systemName); + toast({ title: 'Via mode', description: `Destination ${systemName}. Click systems to add waypoints. Esc to commit.` }); + } else { await SetDestinationForAll(systemName, true, false); toast({ title: 'Destination set', description: systemName }); - if (wantVia) setViaMode(true); - } else { - await AddWaypointForAllByName(systemName, false); - toast({ title: 'Waypoint added', description: systemName }); } } catch (e: any) { - console.error('Set destination failed:', e); + console.error('Failed to set destination:', e); toast({ title: 'Failed to set destination', description: String(e), variant: 'destructive' }); } }; @@ -459,20 +460,8 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho viewBox={`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`} className="cursor-grab active:cursor-grabbing" onMouseDown={handleMouseDown} - onMouseMove={(e) => { - if (isPanning) { - handleMouseMove(e); - } else { - handleSvgMouseMove(e); - } - }} - onMouseUp={(e) => { - if (isPanning) { - handleMouseUp(); - } else { - handleSvgMouseUp(e); - } - }} + onMouseMove={(e) => { if (isPanning) { handleMouseMove(e); } else if (draggingNode) { handleSvgMouseMove(e); } }} + onMouseUp={(e) => { if (isPanning) { handleMouseUp(); } else if (draggingNode) { handleSvgMouseUp(e); } }} onMouseLeave={handleMouseUp} onWheel={handleWheel} onDoubleClick={handleMapDoubleClick} @@ -554,7 +543,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho {viaMode && (
- VIA mode (Esc to exit) + VIA mode: Dest {viaDest} ({viaQueue.length} waypoints). Esc to commit
)} @@ -567,7 +556,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho onRename={(newName) => handleRenameSystem(contextMenu.system.solarSystemName, newName)} onDelete={handleDeleteSystem} onClearConnections={handleClearConnections} - onSetDestination={onSetDestination} + onSetDestination={(systemName) => onSetDestination(systemName, true)} onClose={() => setContextMenu(null)} /> )}