From 78e278d5e86ae3761d41ca83fb4dbeabb3a46a9b Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 5 Nov 2025 20:49:28 -0600 Subject: [PATCH] turn annotation settings into a popover --- .../overlay/detail/AnnotationSettingsPane.tsx | 2 +- .../overlay/detail/SearchDetailDialog.tsx | 69 +++++++++++++++---- .../overlay/detail/TrackingDetails.tsx | 25 +------ 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/web/src/components/overlay/detail/AnnotationSettingsPane.tsx b/web/src/components/overlay/detail/AnnotationSettingsPane.tsx index c180502f4..94c44ecdf 100644 --- a/web/src/components/overlay/detail/AnnotationSettingsPane.tsx +++ b/web/src/components/overlay/detail/AnnotationSettingsPane.tsx @@ -140,7 +140,7 @@ export function AnnotationSettingsPane({ } return ( -
+
{t("trackingDetails.annotationSettings.title")} diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index c6581a796..d3d0b8e5c 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -32,6 +32,7 @@ import { FaChevronRight, } from "react-icons/fa"; import { TrackingDetails } from "./TrackingDetails"; +import { AnnotationSettingsPane } from "./AnnotationSettingsPane"; import { LuSettings } from "react-icons/lu"; import { DetailStreamProvider } from "@/context/detail-stream-context"; import { @@ -74,6 +75,7 @@ import { useIsAdmin } from "@/hooks/use-is-admin"; import { getTranslatedLabel } from "@/utils/i18n"; import { CameraNameLabel } from "@/components/camera/CameraNameLabel"; import { DialogPortal } from "@radix-ui/react-dialog"; +import { useDetailStream } from "@/context/detail-stream-context"; const SEARCH_TABS = ["snapshot", "tracking_details"] as const; export type SearchTab = (typeof SEARCH_TABS)[number]; @@ -147,17 +149,46 @@ function TabsWithActions({ setSearch={setSearch} setSimilarity={setSimilarity} /> -
+ {pageToggle === "tracking_details" && ( + + )} +
+ ); +} + +type AnnotationSettingsPopoverProps = { + search: SearchResult; + showControls?: boolean; + setShowControls?: (v: boolean) => void; +}; + +function AnnotationSettingsPopover({ + search, + showControls, + setShowControls, +}: AnnotationSettingsPopoverProps) { + const { t } = useTranslation(["views/explore"]); + const { annotationOffset, setAnnotationOffset } = useDetailStream(); + const [showZones, setShowZones] = useState(true); + + return ( +
+ - + + + @@ -165,7 +196,23 @@ function TabsWithActions({ -
+ + { + if (typeof value === "function") { + const newValue = value(annotationOffset); + setAnnotationOffset(newValue); + } else { + setAnnotationOffset(value); + } + }} + /> + +
); } @@ -221,8 +268,6 @@ function DialogContentComponent({ /> ) : undefined } - showControls={showControls} - setShowControls={setShowControls} /> ); } diff --git a/web/src/components/overlay/detail/TrackingDetails.tsx b/web/src/components/overlay/detail/TrackingDetails.tsx index 6afb90cce..df4c92c30 100644 --- a/web/src/components/overlay/detail/TrackingDetails.tsx +++ b/web/src/components/overlay/detail/TrackingDetails.tsx @@ -8,8 +8,6 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { getIconForLabel } from "@/utils/iconUtil"; import { LuCircle, LuFolderX } from "react-icons/lu"; import { cn } from "@/lib/utils"; -import { AnnotationSettingsPane } from "./AnnotationSettingsPane"; - import HlsVideoPlayer from "@/components/player/HlsVideoPlayer"; import { baseUrl } from "@/api/baseUrl"; import { REVIEW_PADDING } from "@/types/review"; @@ -40,15 +38,12 @@ type TrackingDetailsProps = { event: Event; fullscreen?: boolean; tabs?: React.ReactNode; - showControls?: boolean; - setShowControls?: (v: boolean) => void; }; export function TrackingDetails({ className, event, tabs, - showControls = false, }: TrackingDetailsProps) { const videoRef = useRef(null); const { t } = useTranslation(["views/explore"]); @@ -58,8 +53,7 @@ export function TrackingDetails({ const [displaySource, _setDisplaySource] = useState<"video" | "image">( "video", ); - const { setSelectedObjectIds, annotationOffset, setAnnotationOffset } = - useDetailStream(); + const { setSelectedObjectIds, annotationOffset } = useDetailStream(); // manualOverride holds a record-stream timestamp explicitly chosen by the // user (eg, clicking a lifecycle row). When null we display `currentTime`. @@ -90,7 +84,6 @@ export function TrackingDetails({ const containerRef = useRef(null); const [_selectedZone, setSelectedZone] = useState(""); const [_lifecycleZones, setLifecycleZones] = useState([]); - const [showZones, setShowZones] = useState(true); const [seekToTimestamp, setSeekToTimestamp] = useState(null); const aspectRatio = useMemo(() => { @@ -464,22 +457,6 @@ export function TrackingDetails({ {t("trackingDetails.autoTrackingTips")}
)} - {showControls && ( - { - if (typeof value === "function") { - const newValue = value(annotationOffset); - setAnnotationOffset(newValue); - } else { - setAnnotationOffset(value); - } - }} - /> - )}