diff --git a/packages/old-import/src/import/collections/board-collection.ts b/packages/old-import/src/import/collections/board-collection.ts index 671ac10c9..18475ea53 100644 --- a/packages/old-import/src/import/collections/board-collection.ts +++ b/packages/old-import/src/import/collections/board-collection.ts @@ -81,7 +81,7 @@ export const createBoardInsertCollection = ( ...boardSizes.map((size) => ({ id: layoutMapping[size], boardId: mappedBoard.id, - columnCount: mapColumnCount(board.config, size), + columnCount: mapColumnCount(board.config.settings.customization.gridstack, size), breakpoint: mapBreakpoint(size), name: getBoardSizeName(size), })), @@ -94,7 +94,17 @@ export const createBoardInsertCollection = ( } logger.debug(`Added sections to board insert collection count=${insertCollection.sections.length}`); - const preparedItems = prepareItems({ apps, widgets }, appsMap, preparedSections, layoutMapping, mappedBoard.id); + const preparedItems = prepareItems( + { + apps, + widgets, + settings: board.config.settings, + }, + appsMap, + preparedSections, + layoutMapping, + mappedBoard.id, + ); preparedItems.forEach(({ layouts, ...item }) => { insertCollection.items.push(item); insertCollection.itemLayouts.push(...layouts); diff --git a/packages/old-import/src/mappers/map-column-count.ts b/packages/old-import/src/mappers/map-column-count.ts index 5aedc26cf..1cf457573 100644 --- a/packages/old-import/src/mappers/map-column-count.ts +++ b/packages/old-import/src/mappers/map-column-count.ts @@ -1,13 +1,16 @@ import type { BoardSize, OldmarrConfig } from "@homarr/old-schema"; -export const mapColumnCount = (old: OldmarrConfig, screenSize: BoardSize) => { +export const mapColumnCount = ( + gridstackSettings: OldmarrConfig["settings"]["customization"]["gridstack"], + screenSize: BoardSize, +) => { switch (screenSize) { case "lg": - return old.settings.customization.gridstack.columnCountLarge; + return gridstackSettings.columnCountLarge; case "md": - return old.settings.customization.gridstack.columnCountMedium; + return gridstackSettings.columnCountMedium; case "sm": - return old.settings.customization.gridstack.columnCountSmall; + return gridstackSettings.columnCountSmall; default: return 10; } diff --git a/packages/old-import/src/move-widgets-and-apps-merge.ts b/packages/old-import/src/move-widgets-and-apps-merge.ts index 2cb773a9f..ac3989172 100644 --- a/packages/old-import/src/move-widgets-and-apps-merge.ts +++ b/packages/old-import/src/move-widgets-and-apps-merge.ts @@ -135,7 +135,7 @@ const moveWidgetsAndAppsInLeftSidebar = ( offset: number, screenSize: BoardSize, ) => { - const columnCount = mapColumnCount(old, screenSize); + const columnCount = mapColumnCount(old.settings.customization.gridstack, screenSize); let requiredHeight = updateItems({ // This should work as the reference of the items did not change, only the array reference did items: [...old.widgets, ...old.apps], @@ -211,7 +211,7 @@ const moveWidgetsAndAppsInRightSidebar = ( offset: number, screenSize: BoardSize, ) => { - const columnCount = mapColumnCount(old, screenSize); + const columnCount = mapColumnCount(old.settings.customization.gridstack, screenSize); const xOffsetDelta = Math.max(columnCount - 2, 0); const requiredHeight = updateItems({ // This should work as the reference of the items did not change, only the array reference did diff --git a/packages/old-import/src/prepare/prepare-items.ts b/packages/old-import/src/prepare/prepare-items.ts index f9c9d55d4..bbb96f171 100644 --- a/packages/old-import/src/prepare/prepare-items.ts +++ b/packages/old-import/src/prepare/prepare-items.ts @@ -1,15 +1,101 @@ -import type { BoardSize, OldmarrConfig } from "@homarr/old-schema"; +import { logger } from "@homarr/log"; +import type { BoardSize, OldmarrApp, OldmarrConfig, OldmarrWidget, SizedShape } from "@homarr/old-schema"; +import { boardSizes } from "@homarr/old-schema"; +import type { GridAlgorithmItem } from "../../../api/src/router/board/grid-algorithm"; +import { generateResponsiveGridFor } from "../../../api/src/router/board/grid-algorithm"; +import { mapColumnCount } from "../mappers/map-column-count"; import { mapApp, mapWidget } from "../mappers/map-item"; export const prepareItems = ( - { apps, widgets }: Pick, + { apps, widgets, settings }: Pick, appsMap: Map, sectionMap: Map, layoutMap: Record, boardId: string, -) => - widgets +) => { + let localApps = apps; + let localWidgets = widgets; + + const incompleteSizes = boardSizes.filter((size) => + widgets + .map((widget) => widget.shape) + .concat(apps.map((app) => app.shape)) + .some((shape) => !shape[size]), + ); + + if (incompleteSizes.length > 0) { + logger.warn( + `Found items with incomplete sizes board=${boardId} count=${incompleteSizes.length} sizes=${incompleteSizes.join(", ")}\nHomarr will automatically generate missing sizes`, + ); + + incompleteSizes.forEach((size) => { + const columnCount = mapColumnCount(settings.customization.gridstack, size); + const previousSize = !incompleteSizes.includes("lg") ? "lg" : incompleteSizes.includes("sm") ? "md" : "sm"; + const previousWidth = mapColumnCount(settings.customization.gridstack, previousSize); + logger.info(`Generating missing size boardId=${boardId} from=${previousSize} to=${size}`); + + const items = widgets + .map((item) => mapItemForGridAlgorithm(item, previousSize)) + .concat(apps.map((item) => mapItemForGridAlgorithm(item, previousSize))); + + const distinctSectionIds = [...new Set(items.map((item) => item.sectionId))]; + distinctSectionIds.forEach((sectionId) => { + const { items: newItems } = generateResponsiveGridFor({ items, previousWidth, width: columnCount, sectionId }); + + localApps = localApps.map((app) => { + const item = newItems.find((item) => item.id === app.id); + if (!item) return app; + + return { + ...app, + shape: { + ...app.shape, + [size]: mapShapeFromGridAlgorithm(item), + }, + }; + }); + + localWidgets = localWidgets.map((widget) => { + const item = newItems.find((item) => item.id === widget.id); + if (!item) return widget; + + return { + ...widget, + shape: { + ...widget.shape, + [size]: mapShapeFromGridAlgorithm(item), + }, + }; + }); + }); + }); + } + + return localWidgets .map((widget) => mapWidget(widget, appsMap, sectionMap, layoutMap, boardId)) - .concat(apps.map((app) => mapApp(app, appsMap, sectionMap, layoutMap, boardId))) + .concat(localApps.map((app) => mapApp(app, appsMap, sectionMap, layoutMap, boardId))) .filter((widget) => widget !== null); +}; + +const mapItemForGridAlgorithm = (item: OldmarrApp | OldmarrWidget, size: BoardSize): GridAlgorithmItem => ({ + width: item.shape[size]?.size.width ?? 1, + height: item.shape[size]?.size.height ?? 1, + xOffset: item.shape[size]?.location.x ?? 0, + yOffset: item.shape[size]?.location.y ?? 0, + sectionId: item.area.type === "sidebar" ? item.area.properties.location : item.area.properties.id, + id: item.id, + type: "item", +}); + +const mapShapeFromGridAlgorithm = (item: GridAlgorithmItem) => + ({ + location: { + x: item.xOffset, + y: item.yOffset, + }, + size: { + width: item.width, + height: item.height, + }, + }) satisfies SizedShape; diff --git a/packages/old-schema/src/index.ts b/packages/old-schema/src/index.ts index 40528c09d..e79beb270 100644 --- a/packages/old-schema/src/index.ts +++ b/packages/old-schema/src/index.ts @@ -4,4 +4,4 @@ export type { OldmarrApp, OldmarrIntegrationType } from "./app"; export type { OldmarrWidget, OldmarrWidgetKind } from "./widget"; export { oldmarrWidgetKinds } from "./widget"; export { boardSizes, getBoardSizeName } from "./tile"; -export type { BoardSize } from "./tile"; +export type { BoardSize, SizedShape } from "./tile"; diff --git a/packages/old-schema/src/tile.ts b/packages/old-schema/src/tile.ts index 2331fbdbe..fc0740765 100644 --- a/packages/old-schema/src/tile.ts +++ b/packages/old-schema/src/tile.ts @@ -56,6 +56,8 @@ export const tileBaseSchema = z.object({ shape: shapeSchema, }); +export type SizedShape = z.infer; + export const boardSizes = objectKeys(shapeSchema._def.shape()); export type BoardSize = (typeof boardSizes)[number];