Persist selected camera group for live (#10448)

* Persist camera group selected

* Cleanup
This commit is contained in:
Nicolas Mowen 2024-03-14 08:27:27 -06:00 committed by GitHub
parent a660e3ae27
commit 2decdeadb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 9 deletions

View File

@ -6,9 +6,8 @@ import {
import { isDesktop } from "react-device-detect"; import { isDesktop } from "react-device-detect";
import useSWR from "swr"; import useSWR from "swr";
import { MdHome } from "react-icons/md"; 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 { Button } from "../ui/button";
import { useNavigate } from "react-router-dom";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
import { getIconForGroup } from "@/utils/iconUtil"; import { getIconForGroup } from "@/utils/iconUtil";
@ -31,7 +30,6 @@ type CameraGroupSelectorProps = {
}; };
export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const navigate = useNavigate();
// tooltip // tooltip
@ -54,7 +52,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
// groups // groups
const [group, setGroup] = useOverlayState("cameraGroup"); const [group, setGroup] = usePersistedOverlayState("cameraGroup");
const groups = useMemo(() => { const groups = useMemo(() => {
if (!config) { if (!config) {
@ -89,7 +87,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
: "text-muted-foreground bg-secondary focus:text-muted-foreground focus:bg-secondary" : "text-muted-foreground bg-secondary focus:text-muted-foreground focus:bg-secondary"
} }
size="xs" size="xs"
onClick={() => (group ? navigate(-1) : null)} onClick={() => (group ? setGroup(undefined, true) : null)}
onMouseEnter={() => (isDesktop ? showTooltip("home") : null)} onMouseEnter={() => (isDesktop ? showTooltip("home") : null)}
onMouseLeave={() => (isDesktop ? showTooltip(undefined) : null)} onMouseLeave={() => (isDesktop ? showTooltip(undefined) : null)}
> >

View File

@ -1,5 +1,6 @@
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { usePersistence } from "./use-persistence";
export default function useOverlayState<S extends string>( export default function useOverlayState<S extends string>(
key: string, key: string,
@ -27,3 +28,38 @@ export default function useOverlayState<S extends string>(
return [overlayStateValue ?? defaultValue, setOverlayStateValue]; return [overlayStateValue ?? defaultValue, setOverlayStateValue];
} }
export function usePersistedOverlayState<S extends string>(
key: string,
defaultValue: S | undefined = undefined,
): [S | undefined, (value: S | undefined, replace?: boolean) => void] {
const [persistedValue, setPersistedValue] = usePersistence<S>(
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<S | undefined>(
() => location.state && location.state[key],
[location, key],
);
return [
overlayStateValue ?? persistedValue ?? defaultValue,
setOverlayStateValue,
];
}

View File

@ -3,7 +3,7 @@ import { get as getData, set as setData } from "idb-keyval";
type usePersistenceReturn<S> = [ type usePersistenceReturn<S> = [
value: S | undefined, value: S | undefined,
setValue: (value: S) => void, setValue: (value: S | undefined) => void,
loaded: boolean, loaded: boolean,
]; ];
@ -15,7 +15,7 @@ export function usePersistence<S>(
const [loaded, setLoaded] = useState<boolean>(false); const [loaded, setLoaded] = useState<boolean>(false);
const setValue = useCallback( const setValue = useCallback(
(value: S) => { (value: S | undefined) => {
setInternalValue(value); setInternalValue(value);
async function update() { async function update() {
await setData(key, value); await setData(key, value);

View File

@ -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 { FrigateConfig } from "@/types/frigateConfig";
import LiveCameraView from "@/views/live/LiveCameraView"; import LiveCameraView from "@/views/live/LiveCameraView";
import LiveDashboardView from "@/views/live/LiveDashboardView"; import LiveDashboardView from "@/views/live/LiveDashboardView";
@ -9,7 +11,7 @@ function Live() {
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const [selectedCameraName, setSelectedCameraName] = useOverlayState("camera"); const [selectedCameraName, setSelectedCameraName] = useOverlayState("camera");
const [cameraGroup] = useOverlayState("cameraGroup"); const [cameraGroup] = usePersistedOverlayState("cameraGroup");
const includesBirdseye = useMemo(() => { const includesBirdseye = useMemo(() => {
if (config && cameraGroup) { if (config && cameraGroup) {