diff --git a/package.json b/package.json index d2f2808..7beb15a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "react-tabs": "^6.1.0", "react-use": "^17.6.0", "sonner": "^2.0.7", - "tailwindcss": "^4.1.11" + "tailwindcss": "^4.1.11", + "use-debounce": "^10.0.6" }, "devDependencies": { "@eslint/js": "^9.32.0", diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index 3f59679..2ff85b1 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -181,8 +181,8 @@ export default function SightingHistoryWidget({ className, title }: SightingHist if (!isSightingModalOpen && modalQueue.length > 0) { const next = modalQueue[0]; - if (next.kind === "NPED") npedSound(); - else hotlistsound(); + // if (next.kind === "NPED") npedSound(); + // else hotlistsound(); setSelectedSighting(next.sighting); setSightingModalOpen(true); diff --git a/src/hooks/useSightingFeed.ts b/src/hooks/useSightingFeed.ts index 4c7d88e..a3d19a8 100644 --- a/src/hooks/useSightingFeed.ts +++ b/src/hooks/useSightingFeed.ts @@ -1,10 +1,13 @@ import { useEffect, useMemo, useRef, useState } from "react"; +import { useDebouncedCallback } from "use-debounce"; import { Query, useQuery } from "@tanstack/react-query"; import type { SightingType } from "../types/types"; -import { useSoundOnChange } from "react-sounds"; +import { useSound } from "react-sounds"; import { useSoundContext } from "../context/SoundContext"; -import { getSoundFileURL } from "../utils/utils"; +import { checkIsHotListHit, getNPEDCategory, getSoundFileURL } from "../utils/utils"; import switchSound from "../assets/sounds/ui/switch.mp3"; +import notification from "../assets/sounds/ui/notification.mp3"; +import popup from "../assets/sounds/ui/popup_open.mp3"; async function fetchSighting(url: string | undefined, ref: number): Promise { const res = await fetch(`${url}${ref}`, { @@ -21,26 +24,21 @@ export function useSightingFeed(url: string | undefined) { const [sessionStarted, setSessionStarted] = useState(false); const [selectedSighting, setSelectedSighting] = useState(null); - const mostRecent = sightings[0] ?? null; - const latestRef = mostRecent?.ref ?? null; - - const first = useRef(true); - const lastSoundAt = useRef(0); - const COOLDOWN_MS = 1500; - const currentRef = useRef(-1); - const lastValidTimestamp = useRef(Date.now()); - - const trigger = useMemo(() => { - if (latestRef == null || !audioArmed) return null; - if (first.current) { - first.current = false; - return Symbol("skip"); + const soundSrcHotlist = useMemo(() => { + if (state?.hotlistSound?.includes(".mp3") || state.hotlistSound?.includes(".wav")) { + const file = state.soundOptions?.find((item) => item.name === state.hotlistSound); + return file?.soundUrl ?? notification; } - const now = Date.now(); - if (now - lastSoundAt.current < COOLDOWN_MS) return Symbol("skip"); - lastSoundAt.current = now; - return latestRef; - }, [audioArmed, latestRef]); + return getSoundFileURL(state?.hotlistSound) ?? notification; + }, [state.hotlistSound, state.soundOptions]); + + const soundSrcNped = useMemo(() => { + if (state?.NPEDsound?.includes(".mp3") || state.NPEDsound?.includes(".wav")) { + const file = state.soundOptions?.find((item) => item.name === state.NPEDsound); + return file?.soundUrl ?? popup; + } + return getSoundFileURL(state.NPEDsound) ?? popup; + }, [state.NPEDsound, state.soundOptions]); const soundSrc = useMemo(() => { if (state?.sightingSound?.includes(".mp3") || state.sightingSound?.includes(".wav")) { @@ -50,6 +48,15 @@ export function useSightingFeed(url: string | undefined) { return getSoundFileURL(state?.sightingSound) ?? switchSound; }, [state.sightingSound, state.soundOptions]); + const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume }); + const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume }); + const { play: sightingSound } = useSound(soundSrc, { volume: state.sightingVolume }); + + const mostRecent = sightings[0] ?? null; + + const currentRef = useRef(-1); + const lastValidTimestamp = useRef(Date.now()); + function refetchInterval(query: Query) { if (!query) return; const data = query.state.data as SightingType | undefined; @@ -78,16 +85,36 @@ export function useSightingFeed(url: string | undefined) { staleTime: 0, }); - //use latestref instead of trigger to revert back + const playHotlistsound = useDebouncedCallback(() => { + hotlistsound(); + }, 500); - useSoundOnChange(soundSrc, trigger, { - volume: state.sightingVolume, - initial: false, - }); + const playNPEDHitSound = useDebouncedCallback(() => { + npedSound(); + }, 500); + + const playSightingHitSound = useDebouncedCallback(() => { + sightingSound(); + }, 500); useEffect(() => { const data = query.data; + if (!data || data.ref === -1) return; + const isHotListHit = checkIsHotListHit(data); + const cat = getNPEDCategory(data); + + const isNPEDHitA = cat === "A"; + const isNPEDHitB = cat === "B"; + const isNPEDHitC = cat === "C"; + + if ((isNPEDHitA && audioArmed) || (isNPEDHitB && audioArmed) || (isNPEDHitC && audioArmed)) { + playNPEDHitSound(); + } else if (isHotListHit && audioArmed) { + playHotlistsound(); + } else if (audioArmed) { + playSightingHitSound(); + } const now = Date.now(); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 0e9f1bc..4f869e5 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -6,7 +6,6 @@ import warning from "../assets/sounds/ui/Warning.wav"; import ding from "../assets/sounds/ui/Ding.wav"; import shutter from "../assets/sounds/ui/shutter.mp3"; import attention from "../assets/sounds/ui/Attention.wav"; - import type { HotlistMatches, SightingType } from "../types/types"; export function getSoundFileURL(name: string) { @@ -158,4 +157,4 @@ export function getHotlistName(obj: HotlistMatches | undefined) { } export const getNPEDCategory = (r?: SightingType | null) => - r?.metadata?.npedJSON?.["NPED CATEGORY"] as "A" | "B" | "C" | undefined; + r?.metadata?.npedJSON?.["NPED CATEGORY"] as "A" | "B" | "C" | "D" | undefined; diff --git a/yarn.lock b/yarn.lock index db3053e..42dbb1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2267,6 +2267,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-debounce@^10.0.6: + version "10.0.6" + resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-10.0.6.tgz#e05060a5e561432ec740c653698f3eb162bd28ec" + integrity sha512-C5OtPyhAZgVoteO9heXMTdW7v/IbFI+8bSVKYCJrSmiWWCLsbUxiBSp4t9v0hNBTGY97bT72ydDIDyGSFWfwXg== + vite@^7.1.0: version "7.1.2" resolved "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz"