- refactored code around hotlist hits and sounds

- improved performace for sounds playing
This commit is contained in:
2025-10-09 14:11:58 +01:00
parent 87be346c3b
commit 063815cac0
7 changed files with 57 additions and 85 deletions

View File

@@ -8,6 +8,7 @@ import { useAlertHitContext } from "../../context/AlertHitContext";
import NPED_CAT_A from "/NPED_Cat_A.svg"; import NPED_CAT_A from "/NPED_Cat_A.svg";
import NPED_CAT_B from "/NPED_Cat_B.svg"; import NPED_CAT_B from "/NPED_Cat_B.svg";
import NPED_CAT_C from "/NPED_Cat_C.svg"; import NPED_CAT_C from "/NPED_Cat_C.svg";
import { checkIsHotListHit } from "../../utils/utils";
type AlertItemProps = { type AlertItemProps = {
item: SightingType; item: SightingType;
@@ -19,9 +20,8 @@ const AlertItem = ({ item }: AlertItemProps) => {
// const {d} = useCameraBlackboard(); // const {d} = useCameraBlackboard();
const motionAway = (item?.motion ?? "").toUpperCase() === "AWAY"; const motionAway = (item?.motion ?? "").toUpperCase() === "AWAY";
// [34].metadata.hotlistMatches["MAV_Hotlist.csv"]
//check if true is in any hotlist property const isHotListHit = checkIsHotListHit(item);
const isHotListHit = item?.metadata?.hotlistMatches?.Hotlist0 === true;
const isNPEDHitA = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "A"; const isNPEDHitA = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "B"; const isNPEDHitB = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "C"; const isNPEDHitC = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";

View File

@@ -10,6 +10,7 @@ import HotListImg from "/Hotlist_Hit.svg";
import NPED_CAT_A from "/NPED_Cat_A.svg"; import NPED_CAT_A from "/NPED_Cat_A.svg";
import NPED_CAT_B from "/NPED_Cat_B.svg"; import NPED_CAT_B from "/NPED_Cat_B.svg";
import NPED_CAT_C from "/NPED_Cat_C.svg"; import NPED_CAT_C from "/NPED_Cat_C.svg";
import { checkIsHotListHit } from "../../utils/utils";
type SightingModalProps = { type SightingModalProps = {
isSightingModalOpen: boolean; isSightingModalOpen: boolean;
@@ -65,7 +66,7 @@ const SightingModal = ({
}; };
const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY"; const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY";
const isHotListHit = sighting?.metadata?.hotlistMatches?.Hotlist0 === true; const isHotListHit = checkIsHotListHit(sighting);
const isNPEDHitA = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "A"; const isNPEDHitA = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "B"; const isNPEDHitB = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "C"; const isNPEDHitC = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";

View File

@@ -13,10 +13,12 @@ import NPED_CAT_A from "/NPED_Cat_A.svg";
import NPED_CAT_B from "/NPED_Cat_B.svg"; import NPED_CAT_B from "/NPED_Cat_B.svg";
import NPED_CAT_C from "/NPED_Cat_C.svg"; import NPED_CAT_C from "/NPED_Cat_C.svg";
import popup from "../../assets/sounds/ui/popup_open.mp3"; import popup from "../../assets/sounds/ui/popup_open.mp3";
import notification from "../../assets/sounds/ui/notification.mp3";
import { useSound } from "react-sounds"; import { useSound } from "react-sounds";
import { useNPEDContext } from "../../context/NPEDUserContext"; import { useNPEDContext } from "../../context/NPEDUserContext";
import { useSoundContext } from "../../context/SoundContext"; import { useSoundContext } from "../../context/SoundContext";
import Loading from "../UI/Loading"; import Loading from "../UI/Loading";
import { checkIsHotListHit } from "../../utils/utils";
function useNow(tickMs = 1000) { function useNow(tickMs = 1000) {
const [, setNow] = useState(() => Date.now()); const [, setNow] = useState(() => Date.now());
@@ -43,11 +45,16 @@ export default function SightingHistoryWidget({
useNow(1000); useNow(1000);
const { state } = useSoundContext(); const { state } = useSoundContext();
const soundSrc = useMemo(() => { const soundSrcNped = useMemo(() => {
return getSoundFileURL(state.NPEDsound) ?? popup; return getSoundFileURL(state.NPEDsound) ?? popup;
}, [state.NPEDsound]); }, [state.NPEDsound]);
const { play } = useSound(soundSrc); const soundSrcHotlist = useMemo(() => {
return getSoundFileURL(state?.hotlists?.[0]?.sound) ?? notification;
}, [state.hotlists]);
const { play: npedSound } = useSound(soundSrcNped);
const { play: hotlistsound } = useSound(soundSrcHotlist);
const { const {
sightings, sightings,
setSelectedSighting, setSelectedSighting,
@@ -78,6 +85,7 @@ export default function SightingHistoryWidget({
}, [mostRecent, sessionStarted, setSessionList]); }, [mostRecent, sessionStarted, setSessionList]);
const hasAutoOpenedRef = useRef(false); const hasAutoOpenedRef = useRef(false);
const npedRef = useRef(false);
const onRowClick = useCallback( const onRowClick = useCallback(
(sighting: SightingType) => { (sighting: SightingType) => {
@@ -98,32 +106,52 @@ export default function SightingHistoryWidget({
const isNPEDHitA = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "A"; const isNPEDHitA = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B"; const isNPEDHitB = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C"; const isNPEDHitC = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
const isNPEDHitD = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
if (isNPEDHitA || isNPEDHitB || isNPEDHitC) { if (isNPEDHitA || isNPEDHitB || isNPEDHitC || isNPEDHitD) {
dispatch({ dispatch({
type: "ADD", type: "ADD",
payload: obj, payload: obj,
}); });
} }
}); });
}, [dispatch, rows]); }, [dispatch]);
useEffect(() => { useEffect(() => {
if (hasAutoOpenedRef.current) return; if (hasAutoOpenedRef.current || npedRef.current) return;
const firstHot = rows?.find((r) => { const firstNPED = rows.find((r) => {
const isHotListHit = r?.metadata?.hotlistMatches?.Hotlist0 === true;
const isNPEDHitA = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "A"; const isNPEDHitA = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "B"; const isNPEDHitB = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "C"; const isNPEDHitC = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
return isNPEDHitA || isNPEDHitB || isNPEDHitC || isHotListHit; const isNPEDHitD = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
return isNPEDHitA || isNPEDHitB || isNPEDHitC || isNPEDHitD;
}); });
const firstHot = rows?.find((r) => {
const isHotListHit = checkIsHotListHit(r);
return isHotListHit;
});
if (firstNPED) {
setSelectedSighting(firstNPED);
console.log("first");
npedSound();
setSightingModalOpen(true);
npedRef.current = true;
}
if (firstHot) { if (firstHot) {
setSelectedSighting(firstHot); setSelectedSighting(firstHot);
play(); hotlistsound();
setSightingModalOpen(true); setSightingModalOpen(true);
hasAutoOpenedRef.current = true; hasAutoOpenedRef.current = true;
} }
}, [play, rows, setSelectedSighting, setSightingModalOpen]); }, [
hotlistsound,
npedSound,
rows,
setSelectedSighting,
setSightingModalOpen,
]);
const handleClose = () => { const handleClose = () => {
setSightingModalOpen(false); setSightingModalOpen(false);
@@ -152,11 +180,8 @@ export default function SightingHistoryWidget({
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B"; obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = const isNPEDHitC =
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C"; obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
const isNPEDHitD =
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY"; const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY";
const isHotListHit = const isHotListHit = checkIsHotListHit(obj);
obj?.metadata?.hotlistMatches?.Hotlist0 === true;
return ( return (
<div <div
key={obj.ref} key={obj.ref}
@@ -164,9 +189,7 @@ export default function SightingHistoryWidget({
onClick={() => onRowClick(obj)} onClick={() => onRowClick(obj)}
> >
<div <div
className={`flex items-center gap-3 mt-2 justify-between ${ className={`flex items-center gap-3 mt-2 justify-between `}
isNPEDHitD ? " border-amber-600" : ""
}`}
> >
<div className={`border p-1 `}> <div className={`border p-1 `}>
<img <img

View File

@@ -15,9 +15,7 @@ type SightingFeedContextType = {
isError: boolean; isError: boolean;
isLoading: boolean; isLoading: boolean;
data: SightingType | undefined; data: SightingType | undefined;
sessionList: SightingType[];
sessionStarted: boolean; sessionStarted: boolean;
setSessionStarted: (started: boolean) => void;
}; };
export const SightingFeedContext = createContext< export const SightingFeedContext = createContext<

View File

@@ -23,9 +23,7 @@ export const SightingFeedProvider = ({
setSelectedSighting, setSelectedSighting,
selectedSighting, selectedSighting,
mostRecent, mostRecent,
sessionList,
sessionStarted, sessionStarted,
setSessionStarted,
} = useSightingFeed(url); } = useSightingFeed(url);
const [isSightingModalOpen, setSightingModalOpen] = useState(false); const [isSightingModalOpen, setSightingModalOpen] = useState(false);
@@ -45,9 +43,7 @@ export const SightingFeedProvider = ({
isLoading, isLoading,
side, side,
data, data,
sessionList,
sessionStarted, sessionStarted,
setSessionStarted,
}} }}
> >
{children} {children}

View File

@@ -3,7 +3,7 @@ import type { SoundAction, SoundState } from "../../types/types";
export const initialState: SoundState = { export const initialState: SoundState = {
sightingSound: "switch", sightingSound: "switch",
NPEDsound: "popup", NPEDsound: "popup",
hotlists: [], hotlists: [{ name: "hotlistName", sound: "notification" }],
soundOptions: [ soundOptions: [
{ name: "switch (Default)", soundFile: null }, { name: "switch (Default)", soundFile: null },
{ name: "popup", soundFile: null }, { name: "popup", soundFile: null },

View File

@@ -1,6 +1,7 @@
import switchSound from "../assets/sounds/ui/switch.mp3"; import switchSound from "../assets/sounds/ui/switch.mp3";
import popup from "../assets/sounds/ui/popup_open.mp3"; import popup from "../assets/sounds/ui/popup_open.mp3";
import notification from "../assets/sounds/ui/notification.mp3"; import notification from "../assets/sounds/ui/notification.mp3";
import type { SightingType } from "../types/types";
export function getSoundFileURL(name: string) { export function getSoundFileURL(name: string) {
const sounds: Record<string, string> = { const sounds: Record<string, string> = {
@@ -129,59 +130,12 @@ export function drawRects(
}); });
} }
// setSelectedRef(data?.ref); export const checkIsHotListHit = (sigthing: SightingType | null) => {
if (!sigthing) return;
//setItems(data); if (sigthing?.metadata?.hotlistMatches) {
const isHotListHit = Object.values(
// const selected = useMemo( sigthing?.metadata?.hotlistMatches
// () => ).includes(true);
// selectedRef == null return isHotListHit;
// ? null }
// : items.find((x) => x?.ref === selectedRef) ?? null, };
// [items, selectedRef]
// );
// const effectiveSelected = selected ?? mostRecent ?? null;
// useEffect(() => {
// let delay = pollMs;
// let dead = false;
// const controller = new AbortController();
// async function tick() {
// try {
// // Pause when tab hidden to save CPU/network
// if (document.hidden) {
// setTimeout(tick, Math.max(delay, 2000));
// return;
// }
// if (obj && typeof obj.ref === "number" && obj.ref > -1) {
// setItems((prev) => {
// const next = [obj, ...prev].slice(0, limit);
// // maintain selection if still present; otherwise select newest if allowed
// const stillExists =
// selectedRef != null && next.some((x) => x?.ref === selectedRef);
// if (autoSelectLatest && !stillExists) {
// setSelectedRef(obj.ref);
// }
// return next;
// });
// setMostRecent(obj);
// mostRecentRef.current = obj.ref;
// delay = pollMs; // reset backoff on success
// }
// } catch {
// // exponential backoff (max 10s)
// delay = Math.min(delay * 2, 10000);
// } finally {
// if (!dead) setTimeout(tick, delay);
// }
// }
// const t = setTimeout(tick, pollMs);
// return () => {
// dead = true;
// controller.abort();
// clearTimeout(t);
// };
// }, [baseUrl, limit, pollMs, autoSelectLatest, selectedRef]);