mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
resolve #1143
This commit is contained in:
parent
3712a8ab80
commit
85de881181
@ -34,7 +34,27 @@ function reducer(state, { type, payload, meta }) {
|
|||||||
draftState.queries[url] = { status: ok ? FetchStatus.LOADED : FetchStatus.ERROR, data, fetchId };
|
draftState.queries[url] = { status: ok ? FetchStatus.LOADED : FetchStatus.ERROR, data, fetchId };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
case 'DELETE': {
|
||||||
|
const { eventId } = payload;
|
||||||
|
|
||||||
|
return produce(state, (draftState) => {
|
||||||
|
Object.keys(draftState.queries).map(function (url, index) {
|
||||||
|
// If no url or data has no array length then just return state.
|
||||||
|
if (!(url in draftState.queries) || !draftState.queries[url].data.length) return state;
|
||||||
|
|
||||||
|
//Find the index to remove
|
||||||
|
const removeIndex = draftState.queries[url].data.map((event) => event.id).indexOf(eventId);
|
||||||
|
if (removeIndex === -1) return;
|
||||||
|
|
||||||
|
// We need to keep track of deleted items, This will be used to calculate "ReachEnd" for auto load new events. Events.jsx
|
||||||
|
const totDeleted = state.queries[url].deleted || 0;
|
||||||
|
|
||||||
|
// Splice the deleted index.
|
||||||
|
draftState.queries[url].data.splice(removeIndex, 1);
|
||||||
|
draftState.queries[url].deleted = totDeleted + 1;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -91,8 +111,23 @@ export function useFetch(url, fetchId) {
|
|||||||
|
|
||||||
const data = state.queries[url].data || null;
|
const data = state.queries[url].data || null;
|
||||||
const status = state.queries[url].status;
|
const status = state.queries[url].status;
|
||||||
|
const deleted = state.queries[url].deleted || 0;
|
||||||
|
|
||||||
return { data, status };
|
return { data, status, deleted };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDelete() {
|
||||||
|
const { dispatch, state } = useContext(Api);
|
||||||
|
|
||||||
|
async function deleteEvent(eventId) {
|
||||||
|
if (!eventId) return { success: false };
|
||||||
|
|
||||||
|
const response = await fetch(`${state.host}/api/events/${eventId}`, { method: 'DELETE' });
|
||||||
|
await dispatch({ type: 'DELETE', payload: { eventId } });
|
||||||
|
return await (response.status < 300 ? response.json() : { success: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return deleteEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useApiHost() {
|
export function useApiHost() {
|
||||||
|
@ -10,7 +10,7 @@ import Dialog from '../components/Dialog';
|
|||||||
import Heading from '../components/Heading';
|
import Heading from '../components/Heading';
|
||||||
import Link from '../components/Link';
|
import Link from '../components/Link';
|
||||||
import VideoPlayer from '../components/VideoPlayer';
|
import VideoPlayer from '../components/VideoPlayer';
|
||||||
import { FetchStatus, useApiHost, useEvent } from '../api';
|
import { FetchStatus, useApiHost, useEvent, useDelete } from '../api';
|
||||||
import { Table, Thead, Tbody, Th, Tr, Td } from '../components/Table';
|
import { Table, Thead, Tbody, Th, Tr, Td } from '../components/Table';
|
||||||
|
|
||||||
export default function Event({ eventId }) {
|
export default function Event({ eventId }) {
|
||||||
@ -18,6 +18,7 @@ export default function Event({ eventId }) {
|
|||||||
const { data, status } = useEvent(eventId);
|
const { data, status } = useEvent(eventId);
|
||||||
const [showDialog, setShowDialog] = useState(false);
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
const [deleteStatus, setDeleteStatus] = useState(FetchStatus.NONE);
|
const [deleteStatus, setDeleteStatus] = useState(FetchStatus.NONE);
|
||||||
|
const setDeleteEvent = useDelete();
|
||||||
|
|
||||||
const handleClickDelete = () => {
|
const handleClickDelete = () => {
|
||||||
setShowDialog(true);
|
setShowDialog(true);
|
||||||
@ -30,8 +31,7 @@ export default function Event({ eventId }) {
|
|||||||
const handleClickDeleteDialog = useCallback(async () => {
|
const handleClickDeleteDialog = useCallback(async () => {
|
||||||
let success;
|
let success;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${apiHost}/api/events/${eventId}`, { method: 'DELETE' });
|
success = await setDeleteEvent(eventId);
|
||||||
success = await (response.status < 300 ? response.json() : { success: true });
|
|
||||||
setDeleteStatus(success ? FetchStatus.LOADED : FetchStatus.ERROR);
|
setDeleteStatus(success ? FetchStatus.LOADED : FetchStatus.ERROR);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setDeleteStatus(FetchStatus.ERROR);
|
setDeleteStatus(FetchStatus.ERROR);
|
||||||
@ -42,7 +42,7 @@ export default function Event({ eventId }) {
|
|||||||
setShowDialog(false);
|
setShowDialog(false);
|
||||||
route('/events', true);
|
route('/events', true);
|
||||||
}
|
}
|
||||||
}, [apiHost, eventId, setShowDialog]);
|
}, [eventId, setShowDialog]);
|
||||||
|
|
||||||
if (status !== FetchStatus.LOADED) {
|
if (status !== FetchStatus.LOADED) {
|
||||||
return <ActivityIndicator />;
|
return <ActivityIndicator />;
|
||||||
@ -64,7 +64,11 @@ export default function Event({ eventId }) {
|
|||||||
<Dialog
|
<Dialog
|
||||||
onDismiss={handleDismissDeleteDialog}
|
onDismiss={handleDismissDeleteDialog}
|
||||||
title="Delete Event?"
|
title="Delete Event?"
|
||||||
text="This event will be permanently deleted along with any related clips and snapshots"
|
text={
|
||||||
|
deleteStatus === FetchStatus.ERROR
|
||||||
|
? 'Could not delete event, please try again.'
|
||||||
|
: 'This event will be permanently deleted along with any related clips and snapshots'
|
||||||
|
}
|
||||||
actions={[
|
actions={[
|
||||||
deleteStatus !== FetchStatus.LOADING
|
deleteStatus !== FetchStatus.LOADING
|
||||||
? { text: 'Delete', color: 'red', onClick: handleClickDeleteDialog }
|
? { text: 'Delete', color: 'red', onClick: handleClickDeleteDialog }
|
||||||
|
@ -20,6 +20,7 @@ const reducer = (state = initialState, action) => {
|
|||||||
meta: { searchString },
|
meta: { searchString },
|
||||||
payload,
|
payload,
|
||||||
} = action;
|
} = action;
|
||||||
|
|
||||||
return produce(state, (draftState) => {
|
return produce(state, (draftState) => {
|
||||||
draftState.searchStrings[searchString] = true;
|
draftState.searchStrings[searchString] = true;
|
||||||
draftState.events.push(...payload);
|
draftState.events.push(...payload);
|
||||||
@ -56,17 +57,17 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
|||||||
const [{ events, reachedEnd, searchStrings }, dispatch] = useReducer(reducer, initialState);
|
const [{ events, reachedEnd, searchStrings }, dispatch] = useReducer(reducer, initialState);
|
||||||
const { searchParams: initialSearchParams } = new URL(window.location);
|
const { searchParams: initialSearchParams } = new URL(window.location);
|
||||||
const [searchString, setSearchString] = useState(`${defaultSearchString(limit)}&${initialSearchParams.toString()}`);
|
const [searchString, setSearchString] = useState(`${defaultSearchString(limit)}&${initialSearchParams.toString()}`);
|
||||||
const { data, status } = useEvents(searchString);
|
const { data, status, deleted } = useEvents(searchString);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data && !(searchString in searchStrings)) {
|
if (data && !(searchString in searchStrings)) {
|
||||||
dispatch({ type: 'APPEND_EVENTS', payload: data, meta: { searchString } });
|
dispatch({ type: 'APPEND_EVENTS', payload: data, meta: { searchString } });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data && Array.isArray(data) && data.length < limit) {
|
if (data && Array.isArray(data) && data.length + deleted < limit) {
|
||||||
dispatch({ type: 'REACHED_END', meta: { searchString } });
|
dispatch({ type: 'REACHED_END', meta: { searchString } });
|
||||||
}
|
}
|
||||||
}, [data, limit, searchString, searchStrings]);
|
}, [data, limit, searchString, searchStrings, deleted]);
|
||||||
|
|
||||||
const [entry, setIntersectNode] = useIntersectionObserver();
|
const [entry, setIntersectNode] = useIntersectionObserver();
|
||||||
|
|
||||||
@ -100,7 +101,6 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const searchParams = useMemo(() => new URLSearchParams(searchString), [searchString]);
|
const searchParams = useMemo(() => new URLSearchParams(searchString), [searchString]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 w-full">
|
<div className="space-y-4 w-full">
|
||||||
<Heading>Events</Heading>
|
<Heading>Events</Heading>
|
||||||
|
Loading…
Reference in New Issue
Block a user