mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 02:29:19 +03:00
Frontend fixes (#22294)
* fix useImageLoaded hook running on every render * fix volume not applying for all cameras * Fix maximum update depth exceeded errors on Review page - use-overlay-state: use refs for location to keep setter identity stable across renders, preventing cascading re-render loops when effects depend on the setter. Add Object.is bail-out guard to skip redundant navigate calls. Move setPersistedValue after bail-out to avoid unnecessary IndexedDB writes. * don't try to fetch previews when motion search dialog is open * revert unneeded changes re-rendering was caused by the overlay state hook, not this one * filter dicts to only use id field in sync recordings
This commit is contained in:
parent
34cc1208a6
commit
c9bd907721
@ -151,7 +151,9 @@ def sync_recordings(
|
|||||||
|
|
||||||
max_inserts = 1000
|
max_inserts = 1000
|
||||||
for batch in chunked(recordings_to_delete, max_inserts):
|
for batch in chunked(recordings_to_delete, max_inserts):
|
||||||
RecordingsToDelete.insert_many(batch).execute()
|
RecordingsToDelete.insert_many(
|
||||||
|
[{"id": r["id"]} for r in batch]
|
||||||
|
).execute()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
deleted = (
|
deleted = (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useContext, useEffect, useMemo } from "react";
|
import { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
||||||
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { usePersistence } from "./use-persistence";
|
import { usePersistence } from "./use-persistence";
|
||||||
import { useUserPersistence } from "./use-user-persistence";
|
import { useUserPersistence } from "./use-user-persistence";
|
||||||
@ -12,20 +12,28 @@ export function useOverlayState<S>(
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const currentLocationState = useMemo(() => location.state, [location]);
|
const locationRef = useRef(location);
|
||||||
|
locationRef.current = location;
|
||||||
|
|
||||||
const setOverlayStateValue = useCallback(
|
const setOverlayStateValue = useCallback(
|
||||||
(value: S, replace: boolean = false) => {
|
(value: S, replace: boolean = false) => {
|
||||||
const newLocationState = { ...currentLocationState };
|
const loc = locationRef.current;
|
||||||
|
const currentValue = loc.state?.[key] as S | undefined;
|
||||||
|
|
||||||
|
if (Object.is(currentValue, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newLocationState = { ...loc.state };
|
||||||
newLocationState[key] = value;
|
newLocationState[key] = value;
|
||||||
navigate(location.pathname + (preserveSearch ? location.search : ""), {
|
navigate(loc.pathname + (preserveSearch ? loc.search : ""), {
|
||||||
state: newLocationState,
|
state: newLocationState,
|
||||||
replace,
|
replace,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// we know that these deps are correct
|
// locationRef is stable so we don't need it in deps
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[key, currentLocationState, navigate],
|
[key, navigate, preserveSearch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const overlayStateValue = useMemo<S | undefined>(
|
const overlayStateValue = useMemo<S | undefined>(
|
||||||
@ -47,7 +55,9 @@ export function usePersistedOverlayState<S extends string>(
|
|||||||
] {
|
] {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const currentLocationState = useMemo(() => location.state, [location]);
|
|
||||||
|
const locationRef = useRef(location);
|
||||||
|
locationRef.current = location;
|
||||||
|
|
||||||
// currently selected value
|
// currently selected value
|
||||||
|
|
||||||
@ -63,14 +73,21 @@ export function usePersistedOverlayState<S extends string>(
|
|||||||
|
|
||||||
const setOverlayStateValue = useCallback(
|
const setOverlayStateValue = useCallback(
|
||||||
(value: S | undefined, replace: boolean = false) => {
|
(value: S | undefined, replace: boolean = false) => {
|
||||||
|
const loc = locationRef.current;
|
||||||
|
const currentValue = loc.state?.[key] as S | undefined;
|
||||||
|
|
||||||
|
if (Object.is(currentValue, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setPersistedValue(value);
|
setPersistedValue(value);
|
||||||
const newLocationState = { ...currentLocationState };
|
const newLocationState = { ...loc.state };
|
||||||
newLocationState[key] = value;
|
newLocationState[key] = value;
|
||||||
navigate(location.pathname, { state: newLocationState, replace });
|
navigate(loc.pathname, { state: newLocationState, replace });
|
||||||
},
|
},
|
||||||
// we know that these deps are correct
|
// locationRef is stable so we don't need it in deps
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[key, currentLocationState, navigate],
|
[key, navigate, setPersistedValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -98,7 +115,9 @@ export function useUserPersistedOverlayState<S extends string>(
|
|||||||
const { auth } = useContext(AuthContext);
|
const { auth } = useContext(AuthContext);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const currentLocationState = useMemo(() => location.state, [location]);
|
|
||||||
|
const locationRef = useRef(location);
|
||||||
|
locationRef.current = location;
|
||||||
|
|
||||||
// currently selected value from URL state
|
// currently selected value from URL state
|
||||||
const overlayStateValue = useMemo<S | undefined>(
|
const overlayStateValue = useMemo<S | undefined>(
|
||||||
@ -112,14 +131,21 @@ export function useUserPersistedOverlayState<S extends string>(
|
|||||||
|
|
||||||
const setOverlayStateValue = useCallback(
|
const setOverlayStateValue = useCallback(
|
||||||
(value: S | undefined, replace: boolean = false) => {
|
(value: S | undefined, replace: boolean = false) => {
|
||||||
|
const loc = locationRef.current;
|
||||||
|
const currentValue = loc.state?.[key] as S | undefined;
|
||||||
|
|
||||||
|
if (Object.is(currentValue, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setPersistedValue(value);
|
setPersistedValue(value);
|
||||||
const newLocationState = { ...currentLocationState };
|
const newLocationState = { ...loc.state };
|
||||||
newLocationState[key] = value;
|
newLocationState[key] = value;
|
||||||
navigate(location.pathname, { state: newLocationState, replace });
|
navigate(loc.pathname, { state: newLocationState, replace });
|
||||||
},
|
},
|
||||||
// we know that these deps are correct
|
// locationRef is stable so we don't need it in deps
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[key, currentLocationState, navigate, setPersistedValue],
|
[key, navigate, setPersistedValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Don't return a value until auth has finished loading
|
// Don't return a value until auth has finished loading
|
||||||
@ -142,17 +168,21 @@ export function useHashState<S extends string>(): [
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const locationRef = useRef(location);
|
||||||
|
locationRef.current = location;
|
||||||
|
|
||||||
const setHash = useCallback(
|
const setHash = useCallback(
|
||||||
(value: S | undefined) => {
|
(value: S | undefined) => {
|
||||||
|
const loc = locationRef.current;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
navigate(location.pathname);
|
navigate(loc.pathname);
|
||||||
} else {
|
} else {
|
||||||
navigate(`${location.pathname}#${value}`, { state: location.state });
|
navigate(`${loc.pathname}#${value}`, { state: loc.state });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// we know that these deps are correct
|
// locationRef is stable so we don't need it in deps
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[location, navigate],
|
[navigate],
|
||||||
);
|
);
|
||||||
|
|
||||||
const hash = useMemo(
|
const hash = useMemo(
|
||||||
|
|||||||
@ -632,9 +632,10 @@ export default function DraggableGridLayout({
|
|||||||
toggleStats={() => toggleStats(camera.name)}
|
toggleStats={() => toggleStats(camera.name)}
|
||||||
volumeState={volumeStates[camera.name]}
|
volumeState={volumeStates[camera.name]}
|
||||||
setVolumeState={(value) =>
|
setVolumeState={(value) =>
|
||||||
setVolumeStates({
|
setVolumeStates((prev) => ({
|
||||||
|
...prev,
|
||||||
[camera.name]: value,
|
[camera.name]: value,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
muteAll={muteAll}
|
muteAll={muteAll}
|
||||||
unmuteAll={unmuteAll}
|
unmuteAll={unmuteAll}
|
||||||
|
|||||||
@ -131,12 +131,10 @@ export default function MotionSearchView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Camera previews – defer until dialog is closed
|
// Camera previews – defer until dialog is closed
|
||||||
const allPreviews = useCameraPreviews(
|
const allPreviews = useCameraPreviews(timeRange, {
|
||||||
isSearchDialogOpen ? { after: 0, before: 0 } : timeRange,
|
camera: selectedCamera ?? undefined,
|
||||||
{
|
fetchPreviews: !isSearchDialogOpen,
|
||||||
camera: selectedCamera ?? undefined,
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ROI state
|
// ROI state
|
||||||
const [polygonPoints, setPolygonPoints] = useState<number[][]>([]);
|
const [polygonPoints, setPolygonPoints] = useState<number[][]>([]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user