Compare commits

...

3 Commits

Author SHA1 Message Date
Dmitry Popov
b1f1098df6 chore: release version v1.13.7 2024-10-31 22:56:59 +01:00
Dmitry Popov
ed5d824c0a refactor(Map): Map event handling refactoring 2024-10-31 19:19:23 +01:00
Dmitry Popov
d8e4631981 refactor(Map): Map event handling refactoring 2024-10-31 19:19:07 +01:00
19 changed files with 2337 additions and 2115 deletions

View File

@@ -79,7 +79,7 @@ export const SystemCustomLabelDialog = ({ systemId, visible, setVisible }: Syste
// @ts-ignore
const handleInput = useCallback(e => {
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9[\](){}]/g, '');
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
}, []);
return (

View File

@@ -90,7 +90,7 @@ export const SystemSettingsDialog = ({ systemId, visible, setVisible }: SystemSe
}, []);
const handleInput = useCallback((e: any) => {
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9[\](){}]/g, '');
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9\-[\](){}]/g, '');
}, []);
return (

View File

@@ -55,11 +55,11 @@ map_subscriptions_enabled =
map_subscription_characters_limit =
config_dir
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 100)
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_CHARACTERS_LIMIT", 10_000)
map_subscription_hubs_limit =
config_dir
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 10)
|> get_int_from_path_or_env("WANDERER_MAP_SUBSCRIPTION_HUBS_LIMIT", 100)
wallet_tracking_enabled =
config_dir

View File

@@ -7,6 +7,8 @@ defmodule WandererApp do
if it comes from the database, an external API or others.
"""
require Logger
@doc """
When used, dispatch to the appropriate domain service
"""

View File

@@ -75,7 +75,7 @@ defmodule WandererApp.Map.PositionCalculator do
def get_available_positions(level, x, y, opts),
do: adjusted_coordinates(1 + level * 2, x, y, opts)
defp edge_coordinates(n, opts) when n > 1 do
defp edge_coordinates(n, _opts) when n > 1 do
min = -div(n, 2)
max = div(n, 2)
# Top edge

View File

@@ -193,7 +193,9 @@ defmodule WandererApp.Map.Server.Impl do
:ok
else
{:error, _error} ->
_error ->
{:ok, character} = WandererApp.Character.get_character(character_id)
broadcast!(map_id, :character_added, character)
:ok
end
end)
@@ -806,7 +808,7 @@ defmodule WandererApp.Map.Server.Impl do
}
end
def handle_event({:options_updated, options}, %{map: map, map_id: map_id} = state),
def handle_event({:options_updated, options}, state),
do: %{
state
| map_opts: [
@@ -1164,11 +1166,10 @@ defmodule WandererApp.Map.Server.Impl do
rtree_name
)
{:ok,
existing_system
|> WandererApp.MapSystemRepo.update_position!(%{position_x: x, position_y: y})
|> WandererApp.MapSystemRepo.cleanup_labels(map_opts)
|> WandererApp.MapSystemRepo.cleanup_tags()}
existing_system
|> WandererApp.MapSystemRepo.update_position!(%{position_x: x, position_y: y})
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|> WandererApp.MapSystemRepo.cleanup_tags()
end
_ ->
@@ -1211,8 +1212,6 @@ defmodule WandererApp.Map.Server.Impl do
solar_system_id: solar_system_id
})
:telemetry.execute([:wanderer_app, :map, :system, :add], %{count: 1})
state
end
@@ -1683,11 +1682,11 @@ defmodule WandererApp.Map.Server.Impl do
defp maybe_add_connection(_map_id, _location, _old_location, _character_id), do: :ok
defp maybe_add_system(map_id, location, old_location, rtree_name, opts)
defp maybe_add_system(map_id, location, old_location, rtree_name, map_opts)
when not is_nil(location) do
case WandererApp.Map.check_location(map_id, location) do
{:ok, location} ->
{:ok, position} = calc_new_system_position(map_id, old_location, rtree_name, opts)
{:ok, position} = calc_new_system_position(map_id, old_location, rtree_name, map_opts)
case WandererApp.MapSystemRepo.get_by_map_and_solar_system_id(
map_id,
@@ -1696,10 +1695,12 @@ defmodule WandererApp.Map.Server.Impl do
{:ok, existing_system} when not is_nil(existing_system) ->
{:ok, updated_system} =
existing_system
|> WandererApp.MapSystemRepo.update_position(%{
|> WandererApp.MapSystemRepo.update_position!(%{
position_x: position.x,
position_y: position.y
})
|> WandererApp.MapSystemRepo.cleanup_labels!(map_opts)
|> WandererApp.MapSystemRepo.cleanup_tags()
@ddrt.insert(
{existing_system.solar_system_id,
@@ -1721,7 +1722,7 @@ defmodule WandererApp.Map.Server.Impl do
_ ->
{:ok, solar_system_info} =
WandererApp.Api.MapSolarSystem.by_solar_system_id(location.solar_system_id)
WandererApp.CachedInfo.get_system_static_info(location.solar_system_id)
WandererApp.MapSystemRepo.create(%{
map_id: map_id,
@@ -1757,7 +1758,7 @@ defmodule WandererApp.Map.Server.Impl do
end
end
defp maybe_add_system(_map_id, _location, _old_location, _rtree_name, _opts), do: :ok
defp maybe_add_system(_map_id, _location, _old_location, _rtree_name, _map_opts), do: :ok
defp calc_new_system_position(map_id, old_location, rtree_name, opts),
do:

View File

@@ -33,7 +33,7 @@ defmodule WandererApp.MapSystemRepo do
{:error, error}
end
def cleanup_labels(%{labels: labels} = system, opts) do
def cleanup_labels!(%{labels: labels} = system, opts) do
store_custom_labels? =
Keyword.get(opts, :store_custom_labels, "false") |> String.to_existing_atom()
@@ -47,7 +47,7 @@ defmodule WandererApp.MapSystemRepo do
def cleanup_tags(system) do
system
|> WandererApp.Api.MapSystem.update_tag!(%{
|> WandererApp.Api.MapSystem.update_tag(%{
tag: nil
})
end
@@ -56,7 +56,7 @@ defmodule WandererApp.MapSystemRepo do
labels
|> Jason.decode!()
|> case do
%{"customLabel" => customLabel} = labels when is_binary(customLabel) ->
%{"customLabel" => customLabel} when is_binary(customLabel) ->
%{"customLabel" => customLabel, "labels" => []}
|> Jason.encode!()

View File

@@ -12,6 +12,7 @@ defmodule WandererAppWeb.MapPicker do
{:ok, socket}
end
@impl true
def update(
%{
current_user: current_user,
@@ -29,6 +30,7 @@ defmodule WandererAppWeb.MapPicker do
end)}
end
@impl true
def render(assigns) do
~H"""
<div id={@id}>
@@ -56,6 +58,7 @@ defmodule WandererAppWeb.MapPicker do
"""
end
@impl true
def handle_event("select", %{"map_slug" => map_slug} = _params, socket) do
notify_to(socket.assigns.notify_to, socket.assigns.event_name, map_slug)

View File

@@ -0,0 +1,49 @@
defmodule WandererAppWeb.MapActivityEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCoreEventHandler}
def handle_server_event(
%{
event: :character_activity,
payload: character_activity
},
socket
),
do: socket |> assign(:character_activity, character_activity)
def handle_server_event(event, socket),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event("show_activity", _, %{assigns: %{map_id: map_id}} = socket) do
Task.async(fn ->
{:ok, character_activity} = map_id |> get_character_activity()
{:character_activity, character_activity}
end)
{:noreply,
socket
|> assign(:show_activity?, true)}
end
def handle_ui_event("hide_activity", _, socket),
do: {:noreply, socket |> assign(show_activity?: false)}
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
defp get_character_activity(map_id) do
{:ok, jumps} = WandererApp.Api.MapChainPassages.by_map_id(%{map_id: map_id})
jumps =
jumps
|> Enum.map(fn p ->
%{p | character: p.character |> MapEventHandler.map_ui_character_stat()}
end)
{:ok, %{jumps: jumps}}
end
end

View File

@@ -0,0 +1,388 @@
defmodule WandererAppWeb.MapCharactersEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCoreEventHandler}
def handle_server_event(%{event: :character_added, payload: character}, socket) do
socket
|> MapEventHandler.push_map_event(
"character_added",
character |> map_ui_character()
)
end
def handle_server_event(%{event: :character_removed, payload: character}, socket) do
socket
|> MapEventHandler.push_map_event(
"character_removed",
character |> map_ui_character()
)
end
def handle_server_event(%{event: :character_updated, payload: character}, socket) do
socket
|> MapEventHandler.push_map_event(
"character_updated",
character |> map_ui_character()
)
end
def handle_server_event(
%{event: :characters_updated},
%{
assigns: %{
map_id: map_id
}
} = socket
) do
characters =
map_id
|> WandererApp.Map.list_characters()
|> Enum.map(&map_ui_character/1)
socket
|> MapEventHandler.push_map_event(
"characters_updated",
characters
)
end
def handle_server_event(
%{event: :present_characters_updated, payload: present_character_eve_ids},
socket
),
do:
socket
|> MapEventHandler.push_map_event(
"present_characters",
present_character_eve_ids
)
def handle_server_event(event, socket),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event(
"add_character",
_,
%{
assigns: %{
current_user: current_user,
map_id: map_id,
user_permissions: %{track_character: true}
}
} = socket
) do
{:ok, character_settings} =
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map_id}) do
{:ok, settings} -> {:ok, settings}
_ -> {:ok, []}
end
{:noreply,
socket
|> assign(
show_tracking?: true,
character_settings: character_settings
)
|> assign_async(:characters, fn ->
{:ok, map} =
map_id
|> WandererApp.MapRepo.get([:acls])
map
|> WandererApp.Maps.load_characters(
character_settings,
current_user.id
)
end)}
end
def handle_ui_event(
"add_character",
_,
%{
assigns: %{
user_permissions: %{track_character: false}
}
} = socket
),
do:
{:noreply,
socket
|> put_flash(
:error,
"You don't have permissions to track characters. Please contact administrator."
)}
def handle_ui_event(
"toggle_track",
%{"character-id" => character_id},
%{
assigns: %{
map_id: map_id,
character_settings: character_settings,
current_user: current_user,
only_tracked_characters: only_tracked_characters
}
} = socket
) do
socket =
case character_settings |> Enum.find(&(&1.character_id == character_id)) do
nil ->
{:ok, map_character_settings} =
WandererApp.Api.MapCharacterSettings.create(%{
character_id: character_id,
map_id: map_id,
tracked: true
})
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
:ok = track_characters([character], map_id, true)
:ok = add_characters([character], map_id, true)
socket
character_setting ->
case character_setting.tracked do
true ->
{:ok, map_character_settings} =
character_setting
|> WandererApp.Api.MapCharacterSettings.untrack()
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
:ok = untrack_characters([character], map_id)
:ok = remove_characters([character], map_id)
if only_tracked_characters do
Process.send_after(self(), :not_all_characters_tracked, 10)
end
socket
_ ->
{:ok, map_character_settings} =
character_setting
|> WandererApp.Api.MapCharacterSettings.track()
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
:ok = track_characters([character], map_id, true)
:ok = add_characters([character], map_id, true)
socket
end
end
%{result: characters} = socket.assigns.characters
{:ok, map_characters} = get_tracked_map_characters(map_id, current_user)
user_character_eve_ids = map_characters |> Enum.map(& &1.eve_id)
{:ok, character_settings} =
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map_id}) do
{:ok, settings} -> {:ok, settings}
_ -> {:ok, []}
end
characters =
characters
|> Enum.map(fn c ->
WandererApp.Maps.map_character(
c,
character_settings |> Enum.find(&(&1.character_id == c.id))
)
end)
{:noreply,
socket
|> assign(user_characters: user_character_eve_ids)
|> assign(has_tracked_characters?: has_tracked_characters?(user_character_eve_ids))
|> assign(character_settings: character_settings)
|> assign_async(:characters, fn ->
{:ok, %{characters: characters}}
end)
|> MapEventHandler.push_map_event(
"init",
%{
user_characters: user_character_eve_ids,
reset: false
}
)}
end
def handle_ui_event("hide_tracking", _, socket),
do: {:noreply, socket |> assign(show_tracking?: false)}
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
def has_tracked_characters?([]), do: false
def has_tracked_characters?(_user_characters), do: true
def get_tracked_map_characters(map_id, current_user) do
case WandererApp.Api.MapCharacterSettings.tracked_by_map(%{
map_id: map_id,
character_ids: current_user.characters |> Enum.map(& &1.id)
}) do
{:ok, settings} ->
{:ok,
settings
|> Enum.map(fn s -> s |> Ash.load!(:character) |> Map.get(:character) end)}
_ ->
{:ok, []}
end
end
def map_ui_character(character),
do:
character
|> Map.take([
:eve_id,
:name,
:online,
:corporation_id,
:corporation_name,
:corporation_ticker,
:alliance_id,
:alliance_name,
:alliance_ticker
])
|> Map.put_new(:ship, WandererApp.Character.get_ship(character))
|> Map.put_new(:location, get_location(character))
def add_characters([], _map_id, _track_character), do: :ok
def add_characters([character | characters], map_id, track_character) do
map_id
|> WandererApp.Map.Server.add_character(character, track_character)
add_characters(characters, map_id, track_character)
end
def remove_characters([], _map_id), do: :ok
def remove_characters([character | characters], map_id) do
map_id
|> WandererApp.Map.Server.remove_character(character.id)
remove_characters(characters, map_id)
end
def untrack_characters(characters, map_id) do
characters
|> Enum.each(fn character ->
WandererAppWeb.Presence.untrack(self(), map_id, character.id)
WandererApp.Cache.put(
"#{inspect(self())}_map_#{map_id}:character_#{character.id}:tracked",
false
)
:ok =
Phoenix.PubSub.unsubscribe(
WandererApp.PubSub,
"character:#{character.eve_id}"
)
end)
end
def track_characters(_, _, false), do: :ok
def track_characters([], _map_id, _is_track_character?), do: :ok
def track_characters(
[character | characters],
map_id,
true
) do
track_character(character, map_id)
track_characters(characters, map_id, true)
end
def track_character(
%{
id: character_id,
eve_id: eve_id,
corporation_id: corporation_id,
alliance_id: alliance_id
},
map_id
) do
WandererAppWeb.Presence.track(self(), map_id, character_id, %{})
case WandererApp.Cache.lookup!(
"#{inspect(self())}_map_#{map_id}:character_#{character_id}:tracked",
false
) do
true ->
:ok
_ ->
:ok =
Phoenix.PubSub.subscribe(
WandererApp.PubSub,
"character:#{eve_id}"
)
:ok =
WandererApp.Cache.put(
"#{inspect(self())}_map_#{map_id}:character_#{character_id}:tracked",
true
)
end
case WandererApp.Cache.lookup(
"#{inspect(self())}_map_#{map_id}:corporation_#{corporation_id}:tracked",
false
) do
{:ok, true} ->
:ok
{:ok, false} ->
:ok =
Phoenix.PubSub.subscribe(
WandererApp.PubSub,
"corporation:#{corporation_id}"
)
:ok =
WandererApp.Cache.put(
"#{inspect(self())}_map_#{map_id}:corporation_#{corporation_id}:tracked",
true
)
end
case WandererApp.Cache.lookup(
"#{inspect(self())}_map_#{map_id}:alliance_#{alliance_id}:tracked",
false
) do
{:ok, true} ->
:ok
{:ok, false} ->
:ok =
Phoenix.PubSub.subscribe(
WandererApp.PubSub,
"alliance:#{alliance_id}"
)
:ok =
WandererApp.Cache.put(
"#{inspect(self())}_map_#{map_id}:alliance_#{alliance_id}:tracked",
true
)
end
:ok = WandererApp.Character.TrackerManager.start_tracking(character_id)
end
defp get_location(character),
do: %{solar_system_id: character.solar_system_id, structure_id: character.structure_id}
end

View File

@@ -0,0 +1,197 @@
defmodule WandererAppWeb.MapConnectionsEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCoreEventHandler}
def handle_server_event(%{event: :update_connection, payload: connection}, socket),
do:
socket
|> MapEventHandler.push_map_event(
"update_connection",
MapEventHandler.map_ui_connection(connection)
)
def handle_server_event(%{event: :remove_connections, payload: connections}, socket) do
connection_ids =
connections |> Enum.map(&MapEventHandler.map_ui_connection/1) |> Enum.map(& &1.id)
socket
|> MapEventHandler.push_map_event(
"remove_connections",
connection_ids
)
end
def handle_server_event(%{event: :add_connection, payload: connection}, socket) do
connections = [MapEventHandler.map_ui_connection(connection)]
socket
|> MapEventHandler.push_map_event(
"add_connections",
connections
)
end
def handle_server_event(event, socket),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event(
"manual_add_connection",
%{"source" => solar_system_source_id, "target" => solar_system_target_id} = _event,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{add_connection: true}
}
} =
socket
) do
map_id
|> WandererApp.Map.Server.add_connection(%{
solar_system_source_id: solar_system_source_id |> String.to_integer(),
solar_system_target_id: solar_system_target_id |> String.to_integer()
})
{:ok, _} =
WandererApp.User.ActivityTracker.track_map_event(:map_connection_added, %{
character_id: tracked_character_ids |> List.first(),
user_id: current_user.id,
map_id: map_id,
solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(),
solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer()
})
{:noreply, socket}
end
def handle_ui_event(
"manual_delete_connection",
%{"source" => solar_system_source_id, "target" => solar_system_target_id} = _event,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{delete_connection: true}
}
} =
socket
) do
map_id
|> WandererApp.Map.Server.delete_connection(%{
solar_system_source_id: solar_system_source_id |> String.to_integer(),
solar_system_target_id: solar_system_target_id |> String.to_integer()
})
{:ok, _} =
WandererApp.User.ActivityTracker.track_map_event(:map_connection_removed, %{
character_id: tracked_character_ids |> List.first(),
user_id: current_user.id,
map_id: map_id,
solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(),
solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer()
})
{:noreply, socket}
end
def handle_ui_event(
"update_connection_" <> param,
%{
"source" => solar_system_source_id,
"target" => solar_system_target_id,
"value" => value
} = _event,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{update_system: true}
}
} =
socket
) do
method_atom =
case param do
"time_status" -> :update_connection_time_status
"mass_status" -> :update_connection_mass_status
"ship_size_type" -> :update_connection_ship_size_type
"locked" -> :update_connection_locked
"custom_info" -> :update_connection_custom_info
_ -> nil
end
key_atom =
case param do
"time_status" -> :time_status
"mass_status" -> :mass_status
"ship_size_type" -> :ship_size_type
"locked" -> :locked
"custom_info" -> :custom_info
_ -> nil
end
{:ok, _} =
WandererApp.User.ActivityTracker.track_map_event(:map_connection_updated, %{
character_id: tracked_character_ids |> List.first(),
user_id: current_user.id,
map_id: map_id,
solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(),
solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer(),
key: key_atom,
value: value
})
apply(WandererApp.Map.Server, method_atom, [
map_id,
%{
solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(),
solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer()
}
|> Map.put_new(key_atom, value)
])
{:noreply, socket}
end
def handle_ui_event(
"get_passages",
%{"from" => from, "to" => to} = _event,
%{assigns: %{map_id: map_id}} = socket
) do
{:ok, passages} = map_id |> get_connection_passages(from, to)
{:reply, passages, socket}
end
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
defp get_connection_passages(map_id, from, to) do
{:ok, passages} = WandererApp.MapChainPassagesRepo.by_connection(map_id, from, to)
passages =
passages
|> Enum.map(fn p ->
%{
p
| character: p.character |> MapEventHandler.map_ui_character_stat()
}
|> Map.put_new(
:ship,
WandererApp.Character.get_ship(%{ship: p.ship_type_id, ship_name: p.ship_name})
)
|> Map.drop([:ship_type_id, :ship_name])
end)
{:ok, %{passages: passages}}
end
end

View File

@@ -0,0 +1,544 @@
defmodule WandererAppWeb.MapCoreEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCharactersEventHandler}
def handle_server_event(:update_permissions, socket) do
DebounceAndThrottle.Debounce.apply(
Process,
:send_after,
[self(), :refresh_permissions, 100],
"update_permissions_#{inspect(self())}",
1000
)
socket
end
def handle_server_event(
:refresh_permissions,
%{assigns: %{current_user: current_user, map_slug: map_slug}} = socket
) do
{:ok, %{id: map_id, user_permissions: user_permissions, owner_id: owner_id}} =
map_slug
|> WandererApp.Api.Map.get_map_by_slug!()
|> Ash.load(:user_permissions, actor: current_user)
user_permissions =
WandererApp.Permissions.get_map_permissions(
user_permissions,
owner_id,
current_user.characters |> Enum.map(& &1.id)
)
case user_permissions do
%{view_system: false} ->
socket
|> Phoenix.LiveView.put_flash(:error, "Your access to the map have been revoked.")
|> Phoenix.LiveView.push_navigate(to: ~p"/maps")
%{track_character: track_character} ->
{:ok, map_characters} =
case WandererApp.Api.MapCharacterSettings.tracked_by_map(%{
map_id: map_id,
character_ids: current_user.characters |> Enum.map(& &1.id)
}) do
{:ok, settings} ->
{:ok,
settings
|> Enum.map(fn s -> s |> Ash.load!(:character) |> Map.get(:character) end)}
_ ->
{:ok, []}
end
case track_character do
false ->
:ok = MapCharactersEventHandler.untrack_characters(map_characters, map_id)
:ok = MapCharactersEventHandler.remove_characters(map_characters, map_id)
_ ->
:ok = MapCharactersEventHandler.track_characters(map_characters, map_id, true)
:ok =
MapCharactersEventHandler.add_characters(map_characters, map_id, track_character)
end
socket
|> assign(user_permissions: user_permissions)
|> MapEventHandler.push_map_event(
"user_permissions",
user_permissions
)
end
end
def handle_server_event(
%{
event: :load_map
},
%{assigns: %{current_user: current_user, map_slug: map_slug}} = socket
) do
ErrorTracker.set_context(%{user_id: current_user.id})
map_slug
|> WandererApp.MapRepo.get_by_slug_with_permissions(current_user)
|> case do
{:ok, map} ->
socket |> init_map(map)
{:error, _} ->
socket
|> put_flash(
:error,
"Something went wrong. Please try one more time or submit an issue."
)
|> push_navigate(to: ~p"/maps")
end
end
def handle_server_event(
%{event: :map_server_started},
socket
),
do: socket |> handle_map_server_started()
def handle_server_event(%{event: :update_map, payload: map_diff}, socket),
do:
socket
|> MapEventHandler.push_map_event(
"map_updated",
map_diff
)
def handle_server_event(
%{event: "presence_diff"},
socket
),
do: socket
def handle_server_event(event, socket) do
Logger.warning(fn -> "unhandled map core event: #{inspect(event)}" end)
socket
end
def handle_ui_event("ui_loaded", _body, %{assigns: %{map_slug: map_slug} = assigns} = socket) do
assigns
|> Map.get(:map_id)
|> case do
map_id when not is_nil(map_id) ->
maybe_start_map(map_id)
_ ->
WandererApp.Cache.insert("map_#{map_slug}:ui_loaded", true)
end
{:noreply, socket}
end
def handle_ui_event(
"live_select_change",
%{"id" => id, "text" => text},
socket
)
when id == "_system_id_live_select_component" do
options =
WandererApp.Api.MapSolarSystem.find_by_name!(%{name: text})
|> Enum.take(100)
|> Enum.map(&map_system/1)
send_update(LiveSelect.Component, options: options, id: id)
{:noreply, socket}
end
def handle_ui_event("toggle_track_" <> character_id, _, socket),
do:
MapCharactersEventHandler.handle_ui_event(
"toggle_track",
%{"character-id" => character_id},
socket
)
def handle_ui_event(
"get_user_settings",
_,
%{assigns: %{map_id: map_id, current_user: current_user}} = socket
) do
{:ok, user_settings} =
WandererApp.MapUserSettingsRepo.get!(map_id, current_user.id)
|> WandererApp.MapUserSettingsRepo.to_form_data()
{:reply, %{user_settings: user_settings}, socket}
end
def handle_ui_event(
"update_user_settings",
user_settings_form,
%{assigns: %{map_id: map_id, current_user: current_user}} = socket
) do
settings =
user_settings_form
|> Map.take(["select_on_spash", "link_signature_on_splash", "delete_connection_with_sigs"])
|> Jason.encode!()
{:ok, user_settings} =
WandererApp.MapUserSettingsRepo.create_or_update(map_id, current_user.id, settings)
{:noreply,
socket |> assign(user_settings_form: user_settings_form, map_user_settings: user_settings)}
end
def handle_ui_event(
"log_map_error",
%{"componentStack" => component_stack, "error" => error},
socket
) do
Logger.error(fn -> "map_ui_error: #{error} \n#{component_stack} " end)
{:noreply,
socket
|> put_flash(:error, "Something went wrong. Please try refresh page or submit an issue.")
|> push_event("js-exec", %{
to: "#map-loader",
attr: "data-loading",
timeout: 100
})}
end
def handle_ui_event("noop", _, socket), do: {:noreply, socket}
def handle_ui_event(
_event,
_body,
%{assigns: %{has_tracked_characters?: false}} =
socket
),
do:
{:noreply,
socket
|> put_flash(
:error,
"You should enable tracking for at least one character."
)}
def handle_ui_event(event, body, socket) do
Logger.warning(fn -> "unhandled map ui event: #{event} #{inspect(body)}" end)
{:noreply, socket}
end
defp maybe_start_map(map_id) do
{:ok, map_server_started} = WandererApp.Cache.lookup("map_#{map_id}:started", false)
if map_server_started do
Process.send_after(self(), %{event: :map_server_started}, 10)
else
WandererApp.Map.Manager.start_map(map_id)
end
end
defp init_map(
%{assigns: %{current_user: current_user, map_slug: map_slug}} = socket,
%{
id: map_id,
deleted: false,
only_tracked_characters: only_tracked_characters,
user_permissions: user_permissions,
name: map_name,
owner_id: owner_id
} = map
) do
user_permissions =
WandererApp.Permissions.get_map_permissions(
user_permissions,
owner_id,
current_user.characters |> Enum.map(& &1.id)
)
{:ok, character_settings} =
case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: map_id}) do
{:ok, settings} -> {:ok, settings}
_ -> {:ok, []}
end
{:ok, %{characters: availaible_map_characters}} =
WandererApp.Maps.load_characters(map, character_settings, current_user.id)
can_view? = user_permissions.view_system
can_track? = user_permissions.track_character
tracked_character_ids =
availaible_map_characters |> Enum.filter(& &1.tracked) |> Enum.map(& &1.id)
all_character_tracked? =
not (availaible_map_characters |> Enum.empty?()) and
availaible_map_characters |> Enum.all?(& &1.tracked)
cond do
(only_tracked_characters and can_track? and all_character_tracked?) or
(not only_tracked_characters and can_view?) ->
Phoenix.PubSub.subscribe(WandererApp.PubSub, map_id)
{:ok, ui_loaded} = WandererApp.Cache.get_and_remove("map_#{map_slug}:ui_loaded", false)
if ui_loaded do
maybe_start_map(map_id)
end
socket
|> assign(
map_id: map_id,
page_title: map_name,
user_permissions: user_permissions,
tracked_character_ids: tracked_character_ids,
only_tracked_characters: only_tracked_characters
)
only_tracked_characters and can_track? and not all_character_tracked? ->
Process.send_after(self(), :not_all_characters_tracked, 10)
socket
true ->
Process.send_after(self(), :no_permissions, 10)
socket
end
end
defp init_map(socket, _map) do
Process.send_after(self(), :no_access, 10)
socket
end
defp handle_map_server_started(
%{
assigns: %{
current_user: current_user,
map_id: map_id,
user_permissions:
%{view_system: true, track_character: track_character} = user_permissions
}
} = 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} <-
MapCharactersEventHandler.get_tracked_map_characters(map_id, current_user),
{:ok, characters_limit} <- map_id |> WandererApp.Map.get_characters_limit(),
{:ok, present_character_ids} <-
WandererApp.Cache.lookup("map_#{map_id}:presence_character_ids", []),
{:ok, kills} <- WandererApp.Cache.lookup("map_#{map_id}:zkb_kills", Map.new()) do
user_character_eve_ids = tracked_map_characters |> Enum.map(& &1.eve_id)
events =
case tracked_map_characters |> Enum.any?(&(&1.access_token == nil)) do
true ->
[:invalid_token_message]
_ ->
[]
end
events =
case tracked_map_characters |> Enum.empty?() do
true ->
events ++ [:empty_tracked_characters]
_ ->
events
end
events =
case present_character_ids |> Enum.count() < characters_limit do
true ->
events ++ [{:track_characters, tracked_map_characters, track_character}]
_ ->
events ++ [:map_character_limit]
end
initial_data =
map_id
|> get_map_data()
|> Map.merge(%{
kills:
kills
|> Enum.filter(fn {_, kills} -> kills > 0 end)
|> Enum.map(&MapEventHandler.map_ui_kill/1),
present_characters:
present_character_ids
|> WandererApp.Character.get_character_eve_ids!(),
user_characters: user_character_eve_ids,
user_permissions: user_permissions,
system_static_infos: nil,
wormhole_types: nil,
effects: nil,
reset: false
})
system_static_infos =
map_id
|> WandererApp.Map.list_systems!()
|> Enum.map(&WandererApp.CachedInfo.get_system_static_info!(&1.solar_system_id))
|> Enum.map(&MapEventHandler.map_ui_system_static_info/1)
initial_data =
initial_data
|> Map.put(
:wormholes,
WandererApp.CachedInfo.get_wormhole_types!()
)
|> Map.put(
:effects,
WandererApp.CachedInfo.get_effects!()
)
|> Map.put(
:system_static_infos,
system_static_infos
)
|> Map.put(:reset, true)
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
})
else
error ->
Logger.error(fn -> "map_start_error: #{error}" end)
Process.send_after(self(), :no_access, 10)
socket
end
end
defp handle_map_server_started(socket) do
Process.send_after(self(), :no_access, 10)
socket
end
defp map_start(
socket,
%{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
initial_data: initial_data,
events: events
} = _started_data
) do
socket =
socket
|> handle_map_start_events(map_id, events)
map_characters = map_id |> WandererApp.Map.list_characters()
socket
|> assign(
map_loaded?: true,
map_user_settings: map_user_settings,
user_characters: user_character_eve_ids,
has_tracked_characters?:
MapCharactersEventHandler.has_tracked_characters?(user_character_eve_ids)
)
|> MapEventHandler.push_map_event(
"init",
initial_data
|> Map.put(
:characters,
map_characters |> Enum.map(&MapCharactersEventHandler.map_ui_character/1)
)
)
|> push_event("js-exec", %{
to: "#map-loader",
attr: "data-loaded"
})
end
defp handle_map_start_events(socket, map_id, events) do
events
|> Enum.reduce(socket, fn event, socket ->
case event do
{:track_characters, map_characters, track_character} ->
:ok =
MapCharactersEventHandler.track_characters(map_characters, map_id, track_character)
:ok = MapCharactersEventHandler.add_characters(map_characters, map_id, track_character)
socket
:invalid_token_message ->
socket
|> put_flash(
:error,
"One of your characters has expired token. Please refresh it on characters page."
)
:empty_tracked_characters ->
socket
|> put_flash(
:info,
"You should enable tracking for at least one character to work with map."
)
:map_character_limit ->
socket
|> put_flash(
:error,
"Map reached its character limit, your characters won't be tracked. Please contact administrator."
)
_ ->
socket
end
end)
end
defp get_map_data(map_id, include_static_data? \\ true) do
{:ok, hubs} = map_id |> WandererApp.Map.list_hubs()
{:ok, connections} = map_id |> WandererApp.Map.list_connections()
{:ok, systems} = map_id |> WandererApp.Map.list_systems()
%{
systems:
systems
|> Enum.map(fn system -> MapEventHandler.map_ui_system(system, include_static_data?) end),
hubs: hubs,
connections: connections |> Enum.map(&MapEventHandler.map_ui_connection/1)
}
end
defp get_tracked_map_characters(map_id, current_user) do
case WandererApp.Api.MapCharacterSettings.tracked_by_map(%{
map_id: map_id,
character_ids: current_user.characters |> Enum.map(& &1.id)
}) do
{:ok, settings} ->
{:ok,
settings
|> Enum.map(fn s -> s |> Ash.load!(:character) |> Map.get(:character) end)}
_ ->
{:ok, []}
end
end
defp map_system(
%{
solar_system_name: solar_system_name,
constellation_name: constellation_name,
region_name: region_name,
solar_system_id: solar_system_id,
class_title: class_title
} = _system
),
do: %{
label: solar_system_name,
value: solar_system_id,
constellation_name: constellation_name,
region_name: region_name,
class_title: class_title
}
end

View File

@@ -0,0 +1,131 @@
defmodule WandererAppWeb.MapRoutesEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCoreEventHandler}
def handle_server_event(
%{
event: :routes,
payload: {solar_system_id, %{routes: routes, systems_static_data: systems_static_data}}
},
socket
),
do:
socket
|> MapEventHandler.push_map_event(
"routes",
%{
solar_system_id: solar_system_id,
loading: false,
routes: routes,
systems_static_data: systems_static_data
}
)
def handle_server_event(event, socket),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event(
"get_routes",
%{"system_id" => solar_system_id, "routes_settings" => routes_settings} = _event,
%{assigns: %{map_id: map_id, map_loaded?: true}} = socket
) do
Task.async(fn ->
{:ok, hubs} = map_id |> WandererApp.Map.list_hubs()
{:ok, routes} =
WandererApp.Maps.find_routes(
map_id,
hubs,
solar_system_id,
get_routes_settings(routes_settings)
)
{:routes, {solar_system_id, routes}}
end)
{:noreply, socket}
end
def handle_ui_event(
"set_autopilot_waypoint",
%{
"character_eve_ids" => character_eve_ids,
"add_to_beginning" => add_to_beginning,
"clear_other_waypoints" => clear_other_waypoints,
"destination_id" => destination_id
} = _event,
%{assigns: %{current_user: current_user, has_tracked_characters?: true}} = socket
) do
character_eve_ids
|> Task.async_stream(fn character_eve_id ->
set_autopilot_waypoint(
current_user,
character_eve_id,
add_to_beginning,
clear_other_waypoints,
destination_id
)
end)
|> Enum.map(fn _result -> :skip end)
{:noreply, socket}
end
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
defp get_routes_settings(%{
"path_type" => path_type,
"include_mass_crit" => include_mass_crit,
"include_eol" => include_eol,
"include_frig" => include_frig,
"include_cruise" => include_cruise,
"avoid_wormholes" => avoid_wormholes,
"avoid_pochven" => avoid_pochven,
"avoid_edencom" => avoid_edencom,
"avoid_triglavian" => avoid_triglavian,
"include_thera" => include_thera,
"avoid" => avoid
}),
do: %{
path_type: path_type,
include_mass_crit: include_mass_crit,
include_eol: include_eol,
include_frig: include_frig,
include_cruise: include_cruise,
avoid_wormholes: avoid_wormholes,
avoid_pochven: avoid_pochven,
avoid_edencom: avoid_edencom,
avoid_triglavian: avoid_triglavian,
include_thera: include_thera,
avoid: avoid
}
defp get_routes_settings(_), do: %{}
defp set_autopilot_waypoint(
current_user,
character_eve_id,
add_to_beginning,
clear_other_waypoints,
destination_id
) do
case current_user.characters
|> Enum.find(fn c -> c.eve_id == character_eve_id end) do
nil ->
:skip
%{id: character_id} = _character ->
character_id
|> WandererApp.Character.set_autopilot_waypoint(destination_id,
add_to_beginning: add_to_beginning,
clear_other_waypoints: clear_other_waypoints
)
:skip
end
end
end

View File

@@ -0,0 +1,350 @@
defmodule WandererAppWeb.MapSignaturesEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCoreEventHandler}
def handle_server_event(
%{
event: :maybe_link_signature,
payload: %{
character_id: character_id,
solar_system_source: solar_system_source,
solar_system_target: solar_system_target
}
},
%{
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_link_signature_on_splash =
map_user_settings
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("link_signature_on_splash")
{:ok, signatures} =
WandererApp.Api.MapSystem.read_by_map_and_solar_system(%{
map_id: map_id,
solar_system_id: solar_system_source
})
|> case do
{:ok, system} ->
{:ok, get_system_signatures(system.id)}
_ ->
{:ok, []}
end
(is_user_character && is_link_signature_on_splash && not (signatures |> Enum.empty?()))
|> case do
true ->
socket
|> MapEventHandler.push_map_event("link_signature_to_system", %{
solar_system_source: solar_system_source,
solar_system_target: solar_system_target
})
false ->
socket
end
end
def handle_server_event(
%{event: :signatures_updated, payload: solar_system_id},
socket
),
do:
socket
|> MapEventHandler.push_map_event(
"signatures_updated",
solar_system_id
)
def handle_server_event(event, socket),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event(
"update_signatures",
%{
"system_id" => solar_system_id,
"added" => added_signatures,
"updated" => updated_signatures,
"removed" => removed_signatures
},
%{
assigns: %{
map_id: map_id,
map_user_settings: map_user_settings,
user_characters: user_characters,
user_permissions: %{update_system: true}
}
} = socket
) do
WandererApp.Api.MapSystem.read_by_map_and_solar_system(%{
map_id: map_id,
solar_system_id: solar_system_id |> String.to_integer()
})
|> case do
{:ok, system} ->
first_character_eve_id =
user_characters |> List.first()
case not is_nil(first_character_eve_id) do
true ->
added_signatures =
added_signatures
|> parse_signatures(first_character_eve_id, system.id)
updated_signatures =
updated_signatures
|> parse_signatures(first_character_eve_id, system.id)
updated_signatures_eve_ids =
updated_signatures
|> Enum.map(fn s -> s.eve_id end)
removed_signatures_eve_ids =
removed_signatures
|> parse_signatures(first_character_eve_id, system.id)
|> Enum.map(fn s -> s.eve_id end)
delete_connection_with_sigs =
map_user_settings
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|> WandererApp.MapUserSettingsRepo.get_boolean_setting(
"delete_connection_with_sigs"
)
WandererApp.Api.MapSystemSignature.by_system_id!(system.id)
|> Enum.filter(fn s -> s.eve_id in removed_signatures_eve_ids end)
|> Enum.each(fn s ->
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_target_id: s.linked_system_id
})
end
s
|> Ash.destroy!()
end)
WandererApp.Api.MapSystemSignature.by_system_id!(system.id)
|> Enum.filter(fn s -> s.eve_id in updated_signatures_eve_ids end)
|> Enum.each(fn s ->
updated = updated_signatures |> Enum.find(fn u -> u.eve_id == s.eve_id end)
if not is_nil(updated) do
s
|> WandererApp.Api.MapSystemSignature.update(updated)
end
end)
added_signatures
|> Enum.map(fn s ->
s |> WandererApp.Api.MapSystemSignature.create!()
end)
Phoenix.PubSub.broadcast!(WandererApp.PubSub, map_id, %{
event: :signatures_updated,
payload: system.solar_system_id
})
{:reply, %{signatures: get_system_signatures(system.id)}, socket}
_ ->
{:reply, %{signatures: []},
socket
|> put_flash(
:error,
"You should enable tracking for at least one character to work with signatures."
)}
end
_ ->
{:noreply, socket}
end
end
def handle_ui_event(
"get_signatures",
%{"system_id" => solar_system_id},
%{
assigns: %{
map_id: map_id
}
} = socket
) do
case WandererApp.Api.MapSystem.read_by_map_and_solar_system(%{
map_id: map_id,
solar_system_id: solar_system_id |> String.to_integer()
}) do
{:ok, system} ->
{:reply, %{signatures: get_system_signatures(system.id)}, socket}
_ ->
{:reply, %{signatures: []}, socket}
end
end
def handle_ui_event(
"link_signature_to_system",
%{
"signature_eve_id" => signature_eve_id,
"solar_system_source" => solar_system_source,
"solar_system_target" => solar_system_target
},
%{
assigns: %{
map_id: map_id,
user_characters: user_characters,
user_permissions: %{update_system: true}
}
} = socket
) do
case WandererApp.Api.MapSystem.read_by_map_and_solar_system(%{
map_id: map_id,
solar_system_id: solar_system_source
}) do
{:ok, system} ->
first_character_eve_id =
user_characters |> List.first()
case not is_nil(first_character_eve_id) do
true ->
WandererApp.Api.MapSystemSignature.by_system_id!(system.id)
|> Enum.filter(fn s -> s.eve_id == signature_eve_id end)
|> Enum.each(fn s ->
s
|> WandererApp.Api.MapSystemSignature.update_linked_system(%{
linked_system_id: solar_system_target
})
end)
Phoenix.PubSub.broadcast!(WandererApp.PubSub, map_id, %{
event: :signatures_updated,
payload: solar_system_source
})
{:noreply, socket}
_ ->
{:noreply,
socket
|> put_flash(
:error,
"You should enable tracking for at least one character to work with signatures."
)}
end
_ ->
{:noreply, socket}
end
end
def handle_ui_event(
"unlink_signature",
%{
"signature_eve_id" => signature_eve_id,
"solar_system_source" => solar_system_source
},
%{
assigns: %{
map_id: map_id,
user_characters: user_characters,
user_permissions: %{update_system: true}
}
} = socket
) do
case WandererApp.Api.MapSystem.read_by_map_and_solar_system(%{
map_id: map_id,
solar_system_id: solar_system_source
}) do
{:ok, system} ->
first_character_eve_id =
user_characters |> List.first()
case not is_nil(first_character_eve_id) do
true ->
WandererApp.Api.MapSystemSignature.by_system_id!(system.id)
|> Enum.filter(fn s -> s.eve_id == signature_eve_id end)
|> Enum.each(fn s ->
s
|> WandererApp.Api.MapSystemSignature.update_linked_system(%{
linked_system_id: nil
})
end)
Phoenix.PubSub.broadcast!(WandererApp.PubSub, map_id, %{
event: :signatures_updated,
payload: solar_system_source
})
{:noreply, socket}
_ ->
{:noreply,
socket
|> put_flash(
:error,
"You should enable tracking for at least one character to work with signatures."
)}
end
_ ->
{:noreply, socket}
end
end
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
defp get_system_signatures(system_id),
do:
system_id
|> WandererApp.Api.MapSystemSignature.by_system_id!()
|> Enum.map(fn %{updated_at: updated_at, linked_system_id: linked_system_id} = s ->
s
|> Map.take([
:eve_id,
:name,
:description,
:kind,
:group,
:type,
:updated_at
])
|> Map.put(:linked_system, MapEventHandler.get_system_static_info(linked_system_id))
|> Map.put(:updated_at, updated_at |> Calendar.strftime("%Y/%m/%d %H:%M:%S"))
end)
defp parse_signatures(signatures, character_eve_id, system_id),
do:
signatures
|> Enum.map(fn %{
"eve_id" => eve_id,
"name" => name,
"kind" => kind,
"group" => group
} = signature ->
%{
system_id: system_id,
eve_id: eve_id,
name: name,
description: Map.get(signature, "description"),
kind: kind,
group: group,
type: Map.get(signature, "type"),
character_eve_id: character_eve_id
}
end)
end

View File

@@ -0,0 +1,326 @@
defmodule WandererAppWeb.MapSystemsEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{MapEventHandler, MapCoreEventHandler}
def handle_server_event(%{event: :add_system, payload: system}, socket),
do:
socket
|> MapEventHandler.push_map_event("add_systems", [MapEventHandler.map_ui_system(system)])
def handle_server_event(%{event: :update_system, payload: system}, socket),
do:
socket
|> MapEventHandler.push_map_event("update_systems", [MapEventHandler.map_ui_system(system)])
def handle_server_event(%{event: :systems_removed, payload: solar_system_ids}, socket),
do:
socket
|> 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_user_settings: map_user_settings}} = socket
) do
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_user_character && is_select_on_spash)
|> case do
true ->
socket
|> MapEventHandler.push_map_event("select_system", solar_system_id)
false ->
socket
end
end
def handle_server_event(%{event: :kills_updated, payload: kills}, socket) do
kills =
kills
|> Enum.map(&MapEventHandler.map_ui_kill/1)
socket
|> MapEventHandler.push_map_event(
"kills_updated",
kills
)
end
def handle_server_event(event, socket),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event(
"add_system",
%{"system_id" => solar_system_id} = _event,
%{
assigns:
%{
map_id: map_id,
map_slug: map_slug,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
user_permissions: %{add_system: true}
} = assigns
} = socket
)
when is_binary(solar_system_id) and solar_system_id != "" do
coordinates = Map.get(assigns, :coordinates)
WandererApp.Map.Server.add_system(
map_id,
%{
solar_system_id: solar_system_id |> String.to_integer(),
coordinates: coordinates
},
current_user.id,
tracked_character_ids |> List.first()
)
{:noreply,
socket
|> push_patch(to: ~p"/#{map_slug}")}
end
def handle_ui_event(
"manual_add_system",
%{"coordinates" => coordinates} = _event,
%{
assigns: %{
has_tracked_characters?: true,
map_slug: map_slug,
user_permissions: %{add_system: true}
}
} =
socket
),
do:
{:noreply,
socket
|> assign(coordinates: coordinates)
|> push_patch(to: ~p"/#{map_slug}/add-system")}
def handle_ui_event(
"add_hub",
%{"system_id" => solar_system_id} = _event,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{update_system: true}
}
} =
socket
) do
map_id
|> WandererApp.Map.Server.add_hub(%{
solar_system_id: solar_system_id
})
{:ok, _} =
WandererApp.User.ActivityTracker.track_map_event(:hub_added, %{
character_id: tracked_character_ids |> List.first(),
user_id: current_user.id,
map_id: map_id,
solar_system_id: solar_system_id
})
{:noreply, socket}
end
def handle_ui_event(
"delete_hub",
%{"system_id" => solar_system_id} = _event,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{update_system: true}
}
} =
socket
) do
map_id
|> WandererApp.Map.Server.remove_hub(%{
solar_system_id: solar_system_id
})
{:ok, _} =
WandererApp.User.ActivityTracker.track_map_event(:hub_removed, %{
character_id: tracked_character_ids |> List.first(),
user_id: current_user.id,
map_id: map_id,
solar_system_id: solar_system_id
})
{:noreply, socket}
end
def handle_ui_event(
"update_system_position",
position,
%{
assigns: %{
map_id: map_id,
has_tracked_characters?: true,
user_permissions: %{update_system: true}
}
} = socket
) do
map_id
|> update_system_position(position)
{:noreply, socket}
end
def handle_ui_event(
"update_system_positions",
positions,
%{
assigns: %{
map_id: map_id,
has_tracked_characters?: true,
user_permissions: %{update_system: true}
}
} = socket
) do
map_id
|> update_system_positions(positions)
{:noreply, socket}
end
def handle_ui_event(
"update_system_" <> param,
%{"system_id" => solar_system_id, "value" => value} = _event,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{update_system: true}
}
} =
socket
) do
method_atom =
case param do
"name" -> :update_system_name
"description" -> :update_system_description
"labels" -> :update_system_labels
"locked" -> :update_system_locked
"tag" -> :update_system_tag
"status" -> :update_system_status
_ -> nil
end
key_atom =
case param do
"name" -> :name
"description" -> :description
"labels" -> :labels
"locked" -> :locked
"tag" -> :tag
"status" -> :status
_ -> :none
end
apply(WandererApp.Map.Server, method_atom, [
map_id,
%{
solar_system_id: "#{solar_system_id}" |> String.to_integer()
}
|> Map.put_new(key_atom, value)
])
{:ok, _} =
WandererApp.User.ActivityTracker.track_map_event(:system_updated, %{
character_id: tracked_character_ids |> List.first(),
user_id: current_user.id,
map_id: map_id,
solar_system_id: "#{solar_system_id}" |> String.to_integer(),
key: key_atom,
value: value
})
{:noreply, socket}
end
def handle_ui_event(
"get_system_static_infos",
%{"solar_system_ids" => solar_system_ids} = _event,
socket
) do
system_static_infos =
solar_system_ids
|> Enum.map(&WandererApp.CachedInfo.get_system_static_info!/1)
|> Enum.map(&MapEventHandler.map_ui_system_static_info/1)
{:reply, %{system_static_infos: system_static_infos}, socket}
end
def handle_ui_event(
"delete_systems",
solar_system_ids,
%{
assigns: %{
map_id: map_id,
current_user: current_user,
tracked_character_ids: tracked_character_ids,
has_tracked_characters?: true,
user_permissions: %{delete_system: true}
}
} =
socket
) do
map_id
|> WandererApp.Map.Server.delete_systems(
solar_system_ids |> Enum.map(&String.to_integer/1),
current_user.id,
tracked_character_ids |> List.first()
)
{:noreply, socket}
end
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
defp update_system_positions(_map_id, []), do: :ok
defp update_system_positions(map_id, [position | rest]) do
update_system_position(map_id, position)
update_system_positions(map_id, rest)
end
defp update_system_position(map_id, %{
"position" => %{"x" => x, "y" => y},
"solar_system_id" => solar_system_id
}),
do:
map_id
|> WandererApp.Map.Server.update_system_position(%{
solar_system_id: solar_system_id |> String.to_integer(),
position_x: x,
position_y: y
})
end

View File

@@ -0,0 +1,292 @@
defmodule WandererAppWeb.MapEventHandler do
use WandererAppWeb, :live_component
use Phoenix.Component
require Logger
alias WandererAppWeb.{
MapActivityEventHandler,
MapCharactersEventHandler,
MapConnectionsEventHandler,
MapCoreEventHandler,
MapRoutesEventHandler,
MapSignaturesEventHandler,
MapSystemsEventHandler
}
@map_characters_events [
:character_added,
:character_removed,
:character_updated,
:characters_updated,
:present_characters_updated
]
@map_characters_ui_events [
"add_character",
"toggle_track",
"hide_tracking"
]
@map_system_events [
:add_system,
:update_system,
:systems_removed,
:maybe_select_system,
:kills_updated
]
@map_system_ui_events [
"add_hub",
"delete_hub",
"add_system",
"delete_systems",
"manual_add_system",
"get_system_static_infos",
"update_system_position",
"update_system_positions",
"update_system_name",
"update_system_description",
"update_system_labels",
"update_system_locked",
"update_system_tag",
"update_system_status"
]
@map_connection_events [
:add_connection,
:remove_connections,
:update_connection
]
@map_connection_ui_events [
"manual_add_connection",
"manual_delete_connection",
"get_passages",
"update_connection_time_status",
"update_connection_mass_status",
"update_connection_ship_size_type",
"update_connection_locked",
"update_connection_custom_info"
]
@map_activity_events [
:character_activity
]
@map_activity_ui_events [
"show_activity",
"hide_activity"
]
@map_routes_events [
:routes
]
@map_routes_ui_events [
"get_routes",
"set_autopilot_waypoint"
]
@map_signatures_events [
:maybe_link_signature,
:signatures_updated
]
@map_signatures_ui_events [
"update_signatures",
"get_signatures",
"link_signature_to_system",
"unlink_signature"
]
def handle_event(socket, %{event: event_name} = event)
when event_name in @map_characters_events,
do: MapCharactersEventHandler.handle_server_event(event, socket)
def handle_event(socket, %{event: event_name} = event)
when event_name in @map_system_events,
do: MapSystemsEventHandler.handle_server_event(event, socket)
def handle_event(socket, %{event: event_name} = event)
when event_name in @map_connection_events,
do: MapConnectionsEventHandler.handle_server_event(event, socket)
def handle_event(socket, %{event: event_name} = event)
when event_name in @map_activity_events,
do: MapActivityEventHandler.handle_server_event(event, socket)
def handle_event(socket, %{event: event_name} = event)
when event_name in @map_routes_events,
do: MapRoutesEventHandler.handle_server_event(event, socket)
def handle_event(socket, %{event: event_name} = event)
when event_name in @map_signatures_events,
do: MapSignaturesEventHandler.handle_server_event(event, socket)
def handle_event(socket, {ref, result}) when is_reference(ref) do
Process.demonitor(ref, [:flush])
case result do
{:map_error, map_error} ->
Process.send_after(self(), map_error, 100)
socket
{event, payload} ->
Process.send_after(
self(),
%{
event: event,
payload: payload
},
10
)
socket
_ ->
socket
end
end
def handle_event(socket, event),
do: MapCoreEventHandler.handle_server_event(event, socket)
def handle_ui_event(event, body, socket)
when event in @map_characters_ui_events,
do: MapSystemsEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket)
when event in @map_system_ui_events,
do: MapSystemsEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket)
when event in @map_connection_ui_events,
do: MapConnectionsEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket)
when event in @map_routes_ui_events,
do: MapRoutesEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket)
when event in @map_signatures_ui_events,
do: MapSignaturesEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket)
when event in @map_activity_ui_events,
do: MapActivityEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket),
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
def get_system_static_info(nil), do: nil
def get_system_static_info(solar_system_id) do
case WandererApp.CachedInfo.get_system_static_info(solar_system_id) do
{:ok, system_static_info} ->
map_ui_system_static_info(system_static_info)
_ ->
%{}
end
end
def push_map_event(socket, type, body),
do:
socket
|> Phoenix.LiveView.Utils.push_event("map_event", %{
type: type,
body: body
})
def map_ui_character_stat(character),
do:
character
|> Map.take([
:eve_id,
:name,
:corporation_ticker,
:alliance_ticker
])
def map_ui_connection(
%{
solar_system_source: solar_system_source,
solar_system_target: solar_system_target,
mass_status: mass_status,
time_status: time_status,
ship_size_type: ship_size_type,
locked: locked
} = _connection
),
do: %{
id: "#{solar_system_source}_#{solar_system_target}",
mass_status: mass_status,
time_status: time_status,
ship_size_type: ship_size_type,
locked: locked,
source: "#{solar_system_source}",
target: "#{solar_system_target}"
}
def map_ui_system(
%{
solar_system_id: solar_system_id,
name: name,
description: description,
position_x: position_x,
position_y: position_y,
locked: locked,
tag: tag,
labels: labels,
status: status,
visible: visible
} = _system,
_include_static_data? \\ true
) do
system_static_info = get_system_static_info(solar_system_id)
%{
id: "#{solar_system_id}",
position: %{x: position_x, y: position_y},
description: description,
name: name,
system_static_info: system_static_info,
labels: labels,
locked: locked,
status: status,
tag: tag,
visible: visible
}
end
def map_ui_system_static_info(nil), do: %{}
def map_ui_system_static_info(system_static_info),
do:
system_static_info
|> Map.take([
:region_id,
:constellation_id,
:solar_system_id,
:solar_system_name,
:solar_system_name_lc,
:constellation_name,
:region_name,
:system_class,
:security,
:type_description,
:class_title,
:is_shattered,
:effect_name,
:effect_power,
:statics,
:wandering,
:triglavian_invasion_status,
:sun_type_id
])
def map_ui_kill({solar_system_id, kills}),
do: %{solar_system_id: solar_system_id, kills: kills}
def map_ui_kill(_kill), do: %{}
end

File diff suppressed because it is too large Load Diff

View File

@@ -104,20 +104,18 @@
id="characters-tracking-table"
class="h-[400px] !overflow-y-auto"
rows={characters}
row_click={fn character -> send(self(), "toggle_track_#{character.id}") end}
>
<:col :let={character} label="Tracked">
<div class="flex items-center gap-3">
<label>
<input
type="checkbox"
class="checkbox"
phx-click="toggle_track"
phx-value-character-id={character.id}
id={"character-track-#{character.id}"}
checked={character.tracked}
/>
</label>
<label class="flex items-center gap-3">
<input
type="checkbox"
class="checkbox"
phx-click="toggle_track"
phx-value-character-id={character.id}
id={"character-track-#{character.id}"}
checked={character.tracked}
/>
<div class="flex items-center gap-3">
<.avatar url={member_icon_url(character.eve_id)} label={character.name} />
<div>
@@ -127,7 +125,7 @@
<div class="text-sm opacity-50"></div>
</div>
</div>
</div>
</label>
</:col>
</.table>
</.async_result>

View File

@@ -8,11 +8,14 @@ defmodule WandererAppWeb.MapsLive do
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
@impl true
def mount(_params, %{"user_id" => user_id} = _session, socket) when not is_nil(user_id) do
def mount(
_params,
%{"user_id" => user_id} = _session,
%{assigns: %{current_user: current_user}} = socket
)
when not is_nil(user_id) do
{:ok, active_characters} = WandererApp.Api.Character.active_by_user(%{user_id: user_id})
current_user = socket.assigns.current_user
user_characters =
active_characters
|> Enum.map(&map_character/1)
@@ -601,16 +604,6 @@ defmodule WandererAppWeb.MapsLive do
{added_acls, removed_acls} = map.acls |> Enum.map(& &1.id) |> _get_acls_diff(form["acls"])
added_acls
|> Enum.each(fn acl_id ->
:telemetry.execute([:wanderer_app, :map, :acl, :add], %{count: 1})
end)
removed_acls
|> Enum.each(fn acl_id ->
:telemetry.execute([:wanderer_app, :map, :acl, :remove], %{count: 1})
end)
Phoenix.PubSub.broadcast(
WandererApp.PubSub,
"maps:#{map.id}",