diff --git a/web/src/components/card/HistoryCard.tsx b/web/src/components/card/HistoryCard.tsx index 114eb1976..3cb151bf8 100644 --- a/web/src/components/card/HistoryCard.tsx +++ b/web/src/components/card/HistoryCard.tsx @@ -3,7 +3,7 @@ import PreviewThumbnailPlayer from "../player/PreviewThumbnailPlayer"; import { Card } from "../ui/card"; import { FrigateConfig } from "@/types/frigateConfig"; import ActivityIndicator from "../ui/activity-indicator"; -import { LuClock } from "react-icons/lu"; +import { LuClock, LuTrash } from "react-icons/lu"; import { HiOutlineVideoCamera } from "react-icons/hi"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { @@ -16,6 +16,7 @@ type HistoryCardProps = { relevantPreview?: Preview; shouldAutoPlay: boolean; onClick?: () => void; + onDelete?: () => void; }; export default function HistoryCard({ @@ -23,6 +24,7 @@ export default function HistoryCard({ timeline, shouldAutoPlay, onClick, + onDelete, }: HistoryCardProps) { const { data: config } = useSWR("config"); @@ -43,16 +45,29 @@ export default function HistoryCard({ shouldAutoPlay={shouldAutoPlay} />
-
- - {formatUnixTimestampToDateTime(timeline.time, { - strftime_fmt: - config.ui.time_format == "24hour" ? "%H:%M:%S" : "%I:%M:%S %p", - time_style: "medium", - date_style: "medium", - })} +
+
+ + {formatUnixTimestampToDateTime(timeline.time, { + strftime_fmt: + config.ui.time_format == "24hour" ? "%H:%M:%S" : "%I:%M:%S %p", + time_style: "medium", + date_style: "medium", + })} +
+ { + e.stopPropagation(); + + if (onDelete) { + onDelete(); + } + }} + />
-
+
{timeline.camera.replaceAll("_", " ")}
diff --git a/web/src/pages/History.tsx b/web/src/pages/History.tsx index e2342e423..e9e5a00a3 100644 --- a/web/src/pages/History.tsx +++ b/web/src/pages/History.tsx @@ -9,6 +9,16 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import axios from "axios"; import TimelinePlayerCard from "@/components/card/TimelinePlayerCard"; import { getHourlyTimelineData } from "@/utils/historyUtil"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; const API_LIMIT = 200; @@ -36,6 +46,7 @@ function History() { const { data: timelinePages, + mutate: updateHistory, size, setSize, isValidating, @@ -86,6 +97,40 @@ function History() { [size, setSize, isValidating, isDone] ); + const [itemsToDelete, setItemsToDelete] = useState(null); + const onDelete = useCallback( + async (timeline: Card) => { + if (timeline.entries.length > 1) { + const uniqueEvents = new Set( + timeline.entries.map((entry) => entry.source_id) + ); + setItemsToDelete(new Array(...uniqueEvents)); + } else { + const response = await axios.delete( + `events/${timeline.entries[0].source_id}` + ); + if (response.status === 200) { + updateHistory(); + } + } + }, + [updateHistory] + ); + const onDeleteMulti = useCallback(async () => { + if (!itemsToDelete) { + return; + } + + const responses = itemsToDelete.map(async (id) => { + return axios.delete(`events/${id}`); + }); + + if ((await responses[0]).status == 200) { + updateHistory(); + setItemsToDelete(null); + } + }, [itemsToDelete, updateHistory]); + if (!config || !timelineCards || timelineCards.length == 0) { return ; } @@ -94,6 +139,31 @@ function History() { <> Review + setItemsToDelete(null)} + > + + + {`Delete ${itemsToDelete?.length} events?`} + + This will delete all events associated with these objects. + + + + setItemsToDelete(null)}> + Cancel + + onDeleteMulti()} + > + Delete + + + + + setPlayback(undefined)} @@ -167,6 +237,7 @@ function History() { onClick={() => { setPlayback(timeline); }} + onDelete={() => onDelete(timeline)} /> ); })}