import { baseUrl } from "@/api/baseUrl"; import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { FrigateConfig } from "@/types/frigateConfig"; import { ReviewSegment } from "@/types/review"; import { getIconForLabel } from "@/utils/iconUtil"; import { isDesktop, isIOS, isSafari } from "react-device-detect"; import useSWR from "swr"; import TimeAgo from "../dynamic/TimeAgo"; import { useCallback, useMemo, useState } from "react"; import useImageLoaded from "@/hooks/use-image-loaded"; import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator"; import { FaCompactDisc } from "react-icons/fa"; import { FaCircleCheck } from "react-icons/fa6"; import { HiTrash } from "react-icons/hi"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, } from "../ui/context-menu"; import { Drawer, DrawerContent } from "../ui/drawer"; import axios from "axios"; import { toast } from "sonner"; type ReviewCardProps = { event: ReviewSegment; currentTime: number; onClick?: () => void; }; export default function ReviewCard({ event, currentTime, onClick, }: ReviewCardProps) { const { data: config } = useSWR("config"); const [imgRef, imgLoaded, onImgLoad] = useImageLoaded(); const formattedDate = useFormattedTimestamp( event.start_time, config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p", ); const isSelected = useMemo( () => event.start_time <= currentTime && (event.end_time ?? Date.now() / 1000) >= currentTime, [event, currentTime], ); const [optionsOpen, setOptionsOpen] = useState(false); const onMarkAsReviewed = useCallback(async () => { await axios.post(`reviews/viewed`, { ids: [event.id] }); event.has_been_reviewed = true; setOptionsOpen(false); }, [event]); const onExport = useCallback(async () => { axios .post( `export/${event.camera}/start/${event.start_time}/end/${event.end_time}`, { playback: "realtime" }, ) .then((response) => { if (response.status == 200) { toast.success( "Successfully started export. View the file in the /exports folder.", { position: "top-center" }, ); } }) .catch((error) => { if (error.response?.data?.message) { toast.error( `Failed to start export: ${error.response.data.message}`, { position: "top-center" }, ); } else { toast.error(`Failed to start export: ${error.message}`, { position: "top-center", }); } }); setOptionsOpen(false); }, [event]); const onDelete = useCallback(async () => { await axios.post(`reviews/delete`, { ids: [event.id] }); event.id = ""; setOptionsOpen(false); }, [event]); const content = (
{ e.preventDefault(); setOptionsOpen(true); } } > { onImgLoad(); }} />
{event.data.objects.map((object) => { return getIconForLabel(object, "size-3 text-white"); })} {event.data.audio.map((audio) => { return getIconForLabel(audio, "size-3 text-white"); })}
{formattedDate}
); if (event.id == "") { return; } if (isDesktop) { return ( {content}
Export
{!event.has_been_reviewed && (
Mark as reviewed
)}
Delete
); } return ( {content}
Export
{!event.has_been_reviewed && (
Mark as reviewed
)}
Delete
); }