diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index b6483c363..fa2255c40 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -12,6 +12,7 @@ import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import { useResize } from '../../../../hooks/use-resize'; import { AppType } from '../../../../types/app'; +import { AreaType } from '../../../../types/area'; import { IWidget } from '../../../../widgets/widgets'; import { useEditModeStore } from '../../Views/useEditModeStore'; import { initializeGridstack } from './init-gridstack'; @@ -51,7 +52,7 @@ export const useGridstack = ( ? x.area.properties.location === areaId : x.area.properties.id === areaId) ) ?? [], - [configVersion] + [configVersion, config?.apps.length] ); const widgets = useMemo(() => { if (!config) return []; @@ -62,7 +63,7 @@ export const useGridstack = ( ? w.area.properties.location === areaId : w.area.properties.id === areaId) ); - }, [configVersion]); + }, [configVersion, config?.widgets.length]); // define items in itemRefs for easy access and reference to items if (Object.keys(itemRefs.current).length !== items.length + (widgets ?? []).length) { @@ -137,58 +138,90 @@ export const useGridstack = ( if (!itemType || !itemId) return; // Updates the config and defines the new position and wrapper of the item - updateConfig(configName, (previous) => { - const currentItem = - itemType === 'app' - ? previous.apps.find((x) => x.id === itemId) - : previous.widgets.find((x) => x.id === itemId); - if (!currentItem) return previous; + updateConfig( + configName, + (previous) => { + const currentItem = + itemType === 'app' + ? previous.apps.find((x) => x.id === itemId) + : previous.widgets.find((x) => x.id === itemId); + if (!currentItem) return previous; - if (areaType === 'sidebar') { - currentItem.area = { - type: areaType, - properties: { - location: areaId as 'right' | 'left', + if (areaType === 'sidebar') { + currentItem.area = { + type: areaType, + properties: { + location: areaId as 'right' | 'left', + }, + }; + } else { + currentItem.area = { + type: areaType, + properties: { + id: areaId, + }, + }; + } + + currentItem.shape = { + location: { + x: addedNode.x ?? currentItem.shape.location.x, + y: addedNode.y ?? currentItem.shape.location.y, + }, + size: { + width: addedNode.w ?? currentItem.shape.size.width, + height: addedNode.h ?? currentItem.shape.size.height, }, }; - } else { - currentItem.area = { - type: areaType, - properties: { - id: areaId, - }, - }; - } - currentItem.shape = { - location: { - x: addedNode.x ?? currentItem.shape.location.x, - y: addedNode.y ?? currentItem.shape.location.y, - }, - size: { - width: addedNode.w ?? currentItem.shape.size.width, - height: addedNode.h ?? currentItem.shape.size.height, - }, - }; + if (itemType === 'app') { + return { + ...previous, + apps: [ + ...previous.apps.filter((x) => x.id !== itemId), + { ...(currentItem as AppType) }, + ], + }; + } - if (itemType === 'app') { return { ...previous, - apps: [ - ...previous.apps.filter((x) => x.id !== itemId), - { ...(currentItem as AppType) }, + widgets: [ + ...previous.widgets.filter((x) => x.id !== itemId), + { ...(currentItem as IWidget) }, ], }; - } + }, + (prev, curr) => { + const isApp = itemType === 'app'; - return { - ...previous, - widgets: [ - ...previous.widgets.filter((x) => x.id !== itemId), - { ...(currentItem as IWidget) }, - ], - }; - }); + if (isApp) { + const currItem = curr.apps.find((x) => x.id === itemId); + const prevItem = prev.apps.find((x) => x.id === itemId); + if (!currItem || !prevItem) return false; + + return ( + currItem.area.type !== prevItem.area.type || + Object.entries(currItem.area.properties).some( + ([key, value]) => + prevItem.area.properties[key as keyof AreaType['properties']] !== value + ) + ); + } + + const currItem = curr.widgets.find((x) => x.id === itemId); + const prevItem = prev.widgets.find((x) => x.id === itemId); + if (!currItem || !prevItem) return false; + + return ( + currItem.area.type !== prevItem.area.type || + Object.entries(currItem.area.properties).some( + ([key, value]) => + prevItem.area.properties[key as keyof AreaType['properties']] !== value + ) + ); + } + ); } : () => {}; diff --git a/src/config/store.ts b/src/config/store.ts index 04342245e..6e5acb0e8 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -20,6 +20,8 @@ export const useConfigStore = create((set, get) => ({ const { configs } = get(); const currentConfig = configs.find((x) => x.value.configProperties.name === name); if (!currentConfig) return; + // copies the value of currentConfig and creates a non reference object named previousConfig + const previousConfig: ConfigType = JSON.parse(JSON.stringify(currentConfig.value)); // TODO: update config on server const updatedConfig = updateCallback(currentConfig.value); @@ -31,7 +33,11 @@ export const useConfigStore = create((set, get) => ({ ], })); - if (shouldRegenerateGridstack) { + if ( + (typeof shouldRegenerateGridstack === 'boolean' && shouldRegenerateGridstack) || + (typeof shouldRegenerateGridstack === 'function' && + shouldRegenerateGridstack(previousConfig, updatedConfig)) + ) { currentConfig.increaseVersion(); } }, @@ -43,6 +49,8 @@ interface UseConfigStoreType { updateConfig: ( name: string, updateCallback: (previous: ConfigType) => ConfigType, - shouldRegenerateGridstack?: boolean + shouldRegenerateGridstace?: + | boolean + | ((previousConfig: ConfigType, currentConfig: ConfigType) => boolean) ) => Promise; }