mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-11-26 19:13:25 +00:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f5d3df003 | ||
|
|
c66cc8868e | ||
|
|
0d6528ce4f | ||
|
|
34c385ac5f | ||
|
|
b6d12e73a9 | ||
|
|
1118858120 | ||
|
|
ae3a34d5bf | ||
|
|
43df42e49b | ||
|
|
e670f3bf03 | ||
|
|
c26a9404c5 | ||
|
|
c0fad4ca92 | ||
|
|
16dbf9378b | ||
|
|
4001fe5eac | ||
|
|
2992dd8f8b | ||
|
|
98a03d1e59 | ||
|
|
2088393c79 | ||
|
|
093042b88a | ||
|
|
e5ef35c186 | ||
|
|
1cd23d5efd | ||
|
|
ead5818a3f | ||
|
|
a5f66ada68 | ||
|
|
0919742853 | ||
|
|
f3efffd259 | ||
|
|
f85317983c | ||
|
|
76f709b768 | ||
|
|
e3b2356302 | ||
|
|
3d810211ee | ||
|
|
7453795dc5 | ||
|
|
9de7cd99ee | ||
|
|
51489c1aa5 | ||
|
|
25dd6de770 | ||
|
|
2a825f5a02 | ||
|
|
908d249eb9 | ||
|
|
6cd119e8f4 | ||
|
|
9a59c8eb75 | ||
|
|
452c022d41 | ||
|
|
27e9bab82a | ||
|
|
edef860530 | ||
|
|
032cb63411 | ||
|
|
a1791ba578 | ||
|
|
3a69fd7786 | ||
|
|
8a90723c2e | ||
|
|
af2fc342c7 | ||
|
|
05ea2fcdbe | ||
|
|
6d4321fead | ||
|
|
3f6364c9ea | ||
|
|
0d11b12282 | ||
|
|
0796bcf7d0 | ||
|
|
0b5bec142a | ||
|
|
a5020b58f2 | ||
|
|
f039a74a8f | ||
|
|
0e6bb7390b | ||
|
|
b52b4eecca | ||
|
|
8186977d1d | ||
|
|
86adcfe4d7 | ||
|
|
ce2dd872c4 | ||
|
|
aadc53c90e | ||
|
|
cbc1b6b5c8 | ||
|
|
1aed7a9232 | ||
|
|
b549189644 | ||
|
|
35279d17b4 | ||
|
|
bb403aa0c5 | ||
|
|
04327c288b | ||
|
|
94d60e40d0 | ||
|
|
8505fcb6b7 | ||
|
|
e0a37f7635 | ||
|
|
9aec57166d | ||
|
|
a3739f2950 | ||
|
|
3d3b152758 | ||
|
|
0e03730543 | ||
|
|
97e07a6511 | ||
|
|
a77a51ba15 | ||
|
|
42e706e1c2 | ||
|
|
025dd06053 | ||
|
|
bcb421d879 | ||
|
|
66056ab54b | ||
|
|
bb92f76ceb | ||
|
|
84076b340b | ||
|
|
48caae5c0e | ||
|
|
77dd23795a | ||
|
|
2771d6304e | ||
|
|
9946edffa4 | ||
|
|
50bf2fd9d3 | ||
|
|
bdcde168aa | ||
|
|
5807142e20 | ||
|
|
ec2d9565b9 | ||
|
|
a18a71c73d | ||
|
|
93a6bd1156 | ||
|
|
581a410aef | ||
|
|
ab02fe988c | ||
|
|
b8d20fb21b | ||
|
|
12fa1a0be8 | ||
|
|
85a84f7507 | ||
|
|
2385313013 | ||
|
|
c7ce727571 | ||
|
|
8b165ff478 | ||
|
|
6d7d0cc72d | ||
|
|
f7eba5d4fd | ||
|
|
73ef6dae73 | ||
|
|
7fa6df1e5e | ||
|
|
e1a2ffb151 | ||
|
|
6d7727a32d | ||
|
|
6d7a94bd5a | ||
|
|
ecc3fb17e1 | ||
|
|
209e2bf0a5 | ||
|
|
b1947e57a4 | ||
|
|
74507501a5 | ||
|
|
c73481fd58 | ||
|
|
7795ad0b0c | ||
|
|
aff768f413 |
@@ -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"
|
||||
|
||||
1256
CHANGELOG.md
1256
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
@import 'primereact/resources/themes/arya-blue/theme.css' layer(primereact);
|
||||
/*@import 'primereact/resources/themes/bootstrap4-dark-blue/theme.css' layer(primereact);*/
|
||||
|
||||
@import '../js/hooks/Mapper/components/map/styles/index.scss';
|
||||
|
||||
@layer tailwind-base {
|
||||
@tailwind base;
|
||||
}
|
||||
@@ -870,3 +872,63 @@ body {
|
||||
}
|
||||
}
|
||||
/* Map refresh END */
|
||||
|
||||
.inputContainer {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
}
|
||||
.inputContainer > span:nth-child(1),
|
||||
.inputContainer > label:nth-child(1) {
|
||||
color: var(--gray-200);
|
||||
font-size: 13px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.inputContainer > :nth-child(2) {
|
||||
border-bottom: 2px dotted #3f3f3f;
|
||||
height: 1px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.smallInputSwitch {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch {
|
||||
height: 1rem;
|
||||
width: 2rem;
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch.p-inputswitch-checked .p-inputswitch-slider::before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch.p-highlight .p-inputswitch-slider:before {
|
||||
transform: translateX(1rem);
|
||||
}
|
||||
.smallInputSwitch .p-inputswitch .p-inputswitch-slider::before {
|
||||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
margin-top: -0.4rem;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.checkboxRoot.sizeXS {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.checkboxRoot.sizeXS .p-checkbox-box,
|
||||
.checkboxRoot.sizeXS .p-checkbox-input {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.checkboxRoot.sizeM {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.checkboxRoot.sizeM .p-checkbox-box,
|
||||
.checkboxRoot.sizeM .p-checkbox-input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
@@ -108,3 +108,7 @@
|
||||
.p-dropdown-empty-message {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,23 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
const onSystemTemporaryName = useCallback((temporaryName?: string) => {
|
||||
const { system, outCommand } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemTemporaryName,
|
||||
data: {
|
||||
system_id: system,
|
||||
value: temporaryName ?? '',
|
||||
},
|
||||
});
|
||||
setSystem(undefined);
|
||||
}, []);
|
||||
|
||||
|
||||
const onSystemStatus = useCallback((status: number) => {
|
||||
const { system, outCommand } = ref.current;
|
||||
if (!system) {
|
||||
@@ -161,6 +178,7 @@ export const useContextMenuSystemHandlers = ({ systems, hubs, outCommand }: UseC
|
||||
onLockToggle,
|
||||
onHubToggle,
|
||||
onSystemTag,
|
||||
onSystemTemporaryName,
|
||||
onSystemStatus,
|
||||
onSystemLabels,
|
||||
onOpenSettings,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useWaypointMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
|
||||
export const useContextMenuSystemItems = ({
|
||||
onDeleteSystem,
|
||||
@@ -44,6 +45,8 @@ export const useContextMenuSystemItems = ({
|
||||
<FastSystemActions
|
||||
systemId={systemId}
|
||||
systemName={system.system_static_info.solar_system_name}
|
||||
regionName={system.system_static_info.region_name}
|
||||
isWH={isWormholeSpace(system.system_static_info.system_class)}
|
||||
showEdit
|
||||
onOpenSettings={onOpenSettings}
|
||||
/>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { WaypointSetContextHandler } from '@/hooks/Mapper/components/contexts/ty
|
||||
import { FastSystemActions } from '@/hooks/Mapper/components/contexts/components';
|
||||
import { useJumpPlannerMenu } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { Route } from '@/hooks/Mapper/types/routes.ts';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
|
||||
export interface ContextMenuSystemInfoProps {
|
||||
systemStatics: Map<number, SolarSystemStaticInfoRaw>;
|
||||
@@ -48,7 +49,6 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
|
||||
if (!systemId || !system) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
className: classes.FastActions,
|
||||
@@ -57,6 +57,8 @@ export const ContextMenuSystemInfo: React.FC<ContextMenuSystemInfoProps> = ({
|
||||
<FastSystemActions
|
||||
systemId={systemId}
|
||||
systemName={system.solar_system_name}
|
||||
regionName={system.region_name}
|
||||
isWH={isWormholeSpace(system.system_class)}
|
||||
onOpenSettings={onOpenSettings}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,13 +9,22 @@ import { PrimeIcons } from 'primereact/api';
|
||||
export interface FastSystemActionsProps {
|
||||
systemId: string;
|
||||
systemName: string;
|
||||
regionName: string;
|
||||
isWH: boolean;
|
||||
showEdit?: boolean;
|
||||
onOpenSettings(): void;
|
||||
}
|
||||
|
||||
export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEdit }: FastSystemActionsProps) => {
|
||||
const ref = useRef({ systemId, systemName });
|
||||
ref.current = { systemId, systemName };
|
||||
export const FastSystemActions = ({
|
||||
systemId,
|
||||
systemName,
|
||||
regionName,
|
||||
isWH,
|
||||
onOpenSettings,
|
||||
showEdit,
|
||||
}: FastSystemActionsProps) => {
|
||||
const ref = useRef({ systemId, systemName, regionName, isWH });
|
||||
ref.current = { systemId, systemName, regionName, isWH };
|
||||
|
||||
const handleOpenZKB = useCallback(
|
||||
() => window.open(`https://zkillboard.com/system/${ref.current.systemId}`, '_blank'),
|
||||
@@ -27,10 +36,17 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
|
||||
[],
|
||||
);
|
||||
|
||||
const handleOpenDotlan = useCallback(
|
||||
() => window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank'),
|
||||
[],
|
||||
);
|
||||
const handleOpenDotlan = useCallback(() => {
|
||||
if (ref.current.isWH) {
|
||||
window.open(`https://evemaps.dotlan.net/system/${ref.current.systemName}`, '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
return window.open(
|
||||
`https://evemaps.dotlan.net/map/${ref.current.regionName.replace(/ /gim, '_')}/${ref.current.systemName}#jumps`,
|
||||
'_blank',
|
||||
);
|
||||
}, []);
|
||||
|
||||
const copySystemNameToClipboard = useCallback(async () => {
|
||||
try {
|
||||
@@ -43,9 +59,9 @@ export const FastSystemActions = ({ systemId, systemName, onOpenSettings, showEd
|
||||
return (
|
||||
<LayoutEventBlocker className={clsx('flex px-2 gap-2 justify-between items-center h-full')}>
|
||||
<div className={clsx('flex gap-2 items-center h-full', classes.Links)}>
|
||||
<WdImgButton source={ZKB_ICON} onClick={handleOpenZKB} />
|
||||
<WdImgButton source={ANOIK_ICON} onClick={handleOpenAnoikis} />
|
||||
<WdImgButton source={DOTLAN_ICON} onClick={handleOpenDotlan} />
|
||||
<WdImgButton tooltip={{ content: 'Open zkillboard' }} source={ZKB_ICON} onClick={handleOpenZKB} />
|
||||
<WdImgButton tooltip={{ content: 'Open Anoikis' }} source={ANOIK_ICON} onClick={handleOpenAnoikis} />
|
||||
<WdImgButton tooltip={{ content: 'Open Dotlan' }} source={DOTLAN_ICON} onClick={handleOpenDotlan} />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-center pl-1">
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.BackgroundAlternateColor {
|
||||
background-color: var(--rf-soft-bg-color, #2f2f2f);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ import ReactFlow, {
|
||||
} from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
import classes from './Map.module.scss';
|
||||
import './styles/neon-theme.scss';
|
||||
import './styles/eve-common.scss';
|
||||
import { MapProvider, useMapState } from './MapProvider';
|
||||
import { useNodesState, useEdgesState, useMapHandlers, useUpdateNodes } from './hooks';
|
||||
import { MapHandlers, OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
@@ -29,11 +27,13 @@ import {
|
||||
useContextMenuConnectionHandlers,
|
||||
useContextMenuRootHandlers,
|
||||
} from './components';
|
||||
import { OnMapSelectionChange } from './map.types';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from './map.types';
|
||||
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
|
||||
import { SolarSystemConnection, SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import clsx from 'clsx';
|
||||
import { useBackgroundVars } from './hooks/useBackgroundVars';
|
||||
|
||||
const DEFAULT_VIEW_PORT = { zoom: 1, x: 0, y: 0 };
|
||||
|
||||
@@ -91,12 +91,16 @@ interface MapCompProps {
|
||||
onSelectionChange: OnMapSelectionChange;
|
||||
onManualDelete(systems: string[]): void;
|
||||
onConnectionInfoClick?(e: SolarSystemConnection): void;
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
onSelectionContextMenu?: NodeSelectionMouseHandler;
|
||||
minimapClasses?: string;
|
||||
isShowMinimap?: boolean;
|
||||
onSystemContextMenu: (event: MouseEvent<Element>, systemId: string) => void;
|
||||
showKSpaceBG?: boolean;
|
||||
isThickConnections?: boolean;
|
||||
isShowBackgroundPattern?: boolean;
|
||||
isSoftBackground?: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const MapComp = ({
|
||||
@@ -111,16 +115,21 @@ const MapComp = ({
|
||||
isShowMinimap,
|
||||
showKSpaceBG,
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
onAddSystem,
|
||||
}: MapCompProps) => {
|
||||
const { getNode } = useReactFlow();
|
||||
const { getNode, getNodes } = useReactFlow();
|
||||
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
|
||||
const [edges, , onEdgesChange] = useEdgesState<Edge<SolarSystemConnection>>(initialEdges);
|
||||
|
||||
useMapHandlers(refn, onSelectionChange);
|
||||
useUpdateNodes(nodes);
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers();
|
||||
const { handleRootContext, ...rootCtxProps } = useContextMenuRootHandlers({ onAddSystem });
|
||||
const { handleConnectionContext, ...connectionCtxProps } = useContextMenuConnectionHandlers();
|
||||
const { update } = useMapState();
|
||||
const { variant, gap, size, color } = useBackgroundVars(theme);
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
params => {
|
||||
@@ -179,6 +188,12 @@ const MapComp = ({
|
||||
(changes: NodeChange[]) => {
|
||||
const systemsIdsToRemove: string[] = [];
|
||||
|
||||
// prevents single node deselection on background / same node click
|
||||
// allows deseletion of all nodes if multiple are currently selected
|
||||
if (changes.length === 1 && changes[0].type == 'select' && changes[0].selected === false) {
|
||||
changes[0].selected = getNodes().filter(node => node.selected).length === 1;
|
||||
}
|
||||
|
||||
const nextChanges = changes.reduce((acc, change) => {
|
||||
if (change.type !== 'remove') {
|
||||
return [...acc, change];
|
||||
@@ -216,7 +231,7 @@ const MapComp = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.MapRoot}>
|
||||
<div className={clsx(classes.MapRoot, { [classes.BackgroundAlternateColor]: isSoftBackground })}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
@@ -263,7 +278,7 @@ const MapComp = ({
|
||||
selectionMode={SelectionMode.Partial}
|
||||
>
|
||||
{isShowMinimap && <MiniMap pannable zoomable ariaLabel="Mini map" className={minimapClasses} />}
|
||||
<Background />
|
||||
{isShowBackgroundPattern && <Background variant={variant} gap={gap} size={size} color={color} />}
|
||||
</ReactFlow>
|
||||
{/* <button className="z-auto btn btn-primary absolute top-20 right-20" onClick={handleGetPassages}>
|
||||
Test // DON NOT REMOVE
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.ConnectionTimeEOL {
|
||||
background-image: linear-gradient(207deg, transparent, #7452c3e3);
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-time-eol));
|
||||
}
|
||||
|
||||
.ConnectionFrigate {
|
||||
background-image: linear-gradient(207deg, transparent, #325d88);
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-frigate));
|
||||
}
|
||||
|
||||
.ConnectionSave {
|
||||
background-image: linear-gradient(207deg, transparent, rgba(155, 102, 45, 0.85));
|
||||
background-image: linear-gradient(207deg, transparent, var(--conn-save));
|
||||
}
|
||||
|
||||
.SelectedItem {
|
||||
background-color: rgba(98, 98, 98, 0.33);
|
||||
background-color: var(--selected-item-bg);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,14 @@ import { Edge } from '@reactflow/core/dist/esm/types/edges';
|
||||
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
|
||||
import clsx from 'clsx';
|
||||
import classes from './ContextMenuConnection.module.scss';
|
||||
import { MASS_STATE_NAMES, MASS_STATE_NAMES_ORDER } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import {
|
||||
MASS_STATE_NAMES,
|
||||
MASS_STATE_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES,
|
||||
SHIP_SIZES_NAMES_ORDER,
|
||||
SHIP_SIZES_NAMES_SHORT,
|
||||
SHIP_SIZES_SIZE,
|
||||
} from '@/hooks/Mapper/components/map/constants.ts';
|
||||
|
||||
export interface ContextMenuConnectionProps {
|
||||
contextMenuRef: RefObject<ContextMenu>;
|
||||
@@ -48,10 +55,6 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.CLOCK,
|
||||
command: onChangeTimeState,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Frigate`,
|
||||
className: clsx({
|
||||
@@ -60,13 +63,9 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.CLOUD,
|
||||
command: () =>
|
||||
onChangeShipSizeStatus(
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.normal : ShipSizeStatus.small,
|
||||
edge.data?.ship_size_type === ShipSizeStatus.small ? ShipSizeStatus.large : ShipSizeStatus.small,
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole
|
||||
? [
|
||||
{
|
||||
label: `Save mass`,
|
||||
className: clsx({
|
||||
@@ -75,19 +74,40 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
icon: PrimeIcons.LOCK,
|
||||
command: () => onToggleMassSave(!edge.data?.locked),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(isWormhole && !isFrigateSize
|
||||
? [
|
||||
...(!isFrigateSize
|
||||
? [
|
||||
{
|
||||
label: `Mass status`,
|
||||
icon: PrimeIcons.CHART_PIE,
|
||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
||||
label: MASS_STATE_NAMES[x],
|
||||
className: clsx({
|
||||
[classes.SelectedItem]: edge.data?.mass_status === x,
|
||||
}),
|
||||
command: () => onChangeMassState(x),
|
||||
})),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
label: `Mass status`,
|
||||
icon: PrimeIcons.CHART_PIE,
|
||||
items: MASS_STATE_NAMES_ORDER.map(x => ({
|
||||
label: MASS_STATE_NAMES[x],
|
||||
label: `Ship Size`,
|
||||
icon: PrimeIcons.CLOUD,
|
||||
items: SHIP_SIZES_NAMES_ORDER.map(x => ({
|
||||
label: (
|
||||
<div className="grid grid-cols-[20px_120px_1fr_40px] gap-2 items-center">
|
||||
<div className="text-[12px] font-bold text-stone-400">{SHIP_SIZES_NAMES_SHORT[x]}</div>
|
||||
<div>{SHIP_SIZES_NAMES[x]}</div>
|
||||
<div></div>
|
||||
<div className="flex justify-end whitespace-nowrap text-[12px] font-bold text-stone-500">
|
||||
{SHIP_SIZES_SIZE[x]} t.
|
||||
</div>
|
||||
</div>
|
||||
) as unknown as string, // TODO my lovely kostyl
|
||||
className: clsx({
|
||||
[classes.SelectedItem]: edge.data?.mass_status === x,
|
||||
[classes.SelectedItem]: edge.data?.ship_size_type === x,
|
||||
}),
|
||||
command: () => onChangeMassState(x),
|
||||
command: () => onChangeShipSizeStatus(x),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -98,7 +118,7 @@ export const ContextMenuConnection: React.FC<ContextMenuConnectionProps> = ({
|
||||
command: onDeleteConnection,
|
||||
},
|
||||
];
|
||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeMassState, onChangeShipSizeStatus]);
|
||||
}, [edge, onChangeTimeState, onDeleteConnection, onChangeShipSizeStatus, onToggleMassSave, onChangeMassState]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -97,14 +97,16 @@ export const useContextMenuConnectionHandlers = () => {
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionMassStatus,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: MassState.normal,
|
||||
},
|
||||
});
|
||||
if (status === ShipSizeStatus.small) {
|
||||
outCommand({
|
||||
type: OutCommand.updateConnectionMassStatus,
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
value: MassState.normal,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onToggleMassSave = useCallback((locked: boolean) => {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useReactFlow, XYPosition } from 'reactflow';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { useMapState } from '../../MapProvider.tsx';
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { OnMapAddSystemCallback } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
|
||||
export const useContextMenuRootHandlers = () => {
|
||||
type UseContextMenuRootHandlers = {
|
||||
onAddSystem?: OnMapAddSystemCallback;
|
||||
};
|
||||
|
||||
export const useContextMenuRootHandlers = ({ onAddSystem }: UseContextMenuRootHandlers = {}) => {
|
||||
const rf = useReactFlow();
|
||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||
const { outCommand } = useMapState();
|
||||
const [position, setPosition] = useState<XYPosition | null>(null);
|
||||
|
||||
const handleRootContext = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -18,14 +20,17 @@ export const useContextMenuRootHandlers = () => {
|
||||
contextMenuRef.current?.show(e);
|
||||
};
|
||||
|
||||
const onAddSystem = () => {
|
||||
outCommand({ type: OutCommand.manualAddSystem, data: { coordinates: position } });
|
||||
};
|
||||
const ref = useRef({ onAddSystem, position });
|
||||
ref.current = { onAddSystem, position };
|
||||
|
||||
const onAddSystemCallback = useCallback(() => {
|
||||
ref.current.onAddSystem?.({ coordinates: position });
|
||||
}, [position]);
|
||||
|
||||
return {
|
||||
handleRootContext,
|
||||
|
||||
contextMenuRef,
|
||||
onAddSystem,
|
||||
onAddSystem: onAddSystemCallback,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
@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;
|
||||
stroke: var(--pastel-yellow);
|
||||
}
|
||||
}
|
||||
|
||||
.EdgePathFront {
|
||||
fill: none;
|
||||
|
||||
stroke: #2c3844;
|
||||
stroke-width: 2px;
|
||||
|
||||
&.MassVerge:not(&.Frigate) {
|
||||
stroke: #af2900;
|
||||
stroke: #af0000;
|
||||
}
|
||||
|
||||
&.MassHalf:not(&.Frigate) {
|
||||
stroke: #a85f00;
|
||||
stroke: #ffd700;
|
||||
}
|
||||
|
||||
&.Frigate {
|
||||
@@ -56,7 +55,6 @@
|
||||
|
||||
.EdgePathBack {
|
||||
fill: none;
|
||||
|
||||
stroke: #80a5c5;
|
||||
stroke-width: 3px;
|
||||
|
||||
@@ -84,7 +82,6 @@
|
||||
&.Gate {
|
||||
stroke: #9aff40;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ClickPath {
|
||||
@@ -93,7 +90,7 @@
|
||||
stroke-width: 8px;
|
||||
}
|
||||
|
||||
.LinkLabel{
|
||||
.LinkLabel {
|
||||
font-size: 9px;
|
||||
line-height: 10px;
|
||||
padding: 2px 4px;
|
||||
@@ -113,7 +110,7 @@
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid #5a7d9a;
|
||||
border: 1px solid var(--pastel-blue);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
z-index: 1001;
|
||||
@@ -127,4 +124,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeS
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
|
||||
import { SHIP_SIZES_DESCRIPTION, SHIP_SIZES_NAMES_SHORT } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
|
||||
const MAP_TRANSLATES: Record<string, string> = {
|
||||
[Position.Top]: 'translate(-48%, 0%)',
|
||||
@@ -30,6 +31,14 @@ const MAP_OFFSETS: Record<string, { x: number; y: number }> = {
|
||||
[Position.Right]: { x: 0, y: 0 },
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_COLORS = {
|
||||
[ShipSizeStatus.small]: 'bg-indigo-400',
|
||||
[ShipSizeStatus.medium]: 'bg-cyan-500',
|
||||
[ShipSizeStatus.large]: '',
|
||||
[ShipSizeStatus.freight]: 'bg-lime-400',
|
||||
[ShipSizeStatus.capital]: 'bg-red-400',
|
||||
};
|
||||
|
||||
export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }: EdgeProps<SolarSystemConnection>) => {
|
||||
const sourceNode = useStore(useCallback(store => store.nodeInternals.get(source), [source]));
|
||||
const targetNode = useStore(useCallback(store => store.nodeInternals.get(target), [target]));
|
||||
@@ -137,6 +146,19 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
|
||||
<span className={clsx(PrimeIcons.LOCK, classes.icon)} />
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{isWormhole && data.ship_size_type !== ShipSizeStatus.large && (
|
||||
<WdTooltipWrapper
|
||||
content={SHIP_SIZES_DESCRIPTION[data.ship_size_type]}
|
||||
className={clsx(
|
||||
classes.LinkLabel,
|
||||
'pointer-events-auto rounded opacity-100 cursor-auto text-neutral-900 font-bold',
|
||||
SHIP_SIZES_COLORS[data.ship_size_type],
|
||||
)}
|
||||
>
|
||||
{SHIP_SIZES_NAMES_SHORT[data.ship_size_type]}
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
</div>
|
||||
</EdgeLabelRenderer>
|
||||
</>
|
||||
|
||||
@@ -1,30 +1,54 @@
|
||||
@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; // Темный фон для подсказок
|
||||
|
||||
.RootCustomNode {
|
||||
display: flex;
|
||||
width: 130px;
|
||||
height: 34px;
|
||||
|
||||
flex-direction: column;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
|
||||
background-color: $tooltip-bg;
|
||||
box-shadow: 0 0 5px rgba($dark-bg, 0.5);
|
||||
border: 1px solid darken($pastel-blue, 10%);
|
||||
background-color: var(--tooltip-bg);
|
||||
box-shadow: 0 0 5px rgba(45, 45, 45, 0.5);
|
||||
border: 1px solid var(--pastel-blue-darken10);
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.lockIcon {
|
||||
font-size: 0.45rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.mapMarkerIcon {
|
||||
font-size: 0.45rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.localCounterIcon {
|
||||
font-size: 0.50rem;
|
||||
}
|
||||
|
||||
.textShadowThin {
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.tagSkyFontMedium {
|
||||
color: #38bdf8; /* same as Tailwind text-sky-400 */
|
||||
font-weight: 500; /* same as font-medium */
|
||||
}
|
||||
|
||||
.hasUserCharacters {
|
||||
/* used to replace text-amber-300 */
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
.systemNameOverflow {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
&.Mataria,
|
||||
&.Amarria,
|
||||
&.Gallente,
|
||||
@@ -81,62 +105,62 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
border-color: var(--pastel-pink);
|
||||
box-shadow: 0 0 10px #9a1af1c2;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background-color: $tooltip-bg;
|
||||
color: $text-color;
|
||||
background-color: var(--tooltip-bg);
|
||||
color: var(--text-color);
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $pastel-pink;
|
||||
border: 1px solid var(--pastel-pink);
|
||||
}
|
||||
|
||||
&.eve-system-status-home {
|
||||
border: 1px solid darken($eve-solar-system-status-color-home, 30%);
|
||||
background-image: linear-gradient(275deg, $eve-solar-system-status-friendly, transparent);
|
||||
border: 1px solid var(--eve-solar-system-status-color-home-dark30);
|
||||
background-image: linear-gradient(275deg, var(--eve-solar-system-status-friendly), transparent);
|
||||
|
||||
&.selected {
|
||||
border-color: $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;
|
||||
border-color: var(--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);
|
||||
}
|
||||
}
|
||||
|
||||
.Bookmarks {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
left: 4px;
|
||||
|
||||
@@ -154,8 +178,6 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
|
||||
//background-color: #833ca4;
|
||||
|
||||
&:not(:first-child) {
|
||||
box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -174,7 +196,6 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
height: 8px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
& > .text {
|
||||
margin-top: 1px;
|
||||
font-size: 9px;
|
||||
@@ -182,6 +203,40 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
@@ -208,11 +263,9 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 2px rgba(231, 146, 52, 0.73);
|
||||
|
||||
color: #ffb01d;
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
@@ -221,7 +274,6 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
}
|
||||
|
||||
.classSystemName {
|
||||
//font-weight: bold;
|
||||
}
|
||||
|
||||
.solarSystemName {
|
||||
@@ -236,20 +288,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
|
||||
.localCounter {
|
||||
display: flex;
|
||||
//align-items: center;
|
||||
gap: 2px;
|
||||
|
||||
& > i {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
& > span {
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
font-weight: 500;
|
||||
//margin-top: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,13 +309,10 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
& > * {
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
/* Firefox kostyl */
|
||||
@-moz-document url-prefix() {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.Handlers {
|
||||
@@ -291,26 +327,22 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid $pastel-blue;
|
||||
border: 1px solid var(--pastel-blue);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: $pastel-pink;
|
||||
border-color: var(--pastel-pink);
|
||||
}
|
||||
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
@@ -322,15 +354,12 @@ $tooltip-bg: #202020; // Темный фон для подсказок
|
||||
&.HandleTop {
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.HandleRight {
|
||||
right: -3px;
|
||||
}
|
||||
|
||||
&.HandleBottom {
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
&.HandleLeft {
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ 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 { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
|
||||
|
||||
import {
|
||||
EFFECT_BACKGROUND_STYLES,
|
||||
LABELS_INFO,
|
||||
@@ -12,8 +16,9 @@ import {
|
||||
} 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 } from '@/hooks/Mapper/components/map/helpers';
|
||||
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';
|
||||
@@ -50,6 +55,11 @@ export const getActivityType = (count: number) => {
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarSystemType>) => {
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowUnsplashedSignatures } = interfaceSettings;
|
||||
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
|
||||
const {
|
||||
system_class,
|
||||
security,
|
||||
@@ -63,9 +73,10 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
solar_system_name,
|
||||
} = data.system_static_info;
|
||||
|
||||
const { locked, name, tag, status, labels, id } = data || {};
|
||||
const signatures = data.system_signatures;
|
||||
|
||||
const { locked, name, tag, status, labels, id, temporary_name: temporaryName } = data || {};
|
||||
|
||||
const customName = solar_system_name !== name ? name : undefined;
|
||||
|
||||
const {
|
||||
data: {
|
||||
@@ -87,8 +98,9 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
const visible = useMemo(() => visibleNodes.has(id), [id, visibleNodes]);
|
||||
|
||||
const charactersInSystem = useMemo(() => {
|
||||
return characters.filter(c => c.location?.solar_system_id === solar_system_id).filter(c => c.online);
|
||||
// eslint-disable-next-line
|
||||
return characters
|
||||
.filter(c => c.location?.solar_system_id === solar_system_id)
|
||||
.filter(c => c.online);
|
||||
}, [characters, presentCharacters, solar_system_id]);
|
||||
|
||||
const isWormhole = isWormholeSpace(system_class);
|
||||
@@ -106,7 +118,6 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
if (!systemKills) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return systemKills;
|
||||
}, [kills, solar_system_id]);
|
||||
|
||||
@@ -128,13 +139,33 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
const space = showKSpaceBG ? REGIONS_MAP[region_id] : '';
|
||||
const regionClass = showKSpaceBG ? SpaceToClass[space] : null;
|
||||
|
||||
const systemName = isTempSystemNameEnabled && temporaryName || solar_system_name;
|
||||
|
||||
const customName = (isTempSystemNameEnabled && temporaryName && name) || (solar_system_name !== name && name);
|
||||
|
||||
const [unsplashedLeft, unsplashedRight] = useMemo(() => {
|
||||
if (!isShowUnsplashedSignatures) {
|
||||
return [[], []];
|
||||
}
|
||||
|
||||
return prepareUnsplashedChunks(
|
||||
signatures
|
||||
.filter(s => s.group === 'Wormhole' && !s.linked_system)
|
||||
.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>
|
||||
<span className={clsx(classes.textShadowThin)}>{labelCustom}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -162,26 +193,36 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(classes.RootCustomNode, regionClass, classes[STATUS_CLASSES[status]], {
|
||||
[classes.selected]: selected,
|
||||
})}
|
||||
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%)]')}>
|
||||
<div
|
||||
className={clsx(
|
||||
classes.classTitle,
|
||||
classTitleColor,
|
||||
classes.textShadowThin,
|
||||
)}
|
||||
>
|
||||
{class_title ?? '-'}
|
||||
</div>
|
||||
{tag != null && tag !== '' && (
|
||||
<div className={clsx(classes.TagTitle, 'text-sky-400 font-medium')}>{tag}</div>
|
||||
<div className={clsx(classes.TagTitle, classes.tagSkyFontMedium)}>{tag}</div>
|
||||
)}
|
||||
<div
|
||||
className={clsx(
|
||||
classes.classSystemName,
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] flex-grow overflow-hidden text-ellipsis whitespace-nowrap font-sans',
|
||||
classes.textShadowThin,
|
||||
classes.systemNameOverflow
|
||||
)}
|
||||
>
|
||||
{solar_system_name}
|
||||
{systemName}
|
||||
</div>
|
||||
|
||||
{isWormhole && (
|
||||
@@ -193,23 +234,24 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
)}
|
||||
|
||||
{effect_name !== null && isWormhole && (
|
||||
<div className={clsx(classes.effect, EFFECT_BACKGROUND_STYLES[effect_name])}></div>
|
||||
<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">
|
||||
<div className={clsx(classes.customName)}>
|
||||
{customName}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWormhole && !customName && (
|
||||
<div
|
||||
className={clsx(
|
||||
'[text-shadow:_0_1px_0_rgb(0_0_0_/_40%)] text-stone-300 whitespace-nowrap overflow-hidden text-ellipsis mr-0.5',
|
||||
)}
|
||||
>
|
||||
<div className={clsx(classes.regionName)}>
|
||||
{region_name}
|
||||
</div>
|
||||
)}
|
||||
@@ -218,15 +260,19 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
|
||||
<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>}
|
||||
|
||||
{locked && <i className={clsx(PrimeIcons.LOCK, classes.lockIcon)} />}
|
||||
{hubs.includes(solar_system_id.toString()) && (
|
||||
<i className={PrimeIcons.MAP_MARKER} style={{ fontSize: '0.45rem', fontWeight: 'bold' }}></i>
|
||||
<i className={clsx(PrimeIcons.MAP_MARKER, classes.mapMarkerIcon)} />
|
||||
)}
|
||||
|
||||
{charactersInSystem.length > 0 && (
|
||||
<div className={clsx(classes.localCounter, { ['text-amber-300']: hasUserCharacters })}>
|
||||
<i className="pi pi-users" style={{ fontSize: '0.50rem' }}></i>
|
||||
<div
|
||||
className={clsx(
|
||||
classes.localCounter,
|
||||
{ [classes.hasUserCharacters]: hasUserCharacters },
|
||||
)}
|
||||
>
|
||||
<i className={clsx('pi pi-users', classes.localCounterIcon)} />
|
||||
<span className="font-sans">{charactersInSystem.length}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -237,6 +283,22 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps<MapSolarS
|
||||
)}
|
||||
</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"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
@import '@/hooks/Mapper/components/map/styles/eve-common-variables';
|
||||
|
||||
.Signature {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
display: block;
|
||||
|
||||
& > .Box {
|
||||
width: 13px;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-color);
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
font-weight: bolder;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { InfoDrawer } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
import classes from './UnsplashedSignature.module.scss';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders';
|
||||
|
||||
import { k162Types } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect';
|
||||
|
||||
interface UnsplashedSignatureProps {
|
||||
signature: SystemSignature;
|
||||
}
|
||||
export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) => {
|
||||
const {
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const whData = useMemo(() => wormholesData[signature.type], [signature.type, wormholesData]);
|
||||
const whClass = useMemo(() => (whData ? WORMHOLES_ADDITIONAL_INFO[whData.dest] : null), [whData]);
|
||||
|
||||
const k162TypeOption = useMemo(() => {
|
||||
if (!signature.custom_info) {
|
||||
return null;
|
||||
}
|
||||
const customInfo = JSON.parse(signature.custom_info);
|
||||
if (!customInfo.k162Type) {
|
||||
return null;
|
||||
}
|
||||
return k162Types.find(x => x.value === customInfo.k162Type);
|
||||
}, [signature]);
|
||||
|
||||
const whClassStyle = useMemo(() => {
|
||||
if (signature.type === 'K162' && k162TypeOption) {
|
||||
const k162Data = wormholesData[k162TypeOption.whClassName];
|
||||
const k162Class = k162Data ? WORMHOLES_ADDITIONAL_INFO[k162Data.dest] : null;
|
||||
return k162Class ? WORMHOLE_CLASS_STYLES[k162Class.wormholeClassID] : '';
|
||||
}
|
||||
return whClass ? WORMHOLE_CLASS_STYLES[whClass.wormholeClassID] : '';
|
||||
}, [signature, whClass, k162TypeOption, wormholesData]);
|
||||
|
||||
return (
|
||||
<WdTooltipWrapper
|
||||
className={clsx(classes.Signature)}
|
||||
content={
|
||||
(
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title={<b className="text-slate-50">{signature.eve_id}</b>}>
|
||||
{renderInfoColumn(signature)}
|
||||
</InfoDrawer>
|
||||
</div>
|
||||
) as React.ReactNode
|
||||
}
|
||||
>
|
||||
<div className={clsx(classes.Box, whClassStyle)}>
|
||||
<svg width="13" height="4" viewBox="0 0 13 4" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="13" height="4" rx="2" className={whClassStyle} fill="currentColor" />
|
||||
</svg>
|
||||
</div>
|
||||
</WdTooltipWrapper>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './UnsplashedSignature.tsx';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ConnectionType, MassState } from '@/hooks/Mapper/types';
|
||||
import { ConnectionType, MassState, ShipSizeStatus } from '@/hooks/Mapper/types';
|
||||
|
||||
export enum SOLAR_SYSTEM_CLASS_IDS {
|
||||
ccp1 = -1,
|
||||
@@ -727,16 +727,41 @@ export const MASS_STATE_NAMES = {
|
||||
[MassState.verge]: 'Verge of collapse',
|
||||
};
|
||||
|
||||
// export const SHIP_SIZES_NAMES_ORDER = [
|
||||
// ShipSizeStatus.small,
|
||||
// ShipSizeStatus.normal,
|
||||
// // ShipSizeStatus.large,
|
||||
// // ShipSizeStatus.capital,
|
||||
// ];
|
||||
//
|
||||
// export const SHIP_SIZES_NAMES = {
|
||||
// [ShipSizeStatus.small]: 'Frigate',
|
||||
// [ShipSizeStatus.normal]: 'Normal',
|
||||
// // [ShipSizeStatus.large]: 'Normal',
|
||||
// // [ShipSizeStatus.capital]: 'Normal',
|
||||
// };
|
||||
export const SHIP_SIZES_NAMES_ORDER = [
|
||||
ShipSizeStatus.small,
|
||||
ShipSizeStatus.medium,
|
||||
ShipSizeStatus.large,
|
||||
ShipSizeStatus.freight,
|
||||
ShipSizeStatus.capital,
|
||||
];
|
||||
|
||||
export const SHIP_SIZES_NAMES = {
|
||||
[ShipSizeStatus.small]: 'Frigate',
|
||||
[ShipSizeStatus.medium]: 'Medium',
|
||||
[ShipSizeStatus.large]: 'Normal',
|
||||
[ShipSizeStatus.freight]: 'Huge',
|
||||
[ShipSizeStatus.capital]: 'Capital',
|
||||
};
|
||||
export const SHIP_SIZES_SIZE = {
|
||||
[ShipSizeStatus.small]: '5K',
|
||||
[ShipSizeStatus.medium]: '62K',
|
||||
[ShipSizeStatus.large]: '375K',
|
||||
[ShipSizeStatus.freight]: '1M',
|
||||
[ShipSizeStatus.capital]: '2M',
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_DESCRIPTION = {
|
||||
[ShipSizeStatus.small]: 'Frigate wormhole - up to Destroyer | 5K t.',
|
||||
[ShipSizeStatus.medium]: 'Cruise wormhole - up to Battlecruiser | 62K t.',
|
||||
[ShipSizeStatus.large]: 'Large wormhole - up to Battleship | 375K t.',
|
||||
[ShipSizeStatus.freight]: 'Huge wormhole - up to Freighter | 1M t.',
|
||||
[ShipSizeStatus.capital]: 'Capital wormhole - up to Capital | 2M t.',
|
||||
};
|
||||
|
||||
export const SHIP_SIZES_NAMES_SHORT = {
|
||||
[ShipSizeStatus.small]: 'S',
|
||||
[ShipSizeStatus.medium]: 'M',
|
||||
[ShipSizeStatus.large]: 'L',
|
||||
[ShipSizeStatus.freight]: 'H',
|
||||
[ShipSizeStatus.capital]: 'XL',
|
||||
};
|
||||
|
||||
@@ -3,3 +3,4 @@ export * from './convertSystem2Node';
|
||||
export * from './getSystemClassStyles';
|
||||
export * from './getShapeClass';
|
||||
export * from './getBackgroundClass';
|
||||
export * from './prepareUnsplashedChunks';
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Helper function to split an array into chunks of size
|
||||
const chunkArray = (array: any[], size: number) => {
|
||||
const chunks = [];
|
||||
for (let i = 0; i < array.length; i += size) {
|
||||
chunks.push(array.slice(i, i + size));
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
|
||||
export const prepareUnsplashedChunks = (items: any[]) => {
|
||||
// Split the items into chunks of 4
|
||||
const chunks = chunkArray(items, 4);
|
||||
|
||||
// Get the column elements
|
||||
const leftColumn: any[] = [];
|
||||
const rightColumn: any[] = [];
|
||||
|
||||
chunks.forEach((chunk, index) => {
|
||||
const column = index % 2 === 0 ? leftColumn : rightColumn;
|
||||
|
||||
chunk.forEach(item => {
|
||||
column.push(item);
|
||||
});
|
||||
});
|
||||
|
||||
return [leftColumn, rightColumn];
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BackgroundVariant } from 'reactflow';
|
||||
|
||||
|
||||
export function useBackgroundVars(themeName?: string) {
|
||||
const [variant, setVariant] = useState<BackgroundVariant>(BackgroundVariant.Dots);
|
||||
const [gap, setGap] = useState<number>(16);
|
||||
const [size, setSize] = useState<number>(1);
|
||||
const [color, setColor] = useState('#81818b')
|
||||
|
||||
useEffect(() => {
|
||||
let themeEl = document.querySelector('.pathfinder-theme, .neon-theme');
|
||||
if (!themeEl) {
|
||||
themeEl = document.documentElement;
|
||||
}
|
||||
|
||||
const style = getComputedStyle(themeEl as HTMLElement);
|
||||
|
||||
const rawVariant = style.getPropertyValue('--rf-bg-variant').replace(/['"]/g, '').trim().toLowerCase();
|
||||
let finalVariant: BackgroundVariant = BackgroundVariant.Dots;
|
||||
if (rawVariant === 'lines') {
|
||||
finalVariant = BackgroundVariant.Lines;
|
||||
} else if (rawVariant === 'cross') {
|
||||
finalVariant = BackgroundVariant.Cross;
|
||||
}
|
||||
|
||||
const cssVarGap = style.getPropertyValue('--rf-bg-gap');
|
||||
const cssVarSize = style.getPropertyValue('--rf-bg-size');
|
||||
const cssColor = style.getPropertyValue('--rf-bg-color');
|
||||
|
||||
const gapNum = parseInt(cssVarGap, 10) || 16;
|
||||
const sizeNum = parseInt(cssVarSize, 10) || 1;
|
||||
|
||||
setVariant(finalVariant);
|
||||
setGap(gapNum);
|
||||
setSize(sizeNum);
|
||||
setColor(cssColor);
|
||||
|
||||
}, [themeName]);
|
||||
|
||||
return { variant, gap, size, color };
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types/system';
|
||||
import { SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { XYPosition } from 'reactflow';
|
||||
|
||||
export type MapSolarSystemType = Omit<SolarSystemRawType, 'position'>;
|
||||
|
||||
@@ -7,3 +8,5 @@ export type OnMapSelectionChange = (event: {
|
||||
systems: string[];
|
||||
connections: Pick<SolarSystemConnection, 'source' | 'target'>[];
|
||||
}) => void;
|
||||
|
||||
export type OnMapAddSystemCallback = (props: { coordinates: XYPosition | null }) => void;
|
||||
|
||||
@@ -1,78 +1,114 @@
|
||||
$eve-link-color-default: #333;
|
||||
$eve-link-color-top-mass-0: #333;
|
||||
$eve-link-color-top-mass-1: #5a4520;
|
||||
$eve-link-color-top-mass-2: #672c2c;
|
||||
$eve-link-color-middle-mass-0: #333;
|
||||
$eve-link-color-middle-mass-1: #333;
|
||||
$eve-link-color-middle-mass-2: #333;
|
||||
$eve-link-color-middle-time-0: #5c5c5c;
|
||||
$eve-link-color-middle-time-1: #ff00cd;
|
||||
$eve-link-color-middle-time-1-border: #99f3ff;
|
||||
:root {
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
$eve-link-color-top-mass-1-time-1: #796300;
|
||||
$eve-link-color-top-mass-2-time-1: #8c1717;
|
||||
$eve-link-color-temp: orange;
|
||||
--pastel-blue-darken10: #4f6b86;
|
||||
--pastel-blue-lighten10: #6da3af;
|
||||
--pastel-pink-darken10: #bb7ca9;
|
||||
--pastel-pink-lighten10: #e0a6cb;
|
||||
|
||||
$eve-effect-pulsar: #40aef5;
|
||||
$eve-effect-magnetar: #f058f8;
|
||||
$eve-effect-wolfRayet: #ef7843;
|
||||
$eve-effect-blackHole: #1b1b1b;
|
||||
$eve-effect-cataclysmicVariable: #ffea90;
|
||||
$eve-effect-redGiant: #fd3c3c;
|
||||
$eve-effect-dazhLiminalityLocus: #ff6464;
|
||||
$eve-effect-imperialStellarObservatory: #6991ce;
|
||||
$eve-effect-stateStellarObservatory: #6991ce;
|
||||
$eve-effect-republicStellarObservatory: #6991ce;
|
||||
$eve-effect-federalStellarObservatory: #6991ce;
|
||||
--pastel-green-darken10: #79a244;
|
||||
--pastel-green-lighten10: #99cf52;
|
||||
|
||||
$eve-wh-type-color-high: #5dffd2;
|
||||
$eve-wh-type-color-low: #d96c07;
|
||||
$eve-wh-type-color-null: #8b3263;
|
||||
$eve-wh-type-color-c1: #69bfce;
|
||||
$eve-wh-type-color-c2: #6991ce;
|
||||
$eve-wh-type-color-c3: #a8cb70;
|
||||
$eve-wh-type-color-c4: #e39c68;
|
||||
$eve-wh-type-color-c5: #de8686;
|
||||
$eve-wh-type-color-c6: #e76363;
|
||||
$eve-wh-type-color-c13: #988cb5;
|
||||
$eve-wh-type-color-drifter: #ff44f6;
|
||||
$eve-wh-type-color-thera: #ffffff;
|
||||
$eve-wh-type-color-zarzakh: #212121;
|
||||
--pastel-yellow-darken10: #e6c44f;
|
||||
--pastel-yellow-lighten10: #ffe874;
|
||||
|
||||
$eve-security-color-10: #2c74df;
|
||||
$eve-security-color-09: #3998e8;
|
||||
$eve-security-color-08: #4dcbf5;
|
||||
$eve-security-color-07: #60d8a2;
|
||||
$eve-security-color-06: #71e454;
|
||||
$eve-security-color-05: #f2fc81;
|
||||
$eve-security-color-04: #d96c07;
|
||||
$eve-security-color-03: #cb440f;
|
||||
$eve-security-color-02: #b91117;
|
||||
$eve-security-color-01: #732020;
|
||||
$eve-security-color-00: #8b3263;
|
||||
$eve-security-color-m-01: #8b3263;
|
||||
$eve-security-color-m-02: #8b3263;
|
||||
$eve-security-color-m-03: #8b3263;
|
||||
$eve-security-color-m-04: #8b3263;
|
||||
$eve-security-color-m-05: #8b3263;
|
||||
$eve-security-color-m-06: #8b3263;
|
||||
$eve-security-color-m-07: #8b3263;
|
||||
$eve-security-color-m-08: #8b3263;
|
||||
$eve-security-color-m-09: #8b3263;
|
||||
$eve-security-color-m-10: #8b3263;
|
||||
/* Eve Link Colors */
|
||||
--eve-link-color-default: #333;
|
||||
--eve-link-color-top-mass-0: #333;
|
||||
--eve-link-color-top-mass-1: #5a4520;
|
||||
--eve-link-color-top-mass-2: #672c2c;
|
||||
--eve-link-color-middle-mass-0: #333;
|
||||
--eve-link-color-middle-mass-1: #333;
|
||||
--eve-link-color-middle-mass-2: #333;
|
||||
--eve-link-color-middle-time-0: #5c5c5c;
|
||||
--eve-link-color-middle-time-1: #ff00cd;
|
||||
--eve-link-color-middle-time-1-border: #99f3ff;
|
||||
--eve-link-color-top-mass-1-time-1: #796300;
|
||||
--eve-link-color-top-mass-2-time-1: #8c1717;
|
||||
--eve-link-color-temp: orange;
|
||||
|
||||
$eve-solar-system-status-unknown: transparent;
|
||||
$eve-solar-system-status-friendly: #3bbd3952;
|
||||
$eve-solar-system-status-warning: #906518a6;
|
||||
$eve-solar-system-status-target: #b439ff6b;
|
||||
$eve-solar-system-status-dangerous: #d54040;
|
||||
$eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
||||
$eve-solar-system-status-home: rgb(197, 253, 67);
|
||||
/* Wormhole Effects */
|
||||
--eve-effect-pulsar: #40aef5;
|
||||
--eve-effect-magnetar: #f058f8;
|
||||
--eve-effect-wolfRayet: #ef7843;
|
||||
--eve-effect-blackHole: #1b1b1b;
|
||||
--eve-effect-cataclysmicVariable: #ffea90;
|
||||
--eve-effect-redGiant: #fd3c3c;
|
||||
--eve-effect-dazhLiminalityLocus: #ff6464;
|
||||
--eve-effect-imperialStellarObservatory: #6991ce;
|
||||
--eve-effect-stateStellarObservatory: #6991ce;
|
||||
--eve-effect-republicStellarObservatory: #6991ce;
|
||||
--eve-effect-federalStellarObservatory: #6991ce;
|
||||
|
||||
$eve-solar-system-status-color-unknown: transparent;
|
||||
$eve-solar-system-status-color-friendly: #3bbd39;
|
||||
$eve-solar-system-status-color-warning: #ffb93b;
|
||||
$eve-solar-system-status-color-target: #b439ff;
|
||||
$eve-solar-system-status-color-dangerous: #d54040;
|
||||
$eve-solar-system-status-color-lookingFor: #43c2fd;
|
||||
$eve-solar-system-status-color-home: rgb(197, 253, 67);
|
||||
/* WH Types */
|
||||
--eve-wh-type-color-high: #5dffd2;
|
||||
--eve-wh-type-color-low: #f79400;
|
||||
--eve-wh-type-color-null: #fc3c3c;
|
||||
--eve-wh-type-color-c1: #69bfce;
|
||||
--eve-wh-type-color-c2: #6991ce;
|
||||
--eve-wh-type-color-c3: #a8cb70;
|
||||
--eve-wh-type-color-c4: #e39c68;
|
||||
--eve-wh-type-color-c5: #de8686;
|
||||
--eve-wh-type-color-c6: #e76363;
|
||||
--eve-wh-type-color-c13: #988cb5;
|
||||
--eve-wh-type-color-drifter: #ff44f6;
|
||||
--eve-wh-type-color-thera: #ffffff;
|
||||
--eve-wh-type-color-zarzakh: #212121;
|
||||
|
||||
/* Security Colors */
|
||||
--eve-security-color-10: #2c74df;
|
||||
--eve-security-color-09: #3998e8;
|
||||
--eve-security-color-08: #4dcbf5;
|
||||
--eve-security-color-07: #60d8a2;
|
||||
--eve-security-color-06: #71e454;
|
||||
--eve-security-color-05: #f2fc81;
|
||||
--eve-security-color-04: #d96c07;
|
||||
--eve-security-color-03: #cb440f;
|
||||
--eve-security-color-02: #b91117;
|
||||
--eve-security-color-01: #732020;
|
||||
--eve-security-color-00: #8b3263;
|
||||
--eve-security-color-m-01: #8b3263;
|
||||
--eve-security-color-m-02: #8b3263;
|
||||
--eve-security-color-m-03: #8b3263;
|
||||
--eve-security-color-m-04: #8b3263;
|
||||
--eve-security-color-m-05: #8b3263;
|
||||
--eve-security-color-m-06: #8b3263;
|
||||
--eve-security-color-m-07: #8b3263;
|
||||
--eve-security-color-m-08: #8b3263;
|
||||
--eve-security-color-m-09: #8b3263;
|
||||
--eve-security-color-m-10: #8b3263;
|
||||
|
||||
/* Solar System Status */
|
||||
--eve-solar-system-status-unknown: transparent;
|
||||
--eve-solar-system-status-friendly: #3bbd3952;
|
||||
--eve-solar-system-status-warning: #906518a6;
|
||||
--eve-solar-system-status-target: #b439ff6b;
|
||||
--eve-solar-system-status-dangerous: #d54040;
|
||||
--eve-solar-system-status-lookingFor: rgba(67, 176, 253, 0.48);
|
||||
--eve-solar-system-status-home: rgb(197, 253, 67);
|
||||
|
||||
--eve-solar-system-status-color-unknown: transparent;
|
||||
--eve-solar-system-status-color-friendly: #3bbd39;
|
||||
--eve-solar-system-status-color-warning: #ffb93b;
|
||||
--eve-solar-system-status-color-target: #b439ff;
|
||||
--eve-solar-system-status-color-dangerous: #d54040;
|
||||
--eve-solar-system-status-color-lookingFor: #43c2fd;
|
||||
--eve-solar-system-status-color-home: rgb(197, 253, 67);
|
||||
|
||||
--eve-solar-system-status-color-friendly-dark20: #2d9b2e;
|
||||
--eve-solar-system-status-friendly-dark30: #28892a;
|
||||
--eve-solar-system-status-color-friendly-dark5: #38b538;
|
||||
--eve-solar-system-status-color-lookingFor-dark15: #32aadf;
|
||||
|
||||
/* Context Menu */
|
||||
--conn-time-eol: #7452c3e3;
|
||||
--conn-frigate: #325d88;
|
||||
--conn-save: rgba(155, 102, 45, 0.85);
|
||||
--selected-item-bg: rgba(98, 98, 98, 0.33);
|
||||
}
|
||||
|
||||
@@ -1,535 +1,448 @@
|
||||
@import "eve-common-variables";
|
||||
@import './eve-common-variables';
|
||||
|
||||
|
||||
.eve-wh-effect-color-pulsar {
|
||||
fill: $eve-effect-pulsar;
|
||||
background-color: $eve-effect-pulsar;
|
||||
fill: var(--eve-effect-pulsar);
|
||||
background-color: var(--eve-effect-pulsar);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-magnetar {
|
||||
fill: $eve-effect-magnetar;
|
||||
background-color: $eve-effect-magnetar;
|
||||
fill: var(--eve-effect-magnetar);
|
||||
background-color: var(--eve-effect-magnetar);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-wolfRayet {
|
||||
fill: $eve-effect-wolfRayet;
|
||||
background-color: $eve-effect-wolfRayet;
|
||||
fill: var(--eve-effect-wolfRayet);
|
||||
background-color: var(--eve-effect-wolfRayet);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-blackHole {
|
||||
fill: $eve-effect-blackHole;
|
||||
background-color: $eve-effect-blackHole;
|
||||
box-shadow: 0 0 8px rgba(255 255 255 / 33);
|
||||
fill: var(--eve-effect-blackHole);
|
||||
background-color: var(--eve-effect-blackHole);
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.33);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-cataclysmicVariable {
|
||||
fill: $eve-effect-cataclysmicVariable;
|
||||
background-color: $eve-effect-cataclysmicVariable;
|
||||
fill: var(--eve-effect-cataclysmicVariable);
|
||||
background-color: var(--eve-effect-cataclysmicVariable);
|
||||
}
|
||||
|
||||
.eve-wh-effect-color-redGiant {
|
||||
fill: $eve-effect-redGiant;
|
||||
background-color: $eve-effect-redGiant;
|
||||
fill: var(--eve-effect-redGiant);
|
||||
background-color: var(--eve-effect-redGiant);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-pulsar {
|
||||
color: $eve-effect-pulsar;
|
||||
color: var(--eve-effect-pulsar);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-magnetar {
|
||||
color: $eve-effect-magnetar;
|
||||
color: var(--eve-effect-magnetar);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-wolfRayet {
|
||||
color: $eve-effect-wolfRayet;
|
||||
color: var(--eve-effect-wolfRayet);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-blackHole {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-cataclysmicVariable {
|
||||
color: $eve-effect-cataclysmicVariable;
|
||||
color: var(--eve-effect-cataclysmicVariable);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-redGiant {
|
||||
color: $eve-effect-redGiant;
|
||||
color: var(--eve-effect-redGiant);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-dazhLiminalityLocus {
|
||||
color: $eve-effect-dazhLiminalityLocus;
|
||||
color: var(--eve-effect-dazhLiminalityLocus);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-imperialStellarObservatory {
|
||||
color: $eve-effect-imperialStellarObservatory;
|
||||
color: var(--eve-effect-imperialStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-stateStellarObservatory {
|
||||
color: $eve-effect-stateStellarObservatory;
|
||||
color: var(--eve-effect-stateStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-republicStellarObservatory {
|
||||
color: $eve-effect-republicStellarObservatory;
|
||||
color: var(--eve-effect-republicStellarObservatory);
|
||||
}
|
||||
|
||||
.text-eve-wh-effect-color-federalStellarObservatory {
|
||||
color: $eve-effect-federalStellarObservatory;
|
||||
color: var(--eve-effect-federalStellarObservatory);
|
||||
}
|
||||
|
||||
/* Security color classes */
|
||||
.eve-security-color-10 {
|
||||
color: $eve-security-color-10 !important;
|
||||
fill: $eve-security-color-10;
|
||||
color: var(--eve-security-color-10) !important;
|
||||
fill: var(--eve-security-color-10);
|
||||
}
|
||||
|
||||
.eve-security-color-09 {
|
||||
color: $eve-security-color-09 !important;
|
||||
fill: $eve-security-color-09;
|
||||
color: var(--eve-security-color-09) !important;
|
||||
fill: var(--eve-security-color-09);
|
||||
}
|
||||
|
||||
.eve-security-color-08 {
|
||||
color: $eve-security-color-08 !important;
|
||||
fill: $eve-security-color-08;
|
||||
color: var(--eve-security-color-08) !important;
|
||||
fill: var(--eve-security-color-08);
|
||||
}
|
||||
|
||||
.eve-security-color-07 {
|
||||
color: $eve-security-color-07 !important;
|
||||
fill: $eve-security-color-07;
|
||||
color: var(--eve-security-color-07) !important;
|
||||
fill: var(--eve-security-color-07);
|
||||
}
|
||||
|
||||
.eve-security-color-06 {
|
||||
color: $eve-security-color-06 !important;
|
||||
fill: $eve-security-color-06;
|
||||
color: var(--eve-security-color-06) !important;
|
||||
fill: var(--eve-security-color-06);
|
||||
}
|
||||
|
||||
.eve-security-color-05 {
|
||||
color: $eve-security-color-05 !important;
|
||||
fill: $eve-security-color-05;
|
||||
color: var(--eve-security-color-05) !important;
|
||||
fill: var(--eve-security-color-05);
|
||||
}
|
||||
|
||||
.eve-security-color-04 {
|
||||
color: $eve-security-color-04 !important;
|
||||
fill: $eve-security-color-04;
|
||||
color: var(--eve-security-color-04) !important;
|
||||
fill: var(--eve-security-color-04);
|
||||
}
|
||||
|
||||
.eve-security-color-03 {
|
||||
color: $eve-security-color-03 !important;
|
||||
fill: $eve-security-color-03;
|
||||
color: var(--eve-security-color-03) !important;
|
||||
fill: var(--eve-security-color-03);
|
||||
}
|
||||
|
||||
.eve-security-color-02 {
|
||||
color: $eve-security-color-02 !important;
|
||||
fill: $eve-security-color-02;
|
||||
color: var(--eve-security-color-02) !important;
|
||||
fill: var(--eve-security-color-02);
|
||||
}
|
||||
|
||||
.eve-security-color-01 {
|
||||
color: $eve-security-color-01 !important;
|
||||
fill: $eve-security-color-01;
|
||||
color: var(--eve-security-color-01) !important;
|
||||
fill: var(--eve-security-color-01);
|
||||
}
|
||||
|
||||
.eve-security-color-00 {
|
||||
color: $eve-security-color-00 !important;
|
||||
fill: $eve-security-color-00;
|
||||
color: var(--eve-security-color-00) !important;
|
||||
fill: var(--eve-security-color-00);
|
||||
}
|
||||
|
||||
.eve-security-color-m-01 {
|
||||
color: $eve-security-color-m-01 !important;
|
||||
fill: $eve-security-color-m-01;
|
||||
color: var(--eve-security-color-m-01) !important;
|
||||
fill: var(--eve-security-color-m-01);
|
||||
}
|
||||
|
||||
.eve-security-color-m-02 {
|
||||
color: $eve-security-color-m-02 !important;
|
||||
fill: $eve-security-color-m-02;
|
||||
color: var(--eve-security-color-m-02) !important;
|
||||
fill: var(--eve-security-color-m-02);
|
||||
}
|
||||
|
||||
.eve-security-color-m-03 {
|
||||
color: $eve-security-color-m-03 !important;
|
||||
fill: $eve-security-color-m-03;
|
||||
color: var(--eve-security-color-m-03) !important;
|
||||
fill: var(--eve-security-color-m-03);
|
||||
}
|
||||
|
||||
.eve-security-color-m-04 {
|
||||
color: $eve-security-color-m-04 !important;
|
||||
fill: $eve-security-color-m-04;
|
||||
color: var(--eve-security-color-m-04) !important;
|
||||
fill: var(--eve-security-color-m-04);
|
||||
}
|
||||
|
||||
.eve-security-color-m-05 {
|
||||
color: $eve-security-color-m-05 !important;
|
||||
fill: $eve-security-color-m-05;
|
||||
color: var(--eve-security-color-m-05) !important;
|
||||
fill: var(--eve-security-color-m-05);
|
||||
}
|
||||
|
||||
.eve-security-color-m-06 {
|
||||
color: $eve-security-color-m-06 !important;
|
||||
fill: $eve-security-color-m-06;
|
||||
color: var(--eve-security-color-m-06) !important;
|
||||
fill: var(--eve-security-color-m-06);
|
||||
}
|
||||
|
||||
.eve-security-color-m-07 {
|
||||
color: $eve-security-color-m-07 !important;
|
||||
fill: $eve-security-color-m-07;
|
||||
color: var(--eve-security-color-m-07) !important;
|
||||
fill: var(--eve-security-color-m-07);
|
||||
}
|
||||
|
||||
.eve-security-color-m-08 {
|
||||
color: $eve-security-color-m-08 !important;
|
||||
fill: $eve-security-color-m-08;
|
||||
color: var(--eve-security-color-m-08) !important;
|
||||
fill: var(--eve-security-color-m-08);
|
||||
}
|
||||
|
||||
.eve-security-color-m-09 {
|
||||
color: $eve-security-color-m-09 !important;
|
||||
fill: $eve-security-color-m-09;
|
||||
color: var(--eve-security-color-m-09) !important;
|
||||
fill: var(--eve-security-color-m-09);
|
||||
}
|
||||
|
||||
.eve-security-color-m-10 {
|
||||
color: $eve-security-color-m-10 !important;
|
||||
fill: $eve-security-color-m-10;
|
||||
color: var(--eve-security-color-m-10) !important;
|
||||
fill: var(--eve-security-color-m-10);
|
||||
}
|
||||
|
||||
/* Security backgrounds */
|
||||
.eve-security-background-10 {
|
||||
background-color: $eve-security-color-10;
|
||||
fill: $eve-security-color-10;
|
||||
background-color: var(--eve-security-color-10);
|
||||
fill: var(--eve-security-color-10);
|
||||
}
|
||||
|
||||
.eve-security-background-09 {
|
||||
background-color: $eve-security-color-09;
|
||||
fill: $eve-security-color-09;
|
||||
background-color: var(--eve-security-color-09);
|
||||
fill: var(--eve-security-color-09);
|
||||
}
|
||||
|
||||
.eve-security-background-08 {
|
||||
background-color: $eve-security-color-08;
|
||||
fill: $eve-security-color-08;
|
||||
background-color: var(--eve-security-color-08);
|
||||
fill: var(--eve-security-color-08);
|
||||
}
|
||||
|
||||
.eve-security-background-07 {
|
||||
background-color: $eve-security-color-07;
|
||||
fill: $eve-security-color-07;
|
||||
background-color: var(--eve-security-color-07);
|
||||
fill: var(--eve-security-color-07);
|
||||
}
|
||||
|
||||
.eve-security-background-06 {
|
||||
background-color: $eve-security-color-06;
|
||||
fill: $eve-security-color-06;
|
||||
background-color: var(--eve-security-color-06);
|
||||
fill: var(--eve-security-color-06);
|
||||
}
|
||||
|
||||
.eve-security-background-05 {
|
||||
background-color: $eve-security-color-05;
|
||||
fill: $eve-security-color-05;
|
||||
background-color: var(--eve-security-color-05);
|
||||
fill: var(--eve-security-color-05);
|
||||
}
|
||||
|
||||
.eve-security-background-04 {
|
||||
background-color: $eve-security-color-04;
|
||||
fill: $eve-security-color-04;
|
||||
background-color: var(--eve-security-color-04);
|
||||
fill: var(--eve-security-color-04);
|
||||
}
|
||||
|
||||
.eve-security-background-03 {
|
||||
background-color: $eve-security-color-03;
|
||||
fill: $eve-security-color-03;
|
||||
background-color: var(--eve-security-color-03);
|
||||
fill: var(--eve-security-color-03);
|
||||
}
|
||||
|
||||
.eve-security-background-02 {
|
||||
background-color: $eve-security-color-02;
|
||||
fill: $eve-security-color-02;
|
||||
background-color: var(--eve-security-color-02);
|
||||
fill: var(--eve-security-color-02);
|
||||
}
|
||||
|
||||
.eve-security-background-01 {
|
||||
background-color: $eve-security-color-01;
|
||||
fill: $eve-security-color-01;
|
||||
background-color: var(--eve-security-color-01);
|
||||
fill: var(--eve-security-color-01);
|
||||
}
|
||||
|
||||
.eve-security-background-00 {
|
||||
background-color: $eve-security-color-00;
|
||||
fill: $eve-security-color-00;
|
||||
background-color: var(--eve-security-color-00);
|
||||
fill: var(--eve-security-color-00);
|
||||
}
|
||||
|
||||
.eve-security-background-m-01 {
|
||||
background-color: $eve-security-color-m-01;
|
||||
fill: $eve-security-color-m-01;
|
||||
background-color: var(--eve-security-color-m-01);
|
||||
fill: var(--eve-security-color-m-01);
|
||||
}
|
||||
|
||||
.eve-security-background-m-02 {
|
||||
background-color: $eve-security-color-m-02;
|
||||
fill: $eve-security-color-m-02;
|
||||
background-color: var(--eve-security-color-m-02);
|
||||
fill: var(--eve-security-color-m-02);
|
||||
}
|
||||
|
||||
.eve-security-background-m-03 {
|
||||
background-color: $eve-security-color-m-03;
|
||||
fill: $eve-security-color-m-03;
|
||||
background-color: var(--eve-security-color-m-03);
|
||||
fill: var(--eve-security-color-m-03);
|
||||
}
|
||||
|
||||
.eve-security-background-m-04 {
|
||||
background-color: $eve-security-color-m-04;
|
||||
fill: $eve-security-color-m-04;
|
||||
background-color: var(--eve-security-color-m-04);
|
||||
fill: var(--eve-security-color-m-04);
|
||||
}
|
||||
|
||||
.eve-security-background-m-05 {
|
||||
background-color: $eve-security-color-m-05;
|
||||
fill: $eve-security-color-m-05;
|
||||
background-color: var(--eve-security-color-m-05);
|
||||
fill: var(--eve-security-color-m-05);
|
||||
}
|
||||
|
||||
.eve-security-background-m-06 {
|
||||
background-color: $eve-security-color-m-06;
|
||||
fill: $eve-security-color-m-06;
|
||||
background-color: var(--eve-security-color-m-06);
|
||||
fill: var(--eve-security-color-m-06);
|
||||
}
|
||||
|
||||
.eve-security-background-m-07 {
|
||||
background-color: $eve-security-color-m-07;
|
||||
fill: $eve-security-color-m-07;
|
||||
background-color: var(--eve-security-color-m-07);
|
||||
fill: var(--eve-security-color-m-07);
|
||||
}
|
||||
|
||||
.eve-security-background-m-08 {
|
||||
background-color: $eve-security-color-m-08;
|
||||
fill: $eve-security-color-m-08;
|
||||
background-color: var(--eve-security-color-m-08);
|
||||
fill: var(--eve-security-color-m-08);
|
||||
}
|
||||
|
||||
.eve-security-background-m-09 {
|
||||
background-color: $eve-security-color-m-09;
|
||||
fill: $eve-security-color-m-09;
|
||||
background-color: var(--eve-security-color-m-09);
|
||||
fill: var(--eve-security-color-m-09);
|
||||
}
|
||||
|
||||
.eve-security-background-m-10 {
|
||||
background-color: $eve-security-color-m-10;
|
||||
fill: $eve-security-color-m-10;
|
||||
background-color: var(--eve-security-color-m-10);
|
||||
fill: var(--eve-security-color-m-10);
|
||||
}
|
||||
|
||||
/* WH Type color classes */
|
||||
.eve-wh-type-color-high {
|
||||
color: $eve-wh-type-color-high;
|
||||
fill: $eve-wh-type-color-high;
|
||||
color: var(--eve-wh-type-color-high) !important;
|
||||
fill: var(--eve-wh-type-color-high);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-low {
|
||||
color: $eve-wh-type-color-low;
|
||||
fill: $eve-wh-type-color-low;
|
||||
color: var(--eve-wh-type-color-low) !important;
|
||||
fill: var(--eve-wh-type-color-low);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-null {
|
||||
color: $eve-wh-type-color-null;
|
||||
fill: $eve-wh-type-color-null;
|
||||
color: var(--eve-wh-type-color-null) !important;
|
||||
fill: var(--eve-wh-type-color-null);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c1 {
|
||||
color: $eve-wh-type-color-c1 !important;
|
||||
fill: $eve-wh-type-color-c1;
|
||||
color: var(--eve-wh-type-color-c1) !important;
|
||||
fill: var(--eve-wh-type-color-c1);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c2 {
|
||||
color: $eve-wh-type-color-c2 !important;
|
||||
fill: $eve-wh-type-color-c2;
|
||||
color: var(--eve-wh-type-color-c2) !important;
|
||||
fill: var(--eve-wh-type-color-c2);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c3 {
|
||||
color: $eve-wh-type-color-c3 !important;
|
||||
fill: $eve-wh-type-color-c3;
|
||||
color: var(--eve-wh-type-color-c3) !important;
|
||||
fill: var(--eve-wh-type-color-c3);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c4 {
|
||||
color: $eve-wh-type-color-c4 !important;
|
||||
fill: $eve-wh-type-color-c4;
|
||||
color: var(--eve-wh-type-color-c4) !important;
|
||||
fill: var(--eve-wh-type-color-c4);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c5 {
|
||||
color: $eve-wh-type-color-c5 !important;
|
||||
fill: $eve-wh-type-color-c5;
|
||||
color: var(--eve-wh-type-color-c5) !important;
|
||||
fill: var(--eve-wh-type-color-c5);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c6 {
|
||||
color: $eve-wh-type-color-c6 !important;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6) !important;
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.eve-wh-type-color-c13 {
|
||||
color: $eve-wh-type-color-c13 !important;
|
||||
fill: $eve-wh-type-color-c13;
|
||||
color: var(--eve-wh-type-color-c13) !important;
|
||||
fill: var(--eve-wh-type-color-c13);
|
||||
}
|
||||
|
||||
.eve-wh-type-color-drifter {
|
||||
color: $eve-wh-type-color-drifter !important;
|
||||
fill: $eve-wh-type-color-drifter;
|
||||
color: var(--eve-wh-type-color-drifter) !important;
|
||||
fill: var(--eve-wh-type-color-drifter);
|
||||
}
|
||||
|
||||
.eve-wh-type-color-thera {
|
||||
color: $eve-wh-type-color-thera !important;
|
||||
fill: $eve-wh-type-color-thera;
|
||||
color: var(--eve-wh-type-color-thera) !important;
|
||||
fill: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
/* WH Type backgrounds */
|
||||
.eve-wh-type-background-high {
|
||||
background-color: $eve-wh-type-color-high;
|
||||
background-color: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-low {
|
||||
background-color: $eve-wh-type-color-low;
|
||||
background-color: var(--eve-wh-type-color-low);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-null {
|
||||
background-color: $eve-wh-type-color-null;
|
||||
background-color: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c1 {
|
||||
background-color: $eve-wh-type-color-c1;
|
||||
background-color: var(--eve-wh-type-color-c1);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c2 {
|
||||
background-color: $eve-wh-type-color-c2;
|
||||
background-color: var(--eve-wh-type-color-c2);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c3 {
|
||||
background-color: $eve-wh-type-color-c3;
|
||||
background-color: var(--eve-wh-type-color-c3);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c4 {
|
||||
background-color: $eve-wh-type-color-c4;
|
||||
background-color: var(--eve-wh-type-color-c4);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c5 {
|
||||
background-color: $eve-wh-type-color-c5;
|
||||
background-color: var(--eve-wh-type-color-c5);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c6 {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-c13 {
|
||||
background-color: $eve-wh-type-color-c13;
|
||||
background-color: var(--eve-wh-type-color-c13);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-drifter {
|
||||
background-color: $eve-wh-type-color-drifter;
|
||||
background-color: var(--eve-wh-type-color-drifter);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-thera {
|
||||
background-color: $eve-wh-type-color-thera;
|
||||
background-color: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-wh-type-background-zarzakh {
|
||||
background-color: $eve-wh-type-color-zarzakh;
|
||||
background-color: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* Kind color classes */
|
||||
.eve-kind-color-high {
|
||||
color: $eve-wh-type-color-high;
|
||||
fill: $eve-wh-type-color-high;
|
||||
color: var(--eve-wh-type-color-high);
|
||||
fill: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-kind-color-low {
|
||||
color: $eve-wh-type-color-low;
|
||||
fill: $eve-wh-type-color-low;
|
||||
color: var(--eve-wh-type-color-low);
|
||||
fill: var(--eve-wh-type-color-low);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.eve-kind-color-null {
|
||||
color: $eve-wh-type-color-null;
|
||||
fill: $eve-wh-type-color-null;
|
||||
color: var(--eve-wh-type-color-null);
|
||||
fill: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-kind-color-wh {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-thera {
|
||||
color: $eve-wh-type-color-thera;
|
||||
fill: $eve-wh-type-color-thera;
|
||||
color: var(--eve-wh-type-color-thera);
|
||||
fill: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-kind-color-abyss {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-penalty {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-pochven {
|
||||
color: $eve-wh-type-color-c6;
|
||||
fill: $eve-wh-type-color-c6;
|
||||
color: var(--eve-wh-type-color-c6);
|
||||
fill: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-color-zarzakh {
|
||||
color: $eve-wh-type-color-zarzakh;
|
||||
fill: $eve-wh-type-color-zarzakh;
|
||||
color: var(--eve-wh-type-color-zarzakh);
|
||||
fill: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* Kind backgrounds */
|
||||
.eve-kind-background-high {
|
||||
background-color: $eve-wh-type-color-high;
|
||||
background-color: var(--eve-wh-type-color-high);
|
||||
}
|
||||
|
||||
.eve-kind-background-low {
|
||||
background-color: $eve-wh-type-color-low;
|
||||
background-color: var(--eve-wh-type-color-low);
|
||||
}
|
||||
|
||||
.eve-kind-background-null {
|
||||
background-color: $eve-wh-type-color-null;
|
||||
background-color: var(--eve-wh-type-color-null);
|
||||
}
|
||||
|
||||
.eve-kind-background-wh {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-thera {
|
||||
background-color: $eve-wh-type-color-thera;
|
||||
background-color: var(--eve-wh-type-color-thera);
|
||||
}
|
||||
|
||||
.eve-kind-background-abyss {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-penalty {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-pochven {
|
||||
background-color: $eve-wh-type-color-c6;
|
||||
background-color: var(--eve-wh-type-color-c6);
|
||||
}
|
||||
|
||||
.eve-kind-background-zarzakh {
|
||||
background-color: $eve-wh-type-color-zarzakh;
|
||||
background-color: var(--eve-wh-type-color-zarzakh);
|
||||
}
|
||||
|
||||
/* System status color classes */
|
||||
.eve-system-status-color-clear {
|
||||
color: $eve-solar-system-status-color-unknown;
|
||||
color: var(--eve-solar-system-status-color-unknown);
|
||||
}
|
||||
|
||||
.eve-system-status-color-home {
|
||||
color: $eve-solar-system-status-color-home;
|
||||
color: var(--eve-solar-system-status-color-home);
|
||||
}
|
||||
|
||||
.eve-system-status-color-friendly {
|
||||
color: $eve-solar-system-status-color-friendly;
|
||||
color: var(--eve-solar-system-status-color-friendly);
|
||||
}
|
||||
|
||||
.eve-system-status-color-lookingFor {
|
||||
color: $eve-solar-system-status-color-lookingFor;
|
||||
color: var(--eve-solar-system-status-color-lookingFor);
|
||||
}
|
||||
|
||||
.eve-system-status-color-warning {
|
||||
color: $eve-solar-system-status-color-warning;
|
||||
color: var(--eve-solar-system-status-color-warning);
|
||||
}
|
||||
|
||||
.eve-system-status-color-target {
|
||||
color: $eve-solar-system-status-color-target;
|
||||
color: var(--eve-solar-system-status-color-target);
|
||||
}
|
||||
|
||||
.eve-system-status-color-dangerous {
|
||||
color: $eve-solar-system-status-color-dangerous;
|
||||
color: var(--eve-solar-system-status-color-dangerous);
|
||||
}
|
||||
|
||||
/* Shapes */
|
||||
.wd-route-system-shape-triangle {
|
||||
clip-path: polygon(50% 0, 0 100%, 100% 100%);
|
||||
}
|
||||
|
||||
.wd-route-system-shape-circle {
|
||||
border-radius: 40%;
|
||||
}
|
||||
|
||||
/* Some additional background classes */
|
||||
.wd-marker-bookmark-color-shattered {
|
||||
background-color: #833ca4;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
|
||||
.wd-marker-bookmark-color-custom {
|
||||
background-color: #282828;
|
||||
border: 1px solid #4c4c4c;
|
||||
|
||||
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
2
assets/js/hooks/Mapper/components/map/styles/index.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import './neon-theme.scss';
|
||||
@import './pathfinder-theme.scss';
|
||||
@@ -1,74 +1,85 @@
|
||||
$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;
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
|
||||
&__node {
|
||||
//cursor: auto;
|
||||
}
|
||||
.neon-theme {
|
||||
--pastel-blue: #5a7d9a;
|
||||
--pastel-pink: #d291bc;
|
||||
--pastel-green: #88b04b;
|
||||
--pastel-yellow: #ffdd59;
|
||||
--dark-bg: #2d2d2d;
|
||||
--text-color: #ffffff;
|
||||
--tooltip-bg: #202020;
|
||||
|
||||
&__pane {
|
||||
cursor: auto;
|
||||
}
|
||||
//&__edge {
|
||||
// stroke: $pastel-pink;
|
||||
// stroke-width: 2px;
|
||||
//
|
||||
// &.selected {
|
||||
// stroke: $pastel-yellow;
|
||||
// }
|
||||
//}
|
||||
--rf-bg-variant: "dots"; /* or lines, if you prefer */
|
||||
--rf-bg-gap: 16; /* some default gap */
|
||||
|
||||
&__handle {
|
||||
//background-color: $pastel-green;
|
||||
//box-shadow: 0 0 5px rgba($pastel-green, 0.5);
|
||||
}
|
||||
.react-flow {
|
||||
color: var(--text-color);
|
||||
|
||||
&__minimap {
|
||||
background-color: rgba(66, 66, 66, 1);
|
||||
opacity: 0.7;
|
||||
//backdrop-filter: blur(5px);
|
||||
border: 1px solid #2f2f2f;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
&__node {
|
||||
/* cursor: auto; */
|
||||
}
|
||||
|
||||
&__minimap-mask {
|
||||
fill: rgba(28, 28, 28, 0.75);
|
||||
}
|
||||
&__pane {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
&__controls {
|
||||
filter: brightness(1.5);
|
||||
}
|
||||
/*
|
||||
&__edge {
|
||||
stroke: var(--pastel-pink);
|
||||
stroke-width: 2px;
|
||||
|
||||
&__minimap-node {
|
||||
fill: #ffb03a;
|
||||
}
|
||||
}
|
||||
&.selected {
|
||||
stroke: var(--pastel-yellow);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
.context-menu-active {
|
||||
background-color: rgba(131, 131, 131, 0.33);
|
||||
}
|
||||
&__handle {
|
||||
/* background-color: var(--pastel-green);
|
||||
box-shadow: 0 0 5px rgba(88, 176, 75, 0.5); // or define an RGB var
|
||||
*/
|
||||
}
|
||||
|
||||
.p-dialog {
|
||||
.p-dialog-header {
|
||||
height: 40px;
|
||||
padding: 1rem;
|
||||
padding-right: 10px !important;
|
||||
}
|
||||
&__minimap {
|
||||
background-color: rgba(66, 66, 66, 1);
|
||||
opacity: 0.7;
|
||||
border: 1px solid #2f2f2f;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.p-dialog-title {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
&__minimap-mask {
|
||||
fill: rgba(28, 28, 28, 0.75);
|
||||
}
|
||||
|
||||
.p-dialog-header-icons {
|
||||
align-self: initial !important;
|
||||
}
|
||||
}
|
||||
&__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,197 @@
|
||||
|
||||
@import './eve-common-variables';
|
||||
@import './eve-common';
|
||||
|
||||
.pathfinder-theme {
|
||||
|
||||
--rf-bg-variant: "lines";
|
||||
--rf-bg-gap: 32;
|
||||
--rf-bg-color: #353535;
|
||||
--rf-soft-bg-color: #282829;
|
||||
|
||||
--pf-text-color: #adadad;
|
||||
--pf-dark-bg: #2d2d2d;
|
||||
--pf-tooltip-bg: #202020;
|
||||
--rf-bg-variant: "lines";
|
||||
--rf-bg-gap: 32;
|
||||
--rf-bg-size: 1;
|
||||
--rf-bg-color: #313131;
|
||||
--eve-effect-pulsar: #428bca;
|
||||
--eve-effect-magnetar: #e06fdf;
|
||||
--eve-effect-wolfRayet: #e28a0d;
|
||||
--eve-effect-blackHole: #000000;
|
||||
--eve-effect-cataclysmicVariable: #ffffbb;
|
||||
--eve-effect-redGiant: #d9534f;
|
||||
|
||||
--eve-wh-type-color-high: #5cb85c;
|
||||
--eve-wh-type-color-low: #e28a0d;
|
||||
--eve-wh-type-color-null: #d9534f;
|
||||
--eve-wh-type-color-c1: #428bca;
|
||||
--eve-wh-type-color-c2: #428bca;
|
||||
--eve-wh-type-color-c3: #e28a0d;
|
||||
--eve-wh-type-color-c4: #e28a0d;
|
||||
--eve-wh-type-color-c5: #d9534f;
|
||||
--eve-wh-type-color-c6: #d9534f;
|
||||
--eve-wh-type-color-c13: #7986cb;
|
||||
--eve-wh-type-color-drifter: #44aa82;
|
||||
|
||||
.MapRoot {
|
||||
background-color: var(--rf-bg-color);
|
||||
|
||||
&.isSoftBackground {
|
||||
background-color: var(--rf-soft-bg-color);
|
||||
}
|
||||
&.soft-bg {
|
||||
--rf-bg-color: var(--rf-soft-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.RootCustomNode {
|
||||
background-color: #313335 !important; /* or var(--pf-dark-bg) */
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: var(--pf-text-color);
|
||||
|
||||
}
|
||||
|
||||
.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 1px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
}
|
||||
|
||||
.classSystemName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
color: var(--pf-text-color);
|
||||
mix-blend-mode: screen;
|
||||
text-shadow: rgba(0, 0, 0, 0.4) 1px 1px;
|
||||
}
|
||||
|
||||
.textEllipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.fontSans {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.BottomRow {
|
||||
.localCounter {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
||||
& > i {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
& > span {
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
font-weight: 700;
|
||||
mix-blend-mode: screen;
|
||||
color: #5cb85c; /* or var(--eve-wh-type-color-high) */
|
||||
}
|
||||
}
|
||||
|
||||
.customName {
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
color: var(--pf-text-color);
|
||||
mix-blend-mode: screen;
|
||||
text-shadow: rgba(0, 0, 0, 0.4) 1px 1px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.tagTitle {
|
||||
font-size: 9px;
|
||||
font-family: inherit !important;
|
||||
font-weight: bold;
|
||||
color: #fbbf24 !important;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
}
|
||||
|
||||
.Handlers {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Handle {
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
border: 1px solid var(--pastel-blue, #5a7d9a);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
&.selected {
|
||||
border-color: var(--pastel-pink, #d291bc);
|
||||
}
|
||||
|
||||
&.HandleTop {
|
||||
top: -2px;
|
||||
}
|
||||
&.HandleRight {
|
||||
right: -2px;
|
||||
}
|
||||
&.HandleBottom {
|
||||
bottom: -2px;
|
||||
}
|
||||
&.HandleLeft {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
&.Tick {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
|
||||
&.HandleTop {
|
||||
top: -3px;
|
||||
}
|
||||
&.HandleRight {
|
||||
right: -3px;
|
||||
}
|
||||
&.HandleBottom {
|
||||
bottom: -3px;
|
||||
}
|
||||
&.HandleLeft {
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow {
|
||||
color: var(--pf-text-color);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.SearchItem {
|
||||
& > * {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.SearchItemEffect {
|
||||
font-weight: initial !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
import { AutoComplete } from 'primereact/autocomplete';
|
||||
import { OutCommand, SearchSystemItem } from '@/hooks/Mapper/types';
|
||||
import { SystemViewStandalone, WHClassView, WHEffectView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import classes from './AddSystemDialog.module.scss';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts';
|
||||
import { sortWHClasses } from '@/hooks/Mapper/helpers';
|
||||
|
||||
export type SearchOnSubmitCallback = (item: SearchSystemItem) => void;
|
||||
|
||||
interface AddSystemDialogProps {
|
||||
title?: string;
|
||||
visible: boolean;
|
||||
setVisible: (visible: boolean) => void;
|
||||
onSubmit?: SearchOnSubmitCallback;
|
||||
excludedSystems?: number[];
|
||||
}
|
||||
|
||||
export const AddSystemDialog = ({
|
||||
title = 'Add system',
|
||||
visible,
|
||||
setVisible,
|
||||
onSubmit,
|
||||
excludedSystems = [],
|
||||
}: AddSystemDialogProps) => {
|
||||
const {
|
||||
outCommand,
|
||||
data: { wormholesData },
|
||||
} = useMapRootState();
|
||||
|
||||
const inputRef = useRef<any>();
|
||||
const onShow = useCallback(() => {
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const [filteredItems, setFilteredItems] = useState<SearchSystemItem[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<SearchSystemItem[] | null>(null);
|
||||
|
||||
const searchItems = useCallback(
|
||||
async (event: { query: string }) => {
|
||||
if (event.query.length < 2) {
|
||||
setFilteredItems([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const query = event.query;
|
||||
|
||||
if (query.length === 0) {
|
||||
setFilteredItems([]);
|
||||
} else {
|
||||
try {
|
||||
const result = await outCommand({
|
||||
type: OutCommand.searchSystems,
|
||||
data: {
|
||||
text: query,
|
||||
},
|
||||
});
|
||||
|
||||
let prepared = (result.systems as SearchSystemItem[]).sort((a, b) => {
|
||||
const amatch = a.label.indexOf(query);
|
||||
const bmatch = b.label.indexOf(query);
|
||||
return amatch - bmatch;
|
||||
});
|
||||
|
||||
if (excludedSystems) {
|
||||
prepared = prepared.filter(x => !excludedSystems.includes(x.system_static_info.solar_system_id));
|
||||
}
|
||||
|
||||
setFilteredItems(prepared);
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
setFilteredItems([]);
|
||||
}
|
||||
}
|
||||
},
|
||||
[excludedSystems, outCommand],
|
||||
);
|
||||
|
||||
const ref = useRef({ onSubmit, selectedItem });
|
||||
ref.current = { onSubmit, selectedItem };
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const { onSubmit, selectedItem } = ref.current;
|
||||
setFilteredItems([]);
|
||||
setSelectedItem([]);
|
||||
|
||||
if (!selectedItem) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmit?.(selectedItem[0]);
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header={title}
|
||||
visible={visible}
|
||||
draggable={false}
|
||||
style={{ width: '520px' }}
|
||||
onShow={onShow}
|
||||
onHide={() => {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-3 px-1.5">
|
||||
<div className="flex flex-col gap-2 py-3.5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<IconField>
|
||||
<AutoComplete
|
||||
ref={inputRef}
|
||||
multiple
|
||||
showEmptyMessage
|
||||
scrollHeight="300px"
|
||||
value={selectedItem}
|
||||
suggestions={filteredItems}
|
||||
completeMethod={searchItems}
|
||||
onChange={e => {
|
||||
setSelectedItem(e.value.length < 2 ? e.value : [e.value[e.value.length - 1]]);
|
||||
}}
|
||||
emptyMessage="Not found any system..."
|
||||
placeholder="Type here..."
|
||||
field="label"
|
||||
id="value"
|
||||
className="w-full"
|
||||
itemTemplate={(item: SearchSystemItem) => {
|
||||
const { security, system_class, effect_power, effect_name, statics } = item.system_static_info;
|
||||
const sortedStatics = sortWHClasses(wormholesData, statics);
|
||||
const isWH = isWormholeSpace(system_class);
|
||||
|
||||
return (
|
||||
<div className={clsx('flex gap-1.5', classes.SearchItem)}>
|
||||
<SystemViewStandalone
|
||||
security={security}
|
||||
system_class={system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
|
||||
{effect_name && isWH && (
|
||||
<WHEffectView
|
||||
effectName={effect_name}
|
||||
effectPower={effect_power}
|
||||
className={classes.SearchItemEffect}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isWH && (
|
||||
<div className="flex gap-1 grow justify-between">
|
||||
<div></div>
|
||||
<div className="flex gap-1">
|
||||
{sortedStatics.map(x => (
|
||||
<WHClassView key={x} whClassName={x} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
selectedItemTemplate={(item: SearchSystemItem) => (
|
||||
<SystemViewStandalone
|
||||
security={item.system_static_info.security}
|
||||
system_class={item.system_static_info.system_class}
|
||||
solar_system_id={item.value}
|
||||
class_title={item.class_title}
|
||||
solar_system_name={item.label}
|
||||
region_name={item.region_name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</IconField>
|
||||
|
||||
<span className="text-[12px] text-stone-400 ml-1">*to search type at least 2 symbols.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
outlined
|
||||
disabled={!selectedItem || selectedItem.length !== 1}
|
||||
size="small"
|
||||
label="Submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './AddSystemDialog';
|
||||
@@ -3,6 +3,7 @@ import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
@@ -22,30 +23,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
|
||||
|
||||
const system = getSystemById(systems, systemId);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [label, setLabel] = useState('');
|
||||
const [temporaryName, setTemporaryName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = new LabelsManager(system.labels || '');
|
||||
|
||||
setName(system.name || '');
|
||||
setLabel(labels.customLabel);
|
||||
setDescription(system.description || '');
|
||||
}, [system]);
|
||||
|
||||
const ref = useRef({ name, description, label, outCommand, systemId, system });
|
||||
ref.current = { name, description, label, outCommand, systemId, system };
|
||||
const ref = useRef({ name, description, temporaryName, label, outCommand, systemId, system });
|
||||
ref.current = { name, description, label, temporaryName, outCommand, systemId, system };
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
const { name, description, label, outCommand, systemId, system } = ref.current;
|
||||
const { name, description, label, temporaryName, outCommand, systemId, system } = ref.current;
|
||||
|
||||
const outLabel = new LabelsManager(system?.labels ?? '');
|
||||
outLabel.updateCustomLabel(label);
|
||||
@@ -58,6 +50,14 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemTemporaryName,
|
||||
data: {
|
||||
system_id: systemId,
|
||||
value: temporaryName,
|
||||
},
|
||||
});
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.updateSystemName,
|
||||
data: {
|
||||
@@ -93,6 +93,21 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
|
||||
}, []);
|
||||
|
||||
// Attention: this effect should be call only on mount.
|
||||
useEffect(() => {
|
||||
const { system } = ref.current;
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = new LabelsManager(system.labels || '');
|
||||
|
||||
setName(system.name || '');
|
||||
setLabel(labels.customLabel);
|
||||
setTemporaryName(system.temporary_name || '');
|
||||
setDescription(system.description || '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="System settings"
|
||||
@@ -167,6 +182,35 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
|
||||
</IconField>
|
||||
</div>
|
||||
|
||||
{isTempSystemNameEnabled && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor="username">Temporary Name</label>
|
||||
|
||||
<IconField>
|
||||
{temporaryName !== '' && (
|
||||
<WdImgButton
|
||||
className="pi pi-trash text-red-400"
|
||||
textSize={WdImageSize.large}
|
||||
tooltip={{
|
||||
content: 'Remove temporary name',
|
||||
className: 'pi p-input-icon',
|
||||
position: TooltipPosition.top,
|
||||
}}
|
||||
onClick={() => setTemporaryName('')}
|
||||
/>
|
||||
)}
|
||||
<InputText
|
||||
id="temporaryName"
|
||||
aria-describedby="temporaryName"
|
||||
autoComplete="off"
|
||||
value={temporaryName}
|
||||
maxLength={10}
|
||||
onChange={e => setTemporaryName(e.target.value)}
|
||||
/>
|
||||
</IconField>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label htmlFor="username">Description</label>
|
||||
<InputTextarea
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virtualscroller';
|
||||
@@ -8,6 +8,10 @@ import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
||||
import { CharacterCard, LayoutEventBlocker, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { sortCharacters } from '@/hooks/Mapper/components/mapInterface/helpers/sortCharacters.ts';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
type CharItemProps = {
|
||||
compact: boolean;
|
||||
@@ -62,6 +66,14 @@ export const LocalCharacters = () => {
|
||||
|
||||
const [systemId] = selectedSystems;
|
||||
|
||||
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
|
||||
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
|
||||
|
||||
const showOffline = useMemo(
|
||||
() => !restrictOfflineShowing || isAdminOrManager,
|
||||
[isAdminOrManager, restrictOfflineShowing],
|
||||
);
|
||||
|
||||
const itemTemplate = useItemTemplate();
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
@@ -70,32 +82,39 @@ export const LocalCharacters = () => {
|
||||
.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id), compact: settings.compact }))
|
||||
.sort(sortCharacters);
|
||||
|
||||
if (!settings.showOffline) {
|
||||
if (!showOffline || !settings.showOffline) {
|
||||
return sorted.filter(c => c.online);
|
||||
}
|
||||
|
||||
return sorted;
|
||||
// eslint-disable-next-line
|
||||
}, [characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
|
||||
}, [showOffline, characters, settings.showOffline, settings.compact, systemId, userCharacters, presentCharacters]);
|
||||
|
||||
const isNobodyHere = sorted.length === 0;
|
||||
const isNotSelectedSystem = selectedSystems.length !== 1;
|
||||
const showList = sorted.length > 0 && selectedSystems.length === 1;
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 145);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full">
|
||||
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
|
||||
<span className="select-none">Local{showList ? ` [${sorted.length}]` : ''}</span>
|
||||
<LayoutEventBlocker className="flex items-center gap-2">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={'Show offline'}
|
||||
value={settings.showOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
|
||||
/>
|
||||
{showOffline && (
|
||||
<WdTooltipWrapper content="Show offline characters in system">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={compact ? '' : 'Show offline'}
|
||||
value={settings.showOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, showOffline: !settings.showOffline }))}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
<span
|
||||
className={clsx('w-4 h-4 cursor-pointer', {
|
||||
@@ -115,7 +134,9 @@ export const LocalCharacters = () => {
|
||||
)}
|
||||
|
||||
{isNobodyHere && !isNotSelectedSystem && (
|
||||
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">Nobody here</div>
|
||||
<div className="w-full h-full flex justify-center items-center select-none text-stone-400/80 text-sm">
|
||||
Nobody here
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showList && (
|
||||
|
||||
@@ -19,6 +19,13 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { RoutesSettingsDialog } from './RoutesSettingsDialog';
|
||||
import { RoutesProvider, useRouteProvider } from './RoutesProvider.tsx';
|
||||
import { ContextMenuSystemInfo, useContextMenuSystemInfoHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
|
||||
const sortByDist = (a: Route, b: Route) => {
|
||||
const distA = a.has_connection ? a.systems?.length || 0 : Infinity;
|
||||
@@ -161,6 +168,12 @@ export const RoutesWidgetContent = () => {
|
||||
export const RoutesWidgetComp = () => {
|
||||
const [routeSettingsVisible, setRouteSettingsVisible] = useState(false);
|
||||
const { data, update } = useRouteProvider();
|
||||
const {
|
||||
data: { hubs = [] },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const preparedHubs = useMemo(() => hubs.map(x => parseInt(x)), [hubs]);
|
||||
|
||||
const isSecure = data.path_type === 'secure';
|
||||
const handleSecureChange = useCallback(() => {
|
||||
@@ -170,27 +183,70 @@ export const RoutesWidgetComp = () => {
|
||||
});
|
||||
}, [data, update]);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 155);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<boolean>(false);
|
||||
|
||||
const onAddSystem = useCallback(() => setOpenAddSystem(true), []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (preparedHubs.includes(item.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.addHub,
|
||||
data: { system_id: item.value },
|
||||
});
|
||||
},
|
||||
[hubs, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full">
|
||||
<div className="flex justify-between items-center text-xs w-full" ref={ref}>
|
||||
<span className="select-none">Routes</span>
|
||||
<LayoutEventBlocker className="flex items-center gap-2">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={'Show shortest'}
|
||||
value={!isSecure}
|
||||
onChange={handleSecureChange}
|
||||
classNameLabel={clsx('text-red-400')}
|
||||
<WdImgButton
|
||||
className={PrimeIcons.PLUS_CIRCLE}
|
||||
onClick={onAddSystem}
|
||||
tooltip={{
|
||||
content: 'Click here to add new system to routes',
|
||||
}}
|
||||
/>
|
||||
|
||||
<WdTooltipWrapper content="Show shortest route">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={compact ? '' : 'Show shortest'}
|
||||
value={!isSecure}
|
||||
onChange={handleSecureChange}
|
||||
classNameLabel={clsx('text-red-400')}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
<WdImgButton
|
||||
className={PrimeIcons.SLIDERS_H}
|
||||
onClick={() => setRouteSettingsVisible(true)}
|
||||
tooltip={{
|
||||
content: 'Click here to open Routes settings',
|
||||
}}
|
||||
/>
|
||||
<WdImgButton className={PrimeIcons.SLIDERS_H} onClick={() => setRouteSettingsVisible(true)} />
|
||||
</LayoutEventBlocker>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<RoutesWidgetContent />
|
||||
<RoutesSettingsDialog visible={routeSettingsVisible} setVisible={setRouteSettingsVisible} />
|
||||
|
||||
<AddSystemDialog
|
||||
title="Add system to routes"
|
||||
visible={openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(false)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
</Widget>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,40 +2,45 @@ import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
|
||||
import {
|
||||
InfoDrawer,
|
||||
LayoutEventBlocker,
|
||||
SystemView,
|
||||
TooltipPosition,
|
||||
WdImgButton,
|
||||
WdCheckbox,
|
||||
WdImgButton,
|
||||
} from '@/hooks/Mapper/components/ui-kit';
|
||||
import { SystemSignaturesContent } from './SystemSignaturesContent';
|
||||
import {
|
||||
Setting,
|
||||
SystemSignatureSettingsDialog,
|
||||
COSMIC_SIGNATURE,
|
||||
COSMIC_ANOMALY,
|
||||
COSMIC_SIGNATURE,
|
||||
DEPLOYABLE,
|
||||
STRUCTURE,
|
||||
STARBASE,
|
||||
SHIP,
|
||||
DRONE,
|
||||
Setting,
|
||||
SHIP,
|
||||
STARBASE,
|
||||
STRUCTURE,
|
||||
SystemSignatureSettingsDialog,
|
||||
} from './SystemSignatureSettingsDialog';
|
||||
import { SignatureGroup } from '@/hooks/Mapper/types';
|
||||
|
||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { PrimeIcons } from 'primereact/api';
|
||||
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { CheckboxChangeEvent } from 'primereact/checkbox';
|
||||
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
|
||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v5';
|
||||
const SIGNATURE_SETTINGS_KEY = 'wanderer_system_signature_settings_v5_2';
|
||||
export const SHOW_DESCRIPTION_COLUMN_SETTING = 'show_description_column_setting';
|
||||
export const SHOW_UPDATED_COLUMN_SETTING = 'SHOW_UPDATED_COLUMN_SETTING';
|
||||
export const LAZY_DELETE_SIGNATURES_SETTING = 'LAZY_DELETE_SIGNATURES_SETTING';
|
||||
export const KEEP_LAZY_DELETE_SETTING = 'KEEP_LAZY_DELETE_ENABLED_SETTING';
|
||||
|
||||
const settings: Setting[] = [
|
||||
{ key: SHOW_UPDATED_COLUMN_SETTING, name: 'Show Updated Column', value: false, isFilter: false },
|
||||
{ key: SHOW_DESCRIPTION_COLUMN_SETTING, name: 'Show Description Column', value: false, isFilter: false },
|
||||
{ key: LAZY_DELETE_SIGNATURES_SETTING, name: 'Lazy Delete Signatures', value: false, isFilter: false },
|
||||
{ key: KEEP_LAZY_DELETE_SETTING, name: 'Keep "Lazy Delete" Enabled', value: false, isFilter: false },
|
||||
{ key: COSMIC_ANOMALY, name: 'Show Anomalies', value: true, isFilter: true },
|
||||
{ key: COSMIC_SIGNATURE, name: 'Show Cosmic Signatures', value: true, isFilter: true },
|
||||
{ key: DEPLOYABLE, name: 'Show Deployables', value: true, isFilter: true },
|
||||
@@ -77,10 +82,10 @@ export const SystemSignatures = () => {
|
||||
setVisible(false);
|
||||
}, []);
|
||||
|
||||
const handleLazyDeleteChange = useCallback((event: CheckboxChangeEvent) => {
|
||||
const handleLazyDeleteChange = useCallback((value: boolean) => {
|
||||
setSettings(settings => {
|
||||
const lazyDelete = settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)!;
|
||||
lazyDelete.value = !!event.checked;
|
||||
lazyDelete.value = value;
|
||||
localStorage.setItem(SIGNATURE_SETTINGS_KEY, JSON.stringify(settings));
|
||||
return [...settings];
|
||||
});
|
||||
@@ -94,21 +99,33 @@ export const SystemSignatures = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const compact = useMaxWidth(ref, 260);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
label={
|
||||
<div className="flex justify-between items-center text-xs w-full h-full">
|
||||
<div className="flex gap-1">System Signatures</div>
|
||||
<div className="flex justify-between items-center text-xs w-full h-full" ref={ref}>
|
||||
<div className="flex justify-between items-center gap-1">
|
||||
{!compact && (
|
||||
<div className="flex whitespace-nowrap text-ellipsis overflow-hidden text-stone-400">
|
||||
Signatures {isNotSelectedSystem ? '' : 'in'}
|
||||
</div>
|
||||
)}
|
||||
{!isNotSelectedSystem && <SystemView systemId={systemId} className="select-none text-center" hideRegion />}
|
||||
</div>
|
||||
|
||||
<LayoutEventBlocker className="flex gap-2.5">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={'Lazy delete'}
|
||||
value={lazyDeleteValue}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={handleLazyDeleteChange}
|
||||
/>
|
||||
<WdTooltipWrapper content="Enable Lazy delete">
|
||||
<WdCheckbox
|
||||
size="xs"
|
||||
labelSide="left"
|
||||
label={compact ? '' : 'Lazy delete'}
|
||||
value={lazyDeleteValue}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300 whitespace-nowrap text-ellipsis overflow-hidden"
|
||||
onChange={(event: CheckboxChangeEvent) => handleLazyDeleteChange(!!event.checked)}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
<WdImgButton
|
||||
className={PrimeIcons.QUESTION_CIRCLE}
|
||||
@@ -149,7 +166,7 @@ export const SystemSignatures = () => {
|
||||
System is not selected
|
||||
</div>
|
||||
) : (
|
||||
<SystemSignaturesContent systemId={systemId} settings={settings} />
|
||||
<SystemSignaturesContent systemId={systemId} settings={settings} onLazyDeleteChange={handleLazyDeleteChange} />
|
||||
)}
|
||||
{visible && (
|
||||
<SystemSignatureSettingsDialog
|
||||
|
||||
@@ -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';
|
||||
@@ -38,6 +41,7 @@ import {
|
||||
SHOW_DESCRIPTION_COLUMN_SETTING,
|
||||
SHOW_UPDATED_COLUMN_SETTING,
|
||||
LAZY_DELETE_SIGNATURES_SETTING,
|
||||
KEEP_LAZY_DELETE_SETTING,
|
||||
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures';
|
||||
type SystemSignaturesSortSettings = {
|
||||
sortField: string;
|
||||
@@ -55,6 +59,7 @@ interface SystemSignaturesContentProps {
|
||||
hideLinkedSignatures?: boolean;
|
||||
selectable?: boolean;
|
||||
onSelect?: (signature: SystemSignature) => void;
|
||||
onLazyDeleteChange?: (value: boolean) => void;
|
||||
}
|
||||
export const SystemSignaturesContent = ({
|
||||
systemId,
|
||||
@@ -62,6 +67,7 @@ export const SystemSignaturesContent = ({
|
||||
hideLinkedSignatures,
|
||||
selectable,
|
||||
onSelect,
|
||||
onLazyDeleteChange,
|
||||
}: SystemSignaturesContentProps) => {
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
@@ -84,12 +90,16 @@ export const SystemSignaturesContent = ({
|
||||
|
||||
const tooltipRef = useRef<WdTooltipHandlers>(null);
|
||||
|
||||
const { clipboardContent } = useClipboard();
|
||||
const { clipboardContent, setClipboardContent } = useClipboard();
|
||||
|
||||
const lazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === LAZY_DELETE_SIGNATURES_SETTING)?.value ?? false;
|
||||
}, [settings]);
|
||||
|
||||
const keepLazyDeleteValue = useMemo(() => {
|
||||
return settings.find(setting => setting.key === KEEP_LAZY_DELETE_SETTING)?.value ?? false;
|
||||
}, [settings]);
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (tableRef.current) {
|
||||
const tableWidth = tableRef.current.offsetWidth;
|
||||
@@ -115,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +216,32 @@ export const SystemSignaturesContent = ({
|
||||
[onSelect, selectable],
|
||||
);
|
||||
|
||||
const handlePaste = async (clipboardContent: string) => {
|
||||
const newSignatures = parseSignatures(
|
||||
clipboardContent,
|
||||
settings.map(x => x.key),
|
||||
);
|
||||
|
||||
handleUpdateSignatures(newSignatures, !lazyDeleteValue);
|
||||
|
||||
if (lazyDeleteValue && !keepLazyDeleteValue) {
|
||||
onLazyDeleteChange?.(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEnterRow = useCallback(
|
||||
(e: DataTableRowMouseEvent) => {
|
||||
setHoveredSig(filteredSignatures[e.index]);
|
||||
tooltipRef.current?.show(e.originalEvent);
|
||||
},
|
||||
[filteredSignatures],
|
||||
);
|
||||
|
||||
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
|
||||
tooltipRef.current?.hide(e.originalEvent);
|
||||
setHoveredSig(null);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (refData.current.selectable) {
|
||||
return;
|
||||
@@ -215,19 +252,11 @@ export const SystemSignaturesContent = ({
|
||||
}
|
||||
|
||||
handlePaste(clipboardContent.text);
|
||||
}, [clipboardContent, selectable, lazyDeleteValue]);
|
||||
|
||||
const handlePaste = async (clipboardContent: string) => {
|
||||
const newSignatures = parseSignatures(
|
||||
clipboardContent,
|
||||
settings.map(x => x.key),
|
||||
);
|
||||
|
||||
handleUpdateSignatures(newSignatures, !lazyDeleteValue);
|
||||
};
|
||||
setClipboardContent(null);
|
||||
}, [clipboardContent, selectable, lazyDeleteValue, keepLazyDeleteValue]);
|
||||
|
||||
useHotkey(true, ['a'], handleSelectAll);
|
||||
useHotkey(false, ['Delete'], handleDeleteSelected);
|
||||
useHotkey(false, ['Backspace', 'Delete'], handleDeleteSelected);
|
||||
|
||||
useEffect(() => {
|
||||
if (!systemId) {
|
||||
@@ -265,19 +294,6 @@ export const SystemSignaturesContent = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleEnterRow = useCallback(
|
||||
(e: DataTableRowMouseEvent) => {
|
||||
setHoveredSig(filteredSignatures[e.index]);
|
||||
tooltipRef.current?.show(e.originalEvent);
|
||||
},
|
||||
[filteredSignatures],
|
||||
);
|
||||
|
||||
const handleLeaveRow = useCallback((e: DataTableRowMouseEvent) => {
|
||||
tooltipRef.current?.hide(e.originalEvent);
|
||||
setHoveredSig(null);
|
||||
}, []);
|
||||
|
||||
const renderToolbar = (/*row: SystemSignature*/) => {
|
||||
return (
|
||||
<div className="flex justify-end items-center gap-2 mr-[4px]">
|
||||
@@ -361,7 +377,6 @@ export const SystemSignaturesContent = ({
|
||||
></Column>
|
||||
<Column
|
||||
field="info"
|
||||
// header="Info"
|
||||
bodyClassName="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||
body={renderInfoColumn}
|
||||
style={{ maxWidth: nameColumnWidth }}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.whFontSize {
|
||||
font-size: 11px !important;
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { renderName } from './renderName.tsx';
|
||||
import classes from './renderInfoColumn.module.scss';
|
||||
|
||||
export const renderInfoColumn = (row: SystemSignature) => {
|
||||
if (!row.group || row.group === SignatureGroup.Wormhole) {
|
||||
@@ -23,12 +22,11 @@ export const renderInfoColumn = (row: SystemSignature) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-start items-center gap-[6px]">
|
||||
<div className="flex justify-start items-center gap-[4px]">
|
||||
{row.type && (
|
||||
<WHClassView
|
||||
className="text-[11px]"
|
||||
classNameWh={classes.whFontSize}
|
||||
highlightName
|
||||
classNameWh="!text-[11px] !font-bold"
|
||||
hideWhClass={!!row.linked_system}
|
||||
whClassName={row.type}
|
||||
noOffset
|
||||
@@ -36,11 +34,10 @@ export const renderInfoColumn = (row: SystemSignature) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{!row.linked_system && !!k162TypeOption && <>{renderK162Type(k162TypeOption, 'text-[11px]')}</>}
|
||||
{!row.linked_system && row.type === 'K162' && !!k162TypeOption && <>{renderK162Type(k162TypeOption)}</>}
|
||||
|
||||
{row.linked_system && (
|
||||
<>
|
||||
{/*<span className="w-4 h-4 hero-arrow-long-right"></span>*/}
|
||||
<span title={row.linked_system?.solar_system_name}>
|
||||
<SystemViewStandalone
|
||||
className={clsx('select-none text-center cursor-context-menu')}
|
||||
|
||||
@@ -16,6 +16,8 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
const { interfaceSettings } = useMapRootState();
|
||||
const { isShowMenu } = interfaceSettings;
|
||||
|
||||
const themeClass = `${interfaceSettings.theme ?? 'neon'}-theme`;
|
||||
|
||||
const [showOnTheMap, setShowOnTheMap] = useState(false);
|
||||
const [showMapSettings, setShowMapSettings] = useState(false);
|
||||
const mapInterface = <MapInterface />;
|
||||
@@ -26,27 +28,29 @@ export const MapRootContent = ({}: MapRootContentProps) => {
|
||||
useSkipContextMenu();
|
||||
|
||||
return (
|
||||
<Layout map={<MapWrapper />}>
|
||||
{!isShowMenu ? (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
||||
<Topbar />
|
||||
<div className={themeClass}>
|
||||
<Layout map={<MapWrapper />}>
|
||||
{!isShowMenu ? (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-[calc(100%-3.5rem)] h-full pointer-events-none">
|
||||
<Topbar />
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
<div className="absolute top-0 right-0 w-14 h-[calc(100%+3.5rem)] pointer-events-auto">
|
||||
<RightBar onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute top-0 left-14 w-[calc(100%-3.5rem)] h-[calc(100%-3.5rem)] pointer-events-none">
|
||||
<Topbar>
|
||||
<MapContextMenu onShowOnTheMap={handleShowOnTheMap} onShowMapSettings={handleShowMapSettings} />
|
||||
</Topbar>
|
||||
{mapInterface}
|
||||
</div>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
)}
|
||||
<OnTheMap show={showOnTheMap} onHide={() => setShowOnTheMap(false)} />
|
||||
<MapSettings show={showMapSettings} onHide={() => setShowMapSettings(false)} />
|
||||
</Layout>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@ import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrap
|
||||
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { MenuItem } from 'primereact/menuitem';
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
export interface MapContextMenuProps {
|
||||
onShowOnTheMap?: () => void;
|
||||
@@ -14,6 +16,8 @@ export interface MapContextMenuProps {
|
||||
export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContextMenuProps) => {
|
||||
const { outCommand, setInterfaceSettings } = useMapRootState();
|
||||
|
||||
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
|
||||
|
||||
const menuRight = useRef<Menu>(null);
|
||||
|
||||
const handleAddCharacter = useCallback(() => {
|
||||
@@ -24,34 +28,40 @@ export const MapContextMenu = ({ onShowOnTheMap, onShowMapSettings }: MapContext
|
||||
}, [outCommand]);
|
||||
|
||||
const items = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: 'Tracking',
|
||||
icon: 'pi pi-user-plus',
|
||||
command: handleAddCharacter,
|
||||
},
|
||||
{
|
||||
label: 'On the map',
|
||||
icon: 'pi pi-hashtag',
|
||||
command: onShowOnTheMap,
|
||||
},
|
||||
{ separator: true },
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: `pi pi-cog`,
|
||||
command: onShowMapSettings,
|
||||
},
|
||||
{
|
||||
label: 'Dock menu',
|
||||
icon: 'pi pi-window-maximize',
|
||||
command: () =>
|
||||
setInterfaceSettings(x => ({
|
||||
...x,
|
||||
isShowMenu: !x.isShowMenu,
|
||||
})),
|
||||
},
|
||||
] as MenuItem[];
|
||||
}, [handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
||||
return (
|
||||
[
|
||||
{
|
||||
label: 'Tracking',
|
||||
icon: 'pi pi-user-plus',
|
||||
command: handleAddCharacter,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
label: 'On the map',
|
||||
icon: 'pi pi-hashtag',
|
||||
command: onShowOnTheMap,
|
||||
visible: canTrackCharacters,
|
||||
},
|
||||
{ separator: true, visible: true },
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: `pi pi-cog`,
|
||||
command: onShowMapSettings,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
label: 'Dock menu',
|
||||
icon: 'pi pi-window-maximize',
|
||||
command: () =>
|
||||
setInterfaceSettings(x => ({
|
||||
...x,
|
||||
isShowMenu: !x.isShowMenu,
|
||||
})),
|
||||
visible: true,
|
||||
},
|
||||
] as MenuItem[]
|
||||
).filter(item => item.visible);
|
||||
}, [canTrackCharacters, handleAddCharacter, onShowMapSettings, onShowOnTheMap, setInterfaceSettings]);
|
||||
|
||||
return (
|
||||
<div className="ml-1">
|
||||
|
||||
@@ -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',
|
||||
@@ -53,35 +58,47 @@ const SYSTEMS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
|
||||
const SIGNATURES_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: UserSettingsRemoteProps.link_signature_on_splash, label: 'Link signature on splash' },
|
||||
{ prop: InterfaceStoredSettingsProps.isShowUnsplashedSignatures, label: 'Show unsplashed signatures' },
|
||||
];
|
||||
|
||||
const CONNECTIONS_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: UserSettingsRemoteProps.delete_connection_with_sigs, label: 'Delete connections to linked signatures' },
|
||||
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
|
||||
];
|
||||
|
||||
const UI_CHECKBOXES_PROPS: CheckboxesList = [
|
||||
{ prop: InterfaceStoredSettingsProps.isShowMenu, label: 'Enable compact map menu bar' },
|
||||
{ prop: InterfaceStoredSettingsProps.isThickConnections, label: 'Thicker connections' },
|
||||
{ prop: InterfaceStoredSettingsProps.isShowBackgroundPattern, label: 'Show background pattern' },
|
||||
{ prop: InterfaceStoredSettingsProps.isSoftBackground, label: 'Enable soft background' },
|
||||
];
|
||||
|
||||
const THEME_OPTIONS = [
|
||||
{ label: 'Default', value: 'neon' },
|
||||
{ label: 'Pathfinder', value: 'pathfinder' },
|
||||
];
|
||||
|
||||
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,
|
||||
});
|
||||
@@ -89,18 +106,15 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
|
||||
const handleChangeChecked = useCallback(
|
||||
(prop: keyof UserSettings) => async (checked: boolean) => {
|
||||
// @ts-ignore
|
||||
if (UserSettingsRemoteList.includes(prop)) {
|
||||
if (UserSettingsRemoteList.includes(prop as any)) {
|
||||
const newRemoteSettings = {
|
||||
...userRemoteSettings,
|
||||
[prop]: checked,
|
||||
};
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.updateUserSettings,
|
||||
data: newRemoteSettings,
|
||||
});
|
||||
|
||||
setUserRemoteSettings(newRemoteSettings);
|
||||
return;
|
||||
}
|
||||
@@ -110,34 +124,40 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
|
||||
[prop]: checked,
|
||||
});
|
||||
},
|
||||
[interfaceSettings, outCommand, setInterfaceSettings, userRemoteSettings],
|
||||
[userRemoteSettings, interfaceSettings, outCommand, setInterfaceSettings],
|
||||
);
|
||||
|
||||
const renderCheckboxesList = (list: CheckboxesList) => {
|
||||
return list.map(x => {
|
||||
return (
|
||||
<PrettySwitchbox
|
||||
key={x.prop}
|
||||
label={x.label}
|
||||
checked={mergedSettings[x.prop]}
|
||||
setChecked={handleChangeChecked(x.prop)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return list.map((x) => (
|
||||
<PrettySwitchbox
|
||||
key={x.prop}
|
||||
label={x.label}
|
||||
checked={mergedSettings[x.prop]}
|
||||
setChecked={handleChangeChecked(x.prop)}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
const handleChangeTheme = useCallback(
|
||||
(newThemeValue: string) => {
|
||||
setInterfaceSettings({
|
||||
...interfaceSettings,
|
||||
theme: newThemeValue,
|
||||
});
|
||||
},
|
||||
[interfaceSettings, setInterfaceSettings]
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header="Map settings"
|
||||
header="Map user settings"
|
||||
visible={show}
|
||||
draggable={false}
|
||||
style={{ width: '550px' }}
|
||||
onShow={handleShow}
|
||||
onHide={() => {
|
||||
if (!show) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!show) return;
|
||||
setActiveIndex(0);
|
||||
onHide();
|
||||
}}
|
||||
@@ -147,26 +167,45 @@ 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>
|
||||
<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)}
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Connections" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(CONNECTIONS_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Signatures" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(SIGNATURES_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="User Interface" headerClassName={styles.verticalTabHeader}>
|
||||
{renderCheckboxesList(UI_CHECKBOXES_PROPS)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Theme" headerClassName={styles.verticalTabHeader}>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<label className="text-sm">Select Theme:</label>
|
||||
<Dropdown
|
||||
className="text-sm"
|
||||
value={interfaceSettings.theme || 'neon'} // default to "neon"
|
||||
options={THEME_OPTIONS}
|
||||
onChange={(e) => handleChangeTheme(e.value)}
|
||||
placeholder="Select a theme"
|
||||
/>
|
||||
</div>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,8 @@ import clsx from 'clsx';
|
||||
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
|
||||
import { CharacterCard, WdCheckbox } from '@/hooks/Mapper/components/ui-kit';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
type WindowLocalSettingsType = {
|
||||
compact: boolean;
|
||||
@@ -50,14 +52,22 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
|
||||
defaultValue: STORED_DEFAULT_VALUES,
|
||||
});
|
||||
|
||||
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
|
||||
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
|
||||
|
||||
const showOffline = useMemo(
|
||||
() => !restrictOfflineShowing || isAdminOrManager,
|
||||
[isAdminOrManager, restrictOfflineShowing],
|
||||
);
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
const out = characters.map(x => ({ ...x, isOwn: userCharacters.includes(x.eve_id) })).sort(sortCharacters);
|
||||
if (!settings.hideOffline) {
|
||||
if (showOffline && !settings.hideOffline) {
|
||||
return out;
|
||||
}
|
||||
|
||||
return out.filter(x => x.online);
|
||||
}, [characters, settings.hideOffline, userCharacters]);
|
||||
}, [showOffline, characters, settings.hideOffline, userCharacters]);
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
@@ -70,14 +80,16 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
|
||||
>
|
||||
<div className={clsx(classes.SidebarContent, '')}>
|
||||
<div className={'flex justify-end items-center gap-2 px-3'}>
|
||||
<WdCheckbox
|
||||
size="m"
|
||||
labelSide="left"
|
||||
label={'Hide offline'}
|
||||
value={settings.hideOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
|
||||
/>
|
||||
{showOffline && (
|
||||
<WdCheckbox
|
||||
size="m"
|
||||
labelSide="left"
|
||||
label={'Hide offline'}
|
||||
value={settings.hideOffline}
|
||||
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
|
||||
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<VirtualScroller
|
||||
|
||||
@@ -6,6 +6,9 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
|
||||
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
|
||||
|
||||
interface RightBarProps {
|
||||
onShowOnTheMap?: () => void;
|
||||
onShowMapSettings?: () => void;
|
||||
@@ -14,6 +17,8 @@ interface RightBarProps {
|
||||
export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) => {
|
||||
const { outCommand, interfaceSettings, setInterfaceSettings } = useMapRootState();
|
||||
|
||||
const canTrackCharacters = useMapCheckPermissions([UserPermission.TRACK_CHARACTER]);
|
||||
|
||||
const isShowMinimap = interfaceSettings.isShowMinimap === undefined ? true : interfaceSettings.isShowMinimap;
|
||||
|
||||
const handleAddCharacter = useCallback(() => {
|
||||
@@ -64,19 +69,21 @@ export const RightBar = ({ onShowOnTheMap, onShowMapSettings }: RightBarProps) =
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
|
||||
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
onClick={onShowOnTheMap}
|
||||
>
|
||||
<i className="pi pi-hashtag"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
{canTrackCharacters && (
|
||||
<WdTooltipWrapper content="Show on the map" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
onClick={onShowOnTheMap}
|
||||
>
|
||||
<i className="pi pi-hashtag"></i>
|
||||
</button>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center mb-2 gap-1">
|
||||
<WdTooltipWrapper content="User settings" position={TooltipPosition.left}>
|
||||
<WdTooltipWrapper content="Map user settings" position={TooltipPosition.left}>
|
||||
<button
|
||||
className="btn bg-transparent text-gray-400 hover:text-white border-transparent hover:bg-transparent py-2 h-auto min-h-auto"
|
||||
type="button"
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.SystemView {
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -2,126 +2,102 @@ import { Dropdown } from 'primereact/dropdown';
|
||||
import clsx from 'clsx';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useMemo } from 'react';
|
||||
import { SystemView } from '@/hooks/Mapper/components/ui-kit';
|
||||
import classes from './SignatureK162TypeSelect.module.scss';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SOLAR_SYSTEM_CLASS_IDS } from '@/hooks/Mapper/components/map/constants.ts';
|
||||
import { WHClassView } from '@/hooks/Mapper/components/ui-kit';
|
||||
|
||||
export const k162Types = [
|
||||
{
|
||||
label: 'Hi-Sec',
|
||||
value: 'hs',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.hs,
|
||||
security: '0.8',
|
||||
whClassName: 'A641',
|
||||
},
|
||||
{
|
||||
label: 'Low-Sec',
|
||||
value: 'ls',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.ls,
|
||||
security: '0.4',
|
||||
whClassName: 'J377',
|
||||
},
|
||||
{
|
||||
label: 'Null-Sec',
|
||||
value: 'ns',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.ns,
|
||||
security: '-0.5',
|
||||
whClassName: 'C248',
|
||||
},
|
||||
{
|
||||
label: 'C1',
|
||||
value: 'c1',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.c1,
|
||||
whClassName: 'E004',
|
||||
},
|
||||
{
|
||||
label: 'C2',
|
||||
value: 'c2',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.c2,
|
||||
whClassName: 'D382',
|
||||
},
|
||||
{
|
||||
label: 'C3',
|
||||
value: 'c3',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.c3,
|
||||
whClassName: 'L477',
|
||||
},
|
||||
{
|
||||
label: 'C4',
|
||||
value: 'c4',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.c4,
|
||||
whClassName: 'M001',
|
||||
},
|
||||
{
|
||||
label: 'C5',
|
||||
value: 'c5',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.c5,
|
||||
whClassName: 'L614',
|
||||
},
|
||||
{
|
||||
label: 'C6',
|
||||
value: 'c6',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.c6,
|
||||
whClassName: 'G008',
|
||||
},
|
||||
{
|
||||
label: 'C13',
|
||||
value: 'c13',
|
||||
whClassName: 'A009',
|
||||
},
|
||||
{
|
||||
label: 'Thera',
|
||||
value: 'thera',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.thera,
|
||||
whClassName: 'F353',
|
||||
},
|
||||
{
|
||||
label: 'Pochven',
|
||||
value: 'pochven',
|
||||
system_class: SOLAR_SYSTEM_CLASS_IDS.pochven,
|
||||
whClassName: 'F216',
|
||||
},
|
||||
];
|
||||
|
||||
import { SolarSystemStaticInfoRaw } from '@/hooks/Mapper/types';
|
||||
|
||||
const renderNoValue = () => <div className="flex gap-2 items-center">-Unknown-</div>;
|
||||
|
||||
// @ts-ignore
|
||||
export const renderK162Type = (
|
||||
option: {
|
||||
label?: string;
|
||||
value: string;
|
||||
security?: string;
|
||||
system_class?: number;
|
||||
},
|
||||
className?: string,
|
||||
) => {
|
||||
export const renderK162Type = (option: {
|
||||
label?: string;
|
||||
value: string;
|
||||
security?: string;
|
||||
system_class?: number;
|
||||
whClassName?: string;
|
||||
}) => {
|
||||
if (!option) {
|
||||
return renderNoValue();
|
||||
}
|
||||
const { value, label = '', system_class = 0, security = '1.0' } = option;
|
||||
const { value, whClassName = '' } = option;
|
||||
if (value == null) {
|
||||
return renderNoValue();
|
||||
}
|
||||
|
||||
const systemInfo: SolarSystemStaticInfoRaw = {
|
||||
region_id: 0,
|
||||
constellation_id: 0,
|
||||
solar_system_id: 0,
|
||||
constellation_name: '',
|
||||
region_name: '',
|
||||
system_class: system_class,
|
||||
security: security,
|
||||
type_description: '',
|
||||
class_title: label,
|
||||
is_shattered: false,
|
||||
effect_name: '',
|
||||
effect_power: 0,
|
||||
statics: [],
|
||||
wandering: [],
|
||||
triglavian_invasion_status: '',
|
||||
sun_type_id: 0,
|
||||
solar_system_name: '',
|
||||
solar_system_name_lc: '',
|
||||
};
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<SystemView systemId="" className={className} showCustomName hideRegion systemInfo={systemInfo} />
|
||||
</div>
|
||||
<WHClassView
|
||||
classNameWh="!text-[11px] !font-bold"
|
||||
hideWhClassName
|
||||
hideTooltip
|
||||
whClassName={whClassName}
|
||||
noOffset
|
||||
useShortTitle
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
export const renderK162TypeOption = option => {
|
||||
return renderK162Type(option, classes.SystemView);
|
||||
};
|
||||
|
||||
export interface SignatureK162TypeSelectProps {
|
||||
name: string;
|
||||
defaultValue?: string;
|
||||
@@ -150,8 +126,8 @@ export const SignatureK162TypeSelect = ({ name, defaultValue = '' }: SignatureK1
|
||||
placeholder="Select K162 type"
|
||||
className={clsx('w-full')}
|
||||
scrollHeight="240px"
|
||||
itemTemplate={renderK162TypeOption}
|
||||
valueTemplate={renderK162TypeOption}
|
||||
itemTemplate={renderK162Type}
|
||||
valueTemplate={renderK162Type}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -17,7 +17,17 @@ const getPossibleWormholes = (systemStatic: SolarSystemStaticInfoRaw, wormholes:
|
||||
|
||||
// @ts-ignore
|
||||
const spawnClassGroup = SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS[whType];
|
||||
const possibleWHTypes = wormholes.filter(x => x.src.includes(spawnClassGroup));
|
||||
const possibleWHTypes = wormholes.filter(x => {
|
||||
return x.src.some(x => {
|
||||
const [group, type] = x.split('-');
|
||||
|
||||
if (type === 'shattered') {
|
||||
return systemStatic.is_shattered && group === spawnClassGroup;
|
||||
}
|
||||
|
||||
return group === spawnClassGroup;
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
statics: possibleWHTypes
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Map } from '@/hooks/Mapper/components/map/Map.tsx';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { OutCommand, OutCommandHandler, SolarSystemConnection } from '@/hooks/Mapper/types';
|
||||
import { MapRootData, useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import { OnMapAddSystemCallback, OnMapSelectionChange } from '@/hooks/Mapper/components/map/map.types.ts';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import { ContextMenuSystem, useContextMenuSystemHandlers } from '@/hooks/Mapper/components/contexts';
|
||||
import {
|
||||
@@ -14,14 +14,18 @@ import classes from './MapWrapper.module.scss';
|
||||
import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections';
|
||||
import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple';
|
||||
import { getSystemById } from '@/hooks/Mapper/helpers';
|
||||
import { Node } from 'reactflow';
|
||||
import { Node, XYPosition } from 'reactflow';
|
||||
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { useMapEventListener } from '@/hooks/Mapper/events';
|
||||
import { emitMapEvent, useMapEventListener } from '@/hooks/Mapper/events';
|
||||
|
||||
import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { useCommonMapEventProcessor } from '@/hooks/Mapper/components/mapWrapper/hooks/useCommonMapEventProcessor.ts';
|
||||
import {
|
||||
AddSystemDialog,
|
||||
SearchOnSubmitCallback,
|
||||
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
|
||||
|
||||
// TODO: INFO - this component needs for abstract work with Map instance
|
||||
export const MapWrapper = () => {
|
||||
@@ -34,6 +38,9 @@ export const MapWrapper = () => {
|
||||
isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap,
|
||||
isShowKSpace,
|
||||
isThickConnections,
|
||||
isShowBackgroundPattern,
|
||||
isSoftBackground,
|
||||
theme,
|
||||
},
|
||||
} = useMapRootState();
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
@@ -45,6 +52,7 @@ export const MapWrapper = () => {
|
||||
const [openSettings, setOpenSettings] = useState<string | null>(null);
|
||||
const [openLinkSignatures, setOpenLinkSignatures] = useState<any | null>(null);
|
||||
const [openCustomLabel, setOpenCustomLabel] = useState<string | null>(null);
|
||||
const [openAddSystem, setOpenAddSystem] = useState<XYPosition | null>(null);
|
||||
const [selectedConnection, setSelectedConnection] = useState<SolarSystemConnection | null>(null);
|
||||
|
||||
const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems });
|
||||
@@ -121,6 +129,28 @@ export const MapWrapper = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onAddSystem: OnMapAddSystemCallback = useCallback(({ coordinates }) => {
|
||||
setOpenAddSystem(coordinates);
|
||||
}, []);
|
||||
|
||||
const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback(
|
||||
async item => {
|
||||
if (ref.current.systems.some(x => x.system_static_info.solar_system_id === item.value)) {
|
||||
emitMapEvent({
|
||||
name: Commands.centerSystem,
|
||||
data: item.value.toString(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await outCommand({
|
||||
type: OutCommand.manualAddSystem,
|
||||
data: { coordinates: openAddSystem, solar_system_id: item.value },
|
||||
});
|
||||
},
|
||||
[openAddSystem, outCommand],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
@@ -135,6 +165,10 @@ export const MapWrapper = () => {
|
||||
showKSpaceBG={isShowKSpace}
|
||||
onManualDelete={handleManualDelete}
|
||||
isThickConnections={isThickConnections}
|
||||
isShowBackgroundPattern={isShowBackgroundPattern}
|
||||
isSoftBackground={isSoftBackground}
|
||||
theme={theme}
|
||||
onAddSystem={onAddSystem}
|
||||
/>
|
||||
|
||||
{openSettings != null && (
|
||||
@@ -149,6 +183,12 @@ export const MapWrapper = () => {
|
||||
<SystemLinkSignatureDialog data={openLinkSignatures} setVisible={() => setOpenLinkSignatures(null)} />
|
||||
)}
|
||||
|
||||
<AddSystemDialog
|
||||
visible={!!openAddSystem}
|
||||
setVisible={() => setOpenAddSystem(null)}
|
||||
onSubmit={handleSubmitAddSystem}
|
||||
/>
|
||||
|
||||
<Connections selectedConnection={selectedConnection} onHide={() => setSelectedConnection(null)} />
|
||||
|
||||
<ContextMenuSystem
|
||||
|
||||
@@ -18,7 +18,7 @@ const Topbar = ({ children }: WithChildren) => {
|
||||
<nav
|
||||
className={clsx(
|
||||
'px-2 flex items-center justify-center min-w-0 h-12 pointer-events-auto',
|
||||
'border-b border-gray-900 bg-gray-800 bg-opacity-5',
|
||||
'border-b border-stone-800 bg-gray-800 bg-opacity-5',
|
||||
'bg-opacity-70 bg-neutral-900',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
.WHClassViewRoot {
|
||||
|
||||
}
|
||||
|
||||
.WHClassViewContent {
|
||||
|
||||
@@ -18,7 +18,9 @@ export interface WHClassViewProps {
|
||||
whClassName: string;
|
||||
noOffset?: boolean;
|
||||
useShortTitle?: boolean;
|
||||
hideTooltip?: boolean;
|
||||
hideWhClass?: boolean;
|
||||
hideWhClassName?: boolean;
|
||||
highlightName?: boolean;
|
||||
className?: string;
|
||||
classNameWh?: string;
|
||||
@@ -28,7 +30,9 @@ export const WHClassView = ({
|
||||
whClassName,
|
||||
noOffset,
|
||||
useShortTitle,
|
||||
hideTooltip,
|
||||
hideWhClass,
|
||||
hideWhClassName,
|
||||
highlightName,
|
||||
className,
|
||||
classNameWh,
|
||||
@@ -45,25 +49,27 @@ export const WHClassView = ({
|
||||
|
||||
return (
|
||||
<div className={clsx(classes.WHClassViewRoot, className)}>
|
||||
<Tooltip
|
||||
target={`.wh-name${whClassName}${uid}`}
|
||||
position="right"
|
||||
mouseTrack
|
||||
mouseTrackLeft={20}
|
||||
mouseTrackTop={30}
|
||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
|
||||
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
|
||||
{!hideTooltip && (
|
||||
<Tooltip
|
||||
target={`.wh-name${whClassName}${uid}`}
|
||||
position="right"
|
||||
mouseTrack
|
||||
mouseTrackLeft={20}
|
||||
mouseTrackTop={30}
|
||||
className="border border-green-300 rounded border-opacity-10 bg-stone-900 bg-opacity-90 "
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Total mass">{prepareMass(whData.total_mass)}</InfoDrawer>
|
||||
<InfoDrawer title="Jump mass">{prepareMass(whData.max_mass_per_jump)}</InfoDrawer>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
|
||||
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<InfoDrawer title="Lifetime">{whData.lifetime}h</InfoDrawer>
|
||||
<InfoDrawer title="Mass regen">{prepareMass(whData.mass_regen)}</InfoDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -73,7 +79,7 @@ export const WHClassView = ({
|
||||
`wh-name${whClassName}${uid}`,
|
||||
)}
|
||||
>
|
||||
<span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>
|
||||
{!hideWhClassName && <span className={clsx({ [whClassStyle]: highlightName })}>{whClassName}</span>}
|
||||
{!hideWhClass && whClass && (
|
||||
<span className={clsx(classes.WHClassName, whClassStyle, classNameWh)}>
|
||||
{useShortTitle ? whClass.shortTitle : whClass.shortName}
|
||||
|
||||
@@ -31,12 +31,15 @@ const prepareEffects = (effects: Record<string, EffectRaw>, effectName: string,
|
||||
return out;
|
||||
};
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export interface WHEffectViewProps {
|
||||
effectName: string;
|
||||
effectPower: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) => {
|
||||
export const WHEffectView = ({ effectName, effectPower, className }: WHEffectViewProps) => {
|
||||
const {
|
||||
data: { effects },
|
||||
} = useMapRootState();
|
||||
@@ -49,7 +52,7 @@ export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) =>
|
||||
[effectName, effectPower, effects],
|
||||
);
|
||||
|
||||
const targetClass = `wh-effect-name${effectInfo.id}`;
|
||||
const targetClass = useMemo(() => `wh-effect-name${effectInfo.id}-${counter++}`, []);
|
||||
|
||||
return (
|
||||
<div className={classes.WHEffectViewRoot}>
|
||||
@@ -84,7 +87,7 @@ export const WHEffectView = ({ effectName, effectPower }: WHEffectViewProps) =>
|
||||
</div>
|
||||
</FixedTooltip>
|
||||
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass)}>
|
||||
<div className={clsx('font-bold select-none cursor-help w-min-content', effectClass, targetClass, className)}>
|
||||
{effectName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { COSMIC_SIGNATURE } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignatureSettingsDialog';
|
||||
import { SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { SignatureGroup, SignatureKind, SystemSignature } from '@/hooks/Mapper/types';
|
||||
import { MAPPING_TYPE_TO_ENG } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
|
||||
|
||||
export const parseSignatures = (value: string, availableKeys: string[]): SystemSignature[] => {
|
||||
const outArr: SystemSignature[] = [];
|
||||
@@ -14,10 +14,12 @@ export const parseSignatures = (value: string, availableKeys: string[]): SystemS
|
||||
continue;
|
||||
}
|
||||
|
||||
const kind = MAPPING_TYPE_TO_ENG[sigArrInfo[1] as SignatureKind];
|
||||
|
||||
outArr.push({
|
||||
eve_id: sigArrInfo[0],
|
||||
kind: availableKeys.includes(sigArrInfo[1]) ? sigArrInfo[1] : COSMIC_SIGNATURE,
|
||||
group: sigArrInfo[2],
|
||||
kind: availableKeys.includes(kind) ? kind : SignatureKind.CosmicSignature,
|
||||
group: sigArrInfo[2] as SignatureGroup,
|
||||
name: sigArrInfo[3],
|
||||
type: '',
|
||||
});
|
||||
|
||||
@@ -30,5 +30,5 @@ export const useClipboard = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { clipboardContent, error, getClipboardContent };
|
||||
return { clipboardContent, error, getClipboardContent, setClipboardContent };
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ const INITIAL_DATA: MapRootData = {
|
||||
selectedSystems: [],
|
||||
selectedConnections: [],
|
||||
userPermissions: {},
|
||||
options: {},
|
||||
};
|
||||
|
||||
export enum InterfaceStoredSettingsProps {
|
||||
@@ -33,6 +34,10 @@ export enum InterfaceStoredSettingsProps {
|
||||
isShowMinimap = 'isShowMinimap',
|
||||
isShowKSpace = 'isShowKSpace',
|
||||
isThickConnections = 'isThickConnections',
|
||||
isShowUnsplashedSignatures = 'isShowUnsplashedSignatures',
|
||||
isShowBackgroundPattern = 'isShowBackgroundPattern',
|
||||
isSoftBackground = 'isSoftBackground',
|
||||
theme = 'theme',
|
||||
}
|
||||
|
||||
export type InterfaceStoredSettings = {
|
||||
@@ -40,6 +45,10 @@ export type InterfaceStoredSettings = {
|
||||
isShowMinimap: boolean;
|
||||
isShowKSpace: boolean;
|
||||
isThickConnections: boolean;
|
||||
isShowUnsplashedSignatures: boolean;
|
||||
isShowBackgroundPattern: boolean;
|
||||
isSoftBackground: boolean;
|
||||
theme: string;
|
||||
};
|
||||
|
||||
export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
@@ -47,7 +56,11 @@ export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
|
||||
isShowMinimap: true,
|
||||
isShowKSpace: false,
|
||||
isThickConnections: false,
|
||||
};
|
||||
isShowUnsplashedSignatures: false,
|
||||
isShowBackgroundPattern: true,
|
||||
isSoftBackground: false,
|
||||
theme: 'neon',
|
||||
}
|
||||
|
||||
export interface MapRootContextProps {
|
||||
update: ContextStoreDataUpdate<MapRootData>;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './useMapInit';
|
||||
export * from './useMapUpdated';
|
||||
export * from './useMapCheckPermissions';
|
||||
export * from './useMapGetOption';
|
||||
export * from './useRoutes';
|
||||
export * from './useCommandsConnections';
|
||||
export * from './useCommandsSystems';
|
||||
|
||||
@@ -2,11 +2,14 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { CommandAddSystems, CommandRemoveSystems, CommandUpdateSystems } from '@/hooks/Mapper/types';
|
||||
import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts';
|
||||
|
||||
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts';
|
||||
export const useCommandsSystems = () => {
|
||||
const {
|
||||
update,
|
||||
data: { systems },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const { addSystemStatic } = useLoadSystemStatic({ systems: [] });
|
||||
@@ -56,5 +59,29 @@ export const useCommandsSystems = () => {
|
||||
update({ systems: out }, true);
|
||||
}, []);
|
||||
|
||||
return { addSystems, removeSystems, updateSystems };
|
||||
const updateSystemSignatures = useCallback(
|
||||
async (systemId: string) => {
|
||||
const { update, systems } = ref.current;
|
||||
|
||||
const { signatures } = await outCommand({
|
||||
type: OutCommand.getSignatures,
|
||||
data: { system_id: `${systemId}` },
|
||||
});
|
||||
|
||||
const out = systems.map(current => {
|
||||
if (current.id === `${systemId}`) {
|
||||
return { ...current, system_signatures: signatures };
|
||||
}
|
||||
|
||||
return current;
|
||||
});
|
||||
|
||||
update({ systems: out }, true);
|
||||
|
||||
emitMapEvent({ name: Commands.updateSystems, data: out });
|
||||
},
|
||||
[outCommand],
|
||||
);
|
||||
|
||||
return { addSystems, removeSystems, updateSystems, updateSystemSignatures };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
export const useMapGetOption = (option: string) => {
|
||||
const {
|
||||
data: { options },
|
||||
} = useMapRootState();
|
||||
|
||||
return useMemo(() => options[option], [option, options]);
|
||||
};
|
||||
@@ -20,6 +20,7 @@ export const useMapInit = () => {
|
||||
present_characters,
|
||||
hubs,
|
||||
user_permissions,
|
||||
options,
|
||||
}: CommandInit) => {
|
||||
const updateData: Partial<MapRootData> = {};
|
||||
|
||||
@@ -60,6 +61,10 @@ export const useMapInit = () => {
|
||||
updateData.hubs = hubs;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
updateData.options = options;
|
||||
}
|
||||
|
||||
if (system_static_infos) {
|
||||
system_static_infos.forEach(static_info => {
|
||||
addSystemStatic(static_info);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CommandRemoveSystems,
|
||||
CommandRoutes,
|
||||
Commands,
|
||||
CommandSignaturesUpdated,
|
||||
CommandUpdateConnection,
|
||||
CommandUpdateSystems,
|
||||
MapHandlers,
|
||||
@@ -31,7 +32,7 @@ import { emitMapEvent } from '@/hooks/Mapper/events';
|
||||
|
||||
export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
const mapInit = useMapInit();
|
||||
const { addSystems, removeSystems, updateSystems } = useCommandsSystems();
|
||||
const { addSystems, removeSystems, updateSystems, updateSystemSignatures } = useCommandsSystems();
|
||||
const { addConnections, removeConnections, updateConnection } = useCommandsConnections();
|
||||
const { charactersUpdated, characterAdded, characterRemoved, characterUpdated, presentCharacters } =
|
||||
useCommandsCharacters();
|
||||
@@ -88,7 +89,7 @@ export const useMapRootHandlers = (ref: ForwardedRef<MapHandlers>) => {
|
||||
break;
|
||||
|
||||
case Commands.signaturesUpdated: // USED
|
||||
// do nothing here
|
||||
updateSystemSignatures(data as CommandSignaturesUpdated);
|
||||
break;
|
||||
|
||||
case Commands.linkSignatureToSystem: // USED
|
||||
|
||||
@@ -14,16 +14,12 @@ export enum TimeStatus {
|
||||
eol,
|
||||
}
|
||||
|
||||
// export enum ShipSizeStatus {
|
||||
// small, // frigates, destroyers - less than 5K t
|
||||
// medium, // less than 20K t
|
||||
// large, // less than 375K t
|
||||
// capital, // less than 1.8M t
|
||||
// }
|
||||
|
||||
export enum ShipSizeStatus {
|
||||
small, // frigates, destroyers - less than 5K t
|
||||
normal,
|
||||
small = 0, // frigates, destroyers - less than 5K t
|
||||
medium = 1, // less than 62K t
|
||||
large = 2, // less than 375K t
|
||||
freight = 3, // less than 1M t
|
||||
capital = 4, // less than 1.8M t
|
||||
}
|
||||
|
||||
export type SolarSystemConnection = {
|
||||
|
||||
@@ -62,6 +62,7 @@ export type CommandInit = {
|
||||
user_permissions: UserPermissions;
|
||||
hubs: string[];
|
||||
routes: RoutesList;
|
||||
options: Record<string, string | boolean>;
|
||||
reset?: boolean;
|
||||
};
|
||||
export type CommandAddSystems = SolarSystemRawType[];
|
||||
@@ -128,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',
|
||||
@@ -152,6 +154,7 @@ export enum OutCommand {
|
||||
getUserSettings = 'get_user_settings',
|
||||
updateUserSettings = 'update_user_settings',
|
||||
unlinkSignature = 'unlink_signature',
|
||||
searchSystems = 'search_systems',
|
||||
}
|
||||
|
||||
export type OutCommandHandler = <T = any>(event: { type: OutCommand; data: any }) => Promise<T>;
|
||||
|
||||
@@ -19,4 +19,5 @@ export type MapUnionTypes = {
|
||||
kills: Record<number, number>;
|
||||
connections: SolarSystemConnection[];
|
||||
userPermissions: Partial<UserPermissions>;
|
||||
options: Record<string, string | boolean>;
|
||||
};
|
||||
|
||||
@@ -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 = 'Боевой район',
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { XYPosition } from 'reactflow';
|
||||
|
||||
import { SystemSignature } from '@/hooks/Mapper/types/signatures';
|
||||
|
||||
export enum SolarSystemStaticInfoRawNames {
|
||||
regionId = 'region_id',
|
||||
constellationId = 'constellation_id',
|
||||
@@ -114,6 +116,18 @@ export type SolarSystemRawType = {
|
||||
tag: string | null;
|
||||
status: number;
|
||||
name: string | null;
|
||||
temporary_name: string | null;
|
||||
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
system_signatures: SystemSignature[];
|
||||
};
|
||||
|
||||
export type SearchSystemItem = {
|
||||
class_title: string;
|
||||
constellation_name: string;
|
||||
label: string;
|
||||
region_name: string;
|
||||
system_static_info: SolarSystemStaticInfoRaw;
|
||||
value: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
export default {
|
||||
mounted() {
|
||||
const hook = this;
|
||||
const url = hook.el.dataset.url;
|
||||
const button = hook.el;
|
||||
const button = this.el;
|
||||
|
||||
button.addEventListener('click', function () {
|
||||
// Get the URL from the data attribute
|
||||
|
||||
button.classList.remove('copied');
|
||||
|
||||
// Copy the URL to the clipboard
|
||||
navigator.clipboard
|
||||
.writeText(url)
|
||||
.writeText(button.dataset.url)
|
||||
.then(() => {
|
||||
button.classList.add('copied');
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import CopyToClipboard from './copyToClipboard';
|
||||
import DownloadJson from './downloadJson';
|
||||
import NewVersionUpdate from './newVersionUpdate';
|
||||
import MapAction from './maps/mapAction';
|
||||
import ShowCharactersAddAlert from './showCharactersAddAlert';
|
||||
|
||||
export default {
|
||||
DownloadJson,
|
||||
@@ -20,4 +21,5 @@ export default {
|
||||
Ping,
|
||||
CopyToClipboard,
|
||||
NewVersionUpdate,
|
||||
ShowCharactersAddAlert,
|
||||
};
|
||||
|
||||
11
assets/js/hooks/showCharactersAddAlert.ts
Normal file
11
assets/js/hooks/showCharactersAddAlert.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default {
|
||||
mounted() {
|
||||
this.pushEvent('restore_show_characters_add_alert', {
|
||||
value: localStorage.getItem('wanderer:hide_characters_add_alert') !== 'true',
|
||||
});
|
||||
|
||||
document.getElementById('characters-add-alert-hide')?.addEventListener('click', e => {
|
||||
localStorage.setItem('wanderer:hide_characters_add_alert', 'true');
|
||||
});
|
||||
},
|
||||
};
|
||||
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
BIN
assets/static/images/news/01-05-map-public-api/generate-key.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -48,6 +48,11 @@ port =
|
||||
|
||||
scheme = System.get_env("WEB_EXTERNAL_SCHEME", "http")
|
||||
|
||||
public_api_disabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_PUBLIC_API_DISABLED", "false")
|
||||
|> String.to_existing_atom()
|
||||
|
||||
map_subscriptions_enabled =
|
||||
config_dir
|
||||
|> get_var_from_path_or_env("WANDERER_MAP_SUBSCRIPTIONS_ENABLED", "false")
|
||||
@@ -83,6 +88,7 @@ config :wanderer_app,
|
||||
admins: admins,
|
||||
corp_id: System.get_env("WANDERER_CORP_ID", "-1") |> String.to_integer(),
|
||||
corp_wallet: System.get_env("WANDERER_CORP_WALLET", ""),
|
||||
public_api_disabled: public_api_disabled,
|
||||
map_subscriptions_enabled: map_subscriptions_enabled,
|
||||
wallet_tracking_enabled: wallet_tracking_enabled,
|
||||
subscription_settings: %{
|
||||
|
||||
@@ -21,6 +21,7 @@ defmodule WandererApp.Api.Map do
|
||||
define(:update_options, action: :update_options)
|
||||
define(:assign_owner, action: :assign_owner)
|
||||
define(:mark_as_deleted, action: :mark_as_deleted)
|
||||
define(:update_api_key, action: :update_api_key)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
@@ -122,6 +123,11 @@ defmodule WandererApp.Api.Map do
|
||||
|
||||
change(set_attribute(:deleted, true))
|
||||
end
|
||||
|
||||
update :update_api_key do
|
||||
accept [:public_api_key]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -141,6 +147,10 @@ defmodule WandererApp.Api.Map do
|
||||
attribute :description, :string
|
||||
attribute :personal_note, :string
|
||||
|
||||
attribute :public_api_key, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :hubs, {:array, :string} do
|
||||
allow_nil?(true)
|
||||
|
||||
|
||||
@@ -12,32 +12,37 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
|
||||
code_interface do
|
||||
define(:create, action: :create)
|
||||
define(:destroy, action: :destroy)
|
||||
|
||||
define(:read_by_map,
|
||||
action: :read_by_map
|
||||
)
|
||||
|
||||
define(:tracked_by_map_filtered,
|
||||
action: :tracked_by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_all,
|
||||
action: :tracked_by_map_all
|
||||
)
|
||||
define(:read_by_map, action: :read_by_map)
|
||||
define(:by_map_filtered, action: :by_map_filtered)
|
||||
define(:tracked_by_map_filtered, action: :tracked_by_map_filtered)
|
||||
define(:tracked_by_map_all, action: :tracked_by_map_all)
|
||||
|
||||
define(:track, action: :track)
|
||||
define(:untrack, action: :untrack)
|
||||
|
||||
define(:follow, action: :follow)
|
||||
define(:unfollow, action: :unfollow)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:map_id,
|
||||
:character_id,
|
||||
:tracked
|
||||
:tracked,
|
||||
:followed
|
||||
]
|
||||
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
|
||||
read :by_map_filtered do
|
||||
argument(:map_id, :string, allow_nil?: false)
|
||||
argument(:character_ids, {:array, :uuid}, allow_nil?: false)
|
||||
|
||||
filter(expr(map_id == ^arg(:map_id) and character_id in ^arg(:character_ids)))
|
||||
end
|
||||
|
||||
read :tracked_by_map_filtered do
|
||||
argument(:map_id, :string, allow_nil?: false)
|
||||
argument(:character_ids, {:array, :uuid}, allow_nil?: false)
|
||||
@@ -64,6 +69,14 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
update :untrack do
|
||||
change(set_attribute(:tracked, false))
|
||||
end
|
||||
|
||||
update :follow do
|
||||
change(set_attribute(:followed, true))
|
||||
end
|
||||
|
||||
update :unfollow do
|
||||
change(set_attribute(:followed, false))
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -74,6 +87,11 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :followed, :boolean do
|
||||
default false
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -128,11 +128,13 @@ defmodule WandererApp.Api.MapConnection do
|
||||
allow_nil?(true)
|
||||
end
|
||||
|
||||
# where 0 - Frigate
|
||||
# where 1 - Medium and Large
|
||||
# where 2 - Capital
|
||||
# where 0 - Frigate (small
|
||||
# where 1 - Medium
|
||||
# where 2 - Large
|
||||
# where 3 - Freight
|
||||
# where 4 - Capital
|
||||
attribute :ship_size_type, :integer do
|
||||
default(1)
|
||||
default(2)
|
||||
|
||||
allow_nil?(true)
|
||||
end
|
||||
|
||||
@@ -46,6 +46,7 @@ defmodule WandererApp.Api.MapSystem do
|
||||
define(:update_locked, action: :update_locked)
|
||||
define(:update_status, action: :update_status)
|
||||
define(:update_tag, action: :update_tag)
|
||||
define(:update_temporary_name, action: :update_temporary_name)
|
||||
define(:update_labels, action: :update_labels)
|
||||
define(:update_position, action: :update_position)
|
||||
define(:update_visible, action: :update_visible)
|
||||
@@ -102,6 +103,10 @@ defmodule WandererApp.Api.MapSystem do
|
||||
accept [:tag]
|
||||
end
|
||||
|
||||
update :update_temporary_name do
|
||||
accept [:temporary_name]
|
||||
end
|
||||
|
||||
update :update_labels do
|
||||
accept [:labels]
|
||||
end
|
||||
@@ -141,6 +146,10 @@ defmodule WandererApp.Api.MapSystem do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :temporary_name, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :labels, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
)
|
||||
|
||||
define(:by_system_id, action: :by_system_id, args: [:system_id])
|
||||
define(:by_linked_system_id, action: :by_linked_system_id, args: [:linked_system_id])
|
||||
end
|
||||
|
||||
actions do
|
||||
@@ -99,6 +100,12 @@ defmodule WandererApp.Api.MapSystemSignature do
|
||||
|
||||
filter(expr(system_id == ^arg(:system_id)))
|
||||
end
|
||||
|
||||
read :by_linked_system_id do
|
||||
argument(:linked_system_id, :integer, allow_nil?: false)
|
||||
|
||||
filter(expr(linked_system_id == ^arg(:linked_system_id)))
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
|
||||
@@ -446,8 +446,13 @@ defmodule WandererApp.Character.Tracker do
|
||||
|> Map.merge(%{alliance_id: alliance_id, corporation_id: corporation_id})
|
||||
|> maybe_update_alliance()
|
||||
|
||||
_error ->
|
||||
Logger.warning("Failed to get corporation info for #{corporation_id}")
|
||||
error ->
|
||||
Logger.warning(
|
||||
"Failed to get corporation info for character #{character_id}: #{inspect(error)}",
|
||||
character_id: character_id,
|
||||
corporation_id: corporation_id
|
||||
)
|
||||
|
||||
state
|
||||
end
|
||||
end
|
||||
@@ -484,7 +489,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
defp maybe_update_location(
|
||||
%{
|
||||
character_id: character_id
|
||||
character_id: character_id,
|
||||
} =
|
||||
state,
|
||||
location
|
||||
|
||||
@@ -9,6 +9,7 @@ defmodule WandererApp.Env do
|
||||
def custom_route_base_url, do: get_key(:custom_route_base_url, "<CUSTOM_ROUTE_BASE_URL>")
|
||||
def invites, do: get_key(:invites, false)
|
||||
def map_subscriptions_enabled?, do: get_key(:map_subscriptions_enabled, false)
|
||||
def public_api_disabled?, do: get_key(:public_api_disabled, false)
|
||||
def wallet_tracking_enabled?, do: get_key(:wallet_tracking_enabled, false)
|
||||
def admins, do: get_key(:admins, [])
|
||||
def admin_username, do: get_key(:admin_username)
|
||||
|
||||
@@ -49,7 +49,7 @@ defmodule WandererApp.EveDataService do
|
||||
end)
|
||||
end)
|
||||
|
||||
Task.await_many(tasks, :timer.minutes(1))
|
||||
Task.await_many(tasks, :timer.minutes(30))
|
||||
end
|
||||
|
||||
def download_file(file_name) do
|
||||
|
||||
@@ -14,6 +14,7 @@ defmodule WandererApp.Map do
|
||||
hubs: [],
|
||||
connections: Map.new(),
|
||||
acls: [],
|
||||
options: Map.new(),
|
||||
characters_limit: nil,
|
||||
hubs_limit: nil
|
||||
|
||||
@@ -69,6 +70,9 @@ defmodule WandererApp.Map do
|
||||
def get_characters_limit(map_id),
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:characters_limit, 100)}
|
||||
|
||||
def get_options(map_id),
|
||||
do: {:ok, map_id |> get_map!() |> Map.get(:options, Map.new())}
|
||||
|
||||
@doc """
|
||||
Returns a full list of characters in the map
|
||||
"""
|
||||
@@ -187,7 +191,7 @@ defmodule WandererApp.Map do
|
||||
case characters |> Enum.member?(character_id) do
|
||||
true ->
|
||||
map_id
|
||||
|> update_map(%{characters: Enum.reject(characters, fn id -> id == character_id end)})
|
||||
|> update_map(%{characters: characters |> Enum.reject(fn id -> id == character_id end)})
|
||||
|
||||
:ok
|
||||
|
||||
@@ -251,6 +255,13 @@ defmodule WandererApp.Map do
|
||||
map
|
||||
end
|
||||
|
||||
def update_options!(%{map_id: map_id} = map, options) do
|
||||
map_id
|
||||
|> update_map(%{options: options})
|
||||
|
||||
map
|
||||
end
|
||||
|
||||
def add_systems!(map, []), do: map
|
||||
|
||||
def add_systems!(%{map_id: map_id} = map, [system | rest]) do
|
||||
|
||||
@@ -129,6 +129,12 @@ defmodule WandererApp.Map.Server do
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_system_tag/2, [update]})
|
||||
|
||||
def update_system_temporary_name(map_id, update) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.cast({&Impl.update_system_temporary_name/2, [update]})
|
||||
|
||||
def update_system_locked(map_id, update) when is_binary(map_id),
|
||||
do:
|
||||
map_id
|
||||
|
||||
@@ -150,6 +150,8 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
|
||||
defdelegate update_system_tag(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_temporary_name(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_locked(state, update), to: SystemsImpl
|
||||
|
||||
defdelegate update_system_labels(state, update), to: SystemsImpl
|
||||
@@ -201,7 +203,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
| map: map |> WandererApp.Map.update_subscription_settings!(subscription_settings)
|
||||
}
|
||||
|
||||
def handle_event(:update_characters, %{map_id: map_id} = state) do
|
||||
def handle_event(:update_characters, state) do
|
||||
Process.send_after(self(), :update_characters, @update_characters_timeout)
|
||||
|
||||
CharactersImpl.update_characters(state)
|
||||
@@ -259,7 +261,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
state
|
||||
end
|
||||
|
||||
def handle_event(:cleanup_systems, %{map_id: map_id} = state) do
|
||||
def handle_event(:cleanup_systems, state) do
|
||||
Process.send_after(self(), :cleanup_systems, @systems_cleanup_timeout)
|
||||
|
||||
state |> SystemsImpl.cleanup_systems()
|
||||
@@ -277,15 +279,11 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
}
|
||||
end
|
||||
|
||||
def handle_event({:options_updated, options}, state),
|
||||
do: %{
|
||||
state
|
||||
| map_opts: [
|
||||
layout: options |> Map.get("layout", "left_to_right"),
|
||||
store_custom_labels:
|
||||
options |> Map.get("store_custom_labels", "false") |> String.to_existing_atom()
|
||||
]
|
||||
}
|
||||
def handle_event({:options_updated, options}, %{map: map} = state) do
|
||||
map |> WandererApp.Map.update_options!(options)
|
||||
|
||||
%{state | map_opts: map_options(options)}
|
||||
end
|
||||
|
||||
def handle_event({ref, _result}, %{map_id: _map_id} = state) do
|
||||
Process.demonitor(ref, [:flush])
|
||||
@@ -319,6 +317,18 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
map |> Map.put_new(attribute, get_in(update, [Access.key(attribute)]))
|
||||
end)}
|
||||
|
||||
defp map_options(options) do
|
||||
[
|
||||
layout: options |> Map.get("layout", "left_to_right"),
|
||||
store_custom_labels:
|
||||
options |> Map.get("store_custom_labels", "false") |> String.to_existing_atom(),
|
||||
show_temp_system_name:
|
||||
options |> Map.get("show_temp_system_name", "false") |> String.to_existing_atom(),
|
||||
restrict_offline_showing:
|
||||
options |> Map.get("restrict_offline_showing", "false") |> String.to_existing_atom()
|
||||
]
|
||||
end
|
||||
|
||||
defp save_map_state(%{map_id: map_id} = _state) do
|
||||
systems_last_activity =
|
||||
map_id
|
||||
@@ -389,22 +399,17 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
systems,
|
||||
connections
|
||||
) do
|
||||
{:ok, options} = WandererApp.MapRepo.options_to_form_data(initial_map)
|
||||
|
||||
map =
|
||||
initial_map
|
||||
|> WandererApp.Map.new()
|
||||
|> WandererApp.Map.update_options!(options)
|
||||
|> WandererApp.Map.update_subscription_settings!(subscription_settings)
|
||||
|> WandererApp.Map.add_systems!(systems)
|
||||
|> WandererApp.Map.add_connections!(connections)
|
||||
|> WandererApp.Map.add_characters!(characters)
|
||||
|
||||
{:ok, map_options} = WandererApp.MapRepo.options_to_form_data(initial_map)
|
||||
|
||||
map_opts = [
|
||||
layout: map_options |> Map.get("layout", "left_to_right"),
|
||||
store_custom_labels:
|
||||
map_options |> Map.get("store_custom_labels", "false") |> String.to_existing_atom()
|
||||
]
|
||||
|
||||
character_ids =
|
||||
map_id
|
||||
|> WandererApp.Map.get_map!()
|
||||
@@ -412,7 +417,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
|
||||
WandererApp.Cache.insert("map_#{map_id}:invalidate_character_ids", character_ids)
|
||||
|
||||
%{state | map: map, map_opts: map_opts}
|
||||
%{state | map: map, map_opts: map_options(options)}
|
||||
end
|
||||
|
||||
def maybe_import_systems(state, %{"systems" => systems} = _settings, user_id, character_id) do
|
||||
@@ -426,7 +431,8 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
"name" => name,
|
||||
"position" => %{"x" => x, "y" => y},
|
||||
"status" => status,
|
||||
"tag" => tag
|
||||
"tag" => tag,
|
||||
"temporary_name" => temporary_name,
|
||||
} = _system,
|
||||
acc ->
|
||||
acc
|
||||
@@ -445,6 +451,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
})
|
||||
|> update_system_status(%{solar_system_id: id |> String.to_integer(), status: status})
|
||||
|> update_system_tag(%{solar_system_id: id |> String.to_integer(), tag: tag})
|
||||
|> update_system_temporary_name(%{solar_system_id: id |> String.to_integer(), temporary_name: temporary_name})
|
||||
|> update_system_locked(%{solar_system_id: id |> String.to_integer(), locked: locked})
|
||||
|> update_system_labels(%{solar_system_id: id |> String.to_integer(), labels: labels})
|
||||
end)
|
||||
|
||||
@@ -15,13 +15,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: track_character
|
||||
tracked: track_character,
|
||||
followed: false
|
||||
}),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :added], %{count: 1})
|
||||
|
||||
:ok
|
||||
else
|
||||
_error ->
|
||||
@@ -89,6 +88,11 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
[]
|
||||
)
|
||||
|
||||
acls =
|
||||
map_id
|
||||
|> WandererApp.Map.get_map!()
|
||||
|> Map.get(:acls, [])
|
||||
|
||||
invalidate_character_ids
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
@@ -96,11 +100,6 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
|> WandererApp.Character.get_character()
|
||||
|> case do
|
||||
{:ok, character} ->
|
||||
acls =
|
||||
map_id
|
||||
|> WandererApp.Map.get_map!()
|
||||
|> Map.get(:acls, [])
|
||||
|
||||
[character_permissions] =
|
||||
WandererApp.Permissions.check_characters_access([character], acls)
|
||||
|
||||
@@ -157,12 +156,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
|> untrack_characters(character_ids)
|
||||
|
||||
map_id
|
||||
|> WandererApp.MapCharacterSettingsRepo.get_tracked_by_map_filtered(character_ids)
|
||||
|> WandererApp.MapCharacterSettingsRepo.get_by_map_filtered(character_ids)
|
||||
|> case do
|
||||
{:ok, settings} ->
|
||||
settings
|
||||
|> Enum.each(fn s ->
|
||||
WandererApp.MapCharacterSettingsRepo.untrack(s)
|
||||
WandererApp.MapCharacterSettingsRepo.destroy!(s)
|
||||
remove_character(map_id, s.character_id)
|
||||
end)
|
||||
|
||||
@@ -382,7 +381,6 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
{:character_location, %{solar_system_id: solar_system_id},
|
||||
%{solar_system_id: old_solar_system_id}}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
|
||||
@@ -333,7 +333,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
solar_system_id: location.solar_system_id,
|
||||
})
|
||||
|
||||
Impl.broadcast!(map_id, :add_connection, connection)
|
||||
@@ -346,9 +346,20 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
:ok
|
||||
{:error, :already_exists} ->
|
||||
# Still broadcast location change in case of followed character
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
|
||||
:ok
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -356,35 +367,33 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
def can_add_location(_scope, nil), do: false
|
||||
|
||||
def can_add_location(:all, _solar_system_id), do: true
|
||||
|
||||
def can_add_location(:none, _solar_system_id), do: false
|
||||
|
||||
def can_add_location(scope, solar_system_id) do
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
{:ok, system_static_info} = get_system_static_info(solar_system_id)
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(system_static_info.system_class) and
|
||||
not (@prohibited_systems |> Enum.member?(solar_system_id)) and
|
||||
@wh_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(system_static_info.system_class) and
|
||||
@known_space |> Enum.member?(system_static_info.system_class)
|
||||
|
||||
:all ->
|
||||
not is_prohibited_system_class?(system_static_info.system_class)
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def is_prohibited_system_class?(system_class) do
|
||||
@prohibited_system_classes |> Enum.member?(system_class)
|
||||
end
|
||||
|
||||
def is_connection_exist(map_id, from_solar_system_id, to_solar_system_id),
|
||||
do:
|
||||
not is_nil(
|
||||
@@ -413,24 +422,21 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
current_system_id: to_solar_system_id
|
||||
})
|
||||
|
||||
system_static_info =
|
||||
case WandererApp.CachedInfo.get_system_static_info(to_solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
system_static_info
|
||||
|
||||
_ ->
|
||||
%{system_class: nil}
|
||||
end
|
||||
{:ok, from_system_static_info} = get_system_static_info(from_solar_system_id)
|
||||
{:ok, to_system_static_info} = get_system_static_info(to_solar_system_id)
|
||||
|
||||
case scope do
|
||||
:wormholes ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(from_system_static_info.system_class) and
|
||||
not is_prohibited_system_class?(to_system_static_info.system_class) and
|
||||
not (@prohibited_systems |> Enum.member?(from_solar_system_id)) and
|
||||
not (@prohibited_systems |> Enum.member?(to_solar_system_id)) and
|
||||
known_jumps |> Enum.empty?() and to_solar_system_id != @jita and
|
||||
from_solar_system_id != @jita
|
||||
|
||||
:stargates ->
|
||||
not (@prohibited_system_classes |> Enum.member?(system_static_info.system_class)) and
|
||||
not is_prohibited_system_class?(from_system_static_info.system_class) and
|
||||
not is_prohibited_system_class?(to_system_static_info.system_class) and
|
||||
not (known_jumps |> Enum.empty?())
|
||||
end
|
||||
end
|
||||
@@ -446,6 +452,16 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_system_static_info(solar_system_id) do
|
||||
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system_static_info} when not is_nil(system_static_info) ->
|
||||
{:ok, system_static_info}
|
||||
|
||||
_ ->
|
||||
{:ok, %{system_class: nil}}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_remove_connection(map_id, location, old_location)
|
||||
when not is_nil(location) and not is_nil(old_location) and
|
||||
location.solar_system_id != old_location.solar_system_id do
|
||||
|
||||
@@ -122,6 +122,13 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
),
|
||||
do: state |> update_system(:update_tag, [:tag], update)
|
||||
|
||||
def update_system_temporary_name(
|
||||
state,
|
||||
update
|
||||
) do
|
||||
state |> update_system(:update_temporary_name, [:temporary_name], update)
|
||||
end
|
||||
|
||||
def update_system_locked(
|
||||
state,
|
||||
update
|
||||
@@ -224,6 +231,19 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
WandererApp.MapConnectionRepo.destroy(map_id, connection)
|
||||
end)
|
||||
|
||||
removed_ids
|
||||
|> Enum.map(fn solar_system_id ->
|
||||
WandererApp.Api.MapSystemSignature.by_linked_system_id!(solar_system_id)
|
||||
end)
|
||||
|> List.flatten()
|
||||
|> Enum.uniq_by(& &1.system_id)
|
||||
|> Enum.each(fn s ->
|
||||
{:ok, %{system: system}} = s |> Ash.load([:system])
|
||||
Ash.destroy!(s)
|
||||
|
||||
Impl.broadcast!(map_id, :signatures_updated, system.solar_system_id)
|
||||
end)
|
||||
|
||||
@ddrt.delete(removed_ids, rtree_name)
|
||||
|
||||
Impl.broadcast!(map_id, :remove_connections, connections_to_remove)
|
||||
@@ -273,6 +293,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|
||||
|> WandererApp.MapSystemRepo.update_visible!(%{visible: true})
|
||||
|> WandererApp.MapSystemRepo.cleanup_tags()
|
||||
|> WandererApp.MapSystemRepo.cleanup_temporary_name()
|
||||
|
||||
@ddrt.insert(
|
||||
{existing_system.solar_system_id,
|
||||
@@ -391,6 +412,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
|> WandererApp.MapSystemRepo.update_position!(%{position_x: x, position_y: y})
|
||||
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|
||||
|> WandererApp.MapSystemRepo.cleanup_tags!()
|
||||
|> WandererApp.MapSystemRepo.cleanup_temporary_name!()
|
||||
|> WandererApp.MapSystemRepo.update_visible(%{visible: true})
|
||||
end
|
||||
|
||||
@@ -473,7 +495,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
state
|
||||
else
|
||||
error ->
|
||||
Logger.error("Fail ed to update system: #{inspect(error, pretty: true)}")
|
||||
Logger.error("Failed to update system: #{inspect(error, pretty: true)}")
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -84,20 +84,22 @@ defmodule WandererApp.Maps do
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: false
|
||||
tracked: false,
|
||||
followed: false
|
||||
}
|
||||
|
||||
def map_character(
|
||||
%{name: name, id: id, eve_id: eve_id, corporation_ticker: corporation_ticker} =
|
||||
_character,
|
||||
%{tracked: tracked} = _character_settings
|
||||
%{tracked: tracked, followed: followed} = _character_settings
|
||||
),
|
||||
do: %{
|
||||
name: name,
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: tracked
|
||||
tracked: tracked,
|
||||
followed: followed
|
||||
}
|
||||
|
||||
@decorate cacheable(
|
||||
|
||||
@@ -11,15 +11,44 @@ defmodule WandererApp.MapCharacterSettingsRepo do
|
||||
character_ids: character_ids
|
||||
})
|
||||
|
||||
def get_by_map_filtered(map_id, character_ids),
|
||||
do:
|
||||
WandererApp.Api.MapCharacterSettings.by_map_filtered(%{
|
||||
map_id: map_id,
|
||||
character_ids: character_ids
|
||||
})
|
||||
|
||||
def get_all_by_map(map_id),
|
||||
do: WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map_id})
|
||||
|
||||
def get_tracked_by_map_all(map_id),
|
||||
do: WandererApp.Api.MapCharacterSettings.tracked_by_map_all(%{map_id: map_id})
|
||||
|
||||
def get_by_map(map_id, character_id) do
|
||||
case get_by_map_filtered(map_id, [character_id]) do
|
||||
{:ok, [setting | _]} ->
|
||||
{:ok, setting}
|
||||
|
||||
{:ok, []} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def track(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track()
|
||||
def untrack(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack()
|
||||
|
||||
def track!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track!()
|
||||
def untrack!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack!()
|
||||
|
||||
def follow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow()
|
||||
def unfollow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow()
|
||||
|
||||
def follow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow!()
|
||||
def unfollow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow!()
|
||||
|
||||
|
||||
def destroy!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.destroy!()
|
||||
end
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
defmodule WandererApp.MapRepo do
|
||||
use WandererApp, :repository
|
||||
|
||||
@default_map_options %{"layout" => "left_to_right", "store_custom_labels" => "false"}
|
||||
@default_map_options %{
|
||||
"layout" => "left_to_right",
|
||||
"store_custom_labels" => "false",
|
||||
"show_temp_system_name" => "false",
|
||||
"restrict_offline_showing" => "false"
|
||||
}
|
||||
|
||||
def get(map_id, relationships \\ []) do
|
||||
map_id
|
||||
|
||||
@@ -16,11 +16,13 @@ defmodule WandererApp.MapSystemRepo do
|
||||
end
|
||||
end
|
||||
|
||||
def get_all_by_map(map_id),
|
||||
do: WandererApp.Api.MapSystem.read_all_by_map(%{map_id: map_id})
|
||||
def get_all_by_map(map_id) do
|
||||
WandererApp.Api.MapSystem.read_all_by_map(%{map_id: map_id})
|
||||
end
|
||||
|
||||
def get_visible_by_map(map_id),
|
||||
do: WandererApp.Api.MapSystem.read_visible_by_map(%{map_id: map_id})
|
||||
def get_visible_by_map(map_id) do
|
||||
WandererApp.Api.MapSystem.read_visible_by_map(%{map_id: map_id})
|
||||
end
|
||||
|
||||
def remove_from_map(map_id, solar_system_id) do
|
||||
WandererApp.Api.MapSystem.read_by_map_and_solar_system!(%{
|
||||
@@ -59,6 +61,20 @@ defmodule WandererApp.MapSystemRepo do
|
||||
})
|
||||
end
|
||||
|
||||
def cleanup_temporary_name(system) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_temporary_name(%{
|
||||
temporary_name: nil
|
||||
})
|
||||
end
|
||||
|
||||
def cleanup_temporary_name!(system) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_temporary_name!(%{
|
||||
temporary_name: nil
|
||||
})
|
||||
end
|
||||
|
||||
def get_filtered_labels(labels, true) when is_binary(labels) do
|
||||
labels
|
||||
|> Jason.decode!()
|
||||
@@ -99,6 +115,11 @@ defmodule WandererApp.MapSystemRepo do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_tag(update)
|
||||
|
||||
def update_temporary_name(system, update) do
|
||||
system
|
||||
|> WandererApp.Api.MapSystem.update_temporary_name(update)
|
||||
end
|
||||
|
||||
def update_labels(system, update),
|
||||
do:
|
||||
system
|
||||
|
||||
47
lib/wanderer_app_web/components/character_activity.ex
Normal file
47
lib/wanderer_app_web/components/character_activity.ex
Normal file
@@ -0,0 +1,47 @@
|
||||
defmodule WandererAppWeb.CharacterActivity do
|
||||
use WandererAppWeb, :live_component
|
||||
use LiveViewEvents
|
||||
|
||||
@impl true
|
||||
def mount(socket) do
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(
|
||||
assigns,
|
||||
socket
|
||||
) do
|
||||
{:ok,
|
||||
socket
|
||||
|> handle_info_or_assign(assigns)}
|
||||
end
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div id={@id}>
|
||||
<.table class="!max-h-[80vh] !overflow-y-auto" id="activity-tbl" rows={@activity}>
|
||||
<:col :let={row} label="Character">
|
||||
<.character_item character={row.character} />
|
||||
</:col>
|
||||
<:col :let={row} label="Passages">
|
||||
<%= row.count %>
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def character_item(assigns) do
|
||||
~H"""
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="avatar">
|
||||
<div class="rounded-md w-12 h-12">
|
||||
<img src={member_icon_url(@character.eve_id)} alt={@character.name} />
|
||||
</div>
|
||||
</div>
|
||||
<%= @character.name %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -143,15 +143,10 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
def server_status(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
class="flex flex-col p-4 items-center absolute bottom-16 left-1 gap-2 tooltip tooltip-right"
|
||||
class="flex flex-col p-4 items-center absolute bottom-16 left-2 gap-2 tooltip tooltip-right"
|
||||
data-tip="server: Tranquility"
|
||||
>
|
||||
<div
|
||||
:if={@online}
|
||||
class="absolute block w-4 h-4 rounded-full shadow-inner bg-green-500 animate-ping"
|
||||
>
|
||||
</div>
|
||||
<div class={"block w-4 h-4 rounded-full shadow-inner #{if @online, do: " bg-green-500", else: "bg-red-500"}"}>
|
||||
<div class={"block w-2 h-2 rounded-full shadow-inner #{if @online, do: " bg-green-500", else: "bg-red-500"}"}>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
@@ -336,6 +331,7 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
"""
|
||||
attr(:id, :any, default: nil)
|
||||
attr(:class, :string, default: nil)
|
||||
attr(:wrapper_class, :string, default: nil)
|
||||
attr(:name, :any)
|
||||
attr(:label, :string, default: nil)
|
||||
attr(:prefix, :string, default: nil)
|
||||
@@ -381,21 +377,63 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
end)
|
||||
|
||||
~H"""
|
||||
<div phx-feedback-for={@name} class="form-control mt-8">
|
||||
<label class="label cursor-pointer gap-2">
|
||||
<span class="label-text"><%= @label %></span>
|
||||
<input type="hidden" name={@name} value="false" />
|
||||
<input
|
||||
type="checkbox"
|
||||
id={@id}
|
||||
name={@name}
|
||||
value="true"
|
||||
checked={@checked}
|
||||
class="checkbox"
|
||||
{@rest}
|
||||
/>
|
||||
<div phx-feedback-for={@name} class="form-control mt-2">
|
||||
<label class="inputContainer" for={@name}>
|
||||
<span><%= @label %></span>
|
||||
<div></div>
|
||||
<div class="smallInputSwitch">
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
class={[
|
||||
"checkboxRoot sizeM p-checkbox p-component",
|
||||
classes("p-highlight": @checked)
|
||||
]}
|
||||
data-p-highlight={@checked}
|
||||
data-p-disabled="false"
|
||||
data-pc-name="checkbox"
|
||||
data-pc-section="root"
|
||||
>
|
||||
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
|
||||
<input
|
||||
id={@id}
|
||||
name={@name}
|
||||
type="checkbox"
|
||||
class="p-checkbox-input"
|
||||
aria-invalid="false"
|
||||
data-pc-section="input"
|
||||
value="true"
|
||||
checked={@checked}
|
||||
{@rest}
|
||||
/>
|
||||
<div
|
||||
class="p-checkbox-box"
|
||||
data-p-highlight={@checked}
|
||||
data-p-disabled="false"
|
||||
data-pc-section="box"
|
||||
>
|
||||
<svg
|
||||
:if={@checked}
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="p-icon p-checkbox-icon"
|
||||
aria-hidden="true"
|
||||
data-pc-section="icon"
|
||||
>
|
||||
<path
|
||||
d="M4.86199 11.5948C4.78717 11.5923 4.71366 11.5745 4.64596 11.5426C4.57826 11.5107 4.51779 11.4652 4.46827 11.4091L0.753985 7.69483C0.683167 7.64891 0.623706 7.58751 0.580092 7.51525C0.536478 7.44299 0.509851 7.36177 0.502221 7.27771C0.49459 7.19366 0.506156 7.10897 0.536046 7.03004C0.565935 6.95111 0.613367 6.88 0.674759 6.82208C0.736151 6.76416 0.8099 6.72095 0.890436 6.69571C0.970973 6.67046 1.05619 6.66385 1.13966 6.67635C1.22313 6.68886 1.30266 6.72017 1.37226 6.76792C1.44186 6.81567 1.4997 6.8786 1.54141 6.95197L4.86199 10.2503L12.6397 2.49483C12.7444 2.42694 12.8689 2.39617 12.9932 2.40745C13.1174 2.41873 13.2343 2.47141 13.3251 2.55705C13.4159 2.64268 13.4753 2.75632 13.4938 2.87973C13.5123 3.00315 13.4888 3.1292 13.4271 3.23768L5.2557 11.4091C5.20618 11.4652 5.14571 11.5107 5.07801 11.5426C5.01031 11.5745 4.9368 11.5923 4.86199 11.5948Z"
|
||||
fill="currentColor"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<label for={@name} class="select-none ml-1.5"></label>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
@@ -403,24 +441,28 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
def input(%{type: "range"} = assigns) do
|
||||
~H"""
|
||||
<div phx-feedback-for={@name}>
|
||||
<label class="form-control w-full">
|
||||
<div class="form-control w-full">
|
||||
<.label for={@id}>
|
||||
<span class="label-text"><%= @label %></span>
|
||||
<span class="label-value"><%= @value %></span>
|
||||
<span><%= @label %></span>
|
||||
<div></div>
|
||||
<%= @value %>
|
||||
</.label>
|
||||
<input
|
||||
type="range"
|
||||
id={@id}
|
||||
name={@name}
|
||||
value={@value}
|
||||
class={[
|
||||
"p-component w-full",
|
||||
@class,
|
||||
@errors != [] && "border-rose-400 focus:border-rose-400"
|
||||
]}
|
||||
{@rest}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="range"
|
||||
id={@id}
|
||||
name={@name}
|
||||
value={@value}
|
||||
class={[
|
||||
"p-component w-full",
|
||||
@class,
|
||||
@errors != [] && "border-rose-400 focus:border-rose-400"
|
||||
]}
|
||||
{@rest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
"""
|
||||
@@ -428,13 +470,20 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
|
||||
def input(%{type: "select"} = assigns) do
|
||||
~H"""
|
||||
<div phx-feedback-for={@name}>
|
||||
<div
|
||||
phx-feedback-for={@name}
|
||||
class={[
|
||||
"inputContainer",
|
||||
@wrapper_class
|
||||
]}
|
||||
>
|
||||
<.label :if={@label} for={@id}><%= @label %></.label>
|
||||
<div :if={@label}></div>
|
||||
<select
|
||||
id={@id}
|
||||
name={@name}
|
||||
class={[
|
||||
"w-full",
|
||||
"p-component",
|
||||
@class
|
||||
]}
|
||||
multiple={@multiple}
|
||||
@@ -503,9 +552,9 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
|
||||
def label(assigns) do
|
||||
~H"""
|
||||
<div for={@for} class="label">
|
||||
<label for={@for} class="inputContainer">
|
||||
<%= render_slot(@inner_block) %>
|
||||
</div>
|
||||
</label>
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
<aside class="h-full w-14 left-0 absolute bg-gray-400 bg-opacity-5 text-gray-200 shadow-lg border-r border-gray-900 bg-opacity-70 bg-neutral-900">
|
||||
<aside class="h-full w-14 left-0 absolute bg-gray-400 bg-opacity-5 text-gray-200 shadow-lg border-r border-stone-800 bg-opacity-70 bg-neutral-900">
|
||||
<.sidebar_nav_links
|
||||
active_tab={@active_tab}
|
||||
show_admin={@show_admin}
|
||||
|
||||
@@ -32,14 +32,22 @@
|
||||
/>
|
||||
<script
|
||||
crossorigin="anonymous"
|
||||
src="https://unpkg.com/react@18/umd/react.production.min.js"
|
||||
integrity={integrity_hash("https://unpkg.com/react@18/umd/react.production.min.js")}
|
||||
src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"
|
||||
integrity={
|
||||
integrity_hash(
|
||||
"https://cdn.jsdelivr.net/npm/react-dom@16/umd/react-dom.development.js https://unpkg.com/react@18/umd/react.production.min.js"
|
||||
)
|
||||
}
|
||||
>
|
||||
</script>
|
||||
<script
|
||||
crossorigin="anonymous"
|
||||
src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
|
||||
integrity={integrity_hash("https://unpkg.com/react-dom@18/umd/react-dom.production.min.js")}
|
||||
src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"
|
||||
integrity={
|
||||
integrity_hash(
|
||||
"https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"
|
||||
)
|
||||
}
|
||||
>
|
||||
</script>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ defmodule WandererAppWeb.MapPicker do
|
||||
:if={maps}
|
||||
type="select"
|
||||
field={f[:map_slug]}
|
||||
class="select h-8 min-h-[0px] !pt-1 !pb-1 text-sm bg-neutral-900"
|
||||
class="select h-8 min-h-[10px] !pt-1 !pb-1 text-sm bg-neutral-900"
|
||||
placeholder="Select a map..."
|
||||
options={Enum.map(@maps.result, fn map -> {map.label, map.value} end)}
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,8 @@ defmodule WandererAppWeb.UserActivity do
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(assigns,
|
||||
def update(
|
||||
assigns,
|
||||
socket
|
||||
) do
|
||||
{:ok,
|
||||
@@ -116,7 +117,6 @@ defmodule WandererAppWeb.UserActivity do
|
||||
<h6 class="text-base leading-[150%] font-semibold dark:text-white">
|
||||
<%= _get_event_data(@event_type, Jason.decode!(@event_data) |> Map.drop(["character_id"])) %>
|
||||
</h6>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
@@ -125,7 +125,6 @@ defmodule WandererAppWeb.UserActivity do
|
||||
@impl true
|
||||
def handle_event("undo", %{"event-data" => event_data} = _params, socket) do
|
||||
# notify_to(socket.assigns.notify_to, socket.assigns.event_name, map_slug)
|
||||
IO.inspect(event_data)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
308
lib/wanderer_app_web/controllers/api_controller.ex
Normal file
308
lib/wanderer_app_web/controllers/api_controller.ex
Normal file
@@ -0,0 +1,308 @@
|
||||
defmodule WandererAppWeb.APIController do
|
||||
use WandererAppWeb, :controller
|
||||
|
||||
import Ash.Query, only: [filter: 2]
|
||||
|
||||
alias WandererApp.Api
|
||||
alias WandererApp.MapSystemRepo
|
||||
alias WandererApp.MapCharacterSettingsRepo
|
||||
alias WandererApp.Api.Character
|
||||
alias WandererApp.CachedInfo
|
||||
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Common
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
GET /api/system-static-info
|
||||
|
||||
Requires 'id' (the solar_system_id)
|
||||
|
||||
Example:
|
||||
GET /api/common/system_static?id=31002229
|
||||
GET /api/common/system_static?id=31002229
|
||||
"""
|
||||
def show_system_static(conn, params) do
|
||||
with {:ok, solar_system_str} <- require_param(params, "id"),
|
||||
{:ok, solar_system_id} <- parse_int(solar_system_str) do
|
||||
case CachedInfo.get_system_static_info(solar_system_id) do
|
||||
{:ok, system} ->
|
||||
data = static_system_to_json(system)
|
||||
json(conn, %{data: data})
|
||||
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "System not found"})
|
||||
end
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Map
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
GET /api/map/systems
|
||||
|
||||
Requires either `?map_id=<UUID>` **OR** `?slug=<map-slug>` in the query params.
|
||||
|
||||
If `?all=true` is provided, **all** systems are returned.
|
||||
Otherwise, only "visible" systems are returned.
|
||||
|
||||
Examples:
|
||||
GET /api/map/systems?map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||
GET /api/map/systems?slug=my-unique-wormhole-map
|
||||
GET /api/map/systems?map_id=<UUID>&all=true
|
||||
"""
|
||||
def list_systems(conn, params) do
|
||||
with {:ok, map_id} <- fetch_map_id(params) do
|
||||
# Decide which function to call based on the "all" param
|
||||
repo_fun =
|
||||
if params["all"] == "true" do
|
||||
&MapSystemRepo.get_all_by_map/1
|
||||
else
|
||||
&MapSystemRepo.get_visible_by_map/1
|
||||
end
|
||||
|
||||
case repo_fun.(map_id) do
|
||||
{:ok, systems} ->
|
||||
data = Enum.map(systems, &map_system_to_json/1)
|
||||
json(conn, %{data: data})
|
||||
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Could not fetch systems for map_id=#{map_id}: #{inspect(reason)}"})
|
||||
end
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
GET /api/map/system
|
||||
|
||||
Requires 'id' (the solar_system_id)
|
||||
plus either ?map_id=<UUID> or ?slug=<map-slug>.
|
||||
|
||||
Example:
|
||||
GET /api/map/system?id=31002229&map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||
GET /api/map/system?id=31002229&slug=my-unique-wormhole-map
|
||||
"""
|
||||
def show_system(conn, params) do
|
||||
with {:ok, solar_system_str} <- require_param(params, "id"),
|
||||
{:ok, solar_system_id} <- parse_int(solar_system_str),
|
||||
{:ok, map_id} <- fetch_map_id(params) do
|
||||
case MapSystemRepo.get_by_map_and_solar_system_id(map_id, solar_system_id) do
|
||||
{:ok, system} ->
|
||||
data = map_system_to_json(system)
|
||||
json(conn, %{data: data})
|
||||
|
||||
{:error, :not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "System not found in map=#{map_id}"})
|
||||
end
|
||||
else
|
||||
{:error, msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@doc """
|
||||
GET /api/map/tracked_characters_with_info
|
||||
|
||||
Example usage:
|
||||
GET /api/map/tracked_characters_with_info?map_id=<uuid>
|
||||
GET /api/map/tracked_characters_with_info?slug=<map-slug>
|
||||
|
||||
Returns a list of tracked records, plus their fully-loaded `character` data.
|
||||
"""
|
||||
def tracked_characters_with_info(conn, params) do
|
||||
with {:ok, map_id} <- fetch_map_id(params),
|
||||
{:ok, settings_list} <- get_tracked_by_map_ids(map_id),
|
||||
{:ok, char_list} <-
|
||||
read_characters_by_ids_wrapper(Enum.map(settings_list, & &1.character_id)) do
|
||||
|
||||
chars_by_id = Map.new(char_list, &{&1.id, &1})
|
||||
|
||||
data =
|
||||
Enum.map(settings_list, fn setting ->
|
||||
found_char = Map.get(chars_by_id, setting.character_id)
|
||||
|
||||
%{
|
||||
id: setting.id,
|
||||
map_id: setting.map_id,
|
||||
character_id: setting.character_id,
|
||||
tracked: setting.tracked,
|
||||
inserted_at: setting.inserted_at,
|
||||
updated_at: setting.updated_at,
|
||||
character:
|
||||
if found_char do
|
||||
character_to_json(found_char)
|
||||
else
|
||||
%{}
|
||||
end
|
||||
}
|
||||
end)
|
||||
|
||||
json(conn, %{data: data})
|
||||
else
|
||||
{:error, :get_tracked_error, reason} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "No tracked records found for map_id: #{inspect(reason)}"})
|
||||
|
||||
{:error, :read_characters_by_ids_error, reason} ->
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: "Could not load Character records: #{inspect(reason)}"})
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
defp get_tracked_by_map_ids(map_id) do
|
||||
case MapCharacterSettingsRepo.get_tracked_by_map_all(map_id) do
|
||||
{:ok, settings_list} ->
|
||||
{:ok, settings_list}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, :get_tracked_error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_characters_by_ids_wrapper(ids) do
|
||||
case read_characters_by_ids(ids) do
|
||||
{:ok, char_list} ->
|
||||
{:ok, char_list}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, :read_characters_by_ids_error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_map_id(%{"map_id" => mid}) when is_binary(mid) and mid != "" do
|
||||
{:ok, mid}
|
||||
end
|
||||
|
||||
defp fetch_map_id(%{"slug" => slug}) when is_binary(slug) and slug != "" do
|
||||
case WandererApp.Api.Map.get_map_by_slug(slug) do
|
||||
{:ok, map} ->
|
||||
{:ok, map.id}
|
||||
|
||||
{:error, _reason} ->
|
||||
{:error, "No map found for slug=#{slug}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_map_id(_), do: {:error, "Must provide either ?map_id=UUID or ?slug=SLUG"}
|
||||
|
||||
defp require_param(params, key) do
|
||||
case params[key] do
|
||||
nil -> {:error, "Missing required param: #{key}"}
|
||||
"" -> {:error, "Param #{key} cannot be empty"}
|
||||
val -> {:ok, val}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_int(str) do
|
||||
case Integer.parse(str) do
|
||||
{num, ""} -> {:ok, num}
|
||||
_ -> {:error, "Invalid integer for param id=#{str}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_characters_by_ids(ids) when is_list(ids) do
|
||||
if ids == [] do
|
||||
{:ok, []}
|
||||
else
|
||||
query =
|
||||
Character
|
||||
|> filter(id in ^ids)
|
||||
|
||||
Api.read(query)
|
||||
end
|
||||
end
|
||||
|
||||
defp map_system_to_json(system) do
|
||||
Map.take(system, [
|
||||
:id,
|
||||
:map_id,
|
||||
:solar_system_id,
|
||||
:name,
|
||||
:custom_name,
|
||||
:temporary_name,
|
||||
:description,
|
||||
:tag,
|
||||
:labels,
|
||||
:locked,
|
||||
:visible,
|
||||
:status,
|
||||
:position_x,
|
||||
:position_y,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
])
|
||||
end
|
||||
|
||||
defp character_to_json(ch) do
|
||||
Map.take(ch, [
|
||||
:id,
|
||||
:eve_id,
|
||||
:name,
|
||||
:corporation_id,
|
||||
:corporation_name,
|
||||
:corporation_ticker,
|
||||
:alliance_id,
|
||||
:alliance_name,
|
||||
:alliance_ticker,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
])
|
||||
end
|
||||
|
||||
|
||||
defp static_system_to_json(system) do
|
||||
system
|
||||
|> Map.take([
|
||||
:solar_system_id,
|
||||
:region_id,
|
||||
:constellation_id,
|
||||
:solar_system_name,
|
||||
:solar_system_name_lc,
|
||||
:constellation_name,
|
||||
:region_name,
|
||||
:system_class,
|
||||
:security,
|
||||
:type_description,
|
||||
:class_title,
|
||||
:is_shattered,
|
||||
:effect_name,
|
||||
:effect_power,
|
||||
:statics,
|
||||
:wandering,
|
||||
:triglavian_invasion_status,
|
||||
:sun_type_id
|
||||
])
|
||||
end
|
||||
|
||||
end
|
||||
@@ -93,13 +93,9 @@ defmodule WandererAppWeb.AuthController do
|
||||
|> redirect(to: ~p"/")
|
||||
end
|
||||
|
||||
def maybe_update_character_user_id(character, user_id) do
|
||||
case character.user_id do
|
||||
nil ->
|
||||
WandererApp.Api.Character.assign_user!(character, %{user_id: user_id})
|
||||
|
||||
_ ->
|
||||
Logger.debug("character already has user_id")
|
||||
end
|
||||
def maybe_update_character_user_id(character, user_id) when not is_nil(user_id) do
|
||||
WandererApp.Api.Character.assign_user!(character, %{user_id: user_id})
|
||||
end
|
||||
|
||||
def maybe_update_character_user_id(_character, _user_id), do: :ok
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user