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:
Nicolas Mowen 2023-10-15 11:01:44 -06:00 committed by GitHub
parent d4d2bb2521
commit 8626160df2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -31,6 +31,9 @@ import Timepicker from '../components/TimePicker';
import TimelineSummary from '../components/TimelineSummary';
import TimelineEventOverlay from '../components/TimelineEventOverlay';
import { Score } from '../icons/Score';
import { About } from '../icons/About';
import MenuIcon from '../icons/Menu';
import { MenuOpen } from '../icons/MenuOpen';
const API_LIMIT = 25;
@ -91,13 +94,15 @@ export default function Events({ path, ...props }) {
showDeleteFavorite: false,
});
const [showInProgress, setShowInProgress] = useState(true);
const eventsFetcher = useCallback(
(path, params) => {
if (searchParams.event) {
path = `${path}/${searchParams.event}`;
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);
},
[searchParams]
@ -116,6 +121,7 @@ export default function Events({ path, ...props }) {
[searchParams]
);
const { data: ongoingEvents } = useSWR(['events', { in_progress: 1, include_thumbnails: 0 }]);
const { data: eventPages, mutate, size, setSize, isValidating } = useSWRInfinite(getKey, eventsFetcher);
const { data: allLabels } = useSWR(['labels']);
@ -238,6 +244,7 @@ export default function Events({ path, ...props }) {
const handleSelectDateRange = useCallback(
(dates) => {
setShowInProgress(false);
setSearchParams({ ...searchParams, before: dates.before, after: dates.after });
setState({ ...state, showDatePicker: false });
},
@ -253,6 +260,7 @@ export default function Events({ path, ...props }) {
const onFilter = useCallback(
(name, value) => {
setShowInProgress(false);
const updatedParams = { ...searchParams, [name]: value };
setSearchParams(updatedParams);
const queryString = Object.keys(updatedParams)
@ -604,13 +612,134 @@ export default function Events({ path, ...props }) {
</Dialog>
)}
<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.map((page, i) => {
const lastPage = eventPages.length === i + 1;
return page.map((event, j) => {
const lastEvent = lastPage && page.length === j + 1;
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
ref={lastEvent ? lastEventRef : false}
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
color="gray"
disabled={uploading.includes(event.id)}
onClick={(e) =>
showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)
}
onClick={(e) => showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)}
>
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
</Button>
@ -732,9 +859,7 @@ export default function Events({ path, ...props }) {
<div>
<TimelineSummary
event={event}
onFrameSelected={(frame, seekSeconds) =>
onEventFrameSelected(event, frame, seekSeconds)
}
onFrameSelected={(frame, seekSeconds) => onEventFrameSelected(event, frame, seekSeconds)}
/>
<div>
<VideoPlayer
@ -749,21 +874,11 @@ export default function Events({ path, ...props }) {
],
}}
seekOptions={{ forward: 10, backward: 5 }}
onReady={(player) => {
this.player = player;
this.player.on('playing', () => {
setEventOverlay(undefined);
});
}}
onDispose={() => {
this.player = null;
}}
onReady={onReady}
onDispose={onDispose}
>
{eventOverlay ? (
<TimelineEventOverlay
eventOverlay={eventOverlay}
cameraConfig={config.cameras[event.camera]}
/>
<TimelineEventOverlay eventOverlay={eventOverlay} cameraConfig={config.cameras[event.camera]} />
) : null}
</VideoPlayer>
</div>
@ -789,15 +904,6 @@ export default function Events({ path, ...props }) {
</div>
</div>
)}
</Fragment>
);
});
})
) : (
<ActivityIndicator />
)}
</div>
<div>{isDone ? null : <ActivityIndicator />}</div>
</div>
);
}