- uploaded files seems to work on desktop version

This commit is contained in:
2025-10-28 13:53:11 +00:00
parent 907555cb0d
commit c8eed55801
5 changed files with 68 additions and 81 deletions

View File

@@ -5,14 +5,10 @@ import { useSoundContext } from "../../../context/SoundContext";
import { useCameraBlackboard } from "../../../hooks/useCameraBlackboard"; import { useCameraBlackboard } from "../../../hooks/useCameraBlackboard";
import { toast } from "sonner"; import { toast } from "sonner";
import SliderComponent from "../../UI/Slider"; import SliderComponent from "../../UI/Slider";
import { useFileUpload } from "../../../hooks/useFileUpload";
const SoundSettingsFields = () => { const SoundSettingsFields = () => {
const { state, dispatch } = useSoundContext(); const { state, dispatch } = useSoundContext();
const { mutation } = useCameraBlackboard(); const { mutation } = useCameraBlackboard();
const { query: fileQuery } = useFileUpload({
queryKey: state.sightingSound ? [state.sightingSound] : undefined,
});
const hotlists: Hotlist[] = state.hotlists; const hotlists: Hotlist[] = state.hotlists;
@@ -27,9 +23,7 @@ const SoundSettingsFields = () => {
hotlistSound: state.hotlistSound ?? "notification", hotlistSound: state.hotlistSound ?? "notification",
hotlists, hotlists,
}; };
const handleSyce = () => {
fileQuery?.refetch();
};
const handleSubmit = async (values: FormValues) => { const handleSubmit = async (values: FormValues) => {
const updatedValues = { const updatedValues = {
...values, ...values,
@@ -149,9 +143,6 @@ const SoundSettingsFields = () => {
> >
Save Settings Save Settings
</button> </button>
<button onClick={handleSyce} type="button">
click
</button>
</Form> </Form>
)} )}
</Formik> </Formik>

View File

@@ -41,8 +41,7 @@ const SoundUpload = () => {
path: "soundSettings", path: "soundSettings",
value: updatedValues, value: updatedValues,
}); });
const responsee = await fileMutation.mutateAsync(values.soundFile); await fileMutation.mutateAsync(values.soundFile);
console.log(responsee);
if (result.reason !== "OK") { if (result.reason !== "OK") {
toast.error("Cannot update sound settings"); toast.error("Cannot update sound settings");
} }
@@ -52,7 +51,7 @@ const SoundUpload = () => {
return ( return (
<Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize> <Formik initialValues={initialValues} onSubmit={handleSubmit} enableReinitialize>
{({ setFieldValue, errors, setFieldError, values }) => ( {({ setFieldValue, errors, setFieldError }) => (
<Form> <Form>
<label htmlFor="soundFile" className=""> <label htmlFor="soundFile" className="">
Sound File Sound File
@@ -66,6 +65,10 @@ 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" 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) => { onChange={(e) => {
if (e.target?.files && e.target?.files[0]?.type === "audio/mpeg") { 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]); const url = URL.createObjectURL(e.target.files[0]);
setFieldValue("soundUrl", url); setFieldValue("soundUrl", url);
setFieldValue("name", e.target.files[0].name); setFieldValue("name", e.target.files[0].name);
@@ -80,11 +83,6 @@ const SoundUpload = () => {
</FormGroup> </FormGroup>
<div className="mt-4 flex flex-col items-center justify-center rounded-2xl border border-slate-800 bg-slate-900/40 p-10 text-center"> <div className="mt-4 flex flex-col items-center justify-center rounded-2xl border border-slate-800 bg-slate-900/40 p-10 text-center">
{!values.soundFile && (
<div className="mb-3 rounded-xl bg-slate-800 px-3 py-1 text-xs uppercase tracking-wider text-slate-400">
No uploaded sound files
</div>
)}
<p className="max-w-md text-slate-300"> <p className="max-w-md text-slate-300">
Uploaded Sound files will appear in the <span className="font-bold">drop downs</span> once they are Uploaded Sound files will appear in the <span className="font-bold">drop downs</span> once they are
uploaded. They can be used for any <span className="text-blue-400">Sighting,</span>{" "} uploaded. They can be used for any <span className="text-blue-400">Sighting,</span>{" "}

View File

@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { HitKind, QueuedHit, ReducedSightingType, SightingType } from "../../types/types"; import type { HitKind, QueuedHit, ReducedSightingType, SightingType } from "../../types/types";
import { BLANK_IMG, getSoundFileURL } from "../../utils/utils"; import { BLANK_IMG } from "../../utils/utils";
import NumberPlate from "../PlateStack/NumberPlate"; import NumberPlate from "../PlateStack/NumberPlate";
import Card from "../UI/Card"; import Card from "../UI/Card";
import CardHeader from "../UI/CardHeader"; import CardHeader from "../UI/CardHeader";
@@ -19,6 +19,7 @@ import { useIntegrationsContext } from "../../context/IntegrationsContext";
import { useSoundContext } from "../../context/SoundContext"; import { useSoundContext } from "../../context/SoundContext";
import Loading from "../UI/Loading"; import Loading from "../UI/Loading";
import { checkIsHotListHit, getNPEDCategory } from "../../utils/utils"; import { checkIsHotListHit, getNPEDCategory } from "../../utils/utils";
import { useCachedSoundSrc } from "../../hooks/usecachedSoundSrc";
function useNow(tickMs = 1000) { function useNow(tickMs = 1000) {
const [, setNow] = useState(() => Date.now()); const [, setNow] = useState(() => Date.now());
@@ -43,21 +44,8 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
useNow(1000); useNow(1000);
const { state } = useSoundContext(); const { state } = useSoundContext();
const soundSrcNped = useMemo(() => { const { src: soundSrcHotlist } = useCachedSoundSrc(state?.hotlistSound, notification);
if (state?.NPEDsound?.includes(".mp3") || state.NPEDsound?.includes(".wav")) { const { src: soundSrcNped } = useCachedSoundSrc(state?.NPEDsound, popup);
const file = state.soundOptions?.find((item) => item.name === state.NPEDsound);
return file?.soundUrl ?? popup;
}
return getSoundFileURL(state.NPEDsound) ?? popup;
}, [state.NPEDsound, state.soundOptions]);
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;
}
return getSoundFileURL(state?.hotlistSound) ?? notification;
}, [state.hotlistSound, state.soundOptions]);
const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume }); const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume });
const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume }); const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume });
@@ -181,8 +169,8 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
if (!isSightingModalOpen && modalQueue.length > 0) { if (!isSightingModalOpen && modalQueue.length > 0) {
const next = modalQueue[0]; const next = modalQueue[0];
// if (next.kind === "NPED") npedSound(); if (next.kind === "NPED") npedSound();
// else hotlistsound(); else hotlistsound();
setSelectedSighting(next.sighting); setSelectedSighting(next.sighting);
setSightingModalOpen(true); setSightingModalOpen(true);

View File

@@ -1,14 +1,14 @@
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useDebouncedCallback } from "use-debounce"; import { useDebouncedCallback } from "use-debounce";
import { Query, useQuery } from "@tanstack/react-query"; import { Query, useQuery } from "@tanstack/react-query";
import type { SightingType } from "../types/types"; import type { SightingType } from "../types/types";
import { useSound } from "react-sounds"; import { useSound } from "react-sounds";
import { useSoundContext } from "../context/SoundContext"; import { useSoundContext } from "../context/SoundContext";
import { checkIsHotListHit, getNPEDCategory, getSoundFileURL } from "../utils/utils"; import { checkIsHotListHit, getNPEDCategory } from "../utils/utils";
import switchSound from "../assets/sounds/ui/switch.mp3"; import switchSound from "../assets/sounds/ui/switch.mp3";
import notification from "../assets/sounds/ui/notification.mp3"; import notification from "../assets/sounds/ui/notification.mp3";
import popup from "../assets/sounds/ui/popup_open.mp3"; import popup from "../assets/sounds/ui/popup_open.mp3";
import { useFileUpload } from "./useFileUpload"; import { useCachedSoundSrc } from "./usecachedSoundSrc";
async function fetchSighting(url: string | undefined, ref: number): Promise<SightingType> { async function fetchSighting(url: string | undefined, ref: number): Promise<SightingType> {
const res = await fetch(`${url}${ref}`, { const res = await fetch(`${url}${ref}`, {
@@ -19,53 +19,15 @@ async function fetchSighting(url: string | undefined, ref: number): Promise<Sigh
} }
export function useSightingFeed(url: string | undefined) { export function useSightingFeed(url: string | undefined) {
const { state, audioArmed, dispatch } = useSoundContext(); const { state, audioArmed } = useSoundContext();
const [sightings, setSightings] = useState<SightingType[]>([]); const [sightings, setSightings] = useState<SightingType[]>([]);
const [selectedRef, setSelectedRef] = useState<number | null>(null); const [selectedRef, setSelectedRef] = useState<number | null>(null);
const [sessionStarted, setSessionStarted] = useState(false); const [sessionStarted, setSessionStarted] = useState(false);
const [selectedSighting, setSelectedSighting] = useState<SightingType | null>(null); const [selectedSighting, setSelectedSighting] = useState<SightingType | null>(null);
const isUploaded = state?.sightingSound?.endsWith(".mp3") || state?.sightingSound?.endsWith(".wav"); const { src: soundSrc } = useCachedSoundSrc(state?.sightingSound, switchSound);
const { src: soundSrcHotlist } = useCachedSoundSrc(state?.hotlistSound, notification);
const fileName = isUploaded ? state.sightingSound : switchSound; const { src: soundSrcNped } = useCachedSoundSrc(state?.NPEDsound, popup);
const { query: fileQuery } = useFileUpload({
queryKey: fileName ? [fileName] : undefined,
});
const objUrlRef = useRef<string | null>(null);
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;
}
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 (isUploaded && fileQuery?.data instanceof Blob) {
if (objUrlRef.current) URL.revokeObjectURL(objUrlRef.current);
objUrlRef.current = URL.createObjectURL(fileQuery.data);
console.log(fileQuery.data);
return objUrlRef.current;
}
return getSoundFileURL(state?.sightingSound) ?? switchSound;
}, [isUploaded, fileQuery?.data, state?.sightingSound]);
useEffect(() => {
return () => {
if (objUrlRef.current) URL.revokeObjectURL(objUrlRef.current);
};
}, []);
const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume }); const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume });
const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume }); const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume });

View File

@@ -0,0 +1,48 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useFileUpload } from "./useFileUpload";
import { getSoundFileURL } from "../utils/utils";
export function useCachedSoundSrc(selected: string | undefined, fallbackUrl: string) {
const isUploaded = !!selected && (selected.endsWith(".mp3") || selected.endsWith(".wav"));
const fileName = isUploaded ? selected : undefined;
const { query } = useFileUpload({
queryKey: fileName ? [fileName] : undefined,
});
const [objectUrl, setObjectUrl] = useState<string>();
const objRef = useRef<string | null>(null);
useEffect(() => {
const blob = query?.data;
if (blob instanceof Blob) {
if (objRef.current) URL.revokeObjectURL(objRef.current);
const url = URL.createObjectURL(blob);
objRef.current = url;
setObjectUrl(url);
} else {
if (objRef.current) URL.revokeObjectURL(objRef.current);
objRef.current = null;
setObjectUrl(undefined);
}
}, [query?.data]);
useEffect(() => {
return () => {
if (objRef.current) URL.revokeObjectURL(objRef.current);
};
}, []);
const src = useMemo(() => {
if (isUploaded && objectUrl) return objectUrl;
if (!selected) return fallbackUrl;
return getSoundFileURL(selected) ?? fallbackUrl;
}, [isUploaded, objectUrl, selected, fallbackUrl]);
return {
src,
isUploaded,
isLoading: !!query?.isLoading,
error: (query?.error as Error) || undefined,
};
}