import { isDesktop, isIOS } from "react-device-detect"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from "../../ui/sheet"; import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, } from "../../ui/drawer"; import { SearchResult } from "@/types/search"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { getIconForLabel } from "@/utils/iconUtil"; import { useApiHost } from "@/api"; import { Button } from "../../ui/button"; import { useCallback, useEffect, useMemo, useState } from "react"; import axios from "axios"; import { toast } from "sonner"; import { Textarea } from "../../ui/textarea"; type SearchDetailDialogProps = { search?: SearchResult; setSearch: (search: SearchResult | undefined) => void; setSimilarity?: () => void; }; export default function SearchDetailDialog({ search, setSearch, setSimilarity, }: SearchDetailDialogProps) { const { data: config } = useSWR("config", { revalidateOnFocus: false, }); const apiHost = useApiHost(); // data const [desc, setDesc] = useState(search?.description); // we have to make sure the current selected search item stays in sync useEffect(() => setDesc(search?.description), [search]); const formattedDate = useFormattedTimestamp( search?.start_time ?? 0, config?.ui.time_format == "24hour" ? "%b %-d %Y, %H:%M" : "%b %-d %Y, %I:%M %p", ); const score = useMemo(() => { if (!search) { return 0; } const value = search.score ?? search.data.top_score; return Math.round(value * 100); }, [search]); const subLabelScore = useMemo(() => { if (!search) { return undefined; } if (search.sub_label) { return Math.round((search.data?.top_score ?? 0) * 100); } else { return undefined; } }, [search]); // api const updateDescription = useCallback(() => { if (!search) { return; } axios .post(`events/${search.id}/description`, { description: desc }) .then((resp) => { if (resp.status == 200) { toast.success("Successfully saved description", { position: "top-center", }); } }) .catch(() => { toast.error("Failed to update the description", { position: "top-center", }); setDesc(search.description); }); }, [desc, search]); // content const Overlay = isDesktop ? Sheet : Drawer; const Content = isDesktop ? SheetContent : DrawerContent; const Header = isDesktop ? SheetHeader : DrawerHeader; const Title = isDesktop ? SheetTitle : DrawerTitle; const Description = isDesktop ? SheetDescription : DrawerDescription; return ( { if (!open) { setSearch(undefined); } }} >
Tracked Object Details Tracked object details
{search && (
Label
{getIconForLabel(search.label, "size-4 text-primary")} {search.label} {search.sub_label && ` (${search.sub_label})`}
Score
{score}%{subLabelScore && ` (${subLabelScore}%)`}
Camera
{search.camera.replaceAll("_", " ")}
Timestamp
{formattedDate}
Description