refactor: split up node hooks (#173)
Some checks failed
Build / 🚀 Deploy to test env (fly.io) (push) Has been cancelled
Build / Manual Approval (push) Has been cancelled
Build / 🛠 Build (1.17, 18.x, 27) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/amd64) (push) Has been cancelled
Build / 🛠 Build Docker Images (linux/arm64) (push) Has been cancelled
Build / merge (push) Has been cancelled
Build / 🏷 Create Release (push) Has been cancelled

This commit is contained in:
guarzo
2025-03-01 04:45:34 -05:00
committed by GitHub
parent 0568533550
commit 5ac8ccbe5c
10 changed files with 300 additions and 196 deletions

View File

@@ -1,9 +1,10 @@
import { useCallback, useMemo, useState, useEffect, useRef } from 'react';
import debounce from 'lodash.debounce';
import { OutCommand } from '@/hooks/Mapper/types/mapHandlers';
import { Commands, OutCommand } from '@/hooks/Mapper/types/mapHandlers';
import { DetailedKill } from '@/hooks/Mapper/types/kills';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useKillsWidgetSettings } from './useKillsWidgetSettings';
import { useMapEventListener, MapEvent } from '@/hooks/Mapper/events';
interface UseSystemKillsProps {
systemId?: string;
@@ -13,16 +14,17 @@ interface UseSystemKillsProps {
sinceHours?: number;
}
function combineKills(existing: DetailedKill[], incoming: DetailedKill[], sinceHours: number): DetailedKill[] {
function combineKills(
existing: DetailedKill[],
incoming: DetailedKill[],
sinceHours: number
): DetailedKill[] {
const cutoff = Date.now() - sinceHours * 60 * 60 * 1000;
const byId: Record<string, DetailedKill> = {};
for (const kill of [...existing, ...incoming]) {
if (!kill.kill_time) {
continue;
}
if (!kill.kill_time) continue;
const killTimeMs = new Date(kill.kill_time).valueOf();
if (killTimeMs >= cutoff) {
byId[kill.killmail_id] = kill;
}
@@ -31,101 +33,117 @@ function combineKills(existing: DetailedKill[], incoming: DetailedKill[], sinceH
return Object.values(byId);
}
export function useSystemKills({ systemId, outCommand, showAllVisible = false, sinceHours = 24 }: UseSystemKillsProps) {
interface DetailedKillsEvent extends MapEvent<Commands> {
payload: Record<string, DetailedKill[]>;
}
export function useSystemKills({
systemId,
outCommand,
showAllVisible = false,
sinceHours = 24,
}: UseSystemKillsProps) {
const { data, update } = useMapRootState();
const { detailedKills = {}, systems = [] } = data;
const [settings] = useKillsWidgetSettings();
const excludedSystems = settings.excludedSystems;
// When showing all visible kills, filter out excluded systems;
// when showAllVisible is false, ignore the exclusion filter.
const updateDetailedKills = useCallback((newKillsMap: Record<string, DetailedKill[]>) => {
update((prev) => {
const oldKills = prev.detailedKills ?? {};
const updated = { ...oldKills };
for (const [sid, killsArr] of Object.entries(newKillsMap)) {
updated[sid] = killsArr;
}
return { ...prev, detailedKills: updated };
}, true);
}, [update]);
useMapEventListener((event: MapEvent<Commands>) => {
if (event.name === Commands.detailedKillsUpdated) {
const detailedEvent = event as DetailedKillsEvent;
if (systemId && !Object.keys(detailedEvent.payload).includes(systemId.toString())) {
return false;
}
updateDetailedKills(detailedEvent.payload);
return true;
}
return false;
});
const effectiveSystemIds = useMemo(() => {
if (showAllVisible) {
return systems.map(s => s.id).filter(id => !excludedSystems.includes(Number(id)));
return systems.map((s) => s.id).filter((id) => !excludedSystems.includes(Number(id)));
}
return systems.map(s => s.id);
return systems.map((s) => s.id);
}, [systems, excludedSystems, showAllVisible]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const didFallbackFetch = useRef(Object.keys(detailedKills).length !== 0);
const mergeKillsIntoGlobal = useCallback(
(killsMap: Record<string, DetailedKill[]>) => {
update(prev => {
const oldMap = prev.detailedKills ?? {};
const updated: Record<string, DetailedKill[]> = { ...oldMap };
const mergeKillsIntoGlobal = useCallback((killsMap: Record<string, DetailedKill[]>) => {
update((prev) => {
const oldMap = prev.detailedKills ?? {};
const updated: Record<string, DetailedKill[]> = { ...oldMap };
for (const [sid, newKills] of Object.entries(killsMap)) {
const existing = updated[sid] ?? [];
const combined = combineKills(existing, newKills, sinceHours);
updated[sid] = combined;
}
return {
...prev,
detailedKills: updated,
};
});
},
[update, sinceHours],
);
const fetchKills = useCallback(
async (forceFallback = false) => {
setIsLoading(true);
setError(null);
try {
let eventType: OutCommand;
let requestData: Record<string, unknown>;
if (showAllVisible || forceFallback) {
eventType = OutCommand.getSystemsKills;
requestData = {
system_ids: effectiveSystemIds,
since_hours: sinceHours,
};
} else if (systemId) {
eventType = OutCommand.getSystemKills;
requestData = {
system_id: systemId,
since_hours: sinceHours,
};
} else {
// If there's no system and not showing all, do nothing
setIsLoading(false);
return;
}
const resp = await outCommand({
type: eventType,
data: requestData,
});
// Single system => `resp.kills`
if (resp?.kills) {
const arr = resp.kills as DetailedKill[];
const sid = systemId ?? 'unknown';
mergeKillsIntoGlobal({ [sid]: arr });
}
// multiple systems => `resp.systems_kills`
else if (resp?.systems_kills) {
mergeKillsIntoGlobal(resp.systems_kills as Record<string, DetailedKill[]>);
} else {
console.warn('[useSystemKills] Unexpected kills response =>', resp);
}
} catch (err) {
console.error('[useSystemKills] Failed to fetch kills:', err);
setError(err instanceof Error ? err.message : 'Error fetching kills');
} finally {
setIsLoading(false);
for (const [sid, newKills] of Object.entries(killsMap)) {
const existing = updated[sid] ?? [];
const combined = combineKills(existing, newKills, sinceHours);
updated[sid] = combined;
}
},
[showAllVisible, systemId, outCommand, effectiveSystemIds, sinceHours, mergeKillsIntoGlobal],
);
return { ...prev, detailedKills: updated };
});
}, [update, sinceHours]);
const fetchKills = useCallback(async (forceFallback = false) => {
setIsLoading(true);
setError(null);
try {
let eventType: OutCommand;
let requestData: Record<string, unknown>;
if (showAllVisible || forceFallback) {
eventType = OutCommand.getSystemsKills;
requestData = {
system_ids: effectiveSystemIds,
since_hours: sinceHours,
};
} else if (systemId) {
eventType = OutCommand.getSystemKills;
requestData = {
system_id: systemId,
since_hours: sinceHours,
};
} else {
setIsLoading(false);
return;
}
const resp = await outCommand({
type: eventType,
data: requestData,
});
if (resp?.kills) {
const arr = resp.kills as DetailedKill[];
const sid = systemId ?? 'unknown';
mergeKillsIntoGlobal({ [sid]: arr });
}
else if (resp?.systems_kills) {
mergeKillsIntoGlobal(resp.systems_kills as Record<string, DetailedKill[]>);
} else {
console.warn('[useSystemKills] Unexpected kills response =>', resp);
}
} catch (err) {
console.error('[useSystemKills] Failed to fetch kills:', err);
setError(err instanceof Error ? err.message : 'Error fetching kills');
} finally {
setIsLoading(false);
}
}, [showAllVisible, systemId, outCommand, effectiveSystemIds, sinceHours, mergeKillsIntoGlobal]);
const debouncedFetchKills = useMemo(
() =>
@@ -138,12 +156,11 @@ export function useSystemKills({ systemId, outCommand, showAllVisible = false, s
const finalKills = useMemo(() => {
if (showAllVisible) {
return effectiveSystemIds.flatMap(sid => detailedKills[sid] ?? []);
return effectiveSystemIds.flatMap((sid) => detailedKills[sid] ?? []);
} else if (systemId) {
return detailedKills[systemId] ?? [];
} else if (didFallbackFetch.current) {
// if we already did a fallback, we may have data for multiple systems
return effectiveSystemIds.flatMap(sid => detailedKills[sid] ?? []);
return effectiveSystemIds.flatMap((sid) => detailedKills[sid] ?? []);
}
return [];
}, [showAllVisible, systemId, effectiveSystemIds, detailedKills]);
@@ -153,9 +170,8 @@ export function useSystemKills({ systemId, outCommand, showAllVisible = false, s
useEffect(() => {
if (!systemId && !showAllVisible && !didFallbackFetch.current) {
didFallbackFetch.current = true;
// Cancel any queued debounced calls, then do the fallback.
debouncedFetchKills.cancel();
fetchKills(true); // forceFallback => fetch as though showAllVisible is true
fetchKills(true);
}
}, [systemId, showAllVisible, debouncedFetchKills, fetchKills]);
@@ -164,14 +180,13 @@ export function useSystemKills({ systemId, outCommand, showAllVisible = false, s
if (showAllVisible || systemId) {
debouncedFetchKills();
// Clean up the debounce on unmount or changes
return () => debouncedFetchKills.cancel();
}
}, [showAllVisible, systemId, effectiveSystemIds, debouncedFetchKills]);
const refetch = useCallback(() => {
debouncedFetchKills.cancel();
fetchKills(); // immediate (non-debounced) call
fetchKills();
}, [debouncedFetchKills, fetchKills]);
return {