Allow deletion of history items (#9030)

* Allow deletion of history items

* Remove type
This commit is contained in:
Nicolas Mowen 2023-12-20 17:38:06 -07:00 committed by Blake Blackshear
parent 7bec162353
commit feb3ee0703
2 changed files with 96 additions and 10 deletions

View File

@ -3,7 +3,7 @@ import PreviewThumbnailPlayer from "../player/PreviewThumbnailPlayer";
import { Card } from "../ui/card"; import { Card } from "../ui/card";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import ActivityIndicator from "../ui/activity-indicator"; 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 { HiOutlineVideoCamera } from "react-icons/hi";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { import {
@ -16,6 +16,7 @@ type HistoryCardProps = {
relevantPreview?: Preview; relevantPreview?: Preview;
shouldAutoPlay: boolean; shouldAutoPlay: boolean;
onClick?: () => void; onClick?: () => void;
onDelete?: () => void;
}; };
export default function HistoryCard({ export default function HistoryCard({
@ -23,6 +24,7 @@ export default function HistoryCard({
timeline, timeline,
shouldAutoPlay, shouldAutoPlay,
onClick, onClick,
onDelete,
}: HistoryCardProps) { }: HistoryCardProps) {
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
@ -43,7 +45,8 @@ export default function HistoryCard({
shouldAutoPlay={shouldAutoPlay} shouldAutoPlay={shouldAutoPlay}
/> />
<div className="p-2"> <div className="p-2">
<div className="text-sm flex"> <div className="text-sm flex justify-between items-center">
<div>
<LuClock className="h-5 w-5 mr-2 inline" /> <LuClock className="h-5 w-5 mr-2 inline" />
{formatUnixTimestampToDateTime(timeline.time, { {formatUnixTimestampToDateTime(timeline.time, {
strftime_fmt: strftime_fmt:
@ -52,7 +55,19 @@ export default function HistoryCard({
date_style: "medium", date_style: "medium",
})} })}
</div> </div>
<div className="capitalize text-sm flex align-center mt-1"> <LuTrash
className="w-5 h-5 m-1 cursor-pointer"
stroke="#f87171"
onClick={(e: Event) => {
e.stopPropagation();
if (onDelete) {
onDelete();
}
}}
/>
</div>
<div className="capitalize text-sm flex items-center mt-1">
<HiOutlineVideoCamera className="h-5 w-5 mr-2 inline" /> <HiOutlineVideoCamera className="h-5 w-5 mr-2 inline" />
{timeline.camera.replaceAll("_", " ")} {timeline.camera.replaceAll("_", " ")}
</div> </div>

View File

@ -9,6 +9,16 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import axios from "axios"; import axios from "axios";
import TimelinePlayerCard from "@/components/card/TimelinePlayerCard"; import TimelinePlayerCard from "@/components/card/TimelinePlayerCard";
import { getHourlyTimelineData } from "@/utils/historyUtil"; import { getHourlyTimelineData } from "@/utils/historyUtil";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
const API_LIMIT = 200; const API_LIMIT = 200;
@ -36,6 +46,7 @@ function History() {
const { const {
data: timelinePages, data: timelinePages,
mutate: updateHistory,
size, size,
setSize, setSize,
isValidating, isValidating,
@ -86,6 +97,40 @@ function History() {
[size, setSize, isValidating, isDone] [size, setSize, isValidating, isDone]
); );
const [itemsToDelete, setItemsToDelete] = useState<string[] | null>(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) { if (!config || !timelineCards || timelineCards.length == 0) {
return <ActivityIndicator />; return <ActivityIndicator />;
} }
@ -94,6 +139,31 @@ function History() {
<> <>
<Heading as="h2">Review</Heading> <Heading as="h2">Review</Heading>
<AlertDialog
open={itemsToDelete != null}
onOpenChange={(_) => setItemsToDelete(null)}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{`Delete ${itemsToDelete?.length} events?`}</AlertDialogTitle>
<AlertDialogDescription>
This will delete all events associated with these objects.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setItemsToDelete(null)}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
className="bg-red-500"
onClick={() => onDeleteMulti()}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<TimelinePlayerCard <TimelinePlayerCard
timeline={playback} timeline={playback}
onDismiss={() => setPlayback(undefined)} onDismiss={() => setPlayback(undefined)}
@ -167,6 +237,7 @@ function History() {
onClick={() => { onClick={() => {
setPlayback(timeline); setPlayback(timeline);
}} }}
onDelete={() => onDelete(timeline)}
/> />
); );
})} })}