mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Allow deletion of history items (#9030)
* Allow deletion of history items * Remove type
This commit is contained in:
parent
7bec162353
commit
feb3ee0703
@ -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,16 +45,29 @@ 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">
|
||||||
<LuClock className="h-5 w-5 mr-2 inline" />
|
<div>
|
||||||
{formatUnixTimestampToDateTime(timeline.time, {
|
<LuClock className="h-5 w-5 mr-2 inline" />
|
||||||
strftime_fmt:
|
{formatUnixTimestampToDateTime(timeline.time, {
|
||||||
config.ui.time_format == "24hour" ? "%H:%M:%S" : "%I:%M:%S %p",
|
strftime_fmt:
|
||||||
time_style: "medium",
|
config.ui.time_format == "24hour" ? "%H:%M:%S" : "%I:%M:%S %p",
|
||||||
date_style: "medium",
|
time_style: "medium",
|
||||||
})}
|
date_style: "medium",
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<LuTrash
|
||||||
|
className="w-5 h-5 m-1 cursor-pointer"
|
||||||
|
stroke="#f87171"
|
||||||
|
onClick={(e: Event) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (onDelete) {
|
||||||
|
onDelete();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="capitalize text-sm flex align-center mt-1">
|
<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>
|
||||||
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
Loading…
Reference in New Issue
Block a user