chore: release version v1.44.0

This commit is contained in:
Dmitry Popov
2025-02-02 22:24:47 +01:00
parent 5bd968acae
commit 304f4b01ab
12 changed files with 151 additions and 76 deletions

View File

@@ -37,6 +37,7 @@ const INITIAL_DATA: MapData = {
userPermissions: {},
systemSignatures: {} as Record<string, SystemSignature[]>,
options: {} as Record<string, string | boolean>,
is_subscription_active: false,
};
export interface MapContextProps {

View File

@@ -103,13 +103,3 @@ export const WIDGETS_CHECKBOXES_PROPS: WidgetsCheckboxesType = [
label: 'Kills',
},
];
export function getWidgetsCheckboxesProps(detailedKillsDisabled: boolean): WidgetsCheckboxesType {
return filterOutKills(WIDGETS_CHECKBOXES_PROPS, detailedKillsDisabled);
}
function filterOutKills<T extends { id: WidgetsIds }>(items: T[], shouldFilter: boolean) {
if (!shouldFilter) return items;
return items.filter((w) => w.id !== WidgetsIds.kills);
}

View File

@@ -9,7 +9,7 @@ import { KillsSettingsDialog } from './components/SystemKillsSettingsDialog';
export const SystemKills: React.FC = () => {
const {
data: { selectedSystems, systems },
data: { selectedSystems, systems, is_subscription_active: isSubscriptionActive },
outCommand,
} = useMapRootState();
@@ -41,40 +41,49 @@ export const SystemKills: React.FC = () => {
<div className="h-full flex flex-col min-h-0">
<div className="flex flex-col flex-1 min-h-0">
<Widget label={<KillsHeader systemId={systemId} onOpenSettings={() => setSettingsDialogVisible(true)} />}>
{isNothingSelected && (
{!isSubscriptionActive && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
No system selected (or toggle Show all systems)
Kills available with &#39;Active&#39; map subscription only (contact map administrators)
</div>
)}
{isSubscriptionActive && (
<>
{isNothingSelected && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
No system selected (or toggle Show all systems)
</div>
)}
{!isNothingSelected && showLoading && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
Loading Kills...
</div>
)}
{!isNothingSelected && showLoading && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
Loading Kills...
</div>
)}
{!isNothingSelected && !showLoading && error && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-red-400 text-sm">
{error}
</div>
)}
{!isNothingSelected && !showLoading && error && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-red-400 text-sm">
{error}
</div>
)}
{!isNothingSelected && !showLoading && !error && (!kills || kills.length === 0) && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
No kills found
</div>
)}
{!isNothingSelected && !showLoading && !error && (!kills || kills.length === 0) && (
<div className="w-full h-full flex justify-center items-center select-none text-center text-stone-400/80 text-sm">
No kills found
</div>
)}
{!isNothingSelected && !showLoading && !error && (
<div className="flex-1 flex flex-col overflow-y-auto">
<SystemKillsContent
key={settings.compact ? 'compact' : 'normal'}
kills={kills}
systemNameMap={systemNameMap}
compact={settings.compact}
onlyOneSystem={!visible}
/>
</div>
{!isNothingSelected && !showLoading && !error && (
<div className="flex-1 flex flex-col overflow-y-auto">
<SystemKillsContent
key={settings.compact ? 'compact' : 'normal'}
kills={kills}
systemNameMap={systemNameMap}
compact={settings.compact}
onlyOneSystem={!visible}
/>
</div>
)}
</>
)}
</Widget>
</div>

View File

@@ -1,5 +1,5 @@
import { PrettySwitchbox } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components';
import { getWidgetsCheckboxesProps, WidgetsIds } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { WIDGETS_CHECKBOXES_PROPS, WidgetsIds } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useCallback } from 'react';
@@ -9,20 +9,17 @@ export interface WidgetsSettingsProps {}
// eslint-disable-next-line no-empty-pattern
export const WidgetsSettings = ({}: WidgetsSettingsProps) => {
const { windowsSettings, toggleWidgetVisibility, resetWidgets, data } = useMapRootState();
const { windowsSettings, toggleWidgetVisibility, resetWidgets } = useMapRootState();
const handleWidgetSettingsChange = useCallback(
(widget: WidgetsIds) => toggleWidgetVisibility(widget),
[toggleWidgetVisibility],
);
const detailedKillsDisabled = data.options?.detailedKillsDisabled === true;
const widgetProps = getWidgetsCheckboxesProps(detailedKillsDisabled);
return (
<div className="flex flex-col h-full gap-2">
<div>
{widgetProps.map(widget => (
{WIDGETS_CHECKBOXES_PROPS.map(widget => (
<PrettySwitchbox
key={widget.id}
label={widget.label}

View File

@@ -22,4 +22,5 @@ export type MapUnionTypes = {
connections: SolarSystemConnection[];
userPermissions: Partial<UserPermissions>;
options: Record<string, string | boolean>;
is_subscription_active: boolean;
};

View File

@@ -70,9 +70,14 @@ defmodule WandererApp.Map do
def get_characters_limit(map_id),
do: {:ok, map_id |> get_map!() |> Map.get(:characters_limit, 50)}
def is_subscription_active?(map_id) do
def is_subscription_active?(map_id),
do: is_subscription_active?(map_id, WandererApp.Env.map_subscriptions_enabled?())
def is_subscription_active?(_map_id, false), do: {:ok, true}
def is_subscription_active?(map_id, _map_subscriptions_enabled) do
{:ok, %{plan: plan}} = WandererApp.Map.SubscriptionManager.get_active_map_subscription(map_id)
not WandererApp.Env.map_subscriptions_enabled?() || plan != :alpha
{:ok, plan != :alpha}
end
def get_options(map_id),

View File

@@ -28,6 +28,8 @@ defmodule WandererApp.Map.ZkbDataFetcher do
@impl true
def handle_info(:fetch_data, %{iteration: iteration} = state) do
zkill_preload_disabled = WandererApp.Env.zkill_preload_disabled?()
WandererApp.Map.RegistryHelper.list_all_maps()
|> Task.async_stream(
fn %{id: map_id, pid: _server_pid} ->
@@ -35,7 +37,11 @@ defmodule WandererApp.Map.ZkbDataFetcher do
if WandererApp.Map.Server.map_pid(map_id) do
update_map_kills(map_id)
unless WandererApp.Env.zkill_preload_disabled?() do
{:ok, is_subscription_active} = map_id |> WandererApp.Map.is_subscription_active?()
can_preload_zkill = not zkill_preload_disabled && is_subscription_active
if can_preload_zkill do
update_detailed_map_kills(map_id)
end
end
@@ -52,7 +58,7 @@ defmodule WandererApp.Map.ZkbDataFetcher do
new_iteration = iteration + 1
cond do
WandererApp.Env.zkill_preload_disabled?() ->
zkill_preload_disabled ->
# If preload is disabled, just update iteration
{:noreply, %{state | iteration: new_iteration}}
@@ -138,11 +144,11 @@ defmodule WandererApp.Map.ZkbDataFetcher do
end)
WandererApp.Cache.put("map_#{map_id}:zkb_ids", updated_ids_map,
ttl: :timer.hours(KillsCache.killmail_ttl)
ttl: :timer.hours(KillsCache.killmail_ttl())
)
WandererApp.Cache.put("map_#{map_id}:zkb_detailed_kills", updated_details_map,
ttl: :timer.hours(KillsCache.killmail_ttl)
ttl: :timer.hours(KillsCache.killmail_ttl())
)
changed_data = Map.take(updated_details_map, changed_systems)

View File

@@ -65,15 +65,18 @@ defmodule WandererApp.Zkb.KillsPreloader do
last_active_maps_result = WandererApp.Api.MapState.get_last_active(cutoff_time)
last_active_maps = resolve_last_active_maps(last_active_maps_result)
active_maps_with_subscription = get_active_maps_with_subscription(last_active_maps)
# Gather systems from those maps
system_tuples = gather_visible_systems(last_active_maps)
system_tuples = gather_visible_systems(active_maps_with_subscription)
unique_systems = Enum.uniq(system_tuples)
Logger.debug("""
[KillsPreloader] Found #{length(unique_systems)} unique systems \
across #{length(last_active_maps)} map(s)
""")
Logger.debug(fn ->
"""
[KillsPreloader] Found #{length(unique_systems)} unique systems \
across #{length(active_maps_with_subscription)} map(s)
"""
end)
# ---- QUICK PASS ----
state_quick = %{state | phase: :quick_pass}
@@ -83,7 +86,9 @@ defmodule WandererApp.Zkb.KillsPreloader do
do_pass(unique_systems, :quick, @quick_hours, @quick_limit, state_quick)
end)
Logger.info("[KillsPreloader] Phase 1 (quick) done => calls_count=#{state_after_quick.calls_count}, elapsed=#{time_quick_ms}ms")
Logger.info(
"[KillsPreloader] Phase 1 (quick) done => calls_count=#{state_after_quick.calls_count}, elapsed=#{time_quick_ms}ms"
)
# ---- EXPANDED PASS ----
state_expanded = %{state_after_quick | phase: :expanded_pass}
@@ -93,7 +98,9 @@ defmodule WandererApp.Zkb.KillsPreloader do
do_pass(unique_systems, :expanded, @quick_hours, @expanded_limit, state_expanded)
end)
Logger.info("[KillsPreloader] Phase 2 (expanded) done => calls_count=#{final_state.calls_count}, elapsed=#{time_expanded_ms}ms")
Logger.info(
"[KillsPreloader] Phase 2 (expanded) done => calls_count=#{final_state.calls_count}, elapsed=#{time_expanded_ms}ms"
)
# Reset phase to :idle
{:noreply, %{final_state | phase: :idle}}
@@ -125,6 +132,13 @@ defmodule WandererApp.Zkb.KillsPreloader do
[]
end
defp get_active_maps_with_subscription(maps) do
maps
|> Enum.filter(fn map ->
{:ok, is_subscription_active} = map.id |> WandererApp.Map.is_subscription_active?()
is_subscription_active
end)
end
defp gather_visible_systems(maps) do
maps
@@ -136,15 +150,19 @@ defmodule WandererApp.Zkb.KillsPreloader do
Enum.map(systems, fn sys -> {the_map_id, sys.solar_system_id} end)
{:error, reason} ->
Logger.warning("[KillsPreloader] get_visible_by_map failed => map_id=#{inspect(the_map_id)}, reason=#{inspect(reason)}")
Logger.warning(
"[KillsPreloader] get_visible_by_map failed => map_id=#{inspect(the_map_id)}, reason=#{inspect(reason)}"
)
[]
end
end)
end
defp do_pass(unique_systems, pass_type, hours, limit, state) do
Logger.info("[KillsPreloader] Starting #{pass_type} pass => #{length(unique_systems)} systems")
Logger.info(
"[KillsPreloader] Starting #{pass_type} pass => #{length(unique_systems)} systems"
)
{final_state, kills_map} =
unique_systems
@@ -167,29 +185,45 @@ defmodule WandererApp.Zkb.KillsPreloader do
end
defp fetch_kills_for_system(system_id, :quick, hours, limit, state) do
Logger.debug("[KillsPreloader] Quick fetch => system=#{system_id}, hours=#{hours}, limit=#{limit}")
Logger.debug(fn ->
"[KillsPreloader] Quick fetch => system=#{system_id}, hours=#{hours}, limit=#{limit}"
end)
case KillsProvider.Fetcher.fetch_kills_for_system(system_id, hours, state, limit: limit, force: false) do
case KillsProvider.Fetcher.fetch_kills_for_system(system_id, hours, state,
limit: limit,
force: false
) do
{:ok, kills, updated_state} ->
{:ok, system_id, kills, updated_state}
{:error, reason, updated_state} ->
Logger.warning("[KillsPreloader] Quick fetch failed => system=#{system_id}, reason=#{inspect(reason)}")
Logger.warning(
"[KillsPreloader] Quick fetch failed => system=#{system_id}, reason=#{inspect(reason)}"
)
{:error, reason, updated_state}
end
end
defp fetch_kills_for_system(system_id, :expanded, hours, limit, state) do
Logger.debug("[KillsPreloader] Expanded fetch => system=#{system_id}, hours=#{hours}, limit=#{limit} (forcing refresh)")
Logger.debug(fn ->
"[KillsPreloader] Expanded fetch => system=#{system_id}, hours=#{hours}, limit=#{limit} (forcing refresh)"
end)
with {:ok, kills_1h, updated_state} <-
KillsProvider.Fetcher.fetch_kills_for_system(system_id, hours, state, limit: limit, force: true),
KillsProvider.Fetcher.fetch_kills_for_system(system_id, hours, state,
limit: limit,
force: true
),
{:ok, final_kills, final_state} <-
maybe_fetch_more_if_needed(system_id, kills_1h, limit, updated_state) do
{:ok, system_id, final_kills, final_state}
else
{:error, reason, updated_state} ->
Logger.warning("[KillsPreloader] Expanded fetch (#{hours}h) failed => system=#{system_id}, reason=#{inspect(reason)}")
Logger.warning(
"[KillsPreloader] Expanded fetch (#{hours}h) failed => system=#{system_id}, reason=#{inspect(reason)}"
)
{:error, reason, updated_state}
end
end
@@ -198,9 +232,15 @@ defmodule WandererApp.Zkb.KillsPreloader do
defp maybe_fetch_more_if_needed(system_id, kills_1h, limit, state) do
if length(kills_1h) < limit do
needed = limit - length(kills_1h)
Logger.debug("[KillsPreloader] Expanding to #{@expanded_hours}h => system=#{system_id}, need=#{needed} more kills")
case KillsProvider.Fetcher.fetch_kills_for_system(system_id, @expanded_hours, state, limit: needed, force: true) do
Logger.debug(fn ->
"[KillsPreloader] Expanding to #{@expanded_hours}h => system=#{system_id}, need=#{needed} more kills"
end)
case KillsProvider.Fetcher.fetch_kills_for_system(system_id, @expanded_hours, state,
limit: needed,
force: true
) do
{:ok, _kills_24h, updated_state2} ->
final_kills =
KillsCache.fetch_cached_kills(system_id)
@@ -209,7 +249,10 @@ defmodule WandererApp.Zkb.KillsPreloader do
{:ok, final_kills, updated_state2}
{:error, reason2, updated_state2} ->
Logger.warning("[KillsPreloader] #{@expanded_hours}h fetch failed => system=#{system_id}, reason=#{inspect(reason2)}")
Logger.warning(
"[KillsPreloader] #{@expanded_hours}h fetch failed => system=#{system_id}, reason=#{inspect(reason2)}"
)
{:error, reason2, updated_state2}
end
else
@@ -243,7 +286,9 @@ defmodule WandererApp.Zkb.KillsPreloader do
do: Logger.error("[KillsPreloader] Expanded fetch task failed => #{inspect(reason)}")
defp broadcast_all_kills(kills_map, pass_type) do
Logger.info("[KillsPreloader] Broadcasting kills => #{map_size(kills_map)} systems (#{pass_type})")
Logger.info(
"[KillsPreloader] Broadcasting kills => #{map_size(kills_map)} systems (#{pass_type})"
)
Phoenix.PubSub.broadcast!(
WandererApp.PubSub,

View File

@@ -40,7 +40,7 @@ defmodule WandererApp.Zkb.KillsProvider.ZkbApi do
defp do_req_get(system_id, page) do
url = "#{@zkillboard_api}/kills/systemID/#{system_id}/page/#{page}/"
Logger.debug("[ZkbApi] GET => system=#{system_id}, page=#{page}, url=#{url}")
Logger.debug(fn -> "[ZkbApi] GET => system=#{system_id}, page=#{page}, url=#{url}" end)
try do
resp = Req.get!(url, decode_body: :json)
@@ -56,6 +56,7 @@ defmodule WandererApp.Zkb.KillsProvider.ZkbApi do
[ZkbApi] do_req_get => exception: #{Exception.message(e)}
#{Exception.format_stacktrace(__STACKTRACE__)}
""")
{:error, :exception}
end
end
@@ -72,7 +73,7 @@ defmodule WandererApp.Zkb.KillsProvider.ZkbApi do
:ok
{:error, limit} ->
Logger.debug("[ZkbApi] RATE_LIMIT => limit=#{inspect(limit)}")
Logger.debug(fn -> "[ZkbApi] RATE_LIMIT => limit=#{inspect(limit)}" end)
{:error, :rate_limited}
end
end

View File

@@ -468,6 +468,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
socket
|> assign(
map_loaded?: true,
is_subscription_active?: Map.get(initial_data, :is_subscription_active, false),
user_characters: user_character_eve_ids,
has_tracked_characters?: has_tracked_characters?
)
@@ -530,6 +531,7 @@ defmodule WandererAppWeb.MapCoreEventHandler do
{:ok, connections} = map_id |> WandererApp.Map.list_connections()
{:ok, systems} = map_id |> WandererApp.Map.list_systems()
{:ok, options} = map_id |> WandererApp.Map.get_options()
{:ok, is_subscription_active} = map_id |> WandererApp.Map.is_subscription_active?()
%{
systems:
@@ -537,7 +539,8 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|> Enum.map(fn system -> MapEventHandler.map_ui_system(system, include_static_data?) end),
hubs: hubs,
connections: connections |> Enum.map(&MapEventHandler.map_ui_connection/1),
options: options
options: options,
is_subscription_active: is_subscription_active
}
end

View File

@@ -34,13 +34,15 @@ defmodule WandererAppWeb.MapAuditLive do
case user_permissions.delete_map do
true ->
{:ok, is_subscription_active} = map_id |> WandererApp.Map.is_subscription_active?()
{:ok,
socket
|> assign(
map_id: map_id,
map_name: map_name,
map_slug: map_slug,
map_subscription_active: WandererApp.Map.is_subscription_active?(map_id),
map_subscription_active: is_subscription_active,
activity: activity,
can_undo_types: [:systems_removed],
period: period || "1H",

View File

@@ -155,7 +155,14 @@ defmodule WandererAppWeb.MapEventHandler do
when event_name in @map_signatures_events,
do: MapSignaturesEventHandler.handle_server_event(event, socket)
def handle_event(socket, %{event: event_name} = event)
def handle_event(
%{
assigns: %{
is_subscription_active?: true
}
} = socket,
%{event: event_name} = event
)
when event_name in @map_kills_events,
do: MapKillsEventHandler.handle_server_event(event, socket)
@@ -212,7 +219,15 @@ defmodule WandererAppWeb.MapEventHandler do
when event in @map_activity_ui_events,
do: MapActivityEventHandler.handle_ui_event(event, body, socket)
def handle_ui_event(event, body, socket)
def handle_ui_event(
event,
body,
%{
assigns: %{
is_subscription_active?: true
}
} = socket
)
when event in @map_kills_ui_events,
do: MapKillsEventHandler.handle_ui_event(event, body, socket)