diff --git a/web/public/locales/en/components/dialog.json b/web/public/locales/en/components/dialog.json index b73e0ca3f9..efc77db1b9 100644 --- a/web/public/locales/en/components/dialog.json +++ b/web/public/locales/en/components/dialog.json @@ -24,6 +24,9 @@ }, "state": { "submitted": "Submitted" + }, + "toast": { + "error": "Failed to submit to Frigate+. Please check your network connection and try again." } } }, diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 518b7e69e6..e8e4368ea4 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -1251,30 +1251,42 @@ function ObjectDetailsTab({ return; } - falsePositive - ? axios.put(`events/${search.id}/false_positive`) - : axios.post(`events/${search.id}/plus`, { - include_annotation: 1, - }); + try { + const resp = falsePositive + ? await axios.put(`events/${search.id}/false_positive`) + : await axios.post(`events/${search.id}/plus`, { + include_annotation: 1, + }); - setState("submitted"); - setSearch({ ...search, plus_id: "new_upload" }); - mutate( - (key) => isEventsKey(key), - (currentData: SearchResult[][] | SearchResult[] | undefined) => - mapSearchResults(currentData, (event) => - event.id === search.id - ? { ...event, plus_id: "new_upload" } - : event, - ), - { - optimisticData: true, - rollbackOnError: true, - revalidate: false, - }, - ); + if (resp.status !== 200 || !resp.data?.success) { + throw new Error(); + } + + setState("submitted"); + setSearch({ ...search, plus_id: "new_upload" }); + mutate( + (key) => isEventsKey(key), + (currentData: SearchResult[][] | SearchResult[] | undefined) => + mapSearchResults(currentData, (event) => + event.id === search.id + ? { ...event, plus_id: "new_upload" } + : event, + ), + { + optimisticData: true, + rollbackOnError: true, + revalidate: false, + }, + ); + } catch { + setState("reviewing"); + toast.error( + t("explore.plus.review.toast.error", { ns: "components/dialog" }), + { position: "top-center" }, + ); + } }, - [search, mutate, mapSearchResults, setSearch, isEventsKey], + [search, mutate, mapSearchResults, setSearch, isEventsKey, t], ); const popoverContainerRef = useRef(null); diff --git a/web/src/components/overlay/detail/TrackingDetails.tsx b/web/src/components/overlay/detail/TrackingDetails.tsx index c0f109e165..88e48f8012 100644 --- a/web/src/components/overlay/detail/TrackingDetails.tsx +++ b/web/src/components/overlay/detail/TrackingDetails.tsx @@ -1225,11 +1225,15 @@ function LifecycleIconRow({ { - const resp = await axios.post( - `/${item.camera}/plus/${item.timestamp + annotationOffset / 1000}`, - ); + try { + const resp = await axios.post( + `/${item.camera}/plus/${item.timestamp + annotationOffset / 1000}`, + ); + + if (resp.status !== 200) { + throw new Error(); + } - if (resp && resp.status == 200) { toast.success( t("toast.success.submittedFrigatePlus", { ns: "components/player", @@ -1238,8 +1242,8 @@ function LifecycleIconRow({ position: "top-center", }, ); - } else { - toast.success( + } catch { + toast.error( t("toast.error.submitFrigatePlusFailed", { ns: "components/player", }), diff --git a/web/src/components/overlay/dialog/FrigatePlusDialog.tsx b/web/src/components/overlay/dialog/FrigatePlusDialog.tsx index d1737df8fa..347d5c10bc 100644 --- a/web/src/components/overlay/dialog/FrigatePlusDialog.tsx +++ b/web/src/components/overlay/dialog/FrigatePlusDialog.tsx @@ -10,6 +10,7 @@ import { isDesktop, isMobile, isSafari } from "react-device-detect"; import { cn } from "@/lib/utils"; import { useCallback, useEffect, useState } from "react"; import axios from "axios"; +import { toast } from "sonner"; import { useTranslation, Trans } from "react-i18next"; import { Button } from "@/components/ui/button"; import ActivityIndicator from "@/components/indicators/activity-indicator"; @@ -48,13 +49,28 @@ export function FrigatePlusDialog({ 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(); + + try { + const resp = falsePositive + ? await axios.put(`events/${upload.id}/false_positive`) + : await axios.post(`events/${upload.id}/plus`, { + include_annotation: 1, + }); + + if (resp.status !== 200 || !resp.data?.success) { + throw new Error(); + } + + setState("submitted"); + onEventUploaded(); + } catch { + setState("reviewing"); + toast.error(t("explore.plus.review.toast.error"), { + position: "top-center", + }); + } }, - [upload, onEventUploaded], + [upload, onEventUploaded, t], ); const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();