mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-01 08:32:18 +03:00
Add UI config messages framework (#22692)
* add config messages to sections and fields * add alert variants * add messages to types * add detect fps, review, and audio messages * add a basic set of messages * remove emptySelectionHintKey from switches widget use the new messages framework and revert the changes made in #22664
This commit is contained in:
@@ -3,6 +3,21 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const audio: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/audio_detectors",
|
||||
messages: [
|
||||
{
|
||||
key: "no-audio-role",
|
||||
messageKey: "configMessages.audio.noAudioRole",
|
||||
severity: "warning",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level === "camera" && ctx.fullCameraConfig) {
|
||||
return !ctx.fullCameraConfig.ffmpeg?.inputs?.some((input) =>
|
||||
input.roles?.includes("audio"),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
],
|
||||
restartRequired: [],
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -3,6 +3,19 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const audioTranscription: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/audio_detectors#audio-transcription",
|
||||
messages: [
|
||||
{
|
||||
key: "audio-detection-disabled",
|
||||
messageKey: "configMessages.audioTranscription.audioDetectionDisabled",
|
||||
severity: "warning",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level === "camera" && ctx.fullCameraConfig) {
|
||||
return ctx.fullCameraConfig.audio.enabled === false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
],
|
||||
restartRequired: [],
|
||||
fieldOrder: ["enabled", "language", "device", "model_size"],
|
||||
hiddenFields: ["enabled_in_config", "live_enabled"],
|
||||
|
||||
@@ -3,6 +3,20 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const birdseye: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/birdseye",
|
||||
messages: [
|
||||
{
|
||||
key: "objects-mode-detect-disabled",
|
||||
messageKey: "configMessages.birdseye.objectsModeDetectDisabled",
|
||||
severity: "info",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
|
||||
return (
|
||||
ctx.formData?.mode === "objects" &&
|
||||
ctx.fullCameraConfig.detect?.enabled === false
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
restartRequired: [],
|
||||
fieldOrder: ["enabled", "mode", "order"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -3,6 +3,21 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const detect: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/camera_specific",
|
||||
fieldMessages: [
|
||||
{
|
||||
key: "fps-greater-than-five",
|
||||
field: "fps",
|
||||
messageKey: "configMessages.detect.fpsGreaterThanFive",
|
||||
severity: "info",
|
||||
position: "after",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
|
||||
const detectFps = ctx.formData?.fps as number | undefined;
|
||||
const streamFps = ctx.fullCameraConfig.detect?.fps;
|
||||
return detectFps != null && streamFps != null && detectFps > 5;
|
||||
},
|
||||
},
|
||||
],
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"width",
|
||||
|
||||
@@ -3,6 +3,26 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const faceRecognition: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/face_recognition",
|
||||
messages: [
|
||||
{
|
||||
key: "global-disabled",
|
||||
messageKey: "configMessages.faceRecognition.globalDisabled",
|
||||
severity: "warning",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera") return false;
|
||||
return ctx.fullConfig.face_recognition?.enabled === false;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "person-not-tracked",
|
||||
messageKey: "configMessages.faceRecognition.personNotTracked",
|
||||
severity: "info",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
|
||||
return !ctx.fullCameraConfig.objects?.track?.includes("person");
|
||||
},
|
||||
},
|
||||
],
|
||||
restartRequired: [],
|
||||
fieldOrder: ["enabled", "min_area"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -3,6 +3,28 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const lpr: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/license_plate_recognition",
|
||||
messages: [
|
||||
{
|
||||
key: "global-disabled",
|
||||
messageKey: "configMessages.lpr.globalDisabled",
|
||||
severity: "warning",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera") return false;
|
||||
return ctx.fullConfig.lpr?.enabled === false;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "vehicle-not-tracked",
|
||||
messageKey: "configMessages.lpr.vehicleNotTracked",
|
||||
severity: "info",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
|
||||
if (ctx.fullCameraConfig.type === "lpr") return false;
|
||||
const tracked = ctx.fullCameraConfig.objects?.track ?? [];
|
||||
return !tracked.some((o) => ["car", "motorcycle"].includes(o));
|
||||
},
|
||||
},
|
||||
],
|
||||
fieldDocs: {
|
||||
enhancement: "/configuration/license_plate_recognition#enhancement",
|
||||
},
|
||||
|
||||
@@ -3,6 +3,19 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const record: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/record",
|
||||
messages: [
|
||||
{
|
||||
key: "no-record-role",
|
||||
messageKey: "configMessages.record.noRecordRole",
|
||||
severity: "warning",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
|
||||
return !ctx.fullCameraConfig.ffmpeg?.inputs?.some((i) =>
|
||||
i.roles?.includes("record"),
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
restartRequired: [],
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -3,6 +3,45 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const review: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/review",
|
||||
messages: [
|
||||
{
|
||||
key: "record-disabled",
|
||||
messageKey: "configMessages.review.recordDisabled",
|
||||
severity: "warning",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level === "camera" && ctx.fullCameraConfig) {
|
||||
return ctx.fullCameraConfig.record.enabled === false;
|
||||
}
|
||||
return ctx.fullConfig.record?.enabled === false;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "detect-disabled",
|
||||
messageKey: "configMessages.review.detectDisabled",
|
||||
severity: "info",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level === "camera" && ctx.fullCameraConfig) {
|
||||
return ctx.fullCameraConfig.detect?.enabled === false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
],
|
||||
fieldMessages: [
|
||||
{
|
||||
key: "detections-all-non-alert",
|
||||
field: "detections.labels",
|
||||
messageKey: "configMessages.review.allNonAlertDetections",
|
||||
severity: "info",
|
||||
position: "after",
|
||||
condition: (ctx) => {
|
||||
const labels = (
|
||||
ctx.formData?.detections as Record<string, unknown> | undefined
|
||||
)?.labels;
|
||||
return !Array.isArray(labels) || labels.length === 0;
|
||||
},
|
||||
},
|
||||
],
|
||||
fieldDocs: {
|
||||
"alerts.labels": "/configuration/review/#alerts-and-detections",
|
||||
"detections.labels": "/configuration/review/#alerts-and-detections",
|
||||
@@ -35,8 +74,6 @@ const review: SectionConfigOverrides = {
|
||||
"ui:widget": "reviewLabels",
|
||||
"ui:options": {
|
||||
suppressMultiSchema: true,
|
||||
emptySelectionHintKey:
|
||||
"configForm.reviewLabels.allNonAlertDetections",
|
||||
},
|
||||
},
|
||||
required_zones: {
|
||||
|
||||
@@ -3,6 +3,17 @@ import type { SectionConfigOverrides } from "./types";
|
||||
const snapshots: SectionConfigOverrides = {
|
||||
base: {
|
||||
sectionDocs: "/configuration/snapshots",
|
||||
messages: [
|
||||
{
|
||||
key: "detect-disabled",
|
||||
messageKey: "configMessages.snapshots.detectDisabled",
|
||||
severity: "info",
|
||||
condition: (ctx) => {
|
||||
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
|
||||
return ctx.fullCameraConfig.detect?.enabled === false;
|
||||
},
|
||||
},
|
||||
],
|
||||
restartRequired: [],
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
import type { FrigateConfig, CameraConfig } from "@/types/frigateConfig";
|
||||
import type { ConfigSectionData } from "@/types/configForm";
|
||||
import type { SectionConfig } from "../sections/BaseSection";
|
||||
|
||||
/** Context provided to message condition functions */
|
||||
export type MessageConditionContext = {
|
||||
fullConfig: FrigateConfig;
|
||||
fullCameraConfig?: CameraConfig;
|
||||
level: "global" | "camera";
|
||||
cameraName?: string;
|
||||
formData: ConfigSectionData;
|
||||
};
|
||||
|
||||
/** Severity levels for conditional messages */
|
||||
export type MessageSeverity = "info" | "warning" | "error";
|
||||
|
||||
/** A conditional message definition */
|
||||
export type ConditionalMessage = {
|
||||
/** Unique key for React list rendering and deduplication */
|
||||
key: string;
|
||||
/** Translation key resolved via t() in the views/settings namespace */
|
||||
messageKey: string;
|
||||
/** Severity level controlling visual styling */
|
||||
severity: MessageSeverity;
|
||||
/** Function returning true when the message should be shown */
|
||||
condition: (ctx: MessageConditionContext) => boolean;
|
||||
};
|
||||
|
||||
/** Field-level conditional message, adds field targeting */
|
||||
export type FieldConditionalMessage = ConditionalMessage & {
|
||||
/** Dot-separated field path (e.g., "enabled", "alerts.labels") */
|
||||
field: string;
|
||||
/** Whether to render before or after the field (default: "before") */
|
||||
position?: "before" | "after";
|
||||
};
|
||||
|
||||
export type SectionConfigOverrides = {
|
||||
base?: SectionConfig;
|
||||
global?: Partial<SectionConfig>;
|
||||
|
||||
Reference in New Issue
Block a user