feat(frontend): implement via mode for setting destinations and add MapNode disableNavigate prop
This commit is contained in:
@@ -15,6 +15,7 @@ interface MapNodeProps {
|
|||||||
security?: number;
|
security?: number;
|
||||||
signatures?: number;
|
signatures?: number;
|
||||||
isDraggable?: boolean;
|
isDraggable?: boolean;
|
||||||
|
disableNavigate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapNode: React.FC<MapNodeProps> = ({
|
export const MapNode: React.FC<MapNodeProps> = ({
|
||||||
@@ -30,7 +31,8 @@ export const MapNode: React.FC<MapNodeProps> = ({
|
|||||||
type,
|
type,
|
||||||
security,
|
security,
|
||||||
signatures,
|
signatures,
|
||||||
isDraggable = false
|
isDraggable = false,
|
||||||
|
disableNavigate = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
@@ -142,7 +144,7 @@ export const MapNode: React.FC<MapNodeProps> = ({
|
|||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onClick();
|
if (!disableNavigate) onClick();
|
||||||
}}
|
}}
|
||||||
onDoubleClick={(e) => {
|
onDoubleClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@@ -9,6 +9,7 @@ import { System, Position, Connection as ConnectionType } from '@/lib/types';
|
|||||||
import { getSecurityColor } from '@/utils/securityColors';
|
import { getSecurityColor } from '@/utils/securityColors';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName } from 'wailsjs/go/main/App';
|
import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName } from 'wailsjs/go/main/App';
|
||||||
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
|
||||||
interface RegionMapProps {
|
interface RegionMapProps {
|
||||||
regionName: string;
|
regionName: string;
|
||||||
@@ -104,7 +105,32 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
}
|
}
|
||||||
}, [isWormholeRegion]);
|
}, [isWormholeRegion]);
|
||||||
|
|
||||||
const handleSystemClick = (systemName: string) => {
|
const ensureAnyLoggedIn = async () => {
|
||||||
|
try {
|
||||||
|
const list = await ListCharacters();
|
||||||
|
if (Array.isArray(list) && list.length > 0) return true;
|
||||||
|
await StartESILogin();
|
||||||
|
toast({ title: 'EVE Login', description: 'Complete login in your browser, then retry.' });
|
||||||
|
return false;
|
||||||
|
} catch (e: any) {
|
||||||
|
await StartESILogin();
|
||||||
|
toast({ title: 'EVE Login', description: 'Complete login in your browser, then retry.', variant: 'destructive' });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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' });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (focusSystem === systemName) return;
|
if (focusSystem === systemName) return;
|
||||||
navigate(`/regions/${regionName}/${systemName}`);
|
navigate(`/regions/${regionName}/${systemName}`);
|
||||||
};
|
};
|
||||||
@@ -374,31 +400,20 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ensureAnyLoggedIn = async () => {
|
|
||||||
try {
|
|
||||||
const list = await ListCharacters();
|
|
||||||
if (Array.isArray(list) && list.length > 0) return true;
|
|
||||||
await StartESILogin();
|
|
||||||
return false;
|
|
||||||
} catch {
|
|
||||||
await StartESILogin();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSetDestination = async (systemName: string, wantVia: boolean) => {
|
const onSetDestination = async (systemName: string, wantVia: boolean) => {
|
||||||
try {
|
try {
|
||||||
if (!(await ensureAnyLoggedIn())) return;
|
if (!(await ensureAnyLoggedIn())) return;
|
||||||
if (!viaMode) {
|
if (!viaMode) {
|
||||||
// First selection: set destination and optionally enter via mode
|
|
||||||
await SetDestinationForAll(systemName, true, false);
|
await SetDestinationForAll(systemName, true, false);
|
||||||
|
toast({ title: 'Destination set', description: systemName });
|
||||||
if (wantVia) setViaMode(true);
|
if (wantVia) setViaMode(true);
|
||||||
} else {
|
} else {
|
||||||
// Append waypoint
|
|
||||||
await AddWaypointForAllByName(systemName, false);
|
await AddWaypointForAllByName(systemName, false);
|
||||||
|
toast({ title: 'Waypoint added', description: systemName });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
console.error('Set destination failed:', e);
|
console.error('Set destination failed:', e);
|
||||||
|
toast({ title: 'Failed to set destination', description: String(e), variant: 'destructive' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -442,7 +457,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
viewBox={`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`}
|
viewBox={`${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`}
|
||||||
className={`cursor-grab active:cursor-grabbing ${viaMode ? 'ring-2 ring-emerald-500' : ''}`}
|
className="cursor-grab active:cursor-grabbing"
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onMouseMove={(e) => {
|
onMouseMove={(e) => {
|
||||||
if (isPanning) {
|
if (isPanning) {
|
||||||
@@ -508,6 +523,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
security={system.security}
|
security={system.security}
|
||||||
signatures={system.signatures}
|
signatures={system.signatures}
|
||||||
isDraggable={isWormholeRegion}
|
isDraggable={isWormholeRegion}
|
||||||
|
disableNavigate={viaMode}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@@ -536,6 +552,12 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
)}
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
{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)
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Context Menu */}
|
{/* Context Menu */}
|
||||||
{contextMenu && (
|
{contextMenu && (
|
||||||
<SystemContextMenu
|
<SystemContextMenu
|
||||||
|
@@ -8,7 +8,7 @@ interface SystemContextMenuProps {
|
|||||||
onRename: (newName: string) => void;
|
onRename: (newName: string) => void;
|
||||||
onDelete: (system: System) => void;
|
onDelete: (system: System) => void;
|
||||||
onClearConnections: (system: System) => void;
|
onClearConnections: (system: System) => void;
|
||||||
onSetDestination: (systemName: string, viaMode: boolean) => void;
|
onSetDestination?: (systemName: string, viaMode: boolean) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +28,16 @@ export const SystemContextMenu = ({ x, y, system, onRename, onDelete, onClearCon
|
|||||||
setIsRenaming(false);
|
setIsRenaming(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSetDestinationClick = (e: React.MouseEvent) => {
|
||||||
|
const via = !!e.shiftKey;
|
||||||
|
if (typeof onSetDestination === 'function') {
|
||||||
|
onSetDestination(system.solarSystemName, via);
|
||||||
|
} else {
|
||||||
|
console.error('onSetDestination not provided');
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
@@ -85,7 +95,7 @@ export const SystemContextMenu = ({ x, y, system, onRename, onDelete, onClearCon
|
|||||||
</button>
|
</button>
|
||||||
<div className="h-px bg-slate-700 my-1" />
|
<div className="h-px bg-slate-700 my-1" />
|
||||||
<button
|
<button
|
||||||
onClick={(e) => { onSetDestination(system.solarSystemName, e.shiftKey); onClose(); }}
|
onClick={handleSetDestinationClick}
|
||||||
className="w-full px-3 py-1 text-left text-emerald-400 hover:bg-slate-700 rounded text-sm"
|
className="w-full px-3 py-1 text-left text-emerald-400 hover:bg-slate-700 rounded text-sm"
|
||||||
title="Shift-click to enter via mode and append waypoints"
|
title="Shift-click to enter via mode and append waypoints"
|
||||||
>
|
>
|
||||||
|
Reference in New Issue
Block a user