mirror of
https://github.com/wanderer-industries/wanderer
synced 2026-03-14 23:56:01 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bf6893524 | ||
|
|
e45e6a39eb | ||
|
|
7f1691b2db | ||
|
|
d1006b329a | ||
|
|
18d50329bc |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -2,6 +2,19 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.97.0](https://github.com/wanderer-industries/wanderer/compare/v1.96.6...v1.97.0) (2026-03-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* signatures: Sync mass status with connection, show it on unsplashed sigs
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Connection context menu and wormhole. Change UI for select wormhole mass state. Change UI for select ship-size for wormhole. Add ability to set mass for signatures
|
||||
|
||||
## [v1.96.6](https://github.com/wanderer-industries/wanderer/compare/v1.96.5...v1.96.6) (2026-03-13)
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
import {
|
||||
MASS_STATE_NAMES,
|
||||
MASS_STATE_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES,
|
||||
SHIP_SIZES_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES_SHORT,
|
||||
SHIP_SIZES_SIZE,
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
@@ -14,6 +6,8 @@ import { MenuItem } from 'primereact/menuitem';
|
||||
import React, { RefObject, useMemo } from 'react';
|
||||
import { Edge } from 'reactflow';
|
||||
import { LifetimeActionsWrapper } from '@/hooks/Mapper/components/map/components/ContextMenuConnection/LifetimeActionsWrapper.tsx';
|
||||
import { MassStatusActionsWrapper } from '@/hooks/Mapper/components/map/components/ContextMenuConnection/MassStatusActionsWrapper.tsx';
|
||||
import { ShipSizeActionsWrapper } from '@/hooks/Mapper/components/map/components/ContextMenuConnection/ShipSizeActionsWrapper.tsx';
|
||||
import classes from './ContextMenuConnection.module.scss';
|
||||
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
import { isNullsecSpace } from '@/hooks/Mapper/components/map/helpers/isKnownSpace.ts';
|
||||
@@ -86,16 +80,28 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
return <LifetimeActionsWrapper lifetime={edge.data?.time_status} onChangeLifetime={onChangeTimeState} />;
|
||||
},
|
||||
},
|
||||
...(!isFrigateSize
|
||||
? [
|
||||
{
|
||||
className: clsx(classes.FastActions, '!h-[54px]'),
|
||||
template: () => {
|
||||
return (
|
||||
<MassStatusActionsWrapper
|
||||
massStatus={edge.data?.mass_status}
|
||||
onChangeMassStatus={onChangeMassState}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
[classes.ConnectionFrigate]: isFrigateSize,
|
||||
}),
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.large : ShipSizeStatus.small,
|
||||
),
|
||||
className: clsx(classes.FastActions, '!h-[64px]'),
|
||||
template: () => {
|
||||
return (
|
||||
<ShipSizeActionsWrapper shipSize={edge.data?.ship_size_type} onChangeShipSize={onChangeShipSizeStatus} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: `Save mass`,
|
||||
@@ -105,41 +111,6 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
...(!isFrigateSize
|
||||
? [
|
||||
{
|
||||
label: `Mass status`,
|
||||
icon: PrimeIcons.CHART_PIE,
|
||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
||||
label: MASS_STATE_NAMES[x],
|
||||
className: clsx({
|
||||
[classes.SelectedItem]: edge.data?.mass_status === x,
|
||||
}),
|
||||
command: () => onChangeMassState(x),
|
||||
})),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: `Ship Size`,
|
||||
icon: PrimeIcons.CLOUD,
|
||||
items: SHIP_SIZES_NAMES_ORDER.map(x => ({
|
||||
label: (
|
||||
<div className="grid grid-cols-[20px_120px_1fr_40px] gap-2 items-center">
|
||||
<div className="text-[12px] font-bold text-stone-400">{SHIP_SIZES_NAMES_SHORT[x]}</div>
|
||||
<div>{SHIP_SIZES_NAMES[x]}</div>
|
||||
<div></div>
|
||||
<div className="flex justify-end whitespace-nowrap text-[12px] font-bold text-stone-500">
|
||||
{SHIP_SIZES_SIZE[x]} t.
|
||||
</div>
|
||||
</div>
|
||||
) as unknown as string, // TODO my lovely kostyl
|
||||
className: clsx({
|
||||
[classes.SelectedItem]: edge.data?.ship_size_type === x,
|
||||
}),
|
||||
command: () => onChangeShipSizeStatus(x),
|
||||
})),
|
||||
},
|
||||
...(bothNullsec
|
||||
? [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LayoutEventBlocker } from '@/hooks/Mapper/components/ui-kit';
|
||||
import {
|
||||
WdMassStatusSelector,
|
||||
WdMassStatusSelectorProps,
|
||||
} from '@/hooks/Mapper/components/ui-kit/WdMassStatusSelector.tsx';
|
||||
|
||||
export const MassStatusActionsWrapper = (props: WdMassStatusSelectorProps) => {
|
||||
return (
|
||||
<LayoutEventBlocker className="flex flex-col gap-1 w-[100%] h-full px-2 pt-[4px]">
|
||||
<div className="text-[12px] text-stone-500 font-semibold">Mass status:</div>
|
||||
|
||||
<WdMassStatusSelector {...props} />
|
||||
</LayoutEventBlocker>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { LayoutEventBlocker } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { WdShipSizeSelector, WdShipSizeSelectorProps } from '@/hooks/Mapper/components/ui-kit/WdShipSizeSelector.tsx';
|
||||
|
||||
export const ShipSizeActionsWrapper = (props: WdShipSizeSelectorProps) => {
|
||||
return (
|
||||
<LayoutEventBlocker className="flex flex-col gap-1 w-[100%] h-full px-2 pt-[4px]">
|
||||
<div className="text-[12px] text-stone-500 font-semibold">Ship size:</div>
|
||||
|
||||
<WdShipSizeSelector {...props} />
|
||||
</LayoutEventBlocker>
|
||||
);
|
||||
};
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
STATUS_CLASSES,
|
||||
} from '@/hooks/Mapper/components/map/constants';
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
import { UnsplashedSignatureColumn } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
|
||||
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { Tag } from 'primereact/tag';
|
||||
@@ -177,17 +177,19 @@ export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>
|
||||
<>
|
||||
{nodeVars.unsplashedLeft.length > 0 && (
|
||||
<div className={classes.Unsplashed}>
|
||||
{nodeVars.unsplashedLeft.map(sig => (
|
||||
<UnsplashedSignature key={sig.eve_id} signature={sig} />
|
||||
))}
|
||||
<UnsplashedSignatureColumn
|
||||
signatures={nodeVars.unsplashedLeft}
|
||||
wormholesData={nodeVars.wormholesData}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{nodeVars.unsplashedRight.length > 0 && (
|
||||
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||
{nodeVars.unsplashedRight.map(sig => (
|
||||
<UnsplashedSignature key={sig.eve_id} signature={sig} />
|
||||
))}
|
||||
<UnsplashedSignatureColumn
|
||||
signatures={nodeVars.unsplashedRight}
|
||||
wormholesData={nodeVars.wormholesData}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
STATUS_CLASSES,
|
||||
} from '@/hooks/Mapper/components/map/constants';
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
import { UnsplashedSignatureColumn } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
|
||||
import { LocalCounter } from '@/hooks/Mapper/components/map/components/LocalCounter';
|
||||
@@ -157,17 +157,19 @@ export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>)
|
||||
<>
|
||||
{nodeVars.unsplashedLeft.length > 0 && (
|
||||
<div className={classes.Unsplashed}>
|
||||
{nodeVars.unsplashedLeft.map(sig => (
|
||||
<UnsplashedSignature key={sig.eve_id} signature={sig} />
|
||||
))}
|
||||
<UnsplashedSignatureColumn
|
||||
signatures={nodeVars.unsplashedLeft}
|
||||
wormholesData={nodeVars.wormholesData}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{nodeVars.unsplashedRight.length > 0 && (
|
||||
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||
{nodeVars.unsplashedRight.map(sig => (
|
||||
<UnsplashedSignature key={sig.eve_id} signature={sig} />
|
||||
))}
|
||||
<UnsplashedSignatureColumn
|
||||
signatures={nodeVars.unsplashedRight}
|
||||
wormholesData={nodeVars.wormholesData}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
@use '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.Signature {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
display: block;
|
||||
|
||||
& > .Box {
|
||||
width: 13px;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-color);
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
font-weight: bolder;
|
||||
display: block;
|
||||
}
|
||||
|
||||
& > .Eol {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { InfoDrawer } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
import { K162_TYPES_MAP } from '@/hooks/Mapper/constants.ts';
|
||||
import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureCustomInfo.ts';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
import clsx from 'clsx';
|
||||
import { useMemo } from 'react';
|
||||
import classes from './UnsplashedSignature.module.scss';
|
||||
|
||||
interface UnsplashedSignatureProps {
|
||||
signature: SystemSignature;
|
||||
}
|
||||
export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) => {
|
||||
const {
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const whData = useMemo(() => wormholesData[signature.type], [signature.type, wormholesData]);
|
||||
const whClass = useMemo(() => (whData ? WORMHOLES_ADDITIONAL_INFO[whData.dest] : null), [whData]);
|
||||
|
||||
const customInfo = useMemo(() => {
|
||||
return parseSignatureCustomInfo(signature.custom_info);
|
||||
}, [signature]);
|
||||
|
||||
const k162TypeOption = useMemo(() => {
|
||||
if (!customInfo?.k162Type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return K162_TYPES_MAP[customInfo.k162Type];
|
||||
}, [customInfo]);
|
||||
|
||||
const isEOL = useMemo(() => {
|
||||
return customInfo?.time_status === TimeStatus._1h;
|
||||
}, [customInfo]);
|
||||
|
||||
const is4H = useMemo(() => {
|
||||
return customInfo?.time_status === TimeStatus._4h;
|
||||
}, [customInfo]);
|
||||
|
||||
const whClassStyle = useMemo(() => {
|
||||
if (signature.type === 'K162' && k162TypeOption) {
|
||||
const k162Data = wormholesData[k162TypeOption.whClassName];
|
||||
const k162Class = k162Data ? WORMHOLES_ADDITIONAL_INFO[k162Data.dest] : null;
|
||||
return k162Class ? WORMHOLE_CLASS_STYLES[k162Class.wormholeClassID] : '';
|
||||
}
|
||||
return whClass ? WORMHOLE_CLASS_STYLES[whClass.wormholeClassID] : '';
|
||||
}, [signature, whClass, k162TypeOption, wormholesData]);
|
||||
|
||||
return (
|
||||
<WdTooltipWrapper
|
||||
className={clsx(classes.Signature)}
|
||||
// @ts-ignore
|
||||
content={
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
|
||||
{renderInfoColumn(signature)}
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
}
|
||||
smallPaddings
|
||||
>
|
||||
<div className={clsx(classes.Box, whClassStyle)}>
|
||||
<svg width="13" height="8" viewBox="0 0 13 8" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="1" width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
|
||||
{isEOL && <rect x="4" width="5" height="6" rx="1" className={clsx(classes.Eol)} fill="#a153ac" />}
|
||||
{is4H && <rect x="4" width="5" height="6" rx="1" className={clsx(classes.Eol)} fill="#d8b4fe" />}
|
||||
</svg>
|
||||
</div>
|
||||
</WdTooltipWrapper>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { InfoDrawer } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureCustomInfo';
|
||||
import { resolveSignatureFillVar } from '@/hooks/Mapper/components/map/helpers/wormholeClassFillVars';
|
||||
import { MassState, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
import { WormholeDataRaw } from '@/hooks/Mapper/types/wormholes';
|
||||
|
||||
interface PillData {
|
||||
signature: SystemSignature;
|
||||
fill: string;
|
||||
isEOL: boolean;
|
||||
is4H: boolean;
|
||||
isVerge: boolean;
|
||||
isHalf: boolean;
|
||||
}
|
||||
|
||||
const PILL_W = 13;
|
||||
const PILL_H = 8;
|
||||
const GAP = 2;
|
||||
const COLS = 4;
|
||||
|
||||
interface UnsplashedSignatureColumnProps {
|
||||
signatures: SystemSignature[];
|
||||
wormholesData: Record<string, WormholeDataRaw>;
|
||||
}
|
||||
|
||||
export const UnsplashedSignatureColumn = ({ signatures, wormholesData }: UnsplashedSignatureColumnProps) => {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
const [hoveredIndex, setHoveredIndex] = useState(-1);
|
||||
|
||||
const pills: PillData[] = useMemo(() => {
|
||||
return signatures.map(sig => {
|
||||
const customInfo = parseSignatureCustomInfo(sig.custom_info);
|
||||
return {
|
||||
signature: sig,
|
||||
fill: resolveSignatureFillVar(sig, wormholesData),
|
||||
isEOL: customInfo.time_status === TimeStatus._1h,
|
||||
is4H: customInfo.time_status === TimeStatus._4h,
|
||||
isVerge: customInfo.mass_status === MassState.verge,
|
||||
isHalf: customInfo.mass_status === MassState.half,
|
||||
};
|
||||
});
|
||||
}, [signatures, wormholesData]);
|
||||
|
||||
const count = pills.length;
|
||||
if (count === 0) return null;
|
||||
|
||||
const cols = Math.min(count, COLS);
|
||||
const rows = Math.ceil(count / COLS);
|
||||
const svgWidth = cols * (PILL_W + GAP) - GAP;
|
||||
const svgHeight = rows * (PILL_H + GAP) - GAP;
|
||||
|
||||
const onMouseMove = useCallback(
|
||||
(e: React.MouseEvent<SVGSVGElement>) => {
|
||||
const svg = svgRef.current;
|
||||
if (!svg) return;
|
||||
const rect = svg.getBoundingClientRect();
|
||||
const localX = e.clientX - rect.left;
|
||||
const localY = e.clientY - rect.top;
|
||||
|
||||
const col = Math.floor(localX / (PILL_W + GAP));
|
||||
const row = Math.floor(localY / (PILL_H + GAP));
|
||||
|
||||
const withinPill =
|
||||
localX - col * (PILL_W + GAP) <= PILL_W && localY - row * (PILL_H + GAP) <= PILL_H;
|
||||
|
||||
const idx = withinPill ? row * COLS + col : -1;
|
||||
setHoveredIndex(idx < count ? idx : -1);
|
||||
},
|
||||
[count],
|
||||
);
|
||||
|
||||
const onMouseLeave = useCallback(() => setHoveredIndex(-1), []);
|
||||
|
||||
// Compute tooltip position relative to viewport
|
||||
let tooltipX = 0;
|
||||
let tooltipY = 0;
|
||||
if (hoveredIndex >= 0 && svgRef.current) {
|
||||
const svgRect = svgRef.current.getBoundingClientRect();
|
||||
const col = hoveredIndex % COLS;
|
||||
const row = Math.floor(hoveredIndex / COLS);
|
||||
tooltipX = svgRect.left + col * (PILL_W + GAP) + PILL_W / 2;
|
||||
tooltipY = svgRect.top + row * (PILL_H + GAP) - 4;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<svg
|
||||
ref={svgRef}
|
||||
width={svgWidth}
|
||||
height={svgHeight}
|
||||
viewBox={`0 0 ${svgWidth} ${svgHeight}`}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{ display: 'block', position: 'relative', top: 3 }}
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{pills.map((pill, i) => {
|
||||
const col = i % COLS;
|
||||
const row = Math.floor(i / COLS);
|
||||
const x = col * (PILL_W + GAP);
|
||||
const y = row * (PILL_H + GAP);
|
||||
return (
|
||||
<g key={pill.signature.eve_id} transform={`translate(${x},${y})`}>
|
||||
<rect y="1" width="13" height="4" rx="2" fill={pill.fill} />
|
||||
{pill.isEOL && <rect x="4" width="5" height="6" rx="1" fill="#a153ac" />}
|
||||
{pill.is4H && <rect x="4" width="5" height="6" rx="1" fill="#d8b4fe" />}
|
||||
{pill.isVerge && <rect x="0" width="5" height="6" rx="1" fill="#af0000" />}
|
||||
{pill.isHalf && <rect x="0" width="5" height="6" rx="1" fill="#ffd700" />}
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
|
||||
{hoveredIndex >= 0 &&
|
||||
pills[hoveredIndex] &&
|
||||
createPortal(
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: tooltipX,
|
||||
top: tooltipY,
|
||||
transform: 'translate(-50%, -100%)',
|
||||
zIndex: 9999,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: 'var(--surface-card, #1e1e2e)',
|
||||
border: '1px solid var(--surface-border, #333)',
|
||||
borderRadius: 6,
|
||||
padding: '4px 6px',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.4)',
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title={<b className="text-slate-50">{pills[hoveredIndex].signature.eve_id}</b>}>
|
||||
{renderInfoColumn(pills[hoveredIndex].signature)}
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1 +1 @@
|
||||
export * from './UnsplashedSignature.tsx';
|
||||
export * from './UnsplashedSignatureColumn.tsx';
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from './getShapeClass';
|
||||
export * from './getBackgroundClass';
|
||||
export * from './prepareUnsplashedChunks';
|
||||
export * from './checkPermissions';
|
||||
export * from './wormholeClassFillVars';
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants';
|
||||
import { K162_TYPES_MAP } from '@/hooks/Mapper/constants';
|
||||
import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureCustomInfo';
|
||||
import { WormholeDataRaw } from '@/hooks/Mapper/types/wormholes';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
|
||||
// Maps WORMHOLE_CLASS_STYLES CSS class names → CSS variable references for inline SVG fill.
|
||||
// The CSS classes set `color: var(--eve-wh-type-color-*)`, but SVG `fill` needs an explicit value.
|
||||
const CLASS_NAME_TO_CSS_VAR: Record<string, string> = {
|
||||
'eve-wh-type-color-c1': 'var(--eve-wh-type-color-c1)',
|
||||
'eve-wh-type-color-c2': 'var(--eve-wh-type-color-c2)',
|
||||
'eve-wh-type-color-c3': 'var(--eve-wh-type-color-c3)',
|
||||
'eve-wh-type-color-c4': 'var(--eve-wh-type-color-c4)',
|
||||
'eve-wh-type-color-c5': 'var(--eve-wh-type-color-c5)',
|
||||
'eve-wh-type-color-c6': 'var(--eve-wh-type-color-c6)',
|
||||
'eve-wh-type-color-high': 'var(--eve-wh-type-color-high)',
|
||||
'eve-wh-type-color-low': 'var(--eve-wh-type-color-low)',
|
||||
'eve-wh-type-color-null': 'var(--eve-wh-type-color-null)',
|
||||
'eve-wh-type-color-thera': 'var(--eve-wh-type-color-thera)',
|
||||
'eve-wh-type-color-c13': 'var(--eve-wh-type-color-c13)',
|
||||
'eve-wh-type-color-drifter': 'var(--eve-wh-type-color-drifter)',
|
||||
'eve-wh-type-color-zarzakh': 'var(--eve-wh-type-color-zarzakh)',
|
||||
// eve-kind-color-abyss and eve-kind-color-penalty both resolve to --eve-wh-type-color-c6
|
||||
'eve-kind-color-abyss': 'var(--eve-wh-type-color-c6)',
|
||||
'eve-kind-color-penalty': 'var(--eve-wh-type-color-c6)',
|
||||
};
|
||||
|
||||
const DEFAULT_FILL = '#833ca4';
|
||||
|
||||
export function resolveSignatureFillVar(
|
||||
signature: SystemSignature,
|
||||
wormholesData: Record<string, WormholeDataRaw>,
|
||||
): string {
|
||||
const customInfo = parseSignatureCustomInfo(signature.custom_info);
|
||||
|
||||
// K162 override: use the k162Type to look up the real destination class
|
||||
if (signature.type === 'K162' && customInfo.k162Type) {
|
||||
const k162Option = K162_TYPES_MAP[customInfo.k162Type];
|
||||
if (k162Option) {
|
||||
const k162Data = wormholesData[k162Option.whClassName];
|
||||
const k162Class = k162Data ? WORMHOLES_ADDITIONAL_INFO[k162Data.dest] : null;
|
||||
if (k162Class) {
|
||||
const className = WORMHOLE_CLASS_STYLES[k162Class.wormholeClassID];
|
||||
if (className && CLASS_NAME_TO_CSS_VAR[className]) {
|
||||
return CLASS_NAME_TO_CSS_VAR[className];
|
||||
}
|
||||
}
|
||||
}
|
||||
return DEFAULT_FILL;
|
||||
}
|
||||
|
||||
// Normal type lookup
|
||||
const whData = wormholesData[signature.type];
|
||||
if (!whData) return DEFAULT_FILL;
|
||||
|
||||
const whClass = WORMHOLES_ADDITIONAL_INFO[whData.dest];
|
||||
if (!whClass) return DEFAULT_FILL;
|
||||
|
||||
const className = WORMHOLE_CLASS_STYLES[whClass.wormholeClassID];
|
||||
if (!className) return DEFAULT_FILL;
|
||||
|
||||
return CLASS_NAME_TO_CSS_VAR[className] || DEFAULT_FILL;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { Regions, REGIONS_MAP, SPACE_TO_CLASS } from '@/hooks/Mapper/constants';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
|
||||
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
import { CharacterTypeRaw, OutCommand, PingType, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { CharacterTypeRaw, OutCommand, PingType, SystemSignature, WormholeDataRaw } from '@/hooks/Mapper/types';
|
||||
import { useUnsplashedSignatures } from './useUnsplashedSignatures';
|
||||
import { useSystemName } from './useSystemName';
|
||||
import { LabelInfo, useLabelsInfo } from './useLabelsInfo';
|
||||
@@ -46,6 +46,7 @@ export interface SolarSystemNodeVars {
|
||||
userCharacters: string[];
|
||||
unsplashedLeft: Array<SystemSignature>;
|
||||
unsplashedRight: Array<SystemSignature>;
|
||||
wormholesData: Record<string, WormholeDataRaw>;
|
||||
isThickConnections: boolean;
|
||||
isRally: boolean;
|
||||
classTitle: string | null;
|
||||
@@ -210,6 +211,7 @@ export const useSolarSystemNode = (props: NodeProps<MapSolarSystemType>): SolarS
|
||||
charactersInSystem,
|
||||
unsplashedLeft,
|
||||
unsplashedRight,
|
||||
wormholesData,
|
||||
isThickConnections,
|
||||
classTitle: class_title,
|
||||
temporaryName: computedTemporaryName,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SystemViewStandalone, TooltipPosition, WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { SignatureGroup, SystemSignature, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { MassState, SignatureGroup, SystemSignature, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { renderK162Type } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
|
||||
@@ -21,17 +21,27 @@ export const renderInfoColumn = (row: SystemSignature) => {
|
||||
{row.temporary_name && <span className={clsx('text-[12px]')}>{row.temporary_name}</span>}
|
||||
|
||||
{customInfo.time_status === TimeStatus._1h && (
|
||||
<WdTooltipWrapper offset={5} position={TooltipPosition.top} content="Signature marked as EOL">
|
||||
<WdTooltipWrapper offset={5} position={TooltipPosition.bottom} content="Signature marked as EOL">
|
||||
<div className="pi pi-clock text-fuchsia-400 text-[11px] mr-[2px]"></div>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{customInfo.isCrit && (
|
||||
<WdTooltipWrapper offset={5} position={TooltipPosition.top} content="Signature marked as Crit">
|
||||
<WdTooltipWrapper offset={5} position={TooltipPosition.bottom} content="Signature marked as Crit">
|
||||
<div className="pi pi-clock text-fuchsia-400 text-[11px] mr-[2px]"></div>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{customInfo.mass_status === MassState.verge && (
|
||||
<WdTooltipWrapper
|
||||
offset={5}
|
||||
position={TooltipPosition.bottom}
|
||||
content="Signature marked as Verge of collapse"
|
||||
>
|
||||
<div className="pi pi-exclamation-triangle text-red-400 text-[11px] mr-[2px]"></div>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{row.type && (
|
||||
<WHClassView
|
||||
className="text-[11px]"
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import { SystemsSettingsProvider } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/Provider.tsx';
|
||||
import { WdButton } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand, SignatureGroup, SystemSignature, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { MassState, OutCommand, SignatureGroup, SystemSignature, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
@@ -15,6 +15,7 @@ type SystemSignaturePrepared = Omit<SystemSignature, 'linked_system'> & {
|
||||
linked_system: string;
|
||||
k162Type: string;
|
||||
time_status: TimeStatus;
|
||||
mass_status: MassState;
|
||||
};
|
||||
|
||||
export interface MapSettingsProps {
|
||||
@@ -59,6 +60,7 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
||||
custom_info: JSON.stringify({
|
||||
k162Type: values.k162Type,
|
||||
time_status: values.time_status,
|
||||
mass_status: values.mass_status,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -139,16 +141,19 @@ export const SignatureSettings = ({ systemId, show, onHide, signatureData }: Map
|
||||
|
||||
let k162Type = null;
|
||||
let time_status = TimeStatus._24h;
|
||||
let mass_status = MassState.normal;
|
||||
if (custom_info) {
|
||||
const customInfo = JSON.parse(custom_info);
|
||||
k162Type = customInfo.k162Type;
|
||||
time_status = customInfo.time_status;
|
||||
mass_status = customInfo.mass_status ?? MassState.normal;
|
||||
}
|
||||
|
||||
signatureForm.reset({
|
||||
linked_system: linked_system?.solar_system_id.toString() ?? undefined,
|
||||
k162Type: k162Type,
|
||||
time_status: time_status,
|
||||
mass_status: mass_status,
|
||||
...rest,
|
||||
});
|
||||
}, [signatureForm, signatureData]);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { SignatureK162TypeSelect } from '@/hooks/Mapper/components/mapRootConten
|
||||
import { SignatureLeadsToSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLeadsToSelect';
|
||||
import { SignatureLifetimeSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureLifetimeSelect.tsx';
|
||||
import { SignatureTempName } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureTempName.tsx';
|
||||
import { SignatureMassStatusSelect } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureMassStatusSelect.tsx';
|
||||
|
||||
export const SignatureGroupContentWormholes = () => {
|
||||
const { watch } = useFormContext<SystemSignature>();
|
||||
@@ -34,6 +35,11 @@ export const SignatureGroupContentWormholes = () => {
|
||||
<SignatureLifetimeSelect name="time_status" />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Mass status:</span>
|
||||
<SignatureMassStatusSelect name="mass_status" />
|
||||
</div>
|
||||
|
||||
<label className="grid grid-cols-[100px_250px_1fr] gap-2 items-center text-[14px]">
|
||||
<span>Temp. Name:</span>
|
||||
<SignatureTempName />
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { MassState, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { WdMassStatusSelector } from '@/hooks/Mapper/components/ui-kit/WdMassStatusSelector.tsx';
|
||||
|
||||
export interface SignatureMassStatusSelectProps {
|
||||
name: string;
|
||||
defaultValue?: MassState;
|
||||
}
|
||||
|
||||
export const SignatureMassStatusSelect = ({
|
||||
name,
|
||||
defaultValue = MassState.normal,
|
||||
}: SignatureMassStatusSelectProps) => {
|
||||
const { control } = useFormContext<SystemSignature>();
|
||||
|
||||
return (
|
||||
<div className="my-1">
|
||||
<Controller
|
||||
// @ts-ignore
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => {
|
||||
// @ts-ignore
|
||||
return <WdMassStatusSelector massStatus={field.value} onChangeMassStatus={e => field.onChange(e)} />;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export * from './SignatureGroupSelect';
|
||||
export * from './SignatureGroupContent';
|
||||
export * from './SignatureK162TypeSelect';
|
||||
export * from './SignatureLifetimeSelect';
|
||||
export * from './SignatureMassStatusSelect';
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { WdButton } from '@/hooks/Mapper/components/ui-kit/WdButton.tsx';
|
||||
import { MassState } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import { BUILT_IN_TOOLTIP_OPTIONS } from './constants.ts';
|
||||
|
||||
const MASS_STATUS = [
|
||||
{
|
||||
id: MassState.verge,
|
||||
label: 'Verge',
|
||||
className: 'bg-red-400 hover:!bg-red-400',
|
||||
inactiveClassName: 'bg-red-400/30',
|
||||
description: 'Mass status: Verge of collapse',
|
||||
},
|
||||
{
|
||||
id: MassState.half,
|
||||
label: 'Half',
|
||||
className: 'bg-orange-300 hover:!bg-orange-300',
|
||||
inactiveClassName: 'bg-orange-300/30',
|
||||
description: 'Mass status: Half',
|
||||
},
|
||||
{
|
||||
id: MassState.normal,
|
||||
label: 'Normal',
|
||||
className: 'bg-indigo-300 hover:!bg-indigo-300',
|
||||
inactiveClassName: 'bg-indigo-300/30',
|
||||
description: 'Mass status: Normal',
|
||||
},
|
||||
];
|
||||
|
||||
export interface WdMassStatusSelectorProps {
|
||||
massStatus?: MassState;
|
||||
onChangeMassStatus(massStatus: MassState): void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WdMassStatusSelector = ({
|
||||
massStatus = MassState.normal,
|
||||
onChangeMassStatus,
|
||||
className,
|
||||
}: WdMassStatusSelectorProps) => {
|
||||
return (
|
||||
<form>
|
||||
<div className={clsx('grid grid-cols-[auto_auto_auto] gap-1', className)}>
|
||||
{MASS_STATUS.map(x => (
|
||||
<WdButton
|
||||
key={x.id}
|
||||
outlined={false}
|
||||
value={x.label}
|
||||
tooltip={x.description}
|
||||
tooltipOptions={BUILT_IN_TOOLTIP_OPTIONS}
|
||||
size="small"
|
||||
className={clsx(
|
||||
`py-[1px] justify-center min-w-auto w-auto border-0 text-[12px] font-bold leading-[20px]`,
|
||||
{ [x.inactiveClassName]: massStatus !== x.id },
|
||||
x.className,
|
||||
)}
|
||||
onClick={() => onChangeMassStatus(x.id)}
|
||||
>
|
||||
{x.label}
|
||||
</WdButton>
|
||||
))}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
import { WdButton } from '@/hooks/Mapper/components/ui-kit/WdButton.tsx';
|
||||
import { ShipSizeStatus } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import { BUILT_IN_TOOLTIP_OPTIONS } from './constants.ts';
|
||||
import {
|
||||
SHIP_SIZES_DESCRIPTION,
|
||||
SHIP_SIZES_NAMES,
|
||||
SHIP_SIZES_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES_SHORT,
|
||||
SHIP_SIZES_SIZE,
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
|
||||
const SHIP_SIZE_STYLES: Record<ShipSizeStatus, { className: string; inactiveClassName: string }> = {
|
||||
[ShipSizeStatus.small]: {
|
||||
className: 'bg-indigo-400 hover:!bg-indigo-400',
|
||||
inactiveClassName: 'bg-indigo-400/30',
|
||||
},
|
||||
[ShipSizeStatus.medium]: {
|
||||
className: 'bg-cyan-500 hover:!bg-cyan-500',
|
||||
inactiveClassName: 'bg-cyan-500/30',
|
||||
},
|
||||
[ShipSizeStatus.large]: {
|
||||
className: 'bg-indigo-300 hover:!bg-indigo-300',
|
||||
inactiveClassName: 'bg-indigo-300/30',
|
||||
},
|
||||
[ShipSizeStatus.freight]: {
|
||||
className: 'bg-indigo-300 hover:!bg-indigo-300',
|
||||
inactiveClassName: 'bg-indigo-300/30',
|
||||
},
|
||||
[ShipSizeStatus.capital]: {
|
||||
className: 'bg-indigo-300 hover:!bg-indigo-300',
|
||||
inactiveClassName: 'bg-indigo-300/30',
|
||||
},
|
||||
};
|
||||
|
||||
export interface WdShipSizeSelectorProps {
|
||||
shipSize?: ShipSizeStatus;
|
||||
onChangeShipSize(shipSize: ShipSizeStatus): void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WdShipSizeSelector = ({
|
||||
shipSize = ShipSizeStatus.large,
|
||||
onChangeShipSize,
|
||||
className,
|
||||
}: WdShipSizeSelectorProps) => {
|
||||
return (
|
||||
<form>
|
||||
<div className={clsx('grid grid-cols-[1fr_1fr_1fr_1fr_1fr] gap-1', className)}>
|
||||
{SHIP_SIZES_NAMES_ORDER.map(size => {
|
||||
const style = SHIP_SIZE_STYLES[size];
|
||||
const tooltip = `${SHIP_SIZES_NAMES[size]} • ${SHIP_SIZES_SIZE[size]} t. ${SHIP_SIZES_DESCRIPTION[size]}`;
|
||||
|
||||
return (
|
||||
<WdButton
|
||||
key={size}
|
||||
outlined={false}
|
||||
value={SHIP_SIZES_NAMES_SHORT[size]}
|
||||
tooltip={tooltip}
|
||||
tooltipOptions={BUILT_IN_TOOLTIP_OPTIONS}
|
||||
size="small"
|
||||
className={clsx(
|
||||
`py-[1px] justify-center min-w-auto w-auto border-0 text-[11px] font-bold leading-[20px]`,
|
||||
{ [style.inactiveClassName]: shipSize !== size },
|
||||
style.className,
|
||||
)}
|
||||
onClick={() => onChangeShipSize(size)}
|
||||
>
|
||||
<span className="text-[11px] font-bold">{SHIP_SIZES_NAMES_SHORT[size]}</span>
|
||||
</WdButton>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -31,6 +31,7 @@ export type SignatureCustomInfo = {
|
||||
k162Type?: string;
|
||||
time_status?: number;
|
||||
isCrit?: boolean;
|
||||
mass_status?: number;
|
||||
};
|
||||
|
||||
export type SystemSignature = {
|
||||
|
||||
@@ -515,15 +515,15 @@ defmodule WandererApp.Map.Operations.Signatures do
|
||||
})
|
||||
end
|
||||
|
||||
# Update connection time_status from signature custom_info
|
||||
signature_time_status =
|
||||
# Update connection time_status and mass_status from signature custom_info
|
||||
{signature_time_status, signature_mass_status} =
|
||||
if not is_nil(signature.custom_info) do
|
||||
case Jason.decode(signature.custom_info) do
|
||||
{:ok, map} -> Map.get(map, "time_status")
|
||||
{:error, _} -> nil
|
||||
{:ok, decoded} -> {Map.get(decoded, "time_status"), Map.get(decoded, "mass_status")}
|
||||
{:error, _} -> {nil, nil}
|
||||
end
|
||||
else
|
||||
nil
|
||||
{nil, nil}
|
||||
end
|
||||
|
||||
# Update connection ship_size_type from signature wormhole type
|
||||
@@ -531,14 +531,14 @@ defmodule WandererApp.Map.Operations.Signatures do
|
||||
|
||||
# Back-link detection: if current signature yields no ship_size_type (e.g., K162),
|
||||
# look for a forward signature in the target system that links back to our source
|
||||
{signature_time_status, signature_ship_size_type} =
|
||||
{signature_time_status, signature_ship_size_type, signature_mass_status} =
|
||||
if is_nil(signature_ship_size_type) do
|
||||
case Server.SignaturesImpl.find_forward_signature(
|
||||
target_system.id,
|
||||
source_system.solar_system_id
|
||||
) do
|
||||
nil ->
|
||||
{signature_time_status, signature_ship_size_type}
|
||||
{signature_time_status, signature_ship_size_type, signature_mass_status}
|
||||
|
||||
forward_sig ->
|
||||
Logger.info(
|
||||
@@ -548,20 +548,33 @@ defmodule WandererApp.Map.Operations.Signatures do
|
||||
|
||||
forward_ship_size = EVEUtil.get_wh_size(forward_sig.type)
|
||||
|
||||
forward_time_status =
|
||||
if is_nil(signature_time_status) and not is_nil(forward_sig.custom_info) do
|
||||
{forward_time_status, forward_mass_status} =
|
||||
if not is_nil(forward_sig.custom_info) do
|
||||
case Jason.decode(forward_sig.custom_info) do
|
||||
{:ok, map} -> Map.get(map, "time_status")
|
||||
{:error, _} -> nil
|
||||
{:ok, decoded} ->
|
||||
fwd_time =
|
||||
if is_nil(signature_time_status),
|
||||
do: Map.get(decoded, "time_status"),
|
||||
else: signature_time_status
|
||||
|
||||
fwd_mass =
|
||||
if is_nil(signature_mass_status),
|
||||
do: Map.get(decoded, "mass_status"),
|
||||
else: signature_mass_status
|
||||
|
||||
{fwd_time, fwd_mass}
|
||||
|
||||
{:error, _} ->
|
||||
{signature_time_status, signature_mass_status}
|
||||
end
|
||||
else
|
||||
signature_time_status
|
||||
{signature_time_status, signature_mass_status}
|
||||
end
|
||||
|
||||
{forward_time_status, forward_ship_size}
|
||||
{forward_time_status, forward_ship_size, forward_mass_status}
|
||||
end
|
||||
else
|
||||
{signature_time_status, signature_ship_size_type}
|
||||
{signature_time_status, signature_ship_size_type, signature_mass_status}
|
||||
end
|
||||
|
||||
if not is_nil(signature_time_status) do
|
||||
@@ -579,6 +592,14 @@ defmodule WandererApp.Map.Operations.Signatures do
|
||||
ship_size_type: signature_ship_size_type
|
||||
})
|
||||
end
|
||||
|
||||
if not is_nil(signature_mass_status) do
|
||||
Server.update_connection_mass_status(map_id, %{
|
||||
solar_system_source_id: source_system.solar_system_id,
|
||||
solar_system_target_id: solar_system_target,
|
||||
mass_status: signature_mass_status
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
# Broadcast update
|
||||
|
||||
@@ -276,7 +276,14 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
map_id,
|
||||
connection_update
|
||||
),
|
||||
do: update_connection(map_id, :update_mass_status, [:mass_status], connection_update)
|
||||
do:
|
||||
update_connection(map_id, :update_mass_status, [:mass_status], connection_update, fn
|
||||
%{mass_status: old_mass_status},
|
||||
%{mass_status: mass_status} = updated_connection ->
|
||||
if mass_status != old_mass_status do
|
||||
maybe_update_linked_signature_mass_status(map_id, updated_connection)
|
||||
end
|
||||
end)
|
||||
|
||||
def update_connection_ship_size_type(
|
||||
map_id,
|
||||
@@ -528,6 +535,71 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
Impl.broadcast!(map_id, :signatures_updated, solar_system_id)
|
||||
end
|
||||
|
||||
defp maybe_update_linked_signature_mass_status(
|
||||
map_id,
|
||||
%{
|
||||
mass_status: mass_status,
|
||||
solar_system_source: solar_system_source,
|
||||
solar_system_target: solar_system_target
|
||||
} = _updated_connection
|
||||
) do
|
||||
with source_system when not is_nil(source_system) <-
|
||||
WandererApp.Map.find_system_by_location(
|
||||
map_id,
|
||||
%{solar_system_id: solar_system_source}
|
||||
),
|
||||
target_system when not is_nil(target_system) <-
|
||||
WandererApp.Map.find_system_by_location(
|
||||
map_id,
|
||||
%{solar_system_id: solar_system_target}
|
||||
),
|
||||
source_linked_signatures <-
|
||||
find_linked_signatures(source_system, target_system),
|
||||
target_linked_signatures <- find_linked_signatures(target_system, source_system) do
|
||||
update_signatures_mass_status(
|
||||
map_id,
|
||||
source_system.solar_system_id,
|
||||
source_linked_signatures,
|
||||
mass_status
|
||||
)
|
||||
|
||||
update_signatures_mass_status(
|
||||
map_id,
|
||||
target_system.solar_system_id,
|
||||
target_linked_signatures,
|
||||
mass_status
|
||||
)
|
||||
else
|
||||
error ->
|
||||
Logger.warning("Failed to update_linked_signature_mass_status: #{inspect(error)}")
|
||||
end
|
||||
end
|
||||
|
||||
defp update_signatures_mass_status(_map_id, _solar_system_id, [], _mass_status), do: :ok
|
||||
|
||||
defp update_signatures_mass_status(map_id, solar_system_id, signatures, mass_status) do
|
||||
signatures
|
||||
|> Enum.each(fn %{custom_info: custom_info_json} = sig ->
|
||||
update_params =
|
||||
if not is_nil(custom_info_json) do
|
||||
updated_custom_info =
|
||||
custom_info_json
|
||||
|> Jason.decode!()
|
||||
|> Map.merge(%{"mass_status" => mass_status})
|
||||
|> Jason.encode!()
|
||||
|
||||
%{custom_info: updated_custom_info}
|
||||
else
|
||||
updated_custom_info = Jason.encode!(%{"mass_status" => mass_status})
|
||||
%{custom_info: updated_custom_info}
|
||||
end
|
||||
|
||||
SignaturesImpl.apply_update_signature(map_id, sig, update_params)
|
||||
end)
|
||||
|
||||
Impl.broadcast!(map_id, :signatures_updated, solar_system_id)
|
||||
end
|
||||
|
||||
def maybe_add_connection(
|
||||
map_id,
|
||||
location,
|
||||
|
||||
@@ -222,6 +222,7 @@ defmodule WandererApp.Map.Server.SignaturesImpl do
|
||||
{:ok, updated} ->
|
||||
maybe_update_connection_time_status(map_id, existing, updated)
|
||||
maybe_update_connection_mass_status(map_id, existing, updated)
|
||||
maybe_sync_custom_mass_status_to_connection(map_id, existing, updated)
|
||||
:ok
|
||||
|
||||
{:error, reason} ->
|
||||
@@ -275,6 +276,29 @@ defmodule WandererApp.Map.Server.SignaturesImpl do
|
||||
|
||||
defp maybe_update_connection_mass_status(_map_id, _old_sig, _updated_sig), do: :ok
|
||||
|
||||
defp maybe_sync_custom_mass_status_to_connection(
|
||||
map_id,
|
||||
%{custom_info: old_custom_info} = _old_sig,
|
||||
%{custom_info: new_custom_info, system_id: system_id, linked_system_id: linked_system_id} =
|
||||
_updated_sig
|
||||
)
|
||||
when not is_nil(linked_system_id) do
|
||||
old_mass_status = get_mass_status(old_custom_info)
|
||||
new_mass_status = get_mass_status(new_custom_info)
|
||||
|
||||
if old_mass_status != new_mass_status and not is_nil(new_mass_status) do
|
||||
{:ok, source_system} = MapSystem.by_id(system_id)
|
||||
|
||||
ConnectionsImpl.update_connection_mass_status(map_id, %{
|
||||
solar_system_source_id: source_system.solar_system_id,
|
||||
solar_system_target_id: linked_system_id,
|
||||
mass_status: new_mass_status
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_sync_custom_mass_status_to_connection(_map_id, _old_sig, _updated_sig), do: :ok
|
||||
|
||||
@doc """
|
||||
Finds the "forward" signature in a target system that links back to the source system.
|
||||
Used for back-link detection: when a K162 is linked from System B → System A,
|
||||
@@ -367,4 +391,12 @@ defmodule WandererApp.Map.Server.SignaturesImpl do
|
||||
|> Jason.decode!()
|
||||
|> Map.get("time_status")
|
||||
end
|
||||
|
||||
defp get_mass_status(nil), do: nil
|
||||
|
||||
defp get_mass_status(custom_info_json) do
|
||||
custom_info_json
|
||||
|> Jason.decode!()
|
||||
|> Map.get("mass_status")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -296,22 +296,23 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do
|
||||
})
|
||||
end
|
||||
|
||||
signature_time_status =
|
||||
{signature_time_status, signature_mass_status} =
|
||||
if not is_nil(signature.custom_info) do
|
||||
signature.custom_info |> Jason.decode!() |> Map.get("time_status")
|
||||
decoded = signature.custom_info |> Jason.decode!()
|
||||
{Map.get(decoded, "time_status"), Map.get(decoded, "mass_status")}
|
||||
else
|
||||
nil
|
||||
{nil, nil}
|
||||
end
|
||||
|
||||
signature_ship_size_type = EVEUtil.get_wh_size(signature.type)
|
||||
|
||||
# Back-link detection: if current signature yields no ship_size_type (e.g., K162),
|
||||
# look for a forward signature in the target system that links back to our source
|
||||
{signature_time_status, signature_ship_size_type} =
|
||||
{signature_time_status, signature_ship_size_type, signature_mass_status} =
|
||||
if is_nil(signature_ship_size_type) do
|
||||
case SignaturesImpl.find_forward_signature(target_system.id, solar_system_source) do
|
||||
nil ->
|
||||
{signature_time_status, signature_ship_size_type}
|
||||
{signature_time_status, signature_ship_size_type, signature_mass_status}
|
||||
|
||||
forward_sig ->
|
||||
Logger.info(
|
||||
@@ -321,17 +322,29 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do
|
||||
|
||||
forward_ship_size = EVEUtil.get_wh_size(forward_sig.type)
|
||||
|
||||
forward_time_status =
|
||||
if is_nil(signature_time_status) and not is_nil(forward_sig.custom_info) do
|
||||
forward_sig.custom_info |> Jason.decode!() |> Map.get("time_status")
|
||||
{forward_time_status, forward_mass_status} =
|
||||
if not is_nil(forward_sig.custom_info) do
|
||||
decoded = forward_sig.custom_info |> Jason.decode!()
|
||||
|
||||
fwd_time =
|
||||
if is_nil(signature_time_status),
|
||||
do: Map.get(decoded, "time_status"),
|
||||
else: signature_time_status
|
||||
|
||||
fwd_mass =
|
||||
if is_nil(signature_mass_status),
|
||||
do: Map.get(decoded, "mass_status"),
|
||||
else: signature_mass_status
|
||||
|
||||
{fwd_time, fwd_mass}
|
||||
else
|
||||
signature_time_status
|
||||
{signature_time_status, signature_mass_status}
|
||||
end
|
||||
|
||||
{forward_time_status, forward_ship_size}
|
||||
{forward_time_status, forward_ship_size, forward_mass_status}
|
||||
end
|
||||
else
|
||||
{signature_time_status, signature_ship_size_type}
|
||||
{signature_time_status, signature_ship_size_type, signature_mass_status}
|
||||
end
|
||||
|
||||
if not is_nil(signature_time_status) do
|
||||
@@ -351,6 +364,15 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do
|
||||
ship_size_type: signature_ship_size_type
|
||||
})
|
||||
end
|
||||
|
||||
if not is_nil(signature_mass_status) do
|
||||
map_id
|
||||
|> WandererApp.Map.Server.update_connection_mass_status(%{
|
||||
solar_system_source_id: solar_system_source,
|
||||
solar_system_target_id: solar_system_target,
|
||||
mass_status: signature_mass_status
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
WandererApp.Map.Server.Impl.broadcast!(map_id, :signatures_updated, solar_system_source)
|
||||
|
||||
Reference in New Issue
Block a user