From 17e89da6caa6aa6b014358ce04ea5077ecb619b7 Mon Sep 17 00:00:00 2001 From: ZhaiSoul <842607283@qq.com> Date: Thu, 23 Oct 2025 13:57:32 +0000 Subject: [PATCH] chore: add ObjectTrack zone friendly name support --- .../components/overlay/ObjectTrackOverlay.tsx | 6 +++- .../components/overlay/detail/ObjectPath.tsx | 36 +++++++++++++------ web/src/components/timeline/DetailStream.tsx | 11 ++++-- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/web/src/components/overlay/ObjectTrackOverlay.tsx b/web/src/components/overlay/ObjectTrackOverlay.tsx index 2bd355306..cb1717355 100644 --- a/web/src/components/overlay/ObjectTrackOverlay.tsx +++ b/web/src/components/overlay/ObjectTrackOverlay.tsx @@ -11,6 +11,7 @@ import { import { TooltipPortal } from "@radix-ui/react-tooltip"; import { cn } from "@/lib/utils"; import { useTranslation } from "react-i18next"; +import { resolveZoneName } from "@/hooks/use-zone-friendly-name"; type ObjectTrackOverlayProps = { camera: string; @@ -127,6 +128,9 @@ export default function ObjectTrackOverlay({ ?.filter((event) => event.data.box !== undefined) .map((event) => { const [left, top, width, height] = event.data.box!; + event.data.zones_friendly_names = event?.data?.zones?.map((zone) => { + return resolveZoneName(config, zone); + }); return { x: left + width / 2, // Center x @@ -136,7 +140,7 @@ export default function ObjectTrackOverlay({ }; }) || [] ); - }, [objectTimeline]); + }, [config, objectTimeline]); // final object path with timeline points included const pathPoints = useMemo(() => { diff --git a/web/src/components/overlay/detail/ObjectPath.tsx b/web/src/components/overlay/detail/ObjectPath.tsx index 0101a71f1..a4a0b36c0 100644 --- a/web/src/components/overlay/detail/ObjectPath.tsx +++ b/web/src/components/overlay/detail/ObjectPath.tsx @@ -7,7 +7,10 @@ import { } from "@/components/ui/tooltip"; import { TooltipPortal } from "@radix-ui/react-tooltip"; import { getLifecycleItemDescription } from "@/utils/lifecycleUtil"; -import { useTranslation } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; +import { resolveZoneName } from "@/hooks/use-zone-friendly-name"; +import { FrigateConfig } from "@/types/frigateConfig"; +import useSWR from "swr"; type ObjectPathProps = { positions?: Position[]; @@ -42,16 +45,25 @@ export function ObjectPath({ visible = true, }: ObjectPathProps) { const { t } = useTranslation(["views/explore"]); + const { data: config } = useSWR("config"); const getAbsolutePositions = useCallback(() => { if (!imgRef.current || !positions) return []; const imgRect = imgRef.current.getBoundingClientRect(); - return positions.map((pos) => ({ - x: pos.x * imgRect.width, - y: pos.y * imgRect.height, - timestamp: pos.timestamp, - lifecycle_item: pos.lifecycle_item, - })); - }, [positions, imgRef]); + return positions.map((pos) => { + if (config && pos.lifecycle_item) { + pos.lifecycle_item.data.zones_friendly_names = + pos.lifecycle_item?.data.zones.map((zone) => { + return resolveZoneName(config, zone); + }); + } + return { + x: pos.x * imgRect.width, + y: pos.y * imgRect.height, + timestamp: pos.timestamp, + lifecycle_item: pos.lifecycle_item, + }; + }); + }, [imgRef, positions, config]); const generateStraightPath = useCallback((points: Position[]) => { if (!points || points.length < 2) return ""; @@ -103,9 +115,11 @@ export function ObjectPath({ - {pos.lifecycle_item - ? getLifecycleItemDescription(pos.lifecycle_item) - : t("objectLifecycle.trackedPoint")} + + {pos.lifecycle_item + ? getLifecycleItemDescription(pos.lifecycle_item) + : t("objectLifecycle.trackedPoint")} + diff --git a/web/src/components/timeline/DetailStream.tsx b/web/src/components/timeline/DetailStream.tsx index 9e9dae904..d1675f3f8 100644 --- a/web/src/components/timeline/DetailStream.tsx +++ b/web/src/components/timeline/DetailStream.tsx @@ -9,7 +9,7 @@ import { formatUnixTimestampToDateTime, formatSecondsToDuration, } from "@/utils/dateUtil"; -import { useTranslation } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import AnnotationOffsetSlider from "@/components/overlay/detail/AnnotationOffsetSlider"; import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; @@ -27,6 +27,7 @@ import { getTranslatedLabel } from "@/utils/i18n"; import EventMenu from "@/components/timeline/EventMenu"; import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog"; import { cn } from "@/lib/utils"; +import { resolveZoneName } from "@/hooks/use-zone-friendly-name"; type DetailStreamProps = { reviewItems?: ReviewSegment[]; @@ -503,6 +504,10 @@ function LifecycleItem({ event, isActive, onSeek }: LifecycleItemProps) { const { t } = useTranslation("views/events"); const { data: config } = useSWR("config"); + event.data.zones_friendly_names = event?.data?.zones?.map((zone) => { + return resolveZoneName(config, zone); + }); + const formattedEventTimestamp = config ? formatUnixTimestampToDateTime(event.timestamp ?? 0, { timezone: config.ui.timezone, @@ -536,7 +541,9 @@ function LifecycleItem({ event, isActive, onSeek }: LifecycleItemProps) {
-
{getLifecycleItemDescription(event)}
+ +
{getLifecycleItemDescription(event)}
+
{formattedEventTimestamp}