mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-09 08:12:48 +03:00
Compare commits
4
Commits
1f0994dfc8
...
15d3741d5d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15d3741d5d | ||
|
|
013b5bd342 | ||
|
|
1f898ec15b | ||
|
|
c717efa67e |
@@ -182,7 +182,7 @@ async def get_motion_search_status_endpoint(
|
||||
)
|
||||
|
||||
job = get_motion_search_job(job_id)
|
||||
if not job:
|
||||
if not job or job.camera != camera_name:
|
||||
return JSONResponse(
|
||||
content={"success": False, "message": "Job not found"},
|
||||
status_code=404,
|
||||
@@ -253,7 +253,7 @@ async def cancel_motion_search_endpoint(
|
||||
)
|
||||
|
||||
job = get_motion_search_job(job_id)
|
||||
if not job:
|
||||
if not job or job.camera != camera_name:
|
||||
return JSONResponse(
|
||||
content={"success": False, "message": "Job not found"},
|
||||
status_code=404,
|
||||
|
||||
@@ -73,7 +73,9 @@ export const VirtualizedEventSegments = forwardRef<
|
||||
Math.ceil((scrollTop + clientHeight) / SEGMENT_HEIGHT) +
|
||||
OVERSCAN_COUNT,
|
||||
);
|
||||
setVisibleRange({ start, end });
|
||||
setVisibleRange((prev) =>
|
||||
prev.start === start && prev.end === end ? prev : { start, end },
|
||||
);
|
||||
}
|
||||
}, [segments.length, timelineRef]);
|
||||
|
||||
|
||||
@@ -77,7 +77,9 @@ export const VirtualizedMotionSegments = forwardRef<
|
||||
Math.ceil((scrollTop + clientHeight) / SEGMENT_HEIGHT) +
|
||||
OVERSCAN_COUNT,
|
||||
);
|
||||
setVisibleRange({ start, end });
|
||||
setVisibleRange((prev) =>
|
||||
prev.start === start && prev.end === end ? prev : { start, end },
|
||||
);
|
||||
}
|
||||
}, [segments.length, timelineRef]);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTimelineUtils } from "./use-timeline-utils";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import useSWR from "swr";
|
||||
@@ -8,6 +8,8 @@ import { useTimeFormat } from "./use-date-utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useUserInteraction from "./use-user-interaction";
|
||||
|
||||
const DRAG_STATE_COMMIT_MS = 100;
|
||||
|
||||
type DraggableElementProps = {
|
||||
contentRef: React.RefObject<HTMLElement | null>;
|
||||
timelineRef: React.RefObject<HTMLDivElement | null>;
|
||||
@@ -61,6 +63,8 @@ function useDraggableElement({
|
||||
|
||||
const [clientYPosition, setClientYPosition] = useState<number | null>(null);
|
||||
const [initialClickAdjustment, setInitialClickAdjustment] = useState(0);
|
||||
const lastDragTimeCommitRef = useRef(0);
|
||||
const pendingDragTimeRef = useRef<number | null>(null);
|
||||
const [elementScrollIntoView, setElementScrollIntoView] = useState(true);
|
||||
const [scrollEdgeSize, setScrollEdgeSize] = useState<number>();
|
||||
const [fullTimelineHeight, setFullTimelineHeight] = useState<number>();
|
||||
@@ -154,9 +158,14 @@ function useDraggableElement({
|
||||
if (isDragging) {
|
||||
setIsDragging(false);
|
||||
setInitialClickAdjustment(0);
|
||||
|
||||
if (pendingDragTimeRef.current !== null && setDraggableElementTime) {
|
||||
setDraggableElementTime(pendingDragTimeRef.current);
|
||||
pendingDragTimeRef.current = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
[isDragging, setIsDragging],
|
||||
[isDragging, setIsDragging, setDraggableElementTime],
|
||||
);
|
||||
|
||||
const timestampToPixels = useCallback(
|
||||
@@ -346,9 +355,21 @@ function useDraggableElement({
|
||||
);
|
||||
|
||||
if (setDraggableElementTime) {
|
||||
setDraggableElementTime(
|
||||
targetSegmentTime + segmentDuration * (offset / segmentHeight),
|
||||
);
|
||||
const newTime =
|
||||
targetSegmentTime + segmentDuration * (offset / segmentHeight);
|
||||
const now = performance.now();
|
||||
|
||||
// don't commit on every animation frame, only commit it at a
|
||||
// set interval to avoid React's nested update limit
|
||||
if (now - lastDragTimeCommitRef.current >= DRAG_STATE_COMMIT_MS) {
|
||||
lastDragTimeCommitRef.current = now;
|
||||
pendingDragTimeRef.current = null;
|
||||
setDraggableElementTime(newTime);
|
||||
} else {
|
||||
// Hold the newest value; handleMouseUp flushes it so the
|
||||
// release still lands exactly where the handle was dropped.
|
||||
pendingDragTimeRef.current = newTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (draggingAtTopEdge || draggingAtBottomEdge) {
|
||||
|
||||
@@ -8,6 +8,7 @@ function useUserInteraction({ elementRef }: UseUserInteractionProps) {
|
||||
const [userInteracting, setUserInteracting] = useState(false);
|
||||
const interactionTimeout = useRef<NodeJS.Timeout>(undefined);
|
||||
const isProgrammaticScroll = useRef(false);
|
||||
const userInteractingRef = useRef(false);
|
||||
|
||||
const setProgrammaticScroll = useCallback(() => {
|
||||
isProgrammaticScroll.current = true;
|
||||
@@ -16,13 +17,18 @@ function useUserInteraction({ elementRef }: UseUserInteractionProps) {
|
||||
useEffect(() => {
|
||||
const handleUserInteraction = () => {
|
||||
if (!isProgrammaticScroll.current) {
|
||||
setUserInteracting(true);
|
||||
// Only commit state on the leading edge
|
||||
if (!userInteractingRef.current) {
|
||||
userInteractingRef.current = true;
|
||||
setUserInteracting(true);
|
||||
}
|
||||
|
||||
if (interactionTimeout.current) {
|
||||
clearTimeout(interactionTimeout.current);
|
||||
}
|
||||
|
||||
interactionTimeout.current = setTimeout(() => {
|
||||
userInteractingRef.current = false;
|
||||
setUserInteracting(false);
|
||||
}, 3000);
|
||||
} else {
|
||||
|
||||
@@ -51,7 +51,12 @@ import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
||||
import { useCameraPreviews } from "@/hooks/use-camera-previews";
|
||||
import { getChunkedTimeDay } from "@/utils/timelineUtil";
|
||||
|
||||
import { MotionData, REVIEW_PADDING, ZoomLevel } from "@/types/review";
|
||||
import {
|
||||
MotionData,
|
||||
REVIEW_PADDING,
|
||||
ReviewSegment,
|
||||
ZoomLevel,
|
||||
} from "@/types/review";
|
||||
import {
|
||||
ASPECT_VERTICAL_LAYOUT,
|
||||
ASPECT_WIDE_LAYOUT,
|
||||
@@ -85,6 +90,7 @@ type MotionSearchViewProps = {
|
||||
};
|
||||
|
||||
const DEFAULT_EXPORT_WINDOW_SECONDS = 60;
|
||||
const NO_REVIEW_EVENTS: ReviewSegment[] = [];
|
||||
|
||||
export default function MotionSearchView({
|
||||
config,
|
||||
@@ -514,6 +520,12 @@ export default function MotionSearchView({
|
||||
: null,
|
||||
);
|
||||
|
||||
const timelineMotionEvents = useMemo(() => motionData ?? [], [motionData]);
|
||||
const timelineNoRecordings = useMemo(
|
||||
() => noRecordings ?? [],
|
||||
[noRecordings],
|
||||
);
|
||||
|
||||
const recordingParams = useMemo(
|
||||
() => ({
|
||||
before: currentTimeRange.before,
|
||||
@@ -1054,11 +1066,11 @@ export default function MotionSearchView({
|
||||
showHandlebar={true}
|
||||
handlebarTime={currentTime}
|
||||
setHandlebarTime={setCurrentTime}
|
||||
events={[]}
|
||||
motion_events={motionData ?? []}
|
||||
noRecordingRanges={noRecordings ?? []}
|
||||
events={NO_REVIEW_EVENTS}
|
||||
motion_events={timelineMotionEvents}
|
||||
noRecordingRanges={timelineNoRecordings}
|
||||
contentRef={contentRef}
|
||||
onHandlebarDraggingChange={(dragging) => setScrubbing(dragging)}
|
||||
onHandlebarDraggingChange={setScrubbing}
|
||||
showExportHandles={
|
||||
(exportMode === "timeline" || exportMode === "timeline_multi") &&
|
||||
Boolean(exportRange)
|
||||
@@ -1489,7 +1501,7 @@ export default function MotionSearchView({
|
||||
isDesktop
|
||||
? mainCameraAspect === "tall"
|
||||
? "mr-2 h-full min-h-0 min-w-0 flex-1 items-center"
|
||||
: "mr-2 h-full min-h-0 min-w-0 flex-1"
|
||||
: "mx-2 h-full min-h-0 min-w-0 flex-1"
|
||||
: mainCameraAspect === "tall"
|
||||
? "flex-1 portrait:h-[40dvh] portrait:max-h-[40dvh] portrait:flex-shrink-0 portrait:flex-grow-0 portrait:basis-auto portrait:items-center portrait:justify-center"
|
||||
: "flex-1 portrait:max-h-[40dvh] portrait:flex-shrink-0 portrait:flex-grow-0 portrait:basis-auto landscape:items-center landscape:justify-center",
|
||||
|
||||
@@ -1199,7 +1199,7 @@ function Timeline({
|
||||
motion_events={motionData ?? []}
|
||||
noRecordingRanges={noRecordings ?? []}
|
||||
contentRef={contentRef}
|
||||
onHandlebarDraggingChange={(scrubbing) => setScrubbing(scrubbing)}
|
||||
onHandlebarDraggingChange={setScrubbing}
|
||||
isZooming={isZooming}
|
||||
zoomDirection={zoomDirection}
|
||||
onZoomChange={handleZoomChange}
|
||||
|
||||
@@ -37,6 +37,9 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
esbuild: {
|
||||
keepNames: true,
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
|
||||
Reference in New Issue
Block a user