mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-11 13:45:25 +03:00
Add ability to interact with review items
This commit is contained in:
parent
a86e22e0fc
commit
f725f19ab2
@ -3,12 +3,24 @@ import { useFormattedTimestamp } from "@/hooks/use-date-utils";
|
|||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { ReviewSegment } from "@/types/review";
|
import { ReviewSegment } from "@/types/review";
|
||||||
import { getIconForLabel } from "@/utils/iconUtil";
|
import { getIconForLabel } from "@/utils/iconUtil";
|
||||||
import { isSafari } from "react-device-detect";
|
import { isDesktop, isSafari } from "react-device-detect";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import TimeAgo from "../dynamic/TimeAgo";
|
import TimeAgo from "../dynamic/TimeAgo";
|
||||||
import { useMemo } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import useImageLoaded from "@/hooks/use-image-loaded";
|
import useImageLoaded from "@/hooks/use-image-loaded";
|
||||||
import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator";
|
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 = {
|
type ReviewCardProps = {
|
||||||
event: ReviewSegment;
|
event: ReviewSegment;
|
||||||
@ -33,10 +45,61 @@ export default function ReviewCard({
|
|||||||
[event, currentTime],
|
[event, currentTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
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 = (
|
||||||
<div
|
<div
|
||||||
className="relative flex w-full cursor-pointer flex-col gap-1.5"
|
className="relative flex w-full cursor-pointer flex-col gap-1.5"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
onContextMenu={
|
||||||
|
isDesktop
|
||||||
|
? undefined
|
||||||
|
: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOptionsOpen(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<ImageLoadingIndicator
|
<ImageLoadingIndicator
|
||||||
className="absolute inset-0"
|
className="absolute inset-0"
|
||||||
@ -69,4 +132,78 @@ export default function ReviewCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (event.id == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
return (
|
||||||
|
<ContextMenu>
|
||||||
|
<ContextMenuTrigger asChild>{content}</ContextMenuTrigger>
|
||||||
|
<ContextMenuContent>
|
||||||
|
<ContextMenuItem>
|
||||||
|
<div
|
||||||
|
className="flex w-full cursor-pointer items-center justify-start gap-2 p-2"
|
||||||
|
onClick={onExport}
|
||||||
|
>
|
||||||
|
<FaCompactDisc className="text-secondary-foreground" />
|
||||||
|
<div className="text-primary">Export</div>
|
||||||
|
</div>
|
||||||
|
</ContextMenuItem>
|
||||||
|
{!event.has_been_reviewed && (
|
||||||
|
<ContextMenuItem>
|
||||||
|
<div
|
||||||
|
className="flex w-full cursor-pointer items-center justify-start gap-2 p-2"
|
||||||
|
onClick={onMarkAsReviewed}
|
||||||
|
>
|
||||||
|
<FaCircleCheck className="text-secondary-foreground" />
|
||||||
|
<div className="text-primary">Mark as reviewed</div>
|
||||||
|
</div>
|
||||||
|
</ContextMenuItem>
|
||||||
|
)}
|
||||||
|
<ContextMenuItem>
|
||||||
|
<div
|
||||||
|
className="flex w-full cursor-pointer items-center justify-start gap-2 p-2"
|
||||||
|
onClick={onDelete}
|
||||||
|
>
|
||||||
|
<HiTrash className="text-secondary-foreground" />
|
||||||
|
<div className="text-primary">Delete</div>
|
||||||
|
</div>
|
||||||
|
</ContextMenuItem>
|
||||||
|
</ContextMenuContent>
|
||||||
|
</ContextMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer open={optionsOpen} onOpenChange={setOptionsOpen}>
|
||||||
|
{content}
|
||||||
|
<DrawerContent>
|
||||||
|
<div
|
||||||
|
className="flex w-full items-center justify-start gap-2 p-2"
|
||||||
|
onClick={onExport}
|
||||||
|
>
|
||||||
|
<FaCompactDisc className="text-secondary-foreground" />
|
||||||
|
<div className="text-primary">Export</div>
|
||||||
|
</div>
|
||||||
|
{!event.has_been_reviewed && (
|
||||||
|
<div
|
||||||
|
className="flex w-full items-center justify-start gap-2 p-2"
|
||||||
|
onClick={onMarkAsReviewed}
|
||||||
|
>
|
||||||
|
<FaCircleCheck className="text-secondary-foreground" />
|
||||||
|
<div className="text-primary">Mark as reviewed</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className="flex w-full items-center justify-start gap-2 p-2"
|
||||||
|
onClick={onDelete}
|
||||||
|
>
|
||||||
|
<HiTrash className="text-secondary-foreground" />
|
||||||
|
<div className="text-primary">Delete</div>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user