From be7bbe6872f62d361b682bbbda1a1d7246406f17 Mon Sep 17 00:00:00 2001 From: Lee Solway Date: Sat, 4 Oct 2025 11:47:49 +0100 Subject: [PATCH] Create a signature list panel + hook into live events --- .../mapRootContent/MapRootContent.tsx | 12 ++ .../WormholeSignaturesDialog.tsx | 175 ++++++++++++++++++ .../WormholeSignaturesDialog/index.ts | 1 + .../hooks/useMapRootHandlers.ts | 3 + assets/js/hooks/Mapper/types/mapHandlers.ts | 6 +- .../event_handlers/map_core_event_handler.ex | 9 + .../live/map/map_live.html.heex | 9 + 7 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/WormholeSignaturesDialog.tsx create mode 100644 assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/index.ts diff --git a/assets/js/hooks/Mapper/components/mapRootContent/MapRootContent.tsx b/assets/js/hooks/Mapper/components/mapRootContent/MapRootContent.tsx index f4844d0e..f7e12b02 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/MapRootContent.tsx +++ b/assets/js/hooks/Mapper/components/mapRootContent/MapRootContent.tsx @@ -9,6 +9,7 @@ import { MapContextMenu } from '@/hooks/Mapper/components/mapRootContent/compone import { useSkipContextMenu } from '@/hooks/Mapper/hooks/useSkipContextMenu'; import { MapSettings } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings'; import { CharacterActivity } from '@/hooks/Mapper/components/mapRootContent/components/CharacterActivity'; +import { WormholeSignaturesDialog } from '@/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog'; import { useCharacterActivityHandlers } from './hooks/useCharacterActivityHandlers'; import { TrackingDialog } from '@/hooks/Mapper/components/mapRootContent/components/TrackingDialog'; import { useMapEventListener } from '@/hooks/Mapper/events'; @@ -33,6 +34,7 @@ export const MapRootContent = ({}: MapRootContentProps) => { const [showOnTheMap, setShowOnTheMap] = useState(false); const [showMapSettings, setShowMapSettings] = useState(false); const [showTrackingDialog, setShowTrackingDialog] = useState(false); + const [showWormholeSignatures, setShowWormholeSignatures] = useState(false); /* Important Notice - this solution needs for use one instance of MapInterface */ const mapInterface = isReady ? : null; @@ -46,6 +48,10 @@ export const MapRootContent = ({}: MapRootContentProps) => { setShowTrackingDialog(true); return true; } + if (event.name === Commands.showWormholeSignatures) { + setShowWormholeSignatures(true); + return true; + } }); useSkipContextMenu(); @@ -91,6 +97,12 @@ export const MapRootContent = ({}: MapRootContentProps) => { {showTrackingDialog && ( setShowTrackingDialog(false)} /> )} + {showWormholeSignatures && ( + setShowWormholeSignatures(false)} + /> + )} {hasOldSettings && } diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/WormholeSignaturesDialog.tsx b/assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/WormholeSignaturesDialog.tsx new file mode 100644 index 00000000..18f8cbd6 --- /dev/null +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/WormholeSignaturesDialog.tsx @@ -0,0 +1,175 @@ +import { useMemo, useState } from 'react'; +import { Dialog } from 'primereact/dialog'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; +import { WormholeDataRaw } from '@/hooks/Mapper/types'; +import { WHClassView } from '@/hooks/Mapper/components/ui-kit'; +import { kgToTons } from '@/hooks/Mapper/utils/kgToTons.ts'; +import { WORMHOLE_CLASS_STYLES, WORMHOLES_ADDITIONAL_INFO } from '@/hooks/Mapper/components/map/constants.ts'; +import clsx from 'clsx'; +import { InputText } from 'primereact/inputtext'; +import { IconField } from 'primereact/iconfield'; +import { InputIcon } from 'primereact/inputicon'; + +export interface WormholeSignaturesDialogProps { + visible: boolean; + onHide: () => void; +} + +const RespawnTag = ({ value }: { value: string }) => ( + {value} +); + +export const WormholeSignaturesDialog = ({ visible, onHide }: WormholeSignaturesDialogProps) => { + const { + data: { wormholes }, + } = useMapRootState(); + + const [filter, setFilter] = useState(''); + + const filtered = useMemo(() => { + const q = filter.trim().toLowerCase(); + + if (!q) return wormholes; + + return wormholes.filter(w => { + const destInfo = WORMHOLES_ADDITIONAL_INFO[w.dest]; + const spawnsLabels = w.src + .map(s => { + const group = s.split('-')[0]; + const info = WORMHOLES_ADDITIONAL_INFO[group]; + if (!info) return s; + return `${info.title} ${info.shortName}`.trim(); + }) + .join(' '); + + return [ + w.name, + destInfo?.title, + destInfo?.shortName, + spawnsLabels, + String(w.total_mass), + String(w.max_mass_per_jump), + w.lifetime, + w.respawn.join(','), + ] + .filter(Boolean) + .join(' ') + .toLowerCase() + .includes(q); + }); + }, [wormholes, filter]); + + const renderName = (w: WormholeDataRaw) => ( +
+ +
+ ); + + const renderRespawn = (w: WormholeDataRaw) => ( +
+ {w.respawn.map(r => ( + + ))} +
+ ); + + const renderSpawns = (w: WormholeDataRaw) => ( +
+ {w.src.map(s => { + const group = s.split('-')[0]; + const info = WORMHOLES_ADDITIONAL_INFO[group]; + if (!info) + return ( + + {s} + + ); + const cls = WORMHOLE_CLASS_STYLES[String(info.wormholeClassID)] || ''; + const label = `${info.shortName}`; + return ( + + {label} + + ); + })} +
+ ); + + return ( + +
+
Reference list of all wormhole types
+ + filter && setFilter('')} + role="button" + aria-label="Clear search" + aria-disabled={!filter} + title={filter ? 'Clear' : 'Nothing to clear'} + /> + setFilter(e.target.value)} /> + +
+ +
+ + + + + kgToTons(w.total_mass)} + bodyClassName="whitespace-normal break-words" + /> + kgToTons(w.max_mass_per_jump)} + bodyClassName="whitespace-normal break-words" + /> + + +
+
+ ); +}; diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/index.ts b/assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/index.ts new file mode 100644 index 00000000..6c970e1c --- /dev/null +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/WormholeSignaturesDialog/index.ts @@ -0,0 +1 @@ +export * from './WormholeSignaturesDialog'; diff --git a/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts b/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts index 1c7c7c57..d1e5b405 100644 --- a/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts +++ b/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts @@ -173,6 +173,9 @@ export const useMapRootHandlers = (ref: ForwardedRef) => { pingCancelled(data as CommandPingCancelled); break; + case Commands.showWormholeSignatures: + break; + default: console.warn(`JOipP Interface handlers: Unknown command: ${type}`, data); break; diff --git a/assets/js/hooks/Mapper/types/mapHandlers.ts b/assets/js/hooks/Mapper/types/mapHandlers.ts index 5f429cda..800d9a4b 100644 --- a/assets/js/hooks/Mapper/types/mapHandlers.ts +++ b/assets/js/hooks/Mapper/types/mapHandlers.ts @@ -40,6 +40,7 @@ export enum Commands { showTracking = 'show_tracking', pingAdded = 'ping_added', pingCancelled = 'ping_cancelled', + showWormholeSignatures = 'show_wormhole_signatures', } export type Command = @@ -75,7 +76,8 @@ export type Command = | Commands.updateTracking | Commands.showTracking | Commands.pingAdded - | Commands.pingCancelled; + | Commands.pingCancelled + | Commands.showWormholeSignatures; export type CommandInit = { systems: SolarSystemRawType[]; @@ -158,6 +160,7 @@ export type CommandUpdateTracking = { }; export type CommandPingAdded = PingData[]; export type CommandPingCancelled = Pick; +export type CommandShowWormholeSignatures = null; export interface UserSettings { primaryCharacterId?: string; @@ -208,6 +211,7 @@ export interface CommandData { [Commands.showTracking]: CommandShowTracking; [Commands.pingAdded]: CommandPingAdded; [Commands.pingCancelled]: CommandPingCancelled; + [Commands.showWormholeSignatures]: CommandShowWormholeSignatures; } export interface MapHandlers { diff --git a/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex b/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex index 6e279f60..e4df33b8 100644 --- a/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex +++ b/lib/wanderer_app_web/live/map/event_handlers/map_core_event_handler.ex @@ -194,6 +194,15 @@ defmodule WandererAppWeb.MapCoreEventHandler do {:reply, %{user_settings: user_settings}, socket} end + def handle_ui_event("show_wormhole_signatures", _event, socket) do + {:noreply, + socket + |> MapEventHandler.push_map_event( + "show_wormhole_signatures", + %{} + )} + end + def handle_ui_event( "update_user_settings", user_settings_form, diff --git a/lib/wanderer_app_web/live/map/map_live.html.heex b/lib/wanderer_app_web/live/map/map_live.html.heex index c83f1d9f..3ef8c0e9 100644 --- a/lib/wanderer_app_web/live/map/map_live.html.heex +++ b/lib/wanderer_app_web/live/map/map_live.html.heex @@ -32,6 +32,15 @@ <.icon name="hero-chart-bar-solid" class="w-6 h-6" /> + + <.link :if={(@user_permissions || %{}) |> Map.get(:delete_map, false)} id={"map-audit-#{@map_slug}"}