mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-17 02:26:43 +03:00
chore: add ObjectTrack zone friendly name support
This commit is contained in:
parent
450623e927
commit
17e89da6ca
@ -11,6 +11,7 @@ import {
|
|||||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { resolveZoneName } from "@/hooks/use-zone-friendly-name";
|
||||||
|
|
||||||
type ObjectTrackOverlayProps = {
|
type ObjectTrackOverlayProps = {
|
||||||
camera: string;
|
camera: string;
|
||||||
@ -127,6 +128,9 @@ export default function ObjectTrackOverlay({
|
|||||||
?.filter((event) => event.data.box !== undefined)
|
?.filter((event) => event.data.box !== undefined)
|
||||||
.map((event) => {
|
.map((event) => {
|
||||||
const [left, top, width, height] = event.data.box!;
|
const [left, top, width, height] = event.data.box!;
|
||||||
|
event.data.zones_friendly_names = event?.data?.zones?.map((zone) => {
|
||||||
|
return resolveZoneName(config, zone);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: left + width / 2, // Center x
|
x: left + width / 2, // Center x
|
||||||
@ -136,7 +140,7 @@ export default function ObjectTrackOverlay({
|
|||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
);
|
);
|
||||||
}, [objectTimeline]);
|
}, [config, objectTimeline]);
|
||||||
|
|
||||||
// final object path with timeline points included
|
// final object path with timeline points included
|
||||||
const pathPoints = useMemo(() => {
|
const pathPoints = useMemo(() => {
|
||||||
|
|||||||
@ -7,7 +7,10 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||||
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
|
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 = {
|
type ObjectPathProps = {
|
||||||
positions?: Position[];
|
positions?: Position[];
|
||||||
@ -42,16 +45,25 @@ export function ObjectPath({
|
|||||||
visible = true,
|
visible = true,
|
||||||
}: ObjectPathProps) {
|
}: ObjectPathProps) {
|
||||||
const { t } = useTranslation(["views/explore"]);
|
const { t } = useTranslation(["views/explore"]);
|
||||||
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
const getAbsolutePositions = useCallback(() => {
|
const getAbsolutePositions = useCallback(() => {
|
||||||
if (!imgRef.current || !positions) return [];
|
if (!imgRef.current || !positions) return [];
|
||||||
const imgRect = imgRef.current.getBoundingClientRect();
|
const imgRect = imgRef.current.getBoundingClientRect();
|
||||||
return positions.map((pos) => ({
|
return positions.map((pos) => {
|
||||||
x: pos.x * imgRect.width,
|
if (config && pos.lifecycle_item) {
|
||||||
y: pos.y * imgRect.height,
|
pos.lifecycle_item.data.zones_friendly_names =
|
||||||
timestamp: pos.timestamp,
|
pos.lifecycle_item?.data.zones.map((zone) => {
|
||||||
lifecycle_item: pos.lifecycle_item,
|
return resolveZoneName(config, zone);
|
||||||
}));
|
});
|
||||||
}, [positions, imgRef]);
|
}
|
||||||
|
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[]) => {
|
const generateStraightPath = useCallback((points: Position[]) => {
|
||||||
if (!points || points.length < 2) return "";
|
if (!points || points.length < 2) return "";
|
||||||
@ -103,9 +115,11 @@ export function ObjectPath({
|
|||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipPortal>
|
<TooltipPortal>
|
||||||
<TooltipContent side="top" className="smart-capitalize">
|
<TooltipContent side="top" className="smart-capitalize">
|
||||||
{pos.lifecycle_item
|
<Trans>
|
||||||
? getLifecycleItemDescription(pos.lifecycle_item)
|
{pos.lifecycle_item
|
||||||
: t("objectLifecycle.trackedPoint")}
|
? getLifecycleItemDescription(pos.lifecycle_item)
|
||||||
|
: t("objectLifecycle.trackedPoint")}
|
||||||
|
</Trans>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</TooltipPortal>
|
</TooltipPortal>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
formatUnixTimestampToDateTime,
|
formatUnixTimestampToDateTime,
|
||||||
formatSecondsToDuration,
|
formatSecondsToDuration,
|
||||||
} from "@/utils/dateUtil";
|
} from "@/utils/dateUtil";
|
||||||
import { useTranslation } from "react-i18next";
|
import { Trans, 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";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -27,6 +27,7 @@ import { getTranslatedLabel } from "@/utils/i18n";
|
|||||||
import EventMenu from "@/components/timeline/EventMenu";
|
import EventMenu from "@/components/timeline/EventMenu";
|
||||||
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
|
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { resolveZoneName } from "@/hooks/use-zone-friendly-name";
|
||||||
|
|
||||||
type DetailStreamProps = {
|
type DetailStreamProps = {
|
||||||
reviewItems?: ReviewSegment[];
|
reviewItems?: ReviewSegment[];
|
||||||
@ -503,6 +504,10 @@ function LifecycleItem({ event, isActive, onSeek }: LifecycleItemProps) {
|
|||||||
const { t } = useTranslation("views/events");
|
const { t } = useTranslation("views/events");
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
|
event.data.zones_friendly_names = event?.data?.zones?.map((zone) => {
|
||||||
|
return resolveZoneName(config, zone);
|
||||||
|
});
|
||||||
|
|
||||||
const formattedEventTimestamp = config
|
const formattedEventTimestamp = config
|
||||||
? formatUnixTimestampToDateTime(event.timestamp ?? 0, {
|
? formatUnixTimestampToDateTime(event.timestamp ?? 0, {
|
||||||
timezone: config.ui.timezone,
|
timezone: config.ui.timezone,
|
||||||
@ -536,7 +541,9 @@ function LifecycleItem({ event, isActive, onSeek }: LifecycleItemProps) {
|
|||||||
<LifecycleIcon lifecycleItem={event} className="size-3" />
|
<LifecycleIcon lifecycleItem={event} className="size-3" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-row justify-between">
|
<div className="flex w-full flex-row justify-between">
|
||||||
<div>{getLifecycleItemDescription(event)}</div>
|
<Trans>
|
||||||
|
<div>{getLifecycleItemDescription(event)}</div>
|
||||||
|
</Trans>
|
||||||
<div className={cn("p-1 text-xs")}>{formattedEventTimestamp}</div>
|
<div className={cn("p-1 text-xs")}>{formattedEventTimestamp}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user