From e2814e95bd85f889ab91881545ba73397761b31f Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Mon, 20 Jan 2025 11:42:38 +0100 Subject: [PATCH 01/18] fix: Fix system statics list (required EVE DB data update). Add system name to signature added/removed audit log --- .../components/user_activity.ex | 51 +++++++++---------- .../map_signatures_event_handler.ex | 2 + priv/repo/data/wormholes.json | 2 +- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/lib/wanderer_app_web/components/user_activity.ex b/lib/wanderer_app_web/components/user_activity.ex index 05cb8e6e..043cc272 100644 --- a/lib/wanderer_app_web/components/user_activity.ex +++ b/lib/wanderer_app_web/components/user_activity.ex @@ -162,40 +162,40 @@ defmodule WandererAppWeb.UserActivity do "solar_system_id" => solar_system_id, "value" => value }) do - system_name = _get_system_name(solar_system_id) + system_name = get_system_name(solar_system_id) try do %{"customLabel" => customLabel, "labels" => labels} = Jason.decode!(value) - "#{system_name} labels - #{inspect(labels)}, customLabel - #{customLabel}" + "#{system_name}: labels - #{inspect(labels)}, customLabel - #{customLabel}" rescue _ -> - "#{system_name} labels - #{inspect(value)}" + "#{system_name}: labels - #{inspect(value)}" end end defp get_event_data(:system_added, %{ "solar_system_id" => solar_system_id }), - do: _get_system_name(solar_system_id) + do: get_system_name(solar_system_id) defp get_event_data(:hub_added, %{ "solar_system_id" => solar_system_id }), - do: _get_system_name(solar_system_id) + do: get_system_name(solar_system_id) defp get_event_data(:hub_removed, %{ "solar_system_id" => solar_system_id }), - do: _get_system_name(solar_system_id) + do: get_system_name(solar_system_id) defp get_event_data(:system_updated, %{ "key" => key, "solar_system_id" => solar_system_id, "value" => value }) do - system_name = _get_system_name(solar_system_id) - "#{system_name} #{key} - #{inspect(value)}" + system_name = get_system_name(solar_system_id) + "#{system_name}: #{key} - #{inspect(value)}" end defp get_event_data(:systems_removed, %{ @@ -203,29 +203,28 @@ defmodule WandererAppWeb.UserActivity do }), do: solar_system_ids - |> Enum.map(&_get_system_name/1) + |> Enum.map(&get_system_name/1) |> Enum.join(", ") - defp get_event_data(:signatures_added, %{ + defp get_event_data(signatures_event, %{ + "solar_system_id" => solar_system_id, "signatures" => signatures - }), - do: - signatures - |> Enum.join(", ") + }) + when signatures_event in [:signatures_added, :signatures_removed], + do: "#{get_system_name(solar_system_id)}: #{signatures |> Enum.join(", ")}" - defp get_event_data(:signatures_removed, %{ + defp get_event_data(signatures_event, %{ "signatures" => signatures - }), - do: - signatures - |> Enum.join(", ") + }) + when signatures_event in [:signatures_added, :signatures_removed], + do: signatures |> Enum.join(", ") defp get_event_data(:map_connection_added, %{ "solar_system_source_id" => solar_system_source_id, "solar_system_target_id" => solar_system_target_id }) do - source_system_name = _get_system_name(solar_system_source_id) - target_system_name = _get_system_name(solar_system_target_id) + source_system_name = get_system_name(solar_system_source_id) + target_system_name = get_system_name(solar_system_target_id) "[#{source_system_name}:#{target_system_name}]" end @@ -233,8 +232,8 @@ defmodule WandererAppWeb.UserActivity do "solar_system_source_id" => solar_system_source_id, "solar_system_target_id" => solar_system_target_id }) do - source_system_name = _get_system_name(solar_system_source_id) - target_system_name = _get_system_name(solar_system_target_id) + source_system_name = get_system_name(solar_system_source_id) + target_system_name = get_system_name(solar_system_target_id) "[#{source_system_name}:#{target_system_name}]" end @@ -244,14 +243,14 @@ defmodule WandererAppWeb.UserActivity do "solar_system_target_id" => solar_system_target_id, "value" => value }) do - source_system_name = _get_system_name(solar_system_source_id) - target_system_name = _get_system_name(solar_system_target_id) + source_system_name = get_system_name(solar_system_source_id) + target_system_name = get_system_name(solar_system_target_id) "[#{source_system_name}:#{target_system_name}] #{key} - #{inspect(value)}" end defp get_event_data(_name, data), do: Jason.encode!(data) - defp _get_system_name(solar_system_id) do + defp get_system_name(solar_system_id) do case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do {:ok, nil} -> solar_system_id 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 3ce4ca8a..f9b29b3c 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 @@ -180,6 +180,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do character_id: first_tracked_character.id, user_id: current_user.id, map_id: map_id, + solar_system_id: solar_system_id, signatures: added_signatures_eve_ids }) end @@ -190,6 +191,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do character_id: first_tracked_character.id, user_id: current_user.id, map_id: map_id, + solar_system_id: solar_system_id, signatures: removed_signatures_eve_ids }) end diff --git a/priv/repo/data/wormholes.json b/priv/repo/data/wormholes.json index f86c4e88..e030bd1d 100644 --- a/priv/repo/data/wormholes.json +++ b/priv/repo/data/wormholes.json @@ -222,7 +222,7 @@ { "mass_regen": 500000000, "dest": "hs", - "src": ["c3"], + "src": ["c3", "c4-shattered"], "static": true, "max_mass_per_jump": 300000000, "lifetime": "24", From fca98ec232564a64abdfa634d83a860ef62b0cd4 Mon Sep 17 00:00:00 2001 From: CI Date: Mon, 20 Jan 2025 10:43:19 +0000 Subject: [PATCH 02/18] chore: release version v1.42.4 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a0bdbad..d045a998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.42.4](https://github.com/wanderer-industries/wanderer/compare/v1.42.3...v1.42.4) (2025-01-20) + + + + +### Bug Fixes: + +* Fix system statics list (required EVE DB data update). Add system name to signature added/removed audit log + ## [v1.42.3](https://github.com/wanderer-industries/wanderer/compare/v1.42.2...v1.42.3) (2025-01-17) diff --git a/mix.exs b/mix.exs index bfa705c2..e0e45bab 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.42.3" + @version "1.42.4" def project do [ From d34509d7a0941cea390314a9cac7487c6627fcde Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Mon, 20 Jan 2025 23:59:58 +0100 Subject: [PATCH 03/18] fix(Map): Fix link signatures on splash. Fix deleting connection on locked system remove. --- assets/js/hooks/Mapper/components/map/Map.tsx | 41 ++++++++- .../components/map/hooks/useMapHandlers.ts | 2 +- .../components/mapWrapper/MapWrapper.tsx | 27 +++--- .../mapRootProvider/MapRootProvider.tsx | 3 + .../hooks/api/useCommandsSystems.ts | 14 ++- .../hooks/useMapRootHandlers.ts | 7 +- .../map/server/map_server_systems_impl.ex | 32 ++++--- .../map_systems_event_handler.ex | 92 ++++++++++--------- 8 files changed, 143 insertions(+), 75 deletions(-) diff --git a/assets/js/hooks/Mapper/components/map/Map.tsx b/assets/js/hooks/Mapper/components/map/Map.tsx index e0302649..49e00766 100644 --- a/assets/js/hooks/Mapper/components/map/Map.tsx +++ b/assets/js/hooks/Mapper/components/map/Map.tsx @@ -2,6 +2,7 @@ import { ForwardedRef, forwardRef, MouseEvent, useCallback, useEffect, useMemo } import ReactFlow, { Background, Edge, + EdgeChange, MiniMap, Node, NodeChange, @@ -83,6 +84,7 @@ interface MapCompProps { onCommand: OutCommandHandler; onSelectionChange: OnMapSelectionChange; onManualDelete(systems: string[]): void; + canRemoveConnection?(connectionId: string): boolean; onConnectionInfoClick?(e: SolarSystemConnection): void; onAddSystem?: OnMapAddSystemCallback; onSelectionContextMenu?: NodeSelectionMouseHandler; @@ -112,8 +114,9 @@ const MapComp = ({ isSoftBackground, theme, onAddSystem, + canRemoveConnection, }: MapCompProps) => { - const { getNode, getNodes } = useReactFlow(); + const { getEdge, getNode, getNodes } = useReactFlow(); const [nodes, , onNodesChange] = useNodesState>(initialNodes); const [edges, , onEdgesChange] = useEdgesState>(initialEdges); @@ -222,6 +225,40 @@ const MapComp = ({ [getNode, getNodes, onManualDelete, onNodesChange], ); + const handleEdgesChange = useCallback( + (changes: EdgeChange[]) => { + const nextChanges = changes.reduce((acc, change) => { + if (change.type !== 'remove') { + return [...acc, change]; + } + + if (canRemoveConnection?.(change.id)) { + return [...acc, change]; + } + + const edge = getEdge(change.id); + if (!edge) { + return [...acc, change]; + } + + const sourceNode = getNode(edge.source); + const targetNode = getNode(edge.target); + if (!sourceNode || !targetNode) { + return [...acc, change]; + } + + if (sourceNode.data.locked || targetNode.data.locked) { + return acc; + } + + return [...acc, change]; + }, [] as EdgeChange[]); + + onEdgesChange(nextChanges); + }, + [getEdge, getNode, onEdgesChange], + ); + useEffect(() => { update(x => ({ ...x, @@ -237,7 +274,7 @@ const MapComp = ({ nodes={nodes} edges={edges} onNodesChange={handleNodesChange} - onEdgesChange={onEdgesChange} + onEdgesChange={handleEdgesChange} onConnect={onConnect} // TODO we need save into session all of this // and on any action do either diff --git a/assets/js/hooks/Mapper/components/map/hooks/useMapHandlers.ts b/assets/js/hooks/Mapper/components/map/hooks/useMapHandlers.ts index dbb8eb60..849596e8 100644 --- a/assets/js/hooks/Mapper/components/map/hooks/useMapHandlers.ts +++ b/assets/js/hooks/Mapper/components/map/hooks/useMapHandlers.ts @@ -70,7 +70,7 @@ export const useMapHandlers = (ref: ForwardedRef, onSelectionChange setTimeout(() => addConnections(data as CommandAddConnections), 100); break; case Commands.removeConnections: - removeConnections(data as CommandRemoveConnections); + setTimeout(() => removeConnections(data as CommandRemoveConnections), 100); break; case Commands.charactersUpdated: charactersUpdated(data as CommandCharactersUpdated); diff --git a/assets/js/hooks/Mapper/components/mapWrapper/MapWrapper.tsx b/assets/js/hooks/Mapper/components/mapWrapper/MapWrapper.tsx index ceb076a7..bce3f1dd 100644 --- a/assets/js/hooks/Mapper/components/mapWrapper/MapWrapper.tsx +++ b/assets/js/hooks/Mapper/components/mapWrapper/MapWrapper.tsx @@ -14,9 +14,10 @@ import classes from './MapWrapper.module.scss'; import { Connections } from '@/hooks/Mapper/components/mapRootContent/components/Connections'; import { ContextMenuSystemMultiple, useContextMenuSystemMultipleHandlers } from '../contexts/ContextMenuSystemMultiple'; import { getSystemById } from '@/hooks/Mapper/helpers'; +import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts'; import { Node, XYPosition } from 'reactflow'; -import { Commands } from '@/hooks/Mapper/types/mapHandlers.ts'; +import { useCommandsSystems } from '@/hooks/Mapper/mapRootProvider/hooks/api'; import { emitMapEvent, useMapEventListener } from '@/hooks/Mapper/events'; import { STORED_INTERFACE_DEFAULT_VALUES } from '@/hooks/Mapper/mapRootProvider/MapRootProvider'; @@ -32,7 +33,7 @@ export const MapWrapper = () => { const { update, outCommand, - data: { selectedConnections, selectedSystems, hubs, systems }, + data: { selectedConnections, selectedSystems, hubs, systems, connections, linkSignatureToSystem }, interfaceSettings: { isShowMenu, isShowMinimap = STORED_INTERFACE_DEFAULT_VALUES.isShowMinimap, @@ -46,25 +47,19 @@ export const MapWrapper = () => { const { deleteSystems } = useDeleteSystems(); const { mapRef, runCommand } = useCommonMapEventProcessor(); + const { updateLinkSignatureToSystem } = useCommandsSystems(); const { open, ...systemContextProps } = useContextMenuSystemHandlers({ systems, hubs, outCommand }); const { handleSystemMultipleContext, ...systemMultipleCtxProps } = useContextMenuSystemMultipleHandlers(); const [openSettings, setOpenSettings] = useState(null); - const [openLinkSignatures, setOpenLinkSignatures] = useState(null); const [openCustomLabel, setOpenCustomLabel] = useState(null); const [openAddSystem, setOpenAddSystem] = useState(null); const [selectedConnection, setSelectedConnection] = useState(null); - const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems }); - ref.current = { selectedConnections, selectedSystems, systemContextProps, systems, deleteSystems }; + const ref = useRef({ selectedConnections, selectedSystems, systemContextProps, systems, connections, deleteSystems }); + ref.current = { selectedConnections, selectedSystems, systemContextProps, systems, connections, deleteSystems }; useMapEventListener(event => { - switch (event.name) { - case Commands.linkSignatureToSystem: - setOpenLinkSignatures(event.data); - return true; - } - runCommand(event); }); @@ -130,6 +125,11 @@ export const MapWrapper = () => { setOpenAddSystem(coordinates); }, []); + const canRemoveConnection = useCallback((connectionId: string) => { + const { connections } = ref.current; + return !connections.some(x => x.id === connectionId); + }, []); + const handleSubmitAddSystem: SearchOnSubmitCallback = useCallback( async item => { if (ref.current.systems.some(x => x.system_static_info.solar_system_id === item.value)) { @@ -166,6 +166,7 @@ export const MapWrapper = () => { isSoftBackground={isSoftBackground} theme={theme} onAddSystem={onAddSystem} + canRemoveConnection={canRemoveConnection} /> {openSettings != null && ( @@ -176,8 +177,8 @@ export const MapWrapper = () => { setOpenCustomLabel(null)} /> )} - {openLinkSignatures != null && ( - setOpenLinkSignatures(null)} /> + {linkSignatureToSystem != null && ( + updateLinkSignatureToSystem(null)} /> )} []; + linkSignatureToSystem: CommandLinkSignatureToSystem | null; }; const INITIAL_DATA: MapRootData = { @@ -34,6 +36,7 @@ const INITIAL_DATA: MapRootData = { selectedConnections: [], userPermissions: {}, options: {}, + linkSignatureToSystem: null, }; export enum InterfaceStoredSettingsProps { diff --git a/assets/js/hooks/Mapper/mapRootProvider/hooks/api/useCommandsSystems.ts b/assets/js/hooks/Mapper/mapRootProvider/hooks/api/useCommandsSystems.ts index a04bc92f..125df212 100644 --- a/assets/js/hooks/Mapper/mapRootProvider/hooks/api/useCommandsSystems.ts +++ b/assets/js/hooks/Mapper/mapRootProvider/hooks/api/useCommandsSystems.ts @@ -1,6 +1,11 @@ import { useMapRootState } from '@/hooks/Mapper/mapRootProvider'; import { useCallback, useRef } from 'react'; -import { CommandAddSystems, CommandRemoveSystems, CommandUpdateSystems } from '@/hooks/Mapper/types'; +import { + CommandAddSystems, + CommandRemoveSystems, + CommandUpdateSystems, + CommandLinkSignatureToSystem, +} from '@/hooks/Mapper/types'; import { useLoadSystemStatic } from '@/hooks/Mapper/mapRootProvider/hooks/useLoadSystemStatic.ts'; import { OutCommand } from '@/hooks/Mapper/types/mapHandlers.ts'; import { emitMapEvent } from '@/hooks/Mapper/events'; @@ -74,5 +79,10 @@ export const useCommandsSystems = () => { [outCommand], ); - return { addSystems, removeSystems, updateSystems, updateSystemSignatures }; + const updateLinkSignatureToSystem = useCallback(async (command: CommandLinkSignatureToSystem) => { + const { update } = ref.current; + update({ linkSignatureToSystem: command }, true); + }, []); + + return { addSystems, removeSystems, updateSystems, updateSystemSignatures, updateLinkSignatureToSystem }; }; diff --git a/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts b/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts index b53281b6..7ce3c325 100644 --- a/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts +++ b/assets/js/hooks/Mapper/mapRootProvider/hooks/useMapRootHandlers.ts @@ -32,7 +32,8 @@ import { emitMapEvent } from '@/hooks/Mapper/events'; export const useMapRootHandlers = (ref: ForwardedRef) => { const mapInit = useMapInit(); - const { addSystems, removeSystems, updateSystems, updateSystemSignatures } = useCommandsSystems(); + const { addSystems, removeSystems, updateSystems, updateSystemSignatures, updateLinkSignatureToSystem } = + useCommandsSystems(); const { addConnections, removeConnections, updateConnection } = useCommandsConnections(); const { charactersUpdated, characterAdded, characterRemoved, characterUpdated, presentCharacters } = useCommandsCharacters(); @@ -93,7 +94,9 @@ export const useMapRootHandlers = (ref: ForwardedRef) => { break; case Commands.linkSignatureToSystem: // USED - // do nothing here + setTimeout(() => { + updateLinkSignatureToSystem(data as CommandLinkSignatureToSystem); + }, 200); break; case Commands.centerSystem: // USED diff --git a/lib/wanderer_app/map/server/map_server_systems_impl.ex b/lib/wanderer_app/map/server/map_server_systems_impl.ex index 70283a36..e1cc2a1c 100644 --- a/lib/wanderer_app/map/server/map_server_systems_impl.ex +++ b/lib/wanderer_app/map/server/map_server_systems_impl.ex @@ -206,16 +206,24 @@ defmodule WandererApp.Map.Server.SystemsImpl do user_id, character_id ) do - removed_system_ids = + filtered_ids = removed_ids |> Enum.map(fn solar_system_id -> WandererApp.Map.find_system_by_location(map_id, %{solar_system_id: solar_system_id}) end) - |> Enum.filter(fn system -> not is_nil(system) end) - |> Enum.map(& &1.id) + |> Enum.filter(fn system -> not is_nil(system) && not system.locked end) + |> Enum.map(&{&1.solar_system_id, &1.id}) + + solar_system_ids_to_remove = + filtered_ids + |> Enum.map(fn {solar_system_id, _} -> solar_system_id end) + + system_ids_to_remove = + filtered_ids + |> Enum.map(fn {_, system_id} -> system_id end) connections_to_remove = - removed_ids + solar_system_ids_to_remove |> Enum.map(fn solar_system_id -> WandererApp.Map.find_connections(map_id, solar_system_id) end) @@ -223,9 +231,9 @@ defmodule WandererApp.Map.Server.SystemsImpl do |> Enum.uniq_by(& &1.id) :ok = WandererApp.Map.remove_connections(map_id, connections_to_remove) - :ok = WandererApp.Map.remove_systems(map_id, removed_ids) + :ok = WandererApp.Map.remove_systems(map_id, solar_system_ids_to_remove) - removed_ids + solar_system_ids_to_remove |> Enum.each(fn solar_system_id -> map_id |> WandererApp.MapSystemRepo.remove_from_map(solar_system_id) @@ -245,7 +253,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do WandererApp.MapConnectionRepo.destroy(map_id, connection) end) - removed_ids + solar_system_ids_to_remove |> Enum.map(fn solar_system_id -> WandererApp.Api.MapSystemSignature.by_linked_system_id!(solar_system_id) end) @@ -259,7 +267,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do end) linked_system_ids = - removed_system_ids + system_ids_to_remove |> Enum.map(fn system_id -> WandererApp.Api.MapSystemSignature.by_system_id!(system_id) |> Enum.filter(fn s -> not is_nil(s.linked_system_id) end) @@ -276,10 +284,10 @@ defmodule WandererApp.Map.Server.SystemsImpl do }) end) - @ddrt.delete(removed_ids, rtree_name) + @ddrt.delete(solar_system_ids_to_remove, rtree_name) Impl.broadcast!(map_id, :remove_connections, connections_to_remove) - Impl.broadcast!(map_id, :systems_removed, removed_ids) + Impl.broadcast!(map_id, :systems_removed, solar_system_ids_to_remove) case not is_nil(user_id) do true -> @@ -288,12 +296,12 @@ defmodule WandererApp.Map.Server.SystemsImpl do character_id: character_id, user_id: user_id, map_id: map_id, - solar_system_ids: removed_ids + solar_system_ids: solar_system_ids_to_remove }) :telemetry.execute( [:wanderer_app, :map, :systems, :remove], - %{count: removed_ids |> Enum.count()} + %{count: solar_system_ids_to_remove |> Enum.count()} ) :ok diff --git a/lib/wanderer_app_web/live/maps/event_handlers/map_systems_event_handler.ex b/lib/wanderer_app_web/live/maps/event_handlers/map_systems_event_handler.ex index 0b000b9e..971a605e 100644 --- a/lib/wanderer_app_web/live/maps/event_handlers/map_systems_event_handler.ex +++ b/lib/wanderer_app_web/live/maps/event_handlers/map_systems_event_handler.ex @@ -21,57 +21,63 @@ defmodule WandererAppWeb.MapSystemsEventHandler do |> MapEventHandler.push_map_event("remove_systems", solar_system_ids) def handle_server_event( - %{ - event: :maybe_select_system, - payload: %{ - character_id: character_id, - solar_system_id: solar_system_id - } - }, - %{assigns: %{current_user: current_user, map_id: map_id, map_user_settings: map_user_settings}} = socket - ) do + %{ + event: :maybe_select_system, + payload: %{ + character_id: character_id, + solar_system_id: solar_system_id + } + }, + %{ + assigns: %{ + current_user: current_user, + map_id: map_id, + map_user_settings: map_user_settings + } + } = socket + ) do + is_user_character = + current_user.characters + |> Enum.map(& &1.id) + |> Enum.member?(character_id) - is_user_character = - current_user.characters - |> Enum.map(& &1.id) - |> Enum.member?(character_id) + is_select_on_spash = + map_user_settings + |> WandererApp.MapUserSettingsRepo.to_form_data!() + |> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash") - is_select_on_spash = - map_user_settings - |> WandererApp.MapUserSettingsRepo.to_form_data!() - |> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash") + is_followed = + case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character_id) do + {:ok, setting} -> setting.followed == true + _ -> false + end - is_followed = - case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character_id) do - {:ok, setting} -> setting.followed == true - _ -> false - end + must_select? = is_user_character && (is_select_on_spash || is_followed) - must_select? = is_user_character && (is_select_on_spash || is_followed) - if not must_select? do + if not must_select? do + socket + else + # Check if we already selected this exact system for this char: + last_selected = + WandererApp.Cache.lookup!( + "char:#{character_id}:map:#{map_id}:last_selected_system_id", + nil + ) + + if last_selected == solar_system_id do + # same system => skip socket else - # Check if we already selected this exact system for this char: - last_selected = - WandererApp.Cache.lookup!( - "char:#{character_id}:map:#{map_id}:last_selected_system_id", - nil - ) + # new system => update cache + push event + WandererApp.Cache.put( + "char:#{character_id}:map:#{map_id}:last_selected_system_id", + solar_system_id + ) - if last_selected == solar_system_id do - # same system => skip - socket - else - # new system => update cache + push event - WandererApp.Cache.put( - "char:#{character_id}:map:#{map_id}:last_selected_system_id", - solar_system_id - ) - - socket - |> MapEventHandler.push_map_event("select_system", solar_system_id) - end + socket + |> MapEventHandler.push_map_event("select_system", solar_system_id) end + end end def handle_server_event(%{event: :kills_updated, payload: kills}, socket) do From 4b1aef8dd9de9ab8fa7fdb6e05795843c26cf061 Mon Sep 17 00:00:00 2001 From: CI Date: Mon, 20 Jan 2025 23:01:42 +0000 Subject: [PATCH 04/18] chore: release version v1.42.5 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d045a998..0c567ba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.42.5](https://github.com/wanderer-industries/wanderer/compare/v1.42.4...v1.42.5) (2025-01-20) + + + + +### Bug Fixes: + +* Map: Fix link signatures on splash. Fix deleting connection on locked system remove. + ## [v1.42.4](https://github.com/wanderer-industries/wanderer/compare/v1.42.3...v1.42.4) (2025-01-20) diff --git a/mix.exs b/mix.exs index e0e45bab..6e6b0d6e 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.42.4" + @version "1.42.5" def project do [ From 230016b90f4cfac0e5e0a67192d5f9a28bf19469 Mon Sep 17 00:00:00 2001 From: guarzo Date: Mon, 20 Jan 2025 18:03:31 -0500 Subject: [PATCH 05/18] feat: add news post for structures widget (#131) --- .../01-20-structure-widget/enable-widget.png | Bin 0 -> 34502 bytes .../news/01-20-structure-widget/widget.png | Bin 0 -> 23864 bytes priv/posts/2025/01-20-structure-widget.md | 151 ++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100755 assets/static/images/news/01-20-structure-widget/enable-widget.png create mode 100755 assets/static/images/news/01-20-structure-widget/widget.png create mode 100644 priv/posts/2025/01-20-structure-widget.md diff --git a/assets/static/images/news/01-20-structure-widget/enable-widget.png b/assets/static/images/news/01-20-structure-widget/enable-widget.png new file mode 100755 index 0000000000000000000000000000000000000000..7fad5fa28f57eae4a029e5475346c61dd9f3ba3e GIT binary patch literal 34502 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 literal 0 HcmV?d00001 diff --git a/priv/posts/2025/01-20-structure-widget.md b/priv/posts/2025/01-20-structure-widget.md new file mode 100644 index 00000000..08c27c30 --- /dev/null +++ b/priv/posts/2025/01-20-structure-widget.md @@ -0,0 +1,151 @@ +%{ +title: "Managing Upwell Structures & Timers with the Structures Widget", +author: "Wanderer Team", +cover_image_uri: "/images/news/structure-widget/cover.png", +tags: ~w(interface guide map structures), +description: "Learn how to track structure information using the Structures Widget." +} + +--- + +### Introduction + +Upwell structures like **Astrahus**, **Athanor**, and more are key strategic points in EVE Online. Staying informed about their statuses—whether they’re anchoring, powered, or reinforced—helps you plan defenses, coordinate attacks, and align with allies. Our **Structures Widget** simplifies the process by allowing you to: + +- Copy structure information directly from the in-game Directional Scanner (`D-Scan`) and paste it into the widget. +- Keep track of **anchoring** or **reinforced** timers, including exact vulnerability windows. +- Share real-time data across the map with your corporation or alliance, ensuring everyone is on the same page. + +In this guide, we’ll explore how to enable the Structures Widget, manage structure data, and make use of the built-in API for remote structure updates. + +--- + +### 1. Enabling the Structure Widget + +![Enabling the Structures Widget](/images/news/structure-widget/enable-widget.png "Enable Structures Widget") + +1. **Open the Map:** +2. **Locate the Widget Settings:** By default, the structure widget panel is not visible. Enable it by going to menu -> map settings -> widgets. +3. **Add the Structures Widget:** Click the checkbox for **Structures** from the list of available widgets. + +> **Tip:** Rearrange your widgets by dragging them around the panel to suit your workflow. + +--- + +### 2. Overview of the Structures Widget + +![Structures Widget Overview](/images/news/01-20-structure-widget/widget.png "Structures Widget") + +Once enabled, the **Structures Widget** appears in the map. It shows: + +- **Structure Type** (Astrahus, Fortizar, etc.) +- **Structure Name** (auto-detected if you paste from D-Scan) +- **Owner** (Corporation ticker) +- **Status** (Powered, Anchoring, Low Power, Reinforced, etc.) +- **Timer** (Reinforced or anchoring end time) + +You can **click** or **double-click** on an entry to edit details like the structure’s owner or add notes about the structure’s purpose or location. + +--- + +### 3. Adding Structures via Copy & Paste + +A fast way to add structure data is by copying from in-game D-Scan or show-info panels: + +1. **In EVE Online:** Open the D-Scan window or structure context menu, select the relevant lines of text, and press **Ctrl + C**. +2. **In the Widget:** Focus on the Structures Widget, click in the widget area, and press **Ctrl + V** to paste or use the **blue** add structure info button. +3. The widget automatically parses the structure names and types. You can also add owners and notes manually. + +This eliminates manual typing and reduces the chance of errors, especially useful when scanning multiple systems. + +--- + +### 4. Tracking Reinforced Timers + +When a structure is in a **Reinforced** or **Anchoring** state, we have a timer to note when it becomes vulnerable or completes anchoring: + +- **Timer Field:** If the structure’s status is set to “Reinforced” or “Anchoring,” the widget enables a **Calendar** pop-up where you can set the _end time_. + +Keep your fleet prepared by referencing this schedule. When the timer hits zero, the structure becomes vulnerable (or finishes anchoring). + +--- + +### 5. Editing and Deleting Structures + +1. **Single-click** a structure entry to select it. +2. Press **Delete** (or **Backspace**) to remove it entirely—useful when clearing out old data or removing outdated structures. +3. **Double-click** to open the **Edit Dialog**: + - Change **Name**, **Owner**, or **Status**. + - Update or remove **Reinforced** timers. + - Add or edit **Notes**. + +Any changes made here are immediately visible to other map users. + +--- + +### 6. API Integration for Automated Timers + +Beyond the in-app widget, there is a dedicated API endpoint to fetch or update structure timers programmatically. This allows advanced users and third-party applications to seamlessly incorporate structure data. + +**Example API Request/Response**: + +```bash +curl -H "Authorization: Bearer YOUR_API_TOKEN" \ +"https://wanderer.yourdomain.space/api/map/structure-timers?slug=yourmap" + + "data": [ + { + "name": "Overlook Hotel", + "status": "Reinforced", + "notes": null, + "owner_id": null, + "solar_system_id": 31000515, + "solar_system_name": "J114942", + "character_eve_id": "2122839817", + "system_id": "4865aec4-b69d-4524-91d3-250b0556322b", + "end_time": "2025-01-22T23:42:03.000000Z", + "owner_name": null, + "owner_ticker": null, + "structure_type": "Astrahus", + "structure_type_id": "35832" + }, + { + "name": "Some Structure", + "status": "Reinforced", + "notes": null, + "owner_id": null, + "solar_system_id": 3100229, + "solar_system_name": "somecustomname", + "character_eve_id": "some name", + "system_id": "ae779ed6-92b3-4349-899d-f1bdf299082f", + "end_time": "2025-01-16T03:04:00.000000Z", + "owner_name": null, + "owner_ticker": null, + "structure_type": "Athanor", + "structure_type_id": "35835" + } + ] +``` + + +With this API, you could, for example, build automated pings on Slack/Discord when timers are about to expire or display status updates on a custom web dashboard. + +> **Note:** Ensure your API token (`Bearer YOUR_API_TOKEN`) matches the api key generated for you map. + +--- + +### 7. Best Practices & Tips + +- **Keep Data Fresh:** Update timers as soon as possible after a structure enters reinforcement. This keeps your corporation or alliance fully informed. +- **Use Notes Effectively:** Add details such as final reinforcement phases or relevant system intel (e.g., known hostiles, safe spots) to help allies plan more effectively. + +--- + +## Conclusion + +The **Structures Widget** is your central hub for monitoring, updating, and sharing information about Upwell structures across New Eden. From real-time timer tracking to simple copy-and-paste integration with D-Scan, this widget streamlines group operations and cuts down on manual data entry. + +Whether you’re a solo explorer managing a personal citadel network or a fleet commander overseeing multiple staging systems, the Structures Widget and its accompanying API ensure you’ll always have up-to-date intel on the structures that matter most. + +Fly safe, +**The Wanderer Team** From 6aac698cd88e9d750d1befd8539ca0aa40025c3e Mon Sep 17 00:00:00 2001 From: CI Date: Mon, 20 Jan 2025 23:04:11 +0000 Subject: [PATCH 06/18] chore: release version v1.43.0 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c567ba9..1f78d911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.43.0](https://github.com/wanderer-industries/wanderer/compare/v1.42.5...v1.43.0) (2025-01-20) + + + + +### Features: + +* add news post for structures widget (#131) + ## [v1.42.5](https://github.com/wanderer-industries/wanderer/compare/v1.42.4...v1.42.5) (2025-01-20) diff --git a/mix.exs b/mix.exs index 6e6b0d6e..414d3a5c 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.42.5" + @version "1.43.0" def project do [ From 869c25cd60f2fbf8e16463c60a20ca308ccee234 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Tue, 21 Jan 2025 00:27:49 +0100 Subject: [PATCH 07/18] chore: release version v1.42.4 --- .../live/maps/event_handlers/map_core_event_handler.ex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/wanderer_app_web/live/maps/event_handlers/map_core_event_handler.ex b/lib/wanderer_app_web/live/maps/event_handlers/map_core_event_handler.ex index f2fa2e90..c886d577 100644 --- a/lib/wanderer_app_web/live/maps/event_handlers/map_core_event_handler.ex +++ b/lib/wanderer_app_web/live/maps/event_handlers/map_core_event_handler.ex @@ -270,6 +270,8 @@ defmodule WandererAppWeb.MapCoreEventHandler do current_user.characters |> Enum.map(& &1.id) ) + {:ok, map_user_settings} = WandererApp.MapUserSettingsRepo.get(map_id, current_user.id) + {:ok, character_settings} = case WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) do {:ok, settings} -> {:ok, settings} @@ -302,6 +304,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do socket |> assign( map_id: map_id, + map_user_settings: map_user_settings, page_title: map_name, user_permissions: user_permissions, tracked_character_ids: tracked_character_ids, @@ -334,7 +337,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do } = socket ) do with {:ok, _} <- current_user |> WandererApp.Api.User.update_last_map(%{last_map_id: map_id}), - {:ok, map_user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user.id), {:ok, tracked_map_characters} <- WandererApp.Maps.get_tracked_map_characters(map_id, current_user), {:ok, characters_limit} <- map_id |> WandererApp.Map.get_characters_limit(), @@ -414,7 +416,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do socket |> map_start(%{ map_id: map_id, - map_user_settings: map_user_settings, user_characters: user_character_eve_ids, initial_data: initial_data, events: events @@ -437,7 +438,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do socket, %{ map_id: map_id, - map_user_settings: map_user_settings, user_characters: user_character_eve_ids, initial_data: initial_data, events: events @@ -468,7 +468,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do socket |> assign( map_loaded?: true, - map_user_settings: map_user_settings, user_characters: user_character_eve_ids, has_tracked_characters?: has_tracked_characters? ) From 4e526040bf427b4a1ac4f35cafff97791c6596c5 Mon Sep 17 00:00:00 2001 From: CI Date: Mon, 20 Jan 2025 23:28:45 +0000 Subject: [PATCH 08/18] chore: release version v1.43.1 --- CHANGELOG.md | 5 +++++ mix.exs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f78d911..c1b4ed2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ +## [v1.43.1](https://github.com/wanderer-industries/wanderer/compare/v1.43.0...v1.43.1) (2025-01-20) + + + + ## [v1.43.0](https://github.com/wanderer-industries/wanderer/compare/v1.42.5...v1.43.0) (2025-01-20) diff --git a/mix.exs b/mix.exs index 414d3a5c..61c4abd3 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.43.0" + @version "1.43.1" def project do [ From a5c346627a87ca5e724b12781743ecc440634e5b Mon Sep 17 00:00:00 2001 From: guarzo Date: Tue, 21 Jan 2025 02:49:58 -0500 Subject: [PATCH 09/18] fix: prevent constraint error for follow/toggle (#132) --- .../live/maps/event_handlers/map_characters_event_handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wanderer_app_web/live/maps/event_handlers/map_characters_event_handler.ex b/lib/wanderer_app_web/live/maps/event_handlers/map_characters_event_handler.ex index 065cda66..7ad6e84a 100644 --- a/lib/wanderer_app_web/live/maps/event_handlers/map_characters_event_handler.ex +++ b/lib/wanderer_app_web/live/maps/event_handlers/map_characters_event_handler.ex @@ -213,7 +213,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do s.character_id in user_char_ids end) - existing = Enum.find(my_settings, &(&1.character_id == clicked_char_id)) + existing = Enum.find(all_settings, &(&1.character_id == clicked_char_id)) {:ok, target_setting} = if not is_nil(existing) do From 0c4818950356104ab3565935c9c62d95fa7471cc Mon Sep 17 00:00:00 2001 From: CI Date: Tue, 21 Jan 2025 07:50:24 +0000 Subject: [PATCH 10/18] chore: release version v1.43.2 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b4ed2a..8c7c0cb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.43.2](https://github.com/wanderer-industries/wanderer/compare/v1.43.1...v1.43.2) (2025-01-21) + + + + +### Bug Fixes: + +* prevent constraint error for follow/toggle (#132) + ## [v1.43.1](https://github.com/wanderer-industries/wanderer/compare/v1.43.0...v1.43.1) (2025-01-20) diff --git a/mix.exs b/mix.exs index 61c4abd3..620c606d 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.43.1" + @version "1.43.2" def project do [ From e094378dc5334bc482df361d9affd31de6db2911 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Tue, 21 Jan 2025 09:46:09 +0100 Subject: [PATCH 11/18] chore: release version v1.43.2 --- .../{widget.png => cover.png} | Bin priv/posts/2025/01-20-structure-widget.md | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename assets/static/images/news/01-20-structure-widget/{widget.png => cover.png} (100%) diff --git a/assets/static/images/news/01-20-structure-widget/widget.png b/assets/static/images/news/01-20-structure-widget/cover.png similarity index 100% rename from assets/static/images/news/01-20-structure-widget/widget.png rename to assets/static/images/news/01-20-structure-widget/cover.png diff --git a/priv/posts/2025/01-20-structure-widget.md b/priv/posts/2025/01-20-structure-widget.md index 08c27c30..032a09ba 100644 --- a/priv/posts/2025/01-20-structure-widget.md +++ b/priv/posts/2025/01-20-structure-widget.md @@ -1,7 +1,7 @@ %{ title: "Managing Upwell Structures & Timers with the Structures Widget", author: "Wanderer Team", -cover_image_uri: "/images/news/structure-widget/cover.png", +cover_image_uri: "/images/news/01-20-structure-widget/cover.png", tags: ~w(interface guide map structures), description: "Learn how to track structure information using the Structures Widget." } @@ -22,7 +22,7 @@ In this guide, we’ll explore how to enable the Structures Widget, manage struc ### 1. Enabling the Structure Widget -![Enabling the Structures Widget](/images/news/structure-widget/enable-widget.png "Enable Structures Widget") +![Enabling the Structures Widget](/images/news/01-20-structure-widget/enable-widget.png "Enable Structures Widget") 1. **Open the Map:** 2. **Locate the Widget Settings:** By default, the structure widget panel is not visible. Enable it by going to menu -> map settings -> widgets. @@ -34,7 +34,7 @@ In this guide, we’ll explore how to enable the Structures Widget, manage struc ### 2. Overview of the Structures Widget -![Structures Widget Overview](/images/news/01-20-structure-widget/widget.png "Structures Widget") +![Structures Widget Overview](/images/news/01-20-structure-widget/cover.png "Structures Widget") Once enabled, the **Structures Widget** appears in the map. It shows: From 30b0556d478f55b11397d10b257e0eacbd3b3f11 Mon Sep 17 00:00:00 2001 From: CI Date: Tue, 21 Jan 2025 08:46:40 +0000 Subject: [PATCH 12/18] chore: release version v1.43.3 --- CHANGELOG.md | 5 +++++ mix.exs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c7c0cb7..756ec709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ +## [v1.43.3](https://github.com/wanderer-industries/wanderer/compare/v1.43.2...v1.43.3) (2025-01-21) + + + + ## [v1.43.2](https://github.com/wanderer-industries/wanderer/compare/v1.43.1...v1.43.2) (2025-01-21) diff --git a/mix.exs b/mix.exs index 620c606d..d9d25c1a 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.43.2" + @version "1.43.3" def project do [ From 4633d26517d75a6eaef7fc766e354e7467cc83fa Mon Sep 17 00:00:00 2001 From: guarzo Date: Tue, 21 Jan 2025 03:47:40 -0500 Subject: [PATCH 13/18] fix: improve structure widget styling (#127) --- .../SystemStructures/SystemStructures.tsx | 5 ++ .../SystemStructuresContent.module.scss | 40 +++++++----- .../SystemStructuresContent.tsx | 64 +++++++++++++++++-- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructures.tsx b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructures.tsx index 13a22cb6..3aba636f 100644 --- a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructures.tsx +++ b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructures.tsx @@ -70,6 +70,11 @@ export const SystemStructures: React.FC = () => { tr > td { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.Tooltip { + white-space: pre-line; + line-height: 1.2rem; +} diff --git a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresContent/SystemStructuresContent.tsx b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresContent/SystemStructuresContent.tsx index 4ce0a1eb..bc62c8b6 100644 --- a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresContent/SystemStructuresContent.tsx +++ b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresContent/SystemStructuresContent.tsx @@ -63,6 +63,7 @@ export const SystemStructuresContent: React.FC = ( size="small" sortMode="single" rowHover + style={{ tableLayout: 'fixed', width: '100%' }} onRowClick={handleRowClick} onRowDoubleClick={handleRowDoubleClick} rowClassName={rowData => { @@ -74,11 +75,56 @@ export const SystemStructuresContent: React.FC = ( ); }} > - - - - - + + + + + ( = ( }} /> )} - style={{ width: '40px', textAlign: 'center' }} + style={{ + width: '40px', + textAlign: 'center', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }} /> From 05c3d20e567f70888819ed8911a32f7787dc3af6 Mon Sep 17 00:00:00 2001 From: CI Date: Tue, 21 Jan 2025 08:48:20 +0000 Subject: [PATCH 14/18] chore: release version v1.43.4 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 756ec709..1185b54f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.43.4](https://github.com/wanderer-industries/wanderer/compare/v1.43.3...v1.43.4) (2025-01-21) + + + + +### Bug Fixes: + +* improve structure widget styling (#127) + ## [v1.43.3](https://github.com/wanderer-industries/wanderer/compare/v1.43.2...v1.43.3) (2025-01-21) diff --git a/mix.exs b/mix.exs index d9d25c1a..3ec5e0bc 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.43.3" + @version "1.43.4" def project do [ From da2e12bdd1399df78a5442f1ca1346a66ea7ac13 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Wed, 22 Jan 2025 18:06:03 +0100 Subject: [PATCH 15/18] fix(Audit): Fix signature added/removed system name --- .../maps/event_handlers/map_signatures_event_handler.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 f9b29b3c..63232faa 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 @@ -130,7 +130,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do if delete_connection_with_sigs && not is_nil(s.linked_system_id) do map_id |> WandererApp.Map.Server.delete_connection(%{ - solar_system_source_id: solar_system_id |> String.to_integer(), + solar_system_source_id: system.solar_system_id, solar_system_target_id: s.linked_system_id }) end @@ -180,7 +180,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do character_id: first_tracked_character.id, user_id: current_user.id, map_id: map_id, - solar_system_id: solar_system_id, + solar_system_id: system.solar_system_id, signatures: added_signatures_eve_ids }) end @@ -191,7 +191,7 @@ defmodule WandererAppWeb.MapSignaturesEventHandler do character_id: first_tracked_character.id, user_id: current_user.id, map_id: map_id, - solar_system_id: solar_system_id, + solar_system_id: system.solar_system_id, signatures: removed_signatures_eve_ids }) end From 5f67cb1dd79d0502350f5d7dfd7688a574094d6a Mon Sep 17 00:00:00 2001 From: CI Date: Wed, 22 Jan 2025 17:07:37 +0000 Subject: [PATCH 16/18] chore: release version v1.43.5 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1185b54f..8251d933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.43.5](https://github.com/wanderer-industries/wanderer/compare/v1.43.4...v1.43.5) (2025-01-22) + + + + +### Bug Fixes: + +* Audit: Fix signature added/removed system name + ## [v1.43.4](https://github.com/wanderer-industries/wanderer/compare/v1.43.3...v1.43.4) (2025-01-21) diff --git a/mix.exs b/mix.exs index 3ec5e0bc..1e2d1331 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.43.4" + @version "1.43.5" def project do [ From dac480b059a5a30c02df8112be6e5f7a8dba44a4 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Wed, 22 Jan 2025 18:40:37 +0100 Subject: [PATCH 17/18] fix(Widgets): Fix widgets not visible on map --- .../Mapper/components/ui-kit/WindowManager/WindowManager.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/js/hooks/Mapper/components/ui-kit/WindowManager/WindowManager.tsx b/assets/js/hooks/Mapper/components/ui-kit/WindowManager/WindowManager.tsx index ceda6eaa..edd790fa 100644 --- a/assets/js/hooks/Mapper/components/ui-kit/WindowManager/WindowManager.tsx +++ b/assets/js/hooks/Mapper/components/ui-kit/WindowManager/WindowManager.tsx @@ -384,6 +384,10 @@ export const WindowManager: React.FC = ({ windows: initialWi next.position.y = container.clientHeight - next.size.height - SNAP_GAP; } + if (next.position.y < 0) { + next.position.y = 0; + } + return next; }); }); From 63571a462fa475f6478ea86d728bfa562ab4d76c Mon Sep 17 00:00:00 2001 From: CI Date: Wed, 22 Jan 2025 17:41:42 +0000 Subject: [PATCH 18/18] chore: release version v1.43.6 --- CHANGELOG.md | 9 +++++++++ mix.exs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8251d933..10905feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ +## [v1.43.6](https://github.com/wanderer-industries/wanderer/compare/v1.43.5...v1.43.6) (2025-01-22) + + + + +### Bug Fixes: + +* Widgets: Fix widgets not visible on map + ## [v1.43.5](https://github.com/wanderer-industries/wanderer/compare/v1.43.4...v1.43.5) (2025-01-22) diff --git a/mix.exs b/mix.exs index 1e2d1331..dc2ff741 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do @source_url "https://github.com/wanderer-industries/wanderer" - @version "1.43.5" + @version "1.43.6" def project do [