diff --git a/package.json b/package.json index 93cc5da..d2f2808 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "country-flag-icons": "^1.5.19", "formik": "^2.4.6", "howler": "^2.2.4", + "rc-slider": "^11.1.9", "react": "^19.1.1", "react-dom": "^19.1.1", "react-modal": "^3.16.3", diff --git a/src/assets/sounds/ui/Beep.wav b/src/assets/sounds/ui/Beep.wav new file mode 100644 index 0000000..015e1f6 Binary files /dev/null and b/src/assets/sounds/ui/Beep.wav differ diff --git a/src/assets/sounds/ui/Ding.wav b/src/assets/sounds/ui/Ding.wav new file mode 100644 index 0000000..91a65a3 Binary files /dev/null and b/src/assets/sounds/ui/Ding.wav differ diff --git a/src/assets/sounds/ui/Warning.wav b/src/assets/sounds/ui/Warning.wav new file mode 100644 index 0000000..e9b946e Binary files /dev/null and b/src/assets/sounds/ui/Warning.wav differ diff --git a/src/assets/sounds/ui/readClick.wav b/src/assets/sounds/ui/readClick.wav new file mode 100644 index 0000000..3892721 Binary files /dev/null and b/src/assets/sounds/ui/readClick.wav differ diff --git a/src/assets/sounds/ui/shutter.mp3 b/src/assets/sounds/ui/shutter.mp3 new file mode 100644 index 0000000..9729d79 Binary files /dev/null and b/src/assets/sounds/ui/shutter.mp3 differ diff --git a/src/components/SettingForms/BearerType/BearerTypeFields.tsx b/src/components/SettingForms/BearerType/BearerTypeFields.tsx index b06dde3..d2207c6 100644 --- a/src/components/SettingForms/BearerType/BearerTypeFields.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeFields.tsx @@ -29,11 +29,7 @@ const BearerTypeFields = () => { }; return ( - + {({ isSubmitting }) => (
@@ -60,11 +56,9 @@ const BearerTypeFields = () => {
diff --git a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx index 5fe42fe..ef554b4 100644 --- a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx +++ b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx @@ -4,10 +4,7 @@ import { useEffect, useState } from "react"; import { faEyeSlash, faEye } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useCameraOutput } from "../../../hooks/useCameraOutput"; -import type { - InitialValuesForm, - InitialValuesFormErrors, -} from "../../../types/types"; +import type { InitialValuesForm, InitialValuesFormErrors } from "../../../types/types"; import { toast } from "sonner"; const ChannelFields = () => { @@ -17,10 +14,8 @@ const ChannelFields = () => { const backOfficeURL = backOfficeQuery?.data?.propBackofficeURL?.value; const username = backOfficeQuery?.data?.propUsername?.value; const password = backOfficeQuery?.data?.propPassword?.value; - const connectTimeoutSeconds = - backOfficeQuery?.data?.propConnectTimeoutSeconds?.value; - const readTimeoutSeconds = - backOfficeQuery?.data?.propReadTimeoutSeconds?.value; + const connectTimeoutSeconds = backOfficeQuery?.data?.propConnectTimeoutSeconds?.value; + const readTimeoutSeconds = backOfficeQuery?.data?.propReadTimeoutSeconds?.value; const initialValues: InitialValuesForm = { backOfficeURL: backOfficeURL ?? "", @@ -44,9 +39,7 @@ const ChannelFields = () => { return null; }; - const validateValues = ( - values: InitialValuesForm - ): InitialValuesFormErrors => { + const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => { const errors: InitialValuesFormErrors = {}; const url = values.backOfficeURL?.trim(); @@ -78,12 +71,7 @@ const ChannelFields = () => { }; return ( - + {({ errors, touched, isSubmitting }) => (
@@ -98,9 +86,7 @@ const ChannelFields = () => { id="backoffice" placeholder="https://www.backoffice.com" className={`p-1.5 border ${ - errors.backOfficeURL && touched.backOfficeURL - ? "border-red-500" - : "border-gray-400 " + errors.backOfficeURL && touched.backOfficeURL ? "border-red-500" : "border-gray-400 " } rounded-lg w-full md:w-60`} /> @@ -112,9 +98,7 @@ const ChannelFields = () => { id="username" placeholder="Back office username" className={`p-1.5 border ${ - errors.username && touched.username - ? "border-red-500" - : "border-gray-400 " + errors.username && touched.username ? "border-red-500" : "border-gray-400 " } rounded-lg w-full md:w-60`} /> @@ -127,9 +111,7 @@ const ChannelFields = () => { id="password" placeholder="Back office password" className={`p-1.5 border ${ - errors.password && touched.password - ? "border-red-500" - : "border-gray-400 " + errors.password && touched.password ? "border-red-500" : "border-gray-400 " } rounded-lg w-full md:w-60`} /> { - + @@ -164,20 +142,16 @@ const ChannelFields = () => { id="readTimeoutSeconds" placeholder="https://example.com" className={`p-1.5 border ${ - errors.readTimeoutSeconds && touched.readTimeoutSeconds - ? "border-red-500" - : "border-gray-400 " + errors.readTimeoutSeconds && touched.readTimeoutSeconds ? "border-red-500" : "border-gray-400 " } rounded-lg w-full md:w-60`} />
diff --git a/src/components/SettingForms/NPED/NPEDHotlist.tsx b/src/components/SettingForms/NPED/NPEDHotlist.tsx index 003a398..b33421f 100644 --- a/src/components/SettingForms/NPED/NPEDHotlist.tsx +++ b/src/components/SettingForms/NPED/NPEDHotlist.tsx @@ -31,7 +31,7 @@ const NPEDHotlist = () => { type="file" name="file" id="file" - className="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) => { if (e.target.files) { if (e.target.files[0].type !== "text/csv") { diff --git a/src/components/SettingForms/Sound/SoundSettingsCard.tsx b/src/components/SettingForms/Sound/SoundSettingsCard.tsx index e2b904c..b4d0e1a 100644 --- a/src/components/SettingForms/Sound/SoundSettingsCard.tsx +++ b/src/components/SettingForms/Sound/SoundSettingsCard.tsx @@ -4,7 +4,7 @@ import SoundSettingsFields from "./SoundSettingsFields"; const SoundSettingsCard = () => { return ( - + diff --git a/src/components/SettingForms/Sound/SoundSettingsFields.tsx b/src/components/SettingForms/Sound/SoundSettingsFields.tsx index 3cc4d6f..0c08b0b 100644 --- a/src/components/SettingForms/Sound/SoundSettingsFields.tsx +++ b/src/components/SettingForms/Sound/SoundSettingsFields.tsx @@ -1,9 +1,10 @@ -import { Field, FieldArray, Form, Formik } from "formik"; +import { Field, Form, Formik } from "formik"; import FormGroup from "../components/FormGroup"; import type { FormValues, Hotlist } from "../../../types/types"; import { useSoundContext } from "../../../context/SoundContext"; import { useCameraBlackboard } from "../../../hooks/useCameraBlackboard"; import { toast } from "sonner"; +import SliderComponent from "../../UI/Slider"; const SoundSettingsFields = () => { const { state, dispatch } = useSoundContext(); @@ -12,22 +13,30 @@ const SoundSettingsFields = () => { const hotlists: Hotlist[] = state.hotlists; const soundOptions = state?.soundOptions?.map((soundOption) => ({ - value: soundOption?.name, + value: soundOption?.soundFileName, label: soundOption?.name, })); const initialValues: FormValues = { sightingSound: state.sightingSound ?? "switch", NPEDsound: state.NPEDsound ?? "popup", + hotlistSound: state.hotlistSound ?? "notification", hotlists, }; const handleSubmit = async (values: FormValues) => { - dispatch({ type: "UPDATE", payload: values }); + const updatedValues = { + ...values, + sightingVolume: state.sightingVolume, + NPEDsoundVolume: state.NPEDsoundVolume, + hotlistSoundVolume: state.hotlistSoundVolume, + }; + + dispatch({ type: "UPDATE", payload: updatedValues }); const result = await mutation.mutateAsync({ operation: "INSERT", path: "soundSettings", - value: values, + value: updatedValues, }); if (result.reason !== "OK") { toast.error("Cannot update sound settings"); @@ -37,55 +46,72 @@ const SoundSettingsFields = () => { }; return ( - {({ values }) => ( + {() => (
- - - {soundOptions?.map(({ value, label }) => { - return ( +
+ + + {soundOptions?.map(({ value, label }) => { + return ( + + ); + })} + + +
+
+ +
+ + + {soundOptions?.map(({ value, label }) => ( - ); - })} - - - - - - {soundOptions?.map(({ value, label }) => ( - - ))} - + ))} + + +

Hotlist Sounds

+
+ + + {soundOptions?.map(({ value, label }) => ( + + ))} + + +
+
+ {/* (
{values?.hotlists?.length > 0 ? ( values?.hotlists?.map((hotlist, index) => ( -
-
- +

Reboot

@@ -160,18 +135,14 @@ const SystemConfigFields = () => { className="bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition w-full md:w-[50%]" onClick={handleSoftReboot} > - {softRebootMutation.isPending || isSubmitting - ? "Rebooting..." - : "Software Reboot"} + {softRebootMutation.isPending || isSubmitting ? "Rebooting..." : "Software Reboot"} )} diff --git a/src/components/SettingForms/System/SystemFileUpload.tsx b/src/components/SettingForms/System/SystemFileUpload.tsx index bf02710..32fd544 100644 --- a/src/components/SettingForms/System/SystemFileUpload.tsx +++ b/src/components/SettingForms/System/SystemFileUpload.tsx @@ -36,7 +36,7 @@ const SystemFileUpload = ({ name, selectedFile }: SystemFileUploadProps) => { type="file" name="softwareUpdate" id="softwareUpdate" - className="file:px-10 file:border file:border-gray-500 file:rounded-lg file:bg-blue-800 file:mr-5 w-full max-w-xs" + 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={(event) => { const file = event.currentTarget.files?.[0]; if (!file) { @@ -44,8 +44,7 @@ const SystemFileUpload = ({ name, selectedFile }: SystemFileUploadProps) => { return; } - if (file?.size > 8 * 1024 * 1024) - toast.error("File is too large (max 8MB)."); + if (file?.size > 8 * 1024 * 1024) toast.error("File is too large (max 8MB)."); setFieldValue(name, file); }} /> @@ -53,7 +52,7 @@ const SystemFileUpload = ({ name, selectedFile }: SystemFileUploadProps) => { )} diff --git a/src/components/SettingForms/WiFi&Modem/WiFiSettingsForm.tsx b/src/components/SettingForms/WiFi&Modem/WiFiSettingsForm.tsx index d4a7fb2..aa2aa71 100644 --- a/src/components/SettingForms/WiFi&Modem/WiFiSettingsForm.tsx +++ b/src/components/SettingForms/WiFi&Modem/WiFiSettingsForm.tsx @@ -37,18 +37,11 @@ const WiFiSettingsForm = () => { await wifiMutation.mutateAsync(wifiConfig); }; return ( - + {({ isSubmitting }) => (
- - -
)} diff --git a/src/components/SettingForms/components/FormGroup.tsx b/src/components/SettingForms/components/FormGroup.tsx index 3d9508f..59515f6 100644 --- a/src/components/SettingForms/components/FormGroup.tsx +++ b/src/components/SettingForms/components/FormGroup.tsx @@ -5,11 +5,7 @@ type FormGroupProps = { }; const FormGroup = ({ children }: FormGroupProps) => { - return ( -
- {children} -
- ); + return
{children}
; }; export default FormGroup; diff --git a/src/components/SightingModal/SightingModal.tsx b/src/components/SightingModal/SightingModal.tsx index 70a18a2..9e31bfc 100644 --- a/src/components/SightingModal/SightingModal.tsx +++ b/src/components/SightingModal/SightingModal.tsx @@ -109,7 +109,7 @@ const SightingModal = ({ isSightingModalOpen, handleClose, sighting, onDelete }: onClick={handleAcknowledgeButton} > - Accept + Acknowledge )}
@@ -122,7 +122,7 @@ const SightingModal = ({ isSightingModalOpen, handleClose, sighting, onDelete }:

Hotlist

- {hotlistName ? hotlistName[0] : "-"} + {hotlistName ? hotlistName[0].replace(/\.csv$/i, "") : "-"}

@@ -157,18 +157,26 @@ const SightingModal = ({ isSightingModalOpen, handleClose, sighting, onDelete }:
{sighting?.seenCount ?? "-"}
-
-
Make
-
{sighting?.make ?? "-"}
-
-
-
Model
-
{sighting?.model ?? "-"}
-
-
-
Colour
-
{sighting?.color ?? "-"}
-
+ {sighting?.make && ( +
+
Make
+
{sighting?.make ?? "-"}
+
+ )} + {sighting?.model || + (!sighting?.model.trim() && ( +
+
Model
+
{sighting?.model ?? "-"}
+
+ ))} + {sighting?.color && ( +
+
Colour
+
{sighting?.color ?? "-"}
+
+ )} +
Time
{sighting?.timeStamp ?? "-"}
@@ -192,7 +200,7 @@ const SightingModal = ({ isSightingModalOpen, handleClose, sighting, onDelete }: onClick={handleAcknowledgeButton} > - Accept + Acknowledge )} {onDelete ? ( diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index 731166d..52317b9 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -38,10 +38,7 @@ type SightingHistoryProps = { className?: string; }; -export default function SightingHistoryWidget({ - className, - title, -}: SightingHistoryProps) { +export default function SightingHistoryWidget({ className, title }: SightingHistoryProps) { useNow(1000); const { state } = useSoundContext(); @@ -50,11 +47,11 @@ export default function SightingHistoryWidget({ }, [state.NPEDsound]); const soundSrcHotlist = useMemo(() => { - return getSoundFileURL(state?.hotlists?.[0]?.sound) ?? notification; - }, [state.hotlists]); + return getSoundFileURL(state?.hotlistSound) ?? notification; + }, [state?.hotlistSound]); - const { play: npedSound } = useSound(soundSrcNped); - const { play: hotlistsound } = useSound(soundSrcHotlist); + const { play: npedSound } = useSound(soundSrcNped, { volume: state.NPEDsoundVolume }); + const { play: hotlistsound } = useSound(soundSrcHotlist, { volume: state.hotlistSoundVolume }); const { sightings, setSelectedSighting, @@ -98,10 +95,7 @@ export default function SightingHistoryWidget({ [setSelectedSighting, setSightingModalOpen] ); - const rows = useMemo( - () => sightings?.filter(Boolean) as SightingType[], - [sightings] - ); + const rows = useMemo(() => sightings?.filter(Boolean) as SightingType[], [sightings]); useEffect(() => { if (!rows?.length) return; @@ -129,13 +123,7 @@ export default function SightingHistoryWidget({ break; } } - }, [ - rows, - hotlistsound, - npedSound, - setSightingModalOpen, - setSelectedSighting, - ]); + }, [rows, hotlistsound, npedSound, setSightingModalOpen, setSelectedSighting]); useEffect(() => { rows?.forEach((obj) => { @@ -187,12 +175,7 @@ export default function SightingHistoryWidget({ }; return ( <> - +
{isLoading && ( @@ -215,45 +198,16 @@ export default function SightingHistoryWidget({ className={`border border-gray-700 rounded-md mb-2 p-2 cursor-pointer `} onClick={() => onRowClick(obj)} > -
+
- colour patch + colour patch
{isHotListHit && ( - hotlistHit - )} - {isNPEDHitA && ( - hotlistHit - )} - {isNPEDHitB && ( - hotlistHit - )} - {isNPEDHitC && ( - hotlistHit + hotlistHit )} + {isNPEDHitA && hotlistHit} + {isNPEDHitB && hotlistHit} + {isNPEDHitC && hotlistHit}
@@ -262,11 +216,7 @@ export default function SightingHistoryWidget({
- + ); } diff --git a/src/components/UI/ModalComponent.tsx b/src/components/UI/ModalComponent.tsx index fd0e759..2910484 100644 --- a/src/components/UI/ModalComponent.tsx +++ b/src/components/UI/ModalComponent.tsx @@ -7,16 +7,12 @@ type ModalComponentProps = { close: () => void; }; -const ModalComponent = ({ - isModalOpen, - children, - close, -}: ModalComponentProps) => { +const ModalComponent = ({ isModalOpen, children, close }: ModalComponentProps) => { return ( {children} diff --git a/src/components/UI/NavigationArrow.tsx b/src/components/UI/NavigationArrow.tsx index 3e2c701..e133a88 100644 --- a/src/components/UI/NavigationArrow.tsx +++ b/src/components/UI/NavigationArrow.tsx @@ -11,7 +11,6 @@ const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => { const navigate = useNavigate(); const navigationDest = (side: string | undefined) => { - console.log(side); if (settingsPage) { navigate("/"); return; diff --git a/src/components/UI/Slider.tsx b/src/components/UI/Slider.tsx new file mode 100644 index 0000000..4178cbd --- /dev/null +++ b/src/components/UI/Slider.tsx @@ -0,0 +1,60 @@ +import "rc-slider/assets/index.css"; +import Slider from "rc-slider"; +import { useSoundContext } from "../../context/SoundContext"; + +const SliderComponent = ({ soundCategory }: { soundCategory: "SIGHTINGVOLUME" | "NPEDVOLUME" | "HOTLISTVOLUME" }) => { + const { dispatch, state } = useSoundContext(); + + const getVolumeOption = (soundCategory: string) => { + if (soundCategory === "SIGHTINGVOLUME") { + return state.sightingVolume; + } + if (soundCategory === "NPEDVOLUME") { + return state.NPEDsoundVolume; + } + if (soundCategory === "HOTLISTVOLUME") { + return state.hotlistSoundVolume; + } + }; + + const volume = getVolumeOption(soundCategory); + + const handleChange = (value: number | number[]) => { + const number = typeof value === "number" ? value : value[0]; + dispatch({ type: soundCategory, payload: number }); + }; + + return ( +
+ + {volume ? volume * 10 : 1} +
+ ); +}; + +export default SliderComponent; diff --git a/src/context/SoundContext.ts b/src/context/SoundContext.ts index 9c5fac2..b48ad68 100644 --- a/src/context/SoundContext.ts +++ b/src/context/SoundContext.ts @@ -7,13 +7,10 @@ type SoundContextType = { audioArmed: boolean; }; -export const SoundContext = createContext( - undefined -); +export const SoundContext = createContext(undefined); export const useSoundContext = () => { const ctx = useContext(SoundContext); - if (!ctx) - throw new Error("useSoundContext must be used within "); + if (!ctx) throw new Error("useSoundContext must be used within "); return ctx; }; diff --git a/src/context/providers/SoundContextProvider.tsx b/src/context/providers/SoundContextProvider.tsx index 7eed5ee..37e75ec 100644 --- a/src/context/providers/SoundContextProvider.tsx +++ b/src/context/providers/SoundContextProvider.tsx @@ -1,11 +1,4 @@ -import { - useEffect, - useMemo, - useReducer, - useRef, - useState, - type ReactNode, -} from "react"; +import { useEffect, useMemo, useReducer, useRef, useState, type ReactNode } from "react"; import { SoundContext } from "../SoundContext"; import { initialState, reducer } from "../reducers/SoundContextReducer"; import { useCameraBlackboard } from "../../hooks/useCameraBlackboard"; @@ -63,13 +56,8 @@ const SoundContextProvider = ({ children }: SoundContextProviderProps) => { }; }, []); - const value = useMemo( - () => ({ state, dispatch, audioArmed }), - [state, audioArmed] - ); - return ( - {children} - ); + const value = useMemo(() => ({ state, dispatch, audioArmed }), [state, audioArmed]); + return {children}; }; export default SoundContextProvider; diff --git a/src/context/reducers/SoundContextReducer.ts b/src/context/reducers/SoundContextReducer.ts index 6250239..86cac6e 100644 --- a/src/context/reducers/SoundContextReducer.ts +++ b/src/context/reducers/SoundContextReducer.ts @@ -3,12 +3,20 @@ import type { SoundAction, SoundState } from "../../types/types"; export const initialState: SoundState = { sightingSound: "switch", NPEDsound: "popup", + hotlistSound: "warning", hotlists: [{ name: "hotlistName", sound: "notification" }], soundOptions: [ - { name: "switch (Default)", soundFile: null }, - { name: "popup", soundFile: null }, - { name: "notification", soundFile: null }, + { name: "Switch (Default)", soundFileName: "switch" }, + { name: "Popup", soundFileName: "popup" }, + { name: "Notification", soundFileName: "notification" }, + { name: "Beep", soundFileName: "beep" }, + { name: "Ding", soundFileName: "ding" }, + { name: "Shutter", soundFileName: "shutter" }, + { name: "Warning (voice)", soundFileName: "warning" }, ], + sightingVolume: 1, + NPEDsoundVolume: 1, + hotlistSoundVolume: 1, }; export function reducer(state: SoundState, action: SoundAction): SoundState { @@ -18,10 +26,14 @@ export function reducer(state: SoundState, action: SoundAction): SoundState { ...state, sightingSound: action.payload.sightingSound, NPEDsound: action.payload.NPEDsound, + hotlistSound: action.payload.hotlistSound, hotlists: action.payload.hotlists?.map((hotlist) => ({ name: hotlist.name, sound: hotlist.sound, })), + NPEDsoundVolume: action.payload.NPEDsoundVolume, + sightingVolume: action.payload.sightingVolume, + hotlistSoundVolume: action.payload.hotlistSoundVolume, }; } @@ -31,6 +43,24 @@ export function reducer(state: SoundState, action: SoundAction): SoundState { soundOptions: [...(state.soundOptions ?? []), action.payload], }; } + // todo: refactor to use single state coupled with sound name. e.g : {name: , volume: } + case "SIGHTINGVOLUME": + return { + ...state, + sightingVolume: action.payload, + }; + + case "NPEDVOLUME": + return { + ...state, + NPEDsoundVolume: action.payload, + }; + + case "HOTLISTVOLUME": + return { + ...state, + hotlistSoundVolume: action.payload, + }; default: return state; diff --git a/src/hooks/useSightingFeed.ts b/src/hooks/useSightingFeed.ts index 35b7f44..d9a386d 100644 --- a/src/hooks/useSightingFeed.ts +++ b/src/hooks/useSightingFeed.ts @@ -75,8 +75,9 @@ export function useSightingFeed(url: string | undefined) { }); //use latestref instead of trigger to revert back + useSoundOnChange(soundSrc, trigger, { - volume: 1, + volume: state.sightingVolume, initial: false, }); diff --git a/src/pages/SystemSettings.tsx b/src/pages/SystemSettings.tsx index 138070c..0d38273 100644 --- a/src/pages/SystemSettings.tsx +++ b/src/pages/SystemSettings.tsx @@ -47,7 +47,7 @@ const SystemSettings = () => { -
+
diff --git a/src/types/types.ts b/src/types/types.ts index 8d35242..37afae9 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -43,9 +43,7 @@ export type CameraSettingValues = { id: number | string; }; -export type CameraSettingErrorValues = Partial< - Record ->; +export type CameraSettingErrorValues = Partial>; export type BearerTypeFieldType = { format: string; @@ -287,18 +285,24 @@ export type FormValues = { sightingSound: SoundValue; NPEDsound: SoundValue; hotlists: Hotlist[]; + hotlistSound: SoundValue; }; export type SoundUploadValue = { name: string; - soundFile: File | null; + soundFileName?: string; + soundFile?: File | null; }; export type SoundState = { sightingSound: SoundValue; NPEDsound: SoundValue; hotlists: Hotlist[]; + hotlistSound: SoundValue; soundOptions?: SoundUploadValue[]; + sightingVolume: number; + NPEDsoundVolume: number; + hotlistSoundVolume: number; }; type UpdateAction = { @@ -307,6 +311,10 @@ type UpdateAction = { sightingSound: SoundValue; NPEDsound: SoundValue; hotlists: Hotlist[]; + sightingVolume: number; + NPEDsoundVolume: number; + hotlistSoundVolume: number; + hotlistSound: SoundValue; }; }; @@ -315,7 +323,12 @@ type AddAction = { payload: SoundUploadValue; }; -export type SoundAction = UpdateAction | AddAction; +type VolumeAction = { + type: "SIGHTINGVOLUME" | "NPEDVOLUME" | "HOTLISTVOLUME"; + payload: number; +}; + +export type SoundAction = UpdateAction | AddAction | VolumeAction; export type WifiSettingValues = { ssid: string; password: string; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 65a4fc8..78c7b30 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,6 +1,11 @@ import switchSound from "../assets/sounds/ui/switch.mp3"; import popup from "../assets/sounds/ui/popup_open.mp3"; import notification from "../assets/sounds/ui/notification.mp3"; +import beep from "../assets/sounds/ui/Beep.wav"; +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 type { HotlistMatches, SightingType } from "../types/types"; export function getSoundFileURL(name: string) { @@ -8,6 +13,10 @@ export function getSoundFileURL(name: string) { switch: switchSound, popup: popup, notification: notification, + beep: beep, + warning: warning, + ding: ding, + shutter: shutter, }; return sounds[name] ?? null; } diff --git a/yarn.lock b/yarn.lock index 76c7760..db3053e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -138,7 +138,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/runtime@^7.1.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.18.3": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== @@ -1044,6 +1044,11 @@ chownr@^3.0.0: resolved "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz" integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== +classnames@^2.2.5: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clsx@^2.0.0, clsx@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" @@ -1875,6 +1880,23 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +rc-slider@^11.1.9: + version "11.1.9" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.9.tgz#d872130fbf4ec51f28543d62e90451091d6f5208" + integrity sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.36.0" + +rc-util@^5.36.0: + version "5.44.4" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.4.tgz#89ee9037683cca01cd60f1a6bbda761457dd6ba5" + integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + react-dom@^19.1.1: version "19.1.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz" @@ -1892,6 +1914,11 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^18.2.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + react-lifecycles-compat@^3.0.0: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"