forked from Github/frigate
Add support for stationary and active timeline entires (#7344)
* Add support for stationary and active timeline entires * Be sure to update stored event data copy with stationary
This commit is contained in:
@@ -3,8 +3,10 @@ import useSWR from 'swr';
|
||||
import ActivityIndicator from './ActivityIndicator';
|
||||
import { formatUnixTimestampToDateTime } from '../utils/dateUtil';
|
||||
import About from '../icons/About';
|
||||
import ActiveObjectIcon from '../icons/ActiveObject';
|
||||
import PlayIcon from '../icons/Play';
|
||||
import ExitIcon from '../icons/Exit';
|
||||
import StationaryObjectIcon from '../icons/StationaryObject';
|
||||
import { Zone } from '../icons/Zone';
|
||||
import { useMemo, useState } from 'preact/hooks';
|
||||
import Button from './Button';
|
||||
@@ -77,31 +79,18 @@ export default function TimelineSummary({ event, onFrameSelected }) {
|
||||
<div className="flex flex-col">
|
||||
<div className="h-14 flex justify-center">
|
||||
<div className="sm:w-1 md:w-1/4 flex flex-row flex-nowrap justify-between overflow-auto">
|
||||
{eventTimeline.map((item, index) =>
|
||||
item.class_type == 'visible' || item.class_type == 'gone' ? (
|
||||
<Button
|
||||
key={index}
|
||||
className="rounded-full"
|
||||
type="iconOnly"
|
||||
color={index == timeIndex ? 'blue' : 'gray'}
|
||||
aria-label={window.innerWidth > 640 ? getTimelineItemDescription(config, item, event) : ''}
|
||||
onClick={() => onSelectMoment(index)}
|
||||
>
|
||||
{item.class_type == 'visible' ? <PlayIcon className="w-8" /> : <ExitIcon className="w-8" />}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
key={index}
|
||||
className="rounded-full"
|
||||
type="iconOnly"
|
||||
color={index == timeIndex ? 'blue' : 'gray'}
|
||||
aria-label={window.innerWidth > 640 ? getTimelineItemDescription(config, item, event) : ''}
|
||||
onClick={() => onSelectMoment(index)}
|
||||
>
|
||||
<Zone className="w-8" />
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
{eventTimeline.map((item, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
className="rounded-full"
|
||||
type="iconOnly"
|
||||
color={index == timeIndex ? 'blue' : 'gray'}
|
||||
aria-label={window.innerWidth > 640 ? getTimelineItemDescription(config, item, event) : ''}
|
||||
onClick={() => onSelectMoment(index)}
|
||||
>
|
||||
{getTimelineIcon(item.class_type)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{timeIndex >= 0 ? (
|
||||
@@ -124,26 +113,54 @@ export default function TimelineSummary({ event, onFrameSelected }) {
|
||||
);
|
||||
}
|
||||
|
||||
function getTimelineItemDescription(config, timelineItem, event) {
|
||||
if (timelineItem.class_type == 'visible') {
|
||||
return `${event.label} detected at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
} else if (timelineItem.class_type == 'entered_zone') {
|
||||
return `${event.label.replaceAll('_', ' ')} entered ${timelineItem.data.zones
|
||||
.join(' and ')
|
||||
.replaceAll('_', ' ')} at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
function getTimelineIcon(classType) {
|
||||
switch (classType) {
|
||||
case 'visible':
|
||||
return <PlayIcon className="w-8" />;
|
||||
case 'gone':
|
||||
return <ExitIcon className="w-8" />;
|
||||
case 'active':
|
||||
return <ActiveObjectIcon className="w-8" />;
|
||||
case 'stationary':
|
||||
return <StationaryObjectIcon className="w-8" />;
|
||||
case 'entered_zone':
|
||||
return <Zone className="w-8" />;
|
||||
}
|
||||
}
|
||||
|
||||
function getTimelineItemDescription(config, timelineItem, event) {
|
||||
switch (timelineItem.class_type) {
|
||||
case 'visible':
|
||||
return `${event.label} detected at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
case 'entered_zone':
|
||||
return `${event.label.replaceAll('_', ' ')} entered ${timelineItem.data.zones
|
||||
.join(' and ')
|
||||
.replaceAll('_', ' ')} at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
case 'active':
|
||||
return `${event.label} became active at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
case 'stationary':
|
||||
return `${event.label} became stationary at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
case 'gone':
|
||||
return `${event.label} left at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
}
|
||||
|
||||
return `${event.label} left at ${formatUnixTimestampToDateTime(timelineItem.timestamp, {
|
||||
date_style: 'short',
|
||||
time_style: 'medium',
|
||||
time_format: config.ui.time_format,
|
||||
})}`;
|
||||
}
|
||||
|
||||
15
web/src/icons/ActiveObject.jsx
Normal file
15
web/src/icons/ActiveObject.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { h } from 'preact';
|
||||
import { memo } from 'preact/compat';
|
||||
|
||||
export function ActiveObject({ className = '' }) {
|
||||
return (
|
||||
<svg className={`fill-current ${className}`} viewBox="0 -960 960 960">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M360-80q-58 0-109-22t-89-60q-38-38-60-89T80-360q0-81 42-148t110-102q20-39 49.5-68.5T350-728q33-68 101-110t149-42q58 0 109 22t89 60q38 38 60 89t22 109q0 85-42 150T728-350q-20 39-49.5 68.5T610-232q-35 68-102 110T360-80Zm0-80q33 0 63.5-10t56.5-30q-58 0-109-22t-89-60q-38-38-60-89t-22-109q-20 26-30 56.5T160-360q0 42 16 78t43 63q27 27 63 43t78 16Zm120-120q33 0 64.5-10t57.5-30q-59 0-110-22.5T403-403q-38-38-60.5-89T320-602q-20 26-30 57.5T280-480q0 42 15.5 78t43.5 63q27 28 63 43.5t78 15.5Zm120-120q18 0 34.5-3t33.5-9q22-60 6.5-115.5T621-621q-38-38-93.5-53.5T412-668q-6 17-9 33.5t-3 34.5q0 42 15.5 78t43.5 63q27 28 63 43.5t78 15.5Zm160-78q20-26 30-57.5t10-64.5q0-42-15.5-78T741-741q-27-28-63-43.5T600-800q-35 0-65.5 10T478-760q59 0 110 22.5t89 60.5q38 38 60.5 89T760-478Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(ActiveObject);
|
||||
15
web/src/icons/StationaryObject.jsx
Normal file
15
web/src/icons/StationaryObject.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { h } from 'preact';
|
||||
import { memo } from 'preact/compat';
|
||||
|
||||
export function StationaryObject({ className = '' }) {
|
||||
return (
|
||||
<svg className={`fill-current ${className}`} viewBox="0 -960 960 960">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(StationaryObject);
|
||||
Reference in New Issue
Block a user