This commit is contained in:
2025-08-09 20:22:50 +02:00
parent a584bc9c55
commit ee2a1fcde0
2 changed files with 94 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ import { getSecurityColor } from '@/utils/securityColors';
import { Header } from './Header'; import { Header } from './Header';
import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName, PostRouteForAllByNames } from 'wailsjs/go/main/App'; import { ListCharacters, StartESILogin, SetDestinationForAll, AddWaypointForAllByName, PostRouteForAllByNames } from 'wailsjs/go/main/App';
import { toast } from '@/hooks/use-toast'; import { toast } from '@/hooks/use-toast';
import { getSystemsRegions } from '@/utils/systemApi';
interface RegionMapProps { interface RegionMapProps {
regionName: string; regionName: string;
@@ -73,6 +74,8 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
const [viaDest, setViaDest] = useState<string | null>(null); const [viaDest, setViaDest] = useState<string | null>(null);
const [viaQueue, setViaQueue] = useState<string[]>([]); const [viaQueue, setViaQueue] = useState<string[]>([]);
const [offRegionLinks, setOffRegionLinks] = useState<Array<{ from: string; to: string; toRegion: string }>>([]);
useEffect(() => { useEffect(() => {
const onKeyDown = async (e: KeyboardEvent) => { const onKeyDown = async (e: KeyboardEvent) => {
if (e.key === 'Escape' && viaMode) { if (e.key === 'Escape' && viaMode) {
@@ -109,6 +112,41 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
setConnections(connections); setConnections(connections);
}, [systems]); }, [systems]);
// Compute off-region links lazily using PB only for missing nodes
useEffect(() => {
const computeOffRegion = async () => {
if (!systems || systems.size === 0) {
setOffRegionLinks([]);
return;
}
const out: Array<{ from: string; to: string; toRegion: string }> = [];
const toLookup: Set<string> = new Set();
for (const [fromName, sys] of systems.entries()) {
const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean);
for (const n of neighbors) {
if (!systems.has(n)) {
toLookup.add(n);
}
}
}
if (toLookup.size === 0) { setOffRegionLinks([]); return; }
const regionMap = await getSystemsRegions(Array.from(toLookup));
for (const [fromName, sys] of systems.entries()) {
const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean);
for (const n of neighbors) {
if (!systems.has(n)) {
const toRegion = regionMap[n];
if (toRegion && toRegion !== regionName) {
out.push({ from: fromName, to: n, toRegion });
}
}
}
}
setOffRegionLinks(out);
};
computeOffRegion();
}, [systems, regionName]);
useEffect(() => { useEffect(() => {
if (isWormholeRegion) { if (isWormholeRegion) {
loadWormholeSystems().then(wormholeSystems => { loadWormholeSystems().then(wormholeSystems => {
@@ -470,6 +508,9 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
<feMergeNode in="SourceGraphic" /> <feMergeNode in="SourceGraphic" />
</feMerge> </feMerge>
</filter> </filter>
<marker id="arrowhead" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L6,3 z" fill="#ffd166" />
</marker>
</defs> </defs>
{/* Render all connections */} {/* Render all connections */}
@@ -512,6 +553,22 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
/> />
))} ))}
{/* Off-region link indicators (clickable arrows) */}
{offRegionLinks.map((link, idx) => {
const pos = positions[link.from];
if (!pos) return null;
const offsetX = 10;
const offsetY = -14;
return (
<g key={`off-${idx}`} transform={`translate(${pos.x + offsetX}, ${pos.y + offsetY})`}>
<rect x={-10} y={-12} width={20} height={14} rx={3} fill="#1f2937" stroke="#ffd166" strokeWidth={1} opacity={0.9} />
<path d="M-6,-5 L6,-5" stroke="#ffd166" strokeWidth={2} markerEnd="url(#arrowhead)" />
<text x={0} y={6} textAnchor="middle" fontSize="7" fill="#ffd166">{link.toRegion}</text>
<rect x={-10} y={-12} width={20} height={20} fill="transparent" onClick={(e) => { e.stopPropagation(); navigate(`/regions/${encodeURIComponent(link.toRegion)}/${encodeURIComponent(link.to)}`); }} />
</g>
);
})}
{/* Highlight focused system */} {/* Highlight focused system */}
{focusSystem && positions[focusSystem] && ( {focusSystem && positions[focusSystem] && (
<circle <circle

View File

@@ -4,3 +4,40 @@ export const getSystemId = async (systemName: string): Promise<string> => {
const system = await pb.collection('regionview').getFirstListItem(`sysname='${systemName}'`); const system = await pb.collection('regionview').getFirstListItem(`sysname='${systemName}'`);
return system.id; return system.id;
}; };
const regionCache: Map<string, string> = new Map();
export const getSystemRegion = async (systemName: string): Promise<string> => {
const key = systemName;
const cached = regionCache.get(key);
if (cached) return cached;
const rec = await pb.collection('regionview').getFirstListItem(`sysname='${systemName}'`);
regionCache.set(key, rec.sysregion);
return rec.sysregion as string;
};
export const getSystemsRegions = async (systemNames: string[]): Promise<Record<string, string>> => {
const result: Record<string, string> = {};
const pending: string[] = [];
for (const name of systemNames) {
const cached = regionCache.get(name);
if (cached) {
result[name] = cached;
} else {
pending.push(name);
}
}
if (pending.length === 0) return result;
// Fetch uncached in parallel
const fetched = await Promise.all(
pending.map(async (name) => {
const rec = await pb.collection('regionview').getFirstListItem(`sysname='${name}'`);
regionCache.set(name, rec.sysregion);
return { name, region: rec.sysregion as string };
})
);
for (const { name, region } of fetched) {
result[name] = region;
}
return result;
};