diff --git a/web/src/components/overlay/dialog/FrigatePlusDialog.tsx b/web/src/components/overlay/dialog/FrigatePlusDialog.tsx index 7eff75335..b57e73755 100644 --- a/web/src/components/overlay/dialog/FrigatePlusDialog.tsx +++ b/web/src/components/overlay/dialog/FrigatePlusDialog.tsx @@ -6,51 +6,199 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Event } from "@/types/event"; -import { isDesktop, isMobile } from "react-device-detect"; -import { ObjectSnapshotTab } from "../detail/SearchDetailDialog"; +import { isDesktop, isMobile, isSafari } from "react-device-detect"; import { cn } from "@/lib/utils"; +import { useCallback, useEffect, useState } from "react"; +import axios from "axios"; +import { useTranslation, Trans } from "react-i18next"; +import { Button } from "@/components/ui/button"; +import ActivityIndicator from "@/components/indicators/activity-indicator"; +import { FaCheckCircle } from "react-icons/fa"; +import { Card, CardContent } from "@/components/ui/card"; +import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; +import ImageLoadingIndicator from "@/components/indicators/ImageLoadingIndicator"; +import { baseUrl } from "@/api/baseUrl"; +import { getTranslatedLabel } from "@/utils/i18n"; +import useImageLoaded from "@/hooks/use-image-loaded"; -type FrigatePlusDialogProps = { +export type FrigatePlusDialogProps = { upload?: Event; dialog?: boolean; onClose: () => void; onEventUploaded: () => void; }; + export function FrigatePlusDialog({ upload, dialog = true, onClose, onEventUploaded, }: FrigatePlusDialogProps) { - if (!upload) { - return; - } - if (dialog) { - return ( - (!open ? onClose() : null)} + const { t, i18n } = useTranslation(["components/dialog"]); + + type SubmissionState = "reviewing" | "uploading" | "submitted"; + const [state, setState] = useState( + upload?.plus_id ? "submitted" : "reviewing", + ); + useEffect(() => { + setState(upload?.plus_id ? "submitted" : "reviewing"); + }, [upload?.plus_id]); + + 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 }); + setState("submitted"); + onEventUploaded(); + }, + [upload, onEventUploaded], + ); + + const [imgRef, imgLoaded, onImgLoad] = useImageLoaded(); + const showCard = + !!upload && + upload.data.type === "object" && + upload.plus_id !== "not_enabled" && + upload.end_time && + upload.label !== "on_demand"; + + if (!dialog || !upload) return null; + + return ( + (!open ? onClose() : null)}> + - - - Submit to Frigate+ - - Submit this snapshot to Frigate+ - - - + Submit to Frigate+ + + Submit this snapshot to Frigate+ + + + +
+ - -
- ); - } +
+ +
+ + {upload.id && ( +
+ {`${upload.label}`} +
+ )} +
+ + {showCard && ( + + +
+
+ {t("explore.plus.submitToPlus.label")} +
+
+ {t("explore.plus.submitToPlus.desc")} +
+
+
+ {state === "reviewing" && ( + <> +
+ {i18n.language === "en" ? ( + /^[aeiou]/i.test(upload.label || "") ? ( + + explore.plus.review.question.ask_an + + ) : ( + + explore.plus.review.question.ask_a + + ) + ) : ( + + explore.plus.review.question.ask_full + + )} +
+
+ + +
+ + )} + {state === "uploading" && } + {state === "submitted" && ( +
+ + {t("explore.plus.review.state.submitted")} +
+ )} +
+
+
+ )} +
+
+
+ + +
+ ); }