mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-05 07:15:34 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51489c1aa5 | ||
|
|
25dd6de770 | ||
|
|
2a825f5a02 | ||
|
|
908d249eb9 |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -2,6 +2,20 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.32.3](https://github.com/wanderer-industries/wanderer/compare/v1.32.2...v1.32.3) (2025-01-02)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix 'Allow only tracked characters' saving
|
||||
|
||||
## [v1.32.2](https://github.com/wanderer-industries/wanderer/compare/v1.32.1...v1.32.2) (2025-01-02)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.32.1](https://github.com/wanderer-industries/wanderer/compare/v1.32.0...v1.32.1) (2024-12-25)
|
||||
|
||||
|
||||
|
||||
@@ -14,31 +14,24 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
define(:create, action: :create)
|
||||
define(:destroy, action: :destroy)
|
||||
|
||||
define(:read_by_map,
|
||||
action: :read_by_map
|
||||
)
|
||||
|
||||
define(:by_map_filtered,
|
||||
action: :by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_filtered,
|
||||
action: :tracked_by_map_filtered
|
||||
)
|
||||
|
||||
define(:tracked_by_map_all,
|
||||
action: :tracked_by_map_all
|
||||
)
|
||||
define(:read_by_map, action: :read_by_map)
|
||||
define(:by_map_filtered, action: :by_map_filtered)
|
||||
define(:tracked_by_map_filtered, action: :tracked_by_map_filtered)
|
||||
define(:tracked_by_map_all, action: :tracked_by_map_all)
|
||||
|
||||
define(:track, action: :track)
|
||||
define(:untrack, action: :untrack)
|
||||
|
||||
define(:follow, action: :follow)
|
||||
define(:unfollow, action: :unfollow)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:map_id,
|
||||
:character_id,
|
||||
:tracked
|
||||
:tracked,
|
||||
:followed
|
||||
]
|
||||
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
@@ -76,6 +69,14 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
update :untrack do
|
||||
change(set_attribute(:tracked, false))
|
||||
end
|
||||
|
||||
update :follow do
|
||||
change(set_attribute(:followed, true))
|
||||
end
|
||||
|
||||
update :unfollow do
|
||||
change(set_attribute(:followed, false))
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
@@ -86,6 +87,11 @@ defmodule WandererApp.Api.MapCharacterSettings do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :followed, :boolean do
|
||||
default false
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
@@ -489,7 +489,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
|
||||
defp maybe_update_location(
|
||||
%{
|
||||
character_id: character_id
|
||||
character_id: character_id,
|
||||
} =
|
||||
state,
|
||||
location
|
||||
|
||||
@@ -15,13 +15,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: track_character
|
||||
tracked: track_character,
|
||||
followed: false
|
||||
}),
|
||||
{:ok, character} <- WandererApp.Character.get_character(character_id) do
|
||||
Impl.broadcast!(map_id, :character_added, character)
|
||||
|
||||
:telemetry.execute([:wanderer_app, :map, :character, :added], %{count: 1})
|
||||
|
||||
:ok
|
||||
else
|
||||
_error ->
|
||||
@@ -382,7 +381,6 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
{:character_location, %{solar_system_id: solar_system_id},
|
||||
%{solar_system_id: old_solar_system_id}}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[:skip]
|
||||
end
|
||||
|
||||
@@ -333,7 +333,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
solar_system_id: location.solar_system_id,
|
||||
})
|
||||
|
||||
Impl.broadcast!(map_id, :add_connection, connection)
|
||||
@@ -346,9 +346,20 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
:ok
|
||||
{:error, :already_exists} ->
|
||||
# Still broadcast location change in case of followed character
|
||||
Impl.broadcast!(map_id, :maybe_select_system, %{
|
||||
character_id: character_id,
|
||||
solar_system_id: location.solar_system_id
|
||||
})
|
||||
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.debug(fn -> "Failed to add connection: #{inspect(error, pretty: true)}" end)
|
||||
|
||||
:ok
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -84,20 +84,22 @@ defmodule WandererApp.Maps do
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: false
|
||||
tracked: false,
|
||||
followed: false
|
||||
}
|
||||
|
||||
def map_character(
|
||||
%{name: name, id: id, eve_id: eve_id, corporation_ticker: corporation_ticker} =
|
||||
_character,
|
||||
%{tracked: tracked} = _character_settings
|
||||
%{tracked: tracked, followed: followed} = _character_settings
|
||||
),
|
||||
do: %{
|
||||
name: name,
|
||||
id: id,
|
||||
eve_id: eve_id,
|
||||
corporation_ticker: corporation_ticker,
|
||||
tracked: tracked
|
||||
tracked: tracked,
|
||||
followed: followed
|
||||
}
|
||||
|
||||
@decorate cacheable(
|
||||
|
||||
@@ -24,11 +24,31 @@ defmodule WandererApp.MapCharacterSettingsRepo do
|
||||
def get_tracked_by_map_all(map_id),
|
||||
do: WandererApp.Api.MapCharacterSettings.tracked_by_map_all(%{map_id: map_id})
|
||||
|
||||
def get_by_map(map_id, character_id) do
|
||||
case get_by_map_filtered(map_id, [character_id]) do
|
||||
{:ok, [setting | _]} ->
|
||||
{:ok, setting}
|
||||
|
||||
{:ok, []} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def track(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track()
|
||||
def untrack(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack()
|
||||
|
||||
def track!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.track!()
|
||||
def untrack!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.untrack!()
|
||||
|
||||
def follow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow()
|
||||
def unfollow(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow()
|
||||
|
||||
def follow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.follow!()
|
||||
def unfollow!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.unfollow!()
|
||||
|
||||
|
||||
def destroy!(settings), do: settings |> WandererApp.Api.MapCharacterSettings.destroy!()
|
||||
end
|
||||
|
||||
@@ -393,6 +393,7 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
data-pc-name="checkbox"
|
||||
data-pc-section="root"
|
||||
>
|
||||
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
|
||||
<input
|
||||
id={@id}
|
||||
name={@name}
|
||||
|
||||
@@ -86,7 +86,8 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: selected_map.id,
|
||||
tracked: true
|
||||
tracked: true,
|
||||
followed: false
|
||||
})
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
@@ -111,7 +111,8 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: character_id,
|
||||
map_id: map_id,
|
||||
tracked: true
|
||||
tracked: true,
|
||||
followed: false
|
||||
})
|
||||
|
||||
character = map_character_settings |> Ash.load!(:character) |> Map.get(:character)
|
||||
@@ -190,6 +191,92 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"toggle_follow",
|
||||
%{"character-id" => clicked_char_id},
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id,
|
||||
current_user: current_user
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
|
||||
{:ok, all_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
# Find and filter user's characters
|
||||
{:ok, user_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
user_char_ids = Enum.map(user_characters, & &1.id)
|
||||
|
||||
my_settings =
|
||||
all_settings
|
||||
|> Enum.filter(fn s ->
|
||||
s.character_id in user_char_ids
|
||||
end)
|
||||
|
||||
existing = Enum.find(my_settings, &(&1.character_id == clicked_char_id))
|
||||
|
||||
{:ok, target_setting} =
|
||||
if existing do
|
||||
{:ok, existing}
|
||||
else
|
||||
WandererApp.MapCharacterSettingsRepo.create(%{
|
||||
character_id: clicked_char_id,
|
||||
map_id: map_id,
|
||||
tracked: true,
|
||||
followed: true
|
||||
})
|
||||
end
|
||||
|
||||
# If the target_setting is already followed => unfollow it
|
||||
if target_setting.followed do
|
||||
{:ok, updated} = WandererApp.MapCharacterSettingsRepo.unfollow(target_setting)
|
||||
else
|
||||
# Only unfollow other rows from the current user
|
||||
for s <- my_settings, s.id != target_setting.id, s.followed == true do
|
||||
WandererApp.MapCharacterSettingsRepo.unfollow!(s)
|
||||
end
|
||||
|
||||
# Ensure the new followed char is tracked
|
||||
if not target_setting.tracked do
|
||||
WandererApp.MapCharacterSettingsRepo.track!(target_setting)
|
||||
|
||||
char = target_setting |> Ash.load!(:character) |> Map.get(:character)
|
||||
:ok = track_characters([char], map_id, true)
|
||||
:ok = add_characters([char], map_id, true)
|
||||
end
|
||||
|
||||
{:ok, updated} = WandererApp.MapCharacterSettingsRepo.follow(target_setting)
|
||||
end
|
||||
|
||||
# re-fetch or re-map to confirm final results in UI
|
||||
%{result: characters} = socket.assigns.characters
|
||||
|
||||
{:ok, tracked_characters} = get_tracked_map_characters(map_id, current_user)
|
||||
user_eve_ids = Enum.map(tracked_characters, & &1.eve_id)
|
||||
|
||||
{:ok, final_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id)
|
||||
|
||||
updated_chars =
|
||||
characters
|
||||
|> Enum.map(fn c ->
|
||||
s = Enum.find(final_settings, &(&1.character_id == c.id))
|
||||
WandererApp.Maps.map_character(c, s)
|
||||
end)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(user_characters: user_eve_ids)
|
||||
|> assign(has_tracked_characters?: has_tracked_characters?(user_eve_ids))
|
||||
|> assign_async(:characters, fn ->
|
||||
{:ok, %{characters: updated_chars}}
|
||||
end)
|
||||
|> MapEventHandler.push_map_event("init", %{user_characters: user_eve_ids, reset: false})
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
||||
def handle_ui_event("hide_tracking", _, socket),
|
||||
do: {:noreply, socket |> assign(show_tracking?: false)}
|
||||
|
||||
|
||||
@@ -162,6 +162,14 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event("toggle_follow_" <> character_id, _, socket),
|
||||
do:
|
||||
MapCharactersEventHandler.handle_ui_event(
|
||||
"toggle_follow",
|
||||
%{"character-id" => character_id},
|
||||
socket
|
||||
)
|
||||
|
||||
def handle_ui_event(
|
||||
"get_user_settings",
|
||||
_,
|
||||
|
||||
@@ -21,32 +21,57 @@ 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_user_settings: map_user_settings}} = socket
|
||||
) do
|
||||
is_user_character =
|
||||
current_user.characters |> Enum.map(& &1.id) |> Enum.member?(character_id)
|
||||
%{
|
||||
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_select_on_spash =
|
||||
map_user_settings
|
||||
|> WandererApp.MapUserSettingsRepo.to_form_data!()
|
||||
|> WandererApp.MapUserSettingsRepo.get_boolean_setting("select_on_spash")
|
||||
is_user_character =
|
||||
current_user.characters
|
||||
|> Enum.map(& &1.id)
|
||||
|> Enum.member?(character_id)
|
||||
|
||||
(is_user_character && is_select_on_spash)
|
||||
|> case do
|
||||
true ->
|
||||
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
|
||||
|
||||
must_select? = is_user_character && (is_select_on_spash || is_followed)
|
||||
if not must_select? do
|
||||
socket
|
||||
|> MapEventHandler.push_map_event("select_system", solar_system_id)
|
||||
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
|
||||
)
|
||||
|
||||
false ->
|
||||
socket
|
||||
end
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
def handle_server_event(%{event: :kills_updated, payload: kills}, socket) do
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
@map_characters_ui_events [
|
||||
"add_character",
|
||||
"toggle_track",
|
||||
"toggle_follow",
|
||||
"hide_tracking"
|
||||
]
|
||||
|
||||
|
||||
@@ -31,39 +31,20 @@
|
||||
</.link>
|
||||
</div>
|
||||
|
||||
<.modal
|
||||
:if={assigns |> Map.get(:show_activity?, false)}
|
||||
id="map-activity-modal"
|
||||
title="Activity of Characters"
|
||||
class="!w-[500px]"
|
||||
show
|
||||
on_cancel={JS.push("hide_activity")}
|
||||
>
|
||||
<.table
|
||||
:if={not (assigns |> Map.get(:character_activity) |> is_nil())}
|
||||
class="!max-h-[80vh] !overflow-y-auto"
|
||||
id="activity-tbl"
|
||||
rows={@character_activity.jumps}
|
||||
>
|
||||
<:col :let={activity} label="Character">
|
||||
<.character_item character={activity.character} />
|
||||
</:col>
|
||||
<:col :let={activity} label="Passages">
|
||||
<%= activity.count %>
|
||||
</:col>
|
||||
</.table>
|
||||
</.modal>
|
||||
|
||||
<.modal
|
||||
:if={assigns |> Map.get(:show_tracking?, false)}
|
||||
id="map-tracking-modal"
|
||||
title="Track Characters"
|
||||
title="Track and Follow Characters"
|
||||
show
|
||||
on_cancel={JS.push("hide_tracking")}
|
||||
>
|
||||
<.async_result :let={characters} assign={@characters}>
|
||||
<:loading><span class="loading loading-dots loading-xs" /></:loading>
|
||||
<:failed :let={reason}><%= reason %></:failed>
|
||||
<:loading>
|
||||
<span class="loading loading-dots loading-xs" />
|
||||
</:loading>
|
||||
<:failed :let={reason}>
|
||||
<%= reason %>
|
||||
</:failed>
|
||||
|
||||
<.table
|
||||
:if={characters}
|
||||
@@ -72,7 +53,7 @@
|
||||
rows={characters}
|
||||
>
|
||||
<:col :let={character} label="Track">
|
||||
<label class="flex items-center gap-3">
|
||||
<label class="flex items-center gap-2 justify-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
@@ -81,17 +62,34 @@
|
||||
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>
|
||||
<div class="font-bold">
|
||||
<%= character.name %><span class="ml-1 text-gray-400">[<%= character.corporation_ticker %>]</span>
|
||||
</div>
|
||||
<div class="text-sm opacity-50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</:col>
|
||||
<:col :let={character} label="Follow">
|
||||
<label class="flex items-center gap-2 justify-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="followed_character"
|
||||
class="radio"
|
||||
phx-click="toggle_follow"
|
||||
phx-value-character-id={character.id}
|
||||
checked={Map.get(character, :followed, false)}
|
||||
/>
|
||||
</label>
|
||||
</:col>
|
||||
<:col :let={character} label="Character">
|
||||
<div class="flex items-center gap-3">
|
||||
<.avatar url={member_icon_url(character.eve_id)} label={character.name} />
|
||||
<div>
|
||||
<div class="font-bold">
|
||||
<%= character.name %>
|
||||
<span class="ml-1 text-gray-400">
|
||||
[<%= character.corporation_ticker %>]
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-sm opacity-50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</:col>
|
||||
</.table>
|
||||
</.async_result>
|
||||
</.modal>
|
||||
|
||||
@@ -203,7 +203,10 @@ defmodule WandererAppWeb.MapsLive do
|
||||
socket.assigns.form,
|
||||
form
|
||||
|> Map.put("acls", form["acls"] || [])
|
||||
|> Map.put("only_tracked_characters", form["only_tracked_characters"] || false)
|
||||
|> Map.put(
|
||||
"only_tracked_characters",
|
||||
(form["only_tracked_characters"] || "false") |> String.to_existing_atom()
|
||||
)
|
||||
)
|
||||
|
||||
{:noreply, socket |> assign(form: form)}
|
||||
@@ -593,7 +596,13 @@ defmodule WandererAppWeb.MapsLive do
|
||||
scope -> scope
|
||||
end
|
||||
|
||||
form = form |> Map.put("scope", scope)
|
||||
form =
|
||||
form
|
||||
|> Map.put("scope", scope)
|
||||
|> Map.put(
|
||||
"only_tracked_characters",
|
||||
(form["only_tracked_characters"] || "false") |> String.to_existing_atom()
|
||||
)
|
||||
|
||||
map
|
||||
|> WandererApp.Api.Map.update(form)
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -2,7 +2,7 @@ defmodule WandererApp.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
@version "1.32.1"
|
||||
@version "1.32.3"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.MigrateResources1 do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:map_character_settings_v1) do
|
||||
add :followed, :boolean, default: false
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_character_settings_v1) do
|
||||
remove :followed
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user