diff --git a/Makefile b/Makefile index d1427b6df..51f12f972 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ default_target: local COMMIT_HASH := $(shell git log -1 --pretty=format:"%h"|tail -1) -VERSION = 0.17.0 +VERSION = 0.17.1 IMAGE_REPO ?= ghcr.io/blakeblackshear/frigate GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BOARDS= #Initialized empty diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index 14845fdb8..6bd7dfbbf 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -77,6 +77,7 @@ import { useStreamingSettings } from "@/context/streaming-settings-provider"; import { Trans, useTranslation } from "react-i18next"; import { CameraNameLabel } from "../camera/FriendlyNameLabel"; import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; +import { useHasFullCameraAccess } from "@/hooks/use-has-full-camera-access"; import { useIsAdmin } from "@/hooks/use-is-admin"; import { useUserPersistedOverlayState } from "@/hooks/use-overlay-state"; @@ -677,7 +678,7 @@ export function CameraGroupEdit({ ); const allowedCameras = useAllowedCameras(); - const isAdmin = useIsAdmin(); + const hasFullCameraAccess = useHasFullCameraAccess(); const [openCamera, setOpenCamera] = useState(); @@ -866,8 +867,7 @@ export function CameraGroupEdit({ {t("group.cameras.desc")} {[ - ...(birdseyeConfig?.enabled && - (isAdmin || "birdseye" in allowedCameras) + ...(birdseyeConfig?.enabled && hasFullCameraAccess ? ["birdseye"] : []), ...Object.keys(config?.cameras ?? {}) diff --git a/web/src/hooks/use-has-full-camera-access.ts b/web/src/hooks/use-has-full-camera-access.ts new file mode 100644 index 000000000..8e7d74501 --- /dev/null +++ b/web/src/hooks/use-has-full-camera-access.ts @@ -0,0 +1,26 @@ +import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; +import useSWR from "swr"; +import { FrigateConfig } from "@/types/frigateConfig"; + +/** + * Returns true if the current user has access to all cameras. + * This is used to determine birdseye access — users who can see + * all cameras should also be able to see the birdseye view. + */ +export function useHasFullCameraAccess() { + const allowedCameras = useAllowedCameras(); + const { data: config } = useSWR("config", { + revalidateOnFocus: false, + }); + + if (!config?.cameras) return false; + + const enabledCameraNames = Object.entries(config.cameras) + .filter(([, cam]) => cam.enabled_in_config) + .map(([name]) => name); + + return ( + enabledCameraNames.length > 0 && + enabledCameraNames.every((name) => allowedCameras.includes(name)) + ); +} diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index 1b4bfb33a..e1a4f4868 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -11,12 +11,12 @@ import { useTranslation } from "react-i18next"; import { useEffect, useMemo, useRef } from "react"; import useSWR from "swr"; import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; -import { useIsAdmin } from "@/hooks/use-is-admin"; +import { useHasFullCameraAccess } from "@/hooks/use-has-full-camera-access"; function Live() { const { t } = useTranslation(["views/live"]); const { data: config } = useSWR("config"); - const isAdmin = useIsAdmin(); + const hasFullCameraAccess = useHasFullCameraAccess(); // selection @@ -90,8 +90,8 @@ function Live() { const allowedCameras = useAllowedCameras(); const includesBirdseye = useMemo(() => { - // Restricted users should never have access to birdseye - if (!isAdmin) { + // Users without access to all cameras should not have access to birdseye + if (!hasFullCameraAccess) { return false; } @@ -106,7 +106,7 @@ function Live() { } else { return false; } - }, [config, cameraGroup, isAdmin]); + }, [config, cameraGroup, hasFullCameraAccess]); const cameras = useMemo(() => { if (!config) { @@ -151,7 +151,9 @@ function Live() { return (
- {selectedCameraName === "birdseye" ? ( + {selectedCameraName === "birdseye" && + hasFullCameraAccess && + config?.birdseye?.enabled ? (