feat: Add camera nickname (#19567)

* refactor: Refactor camera nickname

* fix: fix cameraNameLabel visually

* chore: The Explore search function also displays the Camera's nickname in English

* chore: add mobile page camera nickname

* feat: webpush support camera nickname

* fix: fix storage camera name is null

* chore: fix review detail and context menu camera nickname

* chore: fix use-stats and notification setting camera nickname

* fix: fix stats camera if not nickname need capitalize

* fix: fix debug page open camera web ui i18n and camera nickname support

* fix: fix camera metrics not use nickname

* refactor: refactor use-camera-nickname hook.
This commit is contained in:
GuoQing Liu
2025-08-26 11:15:01 -06:00
committed by GitHub
parent 195f705616
commit d3af748366
31 changed files with 276 additions and 99 deletions
+13 -6
View File
@@ -4,7 +4,7 @@ import CameraInfoDialog from "@/components/overlay/CameraInfoDialog";
import { Skeleton } from "@/components/ui/skeleton";
import { FrigateConfig } from "@/types/frigateConfig";
import { FrigateStats } from "@/types/stats";
import { useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { MdInfo } from "react-icons/md";
import {
Tooltip,
@@ -13,6 +13,8 @@ import {
} from "@/components/ui/tooltip";
import useSWR from "swr";
import { useTranslation } from "react-i18next";
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
import { resolveCameraName } from "@/hooks/use-camera-nickname";
type CameraMetricsProps = {
lastUpdated: number;
@@ -26,6 +28,11 @@ export default function CameraMetrics({
const { t } = useTranslation(["views/system"]);
// camera info dialog
const getCameraName = useCallback(
(cameraId: string) => resolveCameraName(config, cameraId),
[config],
);
const [showCameraInfoDialog, setShowCameraInfoDialog] = useState(false);
const [probeCameraName, setProbeCameraName] = useState<string>();
@@ -142,7 +149,7 @@ export default function CameraMetrics({
}
if (!(key in series)) {
const camName = key.replaceAll("_", " ");
const camName = getCameraName(key);
series[key] = {};
series[key]["ffmpeg"] = {
name: t("cameras.label.cameraFfmpeg", { camName: camName }),
@@ -173,7 +180,7 @@ export default function CameraMetrics({
});
});
return series;
}, [config, statsHistory, t]);
}, [config, getCameraName, statsHistory, t]);
const cameraFpsSeries = useMemo(() => {
if (!statsHistory) {
@@ -193,7 +200,7 @@ export default function CameraMetrics({
Object.entries(stats.cameras).forEach(([key, camStats]) => {
if (!(key in series)) {
const camName = key.replaceAll("_", " ");
const camName = getCameraName(key);
series[key] = {};
series[key]["fps"] = {
name: t("cameras.label.cameraFramesPerSecond", {
@@ -230,7 +237,7 @@ export default function CameraMetrics({
});
});
return series;
}, [statsHistory, t]);
}, [getCameraName, statsHistory, t]);
useEffect(() => {
if (!showCameraInfoDialog) {
@@ -276,7 +283,7 @@ export default function CameraMetrics({
<div className="flex w-full flex-col gap-3">
<div className="flex flex-row items-center justify-between">
<div className="text-sm font-medium text-muted-foreground smart-capitalize">
{camera.name.replaceAll("_", " ")}
<CameraNameLabel camera={camera} />
</div>
<Tooltip>
<TooltipTrigger>