mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-16 13:47:07 +02:00
Show ongoing events at top of events page (#8168)
* Show ongoing events separately * Separate to separate event function * Change icon type * Hide in progress when date range search occurs * Collapse in progress when filtering * Fix event overlay * Make tooltip more clear Co-authored-by: Blake Blackshear <blakeb@blakeshome.com> --------- Co-authored-by: Blake Blackshear <blakeb@blakeshome.com>
This commit is contained in:
parent
d4d2bb2521
commit
8626160df2
@ -31,6 +31,9 @@ import Timepicker from '../components/TimePicker';
|
|||||||
import TimelineSummary from '../components/TimelineSummary';
|
import TimelineSummary from '../components/TimelineSummary';
|
||||||
import TimelineEventOverlay from '../components/TimelineEventOverlay';
|
import TimelineEventOverlay from '../components/TimelineEventOverlay';
|
||||||
import { Score } from '../icons/Score';
|
import { Score } from '../icons/Score';
|
||||||
|
import { About } from '../icons/About';
|
||||||
|
import MenuIcon from '../icons/Menu';
|
||||||
|
import { MenuOpen } from '../icons/MenuOpen';
|
||||||
|
|
||||||
const API_LIMIT = 25;
|
const API_LIMIT = 25;
|
||||||
|
|
||||||
@ -91,13 +94,15 @@ export default function Events({ path, ...props }) {
|
|||||||
showDeleteFavorite: false,
|
showDeleteFavorite: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [showInProgress, setShowInProgress] = useState(true);
|
||||||
|
|
||||||
const eventsFetcher = useCallback(
|
const eventsFetcher = useCallback(
|
||||||
(path, params) => {
|
(path, params) => {
|
||||||
if (searchParams.event) {
|
if (searchParams.event) {
|
||||||
path = `${path}/${searchParams.event}`;
|
path = `${path}/${searchParams.event}`;
|
||||||
return axios.get(path).then((res) => [res.data]);
|
return axios.get(path).then((res) => [res.data]);
|
||||||
}
|
}
|
||||||
params = { ...params, include_thumbnails: 0, limit: API_LIMIT };
|
params = { ...params, in_progress: 0, include_thumbnails: 0, limit: API_LIMIT };
|
||||||
return axios.get(path, { params }).then((res) => res.data);
|
return axios.get(path, { params }).then((res) => res.data);
|
||||||
},
|
},
|
||||||
[searchParams]
|
[searchParams]
|
||||||
@ -116,6 +121,7 @@ export default function Events({ path, ...props }) {
|
|||||||
[searchParams]
|
[searchParams]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data: ongoingEvents } = useSWR(['events', { in_progress: 1, include_thumbnails: 0 }]);
|
||||||
const { data: eventPages, mutate, size, setSize, isValidating } = useSWRInfinite(getKey, eventsFetcher);
|
const { data: eventPages, mutate, size, setSize, isValidating } = useSWRInfinite(getKey, eventsFetcher);
|
||||||
|
|
||||||
const { data: allLabels } = useSWR(['labels']);
|
const { data: allLabels } = useSWR(['labels']);
|
||||||
@ -238,6 +244,7 @@ export default function Events({ path, ...props }) {
|
|||||||
|
|
||||||
const handleSelectDateRange = useCallback(
|
const handleSelectDateRange = useCallback(
|
||||||
(dates) => {
|
(dates) => {
|
||||||
|
setShowInProgress(false);
|
||||||
setSearchParams({ ...searchParams, before: dates.before, after: dates.after });
|
setSearchParams({ ...searchParams, before: dates.before, after: dates.after });
|
||||||
setState({ ...state, showDatePicker: false });
|
setState({ ...state, showDatePicker: false });
|
||||||
},
|
},
|
||||||
@ -253,6 +260,7 @@ export default function Events({ path, ...props }) {
|
|||||||
|
|
||||||
const onFilter = useCallback(
|
const onFilter = useCallback(
|
||||||
(name, value) => {
|
(name, value) => {
|
||||||
|
setShowInProgress(false);
|
||||||
const updatedParams = { ...searchParams, [name]: value };
|
const updatedParams = { ...searchParams, [name]: value };
|
||||||
setSearchParams(updatedParams);
|
setSearchParams(updatedParams);
|
||||||
const queryString = Object.keys(updatedParams)
|
const queryString = Object.keys(updatedParams)
|
||||||
@ -604,13 +612,134 @@ export default function Events({ path, ...props }) {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
{ongoingEvents ? (
|
||||||
|
<div>
|
||||||
|
<div className="flex">
|
||||||
|
<Heading className="py-4" size="sm">
|
||||||
|
Ongoing Events
|
||||||
|
</Heading>
|
||||||
|
<Button
|
||||||
|
className="rounded-full"
|
||||||
|
type="text"
|
||||||
|
color="gray"
|
||||||
|
aria-label="Events for currently tracked objects. Recordings are only saved based on your retain settings. See the recording docs for more info."
|
||||||
|
>
|
||||||
|
<About className="w-5" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="rounded-full ml-auto"
|
||||||
|
type="iconOnly"
|
||||||
|
color="blue"
|
||||||
|
onClick={() => setShowInProgress(!showInProgress)}
|
||||||
|
>
|
||||||
|
{showInProgress ? <MenuOpen className="w-6" /> : <MenuIcon className="w-6" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{showInProgress &&
|
||||||
|
ongoingEvents.map((event, _) => {
|
||||||
|
return (
|
||||||
|
<Event
|
||||||
|
className="my-2"
|
||||||
|
key={event.id}
|
||||||
|
config={config}
|
||||||
|
event={event}
|
||||||
|
eventDetailType={eventDetailType}
|
||||||
|
eventOverlay={eventOverlay}
|
||||||
|
viewEvent={viewEvent}
|
||||||
|
setViewEvent={setViewEvent}
|
||||||
|
uploading={uploading}
|
||||||
|
handleEventDetailTabChange={handleEventDetailTabChange}
|
||||||
|
onEventFrameSelected={onEventFrameSelected}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onDispose={() => {
|
||||||
|
this.player = null;
|
||||||
|
}}
|
||||||
|
onDownloadClick={onDownloadClick}
|
||||||
|
onReady={(player) => {
|
||||||
|
this.player = player;
|
||||||
|
this.player.on('playing', () => {
|
||||||
|
setEventOverlay(undefined);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onSave={onSave}
|
||||||
|
showSubmitToPlus={showSubmitToPlus}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<Heading className="py-4" size="sm">
|
||||||
|
Past Events
|
||||||
|
</Heading>
|
||||||
{eventPages ? (
|
{eventPages ? (
|
||||||
eventPages.map((page, i) => {
|
eventPages.map((page, i) => {
|
||||||
const lastPage = eventPages.length === i + 1;
|
const lastPage = eventPages.length === i + 1;
|
||||||
return page.map((event, j) => {
|
return page.map((event, j) => {
|
||||||
const lastEvent = lastPage && page.length === j + 1;
|
const lastEvent = lastPage && page.length === j + 1;
|
||||||
return (
|
return (
|
||||||
<Fragment key={event.id}>
|
<Event
|
||||||
|
key={event.id}
|
||||||
|
config={config}
|
||||||
|
event={event}
|
||||||
|
eventDetailType={eventDetailType}
|
||||||
|
eventOverlay={eventOverlay}
|
||||||
|
viewEvent={viewEvent}
|
||||||
|
setViewEvent={setViewEvent}
|
||||||
|
lastEvent={lastEvent}
|
||||||
|
lastEventRef={lastEventRef}
|
||||||
|
uploading={uploading}
|
||||||
|
handleEventDetailTabChange={handleEventDetailTabChange}
|
||||||
|
onEventFrameSelected={onEventFrameSelected}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onDispose={() => {
|
||||||
|
this.player = null;
|
||||||
|
}}
|
||||||
|
onDownloadClick={onDownloadClick}
|
||||||
|
onReady={(player) => {
|
||||||
|
this.player = player;
|
||||||
|
this.player.on('playing', () => {
|
||||||
|
setEventOverlay(undefined);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onSave={onSave}
|
||||||
|
showSubmitToPlus={showSubmitToPlus}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<ActivityIndicator />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>{isDone ? null : <ActivityIndicator />}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Event({
|
||||||
|
className = '',
|
||||||
|
config,
|
||||||
|
event,
|
||||||
|
eventDetailType,
|
||||||
|
eventOverlay,
|
||||||
|
viewEvent,
|
||||||
|
setViewEvent,
|
||||||
|
lastEvent,
|
||||||
|
lastEventRef,
|
||||||
|
uploading,
|
||||||
|
handleEventDetailTabChange,
|
||||||
|
onEventFrameSelected,
|
||||||
|
onDelete,
|
||||||
|
onDispose,
|
||||||
|
onDownloadClick,
|
||||||
|
onReady,
|
||||||
|
onSave,
|
||||||
|
showSubmitToPlus,
|
||||||
|
}) {
|
||||||
|
const apiHost = useApiHost();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
<div
|
<div
|
||||||
ref={lastEvent ? lastEventRef : false}
|
ref={lastEvent ? lastEventRef : false}
|
||||||
className="flex bg-slate-100 dark:bg-slate-800 rounded cursor-pointer min-w-[330px]"
|
className="flex bg-slate-100 dark:bg-slate-800 rounded cursor-pointer min-w-[330px]"
|
||||||
@ -688,9 +817,7 @@ export default function Events({ path, ...props }) {
|
|||||||
<Button
|
<Button
|
||||||
color="gray"
|
color="gray"
|
||||||
disabled={uploading.includes(event.id)}
|
disabled={uploading.includes(event.id)}
|
||||||
onClick={(e) =>
|
onClick={(e) => showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)}
|
||||||
showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
|
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
|
||||||
</Button>
|
</Button>
|
||||||
@ -732,9 +859,7 @@ export default function Events({ path, ...props }) {
|
|||||||
<div>
|
<div>
|
||||||
<TimelineSummary
|
<TimelineSummary
|
||||||
event={event}
|
event={event}
|
||||||
onFrameSelected={(frame, seekSeconds) =>
|
onFrameSelected={(frame, seekSeconds) => onEventFrameSelected(event, frame, seekSeconds)}
|
||||||
onEventFrameSelected(event, frame, seekSeconds)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
@ -749,21 +874,11 @@ export default function Events({ path, ...props }) {
|
|||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
seekOptions={{ forward: 10, backward: 5 }}
|
seekOptions={{ forward: 10, backward: 5 }}
|
||||||
onReady={(player) => {
|
onReady={onReady}
|
||||||
this.player = player;
|
onDispose={onDispose}
|
||||||
this.player.on('playing', () => {
|
|
||||||
setEventOverlay(undefined);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onDispose={() => {
|
|
||||||
this.player = null;
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{eventOverlay ? (
|
{eventOverlay ? (
|
||||||
<TimelineEventOverlay
|
<TimelineEventOverlay eventOverlay={eventOverlay} cameraConfig={config.cameras[event.camera]} />
|
||||||
eventOverlay={eventOverlay}
|
|
||||||
cameraConfig={config.cameras[event.camera]}
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
</div>
|
</div>
|
||||||
@ -789,15 +904,6 @@ export default function Events({ path, ...props }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<ActivityIndicator />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>{isDone ? null : <ActivityIndicator />}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user