mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-14 05:06:10 +00:00
Compare commits
65 Commits
ui-issues-
...
v1.37.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af00402546 | ||
|
|
a245842ca4 | ||
|
|
8ddd672f13 | ||
|
|
92f471c0b0 | ||
|
|
9e2a2c5b44 | ||
|
|
5f5d3df003 | ||
|
|
c66cc8868e | ||
|
|
0d6528ce4f | ||
|
|
34c385ac5f | ||
|
|
b6d12e73a9 | ||
|
|
1118858120 | ||
|
|
ae3a34d5bf | ||
|
|
43df42e49b | ||
|
|
e670f3bf03 | ||
|
|
c26a9404c5 | ||
|
|
c0fad4ca92 | ||
|
|
16dbf9378b | ||
|
|
4001fe5eac | ||
|
|
2992dd8f8b | ||
|
|
98a03d1e59 | ||
|
|
2088393c79 | ||
|
|
093042b88a | ||
|
|
e5ef35c186 | ||
|
|
1cd23d5efd | ||
|
|
ead5818a3f | ||
|
|
a5f66ada68 | ||
|
|
0919742853 | ||
|
|
f3efffd259 | ||
|
|
f85317983c | ||
|
|
76f709b768 | ||
|
|
e3b2356302 | ||
|
|
3d810211ee | ||
|
|
7453795dc5 | ||
|
|
9de7cd99ee | ||
|
|
51489c1aa5 | ||
|
|
25dd6de770 | ||
|
|
2a825f5a02 | ||
|
|
908d249eb9 | ||
|
|
6cd119e8f4 | ||
|
|
9a59c8eb75 | ||
|
|
452c022d41 | ||
|
|
27e9bab82a | ||
|
|
edef860530 | ||
|
|
032cb63411 | ||
|
|
a1791ba578 | ||
|
|
3a69fd7786 | ||
|
|
8a90723c2e | ||
|
|
af2fc342c7 | ||
|
|
05ea2fcdbe | ||
|
|
6d4321fead | ||
|
|
3f6364c9ea | ||
|
|
0d11b12282 | ||
|
|
0796bcf7d0 | ||
|
|
0b5bec142a | ||
|
|
a5020b58f2 | ||
|
|
f039a74a8f | ||
|
|
0e6bb7390b | ||
|
|
b52b4eecca | ||
|
|
8186977d1d | ||
|
|
86adcfe4d7 | ||
|
|
ce2dd872c4 | ||
|
|
aadc53c90e | ||
|
|
cbc1b6b5c8 | ||
|
|
1aed7a9232 | ||
|
|
e0a37f7635 |
@@ -6,3 +6,4 @@ export EVE_CLIENT_WITH_WALLET_ID="<EVE_CLIENT_WITH_WALLET_ID>"
|
||||
export EVE_CLIENT_WITH_WALLET_SECRET="<EVE_CLIENT_WITH_WALLET_SECRET>"
|
||||
export GIT_SHA="1111"
|
||||
export WANDERER_INVITES="false"
|
||||
export WANDERER_PUBLIC_API_DISABLED="false"
|
||||
|
||||
1358
CHANGELOG.md
1358
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
|
||||
/*@import 'primereact/resources/themes/bootstrap4-dark-blue/theme.css' layer(primereact);*/
|
||||
|
||||
@import '../js/hooks/Mapper/components/map/styles/index.scss';
|
||||
|
||||
@layer tailwind-base {
|
||||
@tailwind base;
|
||||
}
|
||||
|
||||
@@ -108,3 +108,7 @@
|
||||
.p-dropdown-empty-message {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,23 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
const onSystemTemporaryName = useCallback((temporaryName?: string) => {
|
||||
const { system, outCommand } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemTemporaryName,
|
||||
data: {
|
||||
system_id: system,
|
||||
value: temporaryName ?? '',
|
||||
},
|
||||
});
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
|
||||
const onSystemStatus = useCallback((status: number) => {
|
||||
const { system, outCommand } = ref.current;
|
||||
if (!system) {
|
||||
@@ -161,6 +178,7 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
onLockToggle,
|
||||
onHubToggle,
|
||||
onSystemTag,
|
||||
onSystemTemporaryName,
|
||||
onSystemStatus,
|
||||
onSystemLabels,
|
||||
onOpenSettings,
|
||||
|
||||
@@ -14,9 +14,6 @@ export const useSystemInfo = ({ systemId }: UseSystemInfoProps) => {
|
||||
|
||||
const { systems: systemStatics } = useLoadSystemStatic({ systems: [systemId] });
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('JOipP', `systemStatics`, systemStatics);
|
||||
|
||||
return useMemo(() => {
|
||||
const staticInfo = systemStatics.get(parseInt(systemId));
|
||||
const dynamicInfo = getSystemById(systems, systemId);
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
}
|
||||
|
||||
.BackgroundAlternateColor {
|
||||
|
||||
background-color: var(--rf-soft-bg-color, #2f2f2f);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ import ReactFlow, {
|
||||
} from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
import classes from './Map.module.scss';
|
||||
import './styles/neon-theme.scss';
|
||||
import './styles/eve-common.scss';
|
||||
import { MapProvider, useMapState } from './MapProvider';
|
||||
import { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
|
||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
@@ -29,12 +27,13 @@ import {
|
||||
useContextMenuConnectionHandlers,
|
||||
useContextMenuRootHandlers,
|
||||
} from './components';
|
||||
import { OnMapSelectionChange } from './map.types';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import clsx from 'clsx';
|
||||
import { useBackgroundVars } from './hooks/useBackgroundVars';
|
||||
|
||||
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
||||
|
||||
@@ -92,6 +91,7 @@ interface MapCompProps {
|
||||
onSelectionChange: OnMapSelectionChange;
|
||||
onManualDelete(systems: string[]): void;
|
||||
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
||||
minimapClasses?: string;
|
||||
isShowMinimap?: boolean;
|
||||
@@ -100,6 +100,7 @@ interface MapCompProps {
|
||||
isThickConnections?: boolean;
|
||||
isShowBackgroundPattern?: boolean;
|
||||
isSoftBackground?: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const MapComp = ({
|
||||
@@ -116,16 +117,19 @@ const MapComp = ({
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
onAddSystem,
|
||||
}: MapCompProps) => {
|
||||
const { getNode } = useReactFlow();
|
||||
const { getNode, getNodes } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
|
||||
|
||||
useMapHandlers(refn, onSelectionChange);
|
||||
useUpdateNodes(nodes);
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
|
||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||
const { update } = useMapState();
|
||||
const { variant, gap, size, color } = useBackgroundVars(theme);
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
params => {
|
||||
@@ -184,6 +188,12 @@ const MapComp = ({
|
||||
(changes: NodeChange[]) => {
|
||||
const systemsIdsToRemove: string[] = [];
|
||||
|
||||
// prevents single node deselection on background / same node click
|
||||
// allows deseletion of all nodes if multiple are currently selected
|
||||
if (changes.length === 1 && changes[0].type == 'select' && changes[0].selected === false) {
|
||||
changes[0].selected = getNodes().filter(node => node.selected).length === 1;
|
||||
}
|
||||
|
||||
const nextChanges = changes.reduce((acc, change) => {
|
||||
if (change.type !== 'remove') {
|
||||
return [...acc, change];
|
||||
@@ -221,7 +231,7 @@ const MapComp = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={clsx(classes.MapRoot, { ['bg-neutral-900']: isSoftBackground })}>
|
||||
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
@@ -268,7 +278,7 @@ const MapComp = ({
|
||||
selectionMode={SelectionMode.Partial}
|
||||
>
|
||||
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
|
||||
{isShowBackgroundPattern && <Background />}
|
||||
{isShowBackgroundPattern && <Background variant={variant} gap={gap} size={size} color={color} />}
|
||||
</ReactFlow>
|
||||
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
|
||||
Test // DON NOT REMOVE
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.ConnectionTimeEOL {
|
||||
background-image: linear-gradient(207deg, transparent, #7452c3e3);
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-time-eol));
|
||||
}
|
||||
|
||||
.ConnectionFrigate {
|
||||
background-image: linear-gradient(207deg, transparent, #325d88);
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-frigate));
|
||||
}
|
||||
|
||||
.ConnectionSave {
|
||||
background-image: linear-gradient(207deg, transparent, rgba(155, 102, 45, 0.85));
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-save));
|
||||
}
|
||||
|
||||
.SelectedItem {
|
||||
background-color: rgba(98, 98, 98, 0.33);
|
||||
background-color: var(--selected-item-bg);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,14 @@ import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import classes from './ContextMenuConnection.module.scss';
|
||||
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
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';
|
||||
|
||||
export interface ContextMenuConnectionProps {
|
||||
contextMenuRef: RefObject<ContextMenu>;
|
||||
@@ -48,10 +55,6 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.CLOCK,
|
||||
command: onChangeTimeState,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
@@ -60,13 +63,9 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.large : ShipSizeStatus.small,
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Save mass`,
|
||||
className: clsx({
|
||||
@@ -75,19 +74,40 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole && !isFrigateSize
|
||||
? [
|
||||
...(!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: `Mass status`,
|
||||
icon: PrimeIcons.CHART_PIE,
|
||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
||||
label: MASS_STATE_NAMES[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?.mass_status === x,
|
||||
[classes.SelectedItem]: edge.data?.ship_size_type === x,
|
||||
}),
|
||||
command: () => onChangeMassState(x),
|
||||
command: () => onChangeShipSizeStatus(x),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -98,7 +118,7 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
command: onDeleteConnection,
|
||||
},
|
||||
];
|
||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeMassState, onChangeShipSizeStatus]);
|
||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeShipSizeStatus, onToggleMassSave, onChangeMassState]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -97,14 +97,16 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionMassStatus,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: MassState.normal,
|
||||
},
|
||||
});
|
||||
if (status === ShipSizeStatus.small) {
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionMassStatus,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: MassState.normal,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onToggleMassSave = useCallback((locked: boolean) => {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useReactFlow, XYPosition } from 'reactflow';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { useMapState } from '../../MapProvider.tsx';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { OnMapAddSystemCallback } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
|
||||
export const useContextMenuRootHandlers = () => {
|
||||
type UseContextMenuRootHandlers = {
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
};
|
||||
|
||||
export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHandlers = {}) => {
|
||||
const rf = useReactFlow();
|
||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||
const { outCommand } = useMapState();
|
||||
const [position, setPosition] = useState<XYPosition | null>(null);
|
||||
|
||||
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -18,14 +20,17 @@ export const useContextMenuRootHandlers = () => {
|
||||
contextMenuRef.current?.show(e);
|
||||
};
|
||||
|
||||
const onAddSystem = () => {
|
||||
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
|
||||
};
|
||||
const ref = useRef({ onAddSystem, position });
|
||||
ref.current = { onAddSystem, position };
|
||||
|
||||
const onAddSystemCallback = useCallback(() => {
|
||||
ref.current.onAddSystem?.({ coordinates: position });
|
||||
}, [position]);
|
||||
|
||||
return {
|
||||
handleRootContext,
|
||||
|
||||
contextMenuRef,
|
||||
onAddSystem,
|
||||
onAddSystem: onAddSystemCallback,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,23 +1,47 @@
|
||||
@import "@/hooks/Mapper/components/map/styles/neon-variables";
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.react-flow__edge.selected {
|
||||
.EdgePathBack {
|
||||
stroke: $pastel-yellow;
|
||||
.EdgePathBack {
|
||||
fill: none;
|
||||
stroke: #80a5c5;
|
||||
stroke-width: 3px;
|
||||
|
||||
&.TimeCrit {
|
||||
stroke: #f11ab2;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
&.Hovered {
|
||||
stroke: #b5c8d9;
|
||||
|
||||
&.TimeCrit {
|
||||
stroke: #ef7dce;
|
||||
}
|
||||
}
|
||||
|
||||
&.Tick {
|
||||
stroke-width: 5px;
|
||||
|
||||
&.TimeCrit {
|
||||
stroke-width: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Gate {
|
||||
stroke: #9aff40;
|
||||
}
|
||||
}
|
||||
|
||||
.EdgePathFront {
|
||||
fill: none;
|
||||
|
||||
stroke: #2c3844;
|
||||
stroke-width: 2px;
|
||||
|
||||
&.MassVerge:not(&.Frigate) {
|
||||
stroke: #af2900;
|
||||
stroke: #af0000;
|
||||
}
|
||||
|
||||
&.MassHalf:not(&.Frigate) {
|
||||
stroke: #a85f00;
|
||||
stroke: #ffd700;
|
||||
}
|
||||
|
||||
&.Frigate {
|
||||
@@ -54,46 +78,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
.EdgePathBack {
|
||||
fill: none;
|
||||
|
||||
stroke: #80a5c5;
|
||||
stroke-width: 3px;
|
||||
|
||||
&.TimeCrit {
|
||||
stroke: #f11ab2;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
&.Hovered {
|
||||
stroke: #b5c8d9;
|
||||
|
||||
&.TimeCrit {
|
||||
stroke: #ef7dce;
|
||||
}
|
||||
}
|
||||
|
||||
&.Tick {
|
||||
stroke-width: 5px;
|
||||
|
||||
&.TimeCrit {
|
||||
stroke-width: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Gate {
|
||||
stroke: #9aff40;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ClickPath {
|
||||
fill: none;
|
||||
stroke: none;
|
||||
stroke-width: 8px;
|
||||
}
|
||||
|
||||
.LinkLabel{
|
||||
.Handle {
|
||||
border: 1px solid var(--pastel-blue);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
z-index: 1001;
|
||||
|
||||
&.Tick {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
&.Right {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.LinkLabel {
|
||||
font-size: 9px;
|
||||
line-height: 10px;
|
||||
padding: 2px 4px;
|
||||
@@ -109,22 +116,3 @@
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid #5a7d9a;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
z-index: 1001;
|
||||
|
||||
&.Tick {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
|
||||
&.Right {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeS
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { SHIP_SIZES_DESCRIPTION, SHIP_SIZES_NAMES_SHORT } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
|
||||
const MAP_TRANSLATES: Record<string, string> = {
|
||||
[Position.Top]: 'translate(-48%, 0%)',
|
||||
@@ -30,6 +31,14 @@ const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
|
||||
[Position.Right]: { x: 0, y: 0 },
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_COLORS = {
|
||||
[ShipSizeStatus.small]: 'bg-indigo-400',
|
||||
[ShipSizeStatus.medium]: 'bg-cyan-500',
|
||||
[ShipSizeStatus.large]: '',
|
||||
[ShipSizeStatus.freight]: 'bg-lime-400',
|
||||
[ShipSizeStatus.capital]: 'bg-red-400',
|
||||
};
|
||||
|
||||
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
|
||||
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
|
||||
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
|
||||
@@ -121,7 +130,7 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
/>
|
||||
|
||||
<div
|
||||
className="absolute flex items-center gap-1"
|
||||
className="absolute flex items-center gap-1 pointer-events-none"
|
||||
style={{
|
||||
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||
}}
|
||||
@@ -137,6 +146,19 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{isWormhole && data.ship_size_type !== ShipSizeStatus.large && (
|
||||
<WdTooltipWrapper
|
||||
content={SHIP_SIZES_DESCRIPTION[data.ship_size_type]}
|
||||
className={clsx(
|
||||
classes.LinkLabel,
|
||||
'pointer-events-auto rounded opacity-100 cursor-auto text-neutral-900 font-bold',
|
||||
SHIP_SIZES_COLORS[data.ship_size_type],
|
||||
)}
|
||||
>
|
||||
{SHIP_SIZES_NAMES_SHORT[data.ship_size_type]}
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
</div>
|
||||
</EdgeLabelRenderer>
|
||||
</>
|
||||
|
||||
@@ -1,319 +1,200 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020; // Dark background for tooltips
|
||||
|
||||
.RootCustomNode {
|
||||
display: flex;
|
||||
width: 130px;
|
||||
height: 34px;
|
||||
|
||||
flex-direction: column;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
|
||||
background-color: $tooltip-bg;
|
||||
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
|
||||
border: 1px solid darken($pastel-blue, 10%);
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
||||
&.Mataria,
|
||||
&.Amarria,
|
||||
&.Gallente,
|
||||
&.Caldaria {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
z-index: -1;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Mataria {
|
||||
&::before {
|
||||
background-image: url('/images/mataria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Caldaria {
|
||||
&::before {
|
||||
background-image: url('/images/caldaria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Amarria {
|
||||
&::before {
|
||||
opacity: 0.45;
|
||||
background-image: url('/images/amarr-180.png');
|
||||
background-position-x: 0;
|
||||
background-position-y: -13px;
|
||||
}
|
||||
}
|
||||
|
||||
&.Gallente {
|
||||
&::before {
|
||||
opacity: 0.5;
|
||||
background-image: url('/images/gallente-180.png');
|
||||
background-position-x: 1px;
|
||||
background-position-y: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background-color: $tooltip-bg;
|
||||
color: $text-color;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $pastel-pink;
|
||||
}
|
||||
|
||||
&.eve-system-status-home {
|
||||
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
|
||||
|
||||
&.selected {
|
||||
border-color: $eve-solar-system-status-color-home;
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-friendly {
|
||||
border: 1px solid darken($eve-solar-system-status-color-friendly, 20%);
|
||||
background-image: linear-gradient(275deg, darken($eve-solar-system-status-friendly, 30%), transparent);
|
||||
|
||||
&.selected {
|
||||
border-color: darken($eve-solar-system-status-color-friendly, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-lookingFor {
|
||||
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
|
||||
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-warning {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-warning, transparent);
|
||||
}
|
||||
|
||||
&.eve-system-status-dangerous {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
|
||||
}
|
||||
|
||||
&.eve-system-status-target {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Region backgrounds */
|
||||
.Mataria,
|
||||
.Amarria,
|
||||
.Gallente,
|
||||
.Caldaria {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
z-index: -1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
.Mataria::before {
|
||||
background-image: url('/images/mataria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -14px;
|
||||
}
|
||||
.Caldaria::before {
|
||||
background-image: url('/images/caldaria-180.png');
|
||||
opacity: 0.6;
|
||||
background-position-x: 1px;
|
||||
background-position-y: -10px;
|
||||
}
|
||||
.Amarria::before {
|
||||
opacity: 0.45;
|
||||
background-image: url('/images/amarr-180.png');
|
||||
background-position-x: 0;
|
||||
background-position-y: -13px;
|
||||
}
|
||||
.Gallente::before {
|
||||
opacity: 0.5;
|
||||
background-image: url('/images/gallente-180.png');
|
||||
background-position-x: 1px;
|
||||
background-position-y: 0;
|
||||
}
|
||||
|
||||
/* Node selected styling */
|
||||
.selected {
|
||||
border-color: var(--pastel-pink);
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
/* Eve system status backgrounds, etc. */
|
||||
.eve-system-status-home {
|
||||
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
|
||||
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly), transparent);
|
||||
&.selected {
|
||||
border-color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
}
|
||||
.eve-system-status-friendly {
|
||||
border: 1px solid var(--eve-solar-system-status-color-friendly-dark20);
|
||||
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly-dark30), transparent);
|
||||
&.selected {
|
||||
border-color: var(--eve-solar-system-status-color-friendly-dark5);
|
||||
}
|
||||
}
|
||||
.eve-system-status-lookingFor {
|
||||
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
|
||||
background-image: linear-gradient(275deg, #45ff8f2f, #457fff2f);
|
||||
&.selected {
|
||||
border-color: var(--pastel-pink);
|
||||
}
|
||||
}
|
||||
.eve-system-status-warning {
|
||||
background-image: linear-gradient(275deg, var(--eve-solar-system-status-warning), transparent);
|
||||
}
|
||||
.eve-system-status-dangerous {
|
||||
background-image: linear-gradient(275deg, var(--eve-solar-system-status-dangerous), transparent);
|
||||
}
|
||||
.eve-system-status-target {
|
||||
background-image: linear-gradient(275deg, var(--eve-solar-system-status-target), transparent);
|
||||
}
|
||||
|
||||
/* Bookmarks row */
|
||||
.Bookmarks {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
left: 4px;
|
||||
|
||||
& > .Bookmark {
|
||||
min-width: 13px;
|
||||
height: 22px;
|
||||
position: relative;
|
||||
top: -13px;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
padding-top: 2px;
|
||||
font-weight: bolder;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
|
||||
//background-color: #833ca4;
|
||||
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.BookmarkWithIcon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: -2px;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
|
||||
padding-right: 2px;
|
||||
|
||||
& > .icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
& > .text {
|
||||
margin-top: 1px;
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Unsplashed {
|
||||
position: absolute;
|
||||
width: calc(50% - 4px);
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
left: 2px;
|
||||
|
||||
&--right {
|
||||
left: calc(50% + 6px);
|
||||
}
|
||||
|
||||
& > .Signature {
|
||||
width: 13px;
|
||||
height: 4px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
padding-top: 2px;
|
||||
font-weight: bolder;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
display: block;
|
||||
|
||||
background-color: #833ca4;
|
||||
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
.Bookmark {
|
||||
min-width: 13px;
|
||||
height: 22px;
|
||||
position: relative;
|
||||
top: -13px;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
padding-top: 2px;
|
||||
font-weight: bolder;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
.BookmarkWithIcon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: -2px;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
|
||||
padding-right: 2px;
|
||||
.icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
.text {
|
||||
margin-top: 1px;
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.HeadRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
/* Slight shadow for text */
|
||||
.textShadowThin {
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.tagSkyFontMedium {
|
||||
color: #38bdf8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* HeadRow near the top, possibly ~19px tall. */
|
||||
.HeadRow {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
.classTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgb(0 0 0 / 73%);
|
||||
text-shadow: 0 0 2px rgba(0, 0, 0, 0.73);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.TagTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||
|
||||
color: #ffb01d;
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.classSystemName {
|
||||
//font-weight: bold;
|
||||
}
|
||||
|
||||
.solarSystemName {
|
||||
}
|
||||
}
|
||||
|
||||
/* Usually ~19px tall for bottom row. */
|
||||
.BottomRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 19px;
|
||||
|
||||
.localCounter {
|
||||
display: flex;
|
||||
//align-items: center;
|
||||
gap: 2px;
|
||||
|
||||
& > i {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
& > span {
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
font-weight: 500;
|
||||
//margin-top: 1px;
|
||||
}
|
||||
}
|
||||
.hasUserCharacters {
|
||||
color: #fbbf24;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overflows, effect icons, etc. */
|
||||
.systemNameOverflow {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.effect {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-top: -2px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.statics {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
font-size: 8px;
|
||||
|
||||
& > * {
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
/* The node handlers / double-click area */
|
||||
.Handlers {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
@@ -322,30 +203,22 @@ $tooltip-bg: #202020; // Dark background for tooltips
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid $pastel-blue;
|
||||
border: 1px solid var(--pastel-blue);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
border-color: var(--pastel-pink);
|
||||
}
|
||||
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
@@ -357,17 +230,28 @@ $tooltip-bg: #202020; // Dark background for tooltips
|
||||
&.HandleTop {
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -3px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unsplashed signature containers */
|
||||
.Unsplashed {
|
||||
position: absolute;
|
||||
width: calc(50% - 4px);
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
left: 2px;
|
||||
&--right {
|
||||
left: calc(50% + 6px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { MapSolarSystemType } from '../../map.types';
|
||||
import classes from './SolarSystemNode.module.scss';
|
||||
import clsx from 'clsx';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
|
||||
|
||||
import {
|
||||
EFFECT_BACKGROUND_STYLES,
|
||||
@@ -56,6 +58,8 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
|
||||
const {
|
||||
system_class,
|
||||
security,
|
||||
@@ -71,9 +75,8 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
|
||||
const signatures = data.system_signatures;
|
||||
|
||||
const { locked, name, tag, status, labels, id } = data || {};
|
||||
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
|
||||
|
||||
const customName = solar_system_name !== name ? name : undefined;
|
||||
|
||||
const {
|
||||
data: {
|
||||
@@ -95,8 +98,9 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
|
||||
|
||||
const charactersInSystem = useMemo(() => {
|
||||
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
|
||||
// eslint-disable-next-line
|
||||
return characters
|
||||
.filter(c => c.location?.solar_system_id === solar_system_id)
|
||||
.filter(c => c.online);
|
||||
}, [characters, presentCharacters, solar_system_id]);
|
||||
|
||||
const isWormhole = isWormholeSpace(system_class);
|
||||
@@ -114,7 +118,6 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
if (!systemKills) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return systemKills;
|
||||
}, [kills, solar_system_id]);
|
||||
|
||||
@@ -136,11 +139,13 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||
|
||||
const systemName = (isTempSystemNameEnabled && temporaryName) || solar_system_name;
|
||||
const customName = (isTempSystemNameEnabled && temporaryName && name) || (solar_system_name !== name && name);
|
||||
|
||||
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||
if (!isShowUnsplashedSignatures) {
|
||||
return [[], []];
|
||||
}
|
||||
|
||||
return prepareUnsplashedChunks(
|
||||
signatures
|
||||
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||
@@ -158,20 +163,20 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
<div className={classes.Bookmarks}>
|
||||
{labelCustom !== '' && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.custom)}>
|
||||
<span className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] ">{labelCustom}</span>
|
||||
<span className={classes.textShadowThin}>{labelCustom}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{is_shattered && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
||||
<span className={clsx('pi pi-chart-pie text-[0.55rem]')} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{killsCount && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[getActivityType(killsCount)])}>
|
||||
<div className={clsx(classes.BookmarkWithIcon)}>
|
||||
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
|
||||
<span className={clsx(PrimeIcons.BOLT, 'text-[0.65rem]')} />
|
||||
<span className={clsx(classes.text)}>{killsCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -184,32 +189,46 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
|
||||
[classes.selected]: selected,
|
||||
})}
|
||||
onMouseDownCapture={dbClick}
|
||||
className={clsx(
|
||||
classes.RootCustomNode,
|
||||
regionClass,
|
||||
classes[STATUS_CLASSES[status]],
|
||||
{ [classes.selected]: selected },
|
||||
'flex flex-col w-[130px] h-[34px]',
|
||||
'px-[6px] pt-[2px] pb-[3px] text-[10px]',
|
||||
'leading-[1] space-y-[1px]',
|
||||
'bg-[var(--tooltip-bg)] shadow-[0_0_5px_rgba(45,45,45,0.5)]',
|
||||
'border border-[var(--pastel-blue-darken10)] rounded-[5px]'
|
||||
)}
|
||||
>
|
||||
{visible && (
|
||||
<>
|
||||
<div className={classes.HeadRow}>
|
||||
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
|
||||
<div className={clsx(classes.HeadRow, 'flex items-center gap-[3px]')}>
|
||||
<div className={clsx(classes.classTitle, classTitleColor, classes.textShadowThin)}>
|
||||
{class_title ?? '-'}
|
||||
</div>
|
||||
|
||||
{tag != null && tag !== '' && (
|
||||
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{tag}</div>
|
||||
<div className={clsx(classes.TagTitle, classes.tagSkyFontMedium)}>
|
||||
{tag}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
classes.classSystemName,
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||
classes.textShadowThin,
|
||||
classes.systemNameOverflow,
|
||||
'overflow-hidden'
|
||||
)}
|
||||
>
|
||||
{solar_system_name}
|
||||
{systemName}
|
||||
</div>
|
||||
|
||||
{isWormhole && (
|
||||
<div className={classes.statics}>
|
||||
<div className={clsx(classes.statics, 'flex gap-[2px] text-[8px]')}>
|
||||
{sortedStatics.map(x => (
|
||||
<WormholeClassComp key={x} id={x} />
|
||||
))}
|
||||
@@ -221,40 +240,34 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center gap-[3px]')}>
|
||||
{customName && (
|
||||
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
|
||||
<div className={clsx('font-bold', classes.customName)}>
|
||||
{customName}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && (
|
||||
<div
|
||||
className={clsx(
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||
)}
|
||||
>
|
||||
{region_name}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && <div className={clsx(classes.regionName)}>{region_name}</div>}
|
||||
{isWormhole && !customName && <div />}
|
||||
|
||||
<div className="flex items-center justify-end">
|
||||
<div className="flex gap-1 items-center">
|
||||
{locked && <i className={PrimeIcons.LOCK} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>}
|
||||
|
||||
{hubs.includes(solar_system_id.toString()) && (
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
|
||||
)}
|
||||
|
||||
{charactersInSystem.length > 0 && (
|
||||
<div className={clsx(classes.localCounter, { ['text-amber-300']: hasUserCharacters })}>
|
||||
<i className="pi pi-users" style={{ fontSize: '0.50rem' }}></i>
|
||||
<span className="font-sans">{charactersInSystem.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center ml-auto gap-[2px]">
|
||||
{locked && (
|
||||
<i className={clsx(PrimeIcons.LOCK, 'text-[0.45rem] font-bold')} />
|
||||
)}
|
||||
{hubs.includes(solar_system_id.toString()) && (
|
||||
<i className={clsx(PrimeIcons.MAP_MARKER, 'text-[0.45rem] font-bold')} />
|
||||
)}
|
||||
{charactersInSystem.length > 0 && (
|
||||
<div
|
||||
className={clsx(
|
||||
classes.localCounter,
|
||||
{ [classes.hasUserCharacters]: hasUserCharacters },
|
||||
'flex gap-[2px]'
|
||||
)}
|
||||
>
|
||||
<i className="pi pi-users text-[0.50rem]" />
|
||||
<span className="font-sans text-[0.65rem]">{charactersInSystem.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -268,16 +281,15 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{visible && isShowUnsplashedSignatures && (
|
||||
<div className={clsx([classes.Unsplashed, classes['Unsplashed--right']])}>
|
||||
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||
{unsplashedRight.map(x => (
|
||||
<UnsplashedSignature key={x.sig_id} signature={x} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div onMouseDownCapture={dbClick} className={classes.Handlers}>
|
||||
<div className={classes.Handlers}>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleTop, {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
width: 13px;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
color: var(--text-color);
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
font-weight: bolder;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ConnectionType, MassState } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum SOLAR_SYSTEM_CLASS_IDS {
|
||||
ccp1 = -1,
|
||||
@@ -727,16 +727,41 @@ export const MASS_STATE_NAMES = {
|
||||
[MassState.verge]: 'Verge of collapse',
|
||||
};
|
||||
|
||||
// export const SHIP_SIZES_NAMES_ORDER = [
|
||||
// ShipSizeStatus.small,
|
||||
// ShipSizeStatus.normal,
|
||||
// // ShipSizeStatus.large,
|
||||
// // ShipSizeStatus.capital,
|
||||
// ];
|
||||
//
|
||||
// export const SHIP_SIZES_NAMES = {
|
||||
// [ShipSizeStatus.small]: 'Frigate',
|
||||
// [ShipSizeStatus.normal]: 'Normal',
|
||||
// // [ShipSizeStatus.large]: 'Normal',
|
||||
// // [ShipSizeStatus.capital]: 'Normal',
|
||||
// };
|
||||
export const SHIP_SIZES_NAMES_ORDER = [
|
||||
ShipSizeStatus.small,
|
||||
ShipSizeStatus.medium,
|
||||
ShipSizeStatus.large,
|
||||
ShipSizeStatus.freight,
|
||||
ShipSizeStatus.capital,
|
||||
];
|
||||
|
||||
export const SHIP_SIZES_NAMES = {
|
||||
[ShipSizeStatus.small]: 'Frigate',
|
||||
[ShipSizeStatus.medium]: 'Medium',
|
||||
[ShipSizeStatus.large]: 'Normal',
|
||||
[ShipSizeStatus.freight]: 'Huge',
|
||||
[ShipSizeStatus.capital]: 'Capital',
|
||||
};
|
||||
export const SHIP_SIZES_SIZE = {
|
||||
[ShipSizeStatus.small]: '5K',
|
||||
[ShipSizeStatus.medium]: '62K',
|
||||
[ShipSizeStatus.large]: '375K',
|
||||
[ShipSizeStatus.freight]: '1M',
|
||||
[ShipSizeStatus.capital]: '2M',
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_DESCRIPTION = {
|
||||
[ShipSizeStatus.small]: 'Frigate wormhole - up to Destroyer | 5K t.',
|
||||
[ShipSizeStatus.medium]: 'Cruise wormhole - up to Battlecruiser | 62K t.',
|
||||
[ShipSizeStatus.large]: 'Large wormhole - up to Battleship | 375K t.',
|
||||
[ShipSizeStatus.freight]: 'Huge wormhole - up to Freighter | 1M t.',
|
||||
[ShipSizeStatus.capital]: 'Capital wormhole - up to Capital | 2M t.',
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_NAMES_SHORT = {
|
||||
[ShipSizeStatus.small]: 'S',
|
||||
[ShipSizeStatus.medium]: 'M',
|
||||
[ShipSizeStatus.large]: 'L',
|
||||
[ShipSizeStatus.freight]: 'H',
|
||||
[ShipSizeStatus.capital]: 'XL',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BackgroundVariant } from 'reactflow';
|
||||
|
||||
|
||||
export function useBackgroundVars(themeName?: string) {
|
||||
const [variant, setVariant] = useState<BackgroundVariant>(BackgroundVariant.Dots);
|
||||
const [gap, setGap] = useState<number>(16);
|
||||
const [size, setSize] = useState<number>(1);
|
||||
const [color, setColor] = useState('#81818b')
|
||||
|
||||
useEffect(() => {
|
||||
let themeEl = document.querySelector('.pathfinder-theme, .neon-theme');
|
||||
if (!themeEl) {
|
||||
themeEl = document.documentElement;
|
||||
}
|
||||
|
||||
const style = getComputedStyle(themeEl as HTMLElement);
|
||||
|
||||
const rawVariant = style.getPropertyValue('--rf-bg-variant').replace(/['"]/g, '').trim().toLowerCase();
|
||||
let finalVariant: BackgroundVariant = BackgroundVariant.Dots;
|
||||
if (rawVariant === 'lines') {
|
||||
finalVariant = BackgroundVariant.Lines;
|
||||
} else if (rawVariant === 'cross') {
|
||||
finalVariant = BackgroundVariant.Cross;
|
||||
}
|
||||
|
||||
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
|
||||
const cssVarSize = style.getPropertyValue('--rf-bg-size');
|
||||
const cssColor = style.getPropertyValue('--rf-bg-color');
|
||||
|
||||
const gapNum = parseInt(cssVarGap, 10) || 16;
|
||||
const sizeNum = parseInt(cssVarSize, 10) || 1;
|
||||
|
||||
setVariant(finalVariant);
|
||||
setGap(gapNum);
|
||||
setSize(sizeNum);
|
||||
setColor(cssColor);
|
||||
|
||||
}, [themeName]);
|
||||
|
||||
return { variant, gap, size, color };
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { XYPosition } from 'reactflow';
|
||||
|
||||
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
|
||||
|
||||
@@ -7,3 +8,5 @@ export type OnMapSelectionChange = (event: {
|
||||
systems: string[];
|
||||
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
||||
}) => void;
|
||||
|
||||
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;
|
||||
|
||||
@@ -1,78 +1,114 @@
|
||||
$eve-link-color-default: #333;
|
||||
$eve-link-color-top-mass-0: #333;
|
||||
$eve-link-color-top-mass-1: #5a4520;
|
||||
$eve-link-color-top-mass-2: #672c2c;
|
||||
$eve-link-color-middle-mass-0: #333;
|
||||
$eve-link-color-middle-mass-1: #333;
|
||||
$eve-link-color-middle-mass-2: #333;
|
||||
$eve-link-color-middle-time-0: #5c5c5c;
|
||||
$eve-link-color-middle-time-1: #ff00cd;
|
||||
$eve-link-color-middle-time-1-border: #99f3ff;
|
||||
:root {
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
$eve-link-color-top-mass-1-time-1: #796300;
|
||||
$eve-link-color-top-mass-2-time-1: #8c1717;
|
||||
$eve-link-color-temp: orange;
|
||||
--pastel-blue-darken10: #4f6b86;
|
||||
--pastel-blue-lighten10: #6da3af;
|
||||
--pastel-pink-darken10: #bb7ca9;
|
||||
--pastel-pink-lighten10: #e0a6cb;
|
||||
|
||||
$eve-effect-pulsar: #40aef5;
|
||||
$eve-effect-magnetar: #f058f8;
|
||||
$eve-effect-wolfRayet: #ef7843;
|
||||
$eve-effect-blackHole: #1b1b1b;
|
||||
$eve-effect-cataclysmicVariable: #ffea90;
|
||||
$eve-effect-redGiant: #fd3c3c;
|
||||
$eve-effect-dazhLiminalityLocus: #ff6464;
|
||||
$eve-effect-imperialStellarObservatory: #6991ce;
|
||||
$eve-effect-stateStellarObservatory: #6991ce;
|
||||
$eve-effect-republicStellarObservatory: #6991ce;
|
||||
$eve-effect-federalStellarObservatory: #6991ce;
|
||||
--pastel-green-darken10: #79a244;
|
||||
--pastel-green-lighten10: #99cf52;
|
||||
|
||||
$eve-wh-type-color-high: #5dffd2;
|
||||
$eve-wh-type-color-low: #f79400;
|
||||
$eve-wh-type-color-null: #fc3c3c;
|
||||
$eve-wh-type-color-c1: #69bfce;
|
||||
$eve-wh-type-color-c2: #6991ce;
|
||||
$eve-wh-type-color-c3: #a8cb70;
|
||||
$eve-wh-type-color-c4: #e39c68;
|
||||
$eve-wh-type-color-c5: #de8686;
|
||||
$eve-wh-type-color-c6: #e76363;
|
||||
$eve-wh-type-color-c13: #988cb5;
|
||||
$eve-wh-type-color-drifter: #ff44f6;
|
||||
$eve-wh-type-color-thera: #ffffff;
|
||||
$eve-wh-type-color-zarzakh: #212121;
|
||||
--pastel-yellow-darken10: #e6c44f;
|
||||
--pastel-yellow-lighten10: #ffe874;
|
||||
|
||||
$eve-security-color-10: #2c74df;
|
||||
$eve-security-color-09: #3998e8;
|
||||
$eve-security-color-08: #4dcbf5;
|
||||
$eve-security-color-07: #60d8a2;
|
||||
$eve-security-color-06: #71e454;
|
||||
$eve-security-color-05: #f2fc81;
|
||||
$eve-security-color-04: #d96c07;
|
||||
$eve-security-color-03: #cb440f;
|
||||
$eve-security-color-02: #b91117;
|
||||
$eve-security-color-01: #732020;
|
||||
$eve-security-color-00: #8b3263;
|
||||
$eve-security-color-m-01: #8b3263;
|
||||
$eve-security-color-m-02: #8b3263;
|
||||
$eve-security-color-m-03: #8b3263;
|
||||
$eve-security-color-m-04: #8b3263;
|
||||
$eve-security-color-m-05: #8b3263;
|
||||
$eve-security-color-m-06: #8b3263;
|
||||
$eve-security-color-m-07: #8b3263;
|
||||
$eve-security-color-m-08: #8b3263;
|
||||
$eve-security-color-m-09: #8b3263;
|
||||
$eve-security-color-m-10: #8b3263;
|
||||
/* Eve Link Colors */
|
||||
--eve-link-color-default: #333;
|
||||
--eve-link-color-top-mass-0: #333;
|
||||
--eve-link-color-top-mass-1: #5a4520;
|
||||
--eve-link-color-top-mass-2: #672c2c;
|
||||
--eve-link-color-middle-mass-0: #333;
|
||||
--eve-link-color-middle-mass-1: #333;
|
||||
--eve-link-color-middle-mass-2: #333;
|
||||
--eve-link-color-middle-time-0: #5c5c5c;
|
||||
--eve-link-color-middle-time-1: #ff00cd;
|
||||
--eve-link-color-middle-time-1-border: #99f3ff;
|
||||
--eve-link-color-top-mass-1-time-1: #796300;
|
||||
--eve-link-color-top-mass-2-time-1: #8c1717;
|
||||
--eve-link-color-temp: orange;
|
||||
|
||||
$eve-solar-system-status-unknown: transparent;
|
||||
$eve-solar-system-status-friendly: #3bbd3952;
|
||||
$eve-solar-system-status-warning: #906518a6;
|
||||
$eve-solar-system-status-target: #b439ff6b;
|
||||
$eve-solar-system-status-dangerous: #d54040;
|
||||
$eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
||||
$eve-solar-system-status-home: rgb(197, 253, 67);
|
||||
/* Wormhole Effects */
|
||||
--eve-effect-pulsar: #40aef5;
|
||||
--eve-effect-magnetar: #f058f8;
|
||||
--eve-effect-wolfRayet: #ef7843;
|
||||
--eve-effect-blackHole: #1b1b1b;
|
||||
--eve-effect-cataclysmicVariable: #ffea90;
|
||||
--eve-effect-redGiant: #fd3c3c;
|
||||
--eve-effect-dazhLiminalityLocus: #ff6464;
|
||||
--eve-effect-imperialStellarObservatory: #6991ce;
|
||||
--eve-effect-stateStellarObservatory: #6991ce;
|
||||
--eve-effect-republicStellarObservatory: #6991ce;
|
||||
--eve-effect-federalStellarObservatory: #6991ce;
|
||||
|
||||
$eve-solar-system-status-color-unknown: transparent;
|
||||
$eve-solar-system-status-color-friendly: #3bbd39;
|
||||
$eve-solar-system-status-color-warning: #ffb93b;
|
||||
$eve-solar-system-status-color-target: #b439ff;
|
||||
$eve-solar-system-status-color-dangerous: #d54040;
|
||||
$eve-solar-system-status-color-lookingFor: #43c2fd;
|
||||
$eve-solar-system-status-color-home: rgb(197, 253, 67);
|
||||
/* WH Types */
|
||||
--eve-wh-type-color-high: #5dffd2;
|
||||
--eve-wh-type-color-low: #f79400;
|
||||
--eve-wh-type-color-null: #fc3c3c;
|
||||
--eve-wh-type-color-c1: #69bfce;
|
||||
--eve-wh-type-color-c2: #6991ce;
|
||||
--eve-wh-type-color-c3: #a8cb70;
|
||||
--eve-wh-type-color-c4: #e39c68;
|
||||
--eve-wh-type-color-c5: #de8686;
|
||||
--eve-wh-type-color-c6: #e76363;
|
||||
--eve-wh-type-color-c13: #988cb5;
|
||||
--eve-wh-type-color-drifter: #ff44f6;
|
||||
--eve-wh-type-color-thera: #ffffff;
|
||||
--eve-wh-type-color-zarzakh: #212121;
|
||||
|
||||
/* Security Colors */
|
||||
--eve-security-color-10: #2c74df;
|
||||
--eve-security-color-09: #3998e8;
|
||||
--eve-security-color-08: #4dcbf5;
|
||||
--eve-security-color-07: #60d8a2;
|
||||
--eve-security-color-06: #71e454;
|
||||
--eve-security-color-05: #f2fc81;
|
||||
--eve-security-color-04: #d96c07;
|
||||
--eve-security-color-03: #cb440f;
|
||||
--eve-security-color-02: #b91117;
|
||||
--eve-security-color-01: #732020;
|
||||
--eve-security-color-00: #8b3263;
|
||||
--eve-security-color-m-01: #8b3263;
|
||||
--eve-security-color-m-02: #8b3263;
|
||||
--eve-security-color-m-03: #8b3263;
|
||||
--eve-security-color-m-04: #8b3263;
|
||||
--eve-security-color-m-05: #8b3263;
|
||||
--eve-security-color-m-06: #8b3263;
|
||||
--eve-security-color-m-07: #8b3263;
|
||||
--eve-security-color-m-08: #8b3263;
|
||||
--eve-security-color-m-09: #8b3263;
|
||||
--eve-security-color-m-10: #8b3263;
|
||||
|
||||
/* Solar System Status */
|
||||
--eve-solar-system-status-unknown: transparent;
|
||||
--eve-solar-system-status-friendly: #3bbd3952;
|
||||
--eve-solar-system-status-warning: #906518a6;
|
||||
--eve-solar-system-status-target: #b439ff6b;
|
||||
--eve-solar-system-status-dangerous: #d54040;
|
||||
--eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
||||
--eve-solar-system-status-home: rgb(197, 253, 67);
|
||||
|
||||
--eve-solar-system-status-color-unknown: transparent;
|
||||
--eve-solar-system-status-color-friendly: #3bbd39;
|
||||
--eve-solar-system-status-color-warning: #ffb93b;
|
||||
--eve-solar-system-status-color-target: #b439ff;
|
||||
--eve-solar-system-status-color-dangerous: #d54040;
|
||||
--eve-solar-system-status-color-lookingFor: #43c2fd;
|
||||
--eve-solar-system-status-color-home: rgb(197, 253, 67);
|
||||
|
||||
--eve-solar-system-status-color-friendly-dark20: #2d9b2e;
|
||||
--eve-solar-system-status-friendly-dark30: #28892a;
|
||||
--eve-solar-system-status-color-friendly-dark5: #38b538;
|
||||
--eve-solar-system-status-color-lookingFor-dark15: #32aadf;
|
||||
|
||||
/* Context Menu */
|
||||
--conn-time-eol: #7452c3e3;
|
||||
--conn-frigate: #325d88;
|
||||
--conn-save: rgba(155, 102, 45, 0.85);
|
||||
--selected-item-bg: rgba(98, 98, 98, 0.33);
|
||||
}
|
||||
|
||||
@@ -1,535 +1,448 @@
|
||||
@import "eve-common-variables";
|
||||
@import './eve-common-variables';
|
||||
|
||||
|
||||
.eve-wh-effect-color-pulsar {
|
||||
fill: $eve-effect-pulsar;
|
||||
background-color: $eve-effect-pulsar;
|
||||
fill: var(--eve-effect-pulsar);
|
||||
background-color: var(--eve-effect-pulsar);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-magnetar {
|
||||
fill: $eve-effect-magnetar;
|
||||
background-color: $eve-effect-magnetar;
|
||||
fill: var(--eve-effect-magnetar);
|
||||
background-color: var(--eve-effect-magnetar);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-wolfRayet {
|
||||
fill: $eve-effect-wolfRayet;
|
||||
background-color: $eve-effect-wolfRayet;
|
||||
fill: var(--eve-effect-wolfRayet);
|
||||
background-color: var(--eve-effect-wolfRayet);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-blackHole {
|
||||
fill: $eve-effect-blackHole;
|
||||
background-color: $eve-effect-blackHole;
|
||||
box-shadow: 0 0 8px rgba(255 255 255 / 33);
|
||||
fill: var(--eve-effect-blackHole);
|
||||
background-color: var(--eve-effect-blackHole);
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.33);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-cataclysmicVariable {
|
||||
fill: $eve-effect-cataclysmicVariable;
|
||||
background-color: $eve-effect-cataclysmicVariable;
|
||||
fill: var(--eve-effect-cataclysmicVariable);
|
||||
background-color: var(--eve-effect-cataclysmicVariable);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-redGiant {
|
||||
fill: $eve-effect-redGiant;
|
||||
background-color: $eve-effect-redGiant;
|
||||
fill: var(--eve-effect-redGiant);
|
||||
background-color: var(--eve-effect-redGiant);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-pulsar {
|
||||
color: $eve-effect-pulsar;
|
||||
color: var(--eve-effect-pulsar);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-magnetar {
|
||||
color: $eve-effect-magnetar;
|
||||
color: var(--eve-effect-magnetar);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-wolfRayet {
|
||||
color: $eve-effect-wolfRayet;
|
||||
color: var(--eve-effect-wolfRayet);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-blackHole {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-cataclysmicVariable {
|
||||
color: $eve-effect-cataclysmicVariable;
|
||||
color: var(--eve-effect-cataclysmicVariable);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-redGiant {
|
||||
color: $eve-effect-redGiant;
|
||||
color: var(--eve-effect-redGiant);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-dazhLiminalityLocus {
|
||||
color: $eve-effect-dazhLiminalityLocus;
|
||||
color: var(--eve-effect-dazhLiminalityLocus);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-imperialStellarObservatory {
|
||||
color: $eve-effect-imperialStellarObservatory;
|
||||
color: var(--eve-effect-imperialStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-stateStellarObservatory {
|
||||
color: $eve-effect-stateStellarObservatory;
|
||||
color: var(--eve-effect-stateStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-republicStellarObservatory {
|
||||
color: $eve-effect-republicStellarObservatory;
|
||||
color: var(--eve-effect-republicStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-federalStellarObservatory {
|
||||
color: $eve-effect-federalStellarObservatory;
|
||||
color: var(--eve-effect-federalStellarObservatory);
|
||||
}
|
||||
|
||||
/* Security color classes */
|
||||
.eve-security-color-10 {
|
||||
color: $eve-security-color-10 !important;
|
||||
fill: $eve-security-color-10;
|
||||
color: var(--eve-security-color-10) !important;
|
||||
fill: var(--eve-security-color-10);
|
||||
}
|
||||
|
||||
.eve-security-color-09 {
|
||||
color: $eve-security-color-09 !important;
|
||||
fill: $eve-security-color-09;
|
||||
color: var(--eve-security-color-09) !important;
|
||||
fill: var(--eve-security-color-09);
|
||||
}
|
||||
|
||||
.eve-security-color-08 {
|
||||
color: $eve-security-color-08 !important;
|
||||
fill: $eve-security-color-08;
|
||||
color: var(--eve-security-color-08) !important;
|
||||
fill: var(--eve-security-color-08);
|
||||
}
|
||||
|
||||
.eve-security-color-07 {
|
||||
color: $eve-security-color-07 !important;
|
||||
fill: $eve-security-color-07;
|
||||
color: var(--eve-security-color-07) !important;
|
||||
fill: var(--eve-security-color-07);
|
||||
}
|
||||
|
||||
.eve-security-color-06 {
|
||||
color: $eve-security-color-06 !important;
|
||||
fill: $eve-security-color-06;
|
||||
color: var(--eve-security-color-06) !important;
|
||||
fill: var(--eve-security-color-06);
|
||||
}
|
||||
|
||||
.eve-security-color-05 {
|
||||
color: $eve-security-color-05 !important;
|
||||
fill: $eve-security-color-05;
|
||||
color: var(--eve-security-color-05) !important;
|
||||
fill: var(--eve-security-color-05);
|
||||
}
|
||||
|
||||
.eve-security-color-04 {
|
||||
color: $eve-security-color-04 !important;
|
||||
fill: $eve-security-color-04;
|
||||
color: var(--eve-security-color-04) !important;
|
||||
fill: var(--eve-security-color-04);
|
||||
}
|
||||
|
||||
.eve-security-color-03 {
|
||||
color: $eve-security-color-03 !important;
|
||||
fill: $eve-security-color-03;
|
||||
color: var(--eve-security-color-03) !important;
|
||||
fill: var(--eve-security-color-03);
|
||||
}
|
||||
|
||||
.eve-security-color-02 {
|
||||
color: $eve-security-color-02 !important;
|
||||
fill: $eve-security-color-02;
|
||||
color: var(--eve-security-color-02) !important;
|
||||
fill: var(--eve-security-color-02);
|
||||
}
|
||||
|
||||
.eve-security-color-01 {
|
||||
color: $eve-security-color-01 !important;
|
||||
fill: $eve-security-color-01;
|
||||
color: var(--eve-security-color-01) !important;
|
||||
fill: var(--eve-security-color-01);
|
||||
}
|
||||
|
||||
.eve-security-color-00 {
|
||||
color: $eve-security-color-00 !important;
|
||||
fill: $eve-security-color-00;
|
||||
color: var(--eve-security-color-00) !important;
|
||||
fill: var(--eve-security-color-00);
|
||||
}
|
||||
|
||||
.eve-security-color-m-01 {
|
||||
color: $eve-security-color-m-01 !important;
|
||||
fill: $eve-security-color-m-01;
|
||||
color: var(--eve-security-color-m-01) !important;
|
||||
fill: var(--eve-security-color-m-01);
|
||||
}
|
||||
|
||||
.eve-security-color-m-02 {
|
||||
color: $eve-security-color-m-02 !important;
|
||||
fill: $eve-security-color-m-02;
|
||||
color: var(--eve-security-color-m-02) !important;
|
||||
fill: var(--eve-security-color-m-02);
|
||||
}
|
||||
|
||||
.eve-security-color-m-03 {
|
||||
color: $eve-security-color-m-03 !important;
|
||||
fill: $eve-security-color-m-03;
|
||||
color: var(--eve-security-color-m-03) !important;
|
||||
fill: var(--eve-security-color-m-03);
|
||||
}
|
||||
|
||||
.eve-security-color-m-04 {
|
||||
color: $eve-security-color-m-04 !important;
|
||||
fill: $eve-security-color-m-04;
|
||||
color: var(--eve-security-color-m-04) !important;
|
||||
fill: var(--eve-security-color-m-04);
|
||||
}
|
||||
|
||||
.eve-security-color-m-05 {
|
||||
color: $eve-security-color-m-05 !important;
|
||||
fill: $eve-security-color-m-05;
|
||||
color: var(--eve-security-color-m-05) !important;
|
||||
fill: var(--eve-security-color-m-05);
|
||||
}
|
||||
|
||||
.eve-security-color-m-06 {
|
||||
color: $eve-security-color-m-06 !important;
|
||||
fill: $eve-security-color-m-06;
|
||||
color: var(--eve-security-color-m-06) !important;
|
||||
fill: var(--eve-security-color-m-06);
|
||||
}
|
||||
|
||||
.eve-security-color-m-07 {
|
||||
color: $eve-security-color-m-07 !important;
|
||||
fill: $eve-security-color-m-07;
|
||||
color: var(--eve-security-color-m-07) !important;
|
||||
fill: var(--eve-security-color-m-07);
|
||||
}
|
||||
|
||||
.eve-security-color-m-08 {
|
||||
color: $eve-security-color-m-08 !important;
|
||||
fill: $eve-security-color-m-08;
|
||||
color: var(--eve-security-color-m-08) !important;
|
||||
fill: var(--eve-security-color-m-08);
|
||||
}
|
||||
|
||||
.eve-security-color-m-09 {
|
||||
color: $eve-security-color-m-09 !important;
|
||||
fill: $eve-security-color-m-09;
|
||||
color: var(--eve-security-color-m-09) !important;
|
||||
fill: var(--eve-security-color-m-09);
|
||||
}
|
||||
|
||||
.eve-security-color-m-10 {
|
||||
color: $eve-security-color-m-10 !important;
|
||||
fill: $eve-security-color-m-10;
|
||||
color: var(--eve-security-color-m-10) !important;
|
||||
fill: var(--eve-security-color-m-10);
|
||||
}
|
||||
|
||||
/* Security backgrounds */
|
||||
.eve-security-background-10 {
|
||||
background-color: $eve-security-color-10;
|
||||
fill: $eve-security-color-10;
|
||||
background-color: var(--eve-security-color-10);
|
||||
fill: var(--eve-security-color-10);
|
||||
}
|
||||
|
||||
.eve-security-background-09 {
|
||||
background-color: $eve-security-color-09;
|
||||
fill: $eve-security-color-09;
|
||||
background-color: var(--eve-security-color-09);
|
||||
fill: var(--eve-security-color-09);
|
||||
}
|
||||
|
||||
.eve-security-background-08 {
|
||||
background-color: $eve-security-color-08;
|
||||
fill: $eve-security-color-08;
|
||||
background-color: var(--eve-security-color-08);
|
||||
fill: var(--eve-security-color-08);
|
||||
}
|
||||
|
||||
.eve-security-background-07 {
|
||||
background-color: $eve-security-color-07;
|
||||
fill: $eve-security-color-07;
|
||||
background-color: var(--eve-security-color-07);
|
||||
fill: var(--eve-security-color-07);
|
||||
}
|
||||
|
||||
.eve-security-background-06 {
|
||||
background-color: $eve-security-color-06;
|
||||
fill: $eve-security-color-06;
|
||||
background-color: var(--eve-security-color-06);
|
||||
fill: var(--eve-security-color-06);
|
||||
}
|
||||
|
||||
.eve-security-background-05 {
|
||||
background-color: $eve-security-color-05;
|
||||
fill: $eve-security-color-05;
|
||||
background-color: var(--eve-security-color-05);
|
||||
fill: var(--eve-security-color-05);
|
||||
}
|
||||
|
||||
.eve-security-background-04 {
|
||||
background-color: $eve-security-color-04;
|
||||
fill: $eve-security-color-04;
|
||||
background-color: var(--eve-security-color-04);
|
||||
fill: var(--eve-security-color-04);
|
||||
}
|
||||
|
||||
.eve-security-background-03 {
|
||||
background-color: $eve-security-color-03;
|
||||
fill: $eve-security-color-03;
|
||||
background-color: var(--eve-security-color-03);
|
||||
fill: var(--eve-security-color-03);
|
||||
}
|
||||
|
||||
.eve-security-background-02 {
|
||||
background-color: $eve-security-color-02;
|
||||
fill: $eve-security-color-02;
|
||||
background-color: var(--eve-security-color-02);
|
||||
fill: var(--eve-security-color-02);
|
||||
}
|
||||
|
||||
.eve-security-background-01 {
|
||||
background-color: $eve-security-color-01;
|
||||
fill: $eve-security-color-01;
|
||||
background-color: var(--eve-security-color-01);
|
||||
fill: var(--eve-security-color-01);
|
||||
}
|
||||
|
||||
.eve-security-background-00 {
|
||||
background-color: $eve-security-color-00;
|
||||
fill: $eve-security-color-00;
|
||||
background-color: var(--eve-security-color-00);
|
||||
fill: var(--eve-security-color-00);
|
||||
}
|
||||
|
||||
.eve-security-background-m-01 {
|
||||
background-color: $eve-security-color-m-01;
|
||||
fill: $eve-security-color-m-01;
|
||||
background-color: var(--eve-security-color-m-01);
|
||||
fill: var(--eve-security-color-m-01);
|
||||
}
|
||||
|
||||
.eve-security-background-m-02 {
|
||||
background-color: $eve-security-color-m-02;
|
||||
fill: $eve-security-color-m-02;
|
||||
background-color: var(--eve-security-color-m-02);
|
||||
fill: var(--eve-security-color-m-02);
|
||||
}
|
||||
|
||||
.eve-security-background-m-03 {
|
||||
background-color: $eve-security-color-m-03;
|
||||
fill: $eve-security-color-m-03;
|
||||
background-color: var(--eve-security-color-m-03);
|
||||
fill: var(--eve-security-color-m-03);
|
||||
}
|
||||
|
||||
.eve-security-background-m-04 {
|
||||
background-color: $eve-security-color-m-04;
|
||||
fill: $eve-security-color-m-04;
|
||||
background-color: var(--eve-security-color-m-04);
|
||||
fill: var(--eve-security-color-m-04);
|
||||
}
|
||||
|
||||
.eve-security-background-m-05 {
|
||||
background-color: $eve-security-color-m-05;
|
||||
fill: $eve-security-color-m-05;
|
||||
background-color: var(--eve-security-color-m-05);
|
||||
fill: var(--eve-security-color-m-05);
|
||||
}
|
||||
|
||||
.eve-security-background-m-06 {
|
||||
background-color: $eve-security-color-m-06;
|
||||
fill: $eve-security-color-m-06;
|
||||
background-color: var(--eve-security-color-m-06);
|
||||
fill: var(--eve-security-color-m-06);
|
||||
}
|
||||
|
||||
.eve-security-background-m-07 {
|
||||
background-color: $eve-security-color-m-07;
|
||||
fill: $eve-security-color-m-07;
|
||||
background-color: var(--eve-security-color-m-07);
|
||||
fill: var(--eve-security-color-m-07);
|
||||
}
|
||||
|
||||
.eve-security-background-m-08 {
|
||||
background-color: $eve-security-color-m-08;
|
||||
fill: $eve-security-color-m-08;
|
||||
background-color: var(--eve-security-color-m-08);
|
||||
fill: var(--eve-security-color-m-08);
|
||||
}
|
||||
|
||||
.eve-security-background-m-09 {
|
||||
background-color: $eve-security-color-m-09;
|
||||
fill: $eve-security-color-m-09;
|
||||
background-color: var(--eve-security-color-m-09);
|
||||
fill: var(--eve-security-color-m-09);
|
||||
}
|
||||
|
||||
.eve-security-background-m-10 {
|
||||
background-color: $eve-security-color-m-10;
|
||||
fill: $eve-security-color-m-10;
|
||||
background-color: var(--eve-security-color-m-10);
|
||||
fill: var(--eve-security-color-m-10);
|
||||
}
|
||||
|
||||
/* WH Type color classes */
|
||||
.eve-wh-type-color-high {
|
||||
color: $eve-wh-type-color-high;
|
||||
fill: $eve-wh-type-color-high;
|
||||
color: var(--eve-wh-type-color-high) !important;
|
||||
fill: var(--eve-wh-type-color-high);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-low {
|
||||
color: $eve-wh-type-color-low;
|
||||
fill: $eve-wh-type-color-low;
|
||||
color: var(--eve-wh-type-color-low) !important;
|
||||
fill: var(--eve-wh-type-color-low);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-null {
|
||||
color: $eve-wh-type-color-null;
|
||||
fill: $eve-wh-type-color-null;
|
||||
color: var(--eve-wh-type-color-null) !important;
|
||||
fill: var(--eve-wh-type-color-null);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c1 {
|
||||
color: $eve-wh-type-color-c1 !important;
|
||||
fill: $eve-wh-type-color-c1;
|
||||
color: var(--eve-wh-type-color-c1) !important;
|
||||
fill: var(--eve-wh-type-color-c1);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c2 {
|
||||
color: $eve-wh-type-color-c2 !important;
|
||||
fill: $eve-wh-type-color-c2;
|
||||
color: var(--eve-wh-type-color-c2) !important;
|
||||
fill: var(--eve-wh-type-color-c2);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c3 {
|
||||
color: $eve-wh-type-color-c3 !important;
|
||||
fill: $eve-wh-type-color-c3;
|
||||
color: var(--eve-wh-type-color-c3) !important;
|
||||
fill: var(--eve-wh-type-color-c3);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c4 {
|
||||
color: $eve-wh-type-color-c4 !important;
|
||||
fill: $eve-wh-type-color-c4;
|
||||
color: var(--eve-wh-type-color-c4) !important;
|
||||
fill: var(--eve-wh-type-color-c4);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c5 {
|
||||
color: $eve-wh-type-color-c5 !important;
|
||||
fill: $eve-wh-type-color-c5;
|
||||
color: var(--eve-wh-type-color-c5) !important;
|
||||
fill: var(--eve-wh-type-color-c5);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c6 {
|
||||
color: $eve-wh-type-color-c6 !important;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6) !important;
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c13 {
|
||||
color: $eve-wh-type-color-c13 !important;
|
||||
fill: $eve-wh-type-color-c13;
|
||||
color: var(--eve-wh-type-color-c13) !important;
|
||||
fill: var(--eve-wh-type-color-c13);
|
||||
}
|
||||
|
||||
.eve-wh-type-color-drifter {
|
||||
color: $eve-wh-type-color-drifter !important;
|
||||
fill: $eve-wh-type-color-drifter;
|
||||
color: var(--eve-wh-type-color-drifter) !important;
|
||||
fill: var(--eve-wh-type-color-drifter);
|
||||
}
|
||||
|
||||
.eve-wh-type-color-thera {
|
||||
color: $eve-wh-type-color-thera !important;
|
||||
fill: $eve-wh-type-color-thera;
|
||||
color: var(--eve-wh-type-color-thera) !important;
|
||||
fill: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
/* WH Type backgrounds */
|
||||
.eve-wh-type-background-high {
|
||||
background-color: $eve-wh-type-color-high;
|
||||
background-color: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-low {
|
||||
background-color: $eve-wh-type-color-low;
|
||||
background-color: var(--eve-wh-type-color-low);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-null {
|
||||
background-color: $eve-wh-type-color-null;
|
||||
background-color: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c1 {
|
||||
background-color: $eve-wh-type-color-c1;
|
||||
background-color: var(--eve-wh-type-color-c1);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c2 {
|
||||
background-color: $eve-wh-type-color-c2;
|
||||
background-color: var(--eve-wh-type-color-c2);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c3 {
|
||||
background-color: $eve-wh-type-color-c3;
|
||||
background-color: var(--eve-wh-type-color-c3);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c4 {
|
||||
background-color: $eve-wh-type-color-c4;
|
||||
background-color: var(--eve-wh-type-color-c4);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c5 {
|
||||
background-color: $eve-wh-type-color-c5;
|
||||
background-color: var(--eve-wh-type-color-c5);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c6 {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c13 {
|
||||
background-color: $eve-wh-type-color-c13;
|
||||
background-color: var(--eve-wh-type-color-c13);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-drifter {
|
||||
background-color: $eve-wh-type-color-drifter;
|
||||
background-color: var(--eve-wh-type-color-drifter);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-thera {
|
||||
background-color: $eve-wh-type-color-thera;
|
||||
background-color: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-zarzakh {
|
||||
background-color: $eve-wh-type-color-zarzakh;
|
||||
background-color: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* Kind color classes */
|
||||
.eve-kind-color-high {
|
||||
color: $eve-wh-type-color-high;
|
||||
fill: $eve-wh-type-color-high;
|
||||
color: var(--eve-wh-type-color-high);
|
||||
fill: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-kind-color-low {
|
||||
color: $eve-wh-type-color-low;
|
||||
fill: $eve-wh-type-color-low;
|
||||
color: var(--eve-wh-type-color-low);
|
||||
fill: var(--eve-wh-type-color-low);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.eve-kind-color-null {
|
||||
color: $eve-wh-type-color-null;
|
||||
fill: $eve-wh-type-color-null;
|
||||
color: var(--eve-wh-type-color-null);
|
||||
fill: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-kind-color-wh {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-thera {
|
||||
color: $eve-wh-type-color-thera;
|
||||
fill: $eve-wh-type-color-thera;
|
||||
color: var(--eve-wh-type-color-thera);
|
||||
fill: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-kind-color-abyss {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-penalty {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-pochven {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-zarzakh {
|
||||
color: $eve-wh-type-color-zarzakh;
|
||||
fill: $eve-wh-type-color-zarzakh;
|
||||
color: var(--eve-wh-type-color-zarzakh);
|
||||
fill: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* Kind backgrounds */
|
||||
.eve-kind-background-high {
|
||||
background-color: $eve-wh-type-color-high;
|
||||
background-color: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-kind-background-low {
|
||||
background-color: $eve-wh-type-color-low;
|
||||
background-color: var(--eve-wh-type-color-low);
|
||||
}
|
||||
|
||||
.eve-kind-background-null {
|
||||
background-color: $eve-wh-type-color-null;
|
||||
background-color: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-kind-background-wh {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-thera {
|
||||
background-color: $eve-wh-type-color-thera;
|
||||
background-color: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-kind-background-abyss {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-penalty {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-pochven {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-zarzakh {
|
||||
background-color: $eve-wh-type-color-zarzakh;
|
||||
background-color: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* System status color classes */
|
||||
.eve-system-status-color-clear {
|
||||
color: $eve-solar-system-status-color-unknown;
|
||||
color: var(--eve-solar-system-status-color-unknown);
|
||||
}
|
||||
|
||||
.eve-system-status-color-home {
|
||||
color: $eve-solar-system-status-color-home;
|
||||
color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
|
||||
.eve-system-status-color-friendly {
|
||||
color: $eve-solar-system-status-color-friendly;
|
||||
color: var(--eve-solar-system-status-color-friendly);
|
||||
}
|
||||
|
||||
.eve-system-status-color-lookingFor {
|
||||
color: $eve-solar-system-status-color-lookingFor;
|
||||
color: var(--eve-solar-system-status-color-lookingFor);
|
||||
}
|
||||
|
||||
.eve-system-status-color-warning {
|
||||
color: $eve-solar-system-status-color-warning;
|
||||
color: var(--eve-solar-system-status-color-warning);
|
||||
}
|
||||
|
||||
.eve-system-status-color-target {
|
||||
color: $eve-solar-system-status-color-target;
|
||||
color: var(--eve-solar-system-status-color-target);
|
||||
}
|
||||
|
||||
.eve-system-status-color-dangerous {
|
||||
color: $eve-solar-system-status-color-dangerous;
|
||||
color: var(--eve-solar-system-status-color-dangerous);
|
||||
}
|
||||
|
||||
/* Shapes */
|
||||
.wd-route-system-shape-triangle {
|
||||
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||
}
|
||||
|
||||
.wd-route-system-shape-circle {
|
||||
border-radius: 40%;
|
||||
}
|
||||
|
||||
/* Some additional background classes */
|
||||
.wd-marker-bookmark-color-shattered {
|
||||
background-color: #833ca4;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
|
||||
.wd-marker-bookmark-color-custom {
|
||||
background-color: #282828;
|
||||
border: 1px solid #4c4c4c;
|
||||
|
||||
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import './neon-theme.scss';
|
||||
@import './pathfinder-theme.scss';
|
||||
@@ -1,74 +1,61 @@
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020;
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
|
||||
.react-flow {
|
||||
// background-color: $dark-bg;
|
||||
color: $text-color;
|
||||
.neon-theme {
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
&__node {
|
||||
//cursor: auto;
|
||||
--rf-bg-variant: "dots";
|
||||
--rf-bg-gap: 16;
|
||||
|
||||
.react-flow {
|
||||
color: var(--text-color);
|
||||
|
||||
&__pane {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
&__minimap {
|
||||
background-color: rgba(66, 66, 66, 1);
|
||||
opacity: 0.7;
|
||||
border: 1px solid #2f2f2f;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__minimap-mask {
|
||||
fill: rgba(28, 28, 28, 0.75);
|
||||
}
|
||||
|
||||
&__controls {
|
||||
filter: brightness(1.5);
|
||||
}
|
||||
|
||||
&__minimap-node {
|
||||
fill: #ffb03a;
|
||||
}
|
||||
}
|
||||
|
||||
&__pane {
|
||||
cursor: auto;
|
||||
}
|
||||
//&__edge {
|
||||
// stroke: $pastel-pink;
|
||||
// stroke-width: 2px;
|
||||
//
|
||||
// &.selected {
|
||||
// stroke: $pastel-yellow;
|
||||
// }
|
||||
//}
|
||||
|
||||
&__handle {
|
||||
//background-color: $pastel-green;
|
||||
//box-shadow: 0 0 5px rgba($pastel-green, 0.5);
|
||||
.context-menu-active {
|
||||
background-color: rgba(131, 131, 131, 0.33);
|
||||
}
|
||||
|
||||
&__minimap {
|
||||
background-color: rgba(66, 66, 66, 1);
|
||||
opacity: 0.7;
|
||||
//backdrop-filter: blur(5px);
|
||||
border: 1px solid #2f2f2f;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__minimap-mask {
|
||||
fill: rgba(28, 28, 28, 0.75);
|
||||
}
|
||||
|
||||
&__controls {
|
||||
filter: brightness(1.5);
|
||||
}
|
||||
|
||||
&__minimap-node {
|
||||
fill: #ffb03a;
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-active {
|
||||
background-color: rgba(131, 131, 131, 0.33);
|
||||
}
|
||||
|
||||
.p-dialog {
|
||||
.p-dialog-header {
|
||||
height: 40px;
|
||||
padding: 1rem;
|
||||
padding-right: 10px !important;
|
||||
}
|
||||
|
||||
.p-dialog-title {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
.p-dialog-header-icons {
|
||||
align-self: initial !important;
|
||||
.p-dialog {
|
||||
.p-dialog-header {
|
||||
height: 40px;
|
||||
padding: 1rem;
|
||||
padding-right: 10px !important;
|
||||
}
|
||||
.p-dialog-title {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
.p-dialog-header-icons {
|
||||
align-self: initial !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020;
|
||||
@@ -0,0 +1,188 @@
|
||||
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300;400;700&display=swap');
|
||||
|
||||
.pathfinder-theme {
|
||||
|
||||
--rf-bg-variant: "lines";
|
||||
--rf-bg-gap: 32;
|
||||
--rf-bg-color: #353535;
|
||||
--rf-soft-bg-color: #282829;
|
||||
|
||||
--pf-text-color: #adadad;
|
||||
--pf-dark-bg: #2d2d2d;
|
||||
--pf-tooltip-bg: #202020;
|
||||
--rf-bg-variant: "lines";
|
||||
--rf-bg-gap: 32;
|
||||
--rf-bg-size: 1;
|
||||
--rf-bg-color: #313131;
|
||||
--eve-effect-pulsar: #428bca;
|
||||
--eve-effect-magnetar: #e06fdf;
|
||||
--eve-effect-wolfRayet: #e28a0d;
|
||||
--eve-effect-blackHole: #000000;
|
||||
--eve-effect-cataclysmicVariable: #ffffbb;
|
||||
--eve-effect-redGiant: #d9534f;
|
||||
|
||||
--eve-wh-type-color-high: #5cb85c;
|
||||
--eve-wh-type-color-low: #e28a0d;
|
||||
--eve-wh-type-color-null: #d9534f;
|
||||
--eve-wh-type-color-c1: #428bca;
|
||||
--eve-wh-type-color-c2: #428bca;
|
||||
--eve-wh-type-color-c3: #e28a0d;
|
||||
--eve-wh-type-color-c4: #e28a0d;
|
||||
--eve-wh-type-color-c5: #d9534f;
|
||||
--eve-wh-type-color-c6: #d9534f;
|
||||
--eve-wh-type-color-c13: #7986cb;
|
||||
--eve-wh-type-color-drifter: #44aa82;
|
||||
|
||||
.MapRoot {
|
||||
background-color: var(--rf-bg-color);
|
||||
|
||||
&.isSoftBackground {
|
||||
background-color: var(--rf-soft-bg-color);
|
||||
}
|
||||
&.soft-bg {
|
||||
--rf-bg-color: var(--rf-soft-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.RootCustomNode {
|
||||
background-color: #313335 !important;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
color: var(--pf-text-color);
|
||||
font-family: 'Oxygen', sans-serif !important;
|
||||
}
|
||||
|
||||
.HeadRow {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
.classTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 1px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
}
|
||||
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
color: var(--pf-text-color);
|
||||
mix-blend-mode: screen;
|
||||
text-shadow: rgba(0, 0, 0, 0.4) 1px 1px;
|
||||
}
|
||||
|
||||
.textEllipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.fontSans {
|
||||
font-family: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
.BottomRow {
|
||||
.localCounter {
|
||||
display: flex;
|
||||
|
||||
& > i {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
& > span {
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
font-weight: 700;
|
||||
mix-blend-mode: screen;
|
||||
color: #5cb85c;
|
||||
}
|
||||
}
|
||||
|
||||
.customName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
color: var(--pf-text-color);
|
||||
mix-blend-mode: screen;
|
||||
text-shadow: rgba(0, 0, 0, 0.4) 1px 1px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.tagTitle {
|
||||
font-size: 9px;
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
color: #fbbf24 !important;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
}
|
||||
|
||||
.Handlers {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid var(--pastel-blue, #5a7d9a);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: var(--pastel-pink, #d291bc);
|
||||
}
|
||||
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
&.Tick {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
|
||||
&.HandleTop {
|
||||
top: -3px;
|
||||
}
|
||||
&.HandleRight {
|
||||
right: -3px;
|
||||
}
|
||||
&.HandleBottom {
|
||||
bottom: -3px;
|
||||
}
|
||||
&.HandleLeft {
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow {
|
||||
color: var(--pf-text-color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.SearchItem {
|
||||
& > * {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.SearchItemEffect {
|
||||
font-weight: initial !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
import { AutoComplete } from 'primereact/autocomplete';
|
||||
import { OutCommand, SearchSystemItem } from '@/hooks/Mapper/types';
|
||||
import { SystemViewStandalone, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import classes from './AddSystemDialog.module.scss';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
|
||||
export type SearchOnSubmitCallback = (item: SearchSystemItem) => void;
|
||||
|
||||
interface AddSystemDialogProps {
|
||||
title?: string;
|
||||
visible: boolean;
|
||||
setVisible: (visible: boolean) => void;
|
||||
onSubmit?: SearchOnSubmitCallback;
|
||||
excludedSystems?: number[];
|
||||
}
|
||||
|
||||
export const AddSystemDialog = ({
|
||||
title = 'Add system',
|
||||
visible,
|
||||
setVisible,
|
||||
onSubmit,
|
||||
excludedSystems = [],
|
||||
}: AddSystemDialogProps) => {
|
||||
const {
|
||||
outCommand,
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const inputRef = useRef<any>();
|
||||
const onShow = useCallback(() => {
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const [filteredItems, setFilteredItems] = useState<SearchSystemItem[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<SearchSystemItem[] | null>(null);
|
||||
|
||||
const searchItems = useCallback(
|
||||
async (event: { query: string }) => {
|
||||
if (event.query.length < 2) {
|
||||
setFilteredItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const query = event.query;
|
||||
|
||||
if (query.length === 0) {
|
||||
setFilteredItems([]);
|
||||
} else {
|
||||
try {
|
||||
const result = await outCommand({
|
||||
type: OutCommand.searchSystems,
|
||||
data: {
|
||||
text: query,
|
||||
},
|
||||
});
|
||||
|
||||
let prepared = (result.systems as SearchSystemItem[]).sort((a, b) => {
|
||||
const amatch = a.label.indexOf(query);
|
||||
const bmatch = b.label.indexOf(query);
|
||||
return amatch - bmatch;
|
||||
});
|
||||
|
||||
if (excludedSystems) {
|
||||
prepared = prepared.filter(x => !excludedSystems.includes(x.system_static_info.solar_system_id));
|
||||
}
|
||||
|
||||
setFilteredItems(prepared);
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
setFilteredItems([]);
|
||||
}
|
||||
}
|
||||
},
|
||||
[excludedSystems, outCommand],
|
||||
);
|
||||
|
||||
const ref = useRef({ onSubmit, selectedItem });
|
||||
ref.current = { onSubmit, selectedItem };
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const { onSubmit, selectedItem } = ref.current;
|
||||
setFilteredItems([]);
|
||||
setSelectedItem([]);
|
||||
|
||||
if (!selectedItem) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmit?.(selectedItem[0]);
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header={title}
|
||||
visible={visible}
|
||||
draggable={false}
|
||||
style={{ width: '520px' }}
|
||||
onShow={onShow}
|
||||
onHide={() => {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3 px-1.5">
|
||||
<div className="flex flex-col gap-2 py-3.5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<IconField>
|
||||
<AutoComplete
|
||||
ref={inputRef}
|
||||
multiple
|
||||
showEmptyMessage
|
||||
scrollHeight="300px"
|
||||
value={selectedItem}
|
||||
suggestions={filteredItems}
|
||||
completeMethod={searchItems}
|
||||
onChange={e => {
|
||||
setSelectedItem(e.value.length < 2 ? e.value : [e.value[e.value.length - 1]]);
|
||||
}}
|
||||
emptyMessage="Not found any system..."
|
||||
placeholder="Type here..."
|
||||
field="label"
|
||||
id="value"
|
||||
className="w-full"
|
||||
itemTemplate={(item: SearchSystemItem) => {
|
||||
const { security, system_class, effect_power, effect_name, statics } = item.system_static_info;
|
||||
const sortedStatics = sortWHClasses(wormholesData, statics);
|
||||
const isWH = isWormholeSpace(system_class);
|
||||
|
||||
return (
|
||||
<div className={clsx('flex gap-1.5', classes.SearchItem)}>
|
||||
<SystemViewStandalone
|
||||
security={security}
|
||||
system_class={system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
|
||||
{effect_name && isWH && (
|
||||
<WHEffectView
|
||||
effectName={effect_name}
|
||||
effectPower={effect_power}
|
||||
className={classes.SearchItemEffect}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isWH && (
|
||||
<div className="flex gap-1 grow justify-between">
|
||||
<div></div>
|
||||
<div className="flex gap-1">
|
||||
{sortedStatics.map(x => (
|
||||
<WHClassView key={x} whClassName={x} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
selectedItemTemplate={(item: SearchSystemItem) => (
|
||||
<SystemViewStandalone
|
||||
security={item.system_static_info.security}
|
||||
system_class={item.system_static_info.system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</IconField>
|
||||
|
||||
<span className="text-[12px] text-stone-400 ml-1">*to search type at least 2 symbols.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
outlined
|
||||
disabled={!selectedItem || selectedItem.length !== 1}
|
||||
size="small"
|
||||
label="Submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './AddSystemDialog';
|
||||
@@ -3,6 +3,7 @@ import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
@@ -22,30 +23,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
|
||||
const system = getSystemById(systems, systemId);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [label, setLabel] = useState('');
|
||||
const [temporaryName, setTemporaryName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = new LabelsManager(system.labels || '');
|
||||
|
||||
setName(system.name || '');
|
||||
setLabel(labels.customLabel);
|
||||
setDescription(system.description || '');
|
||||
}, [system]);
|
||||
|
||||
const ref = useRef({ name, description, label, outCommand, systemId, system });
|
||||
ref.current = { name, description, label, outCommand, systemId, system };
|
||||
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system });
|
||||
ref.current = { name, description, label, temporaryName, outCommand, systemId, system };
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
const { name, description, label, outCommand, systemId, system } = ref.current;
|
||||
const { name, description, label, temporaryName, outCommand, systemId, system } = ref.current;
|
||||
|
||||
const outLabel = new LabelsManager(system?.labels ?? '');
|
||||
outLabel.updateCustomLabel(label);
|
||||
@@ -58,6 +50,14 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemTemporaryName,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
value: temporaryName,
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemName,
|
||||
data: {
|
||||
@@ -93,6 +93,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
|
||||
}, []);
|
||||
|
||||
// Attention: this effect should be call only on mount.
|
||||
useEffect(() => {
|
||||
const { system } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = new LabelsManager(system.labels || '');
|
||||
|
||||
setName(system.name || '');
|
||||
setLabel(labels.customLabel);
|
||||
setTemporaryName(system.temporary_name || '');
|
||||
setDescription(system.description || '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="System settings"
|
||||
@@ -167,6 +182,35 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
</IconField>
|
||||
</div>
|
||||
|
||||
{isTempSystemNameEnabled && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor="username">Temporary Name</label>
|
||||
|
||||
<IconField>
|
||||
{temporaryName !== '' && (
|
||||
<WdImgButton
|
||||
className="pi pi-trash text-red-400"
|
||||
textSize={WdImageSize.large}
|
||||
tooltip={{
|
||||
content: 'Remove temporary name',
|
||||
className: 'pi p-input-icon',
|
||||
position: TooltipPosition.top,
|
||||
}}
|
||||
onClick={() => setTemporaryName('')}
|
||||
/>
|
||||
)}
|
||||
<InputText
|
||||
id="temporaryName"
|
||||
aria-describedby="temporaryName"
|
||||
autoComplete="off"
|
||||
value={temporaryName}
|
||||
maxLength={10}
|
||||
onChange={e => setTemporaryName(e.target.value)}
|
||||
/>
|
||||
</IconField>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor="username">Description</label>
|
||||
<InputTextarea
|
||||
|
||||
@@ -21,6 +21,11 @@ import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
|
||||
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
|
||||
const sortByDist = (a: Route, b: Route) => {
|
||||
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
|
||||
@@ -163,6 +168,12 @@ export const RoutesWidgetContent = () => {
|
||||
export const RoutesWidgetComp = () => {
|
||||
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
|
||||
const { data, update } = useRouteProvider();
|
||||
const {
|
||||
data: { hubs = [] },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const preparedHubs = useMemo(() => hubs.map(x => parseInt(x)), [hubs]);
|
||||
|
||||
const isSecure = data.path_type === 'secure';
|
||||
const handleSecureChange = useCallback(() => {
|
||||
@@ -174,6 +185,23 @@ export const RoutesWidgetComp = () => {
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 155);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<boolean>(false);
|
||||
|
||||
const onAddSystem = useCallback(() => setOpenAddSystem(true), []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (preparedHubs.includes(item.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.addHub,
|
||||
data: { system_id: item.value },
|
||||
});
|
||||
},
|
||||
[hubs, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
@@ -181,6 +209,14 @@ export const RoutesWidgetComp = () => {
|
||||
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
|
||||
<span className="select-none">Routes</span>
|
||||
<LayoutEventBlocker className="flex items-center gap-2">
|
||||
<WdImgButton
|
||||
className={PrimeIcons.PLUS_CIRCLE}
|
||||
onClick={onAddSystem}
|
||||
tooltip={{
|
||||
content: 'Click here to add new system to routes',
|
||||
}}
|
||||
/>
|
||||
|
||||
<WdTooltipWrapper content="Show shortest route">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
@@ -191,13 +227,26 @@ export const RoutesWidgetComp = () => {
|
||||
classNameLabel={clsx('text-red-400')}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
|
||||
<WdImgButton
|
||||
className={PrimeIcons.SLIDERS_H}
|
||||
onClick={() => setRouteSettingsVisible(true)}
|
||||
tooltip={{
|
||||
content: 'Click here to open Routes settings',
|
||||
}}
|
||||
/>
|
||||
</LayoutEventBlocker>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<RoutesWidgetContent />
|
||||
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
|
||||
|
||||
<AddSystemDialog
|
||||
title="Add system to routes"
|
||||
visible={openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(false)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,25 +2,26 @@ import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||
import {
|
||||
InfoDrawer,
|
||||
LayoutEventBlocker,
|
||||
SystemView,
|
||||
TooltipPosition,
|
||||
WdImgButton,
|
||||
WdCheckbox,
|
||||
WdImgButton,
|
||||
} from '@/hooks/Mapper/components/ui-kit';
|
||||
import { SystemSignaturesContent } from './SystemSignaturesContent';
|
||||
import {
|
||||
Setting,
|
||||
SystemSignatureSettingsDialog,
|
||||
COSMIC_SIGNATURE,
|
||||
COSMIC_ANOMALY,
|
||||
COSMIC_SIGNATURE,
|
||||
DEPLOYABLE,
|
||||
STRUCTURE,
|
||||
STARBASE,
|
||||
SHIP,
|
||||
DRONE,
|
||||
Setting,
|
||||
SHIP,
|
||||
STARBASE,
|
||||
STRUCTURE,
|
||||
SystemSignatureSettingsDialog,
|
||||
} from './SystemSignatureSettingsDialog';
|
||||
import { SignatureGroup } from '@/hooks/Mapper/types';
|
||||
|
||||
import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
@@ -105,7 +106,14 @@ export const SystemSignatures = () => {
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
|
||||
<div className="flex gap-1 whitespace-nowrap text-ellipsis overflow-hidden">System Signatures</div>
|
||||
<div className="flex justify-between items-center gap-1">
|
||||
{!compact && (
|
||||
<div className="flex whitespace-nowrap text-ellipsis overflow-hidden text-stone-400">
|
||||
Signatures {isNotSelectedSystem ? '' : 'in'}
|
||||
</div>
|
||||
)}
|
||||
{!isNotSelectedSystem && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
|
||||
</div>
|
||||
|
||||
<LayoutEventBlocker className="flex gap-2.5">
|
||||
<WdTooltipWrapper content="Enable Lazy delete">
|
||||
|
||||
@@ -2,7 +2,10 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { parseSignatures } from '@/hooks/Mapper/helpers';
|
||||
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { WdTooltip, WdTooltipHandlers } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { GROUPS_LIST } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
import {
|
||||
getGroupIdByRawGroup,
|
||||
GROUPS_LIST,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
|
||||
import { DataTable, DataTableRowClickEvent, DataTableRowMouseEvent, SortOrder } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
@@ -122,13 +125,14 @@ export const SystemSignaturesContent = ({
|
||||
}
|
||||
|
||||
const isCosmicSignature = x.kind === COSMIC_SIGNATURE;
|
||||
const preparedGroup = getGroupIdByRawGroup(x.group);
|
||||
|
||||
if (isCosmicSignature) {
|
||||
const showCosmicSignatures = settings.find(y => y.key === COSMIC_SIGNATURE)?.value;
|
||||
if (showCosmicSignatures) {
|
||||
return !x.group || groupSettings.find(y => y.key === x.group)?.value;
|
||||
return !x.group || groupSettings.find(y => y.key === preparedGroup)?.value;
|
||||
} else {
|
||||
return !!x.group && groupSettings.find(y => y.key === x.group)?.value;
|
||||
return !!x.group && groupSettings.find(y => y.key === preparedGroup)?.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { GroupType, SignatureGroup } from '@/hooks/Mapper/types';
|
||||
import {
|
||||
GroupType,
|
||||
SignatureGroup,
|
||||
SignatureGroupENG,
|
||||
SignatureGroupRU,
|
||||
SignatureKind,
|
||||
SignatureKindENG,
|
||||
SignatureKindRU,
|
||||
} from '@/hooks/Mapper/types';
|
||||
|
||||
export const TIME_ONE_MINUTE = 1000 * 60;
|
||||
export const TIME_TEN_MINUTES = 1000 * 60 * 10;
|
||||
@@ -24,3 +32,43 @@ export const GROUPS: Record<SignatureGroup, GroupType> = {
|
||||
[SignatureGroup.Wormhole]: { id: SignatureGroup.Wormhole, icon: '/icons/brackets/wormhole.png', ...wh },
|
||||
[SignatureGroup.CosmicSignature]: { id: SignatureGroup.CosmicSignature, icon: '/icons/x_close14.png', w: 9, h: 9 },
|
||||
};
|
||||
|
||||
export const MAPPING_GROUP_TO_ENG = {
|
||||
// ENGLISH
|
||||
[SignatureGroupENG.GasSite]: SignatureGroup.GasSite,
|
||||
[SignatureGroupENG.RelicSite]: SignatureGroup.RelicSite,
|
||||
[SignatureGroupENG.DataSite]: SignatureGroup.DataSite,
|
||||
[SignatureGroupENG.OreSite]: SignatureGroup.OreSite,
|
||||
[SignatureGroupENG.CombatSite]: SignatureGroup.CombatSite,
|
||||
[SignatureGroupENG.Wormhole]: SignatureGroup.Wormhole,
|
||||
[SignatureGroupENG.CosmicSignature]: SignatureGroup.CosmicSignature,
|
||||
|
||||
// RUSSIAN
|
||||
[SignatureGroupRU.GasSite]: SignatureGroup.GasSite,
|
||||
[SignatureGroupRU.RelicSite]: SignatureGroup.RelicSite,
|
||||
[SignatureGroupRU.DataSite]: SignatureGroup.DataSite,
|
||||
[SignatureGroupRU.OreSite]: SignatureGroup.OreSite,
|
||||
[SignatureGroupRU.CombatSite]: SignatureGroup.CombatSite,
|
||||
[SignatureGroupRU.Wormhole]: SignatureGroup.Wormhole,
|
||||
[SignatureGroupRU.CosmicSignature]: SignatureGroup.CosmicSignature,
|
||||
};
|
||||
|
||||
export const MAPPING_TYPE_TO_ENG = {
|
||||
// ENGLISH
|
||||
[SignatureKindENG.CosmicSignature]: SignatureKind.CosmicSignature,
|
||||
[SignatureKindENG.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
|
||||
[SignatureKindENG.Structure]: SignatureKind.Structure,
|
||||
[SignatureKindENG.Ship]: SignatureKind.Ship,
|
||||
[SignatureKindENG.Deployable]: SignatureKind.Deployable,
|
||||
[SignatureKindENG.Drone]: SignatureKind.Drone,
|
||||
|
||||
// RUSSIAN
|
||||
[SignatureKindRU.CosmicSignature]: SignatureKind.CosmicSignature,
|
||||
[SignatureKindRU.CosmicAnomaly]: SignatureKind.CosmicAnomaly,
|
||||
[SignatureKindRU.Structure]: SignatureKind.Structure,
|
||||
[SignatureKindRU.Ship]: SignatureKind.Ship,
|
||||
[SignatureKindRU.Deployable]: SignatureKind.Deployable,
|
||||
[SignatureKindRU.Drone]: SignatureKind.Drone,
|
||||
};
|
||||
|
||||
export const getGroupIdByRawGroup = (val: string) => MAPPING_GROUP_TO_ENG[val as SignatureGroup];
|
||||
|
||||
@@ -16,6 +16,8 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowMenu } = interfaceSettings;
|
||||
|
||||
const themeClass = `${interfaceSettings.theme ?? 'neon'}-theme`;
|
||||
|
||||
const [showOnTheMap, setShowOnTheMap] = useState(false);
|
||||
const [showMapSettings, setShowMapSettings] = useState(false);
|
||||
const mapInterface = <MapInterface />;
|
||||
@@ -26,27 +28,29 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
useSkipContextMenu();
|
||||
|
||||
return (
|
||||
<Layout map={<MapWrapper />}>
|
||||
{!isShowMenu ? (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
||||
<Topbar />
|
||||
<div className={themeClass}>
|
||||
<Layout map={<MapWrapper />}>
|
||||
{!isShowMenu ? (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
||||
<Topbar />
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,8 +3,13 @@ import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import { PrettySwitchbox } from './components';
|
||||
import { InterfaceStoredSettings, InterfaceStoredSettingsProps, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import {
|
||||
InterfaceStoredSettingsProps,
|
||||
useMapRootState,
|
||||
InterfaceStoredSettings,
|
||||
} from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
|
||||
export enum UserSettingsRemoteProps {
|
||||
link_signature_on_splash = 'link_signature_on_splash',
|
||||
@@ -37,96 +42,171 @@ export interface MapSettingsProps {
|
||||
onHide: () => void;
|
||||
}
|
||||
|
||||
type CheckboxesList = {
|
||||
type SettingsListItem = {
|
||||
prop: keyof UserSettings;
|
||||
label: string;
|
||||
}[];
|
||||
type: 'checkbox' | 'dropdown';
|
||||
options?: { label: string; value: string }[];
|
||||
};
|
||||
|
||||
const COMMON_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowMinimap, label: 'Show Minimap' },
|
||||
const COMMON_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isShowMinimap,
|
||||
label: 'Show Minimap',
|
||||
type: 'checkbox',
|
||||
},
|
||||
];
|
||||
|
||||
const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowKSpace, label: 'Highlight Low/High-security systems' },
|
||||
{ prop: UserSettingsRemoteProps.select_on_spash, label: 'Auto-select splashed' },
|
||||
const SYSTEMS_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isShowKSpace,
|
||||
label: 'Highlight Low/High-security systems',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
prop: UserSettingsRemoteProps.select_on_spash,
|
||||
label: 'Auto-select splashed',
|
||||
type: 'checkbox',
|
||||
},
|
||||
];
|
||||
|
||||
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
|
||||
{ prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures, label: 'Show unsplashed signatures' },
|
||||
const SIGNATURES_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||
{
|
||||
prop: UserSettingsRemoteProps.link_signature_on_splash,
|
||||
label: 'Link signature on splash',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures,
|
||||
label: 'Show unsplashed signatures',
|
||||
type: 'checkbox',
|
||||
},
|
||||
];
|
||||
|
||||
const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' },
|
||||
const CONNECTIONS_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||
{
|
||||
prop: UserSettingsRemoteProps.delete_connection_with_sigs,
|
||||
label: 'Delete connections to linked signatures',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isThickConnections,
|
||||
label: 'Thicker connections',
|
||||
type: 'checkbox',
|
||||
},
|
||||
];
|
||||
|
||||
const UI_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
|
||||
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
|
||||
{ prop: InterfaceStoredSettingsProps.isShowBackgroundPattern, label: 'Show background pattern' },
|
||||
{ prop: InterfaceStoredSettingsProps.isSoftBackground, label: 'Enable soft background' },
|
||||
const UI_CHECKBOXES_PROPS: SettingsListItem[] = [
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isShowMenu,
|
||||
label: 'Enable compact map menu bar',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isShowBackgroundPattern,
|
||||
label: 'Show background pattern',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
prop: InterfaceStoredSettingsProps.isSoftBackground,
|
||||
label: 'Enable soft background',
|
||||
type: 'checkbox',
|
||||
},
|
||||
];
|
||||
|
||||
const THEME_OPTIONS = [
|
||||
{ label: 'Default', value: 'neon' },
|
||||
{ label: 'Pathfinder', value: 'pathfinder' },
|
||||
];
|
||||
|
||||
const THEME_SETTING: SettingsListItem = {
|
||||
prop: 'theme',
|
||||
label: 'Theme',
|
||||
type: 'dropdown',
|
||||
options: THEME_OPTIONS,
|
||||
};
|
||||
|
||||
export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({ ...DEFAULT_REMOTE_SETTINGS });
|
||||
const [userRemoteSettings, setUserRemoteSettings] = useState<UserSettingsRemote>({
|
||||
...DEFAULT_REMOTE_SETTINGS,
|
||||
});
|
||||
|
||||
const mergedSettings = useMemo(() => {
|
||||
return {
|
||||
...interfaceSettings,
|
||||
...userRemoteSettings,
|
||||
...interfaceSettings,
|
||||
};
|
||||
}, [userRemoteSettings, interfaceSettings]);
|
||||
|
||||
|
||||
const handleShow = async () => {
|
||||
const { user_settings } = await outCommand({
|
||||
type: OutCommand.getUserSettings,
|
||||
data: null,
|
||||
});
|
||||
|
||||
setUserRemoteSettings({
|
||||
...user_settings,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeChecked = useCallback(
|
||||
(prop: keyof UserSettings) => async (checked: boolean) => {
|
||||
// @ts-ignore
|
||||
if (UserSettingsRemoteList.includes(prop)) {
|
||||
const handleSettingChange = useCallback(
|
||||
async (prop: keyof UserSettings, value: boolean | string) => {
|
||||
if (UserSettingsRemoteList.includes(prop as any)) {
|
||||
const newRemoteSettings = {
|
||||
...userRemoteSettings,
|
||||
[prop]: checked,
|
||||
[prop]: value,
|
||||
};
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.updateUserSettings,
|
||||
data: newRemoteSettings,
|
||||
});
|
||||
|
||||
setUserRemoteSettings(newRemoteSettings);
|
||||
return;
|
||||
} else {
|
||||
setInterfaceSettings({
|
||||
...interfaceSettings,
|
||||
[prop]: value,
|
||||
});
|
||||
}
|
||||
|
||||
setInterfaceSettings({
|
||||
...interfaceSettings,
|
||||
[prop]: checked,
|
||||
});
|
||||
},
|
||||
[interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings],
|
||||
[userRemoteSettings, interfaceSettings, outCommand, setInterfaceSettings],
|
||||
);
|
||||
|
||||
const renderCheckboxesList = (list: CheckboxesList) => {
|
||||
return list.map(x => {
|
||||
const renderSettingItem = (item: SettingsListItem) => {
|
||||
const currentValue = mergedSettings[item.prop];
|
||||
|
||||
if (item.type === 'checkbox') {
|
||||
return (
|
||||
<PrettySwitchbox
|
||||
key={x.prop}
|
||||
label={x.label}
|
||||
checked={mergedSettings[x.prop]}
|
||||
setChecked={handleChangeChecked(x.prop)}
|
||||
key={item.prop}
|
||||
label={item.label}
|
||||
checked={!!currentValue}
|
||||
setChecked={(checked) => handleSettingChange(item.prop, checked)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.type === 'dropdown' && item.options) {
|
||||
return (
|
||||
<div key={item.prop} className="flex items-center gap-2 mt-2">
|
||||
<label className="text-sm">{item.label}:</label>
|
||||
<Dropdown
|
||||
className="text-sm"
|
||||
value={currentValue}
|
||||
options={item.options}
|
||||
onChange={(e) => handleSettingChange(item.prop, e.value)}
|
||||
placeholder="Select a theme"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderSettingsList = (list: SettingsListItem[]) => {
|
||||
return list.map(renderSettingItem);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -137,10 +217,7 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
style={{ width: '550px' }}
|
||||
onShow={handleShow}
|
||||
onHide={() => {
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!show) return;
|
||||
setActiveIndex(0);
|
||||
onHide();
|
||||
}}
|
||||
@@ -150,25 +227,35 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
<div className={styles.verticalTabsContainer}>
|
||||
<TabView
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={e => setActiveIndex(e.index)}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className={styles.verticalTabView}
|
||||
>
|
||||
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">{renderCheckboxesList(COMMON_CHECKBOXES_PROPS)}</div>
|
||||
</TabPanel>
|
||||
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">
|
||||
{renderCheckboxesList(SYSTEMS_CHECKBOXES_PROPS)}
|
||||
{renderSettingsList(COMMON_CHECKBOXES_PROPS)}
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Systems" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="w-full h-full flex flex-col gap-1">
|
||||
{renderSettingsList(SYSTEMS_CHECKBOXES_PROPS)}
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Connections" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)}
|
||||
{renderSettingsList(CONNECTIONS_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
|
||||
{renderSettingsList(SIGNATURES_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(UI_CHECKBOXES_PROPS)}
|
||||
{renderSettingsList(UI_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Theme" headerClassName={styles.verticalTabHeader}>
|
||||
{renderSettingItem(THEME_SETTING)}
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</div>
|
||||
|
||||
@@ -21,9 +21,6 @@ const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes:
|
||||
return x.src.some(x => {
|
||||
const [group, type] = x.split('-');
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('JOipP', `group, type`, group, type);
|
||||
|
||||
if (type === 'shattered') {
|
||||
return systemStatic.is_shattered && group === spawnClassGroup;
|
||||
}
|
||||
@@ -32,11 +29,6 @@ const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes:
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('JOipP', `possibleWHTypes`, possibleWHTypes);
|
||||
|
||||
// debugger;
|
||||
|
||||
return {
|
||||
statics: possibleWHTypes
|
||||
.filter(x => x.respawn.some(y => y === Respawn.static))
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import {
|
||||
@@ -14,14 +14,18 @@ import classes from './MapWrapper.module.scss';
|
||||
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
|
||||
import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { Node } from 'reactflow';
|
||||
import { Node, XYPosition } from 'reactflow';
|
||||
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||
import { emitMapEvent, useMapEventListener } from '@/hooks/Mapper/events';
|
||||
|
||||
import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { useCommonMapEventProcessor } from '@/hooks/Mapper/components/mapWrapper/hooks/useCommonMapEventProcessor.ts';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
|
||||
// TODO: INFO - this component needs for abstract work with Map instance
|
||||
export const MapWrapper = () => {
|
||||
@@ -36,6 +40,7 @@ export const MapWrapper = () => {
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
},
|
||||
} = useMapRootState();
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
@@ -47,6 +52,7 @@ export const MapWrapper = () => {
|
||||
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
||||
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
|
||||
const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<XYPosition | null>(null);
|
||||
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
|
||||
|
||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems });
|
||||
@@ -123,6 +129,28 @@ export const MapWrapper = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onAddSystem: OnMapAddSystemCallback = useCallback(({ coordinates }) => {
|
||||
setOpenAddSystem(coordinates);
|
||||
}, []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (ref.current.systems.some(x => x.system_static_info.solar_system_id === item.value)) {
|
||||
emitMapEvent({
|
||||
name: Commands.centerSystem,
|
||||
data: item.value.toString(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.manualAddSystem,
|
||||
data: { coordinates: openAddSystem, solar_system_id: item.value },
|
||||
});
|
||||
},
|
||||
[openAddSystem, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
@@ -139,6 +167,8 @@ export const MapWrapper = () => {
|
||||
isThickConnections={isThickConnections}
|
||||
isShowBackgroundPattern={isShowBackgroundPattern}
|
||||
isSoftBackground={isSoftBackground}
|
||||
theme={theme}
|
||||
onAddSystem={onAddSystem}
|
||||
/>
|
||||
|
||||
{openSettings != null && (
|
||||
@@ -153,6 +183,12 @@ export const MapWrapper = () => {
|
||||
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
||||
)}
|
||||
|
||||
<AddSystemDialog
|
||||
visible={!!openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(null)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
|
||||
<Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} />
|
||||
|
||||
<ContextMenuSystem
|
||||
|
||||
@@ -31,12 +31,15 @@ const prepareEffects = (effects: Record<string, EffectRaw>, effectName: string,
|
||||
return out;
|
||||
};
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export interface WHEffectViewProps {
|
||||
effectName: string;
|
||||
effectPower: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) => {
|
||||
export const WHEffectView = ({ effectName, effectPower, className }: WHEffectViewProps) => {
|
||||
const {
|
||||
data: { effects },
|
||||
} = useMapRootState();
|
||||
@@ -49,7 +52,7 @@ export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) =>
|
||||
[effectName, effectPower, effects],
|
||||
);
|
||||
|
||||
const targetClass = `wh-effect-name${effectInfo.id}`;
|
||||
const targetClass = useMemo(() => `wh-effect-name${effectInfo.id}-${counter++}`, []);
|
||||
|
||||
return (
|
||||
<div className={classes.WHEffectViewRoot}>
|
||||
@@ -84,7 +87,7 @@ export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) =>
|
||||
</div>
|
||||
</FixedTooltip>
|
||||
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass)}>
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass, className)}>
|
||||
{effectName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SignatureGroup, SignatureKind, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { MAPPING_TYPE_TO_ENG } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
|
||||
export const parseSignatures = (value: string, availableKeys: string[]): SystemSignature[] => {
|
||||
const outArr: SystemSignature[] = [];
|
||||
@@ -14,10 +14,12 @@ export const parseSignatures = (value: string, availableKeys: string[]): SystemS
|
||||
continue;
|
||||
}
|
||||
|
||||
const kind = MAPPING_TYPE_TO_ENG[sigArrInfo[1] as SignatureKind];
|
||||
|
||||
outArr.push({
|
||||
eve_id: sigArrInfo[0],
|
||||
kind: availableKeys.includes(sigArrInfo[1]) ? sigArrInfo[1] : COSMIC_SIGNATURE,
|
||||
group: sigArrInfo[2],
|
||||
kind: availableKeys.includes(kind) ? kind : SignatureKind.CosmicSignature,
|
||||
group: sigArrInfo[2] as SignatureGroup,
|
||||
name: sigArrInfo[3],
|
||||
type: '',
|
||||
});
|
||||
|
||||
@@ -37,6 +37,7 @@ export enum InterfaceStoredSettingsProps {
|
||||
isShowUnsplashedSignatures = 'isShowUnsplashedSignatures',
|
||||
isShowBackgroundPattern = 'isShowBackgroundPattern',
|
||||
isSoftBackground = 'isSoftBackground',
|
||||
theme = 'theme',
|
||||
}
|
||||
|
||||
export type InterfaceStoredSettings = {
|
||||
@@ -47,6 +48,7 @@ export type InterfaceStoredSettings = {
|
||||
isShowUnsplashedSignatures: boolean;
|
||||
isShowBackgroundPattern: boolean;
|
||||
isSoftBackground: boolean;
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
@@ -57,7 +59,8 @@ export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
isShowUnsplashedSignatures: false,
|
||||
isShowBackgroundPattern: true,
|
||||
isSoftBackground: false,
|
||||
};
|
||||
theme: 'neon',
|
||||
}
|
||||
|
||||
export interface MapRootContextProps {
|
||||
update: ContextStoreDataUpdate<MapRootData>;
|
||||
|
||||
@@ -14,16 +14,12 @@ export enum TimeStatus {
|
||||
eol,
|
||||
}
|
||||
|
||||
// export enum ShipSizeStatus {
|
||||
// small, // frigates, destroyers - less than 5K t
|
||||
// medium, // less than 20K t
|
||||
// large, // less than 375K t
|
||||
// capital, // less than 1.8M t
|
||||
// }
|
||||
|
||||
export enum ShipSizeStatus {
|
||||
small, // frigates, destroyers - less than 5K t
|
||||
normal,
|
||||
small = 0, // frigates, destroyers - less than 5K t
|
||||
medium = 1, // less than 62K t
|
||||
large = 2, // less than 375K t
|
||||
freight = 3, // less than 1M t
|
||||
capital = 4, // less than 1.8M t
|
||||
}
|
||||
|
||||
export type SolarSystemConnection = {
|
||||
|
||||
@@ -129,6 +129,7 @@ export enum OutCommand {
|
||||
updateConnectionCustomInfo = 'update_connection_custom_info',
|
||||
updateSignatures = 'update_signatures',
|
||||
updateSystemName = 'update_system_name',
|
||||
updateSystemTemporaryName = 'update_system_temporary_name',
|
||||
updateSystemDescription = 'update_system_description',
|
||||
updateSystemLabels = 'update_system_labels',
|
||||
updateSystemLocked = 'update_system_locked',
|
||||
@@ -153,6 +154,7 @@ export enum OutCommand {
|
||||
getUserSettings = 'get_user_settings',
|
||||
updateUserSettings = 'update_user_settings',
|
||||
unlinkSignature = 'unlink_signature',
|
||||
searchSystems = 'search_systems',
|
||||
}
|
||||
|
||||
export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>;
|
||||
|
||||
@@ -10,6 +10,15 @@ export enum SignatureGroup {
|
||||
CombatSite = 'Combat Site',
|
||||
}
|
||||
|
||||
export enum SignatureKind {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
CosmicAnomaly = 'Cosmic Anomaly',
|
||||
Structure = 'Structure',
|
||||
Ship = 'Ship',
|
||||
Deployable = 'Deployable',
|
||||
Drone = 'Drone',
|
||||
}
|
||||
|
||||
export type GroupType = {
|
||||
id: string;
|
||||
icon: string;
|
||||
@@ -19,7 +28,7 @@ export type GroupType = {
|
||||
|
||||
export type SystemSignature = {
|
||||
eve_id: string;
|
||||
kind: string;
|
||||
kind: SignatureKind;
|
||||
name: string;
|
||||
custom_info?: string;
|
||||
description?: string;
|
||||
@@ -30,3 +39,41 @@ export type SystemSignature = {
|
||||
inserted_at?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export enum SignatureKindENG {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
CosmicAnomaly = 'Cosmic Anomaly',
|
||||
Structure = 'Structure',
|
||||
Ship = 'Ship',
|
||||
Deployable = 'Deployable',
|
||||
Drone = 'Drone',
|
||||
}
|
||||
|
||||
export enum SignatureKindRU {
|
||||
CosmicSignature = 'Скрытый сигнал',
|
||||
CosmicAnomaly = 'Космическая аномалия',
|
||||
Structure = 'Сооружение',
|
||||
Ship = 'Корабль',
|
||||
Deployable = 'Полевые блоки',
|
||||
Drone = 'Дрон',
|
||||
}
|
||||
|
||||
export enum SignatureGroupENG {
|
||||
CosmicSignature = 'Cosmic Signature',
|
||||
Wormhole = 'Wormhole',
|
||||
GasSite = 'Gas Site',
|
||||
RelicSite = 'Relic Site',
|
||||
DataSite = 'Data Site',
|
||||
OreSite = 'Ore Site',
|
||||
CombatSite = 'Combat Site',
|
||||
}
|
||||
|
||||
export enum SignatureGroupRU {
|
||||
CosmicSignature = 'Скрытый сигнал',
|
||||
Wormhole = 'Червоточина',
|
||||
GasSite = 'Газовый район',
|
||||
RelicSite = 'Археологический район',
|
||||
DataSite = 'Информационный район',
|
||||
OreSite = 'Астероидный район',
|
||||
CombatSite = 'Боевой район',
|
||||
}
|
||||
|
||||
@@ -116,7 +116,18 @@ export type SolarSystemRawType = {
|
||||
tag: string | null;
|
||||
status: number;
|
||||
name: string | null;
|
||||
temporary_name: string | null;
|
||||
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
system_signatures: SystemSignature[];
|
||||
};
|
||||
|
||||
export type SearchSystemItem = {
|
||||
class_title: string;
|
||||
constellation_name: string;
|
||||
label: string;
|
||||
region_name: string;
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
value: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
export default {
|
||||
mounted() {
|
||||
const hook = this;
|
||||
const url = hook.el.dataset.url;
|
||||
const button = hook.el;
|
||||
const button = this.el;
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
// Get the URL from the data attribute
|
||||
|
||||
button.classList.remove('copied');
|
||||
|
||||
// Copy the URL to the clipboard
|
||||
navigator.clipboard
|
||||
.writeText(url)
|
||||
.writeText(button.dataset.url)
|
||||
.then(() => {
|
||||
button.classList.add('copied');
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import CopyToClipboard from './copyToClipboard';
|
||||
import DownloadJson from './downloadJson';
|
||||
import NewVersionUpdate from './newVersionUpdate';
|
||||
import MapAction from './maps/mapAction';
|
||||
import ShowCharactersAddAlert from './showCharactersAddAlert';
|
||||
|
||||
export default {
|
||||
DownloadJson,
|
||||
@@ -20,4 +21,5 @@ export default {
|
||||
Ping,
|
||||
CopyToClipboard,
|
||||
NewVersionUpdate,
|
||||
ShowCharactersAddAlert,
|
||||
};
|
||||
|
||||
11
assets/js/hooks/showCharactersAddAlert.ts
Normal file
11
assets/js/hooks/showCharactersAddAlert.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default {
|
||||
mounted() {
|
||||
this.pushEvent('restore_show_characters_add_alert', {
|
||||
value: localStorage.getItem('wanderer:hide_characters_add_alert') !== 'true',
|
||||
});
|
||||
|
||||
document.getElementById('characters-add-alert-hide')?.addEventListener('click', e => {
|
||||
localStorage.setItem('wanderer:hide_characters_add_alert', 'true');
|
||||
});
|
||||
},
|
||||
};
|
||||
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -48,6 +48,11 @@ port =
|
||||
|
||||
scheme = System.get_env("WEB_EXTERNAL_SCHEME", "http")
|
||||
|
||||
public_api_disabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_PUBLIC_API_DISABLED", "false")
|
||||
|> String.to_existing_atom()
|
||||
|
||||
map_subscriptions_enabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_MAP_SUBSCRIPTIONS_ENABLED", "false")
|
||||
@@ -83,6 +88,7 @@ config :wanderer_app,
|
||||
admins: admins,
|
||||
corp_id: System.get_env("WANDERER_CORP_ID", "-1") |> String.to_integer(),
|
||||
corp_wallet: System.get_env("WANDERER_CORP_WALLET", ""),
|
||||
public_api_disabled: public_api_disabled,
|
||||
map_subscriptions_enabled: map_subscriptions_enabled,
|
||||
wallet_tracking_enabled: wallet_tracking_enabled,
|
||||
subscription_settings: %{
|
||||
|
||||
@@ -21,6 +21,7 @@ defmodule WandererApp.Api.Map do
|
||||
define(:update_options, action: :update_options)
|
||||
define(:assign_owner, action: :assign_owner)
|
||||
define(:mark_as_deleted, action: :mark_as_deleted)
|
||||
define(:update_api_key, action: :update_api_key)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
@@ -122,6 +123,11 @@ defmodule WandererApp.Api.Map do
|
||||
|
||||
change(set_attribute(:deleted, true))
|
||||
end
|
||||
|
||||
update :update_api_key do
|
||||
accept [:public_api_key]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -141,6 +147,10 @@ defmodule WandererApp.Api.Map do
|
||||
attribute :description, :string
|
||||
attribute :personal_note, :string
|
||||
|
||||
attribute :public_api_key, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :hubs, {:array, :string} do
|
||||
allow_nil?(true)
|
||||
|
||||
|
||||
@@ -14,31 +14,24 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
define(:create, action: :create)
|
||||
define(:destroy, action: :destroy)
|
||||
|
||||
define(:read_by_map,
|
||||
action: :read_by_map
|
||||
)
|
||||
|
||||
define(:by_map_filtered,
|
||||
action: :by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_filtered,
|
||||
action: :tracked_by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_all,
|
||||
action: :tracked_by_map_all
|
||||
)
|
||||
define(:read_by_map, action: :read_by_map)
|
||||
define(:by_map_filtered, action: :by_map_filtered)
|
||||
define(:tracked_by_map_filtered, action: :tracked_by_map_filtered)
|
||||
define(:tracked_by_map_all, action: :tracked_by_map_all)
|
||||
|
||||
define(:track, action: :track)
|
||||
define(:untrack, action: :untrack)
|
||||
|
||||
define(:follow, action: :follow)
|
||||
define(:unfollow, action: :unfollow)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:map_id,
|
||||
:character_id,
|
||||
:tracked
|
||||
:tracked,
|
||||
:followed
|
||||
]
|
||||
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
@@ -76,6 +69,14 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
update :untrack do
|
||||
change(set_attribute(:tracked, false))
|
||||
end
|
||||
|
||||
update :follow do
|
||||
change(set_attribute(:followed, true))
|
||||
end
|
||||
|
||||
update :unfollow do
|
||||
change(set_attribute(:followed, false))
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -86,6 +87,11 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :followed, :boolean do
|
||||
default false
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -128,11 +128,13 @@ defmodule WandererApp.Api.MapConnection do
|
||||
allow_nil?(true)
|
||||
end
|
||||
|
||||
# where 0 - Frigate
|
||||
# where 1 - Medium and Large
|
||||
# where 2 - Capital
|
||||
# where 0 - Frigate (small
|
||||
# where 1 - Medium
|
||||
# where 2 - Large
|
||||
# where 3 - Freight
|
||||
# where 4 - Capital
|
||||
attribute :ship_size_type, :integer do
|
||||
default(1)
|
||||
default(2)
|
||||
|
||||
allow_nil?(true)
|
||||
end
|
||||
|
||||
@@ -46,6 +46,7 @@ defmodule WandererApp.Api.MapSystem do
|
||||
define(:update_locked, action: :update_locked)
|
||||
define(:update_status, action: :update_status)
|
||||
define(:update_tag, action: :update_tag)
|
||||
define(:update_temporary_name, action: :update_temporary_name)
|
||||
define(:update_labels, action: :update_labels)
|
||||
define(:update_position, action: :update_position)
|
||||
define(:update_visible, action: :update_visible)
|
||||
@@ -102,6 +103,10 @@ defmodule WandererApp.Api.MapSystem do
|
||||
accept [:tag]
|
||||
end
|
||||
|
||||
update :update_temporary_name do
|
||||
accept [:temporary_name]
|
||||
end
|
||||
|
||||
update :update_labels do
|
||||
accept [:labels]
|
||||
end
|
||||
@@ -141,6 +146,10 @@ defmodule WandererApp.Api.MapSystem do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :temporary_name, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :labels, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
@@ -489,7 +489,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
defp maybe_update_location(
|
||||
%{
|
||||
character_id: character_id
|
||||
character_id: character_id,
|
||||
} =
|
||||
state,
|
||||
location
|
||||
|
||||
@@ -9,6 +9,7 @@ defmodule WandererApp.Env do
|
||||
def custom_route_base_url, do: get_key(:custom_route_base_url, "<CUSTOM_ROUTE_BASE_URL>")
|
||||
def invites, do: get_key(:invites, false)
|
||||
def map_subscriptions_enabled?, do: get_key(:map_subscriptions_enabled, false)
|
||||
def public_api_disabled?, do: get_key(:public_api_disabled, false)
|
||||
def wallet_tracking_enabled?, do: get_key(:wallet_tracking_enabled, false)
|
||||
def admins, do: get_key(:admins, [])
|
||||
def admin_username, do: get_key(:admin_username)
|
||||
|
||||
@@ -129,6 +129,12 @@ defmodule WandererApp.Map.Server do
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_system_tag/2, [update]})
|
||||
|
||||
def update_system_temporary_name(map_id, update) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_system_temporary_name/2, [update]})
|
||||
|
||||
def update_system_locked(map_id, update) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|
||||
@@ -150,6 +150,8 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
|
||||
defdelegate update_system_tag(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_temporary_name(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_locked(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_labels(state, update), to: SystemsImpl
|
||||
@@ -201,7 +203,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
| map: map |> WandererApp.Map.update_subscription_settings!(subscription_settings)
|
||||
}
|
||||
|
||||
def handle_event(:update_characters, %{map_id: map_id} = state) do
|
||||
def handle_event(:update_characters, state) do
|
||||
Process.send_after(self(), :update_characters, @update_characters_timeout)
|
||||
|
||||
CharactersImpl.update_characters(state)
|
||||
@@ -259,7 +261,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
state
|
||||
end
|
||||
|
||||
def handle_event(:cleanup_systems, %{map_id: map_id} = state) do
|
||||
def handle_event(:cleanup_systems, state) do
|
||||
Process.send_after(self(), :cleanup_systems, @systems_cleanup_timeout)
|
||||
|
||||
state |> SystemsImpl.cleanup_systems()
|
||||
@@ -320,6 +322,8 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
layout: options |> Map.get("layout", "left_to_right"),
|
||||
store_custom_labels:
|
||||
options |> Map.get("store_custom_labels", "false") |> String.to_existing_atom(),
|
||||
show_temp_system_name:
|
||||
options |> Map.get("show_temp_system_name", "false") |> String.to_existing_atom(),
|
||||
restrict_offline_showing:
|
||||
options |> Map.get("restrict_offline_showing", "false") |> String.to_existing_atom()
|
||||
]
|
||||
@@ -427,7 +431,8 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
"name" => name,
|
||||
"position" => %{"x" => x, "y" => y},
|
||||
"status" => status,
|
||||
"tag" => tag
|
||||
"tag" => tag,
|
||||
"temporary_name" => temporary_name,
|
||||
} = _system,
|
||||
acc ->
|
||||
acc
|
||||
@@ -446,6 +451,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
})
|
||||
|> update_system_status(%{solar_system_id: id |> String.to_integer(), status: status})
|
||||
|> update_system_tag(%{solar_system_id: id |> String.to_integer(), tag: tag})
|
||||
|> update_system_temporary_name(%{solar_system_id: id |> String.to_integer(), temporary_name: temporary_name})
|
||||
|> update_system_locked(%{solar_system_id: id |> String.to_integer(), locked: locked})
|
||||
|> update_system_labels(%{solar_system_id: id |> String.to_integer(), labels: labels})
|
||||
end)
|
||||
|
||||
@@ -15,13 +15,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: track_character
|
||||
tracked: track_character,
|
||||
followed: false
|
||||
}),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :added], %{count: 1})
|
||||
|
||||
:ok
|
||||
else
|
||||
_error ->
|
||||
@@ -382,7 +381,6 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
{:character_location, %{solar_system_id: solar_system_id},
|
||||
%{solar_system_id: old_solar_system_id}}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
|
||||
@@ -333,7 +333,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
solar_system_id: location.solar_system_id,
|
||||
})
|
||||
|
||||
Impl.broadcast!(map_id, :add_connection, connection)
|
||||
@@ -346,9 +346,20 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
:ok
|
||||
{:error, :already_exists} ->
|
||||
# Still broadcast location change in case of followed character
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
|
||||
:ok
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -359,33 +370,30 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
def can_add_location(:none, _solar_system_id), do: false
|
||||
|
||||
def can_add_location(scope, solar_system_id) do
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
{:ok, system_static_info} = get_system_static_info(solar_system_id)
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(system_static_info.system_class) and
|
||||
not (@prohibited_systems |> Enum.member?(solar_system_id)) and
|
||||
@wh_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(system_static_info.system_class) and
|
||||
@known_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:all ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class))
|
||||
not is_prohibited_system_class?(system_static_info.system_class)
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def is_prohibited_system_class?(system_class) do
|
||||
@prohibited_system_classes |> Enum.member?(system_class)
|
||||
end
|
||||
|
||||
def is_connection_exist(map_id, from_solar_system_id, to_solar_system_id),
|
||||
do:
|
||||
not is_nil(
|
||||
@@ -414,24 +422,21 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
current_system_id: to_solar_system_id
|
||||
})
|
||||
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(to_solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
{:ok, from_system_static_info} = get_system_static_info(from_solar_system_id)
|
||||
{:ok, to_system_static_info} = get_system_static_info(to_solar_system_id)
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(from_system_static_info.system_class) and
|
||||
not is_prohibited_system_class?(to_system_static_info.system_class) and
|
||||
not (@prohibited_systems |> Enum.member?(from_solar_system_id)) and
|
||||
not (@prohibited_systems |> Enum.member?(to_solar_system_id)) and
|
||||
known_jumps |> Enum.empty?() and to_solar_system_id != @jita and
|
||||
from_solar_system_id != @jita
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(from_system_static_info.system_class) and
|
||||
not is_prohibited_system_class?(to_system_static_info.system_class) and
|
||||
not (known_jumps |> Enum.empty?())
|
||||
end
|
||||
end
|
||||
@@ -447,6 +452,16 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_system_static_info(solar_system_id) do
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
{:ok, system_static_info}
|
||||
|
||||
_ ->
|
||||
{:ok, %{system_class: nil}}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(map_id, location, old_location)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
|
||||
@@ -122,6 +122,13 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
),
|
||||
do: state |> update_system(:update_tag, [:tag], update)
|
||||
|
||||
def update_system_temporary_name(
|
||||
state,
|
||||
update
|
||||
) do
|
||||
state |> update_system(:update_temporary_name, [:temporary_name], update)
|
||||
end
|
||||
|
||||
def update_system_locked(
|
||||
state,
|
||||
update
|
||||
@@ -286,6 +293,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|
||||
|> WandererApp.MapSystemRepo.update_visible!(%{visible: true})
|
||||
|> WandererApp.MapSystemRepo.cleanup_tags()
|
||||
|> WandererApp.MapSystemRepo.cleanup_temporary_name()
|
||||
|
||||
@ddrt.insert(
|
||||
{existing_system.solar_system_id,
|
||||
@@ -404,6 +412,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
|> WandererApp.MapSystemRepo.update_position!(%{position_x: x, position_y: y})
|
||||
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|
||||
|> WandererApp.MapSystemRepo.cleanup_tags!()
|
||||
|> WandererApp.MapSystemRepo.cleanup_temporary_name!()
|
||||
|> WandererApp.MapSystemRepo.update_visible(%{visible: true})
|
||||
end
|
||||
|
||||
|
||||
@@ -84,20 +84,22 @@ defmodule WandererApp.Maps do
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: false
|
||||
tracked: false,
|
||||
followed: false
|
||||
}
|
||||
|
||||
def map_character(
|
||||
%{name: name, id: id, eve_id: eve_id, corporation_ticker: corporation_ticker} =
|
||||
_character,
|
||||
%{tracked: tracked} = _character_settings
|
||||
%{tracked: tracked, followed: followed} = _character_settings
|
||||
),
|
||||
do: %{
|
||||
name: name,
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: tracked
|
||||
tracked: tracked,
|
||||
followed: followed
|
||||
}
|
||||
|
||||
@decorate cacheable(
|
||||
|
||||
@@ -24,11 +24,31 @@ defmodule WandererApp.MapCharacterSettingsRepo do
|
||||
def get_tracked_by_map_all(map_id),
|
||||
do: WandererApp.Api.MapCharacterSettings.tracked_by_map_all(%{map_id: map_id})
|
||||
|
||||
def get_by_map(map_id, character_id) do
|
||||
case get_by_map_filtered(map_id, [character_id]) do
|
||||
{:ok, [setting | _]} ->
|
||||
{:ok, setting}
|
||||
|
||||
{:ok, []} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def track(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track()
|
||||
def untrack(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack()
|
||||
|
||||
def track!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track!()
|
||||
def untrack!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack!()
|
||||
|
||||
def follow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow()
|
||||
def unfollow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow()
|
||||
|
||||
def follow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow!()
|
||||
def unfollow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow!()
|
||||
|
||||
|
||||
def destroy!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.destroy!()
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ defmodule WandererApp.MapRepo do
|
||||
@default_map_options %{
|
||||
"layout" => "left_to_right",
|
||||
"store_custom_labels" => "false",
|
||||
"show_temp_system_name" => "false",
|
||||
"restrict_offline_showing" => "false"
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,13 @@ defmodule WandererApp.MapSystemRepo do
|
||||
end
|
||||
end
|
||||
|
||||
def get_all_by_map(map_id),
|
||||
do: WandererApp.Api.MapSystem.read_all_by_map(%{map_id: map_id})
|
||||
def get_all_by_map(map_id) do
|
||||
WandererApp.Api.MapSystem.read_all_by_map(%{map_id: map_id})
|
||||
end
|
||||
|
||||
def get_visible_by_map(map_id),
|
||||
do: WandererApp.Api.MapSystem.read_visible_by_map(%{map_id: map_id})
|
||||
def get_visible_by_map(map_id) do
|
||||
WandererApp.Api.MapSystem.read_visible_by_map(%{map_id: map_id})
|
||||
end
|
||||
|
||||
def remove_from_map(map_id, solar_system_id) do
|
||||
WandererApp.Api.MapSystem.read_by_map_and_solar_system!(%{
|
||||
@@ -59,6 +61,20 @@ defmodule WandererApp.MapSystemRepo do
|
||||
})
|
||||
end
|
||||
|
||||
def cleanup_temporary_name(system) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_temporary_name(%{
|
||||
temporary_name: nil
|
||||
})
|
||||
end
|
||||
|
||||
def cleanup_temporary_name!(system) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_temporary_name!(%{
|
||||
temporary_name: nil
|
||||
})
|
||||
end
|
||||
|
||||
def get_filtered_labels(labels, true) when is_binary(labels) do
|
||||
labels
|
||||
|> Jason.decode!()
|
||||
@@ -99,6 +115,11 @@ defmodule WandererApp.MapSystemRepo do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_tag(update)
|
||||
|
||||
def update_temporary_name(system, update) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_temporary_name(update)
|
||||
end
|
||||
|
||||
def update_labels(system, update),
|
||||
do:
|
||||
system
|
||||
|
||||
47
lib/wanderer_app_web/components/character_activity.ex
Normal file
47
lib/wanderer_app_web/components/character_activity.ex
Normal file
@@ -0,0 +1,47 @@
|
||||
defmodule WandererAppWeb.CharacterActivity do
|
||||
use WandererAppWeb, :live_component
|
||||
use LiveViewEvents
|
||||
|
||||
@impl true
|
||||
def mount(socket) do
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(
|
||||
assigns,
|
||||
socket
|
||||
) do
|
||||
{:ok,
|
||||
socket
|
||||
|> handle_info_or_assign(assigns)}
|
||||
end
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div id={@id}>
|
||||
<.table class="!max-h-[80vh] !overflow-y-auto" id="activity-tbl" rows={@activity}>
|
||||
<:col :let={row} label="Character">
|
||||
<.character_item character={row.character} />
|
||||
</:col>
|
||||
<:col :let={row} label="Passages">
|
||||
<%= row.count %>
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def character_item(assigns) do
|
||||
~H"""
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="avatar">
|
||||
<div class="rounded-md w-12 h-12">
|
||||
<img src={member_icon_url(@character.eve_id)} alt={@character.name} />
|
||||
</div>
|
||||
</div>
|
||||
<%= @character.name %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -393,6 +393,7 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
data-pc-name="checkbox"
|
||||
data-pc-section="root"
|
||||
>
|
||||
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
|
||||
<input
|
||||
id={@id}
|
||||
name={@name}
|
||||
|
||||
308
lib/wanderer_app_web/controllers/api_controller.ex
Normal file
308
lib/wanderer_app_web/controllers/api_controller.ex
Normal file
@@ -0,0 +1,308 @@
|
||||
defmodule WandererAppWeb.APIController do
|
||||
use WandererAppWeb, :controller
|
||||
|
||||
import Ash.Query, only: [filter: 2]
|
||||
|
||||
alias WandererApp.Api
|
||||
alias WandererApp.MapSystemRepo
|
||||
alias WandererApp.MapCharacterSettingsRepo
|
||||
alias WandererApp.Api.Character
|
||||
alias WandererApp.CachedInfo
|
||||
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Common
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
GET /api/system-static-info
|
||||
|
||||
Requires 'id' (the solar_system_id)
|
||||
|
||||
Example:
|
||||
GET /api/common/system_static?id=31002229
|
||||
GET /api/common/system_static?id=31002229
|
||||
"""
|
||||
def show_system_static(conn, params) do
|
||||
with {:ok, solar_system_str} <- require_param(params, "id"),
|
||||
{:ok, solar_system_id} <- parse_int(solar_system_str) do
|
||||
case CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system} ->
|
||||
data = static_system_to_json(system)
|
||||
json(conn, %{data: data})
|
||||
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "System not found"})
|
||||
end
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Map
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
GET /api/map/systems
|
||||
|
||||
Requires either `?map_id=<UUID>` **OR** `?slug=<map-slug>` in the query params.
|
||||
|
||||
If `?all=true` is provided, **all** systems are returned.
|
||||
Otherwise, only "visible" systems are returned.
|
||||
|
||||
Examples:
|
||||
GET /api/map/systems?map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||
GET /api/map/systems?slug=my-unique-wormhole-map
|
||||
GET /api/map/systems?map_id=<UUID>&all=true
|
||||
"""
|
||||
def list_systems(conn, params) do
|
||||
with {:ok, map_id} <- fetch_map_id(params) do
|
||||
# Decide which function to call based on the "all" param
|
||||
repo_fun =
|
||||
if params["all"] == "true" do
|
||||
&MapSystemRepo.get_all_by_map/1
|
||||
else
|
||||
&MapSystemRepo.get_visible_by_map/1
|
||||
end
|
||||
|
||||
case repo_fun.(map_id) do
|
||||
{:ok, systems} ->
|
||||
data = Enum.map(systems, &map_system_to_json/1)
|
||||
json(conn, %{data: data})
|
||||
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Could not fetch systems for map_id=#{map_id}: #{inspect(reason)}"})
|
||||
end
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
GET /api/map/system
|
||||
|
||||
Requires 'id' (the solar_system_id)
|
||||
plus either ?map_id=<UUID> or ?slug=<map-slug>.
|
||||
|
||||
Example:
|
||||
GET /api/map/system?id=31002229&map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||
GET /api/map/system?id=31002229&slug=my-unique-wormhole-map
|
||||
"""
|
||||
def show_system(conn, params) do
|
||||
with {:ok, solar_system_str} <- require_param(params, "id"),
|
||||
{:ok, solar_system_id} <- parse_int(solar_system_str),
|
||||
{:ok, map_id} <- fetch_map_id(params) do
|
||||
case MapSystemRepo.get_by_map_and_solar_system_id(map_id, solar_system_id) do
|
||||
{:ok, system} ->
|
||||
data = map_system_to_json(system)
|
||||
json(conn, %{data: data})
|
||||
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "System not found in map=#{map_id}"})
|
||||
end
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@doc """
|
||||
GET /api/map/tracked_characters_with_info
|
||||
|
||||
Example usage:
|
||||
GET /api/map/tracked_characters_with_info?map_id=<uuid>
|
||||
GET /api/map/tracked_characters_with_info?slug=<map-slug>
|
||||
|
||||
Returns a list of tracked records, plus their fully-loaded `character` data.
|
||||
"""
|
||||
def tracked_characters_with_info(conn, params) do
|
||||
with {:ok, map_id} <- fetch_map_id(params),
|
||||
{:ok, settings_list} <- get_tracked_by_map_ids(map_id),
|
||||
{:ok, char_list} <-
|
||||
read_characters_by_ids_wrapper(Enum.map(settings_list, & &1.character_id)) do
|
||||
|
||||
chars_by_id = Map.new(char_list, &{&1.id, &1})
|
||||
|
||||
data =
|
||||
Enum.map(settings_list, fn setting ->
|
||||
found_char = Map.get(chars_by_id, setting.character_id)
|
||||
|
||||
%{
|
||||
id: setting.id,
|
||||
map_id: setting.map_id,
|
||||
character_id: setting.character_id,
|
||||
tracked: setting.tracked,
|
||||
inserted_at: setting.inserted_at,
|
||||
updated_at: setting.updated_at,
|
||||
character:
|
||||
if found_char do
|
||||
character_to_json(found_char)
|
||||
else
|
||||
%{}
|
||||
end
|
||||
}
|
||||
end)
|
||||
|
||||
json(conn, %{data: data})
|
||||
else
|
||||
{:error, :get_tracked_error, reason} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "No tracked records found for map_id: #{inspect(reason)}"})
|
||||
|
||||
{:error, :read_characters_by_ids_error, reason} ->
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: "Could not load Character records: #{inspect(reason)}"})
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
defp get_tracked_by_map_ids(map_id) do
|
||||
case MapCharacterSettingsRepo.get_tracked_by_map_all(map_id) do
|
||||
{:ok, settings_list} ->
|
||||
{:ok, settings_list}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, :get_tracked_error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_characters_by_ids_wrapper(ids) do
|
||||
case read_characters_by_ids(ids) do
|
||||
{:ok, char_list} ->
|
||||
{:ok, char_list}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, :read_characters_by_ids_error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_map_id(%{"map_id" => mid}) when is_binary(mid) and mid != "" do
|
||||
{:ok, mid}
|
||||
end
|
||||
|
||||
defp fetch_map_id(%{"slug" => slug}) when is_binary(slug) and slug != "" do
|
||||
case WandererApp.Api.Map.get_map_by_slug(slug) do
|
||||
{:ok, map} ->
|
||||
{:ok, map.id}
|
||||
|
||||
{:error, _reason} ->
|
||||
{:error, "No map found for slug=#{slug}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_map_id(_), do: {:error, "Must provide either ?map_id=UUID or ?slug=SLUG"}
|
||||
|
||||
defp require_param(params, key) do
|
||||
case params[key] do
|
||||
nil -> {:error, "Missing required param: #{key}"}
|
||||
"" -> {:error, "Param #{key} cannot be empty"}
|
||||
val -> {:ok, val}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_int(str) do
|
||||
case Integer.parse(str) do
|
||||
{num, ""} -> {:ok, num}
|
||||
_ -> {:error, "Invalid integer for param id=#{str}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_characters_by_ids(ids) when is_list(ids) do
|
||||
if ids == [] do
|
||||
{:ok, []}
|
||||
else
|
||||
query =
|
||||
Character
|
||||
|> filter(id in ^ids)
|
||||
|
||||
Api.read(query)
|
||||
end
|
||||
end
|
||||
|
||||
defp map_system_to_json(system) do
|
||||
Map.take(system, [
|
||||
:id,
|
||||
:map_id,
|
||||
:solar_system_id,
|
||||
:name,
|
||||
:custom_name,
|
||||
:temporary_name,
|
||||
:description,
|
||||
:tag,
|
||||
:labels,
|
||||
:locked,
|
||||
:visible,
|
||||
:status,
|
||||
:position_x,
|
||||
:position_y,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
])
|
||||
end
|
||||
|
||||
defp character_to_json(ch) do
|
||||
Map.take(ch, [
|
||||
:id,
|
||||
:eve_id,
|
||||
:name,
|
||||
:corporation_id,
|
||||
:corporation_name,
|
||||
:corporation_ticker,
|
||||
:alliance_id,
|
||||
:alliance_name,
|
||||
:alliance_ticker,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
])
|
||||
end
|
||||
|
||||
|
||||
defp static_system_to_json(system) do
|
||||
system
|
||||
|> Map.take([
|
||||
:solar_system_id,
|
||||
:region_id,
|
||||
:constellation_id,
|
||||
:solar_system_name,
|
||||
:solar_system_name_lc,
|
||||
:constellation_name,
|
||||
:region_name,
|
||||
:system_class,
|
||||
:security,
|
||||
:type_description,
|
||||
:class_title,
|
||||
:is_shattered,
|
||||
:effect_name,
|
||||
:effect_power,
|
||||
:statics,
|
||||
:wandering,
|
||||
:triglavian_invasion_status,
|
||||
:sun_type_id
|
||||
])
|
||||
end
|
||||
|
||||
end
|
||||
@@ -93,13 +93,9 @@ defmodule WandererAppWeb.AuthController do
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
|
||||
def maybe_update_character_user_id(character, user_id) do
|
||||
case character.user_id do
|
||||
nil ->
|
||||
WandererApp.Api.Character.assign_user!(character, %{user_id: user_id})
|
||||
|
||||
_ ->
|
||||
Logger.debug("character already has user_id")
|
||||
end
|
||||
def maybe_update_character_user_id(character, user_id) when not is_nil(user_id) do
|
||||
WandererApp.Api.Character.assign_user!(character, %{user_id: user_id})
|
||||
end
|
||||
|
||||
def maybe_update_character_user_id(_character, _user_id), do: :ok
|
||||
end
|
||||
|
||||
15
lib/wanderer_app_web/controllers/plugs/check_api_disabled.ex
Normal file
15
lib/wanderer_app_web/controllers/plugs/check_api_disabled.ex
Normal file
@@ -0,0 +1,15 @@
|
||||
defmodule WandererAppWeb.Plugs.CheckApiDisabled do
|
||||
import Plug.Conn
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, _opts) do
|
||||
if WandererApp.Env.public_api_disabled?() do
|
||||
conn
|
||||
|> send_resp(403, "Public API is disabled")
|
||||
|> halt()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
62
lib/wanderer_app_web/controllers/plugs/check_map_api_key.ex
Normal file
62
lib/wanderer_app_web/controllers/plugs/check_map_api_key.ex
Normal file
@@ -0,0 +1,62 @@
|
||||
defmodule WandererAppWeb.Plugs.CheckMapApiKey do
|
||||
@moduledoc """
|
||||
A plug that checks the "Authorization: Bearer <token>" header
|
||||
against the map’s stored public_api_key. Halts with 401 if invalid.
|
||||
"""
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, _opts) do
|
||||
header = get_req_header(conn, "authorization") |> List.first()
|
||||
|
||||
case header do
|
||||
"Bearer " <> incoming_token ->
|
||||
case fetch_map_id(conn.query_params) do
|
||||
{:ok, map_id} ->
|
||||
case WandererApp.Api.Map.by_id(map_id) do
|
||||
{:ok, map} ->
|
||||
if map.public_api_key == incoming_token do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> send_resp(401, "Unauthorized (invalid token for map)")
|
||||
|> halt()
|
||||
end
|
||||
|
||||
{:error, _reason} ->
|
||||
conn
|
||||
|> send_resp(404, "Map not found")
|
||||
|> halt()
|
||||
end
|
||||
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> send_resp(400, msg)
|
||||
|> halt()
|
||||
end
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> send_resp(401, "Missing or invalid 'Bearer' token")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_map_id(%{"map_id" => mid}) when is_binary(mid) and mid != "" do
|
||||
{:ok, mid}
|
||||
end
|
||||
|
||||
defp fetch_map_id(%{"slug" => slug}) when is_binary(slug) and slug != "" do
|
||||
case WandererApp.Api.Map.get_map_by_slug(slug) do
|
||||
{:ok, map} ->
|
||||
{:ok, map.id}
|
||||
|
||||
{:error, _reason} ->
|
||||
{:error, "No map found for slug=#{slug}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_map_id(_), do: {:error, "Must provide either ?map_id=UUID or ?slug=SLUG"}
|
||||
end
|
||||
@@ -28,6 +28,7 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(
|
||||
show_characters_add_alert: true,
|
||||
mode: :blocks,
|
||||
wallet_tracking_enabled?: WandererApp.Env.wallet_tracking_enabled?(),
|
||||
characters: characters |> Enum.sort_by(& &1.name, :asc) |> Enum.map(&map_ui_character/1),
|
||||
@@ -45,6 +46,13 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("restore_show_characters_add_alert", %{"value" => value}, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(show_characters_add_alert: value)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("authorize", form, socket) do
|
||||
track_wallet = form |> Map.get("track_wallet", false)
|
||||
|
||||
@@ -29,9 +29,45 @@
|
||||
id="characters-list"
|
||||
class="w-full h-full col-span-2 lg:col-span-1 p-4 pl-20 pb-20 overflow-auto"
|
||||
>
|
||||
<div
|
||||
:if={@show_characters_add_alert}
|
||||
role="alert"
|
||||
class="alert"
|
||||
id="characters-add-alert"
|
||||
phx-hook="ShowCharactersAddAlert"
|
||||
phx-ignore
|
||||
data-key="show_characters_add_alert"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
<span>
|
||||
All added characters will be automatically linked to your user account. You can't have same characters linked to several accounts.
|
||||
</span>
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
id="characters-add-alert-hide"
|
||||
phx-click={JS.toggle_class("hidden", to: "#characters-add-alert")}
|
||||
>
|
||||
Hide
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:if={@mode == :blocks}
|
||||
class="gap-4 grid grid-cols-1 lg:grid-cols-5 md:grid-cols-3 sm:grid-cols-2 "
|
||||
class="gap-4 grid grid-cols-1 lg:grid-cols-5 md:grid-cols-3 sm:grid-cols-2 mt-4"
|
||||
>
|
||||
<.link patch={~p"/characters/authorize"}>
|
||||
<div class="card card-side rounded-none h-full items-center hover:text-white bg-gradient-to-l from-stone-950 to-stone-900 transform transition duration-500">
|
||||
@@ -51,9 +87,24 @@
|
||||
<div class="absolute left-0 bottom-0 w-full h-30 bg-opacity-60 bg-gray-900">
|
||||
<h2 class="absolute w-full flex justify-between px-4 left-0 top-10 text-xs">
|
||||
Corporation:
|
||||
<span
|
||||
:if={
|
||||
is_nil(
|
||||
character
|
||||
|> get(
|
||||
path(:corporation_name),
|
||||
nil
|
||||
)
|
||||
)
|
||||
}
|
||||
class="loading loading-dots loading-xs"
|
||||
/>
|
||||
<span>
|
||||
<%= character
|
||||
|> get(path(:corporation_name), "-") %>
|
||||
|> get(
|
||||
path(:corporation_name),
|
||||
""
|
||||
) %>
|
||||
</span>
|
||||
</h2>
|
||||
<h2 class="absolute w-full flex justify-between px-4 left-0 top-16 text-xs">
|
||||
@@ -65,9 +116,21 @@
|
||||
</h2>
|
||||
<h2 class="absolute left-0 bottom-12 px-4 text-xs w-full flex justify-between">
|
||||
Location:
|
||||
<span
|
||||
:if={
|
||||
is_nil(
|
||||
character
|
||||
|> get(
|
||||
path(:location / :solar_system_info / :solar_system_name, :map),
|
||||
nil
|
||||
)
|
||||
)
|
||||
}
|
||||
class="loading loading-dots loading-xs"
|
||||
/>
|
||||
<span>
|
||||
<%= character
|
||||
|> get(path(:location / :solar_system_info / :solar_system_name, :map), "-") %>
|
||||
|> get(path(:location / :solar_system_info / :solar_system_name, :map), "") %>
|
||||
</span>
|
||||
</h2>
|
||||
<h2
|
||||
@@ -126,7 +189,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :if={@mode == :table} class="flex flex-col gap-4">
|
||||
<div :if={@mode == :table} class="flex flex-col gap-4 mt-4">
|
||||
<.link patch={~p"/characters/authorize"}>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -86,7 +86,8 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: selected_map.id,
|
||||
tracked: true
|
||||
tracked: true,
|
||||
followed: false
|
||||
})
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
@@ -18,15 +18,12 @@ defmodule WandererAppWeb.MapActivityEventHandler do
|
||||
do: MapCoreEventHandler.handle_server_event(event, socket)
|
||||
|
||||
def handle_ui_event("show_activity", _, %{assigns: %{map_id: map_id}} = socket) do
|
||||
Task.async(fn ->
|
||||
{:ok, character_activity} = map_id |> get_character_activity()
|
||||
|
||||
{:character_activity, character_activity}
|
||||
end)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:show_activity?, true)}
|
||||
|> assign(:show_activity?, true)
|
||||
|> assign_async(:character_activity, fn ->
|
||||
map_id |> get_character_activity()
|
||||
end)}
|
||||
end
|
||||
|
||||
def handle_ui_event("hide_activity", _, socket),
|
||||
@@ -44,6 +41,6 @@ defmodule WandererAppWeb.MapActivityEventHandler do
|
||||
%{p | character: p.character |> MapEventHandler.map_ui_character_stat()}
|
||||
end)
|
||||
|
||||
{:ok, %{jumps: jumps}}
|
||||
{:ok, %{character_activity: jumps}}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,35 +66,9 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
def handle_ui_event(
|
||||
"add_character",
|
||||
_,
|
||||
%{
|
||||
assigns: %{
|
||||
current_user: current_user,
|
||||
map_id: map_id,
|
||||
user_permissions: %{track_character: true}
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(show_tracking?: true)
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, map} =
|
||||
map_id
|
||||
|> WandererApp.MapRepo.get([:acls])
|
||||
|
||||
{:ok, character_settings} =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do
|
||||
{:ok, settings} -> {:ok, settings}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
|
||||
map
|
||||
|> WandererApp.Maps.load_characters(
|
||||
character_settings,
|
||||
current_user.id
|
||||
)
|
||||
end)}
|
||||
end
|
||||
socket
|
||||
),
|
||||
do: {:noreply, socket |> add_character()}
|
||||
|
||||
def handle_ui_event(
|
||||
"add_character",
|
||||
@@ -137,7 +111,8 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: true
|
||||
tracked: true,
|
||||
followed: false
|
||||
})
|
||||
|
||||
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
|
||||
@@ -216,12 +191,128 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"toggle_follow",
|
||||
%{"character-id" => clicked_char_id},
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id,
|
||||
current_user: current_user
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
{:ok, all_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
# Find and filter user's characters
|
||||
{:ok, user_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
user_char_ids = Enum.map(user_characters, & &1.id)
|
||||
|
||||
my_settings =
|
||||
all_settings
|
||||
|> Enum.filter(fn s ->
|
||||
s.character_id in user_char_ids
|
||||
end)
|
||||
|
||||
existing = Enum.find(my_settings, &(&1.character_id == clicked_char_id))
|
||||
|
||||
{:ok, target_setting} =
|
||||
if not is_nil(existing) do
|
||||
{:ok, existing}
|
||||
else
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: clicked_char_id,
|
||||
map_id: map_id,
|
||||
tracked: true,
|
||||
followed: true
|
||||
})
|
||||
end
|
||||
|
||||
# If the target_setting is already followed => unfollow it
|
||||
if target_setting.followed do
|
||||
{:ok, updated} = WandererApp.MapCharacterSettingsRepo.unfollow(target_setting)
|
||||
else
|
||||
# Only unfollow other rows from the current user
|
||||
for s <- my_settings, s.id != target_setting.id, s.followed == true do
|
||||
WandererApp.MapCharacterSettingsRepo.unfollow!(s)
|
||||
end
|
||||
|
||||
# Ensure the new followed char is tracked
|
||||
if not target_setting.tracked do
|
||||
WandererApp.MapCharacterSettingsRepo.track!(target_setting)
|
||||
|
||||
char = target_setting |> Ash.load!(:character) |> Map.get(:character)
|
||||
:ok = track_characters([char], map_id, true)
|
||||
:ok = add_characters([char], map_id, true)
|
||||
end
|
||||
|
||||
{:ok, updated} = WandererApp.MapCharacterSettingsRepo.follow(target_setting)
|
||||
end
|
||||
|
||||
# re-fetch or re-map to confirm final results in UI
|
||||
%{result: characters} = socket.assigns.characters
|
||||
|
||||
{:ok, tracked_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
user_eve_ids = Enum.map(tracked_characters, & &1.eve_id)
|
||||
|
||||
{:ok, final_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
updated_chars =
|
||||
characters
|
||||
|> Enum.map(fn c ->
|
||||
s = Enum.find(final_settings, &(&1.character_id == c.id))
|
||||
WandererApp.Maps.map_character(c, s)
|
||||
end)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(user_characters: user_eve_ids)
|
||||
|> assign(has_tracked_characters?: has_tracked_characters?(user_eve_ids))
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, %{characters: updated_chars}}
|
||||
end)
|
||||
|> MapEventHandler.push_map_event("init", %{user_characters: user_eve_ids, reset: false})
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event("hide_tracking", _, socket),
|
||||
do: {:noreply, socket |> assign(show_tracking?: false)}
|
||||
|
||||
def handle_ui_event(event, body, socket),
|
||||
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
|
||||
|
||||
def add_character(
|
||||
%{
|
||||
assigns: %{
|
||||
current_user: current_user,
|
||||
map_id: map_id,
|
||||
user_permissions: %{track_character: true}
|
||||
}
|
||||
} = socket
|
||||
),
|
||||
do:
|
||||
socket
|
||||
|> assign(show_tracking?: true)
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, map} =
|
||||
map_id
|
||||
|> WandererApp.MapRepo.get([:acls])
|
||||
|
||||
{:ok, character_settings} =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do
|
||||
{:ok, settings} -> {:ok, settings}
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
|
||||
map
|
||||
|> WandererApp.Maps.load_characters(
|
||||
character_settings,
|
||||
current_user.id
|
||||
)
|
||||
end)
|
||||
|
||||
def add_character(socket), do: socket
|
||||
|
||||
def has_tracked_characters?([]), do: false
|
||||
def has_tracked_characters?(_user_characters), do: true
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
use Phoenix.Component
|
||||
require Logger
|
||||
|
||||
alias WandererAppWeb.{MapEventHandler, MapCharactersEventHandler}
|
||||
alias WandererAppWeb.{MapEventHandler, MapCharactersEventHandler, MapSystemsEventHandler}
|
||||
|
||||
def handle_server_event(:update_permissions, socket) do
|
||||
DebounceAndThrottle.Debounce.apply(
|
||||
@@ -147,7 +147,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
options =
|
||||
WandererApp.Api.MapSolarSystem.find_by_name!(%{name: text})
|
||||
|> Enum.take(100)
|
||||
|> Enum.map(&map_system/1)
|
||||
|> Enum.map(&MapSystemsEventHandler.map_system/1)
|
||||
|
||||
send_update(LiveSelect.Component, options: options, id: id)
|
||||
|
||||
@@ -162,6 +162,14 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event("toggle_follow_" <> character_id, _, socket),
|
||||
do:
|
||||
MapCharactersEventHandler.handle_ui_event(
|
||||
"toggle_follow",
|
||||
%{"character-id" => character_id},
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event(
|
||||
"get_user_settings",
|
||||
_,
|
||||
@@ -221,11 +229,12 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
|> put_flash(
|
||||
:error,
|
||||
"You should enable tracking for at least one character."
|
||||
)}
|
||||
"You should enable tracking for at least one character!"
|
||||
)
|
||||
|> MapCharactersEventHandler.add_character()}
|
||||
|
||||
def handle_ui_event(event, body, socket) do
|
||||
Logger.warning(fn -> "unhandled map ui event: #{event} #{inspect(body)}" end)
|
||||
Logger.warning(fn -> "unhandled map ui event: #{inspect(event)} #{inspect(body)}" end)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@@ -448,23 +457,35 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
|> filter_map_characters(user_character_eve_ids, user_permissions, options)
|
||||
|> Enum.map(&MapCharactersEventHandler.map_ui_character/1)
|
||||
|
||||
socket
|
||||
|> assign(
|
||||
map_loaded?: true,
|
||||
map_user_settings: map_user_settings,
|
||||
user_characters: user_character_eve_ids,
|
||||
has_tracked_characters?:
|
||||
MapCharactersEventHandler.has_tracked_characters?(user_character_eve_ids)
|
||||
)
|
||||
|> MapEventHandler.push_map_event(
|
||||
"init",
|
||||
initial_data
|
||||
|> Map.put(:characters, map_characters)
|
||||
)
|
||||
|> push_event("js-exec", %{
|
||||
to: "#map-loader",
|
||||
attr: "data-loaded"
|
||||
})
|
||||
has_tracked_characters? =
|
||||
MapCharactersEventHandler.has_tracked_characters?(user_character_eve_ids)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(
|
||||
map_loaded?: true,
|
||||
map_user_settings: map_user_settings,
|
||||
user_characters: user_character_eve_ids,
|
||||
has_tracked_characters?: has_tracked_characters?
|
||||
)
|
||||
|> MapEventHandler.push_map_event(
|
||||
"init",
|
||||
initial_data
|
||||
|> Map.put(:characters, map_characters)
|
||||
)
|
||||
|> push_event("js-exec", %{
|
||||
to: "#map-loader",
|
||||
attr: "data-loaded"
|
||||
})
|
||||
|
||||
case not has_tracked_characters? && user_permissions.track_character do
|
||||
true ->
|
||||
socket
|
||||
|> MapCharactersEventHandler.add_character()
|
||||
|
||||
_ ->
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_map_start_events(socket, map_id, events) do
|
||||
@@ -487,10 +508,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
|
||||
:empty_tracked_characters ->
|
||||
socket
|
||||
|> put_flash(
|
||||
:info,
|
||||
"You should enable tracking for at least one character to work with map."
|
||||
)
|
||||
|
||||
:map_character_limit ->
|
||||
socket
|
||||
@@ -555,21 +572,4 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
user_character_eve_ids |> Enum.member?(character.eve_id)
|
||||
end)
|
||||
end
|
||||
|
||||
defp map_system(
|
||||
%{
|
||||
solar_system_name: solar_system_name,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
solar_system_id: solar_system_id,
|
||||
class_title: class_title
|
||||
} = _system
|
||||
),
|
||||
do: %{
|
||||
label: solar_system_name,
|
||||
value: solar_system_id,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
class_title: class_title
|
||||
}
|
||||
end
|
||||
|
||||
@@ -21,32 +21,57 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
|> MapEventHandler.push_map_event("remove_systems", solar_system_ids)
|
||||
|
||||
def handle_server_event(
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: character_id,
|
||||
solar_system_id: solar_system_id
|
||||
}
|
||||
},
|
||||
%{assigns: %{current_user: current_user, map_user_settings: map_user_settings}} = socket
|
||||
) do
|
||||
is_user_character =
|
||||
current_user.characters |> Enum.map(& &1.id) |> Enum.member?(character_id)
|
||||
%{
|
||||
event: :maybe_select_system,
|
||||
payload: %{
|
||||
character_id: character_id,
|
||||
solar_system_id: solar_system_id
|
||||
}
|
||||
},
|
||||
%{assigns: %{current_user: current_user, map_id: map_id, map_user_settings: map_user_settings}} = socket
|
||||
) do
|
||||
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
is_user_character =
|
||||
current_user.characters
|
||||
|> Enum.map(& &1.id)
|
||||
|> Enum.member?(character_id)
|
||||
|
||||
(is_user_character && is_select_on_spash)
|
||||
|> case do
|
||||
true ->
|
||||
is_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
|
||||
is_followed =
|
||||
case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character_id) do
|
||||
{:ok, setting} -> setting.followed == true
|
||||
_ -> false
|
||||
end
|
||||
|
||||
must_select? = is_user_character && (is_select_on_spash || is_followed)
|
||||
if not must_select? do
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
else
|
||||
# Check if we already selected this exact system for this char:
|
||||
last_selected =
|
||||
WandererApp.Cache.lookup!(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
nil
|
||||
)
|
||||
|
||||
false ->
|
||||
socket
|
||||
end
|
||||
if last_selected == solar_system_id do
|
||||
# same system => skip
|
||||
socket
|
||||
else
|
||||
# new system => update cache + push event
|
||||
WandererApp.Cache.put(
|
||||
"char:#{character_id}:map:#{map_id}:last_selected_system_id",
|
||||
solar_system_id
|
||||
)
|
||||
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_server_event(%{event: :kills_updated, payload: kills}, socket) do
|
||||
@@ -65,55 +90,32 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
do: MapCoreEventHandler.handle_server_event(event, socket)
|
||||
|
||||
def handle_ui_event(
|
||||
"add_system",
|
||||
%{"system_id" => [solar_system_id]} = _event,
|
||||
"manual_add_system",
|
||||
%{"solar_system_id" => solar_system_id, "coordinates" => coordinates} = _event,
|
||||
%{
|
||||
assigns:
|
||||
%{
|
||||
map_id: map_id,
|
||||
map_slug: map_slug,
|
||||
current_user: current_user,
|
||||
tracked_character_ids: tracked_character_ids,
|
||||
user_permissions: %{add_system: true}
|
||||
} = assigns
|
||||
} = socket
|
||||
)
|
||||
when is_binary(solar_system_id) and solar_system_id != "" do
|
||||
coordinates = Map.get(assigns, :coordinates)
|
||||
|
||||
assigns: %{
|
||||
current_user: current_user,
|
||||
has_tracked_characters?: true,
|
||||
map_id: map_id,
|
||||
tracked_character_ids: tracked_character_ids,
|
||||
user_permissions: %{add_system: true}
|
||||
}
|
||||
} =
|
||||
socket
|
||||
) do
|
||||
WandererApp.Map.Server.add_system(
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: solar_system_id |> String.to_integer(),
|
||||
solar_system_id: solar_system_id,
|
||||
coordinates: coordinates
|
||||
},
|
||||
current_user.id,
|
||||
tracked_character_ids |> List.first()
|
||||
)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> push_patch(to: ~p"/#{map_slug}")}
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"manual_add_system",
|
||||
%{"coordinates" => coordinates} = _event,
|
||||
%{
|
||||
assigns: %{
|
||||
has_tracked_characters?: true,
|
||||
map_slug: map_slug,
|
||||
user_permissions: %{add_system: true}
|
||||
}
|
||||
} =
|
||||
socket
|
||||
),
|
||||
do:
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(coordinates: coordinates)
|
||||
|> push_patch(to: ~p"/#{map_slug}/add-system")}
|
||||
|
||||
def handle_ui_event(
|
||||
"add_hub",
|
||||
%{"system_id" => solar_system_id} = _event,
|
||||
@@ -229,6 +231,7 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
"labels" -> :update_system_labels
|
||||
"locked" -> :update_system_locked
|
||||
"tag" -> :update_system_tag
|
||||
"temporary_name" -> :update_system_temporary_name
|
||||
"status" -> :update_system_status
|
||||
_ -> nil
|
||||
end
|
||||
@@ -240,6 +243,7 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
"labels" -> :labels
|
||||
"locked" -> :locked
|
||||
"tag" -> :tag
|
||||
"temporary_name" -> :temporary_name
|
||||
"status" -> :status
|
||||
_ -> :none
|
||||
end
|
||||
@@ -280,6 +284,25 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
{:reply, %{system_static_infos: system_static_infos}, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"search_systems",
|
||||
%{"text" => text} = _event,
|
||||
socket
|
||||
) do
|
||||
systems =
|
||||
WandererApp.Api.MapSolarSystem.find_by_name!(%{name: text})
|
||||
|> Enum.take(100)
|
||||
|> Enum.map(&map_system/1)
|
||||
|> Enum.filter(fn system ->
|
||||
not is_nil(system) && not is_nil(system.system_static_info) &&
|
||||
not WandererApp.Map.Server.ConnectionsImpl.is_prohibited_system_class?(
|
||||
system.system_static_info.system_class
|
||||
)
|
||||
end)
|
||||
|
||||
{:reply, %{systems: systems}, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"delete_systems",
|
||||
solar_system_ids,
|
||||
@@ -307,6 +330,27 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
def handle_ui_event(event, body, socket),
|
||||
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
|
||||
|
||||
def map_system(
|
||||
%{
|
||||
solar_system_name: solar_system_name,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
solar_system_id: solar_system_id,
|
||||
class_title: class_title
|
||||
} = _system
|
||||
) do
|
||||
system_static_info = MapEventHandler.get_system_static_info(solar_system_id)
|
||||
|
||||
%{
|
||||
label: solar_system_name,
|
||||
value: solar_system_id,
|
||||
constellation_name: constellation_name,
|
||||
region_name: region_name,
|
||||
class_title: class_title,
|
||||
system_static_info: system_static_info
|
||||
}
|
||||
end
|
||||
|
||||
defp can_update_system?(:locked, %{lock_system: false} = _user_permissions), do: false
|
||||
defp can_update_system?(_key, _user_permissions), do: true
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
@map_characters_ui_events [
|
||||
"add_character",
|
||||
"toggle_track",
|
||||
"toggle_follow",
|
||||
"hide_tracking"
|
||||
]
|
||||
|
||||
@@ -38,10 +39,10 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
@map_system_ui_events [
|
||||
"add_hub",
|
||||
"delete_hub",
|
||||
"add_system",
|
||||
"delete_systems",
|
||||
"manual_add_system",
|
||||
"get_system_static_infos",
|
||||
"manual_add_system",
|
||||
"search_systems",
|
||||
"update_system_position",
|
||||
"update_system_positions",
|
||||
"update_system_name",
|
||||
@@ -49,6 +50,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
"update_system_labels",
|
||||
"update_system_locked",
|
||||
"update_system_tag",
|
||||
"update_system_temporary_name",
|
||||
"update_system_status"
|
||||
]
|
||||
|
||||
@@ -243,6 +245,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
locked: locked,
|
||||
tag: tag,
|
||||
labels: labels,
|
||||
temporary_name: temporary_name,
|
||||
status: status,
|
||||
visible: visible
|
||||
} = _system,
|
||||
@@ -268,6 +271,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
locked: locked,
|
||||
status: status,
|
||||
tag: tag,
|
||||
temporary_name: temporary_name,
|
||||
visible: visible
|
||||
}
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ defmodule WandererAppWeb.MapLive do
|
||||
|
||||
@impl true
|
||||
def mount(%{"slug" => map_slug} = _params, _session, socket) when is_connected?(socket) do
|
||||
Process.send_after(self(), %{event: :load_map}, Enum.random(10..500))
|
||||
Process.send_after(self(), %{event: :load_map}, Enum.random(10..800))
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
@@ -76,13 +76,16 @@ defmodule WandererAppWeb.MapLive do
|
||||
|
||||
def handle_info(:not_all_characters_tracked, %{assigns: %{map_slug: map_slug}} = socket),
|
||||
do:
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(
|
||||
:error,
|
||||
"You should enable tracking for all characters that have access to this map first!"
|
||||
)
|
||||
|> push_navigate(to: ~p"/tracking/#{map_slug}")}
|
||||
WandererAppWeb.MapEventHandler.handle_ui_event(
|
||||
"add_character",
|
||||
nil,
|
||||
socket
|
||||
|> put_flash(
|
||||
:error,
|
||||
"You should enable tracking for all characters that have access to this map first!"
|
||||
)
|
||||
|> push_navigate(to: ~p"/tracking/#{map_slug}")
|
||||
)
|
||||
|
||||
@impl true
|
||||
def handle_info(info, socket),
|
||||
@@ -99,24 +102,4 @@ defmodule WandererAppWeb.MapLive do
|
||||
socket
|
||||
|> assign(:active_page, :map)
|
||||
end
|
||||
|
||||
defp apply_action(socket, :add_system, _params) do
|
||||
socket
|
||||
|> assign(:active_page, :map)
|
||||
|> assign(:page_title, "Add System")
|
||||
|> assign(:add_system_form, to_form(%{"system_id" => nil}))
|
||||
end
|
||||
|
||||
def character_item(assigns) do
|
||||
~H"""
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="avatar">
|
||||
<div class="rounded-md w-12 h-12">
|
||||
<img src={member_icon_url(@character.eve_id)} alt={@character.name} />
|
||||
</div>
|
||||
</div>
|
||||
<%= @character.name %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,41 +31,6 @@
|
||||
</.link>
|
||||
</div>
|
||||
|
||||
<.modal
|
||||
:if={@live_action in [:add_system] && not is_nil(assigns |> Map.get(:map_slug)) && @map_loaded?}
|
||||
id="add-system-modal"
|
||||
class="!w-[400px]"
|
||||
title="Add System"
|
||||
show
|
||||
on_cancel={JS.patch(~p"/#{@map_slug}")}
|
||||
>
|
||||
<.form :let={f} for={@add_system_form} phx-submit="add_system">
|
||||
<.live_select
|
||||
label="Search system"
|
||||
field={f[:system_id]}
|
||||
update_min_len={2}
|
||||
available_option_class="w-full text-sm"
|
||||
debounce={200}
|
||||
mode={:tags}
|
||||
>
|
||||
<:option :let={option}>
|
||||
<div class="gap-1 w-full flex flex-align-center p-autocomplete-item text-sm">
|
||||
<div class="eve-wh-type-color-c1 text-gray-400 w-8"><%= option.class_title %></div>
|
||||
<div class="text-white w-16"><%= option.label %></div>
|
||||
<div class="text-gray-600 w-20"><%= option.constellation_name %></div>
|
||||
<div class="text-gray-600"><%= option.region_name %></div>
|
||||
</div>
|
||||
</:option>
|
||||
</.live_select>
|
||||
<div class="mt-2 bg-neutral text-neutral-content rounded-md p-1 text-xs w-full">
|
||||
* Start search system. You should type at least 2 symbols.
|
||||
</div>
|
||||
<div class="modal-action mt-0">
|
||||
<.button class="mt-2" type="submit">Add</.button>
|
||||
</div>
|
||||
</.form>
|
||||
</.modal>
|
||||
|
||||
<.modal
|
||||
:if={assigns |> Map.get(:show_activity?, false)}
|
||||
id="map-activity-modal"
|
||||
@@ -74,31 +39,35 @@
|
||||
show
|
||||
on_cancel={JS.push("hide_activity")}
|
||||
>
|
||||
<.table
|
||||
:if={not (assigns |> Map.get(:character_activity) |> is_nil())}
|
||||
class="!max-h-[80vh] !overflow-y-auto"
|
||||
id="activity-tbl"
|
||||
rows={@character_activity.jumps}
|
||||
>
|
||||
<:col :let={activity} label="Character">
|
||||
<.character_item character={activity.character} />
|
||||
</:col>
|
||||
<:col :let={activity} label="Passages">
|
||||
<%= activity.count %>
|
||||
</:col>
|
||||
</.table>
|
||||
<.async_result :let={character_activity} assign={@character_activity}>
|
||||
<:loading>Loading...</:loading>
|
||||
<:failed :let={reason}><%= reason %></:failed>
|
||||
|
||||
<span :if={character_activity}>
|
||||
<.live_component
|
||||
module={WandererAppWeb.CharacterActivity}
|
||||
id="character-activity"
|
||||
activity={character_activity}
|
||||
notify_to={self()}
|
||||
/>
|
||||
</span>
|
||||
</.async_result>
|
||||
</.modal>
|
||||
|
||||
<.modal
|
||||
:if={assigns |> Map.get(:show_tracking?, false)}
|
||||
id="map-tracking-modal"
|
||||
title="Characters tracking"
|
||||
title="Track and Follow Characters"
|
||||
show
|
||||
on_cancel={JS.push("hide_tracking")}
|
||||
>
|
||||
<.async_result :let={characters} assign={@characters}>
|
||||
<:loading><span class="loading loading-dots loading-xs" />.</:loading>
|
||||
<:failed :let={reason}><%= reason %></:failed>
|
||||
<:loading>
|
||||
<span class="loading loading-dots loading-xs" />
|
||||
</:loading>
|
||||
<:failed :let={reason}>
|
||||
<%= reason %>
|
||||
</:failed>
|
||||
|
||||
<.table
|
||||
:if={characters}
|
||||
@@ -106,8 +75,8 @@
|
||||
class="h-[400px] !overflow-y-auto"
|
||||
rows={characters}
|
||||
>
|
||||
<:col :let={character} label="Tracked">
|
||||
<label class="flex items-center gap-3">
|
||||
<:col :let={character} label="Track">
|
||||
<label class="flex items-center gap-2 justify-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
@@ -116,17 +85,34 @@
|
||||
id={"character-track-#{character.id}"}
|
||||
checked={character.tracked}
|
||||
/>
|
||||
<div class="flex items-center gap-3">
|
||||
<.avatar url={member_icon_url(character.eve_id)} label={character.name} />
|
||||
<div>
|
||||
<div class="font-bold">
|
||||
<%= character.name %><span class="ml-1 text-gray-400">[<%= character.corporation_ticker %>]</span>
|
||||
</div>
|
||||
<div class="text-sm opacity-50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</:col>
|
||||
<:col :let={character} label="Follow">
|
||||
<label class="flex items-center gap-2 justify-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="followed_character"
|
||||
class="radio"
|
||||
phx-click="toggle_follow"
|
||||
phx-value-character-id={character.id}
|
||||
checked={Map.get(character, :followed, false)}
|
||||
/>
|
||||
</label>
|
||||
</:col>
|
||||
<:col :let={character} label="Character">
|
||||
<div class="flex items-center gap-3">
|
||||
<.avatar url={member_icon_url(character.eve_id)} label={character.name} />
|
||||
<div>
|
||||
<div class="font-bold">
|
||||
<%= character.name %>
|
||||
<span class="ml-1 text-gray-400">
|
||||
[<%= character.corporation_ticker %>]
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-sm opacity-50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</:col>
|
||||
</.table>
|
||||
</.async_result>
|
||||
</.modal>
|
||||
|
||||
@@ -127,6 +127,7 @@ defmodule WandererAppWeb.MapsLive do
|
||||
|> assign(:page_title, "Maps - Settings")
|
||||
|> assign(:map_slug, map_slug)
|
||||
|> assign(:map_id, map.id)
|
||||
|> assign(:public_api_key, map.public_api_key)
|
||||
|> assign(:map, map)
|
||||
|> assign(
|
||||
export_settings: export_settings |> _get_export_map_data(),
|
||||
@@ -179,6 +180,19 @@ defmodule WandererAppWeb.MapsLive do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
||||
def handle_event("generate-map-api-key", _params, socket) do
|
||||
new_api_key = UUID.uuid4()
|
||||
|
||||
map = WandererApp.Api.Map.by_id!(socket.assigns.map_id)
|
||||
|
||||
{:ok, _updated_map} =
|
||||
WandererApp.Api.Map.update_api_key(map, %{public_api_key: new_api_key})
|
||||
|
||||
{:noreply, assign(socket, public_api_key: new_api_key)}
|
||||
end
|
||||
|
||||
|
||||
@impl true
|
||||
def handle_event(
|
||||
"live_select_change",
|
||||
@@ -203,7 +217,10 @@ defmodule WandererAppWeb.MapsLive do
|
||||
socket.assigns.form,
|
||||
form
|
||||
|> Map.put("acls", form["acls"] || [])
|
||||
|> Map.put("only_tracked_characters", form["only_tracked_characters"] || false)
|
||||
|> Map.put(
|
||||
"only_tracked_characters",
|
||||
(form["only_tracked_characters"] || "false") |> String.to_existing_atom()
|
||||
)
|
||||
)
|
||||
|
||||
{:noreply, socket |> assign(form: form)}
|
||||
@@ -593,7 +610,13 @@ defmodule WandererAppWeb.MapsLive do
|
||||
scope -> scope
|
||||
end
|
||||
|
||||
form = form |> Map.put("scope", scope)
|
||||
form =
|
||||
form
|
||||
|> Map.put("scope", scope)
|
||||
|> Map.put(
|
||||
"only_tracked_characters",
|
||||
(form["only_tracked_characters"] || "false") |> String.to_existing_atom()
|
||||
)
|
||||
|
||||
map
|
||||
|> WandererApp.Api.Map.update(form)
|
||||
@@ -659,7 +682,12 @@ defmodule WandererAppWeb.MapsLive do
|
||||
) do
|
||||
options =
|
||||
options_form
|
||||
|> Map.take(["layout", "store_custom_labels", "restrict_offline_showing"])
|
||||
|> Map.take([
|
||||
"layout",
|
||||
"store_custom_labels",
|
||||
"show_temp_system_name",
|
||||
"restrict_offline_showing"
|
||||
])
|
||||
|
||||
{:ok, updated_map} = WandererApp.MapRepo.update_options(map, options)
|
||||
|
||||
|
||||
@@ -313,6 +313,32 @@
|
||||
style="width: 146px; left: 0px;"
|
||||
>
|
||||
</li>
|
||||
<li
|
||||
:if={not WandererApp.Env.public_api_disabled?()}
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes("p-tabview-selected p-highlight": @active_settings_tab == "public_api")
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_335_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_settings_tab"
|
||||
phx-value-tab="public_api"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-globe-alt-solid" class="w-4 h-4" /> Public Api
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -345,6 +371,11 @@
|
||||
field={f[:store_custom_labels]}
|
||||
label="Store system custom labels"
|
||||
/>
|
||||
<.input
|
||||
type="checkbox"
|
||||
field={f[:show_temp_system_name]}
|
||||
label="Allow Temporary System Names"
|
||||
/>
|
||||
<.input
|
||||
type="checkbox"
|
||||
field={f[:restrict_offline_showing]}
|
||||
@@ -377,6 +408,52 @@
|
||||
</.button>
|
||||
</div>
|
||||
|
||||
<div :if={@active_settings_tab == "public_api"and not WandererApp.Env.public_api_disabled?()} class="p-6">
|
||||
<h2 class="text-lg font-semibold mb-4">Public API</h2>
|
||||
<div class="flex flex-col gap-3 items-start w-full">
|
||||
<div>
|
||||
<input
|
||||
:if={not is_nil(@public_api_key)}
|
||||
class="input input-bordered text-sm truncate bg-neutral-800 text-white w-[350px]"
|
||||
readonly
|
||||
type="text"
|
||||
value={@public_api_key}
|
||||
/>
|
||||
<input
|
||||
:if={is_nil(@public_api_key)}
|
||||
class="input input-bordered text-sm truncate bg-neutral-800 text-gray-400 w-[350px]"
|
||||
readonly
|
||||
type="text"
|
||||
placeholder="No Public API Key yet"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<.button
|
||||
class="btn btn-primary rounded-md"
|
||||
phx-click="generate-map-api-key"
|
||||
>
|
||||
Generate
|
||||
</.button>
|
||||
<.button
|
||||
phx-hook="CopyToClipboard"
|
||||
id="copy-map-api-key"
|
||||
data-url={@public_api_key}
|
||||
disabled={is_nil(@public_api_key)}
|
||||
class={
|
||||
if is_nil(@public_api_key) do
|
||||
"copy-link btn rounded-md transition-colors duration-300
|
||||
bg-gray-500 hover:bg-gray-500 text-gray-300 cursor-not-allowed"
|
||||
else
|
||||
"copy-link btn rounded-md transition-colors duration-300
|
||||
bg-blue-600 hover:bg-blue-700 text-white cursor-pointer"
|
||||
end
|
||||
}
|
||||
>
|
||||
Copy
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :if={@active_settings_tab == "balance"}>
|
||||
<div class="stats w-full bg-primary text-primary-content">
|
||||
<div class="stat">
|
||||
|
||||
@@ -21,9 +21,10 @@ defmodule WandererAppWeb.Router do
|
||||
@frame_src if(@code_reloading, do: ~w('self'), else: ~w())
|
||||
@style_src ~w('self' 'unsafe-inline' https://fonts.googleapis.com)
|
||||
@img_src ~w('self' data: https://images.evetech.net https://web.ccpgamescdn.com https://images.ctfassets.net https://w.appzi.io)
|
||||
@font_src ~w('self' data: https://web.ccpgamescdn.com https://w.appzi.io)
|
||||
@font_src ~w('self' https://fonts.gstatic.com data: https://web.ccpgamescdn.com https://w.appzi.io )
|
||||
@script_src ~w('self' )
|
||||
|
||||
|
||||
pipeline :admin_bauth do
|
||||
plug :admin_basic_auth
|
||||
end
|
||||
@@ -106,8 +107,38 @@ defmodule WandererAppWeb.Router do
|
||||
|
||||
pipeline :api do
|
||||
plug(:accepts, ["json"])
|
||||
plug WandererAppWeb.Plugs.CheckApiDisabled
|
||||
end
|
||||
|
||||
pipeline :api_map do
|
||||
plug WandererAppWeb.Plugs.CheckMapApiKey
|
||||
end
|
||||
|
||||
scope "/api/map", WandererAppWeb do
|
||||
pipe_through [:api_map]
|
||||
pipe_through [:api]
|
||||
|
||||
# GET /api/map/systems?map_id=... or ?slug=...
|
||||
get "/systems", APIController, :list_systems
|
||||
|
||||
# GET /api/map/system-static-info?id=... plus either map_id=... or slug=...
|
||||
get "/system-static-info", APIController, :show_system_static
|
||||
|
||||
# GET /api/map/system?id=... plus either map_id=... or slug=...
|
||||
get "/system", APIController, :show_system
|
||||
|
||||
# GET /api/map/characters?map_id=... or slug=...
|
||||
get "/characters", APIController, :tracked_characters_with_info
|
||||
end
|
||||
|
||||
scope "/api/common", WandererAppWeb do
|
||||
pipe_through [:api]
|
||||
|
||||
# GET /api/common/system-static-info?id=...
|
||||
get "/system-static-info", APIController, :show_system_static
|
||||
|
||||
end
|
||||
|
||||
scope "/", WandererAppWeb do
|
||||
pipe_through [:browser, :blog, :redirect_if_user_is_authenticated]
|
||||
|
||||
@@ -197,7 +228,6 @@ defmodule WandererAppWeb.Router do
|
||||
live("/profile/deposit", ProfileLive, :deposit)
|
||||
live("/profile/subscribe", ProfileLive, :subscribe)
|
||||
live("/:slug/audit", MapAuditLive, :index)
|
||||
live("/:slug/add-system", MapLive, :add_system)
|
||||
live("/:slug", MapLive, :index)
|
||||
end
|
||||
end
|
||||
|
||||
3
mix.exs
3
mix.exs
@@ -2,7 +2,8 @@ defmodule WandererApp.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
@version "1.29.5"
|
||||
|
||||
@version "1.37.1"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
209
priv/posts/2025/01-05-map-public-api.md
Normal file
209
priv/posts/2025/01-05-map-public-api.md
Normal file
@@ -0,0 +1,209 @@
|
||||
%{
|
||||
title: "User Guide: Public API Endpoints for Map Data",
|
||||
author: "Wanderer Team",
|
||||
cover_image_uri: "/images/news/01-05-map-public-api/generate-key.png",
|
||||
tags: ~w(map public-api guide interface),
|
||||
description: "Learn how to use the Wanderer public API endpoints to retrieve system and character data from your map. This guide covers available endpoints, request examples, and sample responses."
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
As part of the Wanderer platform, a public API has been introduced to help users programmatically retrieve map data, such as system information and character tracking details. This guide explains how to use these endpoints, how to authenticate with the API, and what data to expect in the responses.
|
||||
|
||||
**Important:** To use these endpoints, you need a valid API key for the map in question. You can generate or copy this key from within the **Map Settings** modal in the app:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Each request to the Wanderer APIs that being with /api/map must include a valid API key in the `Authorization` header. The format is:
|
||||
|
||||
Authorization: Bearer <YOUR_MAP_API_KEY>
|
||||
|
||||
If the API key is missing or incorrect, you’ll receive a `401 Unauthorized` response.
|
||||
|
||||
No api key is required for routes that being with /api/common
|
||||
|
||||
---
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
### 1. List Systems
|
||||
|
||||
GET /api/map/systems?map_id=<UUID>
|
||||
GET /api/map/systems?slug=<map-slug>
|
||||
|
||||
- **Description:** Retrieves a list of systems associated with the specified map (by `map_id` or `slug`).
|
||||
- **Authentication:** Required via `Authorization` header.
|
||||
- **Parameters:**
|
||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
||||
- `slug` (optional if `map_id` is provided) — the slug identifier of the map.
|
||||
- `all=true` (optional) — if set, returns *all* systems instead of only "visible" systems.
|
||||
|
||||
#### Example Request
|
||||
```
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" "https://wanderer.example.com/api/map/systems?slug=some-slug"
|
||||
```
|
||||
#### Example Response
|
||||
```
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "<REDACTED_ID>",
|
||||
"name": "<REDACTED_NAME>",
|
||||
"status": 0,
|
||||
"tag": null,
|
||||
"visible": false,
|
||||
"description": null,
|
||||
"labels": "<REDACTED_JSON>",
|
||||
"inserted_at": "2025-01-01T13:38:42.875843Z",
|
||||
"updated_at": "2025-01-01T13:40:16.750234Z",
|
||||
"locked": false,
|
||||
"solar_system_id": <REDACTED_NUMBER>,
|
||||
"map_id": "<REDACTED_ID>",
|
||||
"custom_name": null,
|
||||
"position_x": 1125,
|
||||
"position_y": -285
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### 2. Show Single System
|
||||
|
||||
GET /api/map/system?id=<SOLAR_SYSTEM_ID>&map_id=<UUID>
|
||||
GET /api/map/system?id=<SOLAR_SYSTEM_ID>&slug=<map-slug>
|
||||
|
||||
- **Description:** Retrieves information for a specific system on the specified map. You must provide:
|
||||
- `id` (the `solar_system_id`).
|
||||
- Either `map_id` or `slug`.
|
||||
- **Authentication:** Required via `Authorization` header.
|
||||
|
||||
#### Example Request
|
||||
```
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" "https://wanderer.example.com/api/map/system?id=<REDACTED_NUMBER>&slug=<REDACTED_SLUG>"
|
||||
```
|
||||
#### Example Response
|
||||
```
|
||||
{
|
||||
"data": {
|
||||
"id": "<REDACTED_ID>",
|
||||
"name": "<REDACTED_NAME>",
|
||||
"status": 0,
|
||||
"tag": null,
|
||||
"visible": false,
|
||||
"description": null,
|
||||
"labels": "<REDACTED_JSON>",
|
||||
"inserted_at": "2025-01-03T06:30:02.069090Z",
|
||||
"updated_at": "2025-01-03T07:47:07.471051Z",
|
||||
"locked": false,
|
||||
"solar_system_id": <REDACTED_NUMBER>,
|
||||
"map_id": "<REDACTED_ID>",
|
||||
"custom_name": null,
|
||||
"position_x": 1005,
|
||||
"position_y": 765
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
### 2. Show Single System Static Info
|
||||
|
||||
GET /api/common/static-system-info?id=<SOLAR_SYSTEM_ID>
|
||||
|
||||
- **Description:** Retrieves the static information for a specific system.
|
||||
|
||||
- **Authentication:** No API token required
|
||||
|
||||
#### Example Request
|
||||
```
|
||||
curl "https://wanderer.example.com/api/common/static-system-info?id=31002229
|
||||
```
|
||||
#### Example Response
|
||||
```
|
||||
{
|
||||
"data": {
|
||||
"solar_system_id": 31002229,
|
||||
"triglavian_invasion_status": "Normal",
|
||||
"solar_system_name": "J132946",
|
||||
"system_class": 5,
|
||||
"region_id": 11000028,
|
||||
"constellation_id": 21000278,
|
||||
"solar_system_name_lc": "j132946",
|
||||
"constellation_name": "E-C00278",
|
||||
"region_name": "E-R00028",
|
||||
"security": "-1.0",
|
||||
"type_description": "Class 5",
|
||||
"class_title": "C5",
|
||||
"is_shattered": false,
|
||||
"effect_name": null,
|
||||
"effect_power": 5,
|
||||
"statics": [
|
||||
"H296"
|
||||
],
|
||||
"wandering": [
|
||||
"D792",
|
||||
"C140",
|
||||
"Z142"
|
||||
],
|
||||
"sun_type_id": 38
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
---
|
||||
|
||||
### 3. List Tracked Characters
|
||||
|
||||
GET /api/map/characters?map_id=<UUID>
|
||||
GET /api/map/characters?slug=<map-slug>
|
||||
|
||||
- **Description:** Retrieves a list of tracked characters for the specified map (by `map_id` or `slug`), including metadata such as corporation/alliance details.
|
||||
- **Authentication:** Required via `Authorization` header.
|
||||
|
||||
#### Example Request
|
||||
```
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" "https://wanderer.example.com/api/map/characters?slug=some-slug"
|
||||
```
|
||||
#### Example Response
|
||||
```
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "<REDACTED_ID>",
|
||||
"character": {
|
||||
"id": "<REDACTED_ID>",
|
||||
"name": "<REDACTED_NAME>",
|
||||
"inserted_at": "2025-01-01T05:24:18.461721Z",
|
||||
"updated_at": "2025-01-03T07:45:52.294052Z",
|
||||
"alliance_id": "<REDACTED>",
|
||||
"alliance_name": "<REDACTED>",
|
||||
"alliance_ticker": "<REDACTED>",
|
||||
"corporation_id": "<REDACTED>",
|
||||
"corporation_name": "<REDACTED>",
|
||||
"corporation_ticker": "<REDACTED>",
|
||||
"eve_id": "<REDACTED>"
|
||||
},
|
||||
"tracked": true,
|
||||
"map_id": "<REDACTED_ID>"
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Using these APIs, you can programmatically retrieve system and character information from your map. Whether you’re building a custom analytics dashboard, a corp management tool, or just want to explore data outside the standard UI, these endpoints provide a straightforward way to fetch up-to-date map details.
|
||||
|
||||
For questions or additional support, please reach out to the Wanderer Team.
|
||||
|
||||
Fly safe,
|
||||
WANDERER TEAM
|
||||
@@ -755,11 +755,11 @@ groupID,categoryID,groupName,iconID,useBasePrice,anchored,anchorable,fittableNon
|
||||
944,9,Capital Industrial Ship Blueprint,None,1,0,0,0,1
|
||||
945,9,Industrial Command Ship Blueprint,None,1,0,0,0,1
|
||||
952,11,Mission Container,0,0,1,0,0,0
|
||||
954,32,Defensive Subsystem,None,0,0,0,0,1
|
||||
954,32,Defensive Subsystem,3631,0,0,0,0,1
|
||||
955,17,Depricated Subsystems,None,0,0,0,0,0
|
||||
956,32,Offensive Subsystem,None,0,0,0,0,1
|
||||
957,32,Propulsion Subsystem,None,0,0,0,0,1
|
||||
958,32,Core Subsystem,None,0,0,0,0,1
|
||||
956,32,Offensive Subsystem,3641,0,0,0,0,1
|
||||
957,32,Propulsion Subsystem,3646,0,0,0,0,1
|
||||
958,32,Core Subsystem,3636,0,0,0,0,1
|
||||
959,11,Deadspace Sleeper Sleepless Sentinel,0,0,0,0,0,0
|
||||
960,11,Deadspace Sleeper Awakened Sentinel,0,0,0,0,0,0
|
||||
961,11,Deadspace Sleeper Emergent Sentinel,0,0,0,0,0,0
|
||||
@@ -1529,6 +1529,8 @@ groupID,categoryID,groupName,iconID,useBasePrice,anchored,anchorable,fittableNon
|
||||
4811,9,Mercenary Den Blueprint,None,1,0,0,0,1
|
||||
4820,9,Mutaplasmid Blueprint,None,1,0,0,0,1
|
||||
4821,17,Atavum,None,1,0,0,0,1
|
||||
4824,17,Infomorph Systems,None,1,0,0,0,1
|
||||
4825,2,Local Beacon,None,0,1,0,0,0
|
||||
350858,350001,Infantry Weapons,None,1,0,0,0,0
|
||||
351064,350001,Infantry Dropsuits,None,1,0,0,0,0
|
||||
351121,350001,Infantry Modules,None,1,0,0,0,0
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
21
priv/repo/migrations/20241217103838_ship_size.exs
Normal file
21
priv/repo/migrations/20241217103838_ship_size.exs
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.ShipSize do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_chain_v1) do
|
||||
modify :ship_size_type, :bigint, default: 2
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_chain_v1) do
|
||||
modify :ship_size_type, :bigint, default: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.MigrateResources1 do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_character_settings_v1) do
|
||||
add :followed, :boolean, default: false
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_character_settings_v1) do
|
||||
remove :followed
|
||||
end
|
||||
end
|
||||
end
|
||||
21
priv/repo/migrations/20250103005559_add_temporary_name.exs
Normal file
21
priv/repo/migrations/20250103005559_add_temporary_name.exs
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddTemporaryName do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_system_v1) do
|
||||
add :temporary_name, :text
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_system_v1) do
|
||||
remove :temporary_name
|
||||
end
|
||||
end
|
||||
end
|
||||
21
priv/repo/migrations/20250103121539_add_public_api_key.exs
Normal file
21
priv/repo/migrations/20250103121539_add_public_api_key.exs
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddPublicApiKey do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:maps_v1) do
|
||||
add :public_api_key, :text
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:maps_v1) do
|
||||
remove :public_api_key
|
||||
end
|
||||
end
|
||||
end
|
||||
178
priv/resource_snapshots/repo/map_chain_v1/20241217103838.json
Normal file
178
priv/resource_snapshots/repo/map_chain_v1/20241217103838.json
Normal file
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "mass_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "time_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "2",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_size_type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wormhole_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "count_of_passage",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "locked",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "custom_info",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "3495268959E42B3B8D31B5B154B416A5BEF80D57D684375296EA5C1711FE3521",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_v1"
|
||||
}
|
||||
Reference in New Issue
Block a user