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/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 c138f9a..7e7ab3a 100644 --- a/src/components/SettingForms/Sound/SoundSettingsFields.tsx +++ b/src/components/SettingForms/Sound/SoundSettingsFields.tsx @@ -4,6 +4,7 @@ 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(); @@ -40,34 +41,40 @@ const SoundSettingsFields = () => { {({ values }) => (
- - - {soundOptions?.map(({ value, label }) => { - return ( - - ); - })} - +
+ + + {soundOptions?.map(({ value, label }) => { + return ( + + ); + })} + + +
- - - {soundOptions?.map(({ value, label }) => ( - - ))} - +
+ + + {soundOptions?.map(({ value, label }) => ( + + ))} + + +

Hotlist Sounds

diff --git a/src/components/SettingForms/Sound/SoundUploadCard.tsx b/src/components/SettingForms/Sound/SoundUploadCard.tsx index e751959..0c0ea2b 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/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 9673fc3..9e31bfc 100644 --- a/src/components/SightingModal/SightingModal.tsx +++ b/src/components/SightingModal/SightingModal.tsx @@ -122,7 +122,7 @@ const SightingModal = ({ isSightingModalOpen, handleClose, sighting, onDelete }:

Hotlist

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

diff --git a/src/components/UI/Slider.tsx b/src/components/UI/Slider.tsx new file mode 100644 index 0000000..39cdce7 --- /dev/null +++ b/src/components/UI/Slider.tsx @@ -0,0 +1,47 @@ +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 volume = soundCategory === "SIGHTINGVOLUME" ? state.sightingVolume : state.NPEDsoundVolume; + + const handleChange = (value: number | number[]) => { + const number = typeof value === "number" ? value : value[0]; + dispatch({ type: soundCategory, payload: number }); + }; + + return ( +
+ + {volume * 10} +
+ ); +}; + +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..07e2516 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"; @@ -27,7 +20,6 @@ const SoundContextProvider = ({ children }: SoundContextProviderProps) => { operation: "VIEW", path: "soundSettings", }); - dispatch({ type: "UPDATE", payload: result.result }); }; fetchSound(); @@ -63,13 +55,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 5012c74..653caeb 100644 --- a/src/context/reducers/SoundContextReducer.ts +++ b/src/context/reducers/SoundContextReducer.ts @@ -13,6 +13,9 @@ export const initialState: SoundState = { { name: "Shutter", soundFile: "shutter" }, { name: "Warning (voice)", soundFile: "warning" }, ], + sightingVolume: 1, + NPEDsoundVolume: 1, + hotlistSoundVolume: 1, }; export function reducer(state: SoundState, action: SoundAction): SoundState { @@ -35,6 +38,18 @@ 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, + }; 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 f835d0b..8d45b58 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -297,6 +297,9 @@ export type SoundState = { NPEDsound: SoundValue; hotlists: Hotlist[]; soundOptions?: SoundUploadValue[]; + sightingVolume: number; + NPEDsoundVolume: number; + hotlistSoundVolume: number; }; type UpdateAction = { @@ -313,7 +316,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/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"