mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-02 09:15:22 +03:00
event component added to events list. New delete reducer
This commit is contained in:
parent
2d7d766b0b
commit
5e82da8ee3
@ -1,10 +1,11 @@
|
|||||||
import { h } from 'preact';
|
import { h, Fragment } from 'preact';
|
||||||
import ActivityIndicator from '../components/ActivityIndicator';
|
import ActivityIndicator from '../components/ActivityIndicator';
|
||||||
import Heading from '../components/Heading';
|
import Heading from '../components/Heading';
|
||||||
import Link from '../components/Link';
|
import Link from '../components/Link';
|
||||||
import Select from '../components/Select';
|
import Select from '../components/Select';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
|
import Event from './Event';
|
||||||
import { useIntersectionObserver } from '../hooks';
|
import { useIntersectionObserver } from '../hooks';
|
||||||
import { FetchStatus, useApiHost, useConfig, useEvents } from '../api';
|
import { FetchStatus, useApiHost, useConfig, useEvents } from '../api';
|
||||||
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from '../components/Table';
|
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from '../components/Table';
|
||||||
@ -12,9 +13,20 @@ import { useCallback, useEffect, useMemo, useReducer, useState } from 'preact/ho
|
|||||||
|
|
||||||
const API_LIMIT = 25;
|
const API_LIMIT = 25;
|
||||||
|
|
||||||
const initialState = Object.freeze({ events: [], reachedEnd: false, searchStrings: {} });
|
const initialState = Object.freeze({ events: [], reachedEnd: false, searchStrings: {}, deleted: 0 });
|
||||||
const reducer = (state = initialState, action) => {
|
const reducer = (state = initialState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case 'DELETE_EVENT': {
|
||||||
|
const { deletedId } = action;
|
||||||
|
|
||||||
|
return produce(state, (draftState) => {
|
||||||
|
const idx = draftState.events.findIndex((e) => e.id === deletedId);
|
||||||
|
if (idx === -1) return state;
|
||||||
|
|
||||||
|
draftState.events.splice(idx, 1);
|
||||||
|
draftState.deleted++;
|
||||||
|
});
|
||||||
|
}
|
||||||
case 'APPEND_EVENTS': {
|
case 'APPEND_EVENTS': {
|
||||||
const {
|
const {
|
||||||
meta: { searchString },
|
meta: { searchString },
|
||||||
@ -54,11 +66,13 @@ function removeDefaultSearchKeys(searchParams) {
|
|||||||
|
|
||||||
export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
||||||
const apiHost = useApiHost();
|
const apiHost = useApiHost();
|
||||||
const [{ events, reachedEnd, searchStrings }, dispatch] = useReducer(reducer, initialState);
|
const [{ events, reachedEnd, searchStrings, deleted }, dispatch] = useReducer(reducer, initialState);
|
||||||
const { searchParams: initialSearchParams } = new URL(window.location);
|
const { searchParams: initialSearchParams } = new URL(window.location);
|
||||||
|
const [viewEvent, setViewEvent] = useState(null);
|
||||||
const [searchString, setSearchString] = useState(`${defaultSearchString(limit)}&${initialSearchParams.toString()}`);
|
const [searchString, setSearchString] = useState(`${defaultSearchString(limit)}&${initialSearchParams.toString()}`);
|
||||||
const { data, status, deleted } = useEvents(searchString);
|
const { data, status, deletedId } = useEvents(searchString);
|
||||||
|
|
||||||
|
let scrollToRef = {};
|
||||||
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 } });
|
||||||
@ -67,7 +81,11 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
|||||||
if (data && Array.isArray(data) && data.length + deleted < 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, deleted]);
|
|
||||||
|
if (deletedId) {
|
||||||
|
dispatch({ type: 'DELETE_EVENT', deletedId });
|
||||||
|
}
|
||||||
|
}, [data, limit, searchString, searchStrings, deleted, deletedId]);
|
||||||
|
|
||||||
const [entry, setIntersectNode] = useIntersectionObserver();
|
const [entry, setIntersectNode] = useIntersectionObserver();
|
||||||
|
|
||||||
@ -99,8 +117,13 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
|||||||
},
|
},
|
||||||
[limit, pathname, setSearchString]
|
[limit, pathname, setSearchString]
|
||||||
);
|
);
|
||||||
|
const viewEventHandler = (id) => {
|
||||||
|
if (viewEvent === id) return setViewEvent(null);
|
||||||
|
if (id in scrollToRef) scrollToRef[id].scrollIntoView();
|
||||||
|
setViewEvent(id);
|
||||||
|
};
|
||||||
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>
|
||||||
@ -131,12 +154,20 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
|||||||
const end = new Date(parseInt(endTime * 1000, 10));
|
const end = new Date(parseInt(endTime * 1000, 10));
|
||||||
const ref = i === events.length - 1 ? lastCellRef : undefined;
|
const ref = i === events.length - 1 ? lastCellRef : undefined;
|
||||||
return (
|
return (
|
||||||
<Tr data-testid={`event-${id}`} key={id}>
|
<Fragment key={id}>
|
||||||
|
<Tr data-testid={`event-${id}`} className={`${viewEvent === id ? 'border-none' : ''}`}>
|
||||||
<Td className="w-40">
|
<Td className="w-40">
|
||||||
<a href={`/events/${id}`} ref={ref} data-start-time={startTime} data-reached-end={reachedEnd}>
|
<a
|
||||||
|
onClick={() => viewEventHandler(id)}
|
||||||
|
ref={ref}
|
||||||
|
data-start-time={startTime}
|
||||||
|
data-reached-end={reachedEnd}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
|
ref={(el) => (scrollToRef[id] = el)}
|
||||||
width="150"
|
width="150"
|
||||||
height="150"
|
height="150"
|
||||||
|
className="cursor-pointer"
|
||||||
style="min-height: 48px; min-width: 48px;"
|
style="min-height: 48px; min-width: 48px;"
|
||||||
src={`${apiHost}/api/events/${id}/thumbnail.jpg`}
|
src={`${apiHost}/api/events/${id}/thumbnail.jpg`}
|
||||||
/>
|
/>
|
||||||
@ -180,6 +211,16 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
|
|||||||
<Td>{start.toLocaleTimeString()}</Td>
|
<Td>{start.toLocaleTimeString()}</Td>
|
||||||
<Td>{end.toLocaleTimeString()}</Td>
|
<Td>{end.toLocaleTimeString()}</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
{viewEvent === id ? (
|
||||||
|
<Tr className="border-b-1">
|
||||||
|
<Td colspan="8">
|
||||||
|
<div>
|
||||||
|
<Event eventId={viewEvent} close={() => setViewEvent(null)} />
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
) : null}
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user