diff --git a/package.json b/package.json index f71e096c5..3694c00e0 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "d3-selection": "~3.0.0", "d3-zoom": "~3.0.0", "deep-diff": "~1.0.2", + "deep-object-diff": "~1.1.9", "eslint": "~8.57.0", "eslint-config-prettier": "~9.0.0", "eslint-config-react": "~1.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b606b9bb..cf0ba189f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ devDependencies: deep-diff: specifier: ~1.0.2 version: 1.0.2 + deep-object-diff: + specifier: ~1.1.9 + version: 1.1.9 eslint: specifier: ~8.57.0 version: 8.57.0 @@ -3213,6 +3216,10 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /deep-object-diff@1.1.9: + resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} + dev: true + /default-browser-id@3.0.0: resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} engines: {node: '>=12'} diff --git a/src/components/device-page/DeviceSettings.tsx b/src/components/device-page/DeviceSettings.tsx index 94fe2f768..b843b920e 100644 --- a/src/components/device-page/DeviceSettings.tsx +++ b/src/components/device-page/DeviceSettings.tsx @@ -5,6 +5,7 @@ import { ISubmitEvent, UiSchema } from '@rjsf/core'; import { DescriptionField, TitleField } from '../../i18n/rjsf-translation-fields'; import merge from 'lodash/merge'; import { DeviceSettingsProps, DeviceSettingsState, Form, ParamValue } from './settings'; +import { computeSettingsDiff } from '../../utils'; import { ReadTheDocsInfo } from '../ReadTheDocsInfo'; const genericUiSchema: UiSchema = { @@ -35,14 +36,12 @@ export class DeviceSettings extends Component): void => { - const { formData } = params; - this.setState({ updatedDeviceConfig: formData }); - }; updateConfig = async (params: ISubmitEvent): Promise => { const { formData } = params; + const { data } = this.getSchemaAndConfig(); const { setDeviceOptions, device } = this.props; - await setDeviceOptions(device.ieee_address, formData as Record); + const diff = computeSettingsDiff(data, formData); + await setDeviceOptions(device.ieee_address, diff as Record); this.setState({ updatedDeviceConfig: {} }); }; @@ -64,7 +63,6 @@ export class DeviceSettings extends Component diff --git a/src/components/settings-page/index.tsx b/src/components/settings-page/index.tsx index a969be580..1b08a9f64 100644 --- a/src/components/settings-page/index.tsx +++ b/src/components/settings-page/index.tsx @@ -15,7 +15,7 @@ import { WithTranslation, withTranslation } from 'react-i18next'; import { DescriptionField, TitleField } from '../../i18n/rjsf-translation-fields'; import { Stats } from './stats'; import frontendPackageJson from './../../../package.json'; -import { formatDate } from '../../utils'; +import { computeSettingsDiff, formatDate } from '../../utils'; import { saveAs } from 'file-saver'; import Spinner from '../spinner'; import { TranslatedImageLocaliser } from './image-localiser'; @@ -296,10 +296,11 @@ class SettingsPage extends Component< const { formData } = e; const { updateBridgeConfig } = this.props; const { keyName } = this.state; + const diff = computeSettingsDiff(this.getSettingsInfo().currentConfig, formData); if (keyName === ROOT_KEY_NAME) { - updateBridgeConfig(formData); + updateBridgeConfig(diff); } else { - updateBridgeConfig({ [keyName]: formData }); + updateBridgeConfig({ [keyName]: diff }); } }; diff --git a/src/utils.ts b/src/utils.ts index 1c95da154..298f06da7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,6 +6,7 @@ import { saveAs } from 'file-saver'; import local from 'store2'; import debounce from 'lodash/debounce'; +import { diff } from 'deep-object-diff'; export const genDeviceDetailsLink = (deviceIdentifier: string | number): string => (`/device/${deviceIdentifier}`); @@ -133,6 +134,25 @@ export const download = async (data: Record, filename: string) }); } +export const computeSettingsDiff = (before: object, after: object) => { + const diffObj = diff(before, after); + + // diff converts arrays to objects, set original array back here + const setArrays = (localAfter: object, localDiff: object): void => { + for (const [key, value] of Object.entries(localDiff)) { + if (typeof value === 'object') { + if (Array.isArray(localAfter[key])) { + localDiff[key] = localAfter[key]; + } else { + setArrays(localAfter[key], value); + } + } + } + } + setArrays(after, diffObj); + return diffObj; +} + export const sanitizeZ2MDeviceName = (deviceName?: string): string => deviceName ? deviceName.replace(/:|\s|\//g, "-") : "NA"; export const getEndpoints = (obj: Device | Group): Endpoint[] => {