Only allow visible cameras to go live on dashboard (#10671)

* Only show live cameras that are currently visible

* Add back black background

* fix
This commit is contained in:
Nicolas Mowen 2024-03-25 14:56:13 -06:00 committed by GitHub
parent 51db63e42b
commit 6dd6ca5de5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 64 additions and 13 deletions

View File

@ -10,10 +10,10 @@ import { useCameraActivity } from "@/hooks/use-camera-activity";
import { useRecordingsState } from "@/api/ws"; import { useRecordingsState } from "@/api/ws";
import { LivePlayerMode } from "@/types/live"; import { LivePlayerMode } from "@/types/live";
import useCameraLiveMode from "@/hooks/use-camera-live-mode"; import useCameraLiveMode from "@/hooks/use-camera-live-mode";
import { isDesktop } from "react-device-detect";
import CameraActivityIndicator from "../indicators/CameraActivityIndicator"; import CameraActivityIndicator from "../indicators/CameraActivityIndicator";
type LivePlayerProps = { type LivePlayerProps = {
cameraRef?: (ref: HTMLDivElement | null) => void;
className?: string; className?: string;
cameraConfig: CameraConfig; cameraConfig: CameraConfig;
preferredLiveMode?: LivePlayerMode; preferredLiveMode?: LivePlayerMode;
@ -26,6 +26,7 @@ type LivePlayerProps = {
}; };
export default function LivePlayer({ export default function LivePlayer({
cameraRef = undefined,
className, className,
cameraConfig, cameraConfig,
preferredLiveMode, preferredLiveMode,
@ -140,6 +141,8 @@ export default function LivePlayer({
return ( return (
<div <div
ref={cameraRef}
data-camera={cameraConfig.name}
className={`relative flex justify-center ${liveMode == "jsmpeg" ? "size-full" : "w-full"} outline cursor-pointer ${ className={`relative flex justify-center ${liveMode == "jsmpeg" ? "size-full" : "w-full"} outline cursor-pointer ${
activeTracking activeTracking
? "outline-severity_alert outline-3 rounded-2xl shadow-severity_alert" ? "outline-severity_alert outline-3 rounded-2xl shadow-severity_alert"
@ -171,13 +174,11 @@ export default function LivePlayer({
)} )}
</div> </div>
{isDesktop && (
<div className="absolute right-2 top-2 size-4"> <div className="absolute right-2 top-2 size-4">
{recording == "ON" && ( {recording == "ON" && (
<MdCircle className="size-2 drop-shadow-md shadow-danger text-danger animate-pulse" /> <MdCircle className="size-2 drop-shadow-md shadow-danger text-danger animate-pulse" />
)} )}
</div> </div>
)}
</div> </div>
); );
} }

View File

@ -234,7 +234,7 @@ function PreviewVideoPlayer({
return ( return (
<div <div
className={`relative w-full rounded-2xl overflow-hidden ${className ?? ""} ${onClick ? "cursor-pointer" : ""}`} className={`relative w-full rounded-2xl bg-black overflow-hidden ${className ?? ""} ${onClick ? "cursor-pointer" : ""}`}
onClick={onClick} onClick={onClick}
> >
{currentHourFrame && ( {currentHourFrame && (
@ -247,7 +247,7 @@ function PreviewVideoPlayer({
ref={canvasRef} ref={canvasRef}
width={videoWidth} width={videoWidth}
height={videoHeight} 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"}`}
/> />
<video <video
ref={previewRef} ref={previewRef}
@ -281,7 +281,7 @@ function PreviewVideoPlayer({
<Skeleton className="absolute inset-0" /> <Skeleton className="absolute inset-0" />
)} )}
{cameraPreviews && !currentPreview && ( {cameraPreviews && !currentPreview && (
<div className="absolute inset-0 bg-black text-white rounded-2xl flex justify-center items-center"> <div className="absolute inset-0 text-white rounded-2xl flex justify-center items-center">
No Preview Found No Preview Found
</div> </div>
)} )}

View File

@ -11,7 +11,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
import { usePersistence } from "@/hooks/use-persistence"; import { usePersistence } from "@/hooks/use-persistence";
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig"; import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
import { ReviewSegment } from "@/types/review"; 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 { isDesktop, isMobile, isSafari } from "react-device-detect";
import useSWR from "swr"; import useSWR from "swr";
@ -79,6 +79,53 @@ export default function LiveDashboardView({
}; };
}, [visibilityListener]); }, [visibilityListener]);
const [visibleCameras, setVisibleCameras] = useState<string[]>([]);
const visibleCameraObserver = useRef<IntersectionObserver | null>(null);
useEffect(() => {
const visibleCameras = new Set<string>();
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]); const birdseyeConfig = useMemo(() => config?.birdseye, [config]);
return ( return (
@ -149,9 +196,12 @@ export default function LiveDashboardView({
} }
return ( return (
<LivePlayer <LivePlayer
cameraRef={cameraRef}
key={camera.name} key={camera.name}
className={grow} className={grow}
windowVisible={windowVisible} windowVisible={
windowVisible && visibleCameras.includes(camera.name)
}
cameraConfig={camera} cameraConfig={camera}
preferredLiveMode={isSafari ? "webrtc" : "mse"} preferredLiveMode={isSafari ? "webrtc" : "mse"}
onClick={() => onSelectCamera(camera.name)} onClick={() => onSelectCamera(camera.name)}