* Fix api filter hook

cameras, labels, sub labels, plates, and zones could be parsed as numeric values rather than strings, which would break the explore filter. This change adds an optional param to the useApiFilterArgs hook to always parse keys as string[]

* fix notifications register button from being incorrectly disabled
This commit is contained in:
Josh Hawkins 2025-08-06 22:09:43 -05:00 committed by GitHub
parent 5fc030c3f6
commit b6b3178e3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 20 deletions

View File

@ -47,9 +47,9 @@ export default function useApiFilter<
return [filter, setFilter, searchParams]; return [filter, setFilter, searchParams];
} }
export function useApiFilterArgs< export function useApiFilterArgs<F extends FilterType>(
F extends FilterType, arrayKeys: string[],
>(): useApiFilterReturn<F> { ): useApiFilterReturn<F> {
const [rawParams, setRawParams] = useSearchParams(); const [rawParams, setRawParams] = useSearchParams();
const setFilter = useCallback( const setFilter = useCallback(
@ -64,30 +64,37 @@ export function useApiFilterArgs<
const filter: { [key: string]: unknown } = {}; const filter: { [key: string]: unknown } = {};
rawParams.forEach((value, key) => { // always treat these keys as string[], not as a number or event id
const isValidNumber = /^-?\d+(\.\d+)?(?!.)/.test(value); const arrayKeySet = new Set(arrayKeys);
const isValidEventID = /^\d+\.\d+-[a-zA-Z0-9]+$/.test(value);
if ( rawParams.forEach((value, key) => {
value != "true" && if (arrayKeySet.has(key)) {
value != "false" &&
!isValidNumber &&
!isValidEventID
) {
filter[key] = value.includes(",") ? value.split(",") : [value]; filter[key] = value.includes(",") ? value.split(",") : [value];
} else { } else {
if (value != undefined) { const isValidNumber = /^-?\d+(\.\d+)?(?!.)/.test(value);
try { const isValidEventID = /^\d+\.\d+-[a-zA-Z0-9]+$/.test(value);
filter[key] = JSON.parse(value);
} catch { if (
filter[key] = `${value}`; value != "true" &&
value != "false" &&
!isValidNumber &&
!isValidEventID
) {
filter[key] = value.includes(",") ? value.split(",") : [value];
} else {
if (value != undefined) {
try {
filter[key] = JSON.parse(value);
} catch {
filter[key] = `${value}`;
}
} }
} }
} }
}); });
return filter as F; return filter as F;
}, [rawParams]); }, [rawParams, arrayKeys]);
const searchParams = useMemo(() => { const searchParams = useMemo(() => {
if (filter == undefined || Object.keys(filter).length == 0) { if (filter == undefined || Object.keys(filter).length == 0) {

View File

@ -58,7 +58,13 @@ export default function Explore() {
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [searchFilter, setSearchFilter, searchSearchParams] = const [searchFilter, setSearchFilter, searchSearchParams] =
useApiFilterArgs<SearchFilter>(); useApiFilterArgs<SearchFilter>([
"cameras",
"labels",
"sub_labels",
"recognized_license_plate",
"zones",
]);
const searchTerm = useMemo( const searchTerm = useMemo(
() => searchSearchParams?.["query"] || "", () => searchSearchParams?.["query"] || "",

View File

@ -523,7 +523,9 @@ export default function NotificationView({
aria-label={t("notification.registerDevice")} aria-label={t("notification.registerDevice")}
disabled={ disabled={
(!config?.notifications.enabled && (!config?.notifications.enabled &&
notificationCameras.length === 0) || notificationCameras.length === 0 &&
!form.watch("allEnabled") &&
form.watch("cameras").length === 0) ||
publicKey == undefined publicKey == undefined
} }
onClick={() => { onClick={() => {