mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-27 01:08:58 +03:00
Miscellaneous fixes (#23186)
* improve scroll handling for non-modal DropdownMenu in classification and face selection dialogs * clean up * fix incorrect key capitalization * fix profile array overrides not replacing base arrays don't use lodash merge(), it does positional merging and an empty source array doesn't override the destination, and shorter arrays leak destination elements through. backend is unaffected, so the saved config and actual backend functionality was right * only show audio debug tab when audio is enabled in config * move apple_compatibility out of advanced * remove retry_interval from UI 99% of users should never be changing this * hide switch in optionalfieldwidget if editing a profile * add override badges for cameras and profiles collect shared functions into the config util and separate hooks * Use new models endpoint info to determine modalities * clarify language * fix linter --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
co-authored by
Nicolas Mowen
parent
bd1fc1cc72
commit
ca75f06456
@@ -12,6 +12,7 @@ import { isJsonObject } from "@/lib/utils";
|
||||
import {
|
||||
getBaseCameraSectionValue,
|
||||
getEffectiveHiddenFields,
|
||||
pathMatchesHiddenPattern,
|
||||
unsetWithWildcard,
|
||||
} from "@/utils/configUtil";
|
||||
import { extractSectionSchema } from "@/hooks/use-config-schema";
|
||||
@@ -663,3 +664,138 @@ export function useCamerasOverridingSection(
|
||||
return entries;
|
||||
}, [config, sectionPath, schema]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook returning the field-level deltas between a single camera's base
|
||||
* (pre-profile) section value and the effective global baseline. Mirrors
|
||||
* `useConfigOverride`'s comparison logic but exposes per-field deltas so a
|
||||
* popover can list the overridden fields.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const deltas = useCameraSectionDeltas(config, "front_door", "detect");
|
||||
* // [{ fieldPath: "fps", globalValue: 5, cameraValue: 10 }]
|
||||
* ```
|
||||
*/
|
||||
export function useCameraSectionDeltas(
|
||||
config: FrigateConfig | undefined,
|
||||
cameraName: string | undefined,
|
||||
sectionPath: string,
|
||||
): FieldDelta[] {
|
||||
const { data: schema } = useSWR<RJSFSchema>("config/schema.json");
|
||||
return useMemo(() => {
|
||||
if (!config?.cameras || !cameraName || !sectionPath) {
|
||||
return [];
|
||||
}
|
||||
const cameraConfig = config.cameras[cameraName];
|
||||
if (!cameraConfig) return [];
|
||||
|
||||
const sectionMeta = OVERRIDABLE_SECTIONS.find((s) => s.key === sectionPath);
|
||||
const compareFields = sectionMeta?.compareFields;
|
||||
|
||||
const globalValue = collapseEmpty(
|
||||
getEffectiveGlobalBaseline(config, sectionPath, compareFields, schema),
|
||||
);
|
||||
const cameraValue = collapseEmpty(
|
||||
normalizeConfigValue(
|
||||
getBaseCameraSectionValue(config, cameraName, sectionPath),
|
||||
),
|
||||
);
|
||||
|
||||
const hiddenFields = getEffectiveHiddenFields(
|
||||
sectionPath,
|
||||
"camera",
|
||||
config,
|
||||
);
|
||||
|
||||
const deltas: FieldDelta[] = [];
|
||||
for (const delta of collectFieldDeltas(
|
||||
globalValue,
|
||||
cameraValue,
|
||||
compareFields,
|
||||
)) {
|
||||
if (
|
||||
hiddenFields.some((pattern) =>
|
||||
pathMatchesHiddenPattern(delta.fieldPath, pattern),
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
deltas.push(delta);
|
||||
}
|
||||
return deltas;
|
||||
}, [config, cameraName, sectionPath, schema]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook returning the field-level deltas between a single profile's overrides
|
||||
* and the camera's base (pre-profile) section value. Honors per-section
|
||||
* `compareFields` filters and hidden-field patterns so the result matches
|
||||
* what's actually exposed in the UI.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const deltas = useProfileSectionDeltas(config, "front_door", "night", "detect");
|
||||
* // [{ fieldPath: "fps", globalValue: 5, cameraValue: 10, profileName: "night" }]
|
||||
* ```
|
||||
*/
|
||||
export function useProfileSectionDeltas(
|
||||
config: FrigateConfig | undefined,
|
||||
cameraName: string | undefined,
|
||||
profileName: string | undefined,
|
||||
sectionPath: string,
|
||||
): FieldDelta[] {
|
||||
return useMemo(() => {
|
||||
if (!config?.cameras || !cameraName || !profileName || !sectionPath) {
|
||||
return [];
|
||||
}
|
||||
const cameraConfig = config.cameras[cameraName];
|
||||
if (!cameraConfig) return [];
|
||||
|
||||
const profileSection = (
|
||||
cameraConfig.profiles?.[profileName] as
|
||||
| Record<string, unknown>
|
||||
| undefined
|
||||
)?.[sectionPath];
|
||||
if (profileSection == null) return [];
|
||||
|
||||
const sectionMeta = OVERRIDABLE_SECTIONS.find((s) => s.key === sectionPath);
|
||||
const compareFields = sectionMeta?.compareFields;
|
||||
|
||||
const baseValue = collapseEmpty(
|
||||
normalizeConfigValue(
|
||||
getBaseCameraSectionValue(config, cameraName, sectionPath),
|
||||
),
|
||||
);
|
||||
const profileValue = collapseEmpty(
|
||||
normalizeConfigValue(profileSection as JsonValue),
|
||||
);
|
||||
|
||||
const hiddenFields = getEffectiveHiddenFields(
|
||||
sectionPath,
|
||||
"camera",
|
||||
config,
|
||||
);
|
||||
|
||||
const deltas: FieldDelta[] = [];
|
||||
for (const path of collectDefinedLeafPaths(profileValue)) {
|
||||
if (!isPathAllowed(path, compareFields)) continue;
|
||||
if (
|
||||
hiddenFields.some((pattern) => pathMatchesHiddenPattern(path, pattern))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const baseField = get(baseValue, path);
|
||||
const profileField = get(profileValue, path);
|
||||
if (!isEqual(baseField, profileField)) {
|
||||
deltas.push({
|
||||
fieldPath: path,
|
||||
globalValue: baseField,
|
||||
cameraValue: profileField,
|
||||
profileName,
|
||||
});
|
||||
}
|
||||
}
|
||||
return deltas;
|
||||
}, [config, cameraName, profileName, sectionPath]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user