From 7fac91dce480b8a9efac0bf7bb96022418f8d6f8 Mon Sep 17 00:00:00 2001
From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
Date: Mon, 1 Apr 2024 09:23:57 -0500
Subject: [PATCH] UI tweaks and bugfixes (#10775)
* fix wrong segments when changing filters in motion only mode
* pixel alignment, better outlines, and more figma matching
* fix stats from crashing the ui
* separate layout from aspect classes
* check for invalid value
* avoid undefined classnames
---
web/src/App.tsx | 2 +-
web/src/components/timeline/EventSegment.tsx | 14 +-
.../timeline/MotionReviewTimeline.tsx | 38 +++-
.../components/timeline/ReviewTimeline.tsx | 209 +++++++++---------
web/src/hooks/use-draggable-element.ts | 36 ++-
web/src/hooks/use-stats.ts | 6 +-
web/src/views/events/EventView.tsx | 68 +++---
web/src/views/events/RecordingView.tsx | 2 +-
8 files changed, 221 insertions(+), 154 deletions(-)
diff --git a/web/src/App.tsx b/web/src/App.tsx
index c50c53307..743d98711 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -31,7 +31,7 @@ function App() {
{isMobile && }
- {showHandlebar && (
-
- )}
- {showExportHandles && (
+ {children.length > 0 && (
<>
-
-
-
-
-
-
-
+ className={`bg-destructive rounded-full mx-auto ${
+ dense
+ ? "w-12 md:w-20"
+ : segmentDuration < 60
+ ? "w-24"
+ : "w-20"
+ } h-5 ${isDraggingHandlebar && isMobile ? "fixed top-[18px] left-1/2 transform -translate-x-1/2 z-20 w-32 h-[30px] bg-destructive/80" : "static"} flex items-center justify-center`}
+ >
+
+
-
+ )}
+ {showExportHandles && (
+ <>
+
+
+
+ >
+ )}
>
)}
diff --git a/web/src/hooks/use-draggable-element.ts b/web/src/hooks/use-draggable-element.ts
index 15b8773b2..1f957e39d 100644
--- a/web/src/hooks/use-draggable-element.ts
+++ b/web/src/hooks/use-draggable-element.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import { isMobile } from "react-device-detect";
import scrollIntoView from "scroll-into-view-if-needed";
import { useTimelineUtils } from "./use-timeline-utils";
@@ -23,6 +23,7 @@ type DraggableElementProps = {
setIsDragging: React.Dispatch>;
setDraggableElementPosition?: React.Dispatch>;
dense: boolean;
+ timelineSegments: ReactNode[];
};
function useDraggableElement({
@@ -45,6 +46,7 @@ function useDraggableElement({
setIsDragging,
setDraggableElementPosition,
dense,
+ timelineSegments,
}: DraggableElementProps) {
const [clientYPosition, setClientYPosition] = useState(null);
const [initialClickAdjustment, setInitialClickAdjustment] = useState(0);
@@ -213,10 +215,10 @@ function useDraggableElement({
);
useEffect(() => {
- if (timelineRef.current) {
+ if (timelineRef.current && timelineSegments.length) {
setSegments(Array.from(timelineRef.current.querySelectorAll(".segment")));
}
- }, [timelineRef, segmentDuration, timelineDuration, timelineCollapsed]);
+ }, [timelineRef, timelineCollapsed, timelineSegments]);
useEffect(() => {
let animationFrameId: number | null = null;
@@ -426,7 +428,13 @@ function useDraggableElement({
]);
useEffect(() => {
- if (timelineRef.current && draggableElementTime && timelineCollapsed) {
+ if (
+ timelineRef.current &&
+ draggableElementTime &&
+ timelineCollapsed &&
+ timelineSegments &&
+ segments
+ ) {
setFullTimelineHeight(timelineRef.current.scrollHeight);
const alignedSegmentTime = alignStartDateToTimeline(draggableElementTime);
@@ -452,14 +460,30 @@ function useDraggableElement({
if (setDraggableElementTime) {
setDraggableElementTime(searchTime);
}
- break;
+ return;
+ }
+ }
+ }
+ if (!segmentElement) {
+ // segment still not found, just start at the beginning of the timeline or at now()
+ if (segments?.length) {
+ const searchTime = parseInt(
+ segments[0].getAttribute("data-segment-id") || "0",
+ 10,
+ );
+ if (setDraggableElementTime) {
+ setDraggableElementTime(searchTime);
+ }
+ } else {
+ if (setDraggableElementTime) {
+ setDraggableElementTime(timelineStartAligned);
}
}
}
}
// we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [timelineCollapsed]);
+ }, [timelineCollapsed, segments]);
useEffect(() => {
if (timelineRef.current && segments) {
diff --git a/web/src/hooks/use-stats.ts b/web/src/hooks/use-stats.ts
index b5e0407a6..57461d063 100644
--- a/web/src/hooks/use-stats.ts
+++ b/web/src/hooks/use-stats.ts
@@ -37,9 +37,11 @@ export default function useStats(stats: FrigateStats | undefined) {
// check camera cpu usages
Object.entries(stats["cameras"]).forEach(([name, cam]) => {
const ffmpegAvg = parseFloat(
- stats["cpu_usages"][cam["ffmpeg_pid"]].cpu_average,
+ stats["cpu_usages"][cam["ffmpeg_pid"]]?.cpu_average,
+ );
+ const detectAvg = parseFloat(
+ stats["cpu_usages"][cam["pid"]]?.cpu_average,
);
- const detectAvg = parseFloat(stats["cpu_usages"][cam["pid"]].cpu_average);
if (!isNaN(ffmpegAvg) && ffmpegAvg >= 20.0) {
problems.push({
diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx
index 6d49762e3..1524deeea 100644
--- a/web/src/views/events/EventView.tsx
+++ b/web/src/views/events/EventView.tsx
@@ -206,12 +206,12 @@ export default function EventView({
return (
-
+
{isMobile && (
)}
{currentItems &&
@@ -533,7 +533,7 @@ function DetectionReview({
data-segment-start={
alignStartDateToTimeline(value.start_time) - segmentDuration
}
- className={`review-item outline outline-offset-1 rounded-lg shadow-none transition-all my-1 md:my-0 ${selected ? `outline-3 outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-0 duration-500"}`}
+ className="review-item relative rounded-lg"
>
+
);
})}
@@ -563,7 +566,7 @@ function DetectionReview({
)}
-
+
{reviewCameras.map((camera) => {
let grow;
+ let spans;
const aspectRatio = camera.detect.width / camera.detect.height;
if (aspectRatio > 2) {
- grow = "sm:col-span-2 aspect-wide";
+ grow = "aspect-wide";
+ spans = "sm:col-span-2";
} else if (aspectRatio < 1) {
- grow = "md:row-span-2 md:h-full aspect-tall";
+ grow = "md:h-full aspect-tall";
+ spans = "md:row-span-2";
} else {
grow = "aspect-video";
+ spans = "";
}
const detectionType = getDetectionType(camera.name);
return (
-
{
- videoPlayersRef.current[camera.name] = controller;
- }}
- onClick={() =>
- onOpenRecording({
- camera: camera.name,
- startTime: currentTime,
- severity: "significant_motion",
- })
- }
- />
+
+
{
+ videoPlayersRef.current[camera.name] = controller;
+ }}
+ onClick={() =>
+ onOpenRecording({
+ camera: camera.name,
+ startTime: currentTime,
+ severity: "significant_motion",
+ })
+ }
+ />
+
+
);
})}
-