fix: repair restored board layouts
This commit is contained in:
1 parent
bc41125b7f
commit
2657ff4384
4 files changed
+54
-8
No files matched your search
@@ -10,6 +10,7 @@ import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import type { Database, InferInsertModel, InferSelectModel, SQL } from "@homarr/db";
|
||||
import { and, asc, eq, handleTransactionsAsync, inArray, isNull, like, not, or, sql } from "@homarr/db";
|
||||
import { createDbInsertCollectionWithoutTransaction } from "@homarr/db/collection";
|
||||
import { seedProtectedBoardLayoutsAsync } from "@homarr/db/migrations/seed";
|
||||
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
||||
import {
|
||||
boardGroupPermissions,
|
||||
@@ -2037,7 +2038,14 @@ const getElementsForLayout = (board: BoardForLayoutProjection, layoutId: string)
|
||||
return [...itemElements, ...sectionElements];
|
||||
};
|
||||
|
||||
const getFullBoardWithWhereAsync = async (db: Database, where: SQL<unknown>, userId: string | null) => {
|
||||
const protectedLayoutRepairPromises = new Map<string, Promise<void>>();
|
||||
|
||||
const getFullBoardWithWhereAsync = async (
|
||||
db: Database,
|
||||
where: SQL<unknown>,
|
||||
userId: string | null,
|
||||
repairProtectedLayouts = true,
|
||||
) => {
|
||||
const groupPermissionWhere = userId
|
||||
? inArray(
|
||||
boardGroupPermissions.groupId,
|
||||
@@ -2093,6 +2101,18 @@ const getFullBoardWithWhereAsync = async (db: Database, where: SQL<unknown>, use
|
||||
});
|
||||
}
|
||||
|
||||
if (repairProtectedLayouts && boardLayoutsNeedRepair(board.layouts)) {
|
||||
let repairPromise = protectedLayoutRepairPromises.get(board.id);
|
||||
if (!repairPromise) {
|
||||
repairPromise = seedProtectedBoardLayoutsAsync(db, board.id).finally(() => {
|
||||
protectedLayoutRepairPromises.delete(board.id);
|
||||
});
|
||||
protectedLayoutRepairPromises.set(board.id, repairPromise);
|
||||
}
|
||||
await repairPromise;
|
||||
return getFullBoardWithWhereAsync(db, where, userId, false);
|
||||
}
|
||||
|
||||
const { sections, items, layouts, ...otherBoardProperties } = board;
|
||||
|
||||
return {
|
||||
@@ -2138,6 +2158,23 @@ const getFullBoardWithWhereAsync = async (db: Database, where: SQL<unknown>, use
|
||||
};
|
||||
};
|
||||
|
||||
export const boardLayoutsNeedRepair = (
|
||||
boardLayouts: Array<{ id: string; breakpoint: number; role: "mobile" | "base" | "custom" }>,
|
||||
) => {
|
||||
const mobileLayouts = boardLayouts.filter((layout) => layout.role === "mobile");
|
||||
const baseLayouts = boardLayouts.filter((layout) => layout.role === "base");
|
||||
const baseLayout = baseLayouts.at(0);
|
||||
|
||||
return (
|
||||
mobileLayouts.length !== 1 ||
|
||||
baseLayouts.length !== 1 ||
|
||||
mobileLayouts.at(0)?.breakpoint !== 0 ||
|
||||
!baseLayout ||
|
||||
boardLayouts.some((layout) => layout.id !== baseLayout.id && layout.breakpoint >= baseLayout.breakpoint) ||
|
||||
new Set(boardLayouts.map((layout) => layout.breakpoint)).size !== boardLayouts.length
|
||||
);
|
||||
};
|
||||
|
||||
const forKind = <T extends WidgetKind>(kind: T) =>
|
||||
z.object({
|
||||
kind: z.literal(kind),
|
||||
|
||||
@@ -602,6 +602,13 @@ describe("getBoardByName should return board by name", () => {
|
||||
name,
|
||||
...fullBoardProps,
|
||||
});
|
||||
expect(result.layouts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ role: "mobile", breakpoint: 0, columnCount: 3 }),
|
||||
expect.objectContaining({ id: fullBoardProps.layoutId, role: "base", breakpoint: 768 }),
|
||||
]),
|
||||
);
|
||||
expect(result.items.at(0)?.layouts).toHaveLength(2);
|
||||
expect(spy).toHaveBeenCalledWith(expect.anything(), expect.anything(), "view");
|
||||
});
|
||||
|
||||
@@ -1171,12 +1178,12 @@ describe("saveBoard should save full board", () => {
|
||||
expect(firstItem.id).toBe(itemId);
|
||||
expect(firstItem.kind).toBe("clock");
|
||||
expect(SuperJSON.parse<{ is24HourFormat: boolean }>(firstItem.options).is24HourFormat).toBe(false);
|
||||
const firstLayout = expectToBeDefined(firstItem.layouts[0]);
|
||||
expect(firstLayout.sectionId).toBe(sectionId);
|
||||
expect(firstLayout.height).toBe(3);
|
||||
expect(firstLayout.width).toBe(2);
|
||||
expect(firstLayout.xOffset).toBe(7);
|
||||
expect(firstLayout.yOffset).toBe(5);
|
||||
const updatedLayout = expectToBeDefined(firstItem.layouts.find((layout) => layout.layoutId === layoutId));
|
||||
expect(updatedLayout.sectionId).toBe(sectionId);
|
||||
expect(updatedLayout.height).toBe(3);
|
||||
expect(updatedLayout.width).toBe(2);
|
||||
expect(updatedLayout.xOffset).toBe(7);
|
||||
expect(updatedLayout.yOffset).toBe(5);
|
||||
expect(spy).toHaveBeenCalledWith(expect.anything(), expect.anything(), "modify");
|
||||
});
|
||||
it("should fail when board not found", async () => {
|
||||
|
||||
@@ -161,8 +161,9 @@ export const seedDataAsync = async (db: Database) => {
|
||||
await seedProtectedBoardLayoutsAsync(db);
|
||||
};
|
||||
|
||||
export const seedProtectedBoardLayoutsAsync = async (db: Database) => {
|
||||
export const seedProtectedBoardLayoutsAsync = async (db: Database, boardId?: string) => {
|
||||
const dbBoards = await db.query.boards.findMany({
|
||||
where: boardId ? eq(boards.id, boardId) : undefined,
|
||||
with: {
|
||||
layouts: true,
|
||||
items: { with: { layouts: true } },
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"exports": {
|
||||
".": "./index.ts",
|
||||
"./collection": "./collection.ts",
|
||||
"./migrations/seed": "./migrations/seed.ts",
|
||||
"./proxy-reader": "./proxy-reader.ts",
|
||||
"./schema": "./schema/index.ts",
|
||||
"./test": "./test/index.ts",
|
||||
|
||||
Reference in new issue
Block a user