feat(frontend): implement via mode for setting routes with waypoints
This commit is contained in:
@@ -70,14 +70,34 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
|
||||
const [viaMode, setViaMode] = useState(false);
|
||||
const [viaDest, setViaDest] = useState<string | null>(null);
|
||||
const [viaQueue, setViaQueue] = useState<string[]>([]);
|
||||
|
||||
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 && (
|
||||
<div className="absolute top-2 right-2 px-2 py-1 rounded bg-emerald-600 text-white text-xs shadow">
|
||||
VIA mode (Esc to exit)
|
||||
VIA mode: Dest {viaDest} ({viaQueue.length} waypoints). Esc to commit
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user