mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-27 01:38:58 +03:00
Add zones friend name (#20761)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* feat: add zones friendly name * fix: fix the issue where the input field was empty when there was no friendly_name * chore: fix the issue where the friendly name would replace spaces with underscores * docs: update zones docs * Update web/src/components/settings/ZoneEditPane.tsx Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> * Add friendly_name option for zone configuration Added optional friendly name for zones in configuration. * fix: fix the logical error in the null/empty check for the polygons parameter * fix: remove the toast name for zones will use the friendly_name instead * docs: remove emoji tips * revert: revert zones doc ui tips * Update docs/docs/configuration/zones.md Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> * Update docs/docs/configuration/zones.md Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> * Update docs/docs/configuration/zones.md Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> * feat: add friendly zone names to tracking details and lifecycle item descriptions * chore: lint fix * refactor: add friendly zone names to timeline entries and clean up unused code * refactor: add formatList --------- Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
This commit is contained in:
co-authored by
Josh Hawkins
parent
530b69b877
commit
ef19332fe5
@@ -25,7 +25,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import { CameraNameLabel } from "../camera/CameraNameLabel";
|
||||
import { CameraNameLabel } from "../camera/FriendlyNameLabel";
|
||||
import { isDesktop, isMobile } from "react-device-detect";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
|
||||
import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
|
||||
|
||||
type EditRoleCamerasOverlayProps = {
|
||||
show: boolean;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button } from "../ui/button";
|
||||
import { FaVideo } from "react-icons/fa";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CameraNameLabel } from "../camera/CameraNameLabel";
|
||||
import { CameraNameLabel } from "../camera/FriendlyNameLabel";
|
||||
|
||||
type MobileCameraDrawerProps = {
|
||||
allCameras: string[];
|
||||
|
||||
@@ -12,6 +12,7 @@ import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Event } from "@/types/event";
|
||||
import { resolveZoneName } from "@/hooks/use-zone-friendly-name";
|
||||
|
||||
// Use a small tolerance (10ms) for browsers with seek precision by-design issues
|
||||
const TOLERANCE = 0.01;
|
||||
@@ -114,6 +115,10 @@ export default function ObjectTrackOverlay({
|
||||
{ revalidateOnFocus: false },
|
||||
);
|
||||
|
||||
const getZonesFriendlyNames = (zones: string[], config: FrigateConfig) => {
|
||||
return zones?.map((zone) => resolveZoneName(config, zone)) ?? [];
|
||||
};
|
||||
|
||||
const timelineResults = useMemo(() => {
|
||||
// Group timeline entries by source_id
|
||||
if (!timelineData) return selectedObjectIds.map(() => []);
|
||||
@@ -127,8 +132,19 @@ export default function ObjectTrackOverlay({
|
||||
}
|
||||
|
||||
// Return timeline arrays in the same order as selectedObjectIds
|
||||
return selectedObjectIds.map((id) => grouped[id] || []);
|
||||
}, [selectedObjectIds, timelineData]);
|
||||
return selectedObjectIds.map((id) => {
|
||||
const entries = grouped[id] || [];
|
||||
return entries.map((event) => ({
|
||||
...event,
|
||||
data: {
|
||||
...event.data,
|
||||
zones_friendly_names: config
|
||||
? getZonesFriendlyNames(event.data?.zones, config)
|
||||
: [],
|
||||
},
|
||||
}));
|
||||
});
|
||||
}, [selectedObjectIds, timelineData, config]);
|
||||
|
||||
const typeColorMap = useMemo(
|
||||
() => ({
|
||||
|
||||
@@ -8,6 +8,9 @@ import {
|
||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
|
||||
import { 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,31 @@ export function ObjectPath({
|
||||
visible = true,
|
||||
}: ObjectPathProps) {
|
||||
const { t } = useTranslation(["views/explore"]);
|
||||
const { data: config } = useSWR<FrigateConfig>("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) => {
|
||||
return {
|
||||
x: pos.x * imgRect.width,
|
||||
y: pos.y * imgRect.height,
|
||||
timestamp: pos.timestamp,
|
||||
lifecycle_item: pos.lifecycle_item?.data?.zones
|
||||
? {
|
||||
...pos.lifecycle_item,
|
||||
data: {
|
||||
...pos.lifecycle_item?.data,
|
||||
zones_friendly_names: pos.lifecycle_item?.data.zones.map(
|
||||
(zone) => {
|
||||
return resolveZoneName(config, zone);
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
: pos.lifecycle_item,
|
||||
};
|
||||
});
|
||||
}, [imgRef, positions, config]);
|
||||
|
||||
const generateStraightPath = useCallback((points: Position[]) => {
|
||||
if (!points || points.length < 2) return "";
|
||||
|
||||
@@ -80,7 +80,7 @@ import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||
import { getTranslatedLabel } from "@/utils/i18n";
|
||||
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
|
||||
import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
|
||||
import { DialogPortal } from "@radix-ui/react-dialog";
|
||||
import { useDetailStream } from "@/context/detail-stream-context";
|
||||
import { PiSlidersHorizontalBold } from "react-icons/pi";
|
||||
|
||||
@@ -23,6 +23,7 @@ import { Link, useNavigate } from "react-router-dom";
|
||||
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getTranslatedLabel } from "@/utils/i18n";
|
||||
import { resolveZoneName } from "@/hooks/use-zone-friendly-name";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { HiDotsHorizontal } from "react-icons/hi";
|
||||
import axios from "axios";
|
||||
@@ -73,6 +74,12 @@ export function TrackingDetails({
|
||||
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
eventSequence?.map((event) => {
|
||||
event.data.zones_friendly_names = event.data?.zones?.map((zone) => {
|
||||
return resolveZoneName(config, zone);
|
||||
});
|
||||
});
|
||||
|
||||
// Use manualOverride (set when seeking in image mode) if present so
|
||||
// lifecycle rows and overlays follow image-mode seeks. Otherwise fall
|
||||
// back to currentTime used for video mode.
|
||||
@@ -713,7 +720,8 @@ function LifecycleIconRow({
|
||||
}}
|
||||
/>
|
||||
<span className="smart-capitalize">
|
||||
{zone.replaceAll("_", " ")}
|
||||
{item.data?.zones_friendly_names?.[zidx] ??
|
||||
zone.replaceAll("_", " ")}
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
|
||||
@@ -430,7 +430,8 @@ export function ZoneFilterContent({
|
||||
{allZones.map((item) => (
|
||||
<FilterSwitch
|
||||
key={item}
|
||||
label={item.replaceAll("_", " ")}
|
||||
label={item}
|
||||
type={"zone"}
|
||||
isChecked={zones?.includes(item) ?? false}
|
||||
onCheckedChange={(isChecked) => {
|
||||
if (isChecked) {
|
||||
|
||||
Reference in New Issue
Block a user