Compare commits

...

31 Commits

Author SHA1 Message Date
CI
7a82b2c102 chore: release version v1.74.9
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-07-13 17:51:56 +00:00
Dmitry Popov
2db2a47186 Merge pull request #460 from wanderer-industries/fast-forward-bug
fix(Map): Trying to fix problem with fast forwarding after page are i…
2025-07-13 21:51:32 +04:00
DanSylvest
eabb0e8470 fix(Map): Trying to fix problem with fast forwarding after page are inactive some time. 2025-07-13 15:20:33 +03:00
CI
c65b8e5ebd chore: release version v1.74.8
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-07-11 08:19:12 +00:00
Dmitry Popov
bfed1480ae Merge pull request #453 from wanderer-industries/unified-settings
Unified settings
2025-07-11 12:18:41 +04:00
DanSylvest
5ff902f185 fix(Map): removed comments 2025-07-09 21:01:30 +03:00
DanSylvest
8d38345c7f fix(Map): Fixed conflict 2025-07-09 20:23:18 +03:00
DanSylvest
14be9dbb8a Merge branch 'main' into unified-settings
# Conflicts:
#	assets/js/hooks/Mapper/components/map/components/LocalCounter/LocalCounter.tsx
2025-07-09 19:55:29 +03:00
CI
720c26db94 chore: release version v1.74.7
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-07-09 15:43:43 +00:00
Dmitry Popov
6d0b8b845d Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-07-09 17:43:17 +02:00
Dmitry Popov
b2767e000e chore: release version v1.74.5 2025-07-09 17:43:14 +02:00
CI
169f23c2ca chore: release version v1.74.6
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-07-09 14:33:43 +00:00
Dmitry Popov
81f70eafff chore: release version v1.74.5 2025-07-09 16:33:04 +02:00
CI
8b6f600989 chore: release version v1.74.5 2025-07-09 08:19:58 +00:00
Dmitry Popov
fe3617b39f Merge pull request #454 from wanderer-industries/gate-connections
fix(Map): Add background for Pochven's systems. Changed from Region n…
2025-07-09 12:19:32 +04:00
DanSylvest
7fb8d24d73 fix(Map): Add background for Pochven's systems. Changed from Region name to constellation name for pochven systems. Changed connection style for gates (display like common connection). Changed behaviour of connections. 2025-07-08 13:17:03 +03:00
DanSylvest
f03448007d Merge branch 'main' into unified-settings 2025-07-07 17:22:03 +03:00
CI
c317a8bff9 chore: release version v1.74.4
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-07-07 13:59:56 +00:00
Dmitry Popov
618cca39a4 fix(Core): Fixed issue with update system positions 2025-07-07 15:59:23 +02:00
DanSylvest
fe7a98098f fix(Map): Unified settings. Second part: Import/Export 2025-07-07 16:57:06 +03:00
DanSylvest
df49939990 fix(Map): Unified settings. First part: add one place for storing settings 2025-07-06 18:59:40 +03:00
Dmitry Popov
f23f2776f4 chore: release version v1.72.1 2025-07-06 11:33:29 +02:00
CI
4419c86164 chore: release version v1.74.3
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-07-06 08:55:48 +00:00
Dmitry Popov
9848f49b49 fix(Core): Fixed issues with map subscription component 2025-07-06 10:55:21 +02:00
CI
4c23069a0a chore: release version v1.74.2
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-06-30 16:37:22 +00:00
Dmitry Popov
4a1d7be44c fix(Core): Fixed map loading for not existing maps 2025-06-30 18:36:55 +02:00
CI
26d0392da1 chore: release version v1.74.1
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-06-28 10:13:14 +00:00
Dmitry Popov
83b1406cce fix(Core): Mark connections between Pochven systems as known. 2025-06-28 12:12:43 +02:00
CI
fa83185cf5 chore: release version v1.74.0
Some checks failed
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled
2025-06-25 15:51:44 +00:00
Dmitry Popov
97d5010d41 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-06-25 17:51:13 +02:00
Dmitry Popov
e73ad93920 feat(Core): Reverted showing linked signature ID as part of temporary names 2025-06-25 17:51:10 +02:00
76 changed files with 1662 additions and 492 deletions

View File

@@ -2,6 +2,94 @@
<!-- changelog -->
## [v1.74.9](https://github.com/wanderer-industries/wanderer/compare/v1.74.8...v1.74.9) (2025-07-13)
### Bug Fixes:
* Map: Trying to fix problem with fast forwarding after page are inactive some time.
## [v1.74.8](https://github.com/wanderer-industries/wanderer/compare/v1.74.7...v1.74.8) (2025-07-11)
### Bug Fixes:
* Map: removed comments
* Map: Fixed conflict
* Map: Unified settings. Second part: Import/Export
* Map: Unified settings. First part: add one place for storing settings
## [v1.74.7](https://github.com/wanderer-industries/wanderer/compare/v1.74.6...v1.74.7) (2025-07-09)
## [v1.74.6](https://github.com/wanderer-industries/wanderer/compare/v1.74.5...v1.74.6) (2025-07-09)
## [v1.74.5](https://github.com/wanderer-industries/wanderer/compare/v1.74.4...v1.74.5) (2025-07-09)
### Bug Fixes:
* Map: Add background for Pochven's systems. Changed from Region name to constellation name for pochven systems. Changed connection style for gates (display like common connection). Changed behaviour of connections.
## [v1.74.4](https://github.com/wanderer-industries/wanderer/compare/v1.74.3...v1.74.4) (2025-07-07)
### Bug Fixes:
* Core: Fixed issue with update system positions
## [v1.74.3](https://github.com/wanderer-industries/wanderer/compare/v1.74.2...v1.74.3) (2025-07-06)
### Bug Fixes:
* Core: Fixed issues with map subscription component
## [v1.74.2](https://github.com/wanderer-industries/wanderer/compare/v1.74.1...v1.74.2) (2025-06-30)
### Bug Fixes:
* Core: Fixed map loading for not existing maps
## [v1.74.1](https://github.com/wanderer-industries/wanderer/compare/v1.74.0...v1.74.1) (2025-06-28)
### Bug Fixes:
* Core: Mark connections between Pochven systems as known.
## [v1.74.0](https://github.com/wanderer-industries/wanderer/compare/v1.73.0...v1.74.0) (2025-06-25)
### Features:
* Core: Reverted showing linked signature ID as part of temporary names
## [v1.73.0](https://github.com/wanderer-industries/wanderer/compare/v1.72.1...v1.73.0) (2025-06-25)

View File

@@ -212,3 +212,75 @@
.p-inputtext:enabled:hover {
border-color: #335c7e;
}
// --------------- TOAST
.p-toast .p-toast-message {
background-color: #1a1a1a;
color: #e0e0e0;
border-left: 4px solid transparent;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.p-toast .p-toast-message .p-toast-summary {
color: #ffffff;
font-weight: 600;
}
.p-toast .p-toast-message .p-toast-detail {
color: #c0c0c0;
font-size: 13px;
}
.p-toast .p-toast-icon-close {
color: #ffaa00;
transition: background 0.2s;
}
.p-toast .p-toast-icon-close:hover {
background: #333;
color: #fff;
}
.p-toast-message-success {
border-left-color: #f1c40f;
}
.p-toast-message-error {
border-left-color: #e74c3c;
}
.p-toast-message-info {
border-left-color: #3498db;
}
.p-toast-message-warn {
border-left-color: #e67e22;
}
.p-toast-message-success .p-toast-message-icon {
color: #f1c40f;
}
.p-toast-message-error .p-toast-message-icon {
color: #e74c3c;
}
.p-toast-message-info .p-toast-message-icon {
color: #3498db;
}
.p-toast-message-warn .p-toast-message-icon {
color: #e67e22;
}
.p-toast-message-success .p-toast-message-content {
border-left-color: #f1c40f;
}
.p-toast-message-error .p-toast-message-content {
border-left-color: #e74c3c;
}
.p-toast-message-info .p-toast-message-content {
border-left-color: #3498db;
}
.p-toast-message-warn .p-toast-message-content {
border-left-color: #e67e22;
}

View File

@@ -64,9 +64,9 @@ body .p-dialog {
}
.p-dialog-footer {
padding: 1rem;
border-top: 1px solid #ddd;
background: #f4f4f4;
padding: .75rem 1rem;
border-top: none !important;
//background: #f4f4f4;
}
.p-dialog-header-close {

View File

@@ -0,0 +1 @@
export * from './parseMapUserSettings.ts';

View File

@@ -0,0 +1,67 @@
import { MapUserSettings, SettingsWithVersion } from '@/hooks/Mapper/mapRootProvider/types.ts';
const REQUIRED_KEYS = [
'widgets',
'interface',
'onTheMap',
'routes',
'localWidget',
'signaturesWidget',
'killsWidget',
] as const;
type RequiredKeys = (typeof REQUIRED_KEYS)[number];
/** Custom error for any parsing / validation issue */
export class MapUserSettingsParseError extends Error {
constructor(msg: string) {
super(`MapUserSettings parse error: ${msg}`);
}
}
const isNumber = (v: unknown): v is number => typeof v === 'number' && !Number.isNaN(v);
/** Minimal check that an object matches SettingsWithVersion<*> */
const isSettingsWithVersion = (v: unknown): v is SettingsWithVersion<unknown> =>
typeof v === 'object' && v !== null && isNumber((v as any).version) && 'settings' in (v as any);
/** Ensure every required key is present */
const hasAllRequiredKeys = (v: unknown): v is Record<RequiredKeys, unknown> =>
typeof v === 'object' && v !== null && REQUIRED_KEYS.every(k => k in v);
/* ------------------------------ Main parser ------------------------------- */
/**
* Parses and validates a JSON string as `MapUserSettings`.
*
* @throws `MapUserSettingsParseError` если строка не JSON или нарушена структура
*/
export const parseMapUserSettings = (json: unknown): MapUserSettings => {
if (typeof json !== 'string') throw new MapUserSettingsParseError('Input must be a JSON string');
let data: unknown;
try {
data = JSON.parse(json);
} catch (e) {
throw new MapUserSettingsParseError(`Invalid JSON: ${(e as Error).message}`);
}
if (!hasAllRequiredKeys(data)) {
const missing = REQUIRED_KEYS.filter(k => !(k in (data as any)));
throw new MapUserSettingsParseError(`Missing top-level field(s): ${missing.join(', ')}`);
}
for (const key of REQUIRED_KEYS) {
if (!isSettingsWithVersion((data as any)[key])) {
throw new MapUserSettingsParseError(`"${key}" must match SettingsWithVersion<T>`);
}
}
// Everything passes, so cast is safe
return data as MapUserSettings;
};
/* ------------------------------ Usage example ----------------------------- */
// const raw = fetchFromServer(); // string
// const settings = parseMapUserSettings(raw);

View File

@@ -98,6 +98,7 @@ interface MapCompProps {
theme?: string;
pings: PingData[];
minimapPlacement?: PanelPosition;
localShowShipName?: boolean;
}
const MapComp = ({
@@ -117,6 +118,7 @@ const MapComp = ({
onAddSystem,
pings,
minimapPlacement = 'bottom-right',
localShowShipName = false,
}: MapCompProps) => {
const { getNodes } = useReactFlow();
const [nodes, , onNodesChange] = useNodesState<Node<SolarSystemRawType>>(initialNodes);
@@ -212,8 +214,9 @@ const MapComp = ({
showKSpaceBG: showKSpaceBG,
isThickConnections: isThickConnections,
pings,
localShowShipName,
}));
}, [showKSpaceBG, isThickConnections, pings, update]);
}, [showKSpaceBG, isThickConnections, pings, update, localShowShipName]);
return (
<>

View File

@@ -10,6 +10,7 @@ export type MapData = MapUnionTypes & {
showKSpaceBG: boolean;
isThickConnections: boolean;
linkedSigEveId: string;
localShowShipName: boolean;
};
interface MapProviderProps {
@@ -42,6 +43,7 @@ const INITIAL_DATA: MapData = {
followingCharacterEveId: null,
userHubs: [],
pings: [],
localShowShipName: false,
};
export interface MapContextProps {

View File

@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useKillsCounter } from '../../hooks/useKillsCounter';
import { useKillsCounter } from '../../hooks/useKillsCounter.ts';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { WithChildren, WithClassName } from '@/hooks/Mapper/types/common';
import { WithChildren, WithClassName } from '@/hooks/Mapper/types/common.ts';
import {
KILLS_ROW_HEIGHT,
SystemKillsList,

View File

@@ -0,0 +1 @@
export * from './KillsCounter.tsx';

View File

@@ -3,11 +3,11 @@ import clsx from 'clsx';
import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper';
import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit/WdTooltip';
import { CharItemProps, LocalCharactersList } from '../../../mapInterface/widgets/LocalCharacters/components';
import { useLocalCharactersItemTemplate } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from '../../../mapInterface/widgets/LocalCharacters/hooks/useLocalWidgetSettings';
import classes from './SolarSystemLocalCounter.module.scss';
import { useTheme } from '@/hooks/Mapper/hooks/useTheme.ts';
import { AvailableThemes } from '@/hooks/Mapper/mapRootProvider/types.ts';
import classes from './LocalCounter.module.scss';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx';
import { useLocalCharactersItemTemplate } from '@/hooks/Mapper/components/mapInterface/widgets/LocalCharacters/hooks/useLocalCharacters.tsx';
interface LocalCounterProps {
localCounterCharacters: Array<CharItemProps>;
@@ -16,8 +16,10 @@ interface LocalCounterProps {
}
export const LocalCounter = ({ localCounterCharacters, hasUserCharacters, showIcon = true }: LocalCounterProps) => {
const [settings] = useLocalCharacterWidgetSettings();
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
const {
data: { localShowShipName },
} = useMapState();
const itemTemplate = useLocalCharactersItemTemplate(localShowShipName);
const theme = useTheme();
const pilotTooltipContent = useMemo(() => {

View File

@@ -0,0 +1 @@
export * from './LocalCounter';

View File

@@ -1,7 +1,7 @@
import { useCallback, useMemo, useState } from 'react';
import classes from './SolarSystemEdge.module.scss';
import { EdgeLabelRenderer, EdgeProps, getBezierPath, getSmoothStepPath, Position, useStore } from 'reactflow';
import { EdgeLabelRenderer, EdgeProps, getBezierPath, Position, useStore } from 'reactflow';
import { getEdgeParams } from '@/hooks/Mapper/components/map/utils.ts';
import clsx from 'clsx';
import { ConnectionType, MassState, ShipSizeStatus, SolarSystemConnection, TimeStatus } from '@/hooks/Mapper/types';
@@ -51,11 +51,11 @@ export const SolarSystemEdge = ({ id, source, target, markerEnd, style, data }:
const [hovered, setHovered] = useState(false);
const [path, labelX, labelY, sx, sy, tx, ty, sourcePos, targetPos] = useMemo(() => {
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode!, targetNode!);
const offset = isThickConnections ? MAP_OFFSETS_TICK[targetPos] : MAP_OFFSETS[targetPos];
const method = isWormhole ? getBezierPath : getSmoothStepPath;
const method = isWormhole ? getBezierPath : getBezierPath;
const [edgePath, labelX, labelY] = method({
sourceX: sx - offset.x,

View File

@@ -40,6 +40,7 @@ $neon-color-3: rgba(27, 132, 236, 0.40);
z-index: 3;
overflow: hidden;
&.Pochven,
&.Mataria,
&.Amarria,
&.Gallente,
@@ -95,6 +96,15 @@ $neon-color-3: rgba(27, 132, 236, 0.40);
}
}
&.Pochven {
&::after {
opacity: 0.8;
background-image: url('/images/pochven.webp');
background-position-x: 0;
background-position-y: -13px;
}
}
&.selected {
border-color: $pastel-pink;
box-shadow: 0 0 10px #9a1af1c2;

View File

@@ -12,11 +12,11 @@ import {
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { Tag } from 'primereact/tag';
import { LocalCounter } from '@/hooks/Mapper/components/map/components/LocalCounter';
import { KillsCounter } from '@/hooks/Mapper/components/map/components/KillsCounter';
// let render = 0;
export const SolarSystemNodeDefault = memo((props: NodeProps<MapSolarSystemType>) => {

View File

@@ -12,10 +12,10 @@ import {
} from '@/hooks/Mapper/components/map/constants';
import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp';
import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature';
import { LocalCounter } from './SolarSystemLocalCounter';
import { KillsCounter } from './SolarSystemKillsCounter';
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import { TooltipSize } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper/utils.ts';
import { LocalCounter } from '@/hooks/Mapper/components/map/components/LocalCounter';
import { KillsCounter } from '@/hooks/Mapper/components/map/components/KillsCounter';
// let render = 0;
export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>) => {

View File

@@ -1,19 +1,19 @@
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
import { REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
import { useMemo } from 'react';
import { MapSolarSystemType } from '../map.types';
import { NodeProps } from 'reactflow';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useMapGetOption } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
import { useMapState } from '@/hooks/Mapper/components/map/MapProvider';
import { useDoubleClick } from '@/hooks/Mapper/hooks/useDoubleClick';
import { Regions, REGIONS_MAP, Spaces } from '@/hooks/Mapper/constants';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers';
import { sortWHClasses } from '@/hooks/Mapper/helpers';
import { CharacterTypeRaw, OutCommand, PingType, SystemSignature } from '@/hooks/Mapper/types';
import { useMemo } from 'react';
import { NodeProps } from 'reactflow';
import { MapSolarSystemType } from '../map.types';
import { LabelInfo, useLabelsInfo } from './useLabelsInfo';
import { useSystemName } from './useSystemName';
import { useUnsplashedSignatures } from './useUnsplashedSignatures';
import { useSystemName } from './useSystemName';
import { LabelInfo, useLabelsInfo } from './useLabelsInfo';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
function getActivityType(count: number): string {
if (count <= 5) return 'activityNormal';
@@ -65,6 +65,7 @@ const SpaceToClass: Record<string, string> = {
[Spaces.Matar]: 'Mataria',
[Spaces.Amarr]: 'Amarria',
[Spaces.Gallente]: 'Gallente',
[Spaces.Pochven]: 'Pochven',
};
export function useLocalCounter(nodeVars: SolarSystemNodeVars) {
@@ -112,11 +113,13 @@ export const useSolarSystemNode = (props: NodeProps<MapSolarSystemType>): SolarS
region_id,
is_shattered,
solar_system_name,
constellation_name,
} = systemStaticInfo;
const { isShowUnsplashedSignatures } = interfaceSettings;
const isTempSystemNameEnabled = useMapGetOption('show_temp_system_name') === 'true';
const isShowLinkedSigId = useMapGetOption('show_linked_signature_id') === 'true';
const isShowLinkedSigIdTempName = useMapGetOption('show_linked_signature_id_temp_name') === 'true';
const {
data: {
@@ -183,6 +186,8 @@ export const useSolarSystemNode = (props: NodeProps<MapSolarSystemType>): SolarS
const { systemName, computedTemporaryName, customName } = useSystemName({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
});
@@ -192,10 +197,18 @@ export const useSolarSystemNode = (props: NodeProps<MapSolarSystemType>): SolarS
const hubsAsStrings = useMemo(() => hubs.map(item => item.toString()), [hubs]);
const isRally = useMemo(
() => pings.find(x => x.solar_system_id === solar_system_id && x.type === PingType.Rally),
() => !!pings.find(x => x.solar_system_id === solar_system_id && x.type === PingType.Rally),
[pings, solar_system_id],
);
const regionName = useMemo(() => {
if (region_id === Regions.Pochven) {
return constellation_name;
}
return region_name;
}, [constellation_name, region_id, region_name]);
const nodeVars: SolarSystemNodeVars = {
id,
selected,
@@ -230,7 +243,7 @@ export const useSolarSystemNode = (props: NodeProps<MapSolarSystemType>): SolarS
isThickConnections,
classTitle: class_title,
temporaryName: computedTemporaryName,
regionName: region_name,
regionName,
solarSystemName: solar_system_name,
isRally,
};

View File

@@ -4,6 +4,8 @@ import { useMemo } from 'react';
interface UseSystemNameParams {
isTempSystemNameEnabled: boolean;
temporary_name?: string | null;
isShowLinkedSigIdTempName: boolean;
linkedSigPrefix: string | null;
name?: string | null;
systemStaticInfo: SolarSystemStaticInfoRaw;
}
@@ -11,6 +13,8 @@ interface UseSystemNameParams {
export const useSystemName = ({
isTempSystemNameEnabled,
temporary_name,
isShowLinkedSigIdTempName,
linkedSigPrefix,
name,
systemStaticInfo,
}: UseSystemNameParams) => {
@@ -21,8 +25,12 @@ export const useSystemName = ({
return '';
}
if (isShowLinkedSigIdTempName && linkedSigPrefix) {
return temporary_name ? `${linkedSigPrefix}:${temporary_name}` : `${linkedSigPrefix}:${solar_system_name}`;
}
return temporary_name ?? '';
}, [isTempSystemNameEnabled, temporary_name]);
}, [isTempSystemNameEnabled, temporary_name, solar_system_name, isShowLinkedSigIdTempName, linkedSigPrefix]);
const systemName = useMemo(() => {
if (isTempSystemNameEnabled && computedTemporaryName) {

View File

@@ -1,37 +1,48 @@
import { Position, internalsSymbol } from 'reactflow';
import { Position, internalsSymbol, Node } from 'reactflow';
// returns the position (top,right,bottom or right) passed node compared to
function getParams(nodeA, nodeB) {
type Coords = [number, number];
type CoordsWithPosition = [number, number, Position];
function segmentsIntersect(a1: number, a2: number, b1: number, b2: number): boolean {
const [minA, maxA] = a1 < a2 ? [a1, a2] : [a2, a1];
const [minB, maxB] = b1 < b2 ? [b1, b2] : [b2, b1];
return maxA >= minB && maxB >= minA;
}
function getParams(nodeA: Node, nodeB: Node): CoordsWithPosition {
const centerA = getNodeCenter(nodeA);
const centerB = getNodeCenter(nodeB);
const horizontalDiff = Math.abs(centerA.x - centerB.x);
const verticalDiff = Math.abs(centerA.y - centerB.y);
let position: Position;
// when the horizontal difference between the nodes is bigger, we use Position.Left or Position.Right for the handle
if (horizontalDiff > verticalDiff) {
position = centerA.x > centerB.x ? Position.Left : Position.Right;
} else {
// here the vertical difference between the nodes is bigger, so we use Position.Top or Position.Bottom for the handle
if (
segmentsIntersect(
nodeA.positionAbsolute!.x - 10,
nodeA.positionAbsolute!.x - 10 + nodeA.width! + 20,
nodeB.positionAbsolute!.x,
nodeB.positionAbsolute!.x + nodeB.width!,
)
) {
position = centerA.y > centerB.y ? Position.Top : Position.Bottom;
} else {
position = centerA.x > centerB.x ? Position.Left : Position.Right;
}
const [x, y] = getHandleCoordsByPosition(nodeA, position);
return [x, y, position];
}
function getHandleCoordsByPosition(node, handlePosition) {
// all handles are from type source, that's why we use handleBounds.source here
const handle = node[internalsSymbol].handleBounds.source.find(h => h.position === handlePosition);
function getHandleCoordsByPosition(node: Node, handlePosition: Position): Coords {
const handle = node[internalsSymbol]!.handleBounds!.source!.find(h => h.position === handlePosition);
if (!handle) {
throw new Error(`Handle with position ${handlePosition} not found on node ${node.id}`);
}
let offsetX = handle.width / 2;
let offsetY = handle.height / 2;
// this is a tiny detail to make the markerEnd of an edge visible.
// The handle position that gets calculated has the origin top-left, so depending which side we are using, we add a little offset
// when the handlePosition is Position.Right for example, we need to add an offset as big as the handle itself in order to get the correct position
switch (handlePosition) {
case Position.Left:
offsetX = 0;
@@ -47,21 +58,20 @@ function getHandleCoordsByPosition(node, handlePosition) {
break;
}
const x = node.positionAbsolute.x + handle.x + offsetX;
const y = node.positionAbsolute.y + handle.y + offsetY;
const x = node.positionAbsolute!.x + handle.x + offsetX;
const y = node.positionAbsolute!.y + handle.y + offsetY;
return [x, y];
}
function getNodeCenter(node) {
function getNodeCenter(node: Node): { x: number; y: number } {
return {
x: node.positionAbsolute.x + node.width / 2,
y: node.positionAbsolute.y + node.height / 2,
x: node.positionAbsolute!.x + node.width! / 2,
y: node.positionAbsolute!.y + node.height! / 2,
};
}
// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
export function getEdgeParams(source, target) {
export function getEdgeParams(source: Node, target: Node) {
const [sx, sy, sourcePos] = getParams(source, target);
const [tx, ty, targetPos] = getParams(target, source);

View File

@@ -7,10 +7,6 @@ import {
SOLAR_SYSTEM_CLASSES_TO_CLASS_GROUPS,
WORMHOLES_ADDITIONAL_INFO_BY_SHORT_NAME,
} from '@/hooks/Mapper/components/map/constants.ts';
import {
SETTINGS_KEYS,
SignatureSettingsType,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { SystemSignaturesContent } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent';
import { K162_TYPES_MAP } from '@/hooks/Mapper/constants.ts';
import { getWhSize } from '@/hooks/Mapper/helpers/getWhSize';
@@ -18,6 +14,7 @@ import { parseSignatureCustomInfo } from '@/hooks/Mapper/helpers/parseSignatureC
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { CommandLinkSignatureToSystem, SignatureGroup, SystemSignature, TimeStatus } from '@/hooks/Mapper/types';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts';
import { SETTINGS_KEYS, SignatureSettingsType } from '@/hooks/Mapper/constants/signatures';
const K162_SIGNATURE_TYPE = WORMHOLES_ADDITIONAL_INFO_BY_SHORT_NAME['K162'].shortName;

View File

@@ -1,16 +1,16 @@
import { TooltipPosition, WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { InputText } from 'primereact/inputtext';
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 { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
import { OutCommand } from '@/hooks/Mapper/types';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
import { IconField } from 'primereact/iconfield';
import { InputText } from 'primereact/inputtext';
import { InputTextarea } from 'primereact/inputtextarea';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button } from 'primereact/button';
import { OutCommand } from '@/hooks/Mapper/types';
import { IconField } from 'primereact/iconfield';
import { TooltipPosition, WdImageSize, WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts';
import { getSystemStaticInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic';
interface SystemSettingsDialog {
systemId: string;

View File

@@ -6,7 +6,6 @@ import { useMapCheckPermissions, useMapGetOption } from '@/hooks/Mapper/mapRootP
import { UserPermission } from '@/hooks/Mapper/types/permissions';
import { LocalCharactersList } from './components/LocalCharactersList';
import { useLocalCharactersItemTemplate } from './hooks/useLocalCharacters';
import { useLocalCharacterWidgetSettings } from './hooks/useLocalWidgetSettings';
import { LocalCharactersHeader } from './components/LocalCharactersHeader';
import classes from './LocalCharacters.module.scss';
import clsx from 'clsx';
@@ -14,9 +13,9 @@ import clsx from 'clsx';
export const LocalCharacters = () => {
const {
data: { characters, userCharacters, selectedSystems },
storedSettings: { settingsLocal, settingsLocalUpdate },
} = useMapRootState();
const [settings, setSettings] = useLocalCharacterWidgetSettings();
const [systemId] = selectedSystems;
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
@@ -31,12 +30,12 @@ export const LocalCharacters = () => {
.map(x => ({
...x,
isOwn: userCharacters.includes(x.eve_id),
compact: settings.compact,
showShipName: settings.showShipName,
compact: settingsLocal.compact,
showShipName: settingsLocal.showShipName,
}))
.sort(sortCharacters);
if (!showOffline || !settings.showOffline) {
if (!showOffline || !settingsLocal.showOffline) {
return filtered.filter(c => c.online);
}
return filtered;
@@ -44,9 +43,9 @@ export const LocalCharacters = () => {
characters,
systemId,
userCharacters,
settings.compact,
settings.showOffline,
settings.showShipName,
settingsLocal.compact,
settingsLocal.showOffline,
settingsLocal.showShipName,
showOffline,
]);
@@ -54,7 +53,7 @@ export const LocalCharacters = () => {
const isNotSelectedSystem = selectedSystems.length !== 1;
const showList = sorted.length > 0 && selectedSystems.length === 1;
const itemTemplate = useLocalCharactersItemTemplate(settings.showShipName);
const itemTemplate = useLocalCharactersItemTemplate(settingsLocal.showShipName);
return (
<Widget
@@ -63,8 +62,8 @@ export const LocalCharacters = () => {
sortedCount={sorted.length}
showList={showList}
showOffline={showOffline}
settings={settings}
setSettings={setSettings}
settings={settingsLocal}
setSettings={settingsLocalUpdate}
/>
}
>
@@ -81,7 +80,7 @@ export const LocalCharacters = () => {
{showList && (
<LocalCharactersList
items={sorted}
itemSize={settings.compact ? 26 : 41}
itemSize={settingsLocal.compact ? 26 : 41}
itemTemplate={itemTemplate}
containerClassName={clsx(
'w-full h-full overflow-x-hidden overflow-y-auto custom-scrollbar select-none',

View File

@@ -1,21 +0,0 @@
import useLocalStorageState from 'use-local-storage-state';
export interface LocalCharacterWidgetSettings {
compact: boolean;
showOffline: boolean;
version: number;
showShipName: boolean;
}
export const LOCAL_CHARACTER_WIDGET_DEFAULT: LocalCharacterWidgetSettings = {
compact: true,
showOffline: false,
version: 0,
showShipName: false,
};
export function useLocalCharacterWidgetSettings() {
return useLocalStorageState<LocalCharacterWidgetSettings>('kills:widget:settings', {
defaultValue: LOCAL_CHARACTER_WIDGET_DEFAULT,
});
}

View File

@@ -8,8 +8,8 @@ import {
Setting,
SettingsTypes,
SIGNATURE_SETTINGS,
SignatureSettingsType,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { SignatureSettingsType } from '@/hooks/Mapper/constants/signatures.ts';
interface SystemSignatureSettingsDialogProps {
settings: SignatureSettingsType;

View File

@@ -1,21 +1,14 @@
import { useCallback, useState, useEffect, useRef, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { SystemSignaturesContent } from './SystemSignaturesContent';
import { SystemSignatureSettingsDialog } from './SystemSignatureSettingsDialog';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { SystemSignaturesHeader } from './SystemSignatureHeader';
import useLocalStorageState from 'use-local-storage-state';
import { useHotkey } from '@/hooks/Mapper/hooks/useHotkey';
import {
SETTINGS_KEYS,
SETTINGS_VALUES,
SIGNATURE_SETTING_STORE_KEY,
SIGNATURE_WINDOW_ID,
SignatureSettingsType,
getDeletionTimeoutMs,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { getDeletionTimeoutMs } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types/mapHandlers';
import { ExtendedSystemSignature } from '@/hooks/Mapper/types';
import { SETTINGS_KEYS, SIGNATURE_WINDOW_ID, SignatureSettingsType } from '@/hooks/Mapper/constants/signatures';
/**
* Custom hook for managing pending signature deletions and undo countdown.
@@ -126,20 +119,14 @@ export const SystemSignatures = () => {
const {
data: { selectedSystems },
outCommand,
storedSettings: { settingsSignatures, settingsSignaturesUpdate },
} = useMapRootState();
const [currentSettings, setCurrentSettings] = useLocalStorageState<SignatureSettingsType>(
SIGNATURE_SETTING_STORE_KEY,
{
defaultValue: SETTINGS_VALUES,
},
);
const [systemId] = selectedSystems;
const isSystemSelected = useMemo(() => selectedSystems.length === 1, [selectedSystems.length]);
const { pendingIds, countdown, deletedSignatures, addDeleted, handleUndo } = useSignatureUndo(
systemId,
currentSettings,
settingsSignatures,
outCommand,
);
@@ -157,20 +144,20 @@ export const SystemSignatures = () => {
const handleSettingsSave = useCallback(
(newSettings: SignatureSettingsType) => {
setCurrentSettings(newSettings);
settingsSignaturesUpdate(newSettings);
setVisible(false);
},
[setCurrentSettings],
[settingsSignaturesUpdate],
);
const handleLazyDeleteToggle = useCallback(
(value: boolean) => {
setCurrentSettings(prev => ({
settingsSignaturesUpdate(prev => ({
...prev,
[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES]: value,
}));
},
[setCurrentSettings],
[settingsSignaturesUpdate],
);
const openSettings = useCallback(() => setVisible(true), []);
@@ -180,7 +167,7 @@ export const SystemSignatures = () => {
label={
<SystemSignaturesHeader
sigCount={sigCount}
lazyDeleteValue={currentSettings[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES] as boolean}
lazyDeleteValue={settingsSignatures[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES] as boolean}
pendingCount={pendingIds.size}
undoCountdown={countdown}
onLazyDeleteChange={handleLazyDeleteToggle}
@@ -197,7 +184,7 @@ export const SystemSignatures = () => {
) : (
<SystemSignaturesContent
systemId={systemId}
settings={currentSettings}
settings={settingsSignatures}
deletedSignatures={deletedSignatures}
onLazyDeleteChange={handleLazyDeleteToggle}
onCountChange={handleCountChange}
@@ -207,7 +194,7 @@ export const SystemSignatures = () => {
{visible && (
<SystemSignatureSettingsDialog
settings={currentSettings}
settings={settingsSignatures}
onCancel={() => setVisible(false)}
onSave={handleSettingsSave}
/>

View File

@@ -8,7 +8,6 @@ import {
SortOrder,
} from 'primereact/datatable';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import useLocalStorageState from 'use-local-storage-state';
import { SignatureView } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SignatureView';
import {
@@ -17,9 +16,6 @@ import {
GROUPS_LIST,
MEDIUM_MAX_WIDTH,
OTHER_COLUMNS_WIDTH,
SETTINGS_KEYS,
SIGNATURE_WINDOW_ID,
SignatureSettingsType,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants';
import { SignatureSettings } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings';
import { TooltipPosition, WdTooltip, WdTooltipHandlers, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
@@ -36,19 +32,11 @@ import { useClipboard, useHotkey } from '@/hooks/Mapper/hooks';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth';
import { getSignatureRowClass } from '../helpers/rowStyles';
import { useSystemSignaturesData } from '../hooks/useSystemSignaturesData';
import { SETTINGS_KEYS, SIGNATURE_WINDOW_ID, SignatureSettingsType } from '@/hooks/Mapper/constants/signatures.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
const renderColIcon = (sig: SystemSignature) => renderIcon(sig);
type SystemSignaturesSortSettings = {
sortField: string;
sortOrder: SortOrder;
};
const SORT_DEFAULT_VALUES: SystemSignaturesSortSettings = {
sortField: 'inserted_at',
sortOrder: -1,
};
interface SystemSignaturesContentProps {
systemId: string;
settings: SignatureSettingsType;
@@ -79,6 +67,10 @@ export const SystemSignaturesContent = ({
const [nameColumnWidth, setNameColumnWidth] = useState('auto');
const [hoveredSignature, setHoveredSignature] = useState<SystemSignature | null>(null);
const {
storedSettings: { settingsSignatures, settingsSignaturesUpdate },
} = useMapRootState();
const tableRef = useRef<HTMLDivElement>(null);
const tooltipRef = useRef<WdTooltipHandlers>(null);
@@ -87,11 +79,6 @@ export const SystemSignaturesContent = ({
const { clipboardContent, setClipboardContent } = useClipboard();
const [sortSettings, setSortSettings] = useLocalStorageState<{ sortField: string; sortOrder: SortOrder }>(
'window:signatures:sort',
{ defaultValue: SORT_DEFAULT_VALUES },
);
const {
signatures,
selectedSignatures,
@@ -246,8 +233,8 @@ export const SystemSignaturesContent = ({
tooltipRef.current?.hide();
}, []);
const refVars = useRef({ settings, selectedSignatures, setSortSettings });
refVars.current = { settings, selectedSignatures, setSortSettings };
const refVars = useRef({ settings, selectedSignatures, settingsSignatures, settingsSignaturesUpdate });
refVars.current = { settings, selectedSignatures, settingsSignatures, settingsSignaturesUpdate };
// @ts-ignore
const getRowClassName = useCallback(rowData => {
@@ -263,7 +250,12 @@ export const SystemSignaturesContent = ({
}, []);
const handleSortSettings = useCallback(
(e: DataTableStateEvent) => refVars.current.setSortSettings({ sortField: e.sortField, sortOrder: e.sortOrder }),
(e: DataTableStateEvent) =>
refVars.current.settingsSignaturesUpdate({
...refVars.current.settingsSignatures,
[SETTINGS_KEYS.SORT_FIELD]: e.sortField,
[SETTINGS_KEYS.SORT_ORDER]: e.sortOrder,
}),
[],
);
@@ -295,8 +287,8 @@ export const SystemSignaturesContent = ({
rowHover
selectAll
onRowDoubleClick={handleRowClick}
sortField={sortSettings.sortField}
sortOrder={sortSettings.sortOrder}
sortField={settingsSignatures[SETTINGS_KEYS.SORT_FIELD] as string}
sortOrder={settingsSignatures[SETTINGS_KEYS.SORT_ORDER] as SortOrder}
onSort={handleSortSettings}
onRowMouseEnter={onRowMouseEnter}
onRowMouseLeave={onRowMouseLeave}

View File

@@ -11,6 +11,7 @@ import {
SignatureKindFR,
SignatureKindRU,
} from '@/hooks/Mapper/types';
import { SETTINGS_KEYS, SIGNATURES_DELETION_TIMING, SignatureSettingsType } from '@/hooks/Mapper/constants/signatures';
export const TIME_ONE_MINUTE = 1000 * 60;
export const TIME_TEN_MINUTES = TIME_ONE_MINUTE * 10;
@@ -96,44 +97,11 @@ export const getGroupIdByRawGroup = (val: string): SignatureGroup | undefined =>
return MAPPING_GROUP_TO_ENG[val] || undefined;
};
export const SIGNATURE_WINDOW_ID = 'system_signatures_window';
export const SIGNATURE_SETTING_STORE_KEY = 'wanderer_system_signature_settings_v6_5';
export enum SETTINGS_KEYS {
SHOW_DESCRIPTION_COLUMN = 'show_description_column',
SHOW_UPDATED_COLUMN = 'show_updated_column',
SHOW_CHARACTER_COLUMN = 'show_character_column',
LAZY_DELETE_SIGNATURES = 'lazy_delete_signatures',
KEEP_LAZY_DELETE = 'keep_lazy_delete_enabled',
DELETION_TIMING = 'deletion_timing',
COLOR_BY_TYPE = 'color_by_type',
SHOW_CHARACTER_PORTRAIT = 'show_character_portrait',
// From SignatureKind
COSMIC_ANOMALY = SignatureKind.CosmicAnomaly,
COSMIC_SIGNATURE = SignatureKind.CosmicSignature,
DEPLOYABLE = SignatureKind.Deployable,
STRUCTURE = SignatureKind.Structure,
STARBASE = SignatureKind.Starbase,
SHIP = SignatureKind.Ship,
DRONE = SignatureKind.Drone,
// From SignatureGroup
WORMHOLE = SignatureGroup.Wormhole,
RELIC_SITE = SignatureGroup.RelicSite,
DATA_SITE = SignatureGroup.DataSite,
ORE_SITE = SignatureGroup.OreSite,
GAS_SITE = SignatureGroup.GasSite,
COMBAT_SITE = SignatureGroup.CombatSite,
}
export enum SettingsTypes {
flag,
dropdown,
}
export type SignatureSettingsType = { [key in SETTINGS_KEYS]?: unknown };
export type Setting = {
key: SETTINGS_KEYS;
name: string;
@@ -142,12 +110,6 @@ export type Setting = {
options?: { label: string; value: number | string | boolean }[];
};
export enum SIGNATURES_DELETION_TIMING {
IMMEDIATE,
DEFAULT,
EXTENDED,
}
// Now use a stricter type: every timing key maps to a number
export type SignatureDeletionTimingType = Record<SIGNATURES_DELETION_TIMING, number>;
@@ -194,32 +156,6 @@ export const SIGNATURE_SETTINGS = {
],
};
export const SETTINGS_VALUES: SignatureSettingsType = {
[SETTINGS_KEYS.SHOW_UPDATED_COLUMN]: true,
[SETTINGS_KEYS.SHOW_DESCRIPTION_COLUMN]: true,
[SETTINGS_KEYS.SHOW_CHARACTER_COLUMN]: true,
[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES]: true,
[SETTINGS_KEYS.KEEP_LAZY_DELETE]: false,
[SETTINGS_KEYS.DELETION_TIMING]: SIGNATURES_DELETION_TIMING.DEFAULT,
[SETTINGS_KEYS.COLOR_BY_TYPE]: true,
[SETTINGS_KEYS.SHOW_CHARACTER_PORTRAIT]: true,
[SETTINGS_KEYS.COSMIC_ANOMALY]: true,
[SETTINGS_KEYS.COSMIC_SIGNATURE]: true,
[SETTINGS_KEYS.DEPLOYABLE]: true,
[SETTINGS_KEYS.STRUCTURE]: true,
[SETTINGS_KEYS.STARBASE]: true,
[SETTINGS_KEYS.SHIP]: true,
[SETTINGS_KEYS.DRONE]: true,
[SETTINGS_KEYS.WORMHOLE]: true,
[SETTINGS_KEYS.RELIC_SITE]: true,
[SETTINGS_KEYS.DATA_SITE]: true,
[SETTINGS_KEYS.ORE_SITE]: true,
[SETTINGS_KEYS.GAS_SITE]: true,
[SETTINGS_KEYS.COMBAT_SITE]: true,
};
// Now this map is strongly typed as “number” for each timing enum
export const SIGNATURE_DELETION_TIMEOUTS: SignatureDeletionTimingType = {
[SIGNATURES_DELETION_TIMING.IMMEDIATE]: 0,

View File

@@ -1,5 +1,5 @@
import { SignatureSettingsType } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { ExtendedSystemSignature } from '@/hooks/Mapper/types';
import { SignatureSettingsType } from '@/hooks/Mapper/constants/signatures.ts';
export interface UseSystemSignaturesDataProps {
systemId: string;

View File

@@ -5,15 +5,13 @@ import { OutCommand } from '@/hooks/Mapper/types/mapHandlers';
import { useCallback, useEffect, useState } from 'react';
import useRefState from 'react-usestateref';
import {
SETTINGS_KEYS,
getDeletionTimeoutMs,
} from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { getDeletionTimeoutMs } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/constants.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { getActualSigs } from '../helpers';
import { UseSystemSignaturesDataProps } from './types';
import { usePendingDeletions } from './usePendingDeletions';
import { useSignatureFetching } from './useSignatureFetching';
import { SETTINGS_KEYS } from '@/hooks/Mapper/constants/signatures.ts';
export const useSystemSignaturesData = ({
systemId,

View File

@@ -3,7 +3,6 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { Widget } from '@/hooks/Mapper/components/mapInterface/components';
import { SystemKillsList } from './SystemKillsList';
import { KillsHeader } from './components/SystemKillsHeader';
import { useKillsWidgetSettings } from './hooks/useKillsWidgetSettings';
import { useSystemKills } from './hooks/useSystemKills';
import { KillsSettingsDialog } from './components/SystemKillsSettingsDialog';
import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace';
@@ -13,27 +12,25 @@ const SystemKillsContent = () => {
const {
data: { selectedSystems, isSubscriptionActive },
outCommand,
storedSettings: { settingsKills },
} = useMapRootState();
const [systemId] = selectedSystems || [];
const systemStaticInfo = getSystemStaticInfo(systemId)!;
const [settings] = useKillsWidgetSettings();
const visible = settings.showAll;
const { kills, isLoading, error } = useSystemKills({
systemId,
outCommand,
showAllVisible: visible,
sinceHours: settings.timeRange,
showAllVisible: settingsKills.showAll,
sinceHours: settingsKills.timeRange,
});
const isNothingSelected = !systemId && !visible;
const isNothingSelected = !systemId && !settingsKills.showAll;
const showLoading = isLoading && kills.length === 0;
const filteredKills = useMemo(() => {
if (!settings.whOnly || !visible) return kills;
if (!settingsKills.whOnly || !settingsKills.showAll) return kills;
return kills.filter(kill => {
if (!systemStaticInfo) {
console.warn(`System with id ${kill.solar_system_id} not found.`);
@@ -41,7 +38,7 @@ const SystemKillsContent = () => {
}
return isWormholeSpace(systemStaticInfo.system_class);
});
}, [kills, settings.whOnly, systemStaticInfo, visible]);
}, [kills, settingsKills.whOnly, systemStaticInfo, settingsKills.showAll]);
if (!isSubscriptionActive) {
return (
@@ -87,7 +84,9 @@ const SystemKillsContent = () => {
);
}
return <SystemKillsList kills={filteredKills} onlyOneSystem={!visible} timeRange={settings.timeRange} />;
return (
<SystemKillsList kills={filteredKills} onlyOneSystem={!settingsKills.showAll} timeRange={settingsKills.timeRange} />
);
};
export const WSystemKills = () => {

View File

@@ -7,9 +7,9 @@ import {
WdImgButton,
WdTooltipWrapper,
} from '@/hooks/Mapper/components/ui-kit';
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
import { PrimeIcons } from 'primereact/api';
import useMaxWidth from '@/hooks/Mapper/hooks/useMaxWidth.ts';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
interface KillsHeaderProps {
systemId?: string;
@@ -17,11 +17,14 @@ interface KillsHeaderProps {
}
export const KillsHeader: React.FC<KillsHeaderProps> = ({ systemId, onOpenSettings }) => {
const [settings, setSettings] = useKillsWidgetSettings();
const { showAll } = settings;
const {
storedSettings: { settingsKills, settingsKillsUpdate },
} = useMapRootState();
const { showAll } = settingsKills;
const onToggleShowAllVisible = () => {
setSettings(prev => ({ ...prev, showAll: !prev.showAll }));
settingsKillsUpdate(prev => ({ ...prev, showAll: !prev.showAll }));
};
const headerRef = useRef<HTMLDivElement>(null);

View File

@@ -3,12 +3,12 @@ import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import { WdImgButton } from '@/hooks/Mapper/components/ui-kit';
import { PrimeIcons } from 'primereact/api';
import { useKillsWidgetSettings } from '../hooks/useKillsWidgetSettings';
import {
AddSystemDialog,
SearchOnSubmitCallback,
} from '@/hooks/Mapper/components/mapInterface/components/AddSystemDialog';
import { SystemView, TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
interface KillsSettingsDialogProps {
visible: boolean;
@@ -16,12 +16,15 @@ interface KillsSettingsDialogProps {
}
export const KillsSettingsDialog: React.FC<KillsSettingsDialogProps> = ({ visible, setVisible }) => {
const [globalSettings, setGlobalSettings] = useKillsWidgetSettings();
const {
storedSettings: { settingsKills, settingsKillsUpdate },
} = useMapRootState();
const localRef = useRef({
showAll: globalSettings.showAll,
whOnly: globalSettings.whOnly,
excludedSystems: globalSettings.excludedSystems || [],
timeRange: globalSettings.timeRange,
showAll: settingsKills.showAll,
whOnly: settingsKills.whOnly,
excludedSystems: settingsKills.excludedSystems || [],
timeRange: settingsKills.timeRange,
});
const [, forceRender] = useState(0);
@@ -30,14 +33,14 @@ export const KillsSettingsDialog: React.FC<KillsSettingsDialogProps> = ({ visibl
useEffect(() => {
if (visible) {
localRef.current = {
showAll: globalSettings.showAll,
whOnly: globalSettings.whOnly,
excludedSystems: globalSettings.excludedSystems || [],
timeRange: globalSettings.timeRange,
showAll: settingsKills.showAll,
whOnly: settingsKills.whOnly,
excludedSystems: settingsKills.excludedSystems || [],
timeRange: settingsKills.timeRange,
};
forceRender(n => n + 1);
}
}, [visible, globalSettings]);
}, [visible, settingsKills]);
const handleWHChange = useCallback((checked: boolean) => {
localRef.current = {
@@ -75,12 +78,12 @@ export const KillsSettingsDialog: React.FC<KillsSettingsDialogProps> = ({ visibl
}, []);
const handleApply = useCallback(() => {
setGlobalSettings(prev => ({
settingsKillsUpdate(prev => ({
...prev,
...localRef.current,
}));
setVisible(false);
}, [setGlobalSettings, setVisible]);
}, [settingsKillsUpdate, setVisible]);
const handleHide = useCallback(() => {
setVisible(false);

View File

@@ -1,53 +0,0 @@
import { useMemo, useCallback } from 'react';
import useLocalStorageState from 'use-local-storage-state';
export interface KillsWidgetSettings {
showAll: boolean;
whOnly: boolean;
excludedSystems: number[];
version: number;
timeRange: number;
}
export const DEFAULT_KILLS_WIDGET_SETTINGS: KillsWidgetSettings = {
showAll: false,
whOnly: true,
excludedSystems: [],
version: 2,
timeRange: 4,
};
function mergeWithDefaults(settings?: Partial<KillsWidgetSettings>): KillsWidgetSettings {
if (!settings) {
return DEFAULT_KILLS_WIDGET_SETTINGS;
}
return {
...DEFAULT_KILLS_WIDGET_SETTINGS,
...settings,
excludedSystems: Array.isArray(settings.excludedSystems) ? settings.excludedSystems : [],
};
}
export function useKillsWidgetSettings() {
const [rawValue, setRawValue] = useLocalStorageState<KillsWidgetSettings | undefined>('kills:widget:settings');
const value = useMemo<KillsWidgetSettings>(() => {
return mergeWithDefaults(rawValue);
}, [rawValue]);
const setValue = useCallback(
(newVal: KillsWidgetSettings | ((prev: KillsWidgetSettings) => KillsWidgetSettings)) => {
setRawValue(prev => {
const mergedPrev = mergeWithDefaults(prev);
const nextUnmerged = typeof newVal === 'function' ? newVal(mergedPrev) : newVal;
return mergeWithDefaults(nextUnmerged);
});
},
[setRawValue],
);
return [value, setValue] as const;
}

View File

@@ -3,7 +3,6 @@ import debounce from 'lodash.debounce';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useKillsWidgetSettings } from './useKillsWidgetSettings';
interface UseSystemKillsProps {
systemId?: string;
@@ -26,10 +25,12 @@ function combineKills(existing: DetailedKill[], incoming: DetailedKill[]): Detai
}
export function useSystemKills({ systemId, outCommand, showAllVisible = false, sinceHours = 24 }: UseSystemKillsProps) {
const { data, update } = useMapRootState();
const { detailedKills = {}, systems = [] } = data;
const [settings] = useKillsWidgetSettings();
const excludedSystems = settings.excludedSystems;
const {
data: { detailedKills = {}, systems = [] },
update,
storedSettings: { settingsKills },
} = useMapRootState();
const { excludedSystems } = settingsKills;
const effectiveSinceHours = sinceHours;

View File

@@ -14,13 +14,14 @@ import { TrackingDialog } from '@/hooks/Mapper/components/mapRootContent/compone
import { useMapEventListener } from '@/hooks/Mapper/events';
import { Commands } from '@/hooks/Mapper/types';
import { PingsInterface } from '@/hooks/Mapper/components/mapInterface/components';
import { OldSettingsDialog } from '@/hooks/Mapper/components/mapRootContent/components/OldSettingsDialog.tsx';
export interface MapRootContentProps {}
// eslint-disable-next-line no-empty-pattern
export const MapRootContent = ({}: MapRootContentProps) => {
const {
storedSettings: { interfaceSettings },
storedSettings: { interfaceSettings, isReady, hasOldSettings },
data,
} = useMapRootState();
const { isShowMenu } = interfaceSettings;
@@ -34,7 +35,7 @@ export const MapRootContent = ({}: MapRootContentProps) => {
const [showTrackingDialog, setShowTrackingDialog] = useState(false);
/* Important Notice - this solution needs for use one instance of MapInterface */
const mapInterface = <MapInterface />;
const mapInterface = isReady ? <MapInterface /> : null;
const handleShowOnTheMap = useCallback(() => setShowOnTheMap(true), []);
const handleShowMapSettings = useCallback(() => setShowMapSettings(true), []);
@@ -90,6 +91,8 @@ export const MapRootContent = ({}: MapRootContentProps) => {
{showTrackingDialog && (
<TrackingDialog visible={showTrackingDialog} onHide={() => setShowTrackingDialog(false)} />
)}
{hasOldSettings && <OldSettingsDialog />}
</Layout>
</div>
);

View File

@@ -12,6 +12,7 @@ import {
import { WidgetsSettings } from './components/WidgetsSettings';
import { CommonSettings } from './components/CommonSettings';
import { SettingsListItem } from './types.ts';
import { ImportExport } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components/ImportExport.tsx';
export interface MapSettingsProps {
visible: boolean;
@@ -87,6 +88,10 @@ export const MapSettingsComp = ({ visible, onHide }: MapSettingsProps) => {
<TabPanel header="Widgets" className="h-full" headerClassName={styles.verticalTabHeader}>
<WidgetsSettings />
</TabPanel>
<TabPanel header="Import/Export" className="h-full" headerClassName={styles.verticalTabHeader}>
<ImportExport />
</TabPanel>
</TabView>
</div>
</div>

View File

@@ -22,6 +22,7 @@ import { OutCommand } from '@/hooks/Mapper/types';
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
import { Dropdown } from 'primereact/dropdown';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { WithChildren } from '@/hooks/Mapper/types/common.ts';
type MapSettingsContextType = {
renderSettingItem: (item: SettingsListItem) => ReactNode;
@@ -30,7 +31,7 @@ type MapSettingsContextType = {
const MapSettingsContext = createContext<MapSettingsContextType | undefined>(undefined);
export const MapSettingsProvider = ({ children }: { children: ReactNode }) => {
export const MapSettingsProvider = ({ children }: WithChildren) => {
const {
outCommand,
storedSettings: { interfaceSettings, setInterfaceSettings },

View File

@@ -0,0 +1,202 @@
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useCallback, useMemo, useRef } from 'react';
import { Toast } from 'primereact/toast';
import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
import { saveTextFile } from '@/hooks/Mapper/utils/saveToFile.ts';
import { SplitButton } from 'primereact/splitbutton';
import { loadTextFile } from '@/hooks/Mapper/utils';
export const ImportExport = () => {
const {
storedSettings: { getSettingsForExport, applySettings },
data: { map_slug },
} = useMapRootState();
const toast = useRef<Toast | null>(null);
const handleImportFromClipboard = useCallback(async () => {
const text = await navigator.clipboard.readText();
if (text == null || text == '') {
return;
}
try {
const parsed = parseMapUserSettings(text);
if (applySettings(parsed)) {
toast.current?.show({
severity: 'success',
summary: 'Import',
detail: 'Map settings was imported successfully.',
life: 3000,
});
setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, 100);
return;
}
toast.current?.show({
severity: 'warn',
summary: 'Warning',
detail: 'Settings already imported. Or something went wrong.',
life: 3000,
});
} catch (error) {
console.error(`Import from clipboard Error: `, error);
toast.current?.show({
severity: 'error',
summary: 'Error',
detail: 'Some error occurred on import from Clipboard, check console log.',
life: 3000,
});
}
}, [applySettings]);
const handleImportFromFile = useCallback(async () => {
try {
const text = await loadTextFile();
const parsed = parseMapUserSettings(text);
if (applySettings(parsed)) {
toast.current?.show({
severity: 'success',
summary: 'Import',
detail: 'Map settings was imported successfully.',
life: 3000,
});
return;
}
toast.current?.show({
severity: 'warn',
summary: 'Warning',
detail: 'Settings already imported. Or something went wrong.',
life: 3000,
});
} catch (error) {
console.error(`Import from file Error: `, error);
toast.current?.show({
severity: 'error',
summary: 'Error',
detail: 'Some error occurred on import from File, check console log.',
life: 3000,
});
}
}, [applySettings]);
const handleExportToClipboard = useCallback(async () => {
const settings = getSettingsForExport();
if (!settings) {
return;
}
try {
await navigator.clipboard.writeText(settings);
toast.current?.show({
severity: 'success',
summary: 'Export',
detail: 'Map settings copied into clipboard',
life: 3000,
});
} catch (error) {
console.error(`Export to clipboard Error: `, error);
toast.current?.show({
severity: 'error',
summary: 'Error',
detail: 'Some error occurred on copying to clipboard, check console log.',
life: 3000,
});
}
}, [getSettingsForExport]);
const handleExportToFile = useCallback(async () => {
const settings = getSettingsForExport();
if (!settings) {
return;
}
try {
saveTextFile(`map_settings_${map_slug}.json`, settings);
toast.current?.show({
severity: 'success',
summary: 'Export to File',
detail: 'Map settings successfully saved to file',
life: 3000,
});
} catch (error) {
console.error(`Export to cliboard Error: `, error);
toast.current?.show({
severity: 'error',
summary: 'Error',
detail: 'Some error occurred on saving to file, check console log.',
life: 3000,
});
}
}, [getSettingsForExport, map_slug]);
const importItems = useMemo(
() => [
{
label: 'Import from File',
icon: 'pi pi-file-import',
command: handleImportFromFile,
},
],
[handleImportFromFile],
);
const exportItems = useMemo(
() => [
{
label: 'Export as File',
icon: 'pi pi-file-export',
command: handleExportToFile,
},
],
[handleExportToFile],
);
return (
<div className="w-full h-full flex flex-col gap-5">
<div className="flex flex-col gap-1">
<div>
<SplitButton
onClick={handleImportFromClipboard}
icon="pi pi-download"
size="small"
severity="warning"
label="Import from Clipboard"
className="py-[4px]"
model={importItems}
/>
</div>
<span className="text-stone-500 text-[12px]">
*Will read map settings from clipboard. Be careful it could overwrite current settings.
</span>
</div>
<div className="flex flex-col gap-1">
<div>
<SplitButton
onClick={handleExportToClipboard}
icon="pi pi-upload"
size="small"
label="Export to Clipboard"
className="py-[4px]"
model={exportItems}
/>
</div>
<span className="text-stone-500 text-[12px]">*Will save map settings to clipboard.</span>
</div>
<Toast ref={toast} />
</div>
);
};

View File

@@ -0,0 +1,206 @@
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import { ConfirmPopup } from 'primereact/confirmpopup';
import { useCallback, useRef, useState } from 'react';
import { MapUserSettings } from '@/hooks/Mapper/mapRootProvider/types.ts';
import {
DEFAULT_KILLS_WIDGET_SETTINGS,
DEFAULT_ON_THE_MAP_SETTINGS,
DEFAULT_ROUTES_SETTINGS,
DEFAULT_WIDGET_LOCAL_SETTINGS,
getDefaultWidgetProps,
STORED_INTERFACE_DEFAULT_VALUES,
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures.ts';
import { Toast } from 'primereact/toast';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { saveTextFile } from '@/hooks/Mapper/utils';
const createSettings = function <T>(lsSettings: string | null, defaultValues: T) {
return {
version: -1,
settings: lsSettings ? JSON.parse(lsSettings) : defaultValues,
};
};
export const OldSettingsDialog = () => {
const cpRemoveBtnRef = useRef<HTMLElement>();
const [cpRemoveVisible, setCpRemoveVisible] = useState(false);
const handleShowCP = useCallback(() => setCpRemoveVisible(true), []);
const handleHideCP = useCallback(() => setCpRemoveVisible(false), []);
const toast = useRef<Toast | null>(null);
const {
storedSettings: { checkOldSettings },
data: { map_slug },
} = useMapRootState();
const handleExport = useCallback(
async (asFile?: boolean) => {
const interfaceSettings = localStorage.getItem('window:interface:settings');
const widgetRoutes = localStorage.getItem('window:interface:routes');
const widgetLocal = localStorage.getItem('window:interface:local');
const widgetKills = localStorage.getItem('kills:widget:settings');
const onTheMapOld = localStorage.getItem('window:onTheMap:settings');
const widgetsOld = localStorage.getItem('windows:settings:v2');
const signatures = localStorage.getItem('wanderer_system_signature_settings_v6_5');
const out: MapUserSettings = {
killsWidget: createSettings(widgetKills, DEFAULT_KILLS_WIDGET_SETTINGS),
localWidget: createSettings(widgetLocal, DEFAULT_WIDGET_LOCAL_SETTINGS),
widgets: createSettings(widgetsOld, getDefaultWidgetProps()),
routes: createSettings(widgetRoutes, DEFAULT_ROUTES_SETTINGS),
onTheMap: createSettings(onTheMapOld, DEFAULT_ON_THE_MAP_SETTINGS),
signaturesWidget: createSettings(signatures, DEFAULT_SIGNATURE_SETTINGS),
interface: createSettings(interfaceSettings, STORED_INTERFACE_DEFAULT_VALUES),
};
if (asFile) {
if (!out) {
return;
}
try {
saveTextFile(`map_settings_${map_slug}.json`, JSON.stringify(out));
toast.current?.show({
severity: 'success',
summary: 'Export to File',
detail: 'Map settings successfully saved to file',
life: 3000,
});
} catch (error) {
console.error(`Export to cliboard Error: `, error);
toast.current?.show({
severity: 'error',
summary: 'Error',
detail: 'Some error occurred on saving to file, check console log.',
life: 3000,
});
return;
}
return;
}
try {
await navigator.clipboard.writeText(JSON.stringify(out));
toast.current?.show({
severity: 'success',
summary: 'Export to clipboard',
detail: 'Map settings was export successfully.',
life: 3000,
});
} catch (error) {
console.error(`Export to clipboard Error: `, error);
toast.current?.show({
severity: 'error',
summary: 'Error',
detail: 'Some error occurred on copying to clipboard, check console log.',
life: 3000,
});
}
},
[map_slug],
);
const handleExportClipboard = useCallback(async () => {
await handleExport();
}, [handleExport]);
const handleExportAsFile = useCallback(async () => {
await handleExport(true);
}, [handleExport]);
const handleProceed = useCallback(() => {
localStorage.removeItem('window:interface:settings');
localStorage.removeItem('window:interface:routes');
localStorage.removeItem('window:interface:local');
localStorage.removeItem('kills:widget:settings');
localStorage.removeItem('window:onTheMap:settings');
localStorage.removeItem('windows:settings:v2');
localStorage.removeItem('wanderer_system_signature_settings_v6_5');
checkOldSettings();
}, [checkOldSettings]);
return (
<>
<Dialog
header={
<div className="dialog-header">
<span className="pointer-events-none">Old settings detected!</span>
</div>
}
draggable={false}
resizable={false}
closable={false}
visible
onHide={() => null}
className="w-[640px] h-[400px] text-text-color min-h-0"
footer={
<div className="flex items-center justify-end">
<Button
// @ts-ignore
ref={cpRemoveBtnRef}
onClick={handleShowCP}
icon="pi pi-exclamation-triangle"
size="small"
severity="warning"
label="Proceed"
/>
</div>
}
>
<div className="w-full h-full flex flex-col gap-1 items-center justify-center text-stone-400 text-[15px]">
<span>
We detected <span className="text-orange-400">deprecated</span> settings saved in your browser.
</span>
<span>
Now we will give you ability to make <span className="text-orange-400">export</span> your old settings.
</span>
<span>
After click: all settings will saved in your <span className="text-orange-400">clipboard</span>.
</span>
<span>
Then you need to go into <span className="text-orange-400">Map Settings</span> and click{' '}
<span className="text-orange-400">Import from clipboard</span>
</span>
<div className="h-[30px]"></div>
<div className="flex items-center gap-3">
<Button
onClick={handleExportClipboard}
icon="pi pi-copy"
size="small"
severity="info"
label="Export to Clipboard"
/>
<Button
onClick={handleExportAsFile}
icon="pi pi-download"
size="small"
severity="info"
label="Export as File"
/>
</div>
<span className="text-stone-600 text-[12px]">*You will see this dialog until click Export.</span>
</div>
</Dialog>
<ConfirmPopup
target={cpRemoveBtnRef.current}
visible={cpRemoveVisible}
onHide={handleHideCP}
message="After click dialog will disappear. Ready?"
icon="pi pi-exclamation-triangle"
accept={handleProceed}
/>
<Toast ref={toast} />
</>
);
};

View File

@@ -7,24 +7,11 @@ import { VirtualScroller, VirtualScrollerTemplateOptions } from 'primereact/virt
import clsx from 'clsx';
import { CharacterTypeRaw, WithIsOwnCharacter } from '@/hooks/Mapper/types';
import { CharacterCard, TooltipPosition, WdCheckbox, WdImageSize, WdImgButton } 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';
import { InputText } from 'primereact/inputtext';
import { IconField } from 'primereact/iconfield';
type WindowLocalSettingsType = {
compact: boolean;
hideOffline: boolean;
version: number;
};
const STORED_DEFAULT_VALUES: WindowLocalSettingsType = {
compact: true,
hideOffline: false,
version: 0,
};
const itemTemplate = (item: CharacterTypeRaw & WithIsOwnCharacter, options: VirtualScrollerTemplateOptions) => {
return (
<div
@@ -48,14 +35,11 @@ export interface OnTheMapProps {
export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
const {
data: { characters, userCharacters },
storedSettings: { settingsOnTheMap, settingsOnTheMapUpdate },
} = useMapRootState();
const [searchVal, setSearchVal] = useState('');
const [settings, setSettings] = useLocalStorageState<WindowLocalSettingsType>('window:onTheMap:settings', {
defaultValue: STORED_DEFAULT_VALUES,
});
const restrictOfflineShowing = useMapGetOption('restrict_offline_showing');
const isAdminOrManager = useMapCheckPermissions([UserPermission.MANAGE_MAP]);
@@ -107,12 +91,12 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
});
}
if (showOffline && !settings.hideOffline) {
if (showOffline && !settingsOnTheMap.hideOffline) {
return out;
}
return out.filter(x => x.online);
}, [showOffline, searchVal, characters, settings.hideOffline, userCharacters]);
}, [showOffline, searchVal, characters, settingsOnTheMap.hideOffline, userCharacters]);
return (
<Sidebar
@@ -153,9 +137,11 @@ export const OnTheMap = ({ show, onHide }: OnTheMapProps) => {
size="m"
labelSide="left"
label={'Hide offline'}
value={settings.hideOffline}
value={settingsOnTheMap.hideOffline}
classNameLabel="text-stone-400 hover:text-stone-200 transition duration-300"
onChange={() => setSettings(() => ({ ...settings, hideOffline: !settings.hideOffline }))}
onChange={() =>
settingsOnTheMapUpdate(() => ({ ...settingsOnTheMap, hideOffline: !settingsOnTheMap.hideOffline }))
}
/>
)}
</div>

View File

@@ -0,0 +1,49 @@
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
import useLocalStorageState from 'use-local-storage-state';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
export const DebugComponent = () => {
const { outCommand } = useMapRootState();
const [record, setRecord] = useLocalStorageState<boolean>('record', {
defaultValue: false,
});
// @ts-ignore
const [recordsList] = useLocalStorageState<{ type; data }[]>('recordsList', {
defaultValue: [],
});
const handleRunSavedEvents = () => {
recordsList.forEach(record => outCommand(record));
};
return (
<>
<WdTooltipWrapper content="Run saved events" 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={handleRunSavedEvents}
disabled={recordsList.length === 0 || record}
>
<i className="pi pi-forward"></i>
</button>
</WdTooltipWrapper>
<WdTooltipWrapper content="Record" 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={() => setRecord(x => !x)}
>
{!record ? (
<i className="pi pi-play-circle text-green-500"></i>
) : (
<i className="pi pi-stop-circle text-red-500"></i>
)}
</button>
</WdTooltipWrapper>
</>
);
};

View File

@@ -7,6 +7,7 @@ import { TooltipPosition } from '@/hooks/Mapper/components/ui-kit';
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
import { UserPermission } from '@/hooks/Mapper/types/permissions.ts';
// import { DebugComponent } from '@/hooks/Mapper/components/mapRootContent/components/RightBar/DebugComponent.tsx';
interface RightBarProps {
onShowOnTheMap?: () => void;
@@ -79,6 +80,9 @@ export const RightBar = ({
</div>
<div className="flex flex-col items-center mb-2 gap-1">
{/* TODO - do not delete this code needs for debug */}
{/*<DebugComponent />*/}
<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"

View File

@@ -48,7 +48,7 @@ export const MapWrapper = () => {
linkSignatureToSystem,
systemSignatures,
},
storedSettings: { interfaceSettings },
storedSettings: { interfaceSettings, settingsLocal },
} = useMapRootState();
const {
@@ -254,6 +254,7 @@ export const MapWrapper = () => {
pings={pings}
onAddSystem={onAddSystem}
minimapPlacement={minimapPosition}
localShowShipName={settingsLocal.showShipName}
/>
{openSettings != null && (

View File

@@ -33,6 +33,7 @@ export enum Regions {
Solitude = 10000044,
TashMurkon = 10000020,
VergeVendor = 10000068,
Pochven = 10000070,
}
export enum Spaces {
@@ -40,6 +41,7 @@ export enum Spaces {
'Gallente' = 'Gallente',
'Matar' = 'Matar',
'Amarr' = 'Amarr',
'Pochven' = 'Pochven',
}
export const REGIONS_MAP: Record<number, Spaces> = {
@@ -66,6 +68,7 @@ export const REGIONS_MAP: Record<number, Spaces> = {
[Regions.Solitude]: Spaces.Gallente,
[Regions.TashMurkon]: Spaces.Amarr,
[Regions.VergeVendor]: Spaces.Gallente,
[Regions.Pochven]: Spaces.Pochven,
};
export type K162Type = {

View File

@@ -0,0 +1,71 @@
import { SignatureGroup, SignatureKind } from '@/hooks/Mapper/types';
export const SIGNATURE_WINDOW_ID = 'system_signatures_window';
export enum SIGNATURES_DELETION_TIMING {
IMMEDIATE,
DEFAULT,
EXTENDED,
}
export enum SETTINGS_KEYS {
SORT_FIELD = 'sortField',
SORT_ORDER = 'sortOrder',
SHOW_DESCRIPTION_COLUMN = 'show_description_column',
SHOW_UPDATED_COLUMN = 'show_updated_column',
SHOW_CHARACTER_COLUMN = 'show_character_column',
LAZY_DELETE_SIGNATURES = 'lazy_delete_signatures',
KEEP_LAZY_DELETE = 'keep_lazy_delete_enabled',
DELETION_TIMING = 'deletion_timing',
COLOR_BY_TYPE = 'color_by_type',
SHOW_CHARACTER_PORTRAIT = 'show_character_portrait',
// From SignatureKind
COSMIC_ANOMALY = SignatureKind.CosmicAnomaly,
COSMIC_SIGNATURE = SignatureKind.CosmicSignature,
DEPLOYABLE = SignatureKind.Deployable,
STRUCTURE = SignatureKind.Structure,
STARBASE = SignatureKind.Starbase,
SHIP = SignatureKind.Ship,
DRONE = SignatureKind.Drone,
// From SignatureGroup
WORMHOLE = SignatureGroup.Wormhole,
RELIC_SITE = SignatureGroup.RelicSite,
DATA_SITE = SignatureGroup.DataSite,
ORE_SITE = SignatureGroup.OreSite,
GAS_SITE = SignatureGroup.GasSite,
COMBAT_SITE = SignatureGroup.CombatSite,
}
export type SignatureSettingsType = { [key in SETTINGS_KEYS]?: unknown };
export const DEFAULT_SIGNATURE_SETTINGS: SignatureSettingsType = {
[SETTINGS_KEYS.SORT_FIELD]: 'inserted_at',
[SETTINGS_KEYS.SORT_ORDER]: -1,
[SETTINGS_KEYS.SHOW_UPDATED_COLUMN]: true,
[SETTINGS_KEYS.SHOW_DESCRIPTION_COLUMN]: true,
[SETTINGS_KEYS.SHOW_CHARACTER_COLUMN]: true,
[SETTINGS_KEYS.LAZY_DELETE_SIGNATURES]: true,
[SETTINGS_KEYS.KEEP_LAZY_DELETE]: false,
[SETTINGS_KEYS.DELETION_TIMING]: SIGNATURES_DELETION_TIMING.DEFAULT,
[SETTINGS_KEYS.COLOR_BY_TYPE]: true,
[SETTINGS_KEYS.SHOW_CHARACTER_PORTRAIT]: true,
[SETTINGS_KEYS.COSMIC_ANOMALY]: true,
[SETTINGS_KEYS.COSMIC_SIGNATURE]: true,
[SETTINGS_KEYS.DEPLOYABLE]: true,
[SETTINGS_KEYS.STRUCTURE]: true,
[SETTINGS_KEYS.STARBASE]: true,
[SETTINGS_KEYS.SHIP]: true,
[SETTINGS_KEYS.DRONE]: true,
[SETTINGS_KEYS.WORMHOLE]: true,
[SETTINGS_KEYS.RELIC_SITE]: true,
[SETTINGS_KEYS.DATA_SITE]: true,
[SETTINGS_KEYS.ORE_SITE]: true,
[SETTINGS_KEYS.GAS_SITE]: true,
[SETTINGS_KEYS.COMBAT_SITE]: true,
};

View File

@@ -0,0 +1,11 @@
export function getFormattedTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const ms = String(now.getMilliseconds() + 1000).slice(1);
return `${hours}:${minutes}:${seconds} ${ms}`;
}

View File

@@ -1,4 +1,3 @@
export * from './useActualizeSettings';
export * from './useClipboard';
export * from './useHotkey';
export * from './usePageVisibility';

View File

@@ -1,23 +0,0 @@
import { useEffect } from 'react';
type Settings = Record<string, unknown>;
export const useActualizeSettings = <T extends Settings>(defaultVals: T, vals: T, setVals: (newVals: T) => void) => {
useEffect(() => {
let foundNew = false;
const newVals = Object.keys(defaultVals).reduce((acc, x) => {
if (Object.keys(acc).includes(x)) {
return acc;
}
foundNew = true;
// @ts-ignore
return { ...acc, [x]: defaultVals[x] };
}, vals);
if (foundNew) {
setVals(newVals);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};

View File

@@ -1,11 +1,12 @@
import { useState, useEffect } from 'react';
function usePageVisibility() {
const [isVisible, setIsVisible] = useState(!document.hidden);
const getIsVisible = () => !document.hidden;
const [isVisible, setIsVisible] = useState(getIsVisible());
useEffect(() => {
const handleVisibilityChange = () => {
setIsVisible(!document.hidden);
setIsVisible(getIsVisible());
};
document.addEventListener('visibilitychange', handleVisibilityChange);

View File

@@ -19,10 +19,24 @@ import {
} from '@/hooks/Mapper/mapRootProvider/hooks/useStoreWidgets.ts';
import { WindowsManagerOnChange } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { DetailedKill } from '../types/kills';
import { InterfaceStoredSettings, RoutesType } from '@/hooks/Mapper/mapRootProvider/types.ts';
import { DEFAULT_ROUTES_SETTINGS, STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/constants.ts';
import {
InterfaceStoredSettings,
KillsWidgetSettings,
LocalWidgetSettings,
MapUserSettings,
OnTheMapSettingsType,
RoutesType,
} from '@/hooks/Mapper/mapRootProvider/types.ts';
import {
DEFAULT_KILLS_WIDGET_SETTINGS,
DEFAULT_ON_THE_MAP_SETTINGS,
DEFAULT_ROUTES_SETTINGS,
DEFAULT_WIDGET_LOCAL_SETTINGS,
STORED_INTERFACE_DEFAULT_VALUES,
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
import { useMapUserSettings } from '@/hooks/Mapper/mapRootProvider/hooks/useMapUserSettings.ts';
import { useGlobalHooks } from '@/hooks/Mapper/mapRootProvider/hooks/useGlobalHooks.ts';
import { DEFAULT_SIGNATURE_SETTINGS, SignatureSettingsType } from '@/hooks/Mapper/constants/signatures';
export type MapRootData = MapUnionTypes & {
selectedSystems: string[];
@@ -36,6 +50,7 @@ export type MapRootData = MapUnionTypes & {
};
trackingCharactersData: TrackingCharacter[];
loadingPublicRoutes: boolean;
map_slug: string | null;
};
const INITIAL_DATA: MapRootData = {
@@ -70,6 +85,7 @@ const INITIAL_DATA: MapRootData = {
followingCharacterEveId: null,
pings: [],
loadingPublicRoutes: false,
map_slug: null,
};
export enum InterfaceStoredSettingsProps {
@@ -103,6 +119,19 @@ export interface MapRootContextProps {
setInterfaceSettings: Dispatch<SetStateAction<InterfaceStoredSettings>>;
settingsRoutes: RoutesType;
settingsRoutesUpdate: Dispatch<SetStateAction<RoutesType>>;
settingsLocal: LocalWidgetSettings;
settingsLocalUpdate: Dispatch<SetStateAction<LocalWidgetSettings>>;
settingsSignatures: SignatureSettingsType;
settingsSignaturesUpdate: Dispatch<SetStateAction<SignatureSettingsType>>;
settingsOnTheMap: OnTheMapSettingsType;
settingsOnTheMapUpdate: Dispatch<SetStateAction<OnTheMapSettingsType>>;
settingsKills: KillsWidgetSettings;
settingsKillsUpdate: Dispatch<SetStateAction<KillsWidgetSettings>>;
isReady: boolean;
hasOldSettings: boolean;
getSettingsForExport(): string | undefined;
applySettings(settings: MapUserSettings): boolean;
checkOldSettings(): void;
};
}
@@ -134,6 +163,19 @@ const MapRootContext = createContext<MapRootContextProps>({
setInterfaceSettings: () => null,
settingsRoutes: DEFAULT_ROUTES_SETTINGS,
settingsRoutesUpdate: () => null,
settingsLocal: DEFAULT_WIDGET_LOCAL_SETTINGS,
settingsLocalUpdate: () => null,
settingsSignatures: DEFAULT_SIGNATURE_SETTINGS,
settingsSignaturesUpdate: () => null,
settingsOnTheMap: DEFAULT_ON_THE_MAP_SETTINGS,
settingsOnTheMapUpdate: () => null,
settingsKills: DEFAULT_KILLS_WIDGET_SETTINGS,
settingsKillsUpdate: () => null,
isReady: false,
hasOldSettings: false,
getSettingsForExport: () => '',
applySettings: () => false,
checkOldSettings: () => null,
},
});
@@ -154,9 +196,11 @@ const MapRootHandlers = forwardRef(({ children }: WithChildren, fwdRef: Forwarde
export const MapRootProvider = ({ children, fwdRef, outCommand }: MapRootProviderProps) => {
const { update, ref } = useContextStore<MapRootData>({ ...INITIAL_DATA });
const storedSettings = useMapUserSettings();
const storedSettings = useMapUserSettings(ref);
const { windowsSettings, toggleWidgetVisibility, updateWidgetSettings, resetWidgets } =
useStoreWidgets(storedSettings);
const { windowsSettings, toggleWidgetVisibility, updateWidgetSettings, resetWidgets } = useStoreWidgets();
const comments = useComments({ outCommand });
const charactersCache = useCharactersCache({ outCommand });

View File

@@ -1,10 +1,18 @@
import {
AvailableThemes,
InterfaceStoredSettings,
KillsWidgetSettings,
LocalWidgetSettings,
MiniMapPlacement,
OnTheMapSettingsType,
PingsPlacement,
RoutesType,
} from '@/hooks/Mapper/mapRootProvider/types.ts';
import {
CURRENT_WINDOWS_VERSION,
DEFAULT_WIDGETS,
STORED_VISIBLE_WIDGETS_DEFAULT,
} from '@/hooks/Mapper/components/mapInterface/constants.tsx';
export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
isShowMenu: false,
@@ -31,3 +39,29 @@ export const DEFAULT_ROUTES_SETTINGS: RoutesType = {
avoid_triglavian: false,
avoid: [],
};
export const DEFAULT_WIDGET_LOCAL_SETTINGS: LocalWidgetSettings = {
compact: true,
showOffline: false,
version: 0,
showShipName: false,
};
export const DEFAULT_ON_THE_MAP_SETTINGS: OnTheMapSettingsType = {
hideOffline: false,
version: 0,
};
export const DEFAULT_KILLS_WIDGET_SETTINGS: KillsWidgetSettings = {
showAll: false,
whOnly: true,
excludedSystems: [],
version: 2,
timeRange: 4,
};
export const getDefaultWidgetProps = () => ({
version: CURRENT_WINDOWS_VERSION,
visible: STORED_VISIBLE_WIDGETS_DEFAULT,
windows: DEFAULT_WIDGETS,
});

View File

@@ -0,0 +1,22 @@
type Settings = Record<string, unknown>;
export const actualizeSettings = <T extends Settings>(defaultVals: T, vals: T, setVals: (newVals: T) => void) => {
let foundNew = false;
const newVals = Object.keys(defaultVals).reduce((acc, key) => {
if (key in acc) {
return acc;
}
foundNew = true;
return {
...acc,
[key]: defaultVals[key],
};
}, vals);
if (foundNew) {
setVals(newVals);
}
};

View File

@@ -0,0 +1 @@
export * from './actualizeSettings';

View File

@@ -27,6 +27,7 @@ export const useMapInit = () => {
main_character_eve_id,
following_character_eve_id,
user_hubs,
map_slug,
} = props;
const updateData: Partial<MapRootData> = {};
@@ -98,6 +99,10 @@ export const useMapInit = () => {
updateData.followingCharacterEveId = following_character_eve_id;
}
if ('map_slug' in props) {
updateData.map_slug = map_slug;
}
update(updateData);
},
[update, addSystemStatic],

View File

@@ -1,39 +1,222 @@
import useLocalStorageState from 'use-local-storage-state';
import { InterfaceStoredSettings, RoutesType } from '@/hooks/Mapper/mapRootProvider/types.ts';
import { DEFAULT_ROUTES_SETTINGS, STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/constants.ts';
import { useActualizeSettings } from '@/hooks/Mapper/hooks';
import { useEffect } from 'react';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import { MapUserSettings, MapUserSettingsStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
import {
DEFAULT_KILLS_WIDGET_SETTINGS,
DEFAULT_ON_THE_MAP_SETTINGS,
DEFAULT_ROUTES_SETTINGS,
DEFAULT_WIDGET_LOCAL_SETTINGS,
getDefaultWidgetProps,
STORED_INTERFACE_DEFAULT_VALUES,
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
import { useCallback, useEffect, useRef, useState } from 'react';
import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures';
import { MapRootData } from '@/hooks/Mapper/mapRootProvider';
import { useSettingsValueAndSetter } from '@/hooks/Mapper/mapRootProvider/hooks/useSettingsValueAndSetter.ts';
import fastDeepEqual from 'fast-deep-equal';
export const useMigrationRoutesSettingsV1 = (update: (upd: RoutesType) => void) => {
//TODO if current Date is more than 01.01.2026 - remove this hook.
// import { actualizeSettings } from '@/hooks/Mapper/mapRootProvider/helpers';
useEffect(() => {
const items = localStorage.getItem(SESSION_KEY.routes);
if (items) {
update(JSON.parse(items));
localStorage.removeItem(SESSION_KEY.routes);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// TODO - we need provide and compare version
const createWidgetSettingsWithVersion = <T>(settings: T) => {
return {
version: 0,
settings,
};
};
export const useMapUserSettings = () => {
const [interfaceSettings, setInterfaceSettings] = useLocalStorageState<InterfaceStoredSettings>(
'window:interface:settings',
{
defaultValue: STORED_INTERFACE_DEFAULT_VALUES,
},
);
const createDefaultWidgetSettings = (): MapUserSettings => {
return {
killsWidget: createWidgetSettingsWithVersion(DEFAULT_KILLS_WIDGET_SETTINGS),
localWidget: createWidgetSettingsWithVersion(DEFAULT_WIDGET_LOCAL_SETTINGS),
widgets: createWidgetSettingsWithVersion(getDefaultWidgetProps()),
routes: createWidgetSettingsWithVersion(DEFAULT_ROUTES_SETTINGS),
onTheMap: createWidgetSettingsWithVersion(DEFAULT_ON_THE_MAP_SETTINGS),
signaturesWidget: createWidgetSettingsWithVersion(DEFAULT_SIGNATURE_SETTINGS),
interface: createWidgetSettingsWithVersion(STORED_INTERFACE_DEFAULT_VALUES),
};
};
const [settingsRoutes, settingsRoutesUpdate] = useLocalStorageState<RoutesType>('window:interface:routes', {
defaultValue: DEFAULT_ROUTES_SETTINGS,
const EMPTY_OBJ = {};
export const useMapUserSettings = ({ map_slug }: MapRootData) => {
const [isReady, setIsReady] = useState(false);
const [hasOldSettings, setHasOldSettings] = useState(false);
const [mapUserSettings, setMapUserSettings] = useLocalStorageState<MapUserSettingsStructure>('map-user-settings', {
defaultValue: EMPTY_OBJ,
});
useActualizeSettings(STORED_INTERFACE_DEFAULT_VALUES, interfaceSettings, setInterfaceSettings);
useActualizeSettings(DEFAULT_ROUTES_SETTINGS, settingsRoutes, settingsRoutesUpdate);
const ref = useRef({ mapUserSettings, setMapUserSettings, map_slug });
ref.current = { mapUserSettings, setMapUserSettings, map_slug };
useMigrationRoutesSettingsV1(settingsRoutesUpdate);
useEffect(() => {
const { mapUserSettings, setMapUserSettings } = ref.current;
if (map_slug === null) {
return;
}
return { interfaceSettings, setInterfaceSettings, settingsRoutes, settingsRoutesUpdate };
if (!(map_slug in mapUserSettings)) {
setMapUserSettings({
...mapUserSettings,
[map_slug]: createDefaultWidgetSettings(),
});
}
}, [map_slug]);
const [interfaceSettings, setInterfaceSettings] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'interface',
);
const [settingsRoutes, settingsRoutesUpdate] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'routes',
);
const [settingsLocal, settingsLocalUpdate] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'localWidget',
);
const [settingsSignatures, settingsSignaturesUpdate] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'signaturesWidget',
);
const [settingsOnTheMap, settingsOnTheMapUpdate] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'onTheMap',
);
const [settingsKills, settingsKillsUpdate] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'killsWidget',
);
const [windowsSettings, setWindowsSettings] = useSettingsValueAndSetter(
mapUserSettings,
setMapUserSettings,
map_slug,
'widgets',
);
// HERE we MUST work with migrations
useEffect(() => {
if (isReady) {
return;
}
if (map_slug === null) {
return;
}
if (mapUserSettings[map_slug] == null) {
return;
}
// TODO !!!! FROM this date 06.07.2025 - we must work only with migrations
// actualizeSettings(STORED_INTERFACE_DEFAULT_VALUES, interfaceSettings, setInterfaceSettings);
// actualizeSettings(DEFAULT_ROUTES_SETTINGS, settingsRoutes, settingsRoutesUpdate);
// actualizeSettings(DEFAULT_WIDGET_LOCAL_SETTINGS, settingsLocal, settingsLocalUpdate);
// actualizeSettings(DEFAULT_SIGNATURE_SETTINGS, settingsSignatures, settingsSignaturesUpdate);
// actualizeSettings(DEFAULT_ON_THE_MAP_SETTINGS, settingsOnTheMap, settingsOnTheMapUpdate);
// actualizeSettings(DEFAULT_KILLS_WIDGET_SETTINGS, settingsKills, settingsKillsUpdate);
setIsReady(true);
}, [
map_slug,
mapUserSettings,
interfaceSettings,
setInterfaceSettings,
settingsRoutes,
settingsRoutesUpdate,
settingsLocal,
settingsLocalUpdate,
settingsSignatures,
settingsSignaturesUpdate,
settingsOnTheMap,
settingsOnTheMapUpdate,
settingsKills,
settingsKillsUpdate,
isReady,
]);
const checkOldSettings = useCallback(() => {
const interfaceSettings = localStorage.getItem('window:interface:settings');
const widgetRoutes = localStorage.getItem('window:interface:routes');
const widgetLocal = localStorage.getItem('window:interface:local');
const widgetKills = localStorage.getItem('kills:widget:settings');
const onTheMapOld = localStorage.getItem('window:onTheMap:settings');
const widgetsOld = localStorage.getItem('windows:settings:v2');
setHasOldSettings(!!(widgetsOld || interfaceSettings || widgetRoutes || widgetLocal || widgetKills || onTheMapOld));
}, []);
useEffect(() => {
checkOldSettings();
}, [checkOldSettings]);
const getSettingsForExport = useCallback(() => {
const { map_slug } = ref.current;
if (map_slug == null) {
return;
}
return JSON.stringify(ref.current.mapUserSettings[map_slug]);
}, []);
const applySettings = useCallback((settings: MapUserSettings) => {
const { map_slug, mapUserSettings, setMapUserSettings } = ref.current;
if (map_slug == null) {
return false;
}
if (fastDeepEqual(settings, mapUserSettings[map_slug])) {
return false;
}
setMapUserSettings(old => ({
...old,
[map_slug]: settings,
}));
return true;
}, []);
return {
isReady,
hasOldSettings,
interfaceSettings,
setInterfaceSettings,
settingsRoutes,
settingsRoutesUpdate,
settingsLocal,
settingsLocalUpdate,
settingsSignatures,
settingsSignaturesUpdate,
settingsOnTheMap,
settingsOnTheMapUpdate,
settingsKills,
settingsKillsUpdate,
windowsSettings,
setWindowsSettings,
getSettingsForExport,
applySettings,
checkOldSettings,
};
};

View File

@@ -0,0 +1,60 @@
import { Dispatch, SetStateAction, useCallback, useMemo, useRef } from 'react';
import {
MapUserSettings,
MapUserSettingsStructure,
SettingsWithVersion,
} from '@/hooks/Mapper/mapRootProvider/types.ts';
type ExtractSettings<S extends keyof MapUserSettings> =
MapUserSettings[S] extends SettingsWithVersion<infer U> ? U : never;
type Setter<S extends keyof MapUserSettings> = (
value: Partial<ExtractSettings<S>> | ((prev: ExtractSettings<S>) => Partial<ExtractSettings<S>>),
) => void;
type GenerateSettingsReturn<S extends keyof MapUserSettings> = [ExtractSettings<S>, Setter<S>];
export const useSettingsValueAndSetter = <S extends keyof MapUserSettings>(
settings: MapUserSettingsStructure,
setSettings: Dispatch<SetStateAction<MapUserSettingsStructure>>,
mapId: string | null,
setting: S,
): GenerateSettingsReturn<S> => {
const data = useMemo<ExtractSettings<S>>(() => {
if (!mapId) return {} as ExtractSettings<S>;
const mapSettings = settings[mapId];
return (mapSettings?.[setting]?.settings ?? ({} as ExtractSettings<S>)) as ExtractSettings<S>;
}, [mapId, setting, settings]);
const refData = useRef({ mapId, setting, setSettings });
refData.current = { mapId, setting, setSettings };
const setter = useCallback<Setter<S>>(value => {
const { mapId, setting, setSettings } = refData.current;
if (!mapId) return;
setSettings(all => {
const currentMap = all[mapId];
const prev = currentMap[setting].settings as ExtractSettings<S>;
const version = currentMap[setting].version;
const patch =
typeof value === 'function' ? (value as (p: ExtractSettings<S>) => Partial<ExtractSettings<S>>)(prev) : value;
return {
...all,
[mapId]: {
...currentMap,
[setting]: {
version,
settings: { ...(prev as any), ...patch } as ExtractSettings<S>,
},
},
};
});
}, []);
return [data, setter];
};

View File

@@ -1,14 +1,8 @@
import useLocalStorageState from 'use-local-storage-state';
import {
CURRENT_WINDOWS_VERSION,
DEFAULT_WIDGETS,
STORED_VISIBLE_WIDGETS_DEFAULT,
WidgetsIds,
WINDOWS_LOCAL_STORE_KEY,
} from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { DEFAULT_WIDGETS, WidgetsIds } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { WindowProps } from '@/hooks/Mapper/components/ui-kit/WindowManager/types.ts';
import { useCallback, useEffect, useRef } from 'react';
import { /*SNAP_GAP,*/ WindowsManagerOnChange } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { Dispatch, SetStateAction, useCallback, useRef } from 'react';
import { WindowsManagerOnChange } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { getDefaultWidgetProps } from '@/hooks/Mapper/mapRootProvider/constants.ts';
export type StoredWindowProps = Omit<WindowProps, 'content'>;
export type WindowStoreInfo = {
@@ -20,17 +14,12 @@ export type WindowStoreInfo = {
// export type UpdateWidgetSettingsFunc = (widgets: WindowProps[]) => void;
export type ToggleWidgetVisibility = (widgetId: WidgetsIds) => void;
export const getDefaultWidgetProps = () => ({
version: CURRENT_WINDOWS_VERSION,
visible: STORED_VISIBLE_WIDGETS_DEFAULT,
windows: DEFAULT_WIDGETS,
});
export const useStoreWidgets = () => {
const [windowsSettings, setWindowsSettings] = useLocalStorageState<WindowStoreInfo>(WINDOWS_LOCAL_STORE_KEY, {
defaultValue: getDefaultWidgetProps(),
});
interface UseStoreWidgetsProps {
windowsSettings: WindowStoreInfo;
setWindowsSettings: Dispatch<SetStateAction<WindowStoreInfo>>;
}
export const useStoreWidgets = ({ windowsSettings, setWindowsSettings }: UseStoreWidgetsProps) => {
const ref = useRef({ windowsSettings, setWindowsSettings });
ref.current = { windowsSettings, setWindowsSettings };
@@ -83,33 +72,6 @@ export const useStoreWidgets = () => {
});
}, []);
useEffect(() => {
const { setWindowsSettings } = ref.current;
const raw = localStorage.getItem(WINDOWS_LOCAL_STORE_KEY);
if (!raw) {
console.warn('No windows found in local storage!!');
setWindowsSettings(getDefaultWidgetProps());
return;
}
const { version, windows, visible, viewPort } = JSON.parse(raw) as WindowStoreInfo;
if (!version || CURRENT_WINDOWS_VERSION > version) {
setWindowsSettings(getDefaultWidgetProps());
}
// eslint-disable-next-line no-debugger
const out = windows.filter(x => DEFAULT_WIDGETS.find(def => def.id === x.id));
setWindowsSettings({
version: CURRENT_WINDOWS_VERSION,
windows: out as WindowProps[],
visible,
viewPort,
});
}, []);
const resetWidgets = useCallback(() => ref.current.setWindowsSettings(getDefaultWidgetProps()), []);
return {

View File

@@ -1,3 +1,6 @@
import { WindowStoreInfo } from '@/hooks/Mapper/mapRootProvider/hooks/useStoreWidgets.ts';
import { SignatureSettingsType } from '@/hooks/Mapper/constants/signatures.ts';
export enum AvailableThemes {
default = 'default',
pathfinder = 'pathfinder',
@@ -43,3 +46,42 @@ export type RoutesType = {
avoid_triglavian: boolean;
avoid: number[];
};
export type LocalWidgetSettings = {
compact: boolean;
showOffline: boolean;
version: number;
showShipName: boolean;
};
export type OnTheMapSettingsType = {
hideOffline: boolean;
version: number;
};
export type KillsWidgetSettings = {
showAll: boolean;
whOnly: boolean;
excludedSystems: number[];
version: number;
timeRange: number;
};
export type SettingsWithVersion<T> = {
version: number;
settings: T;
};
export type MapUserSettings = {
widgets: SettingsWithVersion<WindowStoreInfo>;
interface: SettingsWithVersion<InterfaceStoredSettings>;
onTheMap: SettingsWithVersion<OnTheMapSettingsType>;
routes: SettingsWithVersion<RoutesType>;
localWidget: SettingsWithVersion<LocalWidgetSettings>;
signaturesWidget: SettingsWithVersion<SignatureSettingsType>;
killsWidget: SettingsWithVersion<KillsWidgetSettings>;
};
export type MapUserSettingsStructure = {
[mapId: string]: MapUserSettings;
};

View File

@@ -97,6 +97,7 @@ export type CommandInit = {
is_subscription_active?: boolean;
main_character_eve_id?: string | null;
following_character_eve_id?: string | null;
map_slug?: string;
};
export type CommandAddSystems = SolarSystemRawType[];

View File

@@ -1,27 +1,121 @@
import { MapHandlers } from '@/hooks/Mapper/types/mapHandlers.ts';
import { RefObject, useCallback } from 'react';
import { RefObject, useCallback, useEffect, useRef } from 'react';
import debounce from 'lodash.debounce';
import usePageVisibility from '@/hooks/Mapper/hooks/usePageVisibility.ts';
// const inIndex = 0;
// const prevEventTime = +new Date();
const LAST_VERSION_KEY = 'wandererLastVersion';
// @ts-ignore
export const useMapperHandlers = (handlerRefs: RefObject<MapHandlers>[], hooksRef: RefObject<any>) => {
const visible = usePageVisibility();
const wasHiddenOnce = useRef(false);
const visibleRef = useRef(visible);
visibleRef.current = visible;
// TODO - do not delete THIS code it needs for debug
// const [record, setRecord] = useLocalStorageState<boolean>('record', {
// defaultValue: false,
// });
// const [recordsList, setRecordsList] = useLocalStorageState<{ type; data }[]>('recordsList', {
// defaultValue: [],
// });
//
// const ref = useRef({ record, setRecord, recordsList, setRecordsList });
// ref.current = { record, setRecord, recordsList, setRecordsList };
//
// const recordBufferRef = useRef<{ type; data }[]>([]);
// useEffect(() => {
// if (record || recordBufferRef.current.length === 0) {
// return;
// }
//
// ref.current.setRecordsList([...recordBufferRef.current]);
// recordBufferRef.current = [];
// }, [record]);
const handleCommand = useCallback(
// @ts-ignore
async ({ type, data }) => {
if (!hooksRef.current) {
return;
}
// TODO - do not delete THIS code it needs for debug
// console.log('JOipP', `OUT`, ref.current.record, { type, data });
// if (ref.current.record) {
// recordBufferRef.current.push({ type, data });
// }
// 'ui_loaded'
return await hooksRef.current.pushEventAsync(type, data);
},
[hooksRef.current],
);
const handleMapEvent = useCallback(({ type, body }) => {
handlerRefs.forEach(ref => {
if (!ref.current) {
// @ts-ignore
const eventsBufferRef = useRef<{ type; body }[]>([]);
const eventTick = useCallback(
debounce(() => {
if (eventsBufferRef.current.length === 0) {
return;
}
ref.current?.command(type, body);
});
const { type, body } = eventsBufferRef.current.shift()!;
handlerRefs.forEach(ref => {
if (!ref.current) {
return;
}
ref.current?.command(type, body);
});
// TODO - do not delete THIS code it needs for debug
// console.log('JOipP', `Tick Buff`, eventsBufferRef.current.length);
if (eventsBufferRef.current.length > 0) {
eventTick();
}
}, 10),
[],
);
const eventTickRef = useRef(eventTick);
eventTickRef.current = eventTick;
// @ts-ignore
const handleMapEvent = useCallback(({ type, body }) => {
// TODO - do not delete THIS code it needs for debug
// const currentTime = +new Date();
// const timeDiff = currentTime - prevEventTime;
// prevEventTime = currentTime;
// console.log('JOipP', `IN [${inIndex++}] [${timeDiff}] ${getFormattedTime()}`, { type, body });
if (!eventTickRef.current || !visibleRef.current) {
return;
}
eventsBufferRef.current.push({ type, body });
eventTickRef.current();
}, []);
useEffect(() => {
if (!visible && !wasHiddenOnce.current) {
wasHiddenOnce.current = true;
return;
}
if (!wasHiddenOnce.current) {
return;
}
if (!visible) {
return;
}
hooksRef.current.pushEventAsync('ui_loaded', { version: localStorage.getItem(LAST_VERSION_KEY) });
}, [hooksRef.current, visible]);
return { handleCommand, handleMapEvent };
};

View File

@@ -1,2 +1,4 @@
export * from './contextStore';
export * from './getQueryVariable';
export * from './loadTextFile';
export * from './saveToFile';

View File

@@ -0,0 +1,27 @@
export function loadTextFile(): Promise<string> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json,.json';
input.onchange = () => {
const file = input.files?.[0];
if (!file) {
reject(new Error('No file selected'));
return;
}
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = () => {
reject(reader.error);
};
reader.readAsText(file);
};
input.click();
});
}

View File

@@ -0,0 +1,33 @@
export function saveTextFile(filename: string, content: string) {
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
export async function saveTextFileInteractive(filename: string, content: string) {
if (!('showSaveFilePicker' in window)) {
throw new Error('File System Access API is not supported in this browser.');
}
const handle = await (window as any).showSaveFilePicker({
suggestedName: filename,
types: [
{
description: 'Text Files',
accept: { 'text/plain': ['.txt', '.json'] },
},
],
});
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

View File

@@ -84,9 +84,9 @@ map_subscription_base_price =
config_dir
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_BASE_PRICE", 100_000_000)
map_subscription_extra_characters_100_price =
map_subscription_extra_characters_50_price =
config_dir
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_EXTRA_CHARACTERS_100_PRICE", 50_000_000)
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_EXTRA_CHARACTERS_50_PRICE", 50_000_000)
map_subscription_extra_hubs_10_price =
config_dir
@@ -167,7 +167,7 @@ config :wanderer_app,
month_12_discount: 0.5
}
],
extra_characters_100: map_subscription_extra_characters_100_price,
extra_characters_50: map_subscription_extra_characters_50_price,
extra_hubs_10: map_subscription_extra_hubs_10_price
}

View File

@@ -97,7 +97,7 @@ defmodule WandererApp.Map.SubscriptionManager do
) do
%{
plans: plans,
extra_characters_100: extra_characters_100,
extra_characters_50: extra_characters_50,
extra_hubs_10: extra_hubs_10
} = WandererApp.Env.subscription_settings()
@@ -113,7 +113,7 @@ defmodule WandererApp.Map.SubscriptionManager do
case characters_limit > plan_characters_limit do
true ->
estimated_price +
(characters_limit - plan_characters_limit) / 100 * extra_characters_100
(characters_limit - plan_characters_limit) / 50 * extra_characters_50
_ ->
estimated_price
@@ -153,7 +153,7 @@ defmodule WandererApp.Map.SubscriptionManager do
) do
%{
plans: plans,
extra_characters_100: extra_characters_100,
extra_characters_50: extra_characters_50,
extra_hubs_10: extra_hubs_10
} = WandererApp.Env.subscription_settings()
@@ -170,7 +170,7 @@ defmodule WandererApp.Map.SubscriptionManager do
case characters_limit > sub_characters_limit do
true ->
additional_price +
(characters_limit - sub_characters_limit) / 100 * extra_characters_100
(characters_limit - sub_characters_limit) / 50 * extra_characters_50
_ ->
additional_price

View File

@@ -30,7 +30,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
@a4 22
@a5 23
@ccp4 24
# @pochven 25
@pochven 25
# @zarzakh 10100
@jita 30_000_142
@@ -51,7 +51,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
@redoubt
]
@known_space [@hs, @ls, @ns]
@known_space [@hs, @ls, @ns, @pochven]
@prohibited_systems [@jita]
@prohibited_system_classes [

View File

@@ -87,11 +87,8 @@ defmodule WandererAppWeb.MapCharacters do
end
@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)
{:noreply, socket}
end
def handle_event("undo", %{"event-data" => _event_data} = _params, socket),
do: {:noreply, socket}
defp is_online?(character_id) do
{:ok, state} = WandererApp.Character.get_character_state(character_id)

View File

@@ -10,6 +10,8 @@ defmodule WandererAppWeb.MapSubscription do
socket =
socket
|> assign(title: "")
|> assign(status: :alpha)
|> assign(balance: 0)
{:ok, socket}
end
@@ -24,19 +26,20 @@ defmodule WandererAppWeb.MapSubscription do
) do
socket = handle_info_or_assign(socket, assigns)
{:ok, %{id: map_id} = map} =
WandererApp.MapRepo.get_by_slug_with_permissions(map_slug, current_user)
{:ok, %{plan: plan} = subscription} =
WandererApp.Map.SubscriptionManager.get_active_map_subscription(map_id)
{:ok, map_balance} = WandererApp.Map.SubscriptionManager.get_balance(map)
{:ok,
socket
|> assign(status: plan)
|> assign(title: get_title(subscription))
|> assign(balance: map_balance)}
with {:ok, %{id: map_id} = map} <-
WandererApp.MapRepo.get_by_slug_with_permissions(map_slug, current_user),
{:ok, %{plan: plan} = subscription} <-
WandererApp.Map.SubscriptionManager.get_active_map_subscription(map_id),
{:ok, map_balance} <- WandererApp.Map.SubscriptionManager.get_balance(map) do
{:ok,
socket
|> assign(status: plan)
|> assign(title: get_title(subscription))
|> assign(balance: map_balance)}
else
_error ->
{:ok, socket}
end
end
@impl true

View File

@@ -126,6 +126,14 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|> assign(show_topup: true)
end
@impl true
def handle_server_event(
{_event, {:flash, type, message}},
socket
) do
socket |> put_flash(type, message)
end
def handle_server_event(event, socket) do
Logger.warning(fn -> "unhandled map core event: #{inspect(event)}" end)
socket
@@ -544,6 +552,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
defp map_start(
%{
assigns: %{
map_slug: map_slug,
current_user: current_user,
needs_tracking_setup: needs_tracking_setup,
main_character_id: main_character_id,
@@ -589,6 +598,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
initial_data
|> Map.merge(map_data)
|> Map.merge(%{
map_slug: map_slug,
main_character_eve_id: main_character_eve_id,
following_character_eve_id: following_character_eve_id,
is_subscription_active: is_subscription_active,

View File

@@ -7,8 +7,12 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
def handle_server_event(%{event: :add_system, payload: system}, socket) do
# Schedule kill update for the new system after a short delay to allow subscription
Process.send_after(self(), %{event: :update_system_kills, payload: system.solar_system_id}, 2000)
Process.send_after(
self(),
%{event: :update_system_kills, payload: system.solar_system_id},
2000
)
socket
|> MapEventHandler.push_map_event("add_systems", [
MapEventHandler.map_ui_system(system)
@@ -319,7 +323,8 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
defp update_system_position(map_id, %{
"position" => %{"x" => x, "y" => y},
"solar_system_id" => solar_system_id
}),
})
when not is_nil(x) and not is_nil(y) and not is_nil(solar_system_id),
do:
map_id
|> WandererApp.Map.Server.update_system_position(%{
@@ -327,4 +332,6 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
position_x: x,
position_y: y
})
defp update_system_position(_map_id, _position), do: :ok
end

View File

@@ -31,8 +31,8 @@ defmodule WandererAppWeb.Maps.MapSubscriptionsComponent do
subscription_form = %{
"plan" => "omega",
"period" => "1",
"characters_limit" => "100",
"hubs_limit" => "10",
"characters_limit" => "50",
"hubs_limit" => "20",
"auto_renew?" => true
}
@@ -522,9 +522,9 @@ defmodule WandererAppWeb.Maps.MapSubscriptionsComponent do
label="Characters limit"
show_value={true}
type="range"
min="100"
min="50"
max="5000"
step="100"
step="50"
class="range range-xs"
/>
<.input

View File

@@ -515,19 +515,19 @@ defmodule WandererAppWeb.MapsLive do
{:ok, tmp_file_path}
end)
Task.async(fn ->
{:ok, data} =
WandererApp.Utils.JSONUtil.read_json(uploaded_file_path)
# Task.async(fn ->
# {:ok, data} =
# WandererApp.Utils.JSONUtil.read_json(uploaded_file_path)
WandererApp.Map.Manager.start_map(map_id)
# WandererApp.Map.Manager.start_map(map_id)
:timer.sleep(1000)
# :timer.sleep(1000)
map_id
|> WandererApp.Map.Server.import_settings(data, current_user.id)
# map_id
# |> WandererApp.Map.Server.import_settings(data, current_user.id)
:imported
end)
# :imported
# end)
{:noreply,
socket
@@ -543,7 +543,7 @@ defmodule WandererAppWeb.MapsLive do
selected_subscription
) do
%{
extra_characters_100: extra_characters_100,
extra_characters_50: extra_characters_50,
extra_hubs_10: extra_hubs_10
} = WandererApp.Env.subscription_settings()
@@ -558,7 +558,7 @@ defmodule WandererAppWeb.MapsLive do
case characters_limit > sub_characters_limit do
true ->
additional_price +
(characters_limit - sub_characters_limit) / 100 * extra_characters_100
(characters_limit - sub_characters_limit) / 50 * extra_characters_50
_ ->
additional_price

View File

@@ -413,6 +413,11 @@
field={f[:show_linked_signature_id]}
label="Show linked signature ID as custom label part"
/>
<.input
type="checkbox"
field={f[:show_linked_signature_id_temp_name]}
label="Show linked signature ID as temporary name part"
/>
<.input
type="checkbox"
field={f[:restrict_offline_showing]}
@@ -427,9 +432,9 @@
for={@import_form}
phx-change="import"
>
<div phx-drop-target="{@uploads.settings.ref}">
<%!-- <div phx-drop-target="{@uploads.settings.ref}">
<.live_file_input upload={@uploads.settings} />
</div>
</div> --%>
</.form>
<progress :if={@importing} class="progress w-56"></progress>
<.button

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.73.0"
@version "1.74.9"
def project do
[