Merge pull request #107 from ibs0d/claude/persist-camera-order-BkmUM

Persist camera order in Fit to Screen mode across reloads
This commit is contained in:
ibs0d 2026-03-22 18:16:50 +11:00 committed by GitHub
commit 00ca5bafc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -138,6 +138,10 @@ export default function DraggableGridLayout({
false, false,
); );
const [fitCameraOrder, setFitCameraOrder] = useUserPersistence<string[]>(
`${cameraGroup}-fit-camera-order`,
);
const [group] = useUserPersistedOverlayState( const [group] = useUserPersistedOverlayState(
"cameraGroup", "cameraGroup",
"default" as string, "default" as string,
@ -458,25 +462,34 @@ export default function DraggableGridLayout({
const h = w; const h = w;
const colsPerRow = fitGridParams.colsPerRow; const colsPerRow = fitGridParams.colsPerRow;
return cameraNames.map((name, index) => ({ // Применить сохранённый порядок если он валиден
// (содержит ровно те же камеры, что и текущий набор)
let orderedNames = cameraNames;
if (fitCameraOrder) {
const savedSet = new Set(fitCameraOrder);
const currentSet = new Set(cameraNames);
if (
savedSet.size === currentSet.size &&
cameraNames.every((name) => savedSet.has(name))
) {
orderedNames = fitCameraOrder;
}
}
return orderedNames.map((name, index) => ({
i: name, i: name,
x: (index % colsPerRow) * w, x: (index % colsPerRow) * w,
y: Math.floor(index / colsPerRow) * h, y: Math.floor(index / colsPerRow) * h,
w, w,
h, h,
})); }));
}, [fitToScreen, fitGridParams, cameras, includeBirdseye, birdseyeConfig]); }, [fitToScreen, fitGridParams, cameras, includeBirdseye, birdseyeConfig, fitCameraOrder]);
const [fitLayoutOverride, setFitLayoutOverride] = useState<Layout | undefined>();
useEffect(() => { useEffect(() => {
setFitLayoutOverride(undefined); // Сбросить сохранённый порядок только если изменился набор камер
}, [ // (добавили/удалили камеру), не при изменении размера окна
fitGridParams?.gridUnitsPerCam, setFitCameraOrder(undefined);
fitGridParams?.colsPerRow, }, [cameras, includeBirdseye]);
cameras,
includeBirdseye,
]);
const handleFitDragStop = useCallback( const handleFitDragStop = useCallback(
( (
@ -492,7 +505,8 @@ export default function DraggableGridLayout({
const colsPerRow = fitGridParams.colsPerRow; const colsPerRow = fitGridParams.colsPerRow;
const draggedId = newItem.i; const draggedId = newItem.i;
const currentOrder = fitLayoutOverride ?? fitLayout ?? []; // Текущий порядок из fitLayout (уже учитывает fitCameraOrder)
const currentOrder = fitLayout ?? [];
const orderedNames = [...currentOrder] const orderedNames = [...currentOrder]
.sort((a, b) => { .sort((a, b) => {
if (a.y !== b.y) return a.y - b.y; if (a.y !== b.y) return a.y - b.y;
@ -514,44 +528,30 @@ export default function DraggableGridLayout({
const sourceIndex = orderedNames.indexOf(draggedId); const sourceIndex = orderedNames.indexOf(draggedId);
const snapBack = orderedNames.map((name, index) => ({
i: name,
x: (index % colsPerRow) * w,
y: Math.floor(index / colsPerRow) * w,
w,
h: w,
}));
if (sourceIndex === -1 || sourceIndex === targetIndex) { if (sourceIndex === -1 || sourceIndex === targetIndex) {
setFitLayoutOverride(snapBack); // Snap back — пересохранить текущий порядок чтобы layout обновился
setFitCameraOrder([...orderedNames]);
return; return;
} }
// Swap
const newOrder = [...orderedNames]; const newOrder = [...orderedNames];
[newOrder[sourceIndex], newOrder[targetIndex]] = [ [newOrder[sourceIndex], newOrder[targetIndex]] = [
newOrder[targetIndex], newOrder[targetIndex],
newOrder[sourceIndex], newOrder[sourceIndex],
]; ];
const normalized = newOrder.map((name, index) => ({ setFitCameraOrder(newOrder);
i: name,
x: (index % colsPerRow) * w,
y: Math.floor(index / colsPerRow) * w,
w,
h: w,
}));
setFitLayoutOverride(normalized);
}, },
[fitToScreen, fitGridParams, fitLayoutOverride, fitLayout], [fitToScreen, fitGridParams, fitLayout, setFitCameraOrder],
); );
const activeGridLayout = useMemo(() => { const activeGridLayout = useMemo(() => {
if (fitToScreen) { if (fitToScreen) {
return fitLayoutOverride ?? fitLayout ?? currentGridLayout; return fitLayout ?? currentGridLayout;
} }
return currentGridLayout; return currentGridLayout;
}, [fitToScreen, fitLayoutOverride, fitLayout, currentGridLayout]); }, [fitToScreen, fitLayout, currentGridLayout]);
const handleResize = ( const handleResize = (
_layout: Layout, _layout: Layout,