mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-23 15:24:31 +03:00
tweaks
This commit is contained in:
parent
83354be7f6
commit
2827561238
@ -5,7 +5,10 @@ import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
|
|||||||
import { useDetailStream } from "@/context/detail-stream-context";
|
import { useDetailStream } from "@/context/detail-stream-context";
|
||||||
import scrollIntoView from "scroll-into-view-if-needed";
|
import scrollIntoView from "scroll-into-view-if-needed";
|
||||||
import useUserInteraction from "@/hooks/use-user-interaction";
|
import useUserInteraction from "@/hooks/use-user-interaction";
|
||||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
import {
|
||||||
|
formatUnixTimestampToDateTime,
|
||||||
|
formatSecondsToDuration,
|
||||||
|
} from "@/utils/dateUtil";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import AnnotationOffsetSlider from "@/components/overlay/detail/AnnotationOffsetSlider";
|
import AnnotationOffsetSlider from "@/components/overlay/detail/AnnotationOffsetSlider";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
@ -13,7 +16,7 @@ import useSWR from "swr";
|
|||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
import { Event } from "@/types/event";
|
import { Event } from "@/types/event";
|
||||||
import { getIconForLabel } from "@/utils/iconUtil";
|
import { getIconForLabel } from "@/utils/iconUtil";
|
||||||
import { ReviewSegment, REVIEW_PADDING } from "@/types/review";
|
import { ReviewSegment } from "@/types/review";
|
||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
@ -49,7 +52,6 @@ export default function DetailStream({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const effectiveTime = currentTime + annotationOffset / 1000;
|
const effectiveTime = currentTime + annotationOffset / 1000;
|
||||||
const PAD = 0; // REVIEW_PADDING ?? 2;
|
|
||||||
const [upload, setUpload] = useState<Event | undefined>(undefined);
|
const [upload, setUpload] = useState<Event | undefined>(undefined);
|
||||||
|
|
||||||
// Ensure we initialize the active review when reviewItems first arrive.
|
// Ensure we initialize the active review when reviewItems first arrive.
|
||||||
@ -64,8 +66,8 @@ export default function DetailStream({
|
|||||||
let closest: { r: ReviewSegment; diff: number } | undefined;
|
let closest: { r: ReviewSegment; diff: number } | undefined;
|
||||||
|
|
||||||
for (const r of reviewItems) {
|
for (const r of reviewItems) {
|
||||||
const start = (r.start_time ?? 0) - PAD;
|
const start = r.start_time ?? 0;
|
||||||
const end = (r.end_time ?? r.start_time ?? start) + PAD;
|
const end = r.end_time ?? r.start_time ?? start;
|
||||||
if (effectiveTime >= start && effectiveTime <= end) {
|
if (effectiveTime >= start && effectiveTime <= end) {
|
||||||
target = r;
|
target = r;
|
||||||
break;
|
break;
|
||||||
@ -78,12 +80,12 @@ export default function DetailStream({
|
|||||||
if (!target && closest) target = closest.r;
|
if (!target && closest) target = closest.r;
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
const start = (target.start_time ?? 0) - PAD;
|
const start = target.start_time ?? 0;
|
||||||
setActiveReviewId(
|
setActiveReviewId(
|
||||||
`review-${target.id ?? target.start_time ?? Math.floor(start)}`,
|
`review-${target.id ?? target.start_time ?? Math.floor(start)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [reviewItems, activeReviewId, effectiveTime, PAD]);
|
}, [reviewItems, activeReviewId, effectiveTime]);
|
||||||
|
|
||||||
// Auto-scroll to current time
|
// Auto-scroll to current time
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -99,8 +101,8 @@ export default function DetailStream({
|
|||||||
let closest: { r: ReviewSegment; diff: number } | undefined;
|
let closest: { r: ReviewSegment; diff: number } | undefined;
|
||||||
|
|
||||||
for (const r of items) {
|
for (const r of items) {
|
||||||
const start = (r.start_time ?? 0) - PAD;
|
const start = r.start_time ?? 0;
|
||||||
const end = (r.end_time ?? r.start_time ?? start) + PAD;
|
const end = r.end_time ?? r.start_time ?? start;
|
||||||
if (effectiveTime >= start && effectiveTime <= end) {
|
if (effectiveTime >= start && effectiveTime <= end) {
|
||||||
target = r;
|
target = r;
|
||||||
break;
|
break;
|
||||||
@ -113,7 +115,7 @@ export default function DetailStream({
|
|||||||
if (!target && closest) target = closest.r;
|
if (!target && closest) target = closest.r;
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
const start = (target.start_time ?? 0) - PAD;
|
const start = target.start_time ?? 0;
|
||||||
const id = `review-${target.id ?? target.start_time ?? Math.floor(start)}`;
|
const id = `review-${target.id ?? target.start_time ?? Math.floor(start)}`;
|
||||||
const element = scrollRef.current.querySelector(
|
const element = scrollRef.current.querySelector(
|
||||||
`[data-review-id="${id}"]`,
|
`[data-review-id="${id}"]`,
|
||||||
@ -132,15 +134,14 @@ export default function DetailStream({
|
|||||||
annotationOffset,
|
annotationOffset,
|
||||||
userInteracting,
|
userInteracting,
|
||||||
setProgrammaticScroll,
|
setProgrammaticScroll,
|
||||||
PAD,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Auto-select active review based on effectiveTime (if inside a review range)
|
// Auto-select active review based on effectiveTime (if inside a review range)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!reviewItems || reviewItems.length === 0) return;
|
if (!reviewItems || reviewItems.length === 0) return;
|
||||||
for (const r of reviewItems) {
|
for (const r of reviewItems) {
|
||||||
const start = (r.start_time ?? 0) - PAD;
|
const start = r.start_time ?? 0;
|
||||||
const end = (r.end_time ?? r.start_time ?? start) + PAD;
|
const end = r.end_time ?? r.start_time ?? start;
|
||||||
if (effectiveTime >= start && effectiveTime <= end) {
|
if (effectiveTime >= start && effectiveTime <= end) {
|
||||||
setActiveReviewId(
|
setActiveReviewId(
|
||||||
`review-${r.id ?? r.start_time ?? Math.floor(start)}`,
|
`review-${r.id ?? r.start_time ?? Math.floor(start)}`,
|
||||||
@ -148,7 +149,7 @@ export default function DetailStream({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [effectiveTime, reviewItems, PAD]);
|
}, [effectiveTime, reviewItems]);
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return <ActivityIndicator />;
|
return <ActivityIndicator />;
|
||||||
@ -173,7 +174,7 @@ export default function DetailStream({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
reviewItems?.map((review: ReviewSegment) => {
|
reviewItems?.map((review: ReviewSegment) => {
|
||||||
const id = `review-${review.id ?? review.start_time ?? Math.floor((review.start_time ?? 0) - PAD)}`;
|
const id = `review-${review.id ?? review.start_time ?? Math.floor(review.start_time ?? 0)}`;
|
||||||
return (
|
return (
|
||||||
<ReviewGroup
|
<ReviewGroup
|
||||||
key={id}
|
key={id}
|
||||||
@ -219,18 +220,14 @@ function ReviewGroup({
|
|||||||
effectiveTime,
|
effectiveTime,
|
||||||
}: ReviewGroupProps) {
|
}: ReviewGroupProps) {
|
||||||
const { t } = useTranslation("views/events");
|
const { t } = useTranslation("views/events");
|
||||||
const PAD = REVIEW_PADDING ?? 2;
|
const start = review.start_time ?? 0;
|
||||||
|
|
||||||
// derive start timestamp from the review
|
|
||||||
const start = (review.start_time ?? 0) - PAD;
|
|
||||||
|
|
||||||
// display time first in the header
|
|
||||||
const displayTime = formatUnixTimestampToDateTime(start, {
|
const displayTime = formatUnixTimestampToDateTime(start, {
|
||||||
timezone: config.ui.timezone,
|
timezone: config.ui.timezone,
|
||||||
date_format:
|
date_format:
|
||||||
config.ui.time_format == "24hour"
|
config.ui.time_format == "24hour"
|
||||||
? t("time.formattedTimestamp.24hour", { ns: "common" })
|
? t("time.formattedTimestampHourMinuteSecond.24hour", { ns: "common" })
|
||||||
: t("time.formattedTimestamp.12hour", { ns: "common" }),
|
: t("time.formattedTimestampHourMinuteSecond.12hour", { ns: "common" }),
|
||||||
time_style: "medium",
|
time_style: "medium",
|
||||||
date_style: "medium",
|
date_style: "medium",
|
||||||
});
|
});
|
||||||
@ -268,6 +265,13 @@ function ReviewGroup({
|
|||||||
}
|
}
|
||||||
}, [review, t, fetchedEvents]);
|
}, [review, t, fetchedEvents]);
|
||||||
|
|
||||||
|
const reviewDuration =
|
||||||
|
review.end_time != null
|
||||||
|
? formatSecondsToDuration(
|
||||||
|
Math.max(0, Math.floor((review.end_time ?? 0) - start)),
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-review-id={id}
|
data-review-id={id}
|
||||||
@ -287,6 +291,11 @@ function ReviewGroup({
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-medium">{displayTime}</div>
|
<div className="text-sm font-medium">{displayTime}</div>
|
||||||
|
{reviewDuration && (
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
{reviewDuration}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="text-xs text-muted-foreground">{reviewInfo}</div>
|
<div className="text-xs text-muted-foreground">{reviewInfo}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -398,7 +407,7 @@ function EventCollapsible({
|
|||||||
event.id != selectedObjectId &&
|
event.id != selectedObjectId &&
|
||||||
(effectiveTime ?? 0) >= (event.start_time ?? 0) &&
|
(effectiveTime ?? 0) >= (event.start_time ?? 0) &&
|
||||||
(effectiveTime ?? 0) <= (event.end_time ?? event.start_time ?? 0) &&
|
(effectiveTime ?? 0) <= (event.end_time ?? event.start_time ?? 0) &&
|
||||||
"bg-secondary outline-[1px] -outline-offset-[0.8px] outline-primary/40",
|
"bg-secondary-highlight outline-[1.5px] -outline-offset-[1.1px] outline-primary/40",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-between">
|
<div className="flex w-full items-center justify-between">
|
||||||
|
|||||||
@ -36,8 +36,8 @@
|
|||||||
--secondary-foreground: hsl(222.2, 17.4%, 36.2%);
|
--secondary-foreground: hsl(222.2, 17.4%, 36.2%);
|
||||||
--secondary-foreground: 222.2 17.4% 36.2%;
|
--secondary-foreground: 222.2 17.4% 36.2%;
|
||||||
|
|
||||||
--secondary-highlight: hsl(0, 0%, 94%);
|
--secondary-highlight: hsl(210, 17.4%, 94%);
|
||||||
--secondary-highlight: 0 0% 94%;
|
--secondary-highlight: 210 17.4% 94%;
|
||||||
|
|
||||||
--neutral: hsl(0, 0%, 45.1%);
|
--neutral: hsl(0, 0%, 45.1%);
|
||||||
--neutral: 0 0% 45.1%;
|
--neutral: 0 0% 45.1%;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user