-
-
-
+
+ {/*Left column*/}
+
+ {isWormhole && info?.locked_at && (
+
+
+ {info.locked_by_name && by {info.locked_by_name}}
+
+ )}
-
+ {/*Right column*/}
+
+
+
+
+
+
+
+
{/*Left column*/}
diff --git a/assets/js/hooks/Mapper/types/connectionPassages.ts b/assets/js/hooks/Mapper/types/connectionPassages.ts
index 6cf06e1c..fb1770ab 100644
--- a/assets/js/hooks/Mapper/types/connectionPassages.ts
+++ b/assets/js/hooks/Mapper/types/connectionPassages.ts
@@ -21,6 +21,8 @@ export type PassageWithSourceTarget = {
export type ConnectionInfoOutput = {
marl_eol_time: string;
+ locked_at: string | null;
+ locked_by_name: string | null;
};
export type ConnectionOutput = {
diff --git a/lib/wanderer_app/api/map_connection.ex b/lib/wanderer_app/api/map_connection.ex
index 7951113e..2e2ccf84 100644
--- a/lib/wanderer_app/api/map_connection.ex
+++ b/lib/wanderer_app/api/map_connection.ex
@@ -20,7 +20,7 @@ defmodule WandererApp.Api.MapConnection do
json_api do
type "map_connections"
- includes([:map])
+ includes([:map, :locked_by])
default_fields([
:solar_system_source,
@@ -32,6 +32,7 @@ defmodule WandererApp.Api.MapConnection do
:wormhole_type,
:count_of_passage,
:locked,
+ :locked_at,
:custom_info
])
@@ -274,6 +275,11 @@ defmodule WandererApp.Api.MapConnection do
public? true
end
+ attribute :locked_at, :utc_datetime_usec do
+ public? true
+ allow_nil? true
+ end
+
attribute :custom_info, :string do
public? true
allow_nil? true
@@ -288,5 +294,9 @@ defmodule WandererApp.Api.MapConnection do
attribute_writable? true
public? true
end
+
+ belongs_to :locked_by, WandererApp.Api.Character do
+ public? true
+ end
end
end
diff --git a/lib/wanderer_app/map/server/map_server_connections_impl.ex b/lib/wanderer_app/map/server/map_server_connections_impl.ex
index 80ce2540..bc6f423e 100644
--- a/lib/wanderer_app/map/server/map_server_connections_impl.ex
+++ b/lib/wanderer_app/map/server/map_server_connections_impl.ex
@@ -213,9 +213,34 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
solar_system_target_id
)
|> case do
- {:ok, %{id: connection_id}} ->
+ {:ok, %{id: connection_id} = connection} ->
connection_mark_eol_time = get_connection_mark_eol_time(map_id, connection_id, nil)
- {:ok, %{marl_eol_time: connection_mark_eol_time}}
+ locked_info = get_connection_locked_info(map_id, connection_id)
+
+ locked_by_name =
+ case locked_info do
+ nil ->
+ nil
+
+ %{locked_by_id: locked_by_id} ->
+ case WandererApp.Api.Character.by_id(locked_by_id) do
+ {:ok, character} -> character.name
+ _ -> nil
+ end
+ end
+
+ locked_at =
+ case locked_info do
+ nil -> nil
+ %{locked_at: locked_at} -> locked_at
+ end
+
+ {:ok,
+ %{
+ marl_eol_time: connection_mark_eol_time,
+ locked_at: locked_at,
+ locked_by_name: locked_by_name
+ }}
_ ->
{:error, :not_found}
@@ -293,8 +318,27 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
def update_connection_locked(
map_id,
connection_update
- ),
- do: update_connection(map_id, :update_locked, [:locked], connection_update)
+ ) do
+ update_connection(map_id, :update_locked, [:locked], connection_update, fn
+ %{locked: old_locked}, %{id: connection_id, locked: new_locked} = _ ->
+ case new_locked do
+ true ->
+ if not old_locked do
+ character_id = Map.get(connection_update, :character_id)
+
+ WandererApp.Cache.put(
+ "map_#{map_id}:conn_#{connection_id}:locked_info",
+ %{locked_by_id: character_id, locked_at: DateTime.utc_now()}
+ )
+ end
+
+ _ ->
+ if old_locked do
+ WandererApp.Cache.delete("map_#{map_id}:conn_#{connection_id}:locked_info")
+ end
+ end
+ end)
+ end
def update_connection_custom_info(
map_id,
@@ -954,6 +998,16 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
end
end
+ def get_connection_locked_info(map_id, connection_id) do
+ case WandererApp.Cache.get("map_#{map_id}:conn_#{connection_id}:locked_info") do
+ nil ->
+ nil
+
+ value ->
+ value
+ end
+ end
+
defp find_solar_system_jump(from_solar_system_id, to_solar_system_id) do
case WandererApp.CachedInfo.get_solar_system_jump(from_solar_system_id, to_solar_system_id) do
{:ok, jump} when not is_nil(jump) -> {:ok, [jump]}
@@ -1033,6 +1087,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
})
WandererApp.Cache.delete("map_#{map_id}:conn_#{connection.id}:start_time")
+ WandererApp.Cache.delete("map_#{map_id}:conn_#{connection.id}:locked_info")
# Clear linked_sig_eve_id on target system when connection is deleted
# This ensures old signatures become orphaned and won't affect future connections
diff --git a/lib/wanderer_app_web/live/map/event_handlers/map_connections_event_handler.ex b/lib/wanderer_app_web/live/map/event_handlers/map_connections_event_handler.ex
index a0a61360..e02b5a4e 100644
--- a/lib/wanderer_app_web/live/map/event_handlers/map_connections_event_handler.ex
+++ b/lib/wanderer_app_web/live/map/event_handlers/map_connections_event_handler.ex
@@ -240,6 +240,7 @@ defmodule WandererAppWeb.MapConnectionsEventHandler do
solar_system_target_id: solar_system_target_id_int
}
|> Map.put_new(key_atom, value)
+ |> Map.put(:character_id, main_character_id)
])
{:noreply, socket}
diff --git a/priv/repo/migrations/20260425000000_add_map_connection_locked_by.exs b/priv/repo/migrations/20260425000000_add_map_connection_locked_by.exs
new file mode 100644
index 00000000..0c4cf396
--- /dev/null
+++ b/priv/repo/migrations/20260425000000_add_map_connection_locked_by.exs
@@ -0,0 +1,21 @@
+defmodule WandererApp.Repo.Migrations.AddMapConnectionLockedBy do
+ @moduledoc """
+ Add locked_by and locked_at tracking to map_chain_v1 connections
+ """
+
+ use Ecto.Migration
+
+ def up do
+ alter table(:map_chain_v1) do
+ add :locked_by_id, :binary_id
+ add :locked_at, :utc_datetime_usec
+ end
+ end
+
+ def down do
+ alter table(:map_chain_v1) do
+ remove :locked_by_id
+ remove :locked_at
+ end
+ end
+end