diff --git a/web/src/components/player/LivePlayer.tsx b/web/src/components/player/LivePlayer.tsx
index 8be3f71b9..a8fe5d3b1 100644
--- a/web/src/components/player/LivePlayer.tsx
+++ b/web/src/components/player/LivePlayer.tsx
@@ -10,10 +10,10 @@ import { useCameraActivity } from "@/hooks/use-camera-activity";
import { useRecordingsState } from "@/api/ws";
import { LivePlayerMode } from "@/types/live";
import useCameraLiveMode from "@/hooks/use-camera-live-mode";
-import { isDesktop } from "react-device-detect";
import CameraActivityIndicator from "../indicators/CameraActivityIndicator";
type LivePlayerProps = {
+ cameraRef?: (ref: HTMLDivElement | null) => void;
className?: string;
cameraConfig: CameraConfig;
preferredLiveMode?: LivePlayerMode;
@@ -26,6 +26,7 @@ type LivePlayerProps = {
};
export default function LivePlayer({
+ cameraRef = undefined,
className,
cameraConfig,
preferredLiveMode,
@@ -140,6 +141,8 @@ export default function LivePlayer({
return (
- {isDesktop && (
-
- {recording == "ON" && (
-
- )}
-
- )}
+
+ {recording == "ON" && (
+
+ )}
+
);
}
diff --git a/web/src/components/player/PreviewPlayer.tsx b/web/src/components/player/PreviewPlayer.tsx
index 197986677..76a16fe9d 100644
--- a/web/src/components/player/PreviewPlayer.tsx
+++ b/web/src/components/player/PreviewPlayer.tsx
@@ -234,7 +234,7 @@ function PreviewVideoPlayer({
return (
{currentHourFrame && (
@@ -247,7 +247,7 @@ function PreviewVideoPlayer({
ref={canvasRef}
width={videoWidth}
height={videoHeight}
- className={`absolute h-full left-1/2 -translate-x-1/2 bg-black ${!loaded && hasCanvas ? "" : "hidden"}`}
+ className={`absolute h-full left-1/2 -translate-x-1/2 ${!loaded && hasCanvas ? "" : "hidden"}`}
/>
)}
{cameraPreviews && !currentPreview && (
-
+
No Preview Found
)}
diff --git a/web/src/views/live/LiveDashboardView.tsx b/web/src/views/live/LiveDashboardView.tsx
index d5a7401c5..649e102b5 100644
--- a/web/src/views/live/LiveDashboardView.tsx
+++ b/web/src/views/live/LiveDashboardView.tsx
@@ -11,7 +11,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
import { usePersistence } from "@/hooks/use-persistence";
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
import { ReviewSegment } from "@/types/review";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { isDesktop, isMobile, isSafari } from "react-device-detect";
import useSWR from "swr";
@@ -79,6 +79,53 @@ export default function LiveDashboardView({
};
}, [visibilityListener]);
+ const [visibleCameras, setVisibleCameras] = useState
([]);
+ const visibleCameraObserver = useRef(null);
+ useEffect(() => {
+ const visibleCameras = new Set();
+ visibleCameraObserver.current = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ const camera = (entry.target as HTMLElement).dataset.camera;
+
+ if (!camera) {
+ return;
+ }
+
+ if (entry.isIntersecting) {
+ visibleCameras.add(camera);
+ } else {
+ visibleCameras.delete(camera);
+ }
+
+ setVisibleCameras([...visibleCameras]);
+ });
+ },
+ { threshold: 0.5 },
+ );
+
+ return () => {
+ visibleCameraObserver.current?.disconnect();
+ };
+ }, []);
+
+ const cameraRef = useCallback(
+ (node: HTMLElement | null) => {
+ if (!visibleCameraObserver.current) {
+ return;
+ }
+
+ try {
+ if (node) visibleCameraObserver.current.observe(node);
+ } catch (e) {
+ // no op
+ }
+ },
+ // we need to listen on the value of the ref
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [visibleCameraObserver.current],
+ );
+
const birdseyeConfig = useMemo(() => config?.birdseye, [config]);
return (
@@ -149,9 +196,12 @@ export default function LiveDashboardView({
}
return (
onSelectCamera(camera.name)}