From ef24370a8c56e079cdfcb1b2494f9fd9254bf505 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Thu, 26 Dec 2024 08:58:06 +0100 Subject: [PATCH] refactor: revert assignment of oldmarr widget mapping (#1780) --- packages/common/src/types.ts | 5 ++ packages/old-import/src/import-items.ts | 2 +- packages/old-import/src/mappers/map-item.ts | 6 +- .../src/widgets/definitions/index.ts | 45 ++++++------- packages/old-import/src/widgets/options.ts | 65 +++++++++++-------- 5 files changed, 73 insertions(+), 50 deletions(-) diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index be56092f5..1dcd019bc 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -11,3 +11,8 @@ export type RemoveReadonly = { }; export type MaybeArray = T | T[]; +export type Inverse = { + [Key in keyof T as T[Key]]: Key; +}; + +type Invertible = Record; diff --git a/packages/old-import/src/import-items.ts b/packages/old-import/src/import-items.ts index a3fa7dbaf..ae4a6fad6 100644 --- a/packages/old-import/src/import-items.ts +++ b/packages/old-import/src/import-items.ts @@ -55,7 +55,7 @@ export const insertItemsAsync = async ( xOffset: screenSizeShape.location.x, yOffset: screenSizeShape.location.y, kind, - options: SuperJSON.stringify(mapOptions(kind, widget.properties, appsMap)), + options: SuperJSON.stringify(mapOptions(widget.type, widget.properties, appsMap)), }); logger.debug(`Inserted widget id=${widget.id} sectionId=${sectionId}`); diff --git a/packages/old-import/src/mappers/map-item.ts b/packages/old-import/src/mappers/map-item.ts index 1262e0e4d..2b11118be 100644 --- a/packages/old-import/src/mappers/map-item.ts +++ b/packages/old-import/src/mappers/map-item.ts @@ -83,7 +83,11 @@ export const mapWidget = ( yOffset: shapeForSize.location.y, kind, options: SuperJSON.stringify( - mapOptions(kind, widget.properties, new Map([...appsMap.entries()].map(([key, value]) => [key, value.id]))), + mapOptions( + widget.type, + widget.properties, + new Map([...appsMap.entries()].map(([key, value]) => [key, value.id])), + ), ), }; }; diff --git a/packages/old-import/src/widgets/definitions/index.ts b/packages/old-import/src/widgets/definitions/index.ts index 5aaa9684f..2a11eb1ac 100644 --- a/packages/old-import/src/widgets/definitions/index.ts +++ b/packages/old-import/src/widgets/definitions/index.ts @@ -1,4 +1,5 @@ import { objectEntries } from "@homarr/common"; +import type { Inverse } from "@homarr/common/types"; import type { WidgetKind } from "@homarr/definitions"; import type { OldmarrBookmarkDefinition } from "./bookmark"; @@ -49,32 +50,32 @@ export type OldmarrWidgetDefinitions = | OldmarrMediaTranscodingDefinition; export const widgetKindMapping = { - app: null, // In oldmarr apps were not widgets - clock: "date", + date: "clock", calendar: "calendar", - downloads: "torrents-status", + "torrents-status": "downloads", weather: "weather", - rssFeed: "rss", - video: "video-stream", + rss: "rssFeed", + "video-stream": "video", iframe: "iframe", - mediaServer: "media-server", - dnsHoleSummary: "dns-hole-summary", - dnsHoleControls: "dns-hole-controls", + "media-server": "mediaServer", + "dns-hole-summary": "dnsHoleSummary", + "dns-hole-controls": "dnsHoleControls", notebook: "notebook", - "smartHome-entityState": "smart-home/entity-state", - "smartHome-executeAutomation": "smart-home/trigger-automation", - "mediaRequests-requestList": "media-requests-list", - "mediaRequests-requestStats": "media-requests-stats", - indexerManager: "indexer-manager", - bookmarks: "bookmark", - healthMonitoring: "health-monitoring", - mediaTranscoding: "media-transcoding", -} satisfies Record; -// Use null for widgets that did not exist in oldmarr -// TODO: revert assignment so that only old widgets are needed in the object, -// this can be done ones all widgets are implemented + "smart-home/entity-state": "smartHome-entityState", + "smart-home/trigger-automation": "smartHome-executeAutomation", + "media-requests-list": "mediaRequests-requestList", + "media-requests-stats": "mediaRequests-requestStats", + "indexer-manager": "indexerManager", + bookmark: "bookmarks", + "health-monitoring": "healthMonitoring", + dashdot: "healthMonitoring", + "media-transcoding": "mediaTranscoding", + dlspeed: null, + usenet: "downloads", +} satisfies Record; export type WidgetMapping = typeof widgetKindMapping; +export type InversedWidgetMapping = Inverse>; -export const mapKind = (kind: OldmarrWidgetDefinitions["id"]): WidgetKind | undefined => - objectEntries(widgetKindMapping).find(([_, value]) => value === kind)?.[0]; +export const mapKind = (kind: OldmarrWidgetDefinitions["id"]): keyof InversedWidgetMapping | null => + objectEntries(widgetKindMapping).find(([key]) => key === kind)?.[1] ?? null; diff --git a/packages/old-import/src/widgets/options.ts b/packages/old-import/src/widgets/options.ts index 078a4ccd5..d969e62ee 100644 --- a/packages/old-import/src/widgets/options.ts +++ b/packages/old-import/src/widgets/options.ts @@ -1,18 +1,18 @@ import { objectEntries } from "@homarr/common"; -import type { WidgetKind } from "@homarr/definitions"; import { logger } from "@homarr/log"; import type { WidgetComponentProps } from "../../../widgets/src/definition"; -import type { OldmarrWidgetDefinitions, WidgetMapping } from "./definitions"; +import { mapKind } from "./definitions"; +import type { InversedWidgetMapping, OldmarrWidgetDefinitions, WidgetMapping } from "./definitions"; // This type enforces, that for all widget mappings there is a corresponding option mapping, // each option of newmarr can be mapped from the value of the oldmarr options type OptionMapping = { - [WidgetKey in keyof WidgetMapping]: WidgetMapping[WidgetKey] extends null + [WidgetKey in keyof InversedWidgetMapping]: InversedWidgetMapping[WidgetKey] extends null ? null : { [OptionsKey in keyof WidgetComponentProps["options"]]: ( - oldOptions: Extract["options"], + oldOptions: Extract["options"], appsMap: Map, ) => WidgetComponentProps["options"][OptionsKey] | undefined; }; @@ -55,12 +55,16 @@ const optionMapping: OptionMapping = { useCustomTimezone: () => true, }, downloads: { - activeTorrentThreshold: (oldOptions) => oldOptions.speedLimitOfActiveTorrents, - applyFilterToRatio: (oldOptions) => oldOptions.displayRatioWithFilter, - categoryFilter: (oldOptions) => oldOptions.labelFilter, - filterIsWhitelist: (oldOptions) => oldOptions.labelFilterIsWhitelist, - enableRowSorting: (oldOptions) => oldOptions.rowSorting, - showCompletedTorrent: (oldOptions) => oldOptions.displayCompletedTorrents, + activeTorrentThreshold: (oldOptions) => + "speedLimitOfActiveTorrents" in oldOptions ? oldOptions.speedLimitOfActiveTorrents : undefined, + applyFilterToRatio: (oldOptions) => + "displayRatioWithFilter" in oldOptions ? oldOptions.displayRatioWithFilter : undefined, + categoryFilter: (oldOptions) => ("labelFilter" in oldOptions ? oldOptions.labelFilter : undefined), + filterIsWhitelist: (oldOptions) => + "labelFilterIsWhitelist" in oldOptions ? oldOptions.labelFilterIsWhitelist : undefined, + enableRowSorting: (oldOptions) => ("rowSorting" in oldOptions ? oldOptions.rowSorting : undefined), + showCompletedTorrent: (oldOptions) => + "displayCompletedTorrents" in oldOptions ? oldOptions.displayCompletedTorrents : undefined, columns: () => ["integration", "name", "progress", "time", "actions"], defaultSort: () => "type", descendingDefaultSort: () => false, @@ -124,45 +128,54 @@ const optionMapping: OptionMapping = { openIndexerSiteInNewTab: (oldOptions) => oldOptions.openIndexerSiteInNewTab, }, healthMonitoring: { - cpu: (oldOptions) => oldOptions.cpu, - memory: (oldOptions) => oldOptions.memory, - fahrenheit: (oldOptions) => oldOptions.fahrenheit, - fileSystem: (oldOptions) => oldOptions.fileSystem, + cpu: (oldOptions) => + "cpu" in oldOptions + ? oldOptions.cpu + : oldOptions.graphsOrder.some((graph) => graph.key === "cpu" && graph.subValues.enabled), + memory: (oldOptions) => + "memory" in oldOptions + ? oldOptions.memory + : oldOptions.graphsOrder.some((graph) => graph.key === "ram" && graph.subValues.enabled), + fahrenheit: (oldOptions) => ("fahrenheit" in oldOptions ? oldOptions.fahrenheit : undefined), + fileSystem: (oldOptions) => + "fileSystem" in oldOptions + ? oldOptions.fileSystem + : oldOptions.graphsOrder.some((graph) => graph.key === "storage" && graph.subValues.enabled), }, mediaTranscoding: { defaultView: (oldOptions) => oldOptions.defaultView, queuePageSize: (oldOptions) => oldOptions.queuePageSize, }, - app: null, }; /** * Maps the oldmarr options to the newmarr options - * @param kind item kind to map + * @param type old widget type * @param oldOptions oldmarr options for this item * @param appsMap map of old app ids to new app ids * @returns newmarr options for this item or null if the item did not exist in oldmarr */ -export const mapOptions = ( - kind: K, - oldOptions: Extract["options"], +export const mapOptions = ( + type: K, + oldOptions: Extract["options"], appsMap: Map, ) => { - logger.debug(`Mapping old homarr options for widget kind=${kind} options=${JSON.stringify(oldOptions)}`); - if (optionMapping[kind] === null) { + logger.debug(`Mapping old homarr options for widget type=${type} options=${JSON.stringify(oldOptions)}`); + const kind = mapKind(type); + if (!kind) { return null; } const mapping = optionMapping[kind]; return objectEntries(mapping).reduce( - (acc, [key, value]) => { - const newValue = value(oldOptions as never, appsMap); - logger.debug(`Mapping old homarr option kind=${kind} key=${key as string} newValue=${newValue as string}`); + (acc, [key, value]: [string, (oldOptions: Record, appsMap: Map) => unknown]) => { + const newValue = value(oldOptions, appsMap); + logger.debug(`Mapping old homarr option kind=${kind} key=${key} newValue=${newValue as string}`); if (newValue !== undefined) { - acc[key as string] = newValue; + acc[key] = newValue; } return acc; }, {} as Record, - ) as WidgetComponentProps["options"]; + ) as WidgetComponentProps>["options"]; };