From bdcde168aa828a3a3c8125bcad3a19337acddeb8 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Thu, 5 Dec 2024 09:52:07 +0100 Subject: [PATCH] feat(Signatures): Show 'Unsplashed' signatures on the map (optionally) --- .../SolarSystemNode.module.scss | 41 +++++++++++- .../SolarSystemNode/SolarSystemNode.tsx | 42 +++++++++++- .../UnsplashedSignature.module.scss | 18 +++++ .../UnsplashedSignature.tsx | 65 +++++++++++++++++++ .../components/UnsplashedSignature/index.ts | 1 + .../Mapper/components/map/helpers/index.ts | 1 + .../map/helpers/prepareUnsplashedChunks.ts | 27 ++++++++ .../SystemSignaturesContent.tsx | 1 - .../components/MapSettings/MapSettings.tsx | 1 + .../mapRootProvider/MapRootProvider.tsx | 3 + .../hooks/api/useCommandsSystems.ts | 31 ++++++++- .../hooks/useMapRootHandlers.ts | 5 +- assets/js/hooks/Mapper/types/system.ts | 3 + .../components/user_activity.ex | 5 +- .../map_signatures_event_handler.ex | 2 +- .../live/maps/map_event_handler.ex | 9 +++ 16 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/UnsplashedSignature.module.scss create mode 100644 assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/UnsplashedSignature.tsx create mode 100644 assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/index.ts create mode 100644 assets/js/hooks/Mapper/components/map/helpers/prepareUnsplashedChunks.ts diff --git a/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.module.scss b/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.module.scss index 133779f5..51e99b71 100644 --- a/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.module.scss +++ b/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.module.scss @@ -6,7 +6,7 @@ $pastel-green: #88b04b; $pastel-yellow: #ffdd59; $dark-bg: #2d2d2d; $text-color: #ffffff; -$tooltip-bg: #202020; // Темный фон для подсказок +$tooltip-bg: #202020; // Dark background for tooltips .RootCustomNode { display: flex; @@ -136,7 +136,7 @@ $tooltip-bg: #202020; // Темный фон для подсказок .Bookmarks { position: absolute; width: 100%; - z-index: 0; + z-index: 1; display: flex; left: 4px; @@ -182,6 +182,42 @@ $tooltip-bg: #202020; // Темный фон для подсказок } } +.Unsplashed { + position: absolute; + width: calc(50% - 4px); + z-index: -1; + display: flex; + flex-wrap: wrap; + gap: 2px; + left: 2px; + + &--right { + left: calc(50% + 6px); + } + + & > .Signature { + width: 13px; + height: 4px; + position: relative; + top: 3px; + border-radius: 5px; + color: #ffffff; + font-size: 8px; + text-align: center; + padding-top: 2px; + font-weight: bolder; + padding-left: 3px; + padding-right: 3px; + display: block; + + background-color: #833ca4; + + &:not(:first-child) { + box-shadow: inset 4px -3px 4px rgba(0, 0, 0, 0.3); + } + } +} + .icon { width: 8px; height: 8px; @@ -276,7 +312,6 @@ $tooltip-bg: #202020; // Темный фон для подсказок position: relative; top: -1px; } - } .Handlers { diff --git a/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.tsx b/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.tsx index ea617dd5..3e641c83 100644 --- a/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.tsx +++ b/assets/js/hooks/Mapper/components/map/components/SolarSystemNode/SolarSystemNode.tsx @@ -3,6 +3,8 @@ import { Handle, Position, WrapNodeProps } from 'reactflow'; import { MapSolarSystemType } from '../../map.types'; import classes from './SolarSystemNode.module.scss'; import clsx from 'clsx'; +import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; + import { EFFECT_BACKGROUND_STYLES, LABELS_INFO, @@ -12,8 +14,9 @@ import { } from '@/hooks/Mapper/components/map/constants.ts'; import { isWormholeSpace } from '@/hooks/Mapper/components/map/helpers/isWormholeSpace.ts'; import { WormholeClassComp } from '@/hooks/Mapper/components/map/components/WormholeClassComp'; +import { UnsplashedSignature } from '@/hooks/Mapper/components/map/components/UnsplashedSignature'; import { useMapState } from '@/hooks/Mapper/components/map/MapProvider.tsx'; -import { getSystemClassStyles } from '@/hooks/Mapper/components/map/helpers'; +import { getSystemClassStyles, prepareUnsplashedChunks } from '@/hooks/Mapper/components/map/helpers'; import { sortWHClasses } from '@/hooks/Mapper/helpers'; import { PrimeIcons } from 'primereact/api'; import { LabelsManager } from '@/hooks/Mapper/utils/labelsManager.ts'; @@ -50,6 +53,9 @@ export const getActivityType = (count: number) => { // eslint-disable-next-line react/display-name export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps) => { + const { interfaceSettings } = useMapRootState(); + const { isShowUnsplashedSignatures } = interfaceSettings; + const { system_class, security, @@ -63,6 +69,8 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps { + if (!isShowUnsplashedSignatures) { + return [[], []]; + } + + return prepareUnsplashedChunks( + signatures + .filter(s => s.group === 'Wormhole' && !s.linked_system) + .map(s => ({ + eve_id: s.eve_id, + type: s.type, + custom_info: s.custom_info, + })), + ); + }, [isShowUnsplashedSignatures, signatures]); + return ( <> {visible && ( @@ -237,6 +261,22 @@ export const SolarSystemNode = memo(({ data, selected }: WrapNodeProps + {visible && isShowUnsplashedSignatures && ( +
+ {unsplashedLeft.map(x => ( + + ))} +
+ )} + + {visible && isShowUnsplashedSignatures && ( +
+ {unsplashedRight.map(x => ( + + ))} +
+ )} +
.Box { + width: 13px; + height: 4px; + border-radius: 4px; + color: #ffffff; + font-size: 8px; + text-align: center; + font-weight: bolder; + display: block; + } +} diff --git a/assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/UnsplashedSignature.tsx b/assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/UnsplashedSignature.tsx new file mode 100644 index 00000000..022a0e71 --- /dev/null +++ b/assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/UnsplashedSignature.tsx @@ -0,0 +1,65 @@ +import { WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit/WdTooltipWrapper'; +import { InfoDrawer } from '@/hooks/Mapper/components/ui-kit'; + +import classes from './UnsplashedSignature.module.scss'; +import { SystemSignature } from '@/hooks/Mapper/types/signatures'; +import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; +import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants.ts'; +import { useMemo } from 'react'; +import clsx from 'clsx'; +import { renderInfoColumn } from '@/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/renders'; + +import { k162Types } from '@/hooks/Mapper/components/mapRootContent/components/SignatureSettings/components/SignatureK162TypeSelect'; + +interface UnsplashedSignatureProps { + signature: SystemSignature; +} +export const UnsplashedSignature = ({ signature }: UnsplashedSignatureProps) => { + const { + data: { wormholesData }, + } = useMapRootState(); + + const whData = useMemo(() => wormholesData[signature.type], [signature.type, wormholesData]); + const whClass = useMemo(() => (whData ? WORMHOLES_ADDITIONAL_INFO[whData.dest] : null), [whData]); + + const k162TypeOption = useMemo(() => { + if (!signature.custom_info) { + return null; + } + const customInfo = JSON.parse(signature.custom_info); + if (!customInfo.k162Type) { + return null; + } + return k162Types.find(x => x.value === customInfo.k162Type); + }, [signature]); + + const whClassStyle = useMemo(() => { + if (signature.type === 'K162' && k162TypeOption) { + const k162Data = wormholesData[k162TypeOption.whClassName]; + const k162Class = k162Data ? WORMHOLES_ADDITIONAL_INFO[k162Data.dest] : null; + return k162Class ? WORMHOLE_CLASS_STYLES[k162Class.wormholeClassID] : ''; + } + return whClass ? WORMHOLE_CLASS_STYLES[whClass.wormholeClassID] : ''; + }, [signature, whClass, k162TypeOption, wormholesData]); + + return ( + + {signature.eve_id}}> + {renderInfoColumn(signature)} + +
+ ) as React.ReactNode + } + > +
+ + + +
+ + ); +}; diff --git a/assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/index.ts b/assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/index.ts new file mode 100644 index 00000000..4036201e --- /dev/null +++ b/assets/js/hooks/Mapper/components/map/components/UnsplashedSignature/index.ts @@ -0,0 +1 @@ +export * from './UnsplashedSignature.tsx'; diff --git a/assets/js/hooks/Mapper/components/map/helpers/index.ts b/assets/js/hooks/Mapper/components/map/helpers/index.ts index ad81ddd3..a13d842a 100644 --- a/assets/js/hooks/Mapper/components/map/helpers/index.ts +++ b/assets/js/hooks/Mapper/components/map/helpers/index.ts @@ -3,3 +3,4 @@ export * from './convertSystem2Node'; export * from './getSystemClassStyles'; export * from './getShapeClass'; export * from './getBackgroundClass'; +export * from './prepareUnsplashedChunks'; diff --git a/assets/js/hooks/Mapper/components/map/helpers/prepareUnsplashedChunks.ts b/assets/js/hooks/Mapper/components/map/helpers/prepareUnsplashedChunks.ts new file mode 100644 index 00000000..b6622aa3 --- /dev/null +++ b/assets/js/hooks/Mapper/components/map/helpers/prepareUnsplashedChunks.ts @@ -0,0 +1,27 @@ +// Helper function to split an array into chunks of size +const chunkArray = (array: any[], size: number) => { + const chunks = []; + for (let i = 0; i < array.length; i += size) { + chunks.push(array.slice(i, i + size)); + } + return chunks; +}; + +export const prepareUnsplashedChunks = (items: any[]) => { + // Split the items into chunks of 4 + const chunks = chunkArray(items, 4); + + // Get the column elements + const leftColumn: any[] = []; + const rightColumn: any[] = []; + + chunks.forEach((chunk, index) => { + const column = index % 2 === 0 ? leftColumn : rightColumn; + + chunk.forEach(item => { + column.push(item); + }); + }); + + return [leftColumn, rightColumn]; +}; diff --git a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent/SystemSignaturesContent.tsx b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent/SystemSignaturesContent.tsx index 48148530..aec206c0 100644 --- a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent/SystemSignaturesContent.tsx +++ b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemSignatures/SystemSignaturesContent/SystemSignaturesContent.tsx @@ -373,7 +373,6 @@ export const SystemSignaturesContent = ({ > { const { update, data: { systems }, + outCommand, } = useMapRootState(); const { addSystemStatic } = useLoadSystemStatic({ systems: [] }); @@ -56,5 +59,29 @@ export const useCommandsSystems = () => { update({ systems: out }, true); }, []); - return { addSystems, removeSystems, updateSystems }; + const updateSystemSignatures = useCallback( + async (systemId: string) => { + const { update, systems } = ref.current; + + const { signatures } = await outCommand({ + type: OutCommand.getSignatures, + data: { system_id: `${systemId}` }, + }); + + const out = systems.map(current => { + if (current.id === `${systemId}`) { + return { ...current, system_signatures: signatures }; + } + + return current; + }); + + update({ systems: out }, true); + + emitMapEvent({ name: Commands.updateSystems, data: out }); + }, + [outCommand], + ); + + return { addSystems, removeSystems, updateSystems, updateSystemSignatures }; }; diff --git a/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts b/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts index a42ef811..b53281b6 100644 --- a/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts +++ b/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts @@ -13,6 +13,7 @@ import { CommandRemoveSystems, CommandRoutes, Commands, + CommandSignaturesUpdated, CommandUpdateConnection, CommandUpdateSystems, MapHandlers, @@ -31,7 +32,7 @@ import { emitMapEvent } from '@/hooks/Mapper/events'; export const useMapRootHandlers = (ref: ForwardedRef) => { const mapInit = useMapInit(); - const { addSystems, removeSystems, updateSystems } = useCommandsSystems(); + const { addSystems, removeSystems, updateSystems, updateSystemSignatures } = useCommandsSystems(); const { addConnections, removeConnections, updateConnection } = useCommandsConnections(); const { charactersUpdated, characterAdded, characterRemoved, characterUpdated, presentCharacters } = useCommandsCharacters(); @@ -88,7 +89,7 @@ export const useMapRootHandlers = (ref: ForwardedRef) => { break; case Commands.signaturesUpdated: // USED - // do nothing here + updateSystemSignatures(data as CommandSignaturesUpdated); break; case Commands.linkSignatureToSystem: // USED diff --git a/assets/js/hooks/Mapper/types/system.ts b/assets/js/hooks/Mapper/types/system.ts index 17365c5b..96a7b62d 100644 --- a/assets/js/hooks/Mapper/types/system.ts +++ b/assets/js/hooks/Mapper/types/system.ts @@ -1,5 +1,7 @@ import { XYPosition } from 'reactflow'; +import { SystemSignature } from '@/hooks/Mapper/types/signatures'; + export enum SolarSystemStaticInfoRawNames { regionId = 'region_id', constellationId = 'constellation_id', @@ -116,4 +118,5 @@ export type SolarSystemRawType = { name: string | null; system_static_info: SolarSystemStaticInfoRaw; + system_signatures: SystemSignature[]; }; diff --git a/lib/wanderer_app_web/components/user_activity.ex b/lib/wanderer_app_web/components/user_activity.ex index 3f77e739..1ec7fc29 100644 --- a/lib/wanderer_app_web/components/user_activity.ex +++ b/lib/wanderer_app_web/components/user_activity.ex @@ -8,7 +8,8 @@ defmodule WandererAppWeb.UserActivity do end @impl true - def update(assigns, + def update( + assigns, socket ) do {:ok, @@ -116,7 +117,6 @@ defmodule WandererAppWeb.UserActivity do
<%= _get_event_data(@event_type, Jason.decode!(@event_data) |> Map.drop(["character_id"])) %>
- """ @@ -125,7 +125,6 @@ defmodule WandererAppWeb.UserActivity do @impl true def handle_event("undo", %{"event-data" => event_data} = _params, socket) do # notify_to(socket.assigns.notify_to, socket.assigns.event_name, map_slug) - IO.inspect(event_data) {:noreply, socket} end diff --git a/lib/wanderer_app_web/live/maps/event_handlers/map_signatures_event_handler.ex b/lib/wanderer_app_web/live/maps/event_handlers/map_signatures_event_handler.ex index cc6f7af1..6a4a094d 100644 --- a/lib/wanderer_app_web/live/maps/event_handlers/map_signatures_event_handler.ex +++ b/lib/wanderer_app_web/live/maps/event_handlers/map_signatures_event_handler.ex @@ -312,7 +312,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do def handle_ui_event(event, body, socket), do: MapCoreEventHandler.handle_ui_event(event, body, socket) - defp get_system_signatures(system_id), + def get_system_signatures(system_id), do: system_id |> WandererApp.Api.MapSystemSignature.by_system_id!() diff --git a/lib/wanderer_app_web/live/maps/map_event_handler.ex b/lib/wanderer_app_web/live/maps/map_event_handler.ex index 3d1d2641..ca4a7477 100644 --- a/lib/wanderer_app_web/live/maps/map_event_handler.ex +++ b/lib/wanderer_app_web/live/maps/map_event_handler.ex @@ -234,6 +234,7 @@ defmodule WandererAppWeb.MapEventHandler do def map_ui_system( %{ + id: system_id, solar_system_id: solar_system_id, name: name, description: description, @@ -249,12 +250,20 @@ defmodule WandererAppWeb.MapEventHandler do ) do system_static_info = get_system_static_info(solar_system_id) + system_signatures = + system_id + |> WandererAppWeb.MapSignaturesEventHandler.get_system_signatures() + |> Enum.filter(fn signature -> + is_nil(signature.linked_system) && signature.group == "Wormhole" + end) + %{ id: "#{solar_system_id}", position: %{x: position_x, y: position_y}, description: description, name: name, system_static_info: system_static_info, + system_signatures: system_signatures, labels: labels, locked: locked, status: status,