fix(import): autofix missing shapes for sidebars and some sections as well (#2723)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { BoardSize, OldmarrConfig } from "@homarr/old-schema";
|
||||
import type { OldmarrConfig } from "@homarr/old-schema";
|
||||
|
||||
export class OldHomarrImportError extends Error {
|
||||
constructor(oldConfig: OldmarrConfig, cause: unknown) {
|
||||
@@ -7,9 +7,3 @@ export class OldHomarrImportError extends Error {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class OldHomarrScreenSizeError extends Error {
|
||||
constructor(type: "app" | "widget", id: string, screenSize: BoardSize) {
|
||||
super(`Screen size not found for type=${type} id=${id} screenSize=${screenSize}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ import { isWidgetRestricted } from "@homarr/auth/shared";
|
||||
import { createId } from "@homarr/db";
|
||||
import { createDbInsertCollectionForTransaction } from "@homarr/db/collection";
|
||||
import { logger } from "@homarr/log";
|
||||
import type { BoardSize } from "@homarr/old-schema";
|
||||
import type { BoardSize, OldmarrConfig } from "@homarr/old-schema";
|
||||
import { boardSizes, getBoardSizeName } from "@homarr/old-schema";
|
||||
|
||||
import { widgetImports } from "../../../../widgets/src";
|
||||
import { fixSectionIssues } from "../../fix-section-issues";
|
||||
import { OldHomarrImportError } from "../../import-error";
|
||||
import { mapBoard } from "../../mappers/map-board";
|
||||
import { mapBreakpoint } from "../../mappers/map-breakpoint";
|
||||
import { mapColumnCount } from "../../mappers/map-column-count";
|
||||
@@ -61,6 +62,13 @@ export const createBoardInsertCollection = (
|
||||
logger.debug(`Added apps to board insert collection count=${insertCollection.apps.length}`);
|
||||
|
||||
preparedBoards.forEach((board) => {
|
||||
if (!hasEnoughItemShapes(board.config)) {
|
||||
throw new OldHomarrImportError(
|
||||
board.config,
|
||||
new Error("Your config contains items without shapes for all board sizes."),
|
||||
);
|
||||
}
|
||||
|
||||
const { wrappers, categories, wrapperIdsToMerge } = fixSectionIssues(board.config);
|
||||
const { apps, widgets } = moveWidgetsAndAppsIfMerge(board.config, wrapperIdsToMerge, {
|
||||
...settings,
|
||||
@@ -130,3 +138,26 @@ export const createBoardInsertCollection = (
|
||||
|
||||
return insertCollection;
|
||||
};
|
||||
|
||||
export const hasEnoughItemShapes = (config: {
|
||||
apps: Pick<OldmarrConfig["apps"][number], "shape">[];
|
||||
widgets: Pick<OldmarrConfig["widgets"][number], "shape">[];
|
||||
}) => {
|
||||
const invalidSizes: BoardSize[] = [];
|
||||
|
||||
for (const size of boardSizes) {
|
||||
if (invalidSizes.includes(size)) continue;
|
||||
|
||||
if (config.apps.some((app) => app.shape[size] === undefined)) {
|
||||
invalidSizes.push(size);
|
||||
}
|
||||
|
||||
if (invalidSizes.includes(size)) continue;
|
||||
|
||||
if (config.widgets.some((widget) => widget.shape[size] === undefined)) {
|
||||
invalidSizes.push(size);
|
||||
}
|
||||
}
|
||||
|
||||
return invalidSizes.length <= 2;
|
||||
};
|
||||
|
||||
53
packages/old-import/src/import/test/board-collection.spec.ts
Normal file
53
packages/old-import/src/import/test/board-collection.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import type { BoardSize } from "@homarr/old-schema";
|
||||
|
||||
import { hasEnoughItemShapes } from "../collections/board-collection";
|
||||
|
||||
const defaultShape = {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
};
|
||||
|
||||
describe("hasEnoughItemShapes should check if there are more than one shape available for automatic reconstruction", () => {
|
||||
test.each([
|
||||
[true, [], []], // no items, so nothing to check
|
||||
[true, [{ lg: true }], []], // lg always exists
|
||||
[true, [], [{ md: true }]], // md always exists
|
||||
[true, [{ md: true, sm: true }], [{ md: true, lg: true }]], // md always exists
|
||||
[true, [{ md: true }], [{ md: true }]], // md always exists
|
||||
[false, [{ md: true }, { md: true }], [{ lg: true }]], // md is missing for widgets
|
||||
[false, [{ md: true }], [{ lg: true }]], // md is missing for widgets
|
||||
[false, [{ md: true }], [{ md: true, lg: true }, { lg: true }]], // md is missing for 2. widget
|
||||
] as [boolean, Shape[], Shape[]][])(
|
||||
"should return %s if there are more than one shape available",
|
||||
(returnValue, appShapes, widgetShapes) => {
|
||||
const result = hasEnoughItemShapes({
|
||||
apps: appShapes.map((shapes) => ({
|
||||
shape: {
|
||||
sm: shapes.sm ? defaultShape : undefined,
|
||||
md: shapes.md ? defaultShape : undefined,
|
||||
lg: shapes.lg ? defaultShape : undefined,
|
||||
},
|
||||
})),
|
||||
widgets: widgetShapes.map((shapes) => ({
|
||||
shape: {
|
||||
sm: shapes.sm ? defaultShape : undefined,
|
||||
md: shapes.md ? defaultShape : undefined,
|
||||
lg: shapes.lg ? defaultShape : undefined,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
expect(result).toBe(returnValue);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
type Shape = Partial<Record<BoardSize, true>>;
|
||||
@@ -3,7 +3,6 @@ import { logger } from "@homarr/log";
|
||||
import type { BoardSize, OldmarrApp, OldmarrConfig, OldmarrWidget } from "@homarr/old-schema";
|
||||
import { boardSizes } from "@homarr/old-schema";
|
||||
|
||||
import { OldHomarrScreenSizeError } from "./import-error";
|
||||
import { mapColumnCount } from "./mappers/map-column-count";
|
||||
import type { OldmarrImportConfiguration } from "./settings";
|
||||
|
||||
@@ -60,7 +59,7 @@ export const moveWidgetsAndAppsIfMerge = (
|
||||
for (const screenSize of boardSizes) {
|
||||
const screenSizeShape = app.shape[screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("app", app.id, screenSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the highest widget in the wrapper to increase the offset accordingly
|
||||
@@ -81,7 +80,7 @@ export const moveWidgetsAndAppsIfMerge = (
|
||||
for (const screenSize of boardSizes) {
|
||||
const screenSizeShape = widget.shape[screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("widget", widget.id, screenSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the highest widget in the wrapper to increase the offset accordingly
|
||||
@@ -145,15 +144,6 @@ const moveWidgetsAndAppsInLeftSidebar = (
|
||||
item.area.properties.location === "left" &&
|
||||
(columnCount >= 2 || item.shape[screenSize]?.location.x === 0),
|
||||
update: (item) => {
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("kind" in item ? "widget" : "app", item.id, screenSize);
|
||||
}
|
||||
// Reduce width to one if column count is one
|
||||
if (screenSizeShape.size.width > columnCount) {
|
||||
screenSizeShape.size.width = columnCount;
|
||||
}
|
||||
|
||||
item.area = {
|
||||
type: "wrapper",
|
||||
properties: {
|
||||
@@ -161,6 +151,14 @@ const moveWidgetsAndAppsInLeftSidebar = (
|
||||
},
|
||||
};
|
||||
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) return;
|
||||
|
||||
// Reduce width to one if column count is one
|
||||
if (screenSizeShape.size.width > columnCount) {
|
||||
screenSizeShape.size.width = columnCount;
|
||||
}
|
||||
|
||||
screenSizeShape.location.y += offset;
|
||||
},
|
||||
});
|
||||
@@ -184,11 +182,6 @@ const moveWidgetsAndAppsInLeftSidebar = (
|
||||
item.area.properties.location === "left" &&
|
||||
item.shape[screenSize]?.location.x === 1,
|
||||
update: (item) => {
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("kind" in item ? "widget" : "app", item.id, screenSize);
|
||||
}
|
||||
|
||||
item.area = {
|
||||
type: "wrapper",
|
||||
properties: {
|
||||
@@ -196,6 +189,9 @@ const moveWidgetsAndAppsInLeftSidebar = (
|
||||
},
|
||||
};
|
||||
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) return;
|
||||
|
||||
screenSizeShape.location.x = 0;
|
||||
screenSizeShape.location.y += offset;
|
||||
},
|
||||
@@ -222,16 +218,6 @@ const moveWidgetsAndAppsInRightSidebar = (
|
||||
item.area.properties.location === "right" &&
|
||||
(columnCount >= 2 || item.shape[screenSize]?.location.x === 0),
|
||||
update: (item) => {
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("kind" in item ? "widget" : "app", item.id, screenSize);
|
||||
}
|
||||
|
||||
// Reduce width to one if column count is one
|
||||
if (screenSizeShape.size.width > columnCount) {
|
||||
screenSizeShape.size.width = columnCount;
|
||||
}
|
||||
|
||||
item.area = {
|
||||
type: "wrapper",
|
||||
properties: {
|
||||
@@ -239,6 +225,14 @@ const moveWidgetsAndAppsInRightSidebar = (
|
||||
},
|
||||
};
|
||||
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) return;
|
||||
|
||||
// Reduce width to one if column count is one
|
||||
if (screenSizeShape.size.width > columnCount) {
|
||||
screenSizeShape.size.width = columnCount;
|
||||
}
|
||||
|
||||
screenSizeShape.location.y += offset;
|
||||
screenSizeShape.location.x += xOffsetDelta;
|
||||
},
|
||||
@@ -260,11 +254,6 @@ const moveWidgetsAndAppsInRightSidebar = (
|
||||
item.area.properties.location === "left" &&
|
||||
item.shape[screenSize]?.location.x === 1,
|
||||
update: (item) => {
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("kind" in item ? "widget" : "app", item.id, screenSize);
|
||||
}
|
||||
|
||||
item.area = {
|
||||
type: "wrapper",
|
||||
properties: {
|
||||
@@ -272,6 +261,9 @@ const moveWidgetsAndAppsInRightSidebar = (
|
||||
},
|
||||
};
|
||||
|
||||
const screenSizeShape = item.shape[screenSize];
|
||||
if (!screenSizeShape) return;
|
||||
|
||||
screenSizeShape.location.x = 0;
|
||||
screenSizeShape.location.y += offset;
|
||||
},
|
||||
@@ -312,16 +304,15 @@ const updateItems = (options: {
|
||||
for (const item of items) {
|
||||
const before = createItemSnapshot(item, options.screenSize);
|
||||
|
||||
options.update(item);
|
||||
|
||||
const screenSizeShape = item.shape[options.screenSize];
|
||||
if (!screenSizeShape) {
|
||||
throw new OldHomarrScreenSizeError("kind" in item ? "widget" : "app", item.id, options.screenSize);
|
||||
}
|
||||
if (!screenSizeShape) return requiredHeight;
|
||||
|
||||
if (screenSizeShape.location.y + screenSizeShape.size.height > requiredHeight) {
|
||||
requiredHeight = screenSizeShape.location.y + screenSizeShape.size.height;
|
||||
}
|
||||
|
||||
options.update(item);
|
||||
const after = createItemSnapshot(item, options.screenSize);
|
||||
|
||||
logger.debug(
|
||||
|
||||
Reference in New Issue
Block a user