chore: fix objectLifecycle zones friendly name support and use Intl.ListFormat

This commit is contained in:
ZhaiSoul 2025-10-23 13:23:28 +00:00
parent 23aba70819
commit 6a72a041d6
4 changed files with 42 additions and 9 deletions

View File

@ -45,8 +45,9 @@ import { useNavigate } from "react-router-dom";
import { ObjectPath } from "./ObjectPath";
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
import { IoPlayCircleOutline } from "react-icons/io5";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import { getTranslatedLabel } from "@/utils/i18n";
import { resolveZoneName } from "@/hooks/use-zone-friendly-name";
type ObjectLifecycleProps = {
className?: string;
@ -62,7 +63,7 @@ export default function ObjectLifecycle({
setPane,
}: ObjectLifecycleProps) {
const { t } = useTranslation(["views/explore"]);
const { data: config } = useSWR<FrigateConfig>("config");
const { data: eventSequence } = useSWR<ObjectLifecycleSequence[]>([
"timeline",
{
@ -70,7 +71,12 @@ export default function ObjectLifecycle({
},
]);
const { data: config } = useSWR<FrigateConfig>("config");
eventSequence?.map((event) => {
event.data.zones_friendly_names = event.data?.zones?.map((zone) => {
return resolveZoneName(config, zone);
});
});
const apiHost = useApiHost();
const navigate = useNavigate();
@ -673,7 +679,9 @@ export default function ObjectLifecycle({
/>
</div>
<div className="flex w-full flex-row justify-between">
<div>{getLifecycleItemDescription(item)}</div>
<Trans>
<div>{getLifecycleItemDescription(item)}</div>
</Trans>
<div className={cn("p-1 text-sm")}>
{formattedEventTimestamp}
</div>
@ -731,7 +739,9 @@ export default function ObjectLifecycle({
}}
/>
<span className="smart-capitalize">
{zone.replaceAll("_", " ")}
{item.data?.zones_friendly_names?.[
zidx
] ?? zone.replaceAll("_", " ")}
</span>
</div>
))}

View File

@ -22,6 +22,7 @@ export type ObjectLifecycleSequence = {
attribute: string;
attribute_box?: [number, number, number, number];
zones: string[];
zones_friendly_names?: string[];
};
class_type: LifecycleClassType;
source_id: string;

View File

@ -1,6 +1,6 @@
import { ObjectLifecycleSequence } from "@/types/timeline";
import { t } from "i18next";
import { getTranslatedLabel } from "./i18n";
import i18n, { getTranslatedLabel } from "./i18n";
export function getLifecycleItemDescription(
lifecycleItem: ObjectLifecycleSequence,
@ -11,6 +11,17 @@ export function getLifecycleItemDescription(
const label = getTranslatedLabel(rawLabel);
let supportedListFormat = false;
let zonesFormatter: Intl.ListFormat | null = null;
if (typeof Intl !== "undefined" && Intl.ListFormat) {
supportedListFormat = true;
zonesFormatter = new Intl.ListFormat(i18n.language, {
style: "long",
type: "conjunction",
});
}
switch (lifecycleItem.class_type) {
case "visible":
return t("objectLifecycle.lifecycleItemDesc.visible", {
@ -21,7 +32,18 @@ export function getLifecycleItemDescription(
return t("objectLifecycle.lifecycleItemDesc.entered_zone", {
ns: "views/explore",
label,
zones: lifecycleItem.data.zones.join(" and ").replaceAll("_", " "),
zones:
supportedListFormat && zonesFormatter
? zonesFormatter.format(
lifecycleItem.data.zones_friendly_names?.map(
(x) => `<strong>${x}</strong>`,
) ??
lifecycleItem.data.zones.map((x) => `<strong>${x}</strong>`),
)
: (
lifecycleItem.data.zones_friendly_names ??
lifecycleItem.data.zones
).join(" and "),
});
case "active":
return t("objectLifecycle.lifecycleItemDesc.active", {

View File

@ -1,8 +1,8 @@
{
"compilerOptions": {
"target": "ES2020",
"target": "ES2021",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable", "ES2021.String"],
"lib": ["ES2021", "DOM", "DOM.Iterable", "ES2021.String"],
"module": "ESNext",
"skipLibCheck": true,
"baseUrl": ".",