diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index 326ce7946..7875cc3e0 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -6,9 +6,8 @@ import { import { isDesktop } from "react-device-detect"; import useSWR from "swr"; import { MdHome } from "react-icons/md"; -import useOverlayState from "@/hooks/use-overlay-state"; +import { usePersistedOverlayState } from "@/hooks/use-overlay-state"; import { Button } from "../ui/button"; -import { useNavigate } from "react-router-dom"; import { useCallback, useMemo, useState } from "react"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { getIconForGroup } from "@/utils/iconUtil"; @@ -31,7 +30,6 @@ type CameraGroupSelectorProps = { }; export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { const { data: config } = useSWR("config"); - const navigate = useNavigate(); // tooltip @@ -54,7 +52,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { // groups - const [group, setGroup] = useOverlayState("cameraGroup"); + const [group, setGroup] = usePersistedOverlayState("cameraGroup"); const groups = useMemo(() => { if (!config) { @@ -89,7 +87,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { : "text-muted-foreground bg-secondary focus:text-muted-foreground focus:bg-secondary" } size="xs" - onClick={() => (group ? navigate(-1) : null)} + onClick={() => (group ? setGroup(undefined, true) : null)} onMouseEnter={() => (isDesktop ? showTooltip("home") : null)} onMouseLeave={() => (isDesktop ? showTooltip(undefined) : null)} > diff --git a/web/src/hooks/use-overlay-state.tsx b/web/src/hooks/use-overlay-state.tsx index 5f39c6311..12f87be57 100644 --- a/web/src/hooks/use-overlay-state.tsx +++ b/web/src/hooks/use-overlay-state.tsx @@ -1,5 +1,6 @@ import { useCallback, useMemo } from "react"; import { useLocation, useNavigate } from "react-router-dom"; +import { usePersistence } from "./use-persistence"; export default function useOverlayState( key: string, @@ -27,3 +28,38 @@ export default function useOverlayState( return [overlayStateValue ?? defaultValue, setOverlayStateValue]; } + +export function usePersistedOverlayState( + key: string, + defaultValue: S | undefined = undefined, +): [S | undefined, (value: S | undefined, replace?: boolean) => void] { + const [persistedValue, setPersistedValue] = usePersistence( + key, + defaultValue, + ); + const location = useLocation(); + const navigate = useNavigate(); + const currentLocationState = location.state; + + const setOverlayStateValue = useCallback( + (value: S | undefined, replace: boolean = false) => { + setPersistedValue(value); + const newLocationState = { ...currentLocationState }; + newLocationState[key] = value; + navigate(location.pathname, { state: newLocationState, replace }); + }, + // we know that these deps are correct + // eslint-disable-next-line react-hooks/exhaustive-deps + [key, navigate], + ); + + const overlayStateValue = useMemo( + () => location.state && location.state[key], + [location, key], + ); + + return [ + overlayStateValue ?? persistedValue ?? defaultValue, + setOverlayStateValue, + ]; +} diff --git a/web/src/hooks/use-persistence.ts b/web/src/hooks/use-persistence.ts index 2f73023fd..1b2f2a4d4 100644 --- a/web/src/hooks/use-persistence.ts +++ b/web/src/hooks/use-persistence.ts @@ -3,7 +3,7 @@ import { get as getData, set as setData } from "idb-keyval"; type usePersistenceReturn = [ value: S | undefined, - setValue: (value: S) => void, + setValue: (value: S | undefined) => void, loaded: boolean, ]; @@ -15,7 +15,7 @@ export function usePersistence( const [loaded, setLoaded] = useState(false); const setValue = useCallback( - (value: S) => { + (value: S | undefined) => { setInternalValue(value); async function update() { await setData(key, value); diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index 2233c29cd..4d7b58780 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -1,4 +1,6 @@ -import useOverlayState from "@/hooks/use-overlay-state"; +import useOverlayState, { + usePersistedOverlayState, +} from "@/hooks/use-overlay-state"; import { FrigateConfig } from "@/types/frigateConfig"; import LiveCameraView from "@/views/live/LiveCameraView"; import LiveDashboardView from "@/views/live/LiveDashboardView"; @@ -9,7 +11,7 @@ function Live() { const { data: config } = useSWR("config"); const [selectedCameraName, setSelectedCameraName] = useOverlayState("camera"); - const [cameraGroup] = useOverlayState("cameraGroup"); + const [cameraGroup] = usePersistedOverlayState("cameraGroup"); const includesBirdseye = useMemo(() => { if (config && cameraGroup) {