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

View File

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

View File

@ -1,6 +1,6 @@
import { ObjectLifecycleSequence } from "@/types/timeline"; import { ObjectLifecycleSequence } from "@/types/timeline";
import { t } from "i18next"; import { t } from "i18next";
import { getTranslatedLabel } from "./i18n"; import i18n, { getTranslatedLabel } from "./i18n";
export function getLifecycleItemDescription( export function getLifecycleItemDescription(
lifecycleItem: ObjectLifecycleSequence, lifecycleItem: ObjectLifecycleSequence,
@ -11,6 +11,17 @@ export function getLifecycleItemDescription(
const label = getTranslatedLabel(rawLabel); 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) { switch (lifecycleItem.class_type) {
case "visible": case "visible":
return t("objectLifecycle.lifecycleItemDesc.visible", { return t("objectLifecycle.lifecycleItemDesc.visible", {
@ -21,7 +32,18 @@ export function getLifecycleItemDescription(
return t("objectLifecycle.lifecycleItemDesc.entered_zone", { return t("objectLifecycle.lifecycleItemDesc.entered_zone", {
ns: "views/explore", ns: "views/explore",
label, 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": case "active":
return t("objectLifecycle.lifecycleItemDesc.active", { return t("objectLifecycle.lifecycleItemDesc.active", {

View File

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