From 3937330ce43dfa0f765bfbc316edcaad996db20d Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Tue, 11 Mar 2025 17:47:30 +0100 Subject: [PATCH] fix: fixed activity aggregation and new user tracking (#230) --- .../CharacterActivity.module.scss | 2 - .../TrackAndFollow/TrackAndFollow.module.scss | 4 +- .../TrackAndFollow/TrackAndFollow.tsx | 37 ++- .../TrackingCharacterWrapper.tsx | 40 +--- .../components/TrackAndFollow/types.ts | 7 +- .../live/characters/characters_live.html.heex | 2 +- .../map_characters_event_handler.ex | 213 ++++-------------- .../event_handlers/map_core_event_handler.ex | 16 -- .../live/maps/map_event_handler.ex | 5 +- lib/wanderer_app_web/live/maps/map_live.ex | 17 +- .../live/maps/maps_live.html.heex | 2 +- 11 files changed, 84 insertions(+), 261 deletions(-) diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/CharacterActivity/CharacterActivity.module.scss b/assets/js/hooks/Mapper/components/mapRootContent/components/CharacterActivity/CharacterActivity.module.scss index 7d72f93a..8ac9cae6 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/CharacterActivity/CharacterActivity.module.scss +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/CharacterActivity/CharacterActivity.module.scss @@ -1,7 +1,5 @@ :global { .p-datatable .p-datatable-thead > tr > th { - background-color: var(--surface-ground); - padding: 0.5rem; text-align: center; white-space: normal; overflow: visible; diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.module.scss b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.module.scss index a5b997d1..3dd8d878 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.module.scss +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.module.scss @@ -1,3 +1,3 @@ .trackFollowHeader { - background-color: var(--surface-ground); -} \ No newline at end of file + background-color: #1e1e1e; +} diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.tsx b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.tsx index 12188eb7..81fdc1b4 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.tsx +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackAndFollow.tsx @@ -29,16 +29,14 @@ export const TrackAndFollow = ({ visible, onHide }: TrackAndFollowProps) => { useEffect(() => { if (trackingCharactersData) { - const newTrackedCharacters = trackingCharactersData - .filter(character => character.tracked) - .map(character => character.id); + const newTrackedCharacters = trackingCharactersData.filter(tc => tc.tracked).map(tc => tc.character.eve_id); setTrackedCharacters(newTrackedCharacters); - const followedChar = trackingCharactersData.find(character => character.followed); + const followedChar = trackingCharactersData.find(tc => tc.followed); - if (followedChar?.id !== followedCharacter) { - setFollowedCharacter(followedChar?.id || null); + if (followedChar?.character?.eve_id !== followedCharacter) { + setFollowedCharacter(followedChar?.character?.eve_id || null); } } }, [followedCharacter, trackingCharactersData]); @@ -90,15 +88,15 @@ export const TrackAndFollow = ({ visible, onHide }: TrackAndFollowProps) => { }); }; - const rowTemplate = (character: TrackingCharacter) => { + const rowTemplate = (tc: TrackingCharacter) => { return ( handleTrackToggle(character.id)} - onFollowToggle={() => handleFollowToggle(character.id)} + key={tc.character.eve_id} + character={tc.character} + isTracked={trackedCharacters.includes(tc.character.eve_id)} + isFollowed={followedCharacter === tc.character.eve_id} + onTrackToggle={() => handleTrackToggle(tc.character.eve_id)} + onFollowToggle={() => handleFollowToggle(tc.character.eve_id)} /> ); }; @@ -108,18 +106,11 @@ export const TrackAndFollow = ({ visible, onHide }: TrackAndFollowProps) => { header={renderHeader()} visible={visible} onHide={onHide} - className="w-[500px] bg-surface-card text-text-color" + className="w-[500px] text-text-color" + contentClassName="!p-0" >
-
+
Track
Follow
Character
diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackingCharacterWrapper.tsx b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackingCharacterWrapper.tsx index 1874ed19..a5bbe5b6 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackingCharacterWrapper.tsx +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/TrackingCharacterWrapper.tsx @@ -1,11 +1,10 @@ -import { TrackingCharacter } from './types'; import { WdCheckbox } from '@/hooks/Mapper/components/ui-kit/WdCheckbox/WdCheckbox'; import WdRadioButton from '@/hooks/Mapper/components/ui-kit/WdRadioButton'; -import { TooltipPosition, WdTooltipWrapper } from '../../../ui-kit'; -import classes from './TrackingCharacterWrapper.module.scss'; +import { CharacterCard, TooltipPosition, WdTooltipWrapper } from '../../../ui-kit'; +import { CharacterTypeRaw } from '@/hooks/Mapper/types'; interface TrackingCharacterWrapperProps { - character: TrackingCharacter; + character: CharacterTypeRaw; isTracked: boolean; isFollowed: boolean; onTrackToggle: () => void; @@ -19,17 +18,11 @@ export const TrackingCharacterWrapper = ({ onTrackToggle, onFollowToggle, }: TrackingCharacterWrapperProps) => { - const trackCheckboxId = `track-${character.id}`; - const followRadioId = `follow-${character.id}`; + const trackCheckboxId = `track-${character.eve_id}`; + const followRadioId = `follow-${character.eve_id}`; return ( -
+
@@ -46,25 +39,8 @@ export const TrackingCharacterWrapper = ({
-
-
-
- {character.name} -
-
- - {character.name} - - [{character.corporation_ticker}] - {character.alliance_ticker && ( - [{character.alliance_ticker}] - )} -
-
+
+
); diff --git a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/types.ts b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/types.ts index cf586a90..036cffa2 100644 --- a/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/types.ts +++ b/assets/js/hooks/Mapper/components/mapRootContent/components/TrackAndFollow/types.ts @@ -1,12 +1,9 @@ +import { CharacterTypeRaw } from '@/hooks/Mapper/types'; /** * Interface for a character that can be tracked and followed */ export interface TrackingCharacter { - id: string; - name: string; - corporation_ticker: string; - alliance_ticker?: string; - portrait_url: string; + character: CharacterTypeRaw; tracked: boolean; followed: boolean; } diff --git a/lib/wanderer_app_web/live/characters/characters_live.html.heex b/lib/wanderer_app_web/live/characters/characters_live.html.heex index cb13a989..b56d73b9 100644 --- a/lib/wanderer_app_web/live/characters/characters_live.html.heex +++ b/lib/wanderer_app_web/live/characters/characters_live.html.heex @@ -67,7 +67,7 @@
<.link patch={~p"/characters/authorize"}>
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 7aef6f68..03a2a04c 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 @@ -103,56 +103,23 @@ defmodule WandererAppWeb.MapCharactersEventHandler do } } = socket ) do - # Get all character settings to preserve followed state - {:ok, all_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) - # Get tracked characters {:ok, map_characters} = WandererApp.Maps.get_tracked_map_characters(map_id, current_user) user_character_eve_ids = map_characters |> Enum.map(& &1.eve_id) + {:ok, tracking_data} = build_tracking_data(map_id, current_user) + # Update socket assigns but don't affect followed state - socket = - socket - |> assign(user_characters: user_character_eve_ids) - |> assign(has_tracked_characters?: has_tracked_characters?(user_character_eve_ids)) - - # Get the map with ACLs for building tracking data - {:ok, map} = WandererApp.Api.Map.by_id(map_id) - map = Ash.load!(map, :acls) - - # Get characters that have access to the map - {:ok, %{characters: characters_with_access}} = - WandererApp.Maps.load_characters(map, all_settings, current_user.id) - - {:ok, latest_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) - - # Create tracking data that preserves followed state - tracking_data = - characters_with_access - |> Enum.map(fn char -> - # Find existing settings to preserve followed state - # Use the latest settings to ensure we have the most up-to-date followed state - setting = Enum.find(latest_settings, &(&1.character_id == char.id)) - # Keep the existing tracked and followed states - tracked = if setting, do: setting.tracked, else: false - followed = if setting, do: setting.followed, else: false - - %{ - id: char.eve_id, - name: char.name, - corporation_ticker: char.corporation_ticker, - alliance_ticker: Map.get(char, :alliance_ticker, ""), - portrait_url: EVEUtil.get_portrait_url(char.eve_id), - tracked: tracked, - followed: followed - } - end) - socket + |> assign(user_characters: user_character_eve_ids) + |> assign(has_tracked_characters?: has_tracked_characters?(user_character_eve_ids)) |> MapEventHandler.push_map_event( - "tracking_characters_data", - %{characters: tracking_data} + "init", + %{ + user_characters: user_character_eve_ids, + reset: false + } ) end @@ -162,7 +129,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do # UI Event Handlers def handle_ui_event( "toggle_track", - %{"character-id" => clicked_char_id}, + %{"character-id" => character_eve_id}, %{ assigns: %{ map_id: map_id, @@ -185,7 +152,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do # Find the character we're toggling with {:ok, character} <- - WandererApp.Character.find_character_by_eve_id(current_user, clicked_char_id), + WandererApp.Character.find_character_by_eve_id(current_user, character_eve_id), {:ok, updated_settings} <- toggle_character_tracking(character, map_id, only_tracked_characters) do # Get the map with ACLs @@ -212,45 +179,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do end end - # Get updated settings after potentially restoring followed state - {:ok, new_all_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) - - # Create tracking data for characters with access to the map - tracking_data = - characters_with_access - |> Enum.map(fn char -> - # For the character being toggled, use the updated settings - setting = - if "#{char.eve_id}" == "#{clicked_char_id}" do - updated_settings - else - # For other characters, use the updated settings - current_setting = Enum.find(new_all_settings, &(&1.character_id == char.id)) - - # If this was the previously followed character, make sure it's still followed - if followed_character_id && followed_character_id == char.id && - current_setting && !current_setting.followed do - # This character was previously followed but is no longer followed - # Restore the followed state - %{current_setting | followed: true} - else - current_setting - end - end - - tracked = if setting, do: setting.tracked, else: false - followed = if setting, do: setting.followed, else: false - - %{ - id: char.eve_id, - name: char.name, - corporation_ticker: char.corporation_ticker, - alliance_ticker: Map.get(char, :alliance_ticker, ""), - portrait_url: EVEUtil.get_portrait_url(char.eve_id), - tracked: tracked, - followed: followed - } - end) + {:ok, tracking_data} = build_tracking_data(map_id, current_user) {:noreply, socket @@ -270,7 +199,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do %{assigns: %{map_id: map_id, current_user: current_user}} = socket ) do # Create tracking data for characters with access to the map - {:ok, tracking_data} = get_tracking_data(map_id, current_user) + {:ok, tracking_data} = build_tracking_data(map_id, current_user) {:noreply, socket @@ -304,14 +233,10 @@ defmodule WandererAppWeb.MapCharactersEventHandler do toggle_character_follow(map_id, clicked_char, is_already_followed) do # Get the state after the toggle_character_follow operation {:ok, all_settings_after} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) - _followed_after = all_settings_after |> Enum.find(& &1.followed) # Build tracking data {:ok, tracking_data} = build_tracking_data(map_id, current_user) - # Get the followed character in the tracking data - _followed_in_tracking = tracking_data |> Enum.find(& &1.followed) - {:noreply, socket |> MapEventHandler.push_map_event("tracking_characters_data", %{characters: tracking_data})} @@ -355,66 +280,9 @@ defmodule WandererAppWeb.MapCharactersEventHandler do def handle_ui_event("hide_activity", _, socket), do: {:noreply, socket |> assign(show_activity?: false)} - def handle_ui_event("add_character", _, socket) do - {:noreply, - socket - |> MapEventHandler.push_map_event("show_tracking", %{})} - 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." - )} - end - def handle_ui_event(event, body, socket), do: MapCoreEventHandler.handle_ui_event(event, body, socket) - defp get_tracking_data(map_id, current_user) do - # Get character settings for this map - {:ok, character_settings} = WandererApp.MapCharacterSettingsRepo.get_all_by_map(map_id) - - # Get the map with ACLs - {:ok, map} = WandererApp.Api.Map.by_id(map_id) - map = Ash.load!(map, :acls) - - # Get all user characters - {:ok, _all_user_characters} = - WandererApp.Api.Character.active_by_user(%{user_id: current_user.id}) - - # Get characters that have access to the map using load_characters - # This will include all characters with access, even if they're not tracked - {:ok, %{characters: characters_with_access}} = - WandererApp.Maps.load_characters(map, character_settings, current_user.id) - - # Create tracking data for characters with access to the map - {:ok, - characters_with_access - |> Enum.map(fn char -> - # Find settings for this character if they exist - setting = Enum.find(character_settings, &(&1.character_id == char.id)) - tracked = if setting, do: setting.tracked, else: false - followed = if setting, do: setting.followed, else: false - - %{ - id: char.eve_id, - name: char.name, - corporation_ticker: char.corporation_ticker, - alliance_ticker: Map.get(char, :alliance_ticker, ""), - portrait_url: EVEUtil.get_portrait_url(char.eve_id), - tracked: tracked, - followed: followed - } - end)} - end - def has_tracked_characters?([]), do: false def has_tracked_characters?(_user_characters), do: true @@ -582,20 +450,15 @@ defmodule WandererAppWeb.MapCharactersEventHandler do socket = init_tracking_state(socket, current_user) needs_tracking_setup = - needs_tracking_setup?(characters_with_access, character_settings, user_permissions) - - socket = - socket - |> assign(:needs_tracking_setup, needs_tracking_setup) - |> then(fn socket -> - if needs_tracking_setup do - socket - else - socket - end - end) + needs_tracking_setup?( + socket.assigns.only_tracked_characters, + characters_with_access, + character_settings, + user_permissions + ) socket + |> assign(:needs_tracking_setup, needs_tracking_setup) end defp get_map_with_acls(map_id) do @@ -619,7 +482,19 @@ defmodule WandererAppWeb.MapCharactersEventHandler do ) end - def needs_tracking_setup?(characters, character_settings, user_permissions) do + def needs_tracking_setup?( + only_tracked_characters, + characters, + character_settings, + user_permissions + ) do + tracked_count = + characters + |> Enum.count(fn char -> + setting = Enum.find(character_settings, &(&1.character_id == char.id)) + setting && setting.tracked + end) + untracked_count = characters |> Enum.count(fn char -> @@ -627,7 +502,8 @@ defmodule WandererAppWeb.MapCharactersEventHandler do setting == nil || !setting.tracked end) - untracked_count > 0 && user_permissions.track_character + user_permissions.track_character && + ((untracked_count > 0 && only_tracked_characters) || tracked_count == 0) end @doc """ @@ -839,11 +715,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do followed = if setting, do: setting.followed, else: false %{ - id: char.eve_id, - name: char.name, - corporation_ticker: char.corporation_ticker, - alliance_ticker: Map.get(char, :alliance_ticker, ""), - portrait_url: EVEUtil.get_portrait_url(char.eve_id), + character: char |> MapEventHandler.map_ui_character_stat(), tracked: tracked, followed: followed } @@ -854,7 +726,7 @@ defmodule WandererAppWeb.MapCharactersEventHandler do end # Helper function to toggle character tracking - defp toggle_character_tracking(character, map_id, _only_tracked_characters) do + defp toggle_character_tracking(character, map_id, only_tracked_characters) do case WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character.id) do {:ok, existing_settings} -> if existing_settings.tracked do @@ -862,6 +734,13 @@ defmodule WandererAppWeb.MapCharactersEventHandler do {:ok, updated_settings} = WandererApp.MapCharacterSettingsRepo.untrack(existing_settings) + :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 + # If the character was followed, we need to unfollow it too # But we should NOT unfollow other characters if existing_settings.followed do @@ -877,6 +756,10 @@ defmodule WandererAppWeb.MapCharactersEventHandler do {:ok, updated_settings} = WandererApp.MapCharacterSettingsRepo.track(existing_settings) + :ok = track_characters([character], map_id, true) + :ok = add_characters([character], map_id, true) + Process.send_after(self(), %{event: :refresh_user_characters}, 10) + {:ok, updated_settings} end 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 383e3353..b7d08963 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 @@ -174,22 +174,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do {:noreply, socket} end - def handle_ui_event("toggle_track", %{"character-id" => character_id}, socket), - do: - MapCharactersEventHandler.handle_ui_event( - "toggle_track", - %{"character-id" => character_id}, - socket - ) - - def handle_ui_event("toggle_follow", %{"character-id" => character_id}, socket), - do: - MapCharactersEventHandler.handle_ui_event( - "toggle_follow", - %{"character-id" => character_id}, - socket - ) - def handle_ui_event( "get_user_settings", _, diff --git a/lib/wanderer_app_web/live/maps/map_event_handler.ex b/lib/wanderer_app_web/live/maps/map_event_handler.ex index 24552fca..bab34be6 100644 --- a/lib/wanderer_app_web/live/maps/map_event_handler.ex +++ b/lib/wanderer_app_web/live/maps/map_event_handler.ex @@ -25,7 +25,6 @@ defmodule WandererAppWeb.MapEventHandler do ] @map_characters_ui_events [ - "add_character", "toggle_track", "toggle_follow", "hide_tracking", @@ -84,9 +83,7 @@ defmodule WandererAppWeb.MapEventHandler do @map_activity_ui_events [ "show_activity", - "hide_activity", - "toggle_follow", - "toggle_track" + "hide_activity" ] @map_routes_events [ diff --git a/lib/wanderer_app_web/live/maps/map_live.ex b/lib/wanderer_app_web/live/maps/map_live.ex index daa2c3cc..0e9e62e5 100644 --- a/lib/wanderer_app_web/live/maps/map_live.ex +++ b/lib/wanderer_app_web/live/maps/map_live.ex @@ -76,16 +76,13 @@ defmodule WandererAppWeb.MapLive do def handle_info(:not_all_characters_tracked, %{assigns: %{map_slug: map_slug}} = socket), do: - WandererAppWeb.MapEventHandler.handle_ui_event( - "add_character", - nil, - socket - |> put_flash( - :error, - "You should enable tracking for all characters that have access to this map first!" - ) - |> push_navigate(to: ~p"/tracking/#{map_slug}") - ) + {:noreply, + socket + |> put_flash( + :error, + "You should enable tracking for all characters that have access to this map first!" + ) + |> push_navigate(to: ~p"/tracking/#{map_slug}")} @impl true def handle_info(info, socket), diff --git a/lib/wanderer_app_web/live/maps/maps_live.html.heex b/lib/wanderer_app_web/live/maps/maps_live.html.heex index d368d311..328efcda 100644 --- a/lib/wanderer_app_web/live/maps/maps_live.html.heex +++ b/lib/wanderer_app_web/live/maps/maps_live.html.heex @@ -1,7 +1,7 @@
<%= if @maps != [] do %> -
+
<.link :if={not @restrict_maps_creation?} class="card h-[250px] rounded-none bg-gradient-to-l from-stone-950 to-stone-900 hover:text-white transform transition duration-500"