From a8abed224674056f6e84df346110baa4075166c0 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Wed, 29 Oct 2025 15:04:40 +0000 Subject: [PATCH] - added feature to cache sounds for cross devices - should work in theory --- .../SettingForms/Sound/SoundUpload.tsx | 7 +++--- .../SettingForms/Sound/SoundUploadCard.tsx | 2 +- .../SightingsWidget/SightingWidget.tsx | 4 ++-- src/components/UI/CardHeader.tsx | 13 +++------- src/hooks/useFileUpload.ts | 6 +++-- src/hooks/useSightingFeed.ts | 6 ++--- src/hooks/usecachedSoundSrc.ts | 14 ++++++++--- src/types/types.ts | 1 + src/utils/cacheSound.ts | 5 +++- src/utils/soundResolver.ts | 24 +++++++++++++++++++ 10 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 src/utils/soundResolver.ts diff --git a/src/components/SettingForms/Sound/SoundUpload.tsx b/src/components/SettingForms/Sound/SoundUpload.tsx index 0ed61a2..7de9851 100644 --- a/src/components/SettingForms/Sound/SoundUpload.tsx +++ b/src/components/SettingForms/Sound/SoundUpload.tsx @@ -18,9 +18,11 @@ const SoundUpload = () => { soundFile: null, soundFileName: "", soundUrl: "", + uploadedAt: Date.now(), }; const handleSubmit = async (values: SoundUploadValue) => { + console.log(values); if (!values.soundFile) { toast.warning("Please select an audio file"); return; @@ -65,15 +67,12 @@ const SoundUpload = () => { className="mt-4 w-full flex flex-col items-center justify-center rounded-2xl border border-slate-800 bg-slate-900/40 p-10 text-center file:px-3 file:border file:border-gray-500 file:rounded-lg file:bg-blue-800 file:mr-5" onChange={(e) => { if (e.target?.files && e.target?.files[0]?.type === "audio/mpeg") { - if (e.target.files[0].size / (1024 * 1024) >= 1) { - toast.error("File is too large. Max size is 1MB"); - return; - } const url = URL.createObjectURL(e.target.files[0]); setFieldValue("soundUrl", url); setFieldValue("name", e.target.files[0].name); setFieldValue("soundFileName", e.target.files[0].name); setFieldValue("soundFile", e.target.files[0]); + setFieldValue("uploadedAt", Date.now()); } else { setFieldError("soundFile", "Not an mp3 file"); toast.error("Not an mp3 file"); diff --git a/src/components/SettingForms/Sound/SoundUploadCard.tsx b/src/components/SettingForms/Sound/SoundUploadCard.tsx index 0c0ea2b..c8884fe 100644 --- a/src/components/SettingForms/Sound/SoundUploadCard.tsx +++ b/src/components/SettingForms/Sound/SoundUploadCard.tsx @@ -4,7 +4,7 @@ import SoundUpload from "./SoundUpload"; const SoundUploadCard = () => { return ( - + diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index 9548880..11827b8 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -44,8 +44,8 @@ export default function SightingHistoryWidget({ className, title }: SightingHist useNow(1000); const { state } = useSoundContext(); - const { src: soundSrcHotlist } = useCachedSoundSrc(state?.hotlistSound, notification); - const { src: soundSrcNped } = useCachedSoundSrc(state?.NPEDsound, popup); + const { src: soundSrcHotlist } = useCachedSoundSrc(state?.hotlistSound, state?.soundOptions, notification); + const { src: soundSrcNped } = useCachedSoundSrc(state?.NPEDsound, state?.soundOptions, popup); const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume }); const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume }); diff --git a/src/components/UI/CardHeader.tsx b/src/components/UI/CardHeader.tsx index 2d7c369..7a3893c 100644 --- a/src/components/UI/CardHeader.tsx +++ b/src/components/UI/CardHeader.tsx @@ -11,25 +11,18 @@ type CameraOverviewHeaderProps = { sighting?: SightingType | null; }; -const CardHeader = ({ - title, - icon, - img, - sighting, -}: CameraOverviewHeaderProps) => { +const CardHeader = ({ title, icon, img, sighting }: CameraOverviewHeaderProps) => { return (
{icon && }

{title}

- {img && ( - Logo - )} + {img && Logo} {sighting?.vrm && }
); diff --git a/src/hooks/useFileUpload.ts b/src/hooks/useFileUpload.ts index b1da0b9..51a70e4 100644 --- a/src/hooks/useFileUpload.ts +++ b/src/hooks/useFileUpload.ts @@ -23,7 +23,9 @@ const uploadFile = async (file: File) => { const getUploadFiles = async ({ queryKey }: { queryKey: string[] }) => { const [, fileName] = queryKey; - const url = `${camBase}/Mobile/${fileName}`; + + // console.log(`${camBase}/Mobile/${fileName}`); + const url = fileName; return getOrCacheBlob(url); }; @@ -38,7 +40,7 @@ export const useFileUpload = ({ queryKey }: UseFileUploadProps) => { mutationFn: (file: File) => uploadFile(file), mutationKey: ["uploadFile"], onError: (err) => toast.error(err ? err.message : ""), - onSuccess: (msg) => toast.success(msg), + onSuccess: async (msg) => toast.success(msg), }); return { query: queryKey ? query : undefined, mutation }; diff --git a/src/hooks/useSightingFeed.ts b/src/hooks/useSightingFeed.ts index 2524041..520b4c0 100644 --- a/src/hooks/useSightingFeed.ts +++ b/src/hooks/useSightingFeed.ts @@ -25,9 +25,9 @@ export function useSightingFeed(url: string | undefined) { const [sessionStarted, setSessionStarted] = useState(false); const [selectedSighting, setSelectedSighting] = useState(null); - const { src: soundSrc } = useCachedSoundSrc(state?.sightingSound, switchSound); - const { src: soundSrcHotlist } = useCachedSoundSrc(state?.hotlistSound, notification); - const { src: soundSrcNped } = useCachedSoundSrc(state?.NPEDsound, popup); + const { src: soundSrc } = useCachedSoundSrc(state?.sightingSound, state?.soundOptions, switchSound); + const { src: soundSrcHotlist } = useCachedSoundSrc(state?.hotlistSound, state?.soundOptions, notification); + const { src: soundSrcNped } = useCachedSoundSrc(state?.NPEDsound, state?.soundOptions, popup); const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume }); const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume }); diff --git a/src/hooks/usecachedSoundSrc.ts b/src/hooks/usecachedSoundSrc.ts index 192998e..ed99974 100644 --- a/src/hooks/usecachedSoundSrc.ts +++ b/src/hooks/usecachedSoundSrc.ts @@ -1,13 +1,20 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useFileUpload } from "./useFileUpload"; import { getSoundFileURL } from "../utils/utils"; +import type { SoundUploadValue } from "../types/types"; +import { resolveSoundSource } from "../utils/soundResolver"; -export function useCachedSoundSrc(selected: string | undefined, fallbackUrl: string) { +export function useCachedSoundSrc( + selected: string | undefined, + soundOptions: SoundUploadValue[] | undefined, + fallbackUrl: string +) { const isUploaded = !!selected && (selected.endsWith(".mp3") || selected.endsWith(".wav")); - const fileName = isUploaded ? selected : undefined; + + const resolved = resolveSoundSource(selected, soundOptions); const { query } = useFileUpload({ - queryKey: fileName ? [fileName] : undefined, + queryKey: resolved?.type === "uploaded" ? [resolved?.url] : undefined, }); const [objectUrl, setObjectUrl] = useState(); @@ -15,6 +22,7 @@ export function useCachedSoundSrc(selected: string | undefined, fallbackUrl: str useEffect(() => { const blob = query?.data; + if (blob instanceof Blob) { if (objRef.current) URL.revokeObjectURL(objRef.current); const url = URL.createObjectURL(blob); diff --git a/src/types/types.ts b/src/types/types.ts index 2eea1c4..906af0b 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -294,6 +294,7 @@ export type SoundUploadValue = { soundFileName?: string; soundFile?: File | null; soundUrl?: string; + uploadedAt?: number; }; export type SoundState = { diff --git a/src/utils/cacheSound.ts b/src/utils/cacheSound.ts index 536e321..8ccc3c3 100644 --- a/src/utils/cacheSound.ts +++ b/src/utils/cacheSound.ts @@ -1,12 +1,15 @@ export async function getOrCacheBlob(url: string) { + console.log(url); const cache = await caches.open("app-sounds-v1"); + if (cache) console.log(cache); const hit = await cache.match(url); if (hit) return await hit.blob(); const res = await fetch(url, { cache: "no-store" }); + console.log("fetching..."); if (!res.ok) throw new Error(`Fetch failed: ${res.status}`); - await cache.put(url, res.clone()); + await cache.put(url, res.clone()); return await res.blob(); } diff --git a/src/utils/soundResolver.ts b/src/utils/soundResolver.ts new file mode 100644 index 0000000..5da4bd7 --- /dev/null +++ b/src/utils/soundResolver.ts @@ -0,0 +1,24 @@ +import { getSoundFileURL } from "./utils"; +import { CAM_BASE } from "./config"; +import type { SoundUploadValue } from "../types/types"; + +export function resolveSoundSource( + selected: string | undefined, + soundOptions: SoundUploadValue[] | undefined +): { type: "uploaded"; url: string } | { type: "builtin"; url: string } | undefined { + if (!selected) return undefined; + + const isFile = selected.endsWith(".mp3") || selected.endsWith(".wav"); + + if (isFile) { + const match = soundOptions?.find((o) => o.soundFileName === selected); + const version = match?.uploadedAt ?? 0; + const url = `${CAM_BASE}/Mobile/${encodeURIComponent(selected)}?v=${version}`; + return { type: "uploaded", url }; + } + + const builtin = getSoundFileURL(selected); + if (builtin) return { type: "builtin", url: builtin }; + + return undefined; +}