Compare commits
59 Commits
revert-94-
...
v1.38.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3779961d6 | ||
|
|
d93fc29734 | ||
|
|
c67918aca5 | ||
|
|
a9f276c95a | ||
|
|
7cee4894a5 | ||
|
|
edf8bef813 | ||
|
|
71636e895e | ||
|
|
7ff9689b76 | ||
|
|
5a4d819622 | ||
|
|
3117d85648 | ||
|
|
114133ecd2 | ||
|
|
bf8a1197e4 | ||
|
|
54c06a1fc0 | ||
|
|
f83b4a2ba7 | ||
|
|
d34e7b8d8a | ||
|
|
fa0c7f3c66 | ||
|
|
5f58645b41 | ||
|
|
b1149cecaf | ||
|
|
8f28d2be65 | ||
|
|
d758b54ef8 | ||
|
|
58293b4dc4 | ||
|
|
f2083f4256 | ||
|
|
6c7bd5804e | ||
|
|
483ae21e89 | ||
|
|
f734565844 | ||
|
|
8c718ba181 | ||
|
|
c8d8734601 | ||
|
|
5c757e8255 | ||
|
|
82f90ef759 | ||
|
|
167c8eea6b | ||
|
|
d76079d4c7 | ||
|
|
bf9c4cda02 | ||
|
|
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 |
@@ -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"
|
||||
|
||||
14
.github/workflows/build.yml
vendored
@@ -122,6 +122,8 @@ jobs:
|
||||
name: 🛠 Build Docker Images
|
||||
needs: build
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
release-notes: ${{ steps.get-content.outputs.string }}
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
@@ -212,12 +214,6 @@ jobs:
|
||||
maxLength: 500
|
||||
truncationSymbol: "…"
|
||||
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ steps.get-content.outputs.string }}
|
||||
|
||||
create-release:
|
||||
name: 🏷 Create Release
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -249,3 +245,9 @@ jobs:
|
||||
## How to Promote?
|
||||
In order to promote this to prod, edit the draft and press **"Publish release"**.
|
||||
draft: true
|
||||
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ needs.docker.outputs.release-notes }}
|
||||
|
||||
1539
CHANGELOG.md
@@ -9,6 +9,9 @@ WORKDIR /app
|
||||
# set build ENV
|
||||
ENV MIX_ENV="prod"
|
||||
|
||||
# Set ERL_FLAGS for ARM compatibility
|
||||
ENV ERL_FLAGS="+JPperf true"
|
||||
|
||||
# install mix dependencies
|
||||
COPY mix.exs mix.lock ./
|
||||
RUN rm -Rf _build deps && mix deps.get --only $MIX_ENV
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
181
assets/js/hooks/Mapper/components/hooks/useSolarSystemNode.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
// feel free to rename these imports or the file path as you see fit
|
||||
import { useMemo } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
|
||||
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
||||
import { MapSolarSystemType } from '../../map.types';
|
||||
import { LABELS_INFO, LABELS_ORDER, getActivityType } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
|
||||
const SpaceToClass: Record<string, string> = {
|
||||
[Spaces.Caldari]: 'Caldaria',
|
||||
[Spaces.Matar]: 'Mataria',
|
||||
[Spaces.Amarr]: 'Amarria',
|
||||
[Spaces.Gallente]: 'Gallente',
|
||||
};
|
||||
|
||||
const sortedLabels = (labels: string[]) => {
|
||||
if (!labels) return [];
|
||||
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
||||
};
|
||||
|
||||
interface UseSolarSystemNodeParams {
|
||||
data: MapSolarSystemType;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export function useSolarSystemNode({ data, selected }: UseSolarSystemNodeParams) {
|
||||
// 1) Bring in relevant global state
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
const isShowLinkedSigId = useMapGetOption('show_linked_signature_id') === 'true';
|
||||
|
||||
const {
|
||||
data: {
|
||||
characters,
|
||||
presentCharacters,
|
||||
wormholesData,
|
||||
hubs,
|
||||
kills,
|
||||
userCharacters,
|
||||
isConnecting,
|
||||
hoverNodeId,
|
||||
visibleNodes,
|
||||
showKSpaceBG,
|
||||
isThickConnections,
|
||||
},
|
||||
outCommand,
|
||||
} = useMapState();
|
||||
|
||||
// 2) Extract data from the node
|
||||
const {
|
||||
system_class,
|
||||
security,
|
||||
class_title,
|
||||
solar_system_id,
|
||||
statics,
|
||||
effect_name,
|
||||
region_name,
|
||||
region_id,
|
||||
is_shattered,
|
||||
solar_system_name,
|
||||
} = data.system_static_info;
|
||||
|
||||
const {
|
||||
locked,
|
||||
name,
|
||||
tag,
|
||||
status,
|
||||
labels,
|
||||
id,
|
||||
temporary_name: temporaryName,
|
||||
linked_sig_eve_id: linkedSigEveId = '',
|
||||
} = data || {};
|
||||
const signatures = data.system_signatures;
|
||||
|
||||
// 3) Compute derived values
|
||||
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);
|
||||
}, [characters, presentCharacters, solar_system_id]);
|
||||
|
||||
const isWormhole = isWormholeSpace(system_class);
|
||||
|
||||
const classTitleColor = useMemo(
|
||||
() => getSystemClassStyles({ systemClass: system_class, security }),
|
||||
[security, system_class],
|
||||
);
|
||||
|
||||
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
||||
|
||||
const linkedSigPrefix = useMemo(() => (linkedSigEveId ? linkedSigEveId.split('-')[0] : null), [linkedSigEveId]);
|
||||
|
||||
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
||||
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
|
||||
const labelCustom = useMemo(
|
||||
() =>
|
||||
isShowLinkedSigId && linkedSigPrefix
|
||||
? `${linkedSigPrefix}・${labelsManager.customLabel}`
|
||||
: labelsManager.customLabel,
|
||||
[linkedSigPrefix, isShowLinkedSigId, labelsManager],
|
||||
);
|
||||
|
||||
const killsCount = useMemo(() => {
|
||||
const systemKills = kills[solar_system_id];
|
||||
if (!systemKills) return null;
|
||||
return systemKills;
|
||||
}, [kills, solar_system_id]);
|
||||
|
||||
const hasUserCharacters = useMemo(() => {
|
||||
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
|
||||
}, [charactersInSystem, userCharacters]);
|
||||
|
||||
const dbClick = useDoubleClick(() => {
|
||||
outCommand({
|
||||
type: OutCommand.openSettings,
|
||||
data: {
|
||||
system_id: solar_system_id.toString(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const showHandlers = isConnecting || hoverNodeId === id;
|
||||
|
||||
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)
|
||||
.map(s => ({
|
||||
eve_id: s.eve_id,
|
||||
type: s.type,
|
||||
custom_info: s.custom_info,
|
||||
})),
|
||||
);
|
||||
}, [isShowUnsplashedSignatures, signatures]);
|
||||
|
||||
return {
|
||||
selected,
|
||||
visible,
|
||||
isWormhole,
|
||||
classTitleColor,
|
||||
killsCount,
|
||||
hasUserCharacters,
|
||||
showHandlers,
|
||||
regionClass,
|
||||
systemName,
|
||||
customName,
|
||||
labelCustom,
|
||||
is_shattered,
|
||||
tag,
|
||||
status,
|
||||
labelsInfo,
|
||||
dbClick,
|
||||
sortedStatics,
|
||||
effect_name,
|
||||
region_name,
|
||||
solar_system_id,
|
||||
locked,
|
||||
hubs,
|
||||
charactersInSystem,
|
||||
unsplashedLeft,
|
||||
unsplashedRight,
|
||||
isThickConnections,
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
.MapRoot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.BackgroundAlternateColor {
|
||||
background-color: var(--rf-bg-color, #0C0A09);
|
||||
|
||||
&.BackgroundAlternateColor {
|
||||
background-color: var(--rf-soft-bg-color, #171717);
|
||||
--rf-node-bg-color: var(--rf-node-soft-bg-color, #202020);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect } from 'react';
|
||||
import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } from 'react';
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
ConnectionMode,
|
||||
@@ -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';
|
||||
@@ -25,16 +23,19 @@ import {
|
||||
ContextMenuConnection,
|
||||
ContextMenuRoot,
|
||||
SolarSystemEdge,
|
||||
SolarSystemNode,
|
||||
SolarSystemNodeDefault,
|
||||
SolarSystemNodeTheme,
|
||||
useContextMenuConnectionHandlers,
|
||||
useContextMenuRootHandlers,
|
||||
} from './components';
|
||||
import { wrapNode } from './utils/wrapNode';
|
||||
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 };
|
||||
|
||||
@@ -76,11 +77,8 @@ const initialEdges = [
|
||||
},
|
||||
];
|
||||
|
||||
const nodeTypes = {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
custom: SolarSystemNode,
|
||||
} as never;
|
||||
|
||||
|
||||
|
||||
const edgeTypes = {
|
||||
floating: SolarSystemEdge,
|
||||
@@ -101,6 +99,7 @@ interface MapCompProps {
|
||||
isThickConnections?: boolean;
|
||||
isShowBackgroundPattern?: boolean;
|
||||
isSoftBackground?: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const MapComp = ({
|
||||
@@ -117,17 +116,30 @@ const MapComp = ({
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
onAddSystem,
|
||||
}: MapCompProps) => {
|
||||
const { getNode, getNodes } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
|
||||
|
||||
|
||||
const nodeTypes = useMemo(() => {
|
||||
return {
|
||||
custom:
|
||||
theme !== '' && theme !== 'default'
|
||||
? wrapNode(SolarSystemNodeTheme)
|
||||
: wrapNode(SolarSystemNodeDefault),
|
||||
};
|
||||
}, [theme]);
|
||||
|
||||
|
||||
useMapHandlers(refn, onSelectionChange);
|
||||
useUpdateNodes(nodes);
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
|
||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||
const { update } = useMapState();
|
||||
const { variant, gap, size, color } = useBackgroundVars(theme);
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
params => {
|
||||
@@ -229,7 +241,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}
|
||||
@@ -276,7 +288,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);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,38 @@
|
||||
@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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,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)`,
|
||||
}}
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
import { memo, useMemo } from 'react';
|
||||
import { Handle, Position, WrapNodeProps } from 'reactflow';
|
||||
import { MapSolarSystemType } from '../../map.types';
|
||||
import classes from './SolarSystemNode.module.scss';
|
||||
import clsx from 'clsx';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
import {
|
||||
EFFECT_BACKGROUND_STYLES,
|
||||
LABELS_INFO,
|
||||
LABELS_ORDER,
|
||||
MARKER_BOOKMARK_BG_STYLES,
|
||||
STATUS_CLASSES,
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick.ts';
|
||||
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
||||
|
||||
const SpaceToClass: Record<string, string> = {
|
||||
[Spaces.Caldari]: classes.Caldaria,
|
||||
[Spaces.Matar]: classes.Mataria,
|
||||
[Spaces.Amarr]: classes.Amarria,
|
||||
[Spaces.Gallente]: classes.Gallente,
|
||||
};
|
||||
|
||||
const sortedLabels = (labels: string[]) => {
|
||||
if (!labels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
||||
};
|
||||
|
||||
export const getActivityType = (count: number) => {
|
||||
if (count <= 5) {
|
||||
return 'activityNormal';
|
||||
}
|
||||
|
||||
if (count <= 30) {
|
||||
return 'activityWarn';
|
||||
}
|
||||
|
||||
return 'activityDanger';
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||
|
||||
const {
|
||||
system_class,
|
||||
security,
|
||||
class_title,
|
||||
solar_system_id,
|
||||
statics,
|
||||
effect_name,
|
||||
region_name,
|
||||
region_id,
|
||||
is_shattered,
|
||||
solar_system_name,
|
||||
} = data.system_static_info;
|
||||
|
||||
const signatures = data.system_signatures;
|
||||
|
||||
const { locked, name, tag, status, labels, id } = data || {};
|
||||
|
||||
const customName = solar_system_name !== name ? name : undefined;
|
||||
|
||||
const {
|
||||
data: {
|
||||
characters,
|
||||
presentCharacters,
|
||||
wormholesData,
|
||||
hubs,
|
||||
kills,
|
||||
userCharacters,
|
||||
isConnecting,
|
||||
hoverNodeId,
|
||||
visibleNodes,
|
||||
showKSpaceBG,
|
||||
isThickConnections,
|
||||
},
|
||||
outCommand,
|
||||
} = useMapState();
|
||||
|
||||
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
|
||||
}, [characters, presentCharacters, solar_system_id]);
|
||||
|
||||
const isWormhole = isWormholeSpace(system_class);
|
||||
const classTitleColor = useMemo(
|
||||
() => getSystemClassStyles({ systemClass: system_class, security }),
|
||||
[security, system_class],
|
||||
);
|
||||
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
||||
const lebM = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
||||
const labelsInfo = useMemo(() => sortedLabels(lebM.list), [lebM]);
|
||||
const labelCustom = useMemo(() => lebM.customLabel, [lebM]);
|
||||
|
||||
const killsCount = useMemo(() => {
|
||||
const systemKills = kills[solar_system_id];
|
||||
if (!systemKills) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return systemKills;
|
||||
}, [kills, solar_system_id]);
|
||||
|
||||
const hasUserCharacters = useMemo(() => {
|
||||
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
|
||||
}, [charactersInSystem, userCharacters]);
|
||||
|
||||
const dbClick = useDoubleClick(() => {
|
||||
outCommand({
|
||||
type: OutCommand.openSettings,
|
||||
data: {
|
||||
system_id: solar_system_id.toString(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const showHandlers = isConnecting || hoverNodeId === id;
|
||||
|
||||
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||
|
||||
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||
if (!isShowUnsplashedSignatures) {
|
||||
return [[], []];
|
||||
}
|
||||
|
||||
return prepareUnsplashedChunks(
|
||||
signatures
|
||||
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||
.map(s => ({
|
||||
eve_id: s.eve_id,
|
||||
type: s.type,
|
||||
custom_info: s.custom_info,
|
||||
})),
|
||||
);
|
||||
}, [isShowUnsplashedSignatures, signatures]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{is_shattered && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
||||
</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(classes.text)}>{killsCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{labelsInfo.map(x => (
|
||||
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
|
||||
{x.shortName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
|
||||
[classes.selected]: selected,
|
||||
})}
|
||||
>
|
||||
{visible && (
|
||||
<>
|
||||
<div className={classes.HeadRow}>
|
||||
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
|
||||
{class_title ?? '-'}
|
||||
</div>
|
||||
{tag != null && tag !== '' && (
|
||||
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{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',
|
||||
)}
|
||||
>
|
||||
{solar_system_name}
|
||||
</div>
|
||||
|
||||
{isWormhole && (
|
||||
<div className={classes.statics}>
|
||||
{sortedStatics.map(x => (
|
||||
<WormholeClassComp key={x} id={x} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{effect_name !== null && isWormhole && (
|
||||
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||
{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">
|
||||
{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 />}
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{visible && isShowUnsplashedSignatures && (
|
||||
<div className={classes.Unsplashed}>
|
||||
{unsplashedLeft.map(x => (
|
||||
<UnsplashedSignature key={x.sig_id} signature={x} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{visible && isShowUnsplashedSignatures && (
|
||||
<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}>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleTop, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Top}
|
||||
id="a"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleRight, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Right}
|
||||
id="b"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleBottom, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Bottom}
|
||||
id="c"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleLeft, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Left}
|
||||
id="d"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -85,7 +85,7 @@ $tooltip-bg: #202020; // Dark background for tooltips
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
&.tooltip {
|
||||
background-color: $tooltip-bg;
|
||||
color: $text-color;
|
||||
padding: 5px 10px;
|
||||
@@ -94,42 +94,59 @@ $tooltip-bg: #202020; // Dark background for tooltips
|
||||
}
|
||||
|
||||
&.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);
|
||||
|
||||
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-home),
|
||||
transparent
|
||||
);
|
||||
&.selected {
|
||||
border-color: $eve-solar-system-status-color-home;
|
||||
border-color: var(--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);
|
||||
|
||||
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: darken($eve-solar-system-status-color-friendly, 5%);
|
||||
border-color: var(--eve-solar-system-status-color-friendly-dark5);
|
||||
}
|
||||
}
|
||||
|
||||
&.eve-system-status-lookingFor {
|
||||
border: 1px solid darken($eve-solar-system-status-color-lookingFor, 15%);
|
||||
border: 1px solid var(--eve-solar-system-status-color-lookingFor-dark15);
|
||||
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);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-warning),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
&.eve-system-status-dangerous {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-dangerous, transparent);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-dangerous),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
&.eve-system-status-target {
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-target, transparent);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-target),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +259,10 @@ $tooltip-bg: #202020; // Dark background for tooltips
|
||||
|
||||
.TagTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
font-weight: medium;
|
||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||
|
||||
color: #ffb01d;
|
||||
color: var(--rf-tag-color, #38BDF8);
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@@ -0,0 +1,227 @@
|
||||
import { memo } from 'react';
|
||||
import { Handle, Position } from 'reactflow';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import classes from './SolarSystemNodeDefault.module.scss';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
|
||||
|
||||
import {
|
||||
MARKER_BOOKMARK_BG_STYLES,
|
||||
STATUS_CLASSES,
|
||||
EFFECT_BACKGROUND_STYLES,
|
||||
} from '@/hooks/Mapper/components/map/constants';
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
|
||||
export const SolarSystemNodeDefault = memo(props => {
|
||||
const {
|
||||
charactersInSystem,
|
||||
classTitle,
|
||||
classTitleColor,
|
||||
customName,
|
||||
effectName,
|
||||
hasUserCharacters,
|
||||
hubs,
|
||||
visible,
|
||||
labelCustom,
|
||||
labelsInfo,
|
||||
locked,
|
||||
isShattered,
|
||||
isThickConnections,
|
||||
isWormhole,
|
||||
killsCount,
|
||||
killsActivityType,
|
||||
regionClass,
|
||||
regionName,
|
||||
status,
|
||||
selected,
|
||||
tag,
|
||||
showHandlers,
|
||||
systemName,
|
||||
sortedStatics,
|
||||
solarSystemId,
|
||||
unsplashedLeft,
|
||||
unsplashedRight,
|
||||
dbClick: handleDbClick,
|
||||
} = useSolarSystemNode(props);
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isShattered && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{killsCount && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[killsActivityType!])}>
|
||||
<div className={clsx(classes.BookmarkWithIcon)}>
|
||||
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
|
||||
<span className={clsx(classes.text)}>{killsCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{labelsInfo.map(x => (
|
||||
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
|
||||
{x.shortName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(classes.RootCustomNode, regionClass && classes[regionClass], classes[STATUS_CLASSES[status]], {
|
||||
[classes.selected]: selected,
|
||||
})}
|
||||
>
|
||||
{visible && (
|
||||
<>
|
||||
<div className={classes.HeadRow}>
|
||||
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
|
||||
{classTitle ?? '-'}
|
||||
</div>
|
||||
|
||||
{tag != null && tag !== '' && (
|
||||
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{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',
|
||||
)}
|
||||
>
|
||||
{systemName}
|
||||
</div>
|
||||
|
||||
{isWormhole && (
|
||||
<div className={classes.statics}>
|
||||
{sortedStatics.map(whClass => (
|
||||
<WormholeClassComp key={whClass} id={whClass} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{effectName !== null && isWormhole && (
|
||||
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effectName])} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||
{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">
|
||||
{customName}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && (
|
||||
<div className="[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5">
|
||||
{regionName}
|
||||
</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' }} />}
|
||||
|
||||
{hubs.includes(solarSystemId.toString()) && (
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }} />
|
||||
)}
|
||||
|
||||
{charactersInSystem.length > 0 && (
|
||||
<div
|
||||
className={clsx(classes.localCounter, {
|
||||
['text-amber-300']: hasUserCharacters,
|
||||
})}
|
||||
>
|
||||
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
|
||||
<span className="font-sans">{charactersInSystem.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{visible && (
|
||||
<>
|
||||
{unsplashedLeft.length > 0 && (
|
||||
<div className={classes.Unsplashed}>
|
||||
{unsplashedLeft.map(sig => (
|
||||
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{unsplashedRight.length > 0 && (
|
||||
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||
{unsplashedRight.map(sig => (
|
||||
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div onMouseDownCapture={handleDbClick} className={classes.Handlers}>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleTop, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Top}
|
||||
id="a"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleRight, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Right}
|
||||
id="b"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleBottom, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Bottom}
|
||||
id="c"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleLeft, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Left}
|
||||
id="d"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
SolarSystemNodeDefault.displayName = 'SolarSystemNodeDefault';
|
||||
@@ -0,0 +1,407 @@
|
||||
@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: var(--rf-node-bg-color, #202020) !important;
|
||||
color: var(--rf-text-color, #ffffff);
|
||||
font-family: var(--rf-node-font-family, inherit) !important;
|
||||
font-weight: var(--rf-node-font-weight, inherit) !important;
|
||||
|
||||
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 var(--eve-solar-system-status-color-home-dark30);
|
||||
background-image: linear-gradient(
|
||||
275deg,
|
||||
var(--eve-solar-system-status-home),
|
||||
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: $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 {
|
||||
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;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.HeadRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
.classTitle {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgb(0 0 0 / 73%);
|
||||
}
|
||||
|
||||
.TagTitle {
|
||||
font-size: 11px;
|
||||
font-weight: medium;
|
||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||
|
||||
color: var(--rf-tag-color, #38BDF8);
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.solarSystemName {
|
||||
}
|
||||
}
|
||||
|
||||
.BottomRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 19px;
|
||||
|
||||
.regionName {
|
||||
color: var(--rf-region-name, #D6D3D1)
|
||||
}
|
||||
|
||||
.customName {
|
||||
color: var(--rf-custom-name, #93C5FD)
|
||||
}
|
||||
|
||||
.localCounter {
|
||||
display: flex;
|
||||
//align-items: center;
|
||||
gap: 2px;
|
||||
|
||||
.hasUserCharacters {
|
||||
color: var(--rf-has-user-characters, #fbbf24);
|
||||
}
|
||||
|
||||
& > i {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
& > span {
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
font-weight: 500;
|
||||
//margin-top: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.Handlers {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid $pastel-blue;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
}
|
||||
|
||||
&.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
import { memo } from 'react';
|
||||
import { Handle, Position } from 'reactflow';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import classes from './SolarSystemNodeTheme.module.scss';
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { useSolarSystemNode } from '../../hooks/useSolarSystemNode';
|
||||
|
||||
import {
|
||||
MARKER_BOOKMARK_BG_STYLES,
|
||||
STATUS_CLASSES,
|
||||
EFFECT_BACKGROUND_STYLES,
|
||||
} from '@/hooks/Mapper/components/map/constants';
|
||||
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
|
||||
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
|
||||
|
||||
export const SolarSystemNodeTheme = memo(props => {
|
||||
const {
|
||||
charactersInSystem,
|
||||
classTitle,
|
||||
classTitleColor,
|
||||
customName,
|
||||
effectName,
|
||||
hasUserCharacters,
|
||||
hubs,
|
||||
visible,
|
||||
labelCustom,
|
||||
labelsInfo,
|
||||
locked,
|
||||
isShattered,
|
||||
isThickConnections,
|
||||
isWormhole,
|
||||
killsCount,
|
||||
killsActivityType,
|
||||
regionClass,
|
||||
regionName,
|
||||
status,
|
||||
selected,
|
||||
tag,
|
||||
showHandlers,
|
||||
systemName,
|
||||
sortedStatics,
|
||||
solarSystemId,
|
||||
unsplashedLeft,
|
||||
unsplashedRight,
|
||||
dbClick: handleDbClick,
|
||||
} = useSolarSystemNode(props);
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isShattered && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES.shattered)}>
|
||||
<span className={clsx('pi pi-chart-pie', classes.icon)} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{killsCount && (
|
||||
<div className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[killsActivityType!])}>
|
||||
<div className={clsx(classes.BookmarkWithIcon)}>
|
||||
<span className={clsx(PrimeIcons.BOLT, classes.icon)} />
|
||||
<span className={clsx(classes.text)}>{killsCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{labelsInfo.map(x => (
|
||||
<div key={x.id} className={clsx(classes.Bookmark, MARKER_BOOKMARK_BG_STYLES[x.id])}>
|
||||
{x.shortName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(classes.RootCustomNode, regionClass && classes[regionClass], classes[STATUS_CLASSES[status]], {
|
||||
[classes.selected]: selected,
|
||||
})}
|
||||
>
|
||||
{visible && (
|
||||
<>
|
||||
<div className={classes.HeadRow}>
|
||||
<div className={clsx(classes.classTitle, classTitleColor, '[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]')}>
|
||||
{classTitle ?? '-'}
|
||||
</div>
|
||||
|
||||
{tag != null && tag !== '' && <div className={clsx(classes.TagTitle)}>{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',
|
||||
)}
|
||||
>
|
||||
{systemName}
|
||||
</div>
|
||||
|
||||
{isWormhole && (
|
||||
<div className={classes.statics}>
|
||||
{sortedStatics.map(whClass => (
|
||||
<WormholeClassComp key={whClass} id={whClass} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{effectName !== null && isWormhole && (
|
||||
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effectName])} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={clsx(classes.BottomRow, 'flex items-center justify-between')}>
|
||||
{customName && (
|
||||
<div
|
||||
className={clsx(
|
||||
classes.CustomName,
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||
)}
|
||||
>
|
||||
{customName}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && (
|
||||
<div
|
||||
className={clsx(
|
||||
classes.RegionName,
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||
)}
|
||||
>
|
||||
{regionName}
|
||||
</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' }} />}
|
||||
|
||||
{hubs.includes(solarSystemId.toString()) && (
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }} />
|
||||
)}
|
||||
|
||||
{charactersInSystem.length > 0 && (
|
||||
<div
|
||||
className={clsx(classes.localCounter, {
|
||||
[classes.hasUserCharacters]: hasUserCharacters,
|
||||
})}
|
||||
>
|
||||
<i className="pi pi-users" style={{ fontSize: '0.50rem' }} />
|
||||
<span className="font-sans">{charactersInSystem.length}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{visible && (
|
||||
<>
|
||||
{unsplashedLeft.length > 0 && (
|
||||
<div className={classes.Unsplashed}>
|
||||
{unsplashedLeft.map(sig => (
|
||||
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{unsplashedRight.length > 0 && (
|
||||
<div className={clsx(classes.Unsplashed, classes['Unsplashed--right'])}>
|
||||
{unsplashedRight.map(sig => (
|
||||
<UnsplashedSignature key={sig.sig_id} signature={sig} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<div onMouseDownCapture={handleDbClick} className={classes.Handlers}>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleTop, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Top}
|
||||
id="a"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleRight, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Right}
|
||||
id="b"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleBottom, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Bottom}
|
||||
id="c"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
className={clsx(classes.Handle, classes.HandleLeft, {
|
||||
[classes.selected]: selected,
|
||||
[classes.Tick]: isThickConnections,
|
||||
})}
|
||||
style={{ visibility: showHandlers ? 'visible' : 'hidden' }}
|
||||
position={Position.Left}
|
||||
id="d"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
SolarSystemNodeTheme.displayName = 'SolarSystemNodeTheme';
|
||||
@@ -1 +1,2 @@
|
||||
export * from './SolarSystemNode';
|
||||
export * from './SolarSystemNodeDefault';
|
||||
export * from './SolarSystemNodeTheme';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
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(() => {
|
||||
// match any element whose entire `class` attribute ends with "-theme"
|
||||
let themeEl = document.querySelector('[class$="-theme"]');
|
||||
|
||||
// If none is found, fall back to the <html> element
|
||||
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-pattern-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 };
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
|
||||
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
|
||||
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
|
||||
import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { LABELS_INFO, LABELS_ORDER } from '@/hooks/Mapper/components/map/constants';
|
||||
|
||||
function getActivityType(count: number) {
|
||||
if (count <= 5) return 'activityNormal';
|
||||
if (count <= 30) return 'activityWarn';
|
||||
return 'activityDanger';
|
||||
}
|
||||
|
||||
const SpaceToClass: Record<string, string> = {
|
||||
[Spaces.Caldari]: 'Caldaria',
|
||||
[Spaces.Matar]: 'Mataria',
|
||||
[Spaces.Amarr]: 'Amarria',
|
||||
[Spaces.Gallente]: 'Gallente',
|
||||
};
|
||||
|
||||
function sortedLabels(labels: string[]) {
|
||||
if (!labels) return [];
|
||||
return LABELS_ORDER.filter(x => labels.includes(x)).map(x => LABELS_INFO[x]);
|
||||
}
|
||||
|
||||
export function useSolarSystemNode(props: any) {
|
||||
const { data, selected, id } = props;
|
||||
const {
|
||||
system_static_info,
|
||||
system_signatures,
|
||||
locked,
|
||||
name,
|
||||
tag,
|
||||
status,
|
||||
labels,
|
||||
temporary_name,
|
||||
linked_sig_eve_id: linkedSigEveId = '',
|
||||
} = data;
|
||||
|
||||
const {
|
||||
system_class,
|
||||
security,
|
||||
class_title,
|
||||
solar_system_id,
|
||||
statics,
|
||||
effect_name,
|
||||
region_name,
|
||||
region_id,
|
||||
is_shattered,
|
||||
solar_system_name,
|
||||
} = system_static_info;
|
||||
|
||||
// Global map state
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
const isShowLinkedSigId = useMapGetOption('show_linked_signature_id') === 'true';
|
||||
|
||||
const {
|
||||
data: {
|
||||
characters,
|
||||
presentCharacters,
|
||||
wormholesData,
|
||||
hubs,
|
||||
kills,
|
||||
userCharacters,
|
||||
isConnecting,
|
||||
hoverNodeId,
|
||||
visibleNodes,
|
||||
showKSpaceBG,
|
||||
isThickConnections,
|
||||
},
|
||||
outCommand,
|
||||
} = useMapState();
|
||||
|
||||
// logic
|
||||
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
|
||||
}, [characters, presentCharacters, solar_system_id]);
|
||||
|
||||
const isWormhole = isWormholeSpace(system_class);
|
||||
|
||||
const classTitleColor = useMemo(
|
||||
() => getSystemClassStyles({ systemClass: system_class, security }),
|
||||
[security, system_class],
|
||||
);
|
||||
|
||||
const sortedStatics = useMemo(() => sortWHClasses(wormholesData, statics), [wormholesData, statics]);
|
||||
|
||||
const linkedSigPrefix = useMemo(() => (linkedSigEveId ? linkedSigEveId.split('-')[0] : null), [linkedSigEveId]);
|
||||
|
||||
const labelsManager = useMemo(() => new LabelsManager(labels ?? ''), [labels]);
|
||||
const labelsInfo = useMemo(() => sortedLabels(labelsManager.list), [labelsManager]);
|
||||
const labelCustom = useMemo(
|
||||
() =>
|
||||
isShowLinkedSigId && linkedSigPrefix
|
||||
? `${linkedSigPrefix}・${labelsManager.customLabel}`
|
||||
: labelsManager.customLabel,
|
||||
[linkedSigPrefix, isShowLinkedSigId, labelsManager],
|
||||
);
|
||||
|
||||
const killsCount = useMemo(() => kills[solar_system_id] ?? null, [kills, solar_system_id]);
|
||||
const killsActivityType = killsCount ? getActivityType(killsCount) : null;
|
||||
|
||||
const hasUserCharacters = useMemo(() => {
|
||||
return charactersInSystem.some(x => userCharacters.includes(x.eve_id));
|
||||
}, [charactersInSystem, userCharacters]);
|
||||
|
||||
const dbClick = useDoubleClick(() => {
|
||||
outCommand({
|
||||
type: OutCommand.openSettings,
|
||||
data: { system_id: solar_system_id.toString() },
|
||||
});
|
||||
});
|
||||
|
||||
const showHandlers = isConnecting || hoverNodeId === id;
|
||||
|
||||
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||
|
||||
const systemName = (isTempSystemNameEnabled && temporary_name) || solar_system_name;
|
||||
const customName = (isTempSystemNameEnabled && temporary_name && name) || (solar_system_name !== name && name);
|
||||
|
||||
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||
if (!isShowUnsplashedSignatures) {
|
||||
return [[], []];
|
||||
}
|
||||
return prepareUnsplashedChunks(
|
||||
system_signatures
|
||||
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||
.map(s => ({
|
||||
eve_id: s.eve_id,
|
||||
type: s.type,
|
||||
custom_info: s.custom_info,
|
||||
})),
|
||||
);
|
||||
}, [isShowUnsplashedSignatures, system_signatures]);
|
||||
|
||||
const nodeVars = {
|
||||
// original props
|
||||
id,
|
||||
selected,
|
||||
// computed
|
||||
visible,
|
||||
isWormhole,
|
||||
classTitleColor,
|
||||
killsCount,
|
||||
killsActivityType,
|
||||
hasUserCharacters,
|
||||
showHandlers,
|
||||
regionClass,
|
||||
systemName,
|
||||
customName,
|
||||
labelCustom,
|
||||
isShattered: is_shattered,
|
||||
tag,
|
||||
status,
|
||||
labelsInfo,
|
||||
dbClick,
|
||||
sortedStatics,
|
||||
effectName: effect_name,
|
||||
regionName: region_name,
|
||||
solarSystemId: solar_system_id,
|
||||
solarSystemName: solar_system_name,
|
||||
locked,
|
||||
hubs,
|
||||
name: name,
|
||||
isConnecting,
|
||||
hoverNodeId,
|
||||
charactersInSystem,
|
||||
unsplashedLeft,
|
||||
unsplashedRight,
|
||||
isThickConnections,
|
||||
classTitle: class_title,
|
||||
temporaryName: temporary_name,
|
||||
};
|
||||
|
||||
return nodeVars;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
|
||||
.default-theme {
|
||||
--rf-bg-color: #0C0A09;
|
||||
--rf-soft-bg-color: #171717;
|
||||
|
||||
--rf-node-bg-color: #202020;
|
||||
--rf-node-soft-bg-color: #202020;
|
||||
--rf-text-color: #ffffff;
|
||||
--rf-tag-color: #38BDF8;
|
||||
--rf-region-name: #D6D3D1;
|
||||
--rf-custom-name: #93C5FD;
|
||||
|
||||
|
||||
--rf-bg-variant: "dots";
|
||||
--rf-bg-gap: 15;
|
||||
--rf-bg-size: 1;
|
||||
--rf-bg-pattern-color: #81818a;
|
||||
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,78 +1,121 @@
|
||||
$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;
|
||||
$friendlyBase: #3bbd39;
|
||||
$friendlyAlpha: #3bbd3952;
|
||||
$friendlyDark20: darken($friendlyBase, 20%);
|
||||
$friendlyDark30: darken($friendlyBase, 30%);
|
||||
$friendlyDark5: darken($friendlyBase, 5%);
|
||||
|
||||
$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;
|
||||
$lookingForBase: #43c2fd;
|
||||
$lookingForAlpha: rgba(67, 176, 253, 0.48);
|
||||
$lookingForDark15: darken($lookingForBase, 15%);
|
||||
|
||||
$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;
|
||||
$homeBase: rgb(197, 253, 67);
|
||||
$homeAlpha: rgba(197, 253, 67, 0.32);
|
||||
$homeDark30: darken($homeBase, 30%);
|
||||
|
||||
$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-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);
|
||||
:root {
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
$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);
|
||||
--pastel-blue-darken10: #4f6b86;
|
||||
--pastel-blue-lighten10: #6da3af;
|
||||
--pastel-pink-darken10: #bb7ca9;
|
||||
--pastel-pink-lighten10: #e0a6cb;
|
||||
--pastel-green-darken10: #79a244;
|
||||
--pastel-green-lighten10: #99cf52;
|
||||
--pastel-yellow-darken10: #e6c44f;
|
||||
--pastel-yellow-lighten10: #ffe874;
|
||||
|
||||
--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-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-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;
|
||||
|
||||
--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-solar-system-status-unknown: transparent;
|
||||
--eve-solar-system-status-color-unknown: transparent;
|
||||
--eve-solar-system-status-home: #{$homeAlpha};
|
||||
--eve-solar-system-status-color-home: #{$homeBase};
|
||||
--eve-solar-system-status-color-home-dark30: #{$homeDark30};
|
||||
--eve-solar-system-status-friendly: #{$friendlyAlpha};
|
||||
--eve-solar-system-status-color-friendly: #{$friendlyBase};
|
||||
--eve-solar-system-status-friendly-dark30: #{$friendlyDark30};
|
||||
--eve-solar-system-status-color-friendly-dark20: #{$friendlyDark20};
|
||||
--eve-solar-system-status-color-friendly-dark5: #{$friendlyDark5};
|
||||
--eve-solar-system-status-lookingFor: #{$lookingForAlpha};
|
||||
--eve-solar-system-status-color-lookingFor: #{$lookingForBase};
|
||||
--eve-solar-system-status-color-lookingFor-dark15: #{$lookingForDark15};
|
||||
--eve-solar-system-status-warning: #906518a6;
|
||||
--eve-solar-system-status-color-warning: #ffb93b;
|
||||
--eve-solar-system-status-target: #b439ff6b;
|
||||
--eve-solar-system-status-color-target: #b439ff;
|
||||
--eve-solar-system-status-dangerous: #d54040;
|
||||
--eve-solar-system-status-color-dangerous: #d54040;
|
||||
|
||||
--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,504 @@
|
||||
@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: var(--eve-solar-system-status-color-dangerous);
|
||||
}
|
||||
|
||||
.eve-system-status-color-dangerous {
|
||||
color: $eve-solar-system-status-color-dangerous;
|
||||
.eve-system-status-clear {
|
||||
background-color: var(--eve-solar-system-status-unknown);
|
||||
}
|
||||
.eve-system-status-home {
|
||||
background-color: var(--eve-solar-system-status-home);
|
||||
}
|
||||
.eve-system-status-friendly {
|
||||
background-color: var(--eve-solar-system-status-friendly);
|
||||
}
|
||||
.eve-system-status-lookingFor {
|
||||
background-color: var(--eve-solar-system-status-lookingFor);
|
||||
}
|
||||
.eve-system-status-warning {
|
||||
background-color: var(--eve-solar-system-status-warning);
|
||||
}
|
||||
.eve-system-status-target {
|
||||
background-color: var(--eve-solar-system-status-target);
|
||||
}
|
||||
.eve-system-status-dangerous {
|
||||
background-color: var(--eve-solar-system-status-dangerous);
|
||||
}
|
||||
|
||||
.eve-system-status-clear {
|
||||
background-color: var(--eve-solar-system-status-unknown);
|
||||
color: var(--eve-solar-system-status-color-unknown);
|
||||
}
|
||||
|
||||
.eve-system-status-home {
|
||||
background-color: var(--eve-solar-system-status-home);
|
||||
color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
|
||||
.eve-system-status-friendly {
|
||||
background-color: var(--eve-solar-system-status-friendly);
|
||||
color: var(--eve-solar-system-status-color-friendly);
|
||||
}
|
||||
|
||||
.eve-system-status-lookingFor {
|
||||
background-color: var(--eve-solar-system-status-lookingFor);
|
||||
color: var(--eve-solar-system-status-color-lookingFor);
|
||||
}
|
||||
|
||||
.eve-system-status-warning {
|
||||
background-color: var(--eve-solar-system-status-warning);
|
||||
color: var(--eve-solar-system-status-color-warning);
|
||||
}
|
||||
|
||||
.eve-system-status-target {
|
||||
background-color: var(--eve-solar-system-status-target);
|
||||
color: var(--eve-solar-system-status-color-target);
|
||||
}
|
||||
|
||||
.eve-system-status-dangerous {
|
||||
background-color: var(--eve-solar-system-status-dangerous);
|
||||
color: var(--eve-solar-system-status-color-dangerous);
|
||||
}
|
||||
|
||||
.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;
|
||||
@@ -572,3 +541,49 @@
|
||||
.wd-marker-bookmark-color-danger {
|
||||
background-color: #d10600;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import './default-theme.scss';
|
||||
@import './pathfinder-theme.scss';
|
||||
@@ -1,74 +0,0 @@
|
||||
$pastel-blue: #5a7d9a;
|
||||
$pastel-pink: #d291bc;
|
||||
$pastel-green: #88b04b;
|
||||
$pastel-yellow: #ffdd59;
|
||||
$dark-bg: #2d2d2d;
|
||||
$text-color: #ffffff;
|
||||
$tooltip-bg: #202020;
|
||||
|
||||
.react-flow {
|
||||
// background-color: $dark-bg;
|
||||
color: $text-color;
|
||||
|
||||
&__node {
|
||||
//cursor: auto;
|
||||
}
|
||||
|
||||
&__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);
|
||||
}
|
||||
|
||||
&__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;
|
||||
}
|
||||
}
|
||||
@@ -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,51 @@
|
||||
|
||||
@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-color: #000000;
|
||||
--rf-soft-bg-color: #282828;
|
||||
|
||||
--rf-node-bg-color: #202020;
|
||||
--rf-node-soft-bg-color: #313335;
|
||||
--rf-node-font-weight: bold;
|
||||
--rf-text-color: #adadad;
|
||||
--rf-region-name: var(--rf-text-color);
|
||||
--rf-custom-name: var(--rf-text-color);
|
||||
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
--rf-bg-variant: "lines";
|
||||
--rf-bg-gap: 32;
|
||||
--rf-bg-size: 1;
|
||||
--rf-bg-pattern-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;
|
||||
|
||||
|
||||
--rf-node-font-weight: bold;
|
||||
--rf-node-line-height: normal;
|
||||
--rf-node-font-family: 'Oxygen', sans-serif;
|
||||
--rf-node-text-color: var(--pf-text-color);
|
||||
|
||||
--rf-tag-color: #fbbf24;
|
||||
--rf-has-user-characters: #5cb85c;
|
||||
}
|
||||
11
assets/js/hooks/Mapper/components/map/utils/wrapNode.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
// wrapNode.ts
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { SolarSystemNodeProps } from '../components/SolarSystemNode';
|
||||
|
||||
export function wrapNode<T>(
|
||||
SolarSystemNode: React.FC<SolarSystemNodeProps<T>>
|
||||
): React.FC<NodeProps<T>> {
|
||||
return function NodeAdapter(props) {
|
||||
return <SolarSystemNode {...props} />;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
.RouteSystem {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ffffff;
|
||||
|
||||
cursor: pointer;
|
||||
transition: opacity 200ms;
|
||||
|
||||
@@ -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 ?? 'default'}-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' },
|
||||
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
|
||||
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.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: 'default' },
|
||||
{ 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>
|
||||
|
||||
@@ -40,6 +40,7 @@ export const MapWrapper = () => {
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
},
|
||||
} = useMapRootState();
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
@@ -166,6 +167,7 @@ export const MapWrapper = () => {
|
||||
isThickConnections={isThickConnections}
|
||||
isShowBackgroundPattern={isShowBackgroundPattern}
|
||||
isSoftBackground={isSoftBackground}
|
||||
theme={theme}
|
||||
onAddSystem={onAddSystem}
|
||||
/>
|
||||
|
||||
|
||||
@@ -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: 'default',
|
||||
}
|
||||
|
||||
export interface MapRootContextProps {
|
||||
update: ContextStoreDataUpdate<MapRootData>;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,6 +116,7 @@ export type SolarSystemRawType = {
|
||||
tag: string | null;
|
||||
status: number;
|
||||
name: string | null;
|
||||
temporary_name: string | null;
|
||||
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
system_signatures: SystemSignature[];
|
||||
|
||||
@@ -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');
|
||||
})
|
||||
|
||||
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/static/images/news/01-11-map-settings-guide/cover.png
Normal file
|
After Width: | Height: | Size: 14 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")
|
||||
@@ -61,6 +66,18 @@ map_subscription_hubs_limit =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 100)
|
||||
|
||||
map_connection_auto_expire_hours =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_CONNECTION_AUTO_EXPIRE_HOURS", 24)
|
||||
|
||||
map_connection_auto_eol_hours =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_CONNECTION_AUTO_EOL_HOURS", 21)
|
||||
|
||||
map_connection_eol_expire_timeout_mins =
|
||||
config_dir
|
||||
|> get_int_from_path_or_env("WANDERER_MAP_CONNECTION_EOL_EXPIRE_TIMEOUT_MINS", 30)
|
||||
|
||||
wallet_tracking_enabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_WALLET_TRACKING_ENABLED", "false")
|
||||
@@ -83,7 +100,11 @@ 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,
|
||||
map_connection_auto_expire_hours: map_connection_auto_expire_hours,
|
||||
map_connection_auto_eol_hours: map_connection_auto_eol_hours,
|
||||
map_connection_eol_expire_timeout_mins: map_connection_eol_expire_timeout_mins,
|
||||
wallet_tracking_enabled: wallet_tracking_enabled,
|
||||
subscription_settings: %{
|
||||
plans: [
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ 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_linked_sig_eve_id, action: :update_linked_sig_eve_id)
|
||||
define(:update_position, action: :update_position)
|
||||
define(:update_visible, action: :update_visible)
|
||||
end
|
||||
@@ -102,6 +104,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
|
||||
@@ -112,6 +118,10 @@ defmodule WandererApp.Api.MapSystem do
|
||||
change(set_attribute(:visible, true))
|
||||
end
|
||||
|
||||
update :update_linked_sig_eve_id do
|
||||
accept [:linked_sig_eve_id]
|
||||
end
|
||||
|
||||
update :update_visible do
|
||||
accept [:visible]
|
||||
end
|
||||
@@ -141,6 +151,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
|
||||
@@ -184,6 +198,10 @@ defmodule WandererApp.Api.MapSystem do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :linked_sig_eve_id, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
defmodule WandererApp.Env do
|
||||
@moduledoc false
|
||||
use Nebulex.Caching
|
||||
|
||||
@app :wanderer_app
|
||||
|
||||
@@ -9,6 +10,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)
|
||||
@@ -16,5 +18,25 @@ defmodule WandererApp.Env do
|
||||
def corp_wallet, do: get_key(:corp_wallet, "")
|
||||
def corp_eve_id, do: get_key(:corp_id, -1)
|
||||
def subscription_settings, do: get_key(:subscription_settings)
|
||||
|
||||
@decorate cacheable(
|
||||
cache: WandererApp.Cache,
|
||||
key: "map-connection-auto-expire-hours"
|
||||
)
|
||||
def map_connection_auto_expire_hours, do: get_key(:map_connection_auto_expire_hours)
|
||||
|
||||
@decorate cacheable(
|
||||
cache: WandererApp.Cache,
|
||||
key: "map-connection-auto-eol-hours"
|
||||
)
|
||||
def map_connection_auto_eol_hours, do: get_key(:map_connection_auto_eol_hours)
|
||||
|
||||
@decorate cacheable(
|
||||
cache: WandererApp.Cache,
|
||||
key: "map-connection-eol-expire-timeout-mins"
|
||||
)
|
||||
def map_connection_eol_expire_timeout_mins,
|
||||
do: get_key(:map_connection_eol_expire_timeout_mins)
|
||||
|
||||
def get_key(key, default \\ nil), do: Application.get_env(@app, key, default)
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
}
|
||||
|
||||
@cache_opts [cache: true]
|
||||
@retry_opts [retry: false, retry_log_level: :warning]
|
||||
@retry_opts [max_retries: 1, retry_log_level: :warning]
|
||||
@timeout_opts [receive_timeout: :timer.seconds(30)]
|
||||
@api_retry_count 1
|
||||
|
||||
|
||||
@@ -70,6 +70,11 @@ defmodule WandererApp.Map do
|
||||
def get_characters_limit(map_id),
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:characters_limit, 100)}
|
||||
|
||||
def is_subscription_active?(map_id) do
|
||||
{:ok, %{plan: plan}} = WandererApp.Map.SubscriptionManager.get_active_map_subscription(map_id)
|
||||
not WandererApp.Env.map_subscriptions_enabled?() || plan != :alpha
|
||||
end
|
||||
|
||||
def get_options(map_id),
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:options, Map.new())}
|
||||
|
||||
|
||||
@@ -9,20 +9,38 @@ defmodule WandererApp.Map.Audit do
|
||||
@logger Application.compile_env(:wanderer_app, :logger)
|
||||
|
||||
@week_seconds :timer.hours(24 * 7)
|
||||
@month_seconds @week_seconds * 4
|
||||
@audit_expired_seconds @month_seconds * 3
|
||||
|
||||
def track_map_subscription_event(event_type, metadata) do
|
||||
case event_type do
|
||||
"subscription.created" ->
|
||||
track_map_event(event_type, metadata)
|
||||
|
||||
"subscription.updated" ->
|
||||
track_map_event(event_type, metadata)
|
||||
|
||||
"subscription.deleted" ->
|
||||
track_map_event(event_type, metadata)
|
||||
|
||||
_ ->
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def archive() do
|
||||
@logger.info("Start map audit arhiving...")
|
||||
Logger.info("Start map audit arhiving...")
|
||||
|
||||
WandererApp.Api.UserActivity
|
||||
|> Ash.Query.filter(inserted_at: [less_than: _get_expired_at()])
|
||||
|> Ash.Query.filter(inserted_at: [less_than: get_expired_at()])
|
||||
|> Ash.bulk_destroy!(:archive, %{}, batch_size: 100)
|
||||
|
||||
@logger.info(fn -> "Audit arhived" end)
|
||||
Logger.info(fn -> "Audit arhived" end)
|
||||
:ok
|
||||
end
|
||||
|
||||
def get_activity_page(map_id, page, per_page, period, activity) do
|
||||
{from, to} = period |> _get_period()
|
||||
{from, to} = period |> get_period()
|
||||
|
||||
query =
|
||||
WandererApp.Api.UserActivity
|
||||
@@ -84,52 +102,43 @@ defmodule WandererApp.Map.Audit do
|
||||
|
||||
def track_map_event(_event_type, _metadata), do: {:ok, nil}
|
||||
|
||||
defp _get_period("1H") do
|
||||
defp get_period("1H") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-1 * 3600, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp _get_period("1D") do
|
||||
defp get_period("1D") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp _get_period("1W") do
|
||||
defp get_period("1W") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 7, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
# defp _get_period("1M") do
|
||||
# now = DateTime.utc_now()
|
||||
# start_date = now |> DateTime.add(-24 * 3600 * 31, :second)
|
||||
# {start_date, now}
|
||||
# end
|
||||
|
||||
# defp _get_period("ALL") do
|
||||
# now = DateTime.utc_now()
|
||||
|
||||
# start_date = %{
|
||||
# now
|
||||
# | year: 2000,
|
||||
# month: 1,
|
||||
# day: 1,
|
||||
# hour: 00,
|
||||
# minute: 00,
|
||||
# second: 00,
|
||||
# microsecond: {0, 0}
|
||||
# }
|
||||
|
||||
# {start_date, now}
|
||||
# end
|
||||
|
||||
defp _get_period(_) do
|
||||
defp get_period("1M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-1 * 3600, :second)
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 31, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp _get_expired_at(), do: DateTime.utc_now() |> DateTime.add(-@week_seconds, :second)
|
||||
defp get_period("2M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 31 * 2, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("3M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 31 * 3, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period(_), do: get_period("1H")
|
||||
|
||||
defp get_expired_at(), do: DateTime.utc_now() |> DateTime.add(-@audit_expired_seconds, :second)
|
||||
end
|
||||
|
||||
@@ -105,6 +105,12 @@ defmodule WandererApp.Map.Server do
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_system_position/2, [update]})
|
||||
|
||||
def update_system_linked_sig_eve_id(map_id, update) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_system_linked_sig_eve_id/2, [update]})
|
||||
|
||||
def update_system_name(map_id, update) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
@@ -129,6 +135,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,10 +150,14 @@ 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
|
||||
|
||||
defdelegate update_system_linked_sig_eve_id(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_position(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate add_hub(state, hub_info), to: SystemsImpl
|
||||
@@ -201,7 +205,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 +263,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 +324,10 @@ 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_linked_signature_id:
|
||||
options |> Map.get("show_linked_signature_id", "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 +435,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 +455,10 @@ 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)
|
||||
|
||||
@@ -67,20 +67,29 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
# @unknown 100_100
|
||||
#
|
||||
@connection_time_status_eol 1
|
||||
@connection_auto_eol_hours 21
|
||||
@connection_auto_expire_hours 24
|
||||
@connection_eol_expire_timeout :timer.hours(3) + :timer.minutes(30)
|
||||
|
||||
@connection_type_wormhole 0
|
||||
@connection_type_stargate 1
|
||||
|
||||
def get_connection_auto_expire_hours(), do: WandererApp.Env.map_connection_auto_expire_hours()
|
||||
|
||||
def get_connection_auto_eol_hours(), do: WandererApp.Env.map_connection_auto_eol_hours()
|
||||
|
||||
def get_eol_expire_timeout_mins(), do: WandererApp.Env.map_connection_eol_expire_timeout_mins()
|
||||
|
||||
def get_eol_expire_timeout(),
|
||||
do:
|
||||
:timer.hours(get_connection_auto_expire_hours() - get_connection_auto_eol_hours()) +
|
||||
:timer.minutes(get_eol_expire_timeout_mins())
|
||||
|
||||
def init_eol_cache(map_id, connections_eol_time) do
|
||||
eol_expire_timeout = get_eol_expire_timeout()
|
||||
|
||||
connections_eol_time
|
||||
|> Enum.each(fn {connection_id, connection_eol_time} ->
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:conn_#{connection_id}:mark_eol_time",
|
||||
connection_eol_time,
|
||||
ttl: @connection_eol_expire_timeout
|
||||
ttl: eol_expire_timeout
|
||||
)
|
||||
end)
|
||||
end
|
||||
@@ -155,7 +164,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
WandererApp.Cache.put(
|
||||
"map_#{map_id}:conn_#{connection_id}:mark_eol_time",
|
||||
DateTime.utc_now(),
|
||||
ttl: @connection_eol_expire_timeout
|
||||
ttl: get_eol_expire_timeout()
|
||||
)
|
||||
|
||||
_ ->
|
||||
@@ -194,6 +203,9 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
do: update_connection(state, :update_custom_info, [:custom_info], connection_update)
|
||||
|
||||
def cleanup_connections(%{map_id: map_id} = state) do
|
||||
connection_auto_expire_hours = get_connection_auto_expire_hours()
|
||||
connection_auto_eol_hours = get_connection_auto_eol_hours()
|
||||
|
||||
state =
|
||||
map_id
|
||||
|> WandererApp.Map.list_connections!()
|
||||
@@ -205,7 +217,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
} ->
|
||||
type != @connection_type_stargate &&
|
||||
DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
|
||||
@connection_auto_eol_hours &&
|
||||
connection_auto_eol_hours &&
|
||||
is_connection_valid(
|
||||
:wormholes,
|
||||
solar_system_source_id,
|
||||
@@ -262,9 +274,9 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
not is_connection_exist ||
|
||||
(type != @connection_type_stargate && is_connection_valid &&
|
||||
(DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
|
||||
@connection_auto_expire_hours ||
|
||||
connection_auto_expire_hours ||
|
||||
DateTime.diff(DateTime.utc_now(), connection_mark_eol_time, :hour) >=
|
||||
@connection_auto_expire_hours - @connection_auto_eol_hours))
|
||||
connection_auto_expire_hours - connection_auto_eol_hours))
|
||||
end)
|
||||
|> Enum.reduce(state, fn %{
|
||||
solar_system_source: solar_system_source_id,
|
||||
@@ -333,7 +345,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,20 +358,19 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
: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
|
||||
})
|
||||
{: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
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
|
||||
:ok
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -134,6 +141,12 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
),
|
||||
do: state |> update_system(:update_labels, [:labels], update)
|
||||
|
||||
def update_system_linked_sig_eve_id(
|
||||
state,
|
||||
update
|
||||
),
|
||||
do: state |> update_system(:update_linked_sig_eve_id, [:linked_sig_eve_id], update)
|
||||
|
||||
def update_system_position(
|
||||
%{rtree_name: rtree_name} = state,
|
||||
update
|
||||
@@ -277,7 +290,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
location.solar_system_id
|
||||
) do
|
||||
{:ok, existing_system} when not is_nil(existing_system) ->
|
||||
{:ok, updated_system} =
|
||||
updated_system =
|
||||
existing_system
|
||||
|> WandererApp.MapSystemRepo.update_position!(%{
|
||||
position_x: position.x,
|
||||
@@ -285,7 +298,9 @@ 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_tags!()
|
||||
|> WandererApp.MapSystemRepo.cleanup_temporary_name!()
|
||||
|> WandererApp.MapSystemRepo.cleanup_linked_sig_eve_id!()
|
||||
|
||||
@ddrt.insert(
|
||||
{existing_system.solar_system_id,
|
||||
@@ -404,6 +419,8 @@ 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.cleanup_linked_sig_eve_id!()
|
||||
|> WandererApp.MapSystemRepo.update_visible(%{visible: true})
|
||||
end
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ defmodule WandererApp.MapRepo do
|
||||
@default_map_options %{
|
||||
"layout" => "left_to_right",
|
||||
"store_custom_labels" => "false",
|
||||
"show_linked_signature_id" => "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,27 @@ 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 cleanup_linked_sig_eve_id!(system) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_linked_sig_eve_id!(%{
|
||||
linked_sig_eve_id: nil
|
||||
})
|
||||
end
|
||||
|
||||
def get_filtered_labels(labels, true) when is_binary(labels) do
|
||||
labels
|
||||
|> Jason.decode!()
|
||||
@@ -99,6 +122,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
|
||||
@@ -109,6 +137,16 @@ defmodule WandererApp.MapSystemRepo do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_labels!(update)
|
||||
|
||||
def update_linked_sig_eve_id(system, update),
|
||||
do:
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_linked_sig_eve_id(update)
|
||||
|
||||
def update_linked_sig_eve_id!(system, update),
|
||||
do:
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_linked_sig_eve_id!(update)
|
||||
|
||||
def update_position(system, update),
|
||||
do:
|
||||
system
|
||||
|
||||
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
|
||||
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
@@ -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
|
||||
@@ -192,16 +192,15 @@ 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
|
||||
|
||||
"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
|
||||
@@ -217,7 +216,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
existing = Enum.find(my_settings, &(&1.character_id == clicked_char_id))
|
||||
|
||||
{:ok, target_setting} =
|
||||
if existing do
|
||||
if not is_nil(existing) do
|
||||
{:ok, existing}
|
||||
else
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
@@ -276,7 +275,6 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
||||
def handle_ui_event("hide_tracking", _, socket),
|
||||
do: {:noreply, socket |> assign(show_tracking?: false)}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event("toggle_follow_" <> character_id, _, socket),
|
||||
def handle_ui_event("toggle_follow_" <> character_id, _, socket),
|
||||
do:
|
||||
MapCharactersEventHandler.handle_ui_event(
|
||||
"toggle_follow",
|
||||
@@ -234,7 +234,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
|> 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
|
||||
|
||||
|
||||
@@ -234,6 +234,12 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do
|
||||
})
|
||||
end)
|
||||
|
||||
map_id
|
||||
|> WandererApp.Map.Server.update_system_linked_sig_eve_id(%{
|
||||
solar_system_id: solar_system_target,
|
||||
linked_sig_eve_id: signature_eve_id
|
||||
})
|
||||
|
||||
Phoenix.PubSub.broadcast!(WandererApp.PubSub, map_id, %{
|
||||
event: :signatures_updated,
|
||||
payload: solar_system_source
|
||||
|
||||
@@ -231,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
|
||||
@@ -242,6 +243,7 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
"labels" -> :labels
|
||||
"locked" -> :locked
|
||||
"tag" -> :tag
|
||||
"temporary_name" -> :temporary_name
|
||||
"status" -> :status
|
||||
_ -> :none
|
||||
end
|
||||
|
||||
@@ -5,6 +5,8 @@ defmodule WandererAppWeb.MapAuditLive do
|
||||
|
||||
alias WandererAppWeb.UserActivity
|
||||
|
||||
@active_subscription_periods ["2M", "3M"]
|
||||
|
||||
def mount(
|
||||
%{"slug" => map_slug, "period" => period, "activity" => activity} = _params,
|
||||
_session,
|
||||
@@ -38,6 +40,7 @@ defmodule WandererAppWeb.MapAuditLive do
|
||||
map_id: map_id,
|
||||
map_name: map_name,
|
||||
map_slug: map_slug,
|
||||
map_subscription_active: WandererApp.Map.is_subscription_active?(map_id),
|
||||
activity: activity,
|
||||
can_undo_types: [:systems_removed],
|
||||
period: period || "1H",
|
||||
@@ -117,23 +120,26 @@ defmodule WandererAppWeb.MapAuditLive do
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("undo", %{"event-data" => event_data, "event-type" => "systems_removed"}, %{assigns: %{map_id: map_id, current_user: current_user}} = socket) do
|
||||
def handle_event(
|
||||
"undo",
|
||||
%{"event-data" => event_data, "event-type" => "systems_removed"},
|
||||
%{assigns: %{map_id: map_id, current_user: current_user}} = socket
|
||||
) do
|
||||
{:ok, %{"solar_system_ids" => solar_system_ids}} = Jason.decode(event_data)
|
||||
|
||||
solar_system_ids
|
||||
|> Enum.each(fn solar_system_id ->
|
||||
WandererApp.Map.Server.add_system(
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: solar_system_id,
|
||||
coordinates: nil,
|
||||
use_old_coordinates: true
|
||||
},
|
||||
current_user.id,
|
||||
nil
|
||||
)
|
||||
end)
|
||||
|
||||
|> Enum.each(fn solar_system_id ->
|
||||
WandererApp.Map.Server.add_system(
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: solar_system_id,
|
||||
coordinates: nil,
|
||||
use_old_coordinates: true
|
||||
},
|
||||
current_user.id,
|
||||
nil
|
||||
)
|
||||
end)
|
||||
|
||||
{:noreply, socket |> put_flash(:info, "Systems restored!")}
|
||||
end
|
||||
@@ -171,9 +177,18 @@ defmodule WandererAppWeb.MapAuditLive do
|
||||
end
|
||||
|
||||
defp load_activity(socket, new_page) when new_page >= 1 do
|
||||
%{activity: activity, per_page: per_page, page: cur_page, map_id: map_id, period: period} =
|
||||
%{
|
||||
activity: activity,
|
||||
per_page: per_page,
|
||||
page: cur_page,
|
||||
map_id: map_id,
|
||||
map_subscription_active: map_subscription_active,
|
||||
period: period
|
||||
} =
|
||||
socket.assigns
|
||||
|
||||
period = get_valid_period(period, map_subscription_active)
|
||||
|
||||
with {:ok, page} <-
|
||||
WandererApp.Map.Audit.get_activity_page(map_id, new_page, per_page, period, activity) do
|
||||
{activity, at, limit} =
|
||||
@@ -199,4 +214,14 @@ defmodule WandererAppWeb.MapAuditLive do
|
||||
_ -> socket
|
||||
end
|
||||
end
|
||||
|
||||
defp get_valid_period(period, true), do: period
|
||||
|
||||
defp get_valid_period(period, _map_subscription_active) do
|
||||
if period in @active_subscription_periods do
|
||||
"1H"
|
||||
else
|
||||
period
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
>
|
||||
WEEK
|
||||
</button>
|
||||
<%!-- <button
|
||||
<button
|
||||
:if={@map_subscription_active}
|
||||
phx-click="set_period"
|
||||
phx-value-period="1M"
|
||||
class={[
|
||||
@@ -73,8 +74,31 @@
|
||||
classes(" !bg-neutral-800 !text-white": @period == "1M")
|
||||
]}
|
||||
>
|
||||
M
|
||||
1M
|
||||
</button>
|
||||
<button
|
||||
:if={@map_subscription_active}
|
||||
phx-click="set_period"
|
||||
phx-value-period="2M"
|
||||
class={[
|
||||
"btn btn-sm join-item font-medium text-neutral bg-neutral-700",
|
||||
classes(" !bg-neutral-800 !text-white": @period == "2M")
|
||||
]}
|
||||
>
|
||||
2M
|
||||
</button>
|
||||
<button
|
||||
:if={@map_subscription_active}
|
||||
phx-click="set_period"
|
||||
phx-value-period="3M"
|
||||
class={[
|
||||
"btn btn-sm join-item font-medium text-neutral bg-neutral-700",
|
||||
classes(" !bg-neutral-800 !text-white": @period == "3M")
|
||||
]}
|
||||
>
|
||||
3M
|
||||
</button>
|
||||
<%!--
|
||||
<button
|
||||
phx-click="set_period"
|
||||
phx-value-period="ALL"
|
||||
|
||||
@@ -50,6 +50,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
"update_system_labels",
|
||||
"update_system_locked",
|
||||
"update_system_tag",
|
||||
"update_system_temporary_name",
|
||||
"update_system_status"
|
||||
]
|
||||
|
||||
@@ -244,6 +245,8 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
locked: locked,
|
||||
tag: tag,
|
||||
labels: labels,
|
||||
linked_sig_eve_id: linked_sig_eve_id,
|
||||
temporary_name: temporary_name,
|
||||
status: status,
|
||||
visible: visible
|
||||
} = _system,
|
||||
@@ -267,8 +270,10 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
system_signatures: system_signatures,
|
||||
labels: labels,
|
||||
locked: locked,
|
||||
linked_sig_eve_id: linked_sig_eve_id,
|
||||
status: status,
|
||||
tag: tag,
|
||||
temporary_name: temporary_name,
|
||||
visible: visible
|
||||
}
|
||||
end
|
||||
|
||||
@@ -77,13 +77,14 @@ defmodule WandererAppWeb.MapLive do
|
||||
def handle_info(:not_all_characters_tracked, %{assigns: %{map_slug: map_slug}} = socket),
|
||||
do:
|
||||
WandererAppWeb.MapEventHandler.handle_ui_event(
|
||||
%{event: "add_character"},
|
||||
"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
|
||||
@@ -101,17 +102,4 @@ defmodule WandererAppWeb.MapLive do
|
||||
socket
|
||||
|> assign(:active_page, :map)
|
||||
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
|
||||
|
||||
@@ -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,17 @@ 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",
|
||||
@@ -668,7 +680,13 @@ defmodule WandererAppWeb.MapsLive do
|
||||
) do
|
||||
options =
|
||||
options_form
|
||||
|> Map.take(["layout", "store_custom_labels", "restrict_offline_showing"])
|
||||
|> Map.take([
|
||||
"layout",
|
||||
"store_custom_labels",
|
||||
"show_linked_signature_id",
|
||||
"show_temp_system_name",
|
||||
"restrict_offline_showing"
|
||||
])
|
||||
|
||||
{:ok, updated_map} = WandererApp.MapRepo.update_options(map, options)
|
||||
|
||||
|
||||
@@ -313,6 +313,34 @@
|
||||
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 +373,16 @@
|
||||
field={f[:store_custom_labels]}
|
||||
label="Store system custom labels"
|
||||
/>
|
||||
<.input
|
||||
type="checkbox"
|
||||
field={f[:show_linked_signature_id]}
|
||||
label="Show linked signature ID as custom label part"
|
||||
/>
|
||||
<.input
|
||||
type="checkbox"
|
||||
field={f[:show_temp_system_name]}
|
||||
label="Allow temporary system names"
|
||||
/>
|
||||
<.input
|
||||
type="checkbox"
|
||||
field={f[:restrict_offline_showing]}
|
||||
@@ -377,6 +415,55 @@
|
||||
</.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]
|
||||
|
||||
|
||||
3
mix.exs
@@ -2,7 +2,8 @@ defmodule WandererApp.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
@version "1.32.6"
|
||||
|
||||
@version "1.38.5"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||