diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 2ea44d8c9..c6581a796 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 { LuSettings } from "react-icons/lu"; import { DetailStreamProvider } from "@/context/detail-stream-context"; import { MobilePage, @@ -77,6 +78,234 @@ import { DialogPortal } from "@radix-ui/react-dialog"; const SEARCH_TABS = ["snapshot", "tracking_details"] as const; export type SearchTab = (typeof SEARCH_TABS)[number]; +type TabsWithActionsProps = { + search: SearchResult; + searchTabs: SearchTab[]; + pageToggle: SearchTab; + setPageToggle: (v: SearchTab) => void; + config?: FrigateConfig; + setSearch: (s: SearchResult | undefined) => void; + setSimilarity?: () => void; + showControls?: boolean; + setShowControls?: (v: boolean) => void; +}; + +function TabsWithActions({ + search, + searchTabs, + pageToggle, + setPageToggle, + config, + setSearch, + setSimilarity, + showControls, + setShowControls, +}: TabsWithActionsProps) { + const { t } = useTranslation(["views/explore", "views/faceLibrary"]); + + if (!search) return null; + + return ( +
+ +
+ { + if (value) { + setPageToggle(value); + } + }} + > + {Object.values(searchTabs).map((item) => ( + +
+ {item === "snapshot" + ? search?.has_snapshot + ? t("type.snapshot") + : t("type.thumbnail") + : t(`type.${item}`)} +
+
+ ))} +
+ +
+
+ +
+ + + + + + + {t("trackingDetails.adjustAnnotationSettings")} + + + +
+
+ ); +} + +type DialogContentComponentProps = { + page: SearchTab; + search: SearchResult; + isDesktop: boolean; + apiHost: string; + config?: FrigateConfig; + searchTabs: SearchTab[]; + pageToggle: SearchTab; + setPageToggle: (v: SearchTab) => void; + setSearch: (s: SearchResult | undefined) => void; + setInputFocused: React.Dispatch>; + setSimilarity?: () => void; + showControls?: boolean; + setShowControls?: (v: boolean) => void; +}; + +function DialogContentComponent({ + page, + search, + isDesktop, + apiHost, + config, + searchTabs, + pageToggle, + setPageToggle, + setSearch, + setInputFocused, + setSimilarity, + showControls, + setShowControls, +}: DialogContentComponentProps) { + if (page === "tracking_details") { + return ( + + ) : undefined + } + showControls={showControls} + setShowControls={setShowControls} + /> + ); + } + + // Snapshot page content + const snapshotElement = search.has_snapshot ? ( + + ) : ( +
+ +
+ ); + + if (isDesktop) { + return ( +
+
+ {snapshotElement} +
+
+ +
+ +
+
+
+ ); + } + + // mobile + return ( + <> + {snapshotElement} + + + ); +} + type SearchDetailDialogProps = { search?: SearchResult; page: SearchTab; @@ -87,6 +316,7 @@ type SearchDetailDialogProps = { onPrevious?: () => void; onNext?: () => void; }; + export default function SearchDetailDialog({ search, page, @@ -135,6 +365,10 @@ export default function SearchDetailDialog({ } }, [search]); + // show/hide annotation settings + + const [showControls, setShowControls] = useState(false); + const searchTabs = useMemo(() => { if (!config || !search) { return []; @@ -160,44 +394,6 @@ export default function SearchDetailDialog({ } }, [pageToggle, searchTabs, setSearchPage]); - // Tabs component for reuse - const tabsComponent = ( - -
- { - if (value) { - setPageToggle(value); - } - }} - > - {Object.values(searchTabs).map((item) => ( - -
- {item === "snapshot" - ? search?.has_snapshot - ? t("type.snapshot") - : t("type.thumbnail") - : t(`type.${item}`)} -
-
- ))} -
- -
-
- ); - if (!search) { return; } @@ -293,152 +489,38 @@ export default function SearchDetailDialog({ - {isDesktop ? ( - page === "tracking_details" ? ( - - } + + {!isDesktop && ( +
+ - ) : ( -
-
- {page === "snapshot" && search.has_snapshot && ( - - )} - {page === "snapshot" && !search.has_snapshot && ( -
- -
- )} -
-
-
- {page == "snapshot" && ( - - )} -
-
-
- ) - ) : ( - <> - -
- { - if (value) { - setPageToggle(value); - } - }} - > - {Object.values(searchTabs).map((item) => ( - -
- {t(`type.${item}`)} -
-
- ))} -
- -
-
- {page == "snapshot" && ( - <> - {search.has_snapshot && ( - - )} - {page == "snapshot" && !search.has_snapshot && ( - - )} - - - )} - {page == "tracking_details" && ( - - )} - +
)} + + @@ -449,17 +531,13 @@ type ObjectDetailsTabProps = { search: SearchResult; config?: FrigateConfig; setSearch: (search: SearchResult | undefined) => void; - setSimilarity?: () => void; setInputFocused: React.Dispatch>; - tabs?: React.ReactNode; }; function ObjectDetailsTab({ search, config, setSearch, - setSimilarity, setInputFocused, - tabs, }: ObjectDetailsTabProps) { const { t, i18n } = useTranslation([ "views/explore", @@ -895,20 +973,6 @@ function ObjectDetailsTab({ const popoverContainerRef = useRef(null); return (
- {tabs && ( -
-
{tabs}
-
- -
-
- )} -
@@ -1301,12 +1365,17 @@ function ObjectDetailsTab({ type ObjectSnapshotTabProps = { search: Event; + className?: string; + onEventUploaded?: () => void; }; -export function ObjectSnapshotTab({ search }: ObjectSnapshotTabProps) { +export function ObjectSnapshotTab({ + search, + className, +}: ObjectSnapshotTabProps) { const [imgRef, imgLoaded, onImgLoad] = useImageLoaded(); return ( -
+
void; }; export function TrackingDetails({ className, event, tabs, - actions, + showControls = false, }: TrackingDetailsProps) { const videoRef = useRef(null); const { t } = useTranslation(["views/explore"]); @@ -95,7 +90,6 @@ export function TrackingDetails({ const containerRef = useRef(null); const [_selectedZone, setSelectedZone] = useState(""); const [_lifecycleZones, setLifecycleZones] = useState([]); - const [showControls, setShowControls] = useState(false); const [showZones, setShowZones] = useState(true); const [seekToTimestamp, setSeekToTimestamp] = useState(null); @@ -454,10 +448,9 @@ export function TrackingDetails({
- {isDesktop && (tabs || actions) && ( + {isDesktop && tabs && (
{tabs}
-
{actions}
)}
-
-
- - - - - - - {t("trackingDetails.adjustAnnotationSettings")} - - - -
-
- {config?.cameras[event.camera]?.onvif.autotracking .enabled_in_config && (