Move plus dialog to separate component

This commit is contained in:
Nicolas Mowen 2024-08-14 13:15:35 -06:00
parent 943114c052
commit 9d18061d0f
2 changed files with 165 additions and 138 deletions

View File

@ -0,0 +1,123 @@
import { baseUrl } from "@/api/baseUrl";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Event } from "@/types/event";
import { FrigateConfig } from "@/types/frigateConfig";
import axios from "axios";
import { useCallback, useMemo } from "react";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import useSWR from "swr";
type FrigatePlusDialogProps = {
upload?: Event;
onClose: () => void;
onEventUploaded: () => void;
};
export function FrigatePlusDialog({
upload,
onClose,
onEventUploaded,
}: FrigatePlusDialogProps) {
const { data: config } = useSWR<FrigateConfig>("config");
// layout
const grow = useMemo(() => {
if (!config || !upload) {
return "";
}
const camera = config.cameras[upload.camera];
if (!camera) {
return "";
}
if (camera.detect.width / camera.detect.height < 16 / 9) {
return "aspect-video object-contain";
}
return "";
}, [config, upload]);
// upload
const onSubmitToPlus = useCallback(
async (falsePositive: boolean) => {
if (!upload) {
return;
}
falsePositive
? axios.put(`events/${upload.id}/false_positive`)
: axios.post(`events/${upload.id}/plus`, {
include_annotation: 1,
});
onEventUploaded();
onClose();
},
[upload, onClose, onEventUploaded],
);
return (
<Dialog
open={upload != undefined}
onOpenChange={(open) => (!open ? onClose() : null)}
>
<DialogContent className="md:max-w-3xl lg:max-w-4xl xl:max-w-7xl">
<TransformWrapper minScale={1.0} wheel={{ smoothStep: 0.005 }}>
<DialogHeader>
<DialogTitle>Submit To Frigate+</DialogTitle>
<DialogDescription>
Objects in locations you want to avoid are not false positives.
Submitting them as false positives will confuse the model.
</DialogDescription>
</DialogHeader>
<TransformComponent
wrapperStyle={{
width: "100%",
height: "100%",
}}
contentStyle={{
position: "relative",
width: "100%",
height: "100%",
}}
>
{upload?.id && (
<img
className={`w-full ${grow} bg-black`}
src={`${baseUrl}api/events/${upload?.id}/snapshot.jpg`}
alt={`${upload?.label}`}
/>
)}
</TransformComponent>
<DialogFooter>
<Button onClick={onClose}>Cancel</Button>
<Button
className="bg-success"
onClick={() => onSubmitToPlus(false)}
>
This is a {upload?.label}
</Button>
<Button
className="text-white"
variant="destructive"
onClick={() => onSubmitToPlus(true)}
>
This is not a {upload?.label}
</Button>
</DialogFooter>
</TransformWrapper>
</DialogContent>
</Dialog>
);
}

View File

@ -3,15 +3,8 @@ import { CamerasFilterButton } from "@/components/filter/CamerasFilterButton";
import { GeneralFilterContent } from "@/components/filter/ReviewFilterGroup"; import { GeneralFilterContent } from "@/components/filter/ReviewFilterGroup";
import Chip from "@/components/indicators/Chip"; import Chip from "@/components/indicators/Chip";
import ActivityIndicator from "@/components/indicators/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
import { import {
DropdownMenu, DropdownMenu,
@ -45,12 +38,11 @@ import { LuFolderX } from "react-icons/lu";
import { PiSlidersHorizontalFill } from "react-icons/pi"; import { PiSlidersHorizontalFill } from "react-icons/pi";
import useSWR from "swr"; import useSWR from "swr";
import useSWRInfinite from "swr/infinite"; import useSWRInfinite from "swr/infinite";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
const API_LIMIT = 100; const API_LIMIT = 100;
export default function SubmitPlus() { export default function SubmitPlus() {
const { data: config } = useSWR<FrigateConfig>("config"); // title
useEffect(() => { useEffect(() => {
document.title = "Plus - Frigate"; document.title = "Plus - Frigate";
@ -155,77 +147,6 @@ export default function SubmitPlus() {
[isValidating, isDone, size, setSize], [isValidating, isDone, size, setSize],
); );
// layout
const grow = useMemo(() => {
if (!config || !upload) {
return "";
}
const camera = config.cameras[upload.camera];
if (!camera) {
return "";
}
if (camera.detect.width / camera.detect.height < 16 / 9) {
return "aspect-video object-contain";
}
return "";
}, [config, upload]);
const onSubmitToPlus = useCallback(
async (falsePositive: boolean) => {
if (!upload) {
return;
}
falsePositive
? axios.put(`events/${upload.id}/false_positive`)
: axios.post(`events/${upload.id}/plus`, {
include_annotation: 1,
});
refresh(
(data: Event[][] | undefined) => {
if (!data) {
return data;
}
let pageIndex = -1;
let index = -1;
data.forEach((page, pIdx) => {
const search = page.findIndex((e) => e.id == upload.id);
if (search != -1) {
pageIndex = pIdx;
index = search;
}
});
if (index == -1) {
return data;
}
return [
...data.slice(0, pageIndex),
[
...data[pageIndex].slice(0, index),
{ ...data[pageIndex][index], plus_id: "new_upload" },
...data[pageIndex].slice(index + 1),
],
...data.slice(pageIndex + 1),
];
},
{ revalidate: false, populateCache: true },
);
setUpload(undefined);
},
[refresh, upload],
);
return ( return (
<div className="flex size-full flex-col"> <div className="flex size-full flex-col">
<div className="scrollbar-container flex h-16 w-full items-center justify-between overflow-x-auto px-2"> <div className="scrollbar-container flex h-16 w-full items-center justify-between overflow-x-auto px-2">
@ -254,63 +175,46 @@ export default function SubmitPlus() {
) : ( ) : (
<> <>
<div className="grid w-full gap-2 p-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5"> <div className="grid w-full gap-2 p-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
<Dialog <FrigatePlusDialog
open={upload != undefined} upload={upload}
onOpenChange={(open) => (!open ? setUpload(undefined) : null)} onClose={() => setUpload(undefined)}
> onEventUploaded={() => {
<DialogContent className="md:max-w-3xl lg:max-w-4xl xl:max-w-7xl"> refresh(
<TransformWrapper (data: Event[][] | undefined) => {
minScale={1.0} if (!data || !upload) {
wheel={{ smoothStep: 0.005 }} return data;
> }
<DialogHeader>
<DialogTitle>Submit To Frigate+</DialogTitle> let pageIndex = -1;
<DialogDescription> let index = -1;
Objects in locations you want to avoid are not false
positives. Submitting them as false positives will data.forEach((page, pIdx) => {
confuse the model. const search = page.findIndex((e) => e.id == upload.id);
</DialogDescription>
</DialogHeader> if (search != -1) {
<TransformComponent pageIndex = pIdx;
wrapperStyle={{ index = search;
width: "100%", }
height: "100%", });
}}
contentStyle={{ if (index == -1) {
position: "relative", return data;
width: "100%", }
height: "100%",
}} return [
> ...data.slice(0, pageIndex),
{upload?.id && ( [
<img ...data[pageIndex].slice(0, index),
className={`w-full ${grow} bg-black`} { ...data[pageIndex][index], plus_id: "new_upload" },
src={`${baseUrl}api/events/${upload?.id}/snapshot.jpg`} ...data[pageIndex].slice(index + 1),
alt={`${upload?.label}`} ],
/> ...data.slice(pageIndex + 1),
)} ];
</TransformComponent> },
<DialogFooter> { revalidate: false, populateCache: true },
<Button onClick={() => setUpload(undefined)}> );
Cancel }}
</Button> />
<Button
className="bg-success"
onClick={() => onSubmitToPlus(false)}
>
This is a {upload?.label}
</Button>
<Button
className="text-white"
variant="destructive"
onClick={() => onSubmitToPlus(true)}
>
This is not a {upload?.label}
</Button>
</DialogFooter>
</TransformWrapper>
</DialogContent>
</Dialog>
{events?.map((event) => { {events?.map((event) => {
if (event.data.type != "object" || event.plus_id) { if (event.data.type != "object" || event.plus_id) {