Update
This commit is contained in:
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
};
|
||||||
|
Reference in New Issue
Block a user