feat: track Save Mass (locked) status on connections

This commit is contained in:
Arkezar
2026-04-25 18:27:04 +00:00
parent 16e86b0bda
commit 2f86fc9f17
6 changed files with 122 additions and 21 deletions
@@ -196,23 +196,35 @@ export const Connections = ({ selectedConnection, onHide }: OnTheMapProps) => {
{/* Connection Info */}
<div className="px-2 flex flex-col gap-2">
{/* Connection Info Row */}
<InfoDrawer title="Connection" rightSide>
<div className="flex justify-end gap-2 items-center">
<SystemView
showCustomName
systemId={cnInfo.source}
className={clsx(classes.InfoTextSize, 'select-none text-center')}
hideRegion
/>
<span className="pi pi-angle-double-right text-stone-500 text-[15px]"></span>
<SystemView
showCustomName
systemId={cnInfo.target}
className={clsx(classes.InfoTextSize, 'select-none text-center')}
hideRegion
/>
<div className="flex justify-between gap-2">
{/*Left column*/}
<div>
{isWormhole && info?.locked_at && (
<InfoDrawer title="Save Mass">
<TimeAgo timestamp={info.locked_at} />
{info.locked_by_name && <span className="text-neutral-400"> by {info.locked_by_name}</span>}
</InfoDrawer>
)}
</div>
</InfoDrawer>
{/*Right column*/}
<InfoDrawer title="Connection" rightSide>
<div className="flex justify-end gap-2 items-center">
<SystemView
showCustomName
systemId={cnInfo.source}
className={clsx(classes.InfoTextSize, 'select-none text-center')}
hideRegion
/>
<span className="pi pi-angle-double-right text-stone-500 text-[15px]"></span>
<SystemView
showCustomName
systemId={cnInfo.target}
className={clsx(classes.InfoTextSize, 'select-none text-center')}
hideRegion
/>
</div>
</InfoDrawer>
</div>
<div className="flex justify-between gap-2">
{/*Left column*/}
@@ -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 = {
+11 -1
View File
@@ -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
@@ -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
@@ -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}
@@ -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