From a0928cf3257c45a28c293da6c54e782cca61c758 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:46:01 -0500 Subject: [PATCH] limit cameras in camera groups --- .../components/filter/CameraGroupSelector.tsx | 22 ++++++++++++++----- web/src/hooks/use-is-custom-role.ts | 11 ++++++++++ web/src/pages/Live.tsx | 13 +++++------ 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 web/src/hooks/use-is-custom-role.ts diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index 6e20687cc..365fc8ccf 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -77,6 +77,8 @@ import { DialogTrigger } from "@radix-ui/react-dialog"; import { useStreamingSettings } from "@/context/streaming-settings-provider"; import { Trans, useTranslation } from "react-i18next"; import { CameraNameLabel } from "../camera/CameraNameLabel"; +import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; +import { useIsCustomRole } from "@/hooks/use-is-custom-role"; type CameraGroupSelectorProps = { className?: string; @@ -650,6 +652,9 @@ export function CameraGroupEdit({ allGroupsStreamingSettings[editingGroup?.[0] ?? ""], ); + const allowedCameras = useAllowedCameras(); + const isCustomRole = useIsCustomRole(); + const [openCamera, setOpenCamera] = useState(); const birdseyeConfig = useMemo(() => config?.birdseye, [config]); @@ -837,12 +842,17 @@ export function CameraGroupEdit({ {t("group.cameras.desc")} {[ - ...(birdseyeConfig?.enabled ? ["birdseye"] : []), - ...Object.keys(config?.cameras ?? {}).sort( - (a, b) => - (config?.cameras[a]?.ui?.order ?? 0) - - (config?.cameras[b]?.ui?.order ?? 0), - ), + ...(birdseyeConfig?.enabled && + (!isCustomRole || "birdseye" in allowedCameras) + ? ["birdseye"] + : []), + ...Object.keys(config?.cameras ?? {}) + .filter((camera) => allowedCameras.includes(camera)) + .sort( + (a, b) => + (config?.cameras[a]?.ui?.order ?? 0) - + (config?.cameras[b]?.ui?.order ?? 0), + ), ].map((camera) => (
diff --git a/web/src/hooks/use-is-custom-role.ts b/web/src/hooks/use-is-custom-role.ts new file mode 100644 index 000000000..a78831230 --- /dev/null +++ b/web/src/hooks/use-is-custom-role.ts @@ -0,0 +1,11 @@ +import { useContext } from "react"; +import { AuthContext } from "@/context/auth-context"; + +export function useIsCustomRole() { + const { auth } = useContext(AuthContext); + return !( + auth.user?.role === "admin" || + auth.user?.role == "viewer" || + !auth.isAuthenticated + ); +} diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index fb386b31b..4b446ea32 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -11,15 +11,15 @@ import LiveCameraView from "@/views/live/LiveCameraView"; import LiveDashboardView from "@/views/live/LiveDashboardView"; import { useTranslation } from "react-i18next"; -import { useContext, useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo, useRef } from "react"; import useSWR from "swr"; import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; -import { AuthContext } from "@/context/auth-context"; +import { useIsCustomRole } from "@/hooks/use-is-custom-role"; function Live() { const { t } = useTranslation(["views/live"]); const { data: config } = useSWR("config"); - const { auth } = useContext(AuthContext); + const isCustomRole = useIsCustomRole(); // selection @@ -93,16 +93,13 @@ function Live() { cameraGroup && config.camera_groups[cameraGroup] && cameraGroup != "default" && - (auth.user?.role === "admin" || - auth.user?.role == "viewer" || - !auth.isAuthenticated || - "birdseye" in allowedCameras) + (!isCustomRole || "birdseye" in allowedCameras) ) { return config.camera_groups[cameraGroup].cameras.includes("birdseye"); } else { return false; } - }, [config, cameraGroup, allowedCameras, auth]); + }, [config, cameraGroup, allowedCameras, isCustomRole]); const cameras = useMemo(() => { if (!config) {