From 7a8529b841594bc31df18c4e53413a092d58a093 Mon Sep 17 00:00:00 2001 From: Dan Ditomaso Date: Sun, 14 Jun 2026 20:03:50 -0400 Subject: [PATCH] =?UTF-8?q?Revert=20"fix(settings):=20fix=20unsaved=20chan?= =?UTF-8?q?ge=20detection=20for=20non-admin-message=20con=E2=80=A6"=20(#11?= =?UTF-8?q?00)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 87043f35cbe3d26c3183074e3215c8501841ea43. --- .../Settings/Security/Security.tsx | 17 +------ .../core/stores/deviceStore/changeRegistry.ts | 51 ++----------------- .../stores/deviceStore/deviceStore.mock.ts | 2 - .../web/src/core/stores/deviceStore/index.ts | 22 -------- .../web/src/core/utils/deepCompareConfig.ts | 7 --- packages/web/src/pages/Settings/index.tsx | 16 +++--- 6 files changed, 13 insertions(+), 102 deletions(-) diff --git a/packages/web/src/components/PageComponents/Settings/Security/Security.tsx b/packages/web/src/components/PageComponents/Settings/Security/Security.tsx index 873941a6..b5fdf3f1 100644 --- a/packages/web/src/components/PageComponents/Settings/Security/Security.tsx +++ b/packages/web/src/components/PageComponents/Settings/Security/Security.tsx @@ -9,7 +9,7 @@ import { PkiRegenerateDialog } from "@components/Dialog/PkiRegenerateDialog.tsx" import { createZodResolver } from "@components/Form/createZodResolver.ts"; import { DynamicForm, type DynamicFormFormInit } from "@components/Form/DynamicForm.tsx"; import { useDevice } from "@core/stores"; -import { deepCompareConfig, normalizeBytes } from "@core/utils/deepCompareConfig.ts"; +import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts"; import { getX25519PrivateKey, getX25519PublicKey } from "@core/utils/x25519.ts"; import { fromByteArray, toByteArray } from "base64-js"; import { useEffect, useState } from "react"; @@ -87,20 +87,7 @@ export const Security = ({ onFormInit }: SecurityConfigProps) => { ], }; - // Normalize empty byte arrays -> undefined for comparison so - // empty base64 strings from the form match undefined/empty fields - // in the existing config and allow removeChange to work. - const normalizeSecurity = (s: ParsedSecurity | undefined) => { - if (!s) return s; - return { - ...s, - privateKey: normalizeBytes(s.privateKey), - publicKey: normalizeBytes(s.publicKey), - adminKey: s.adminKey?.map((b) => normalizeBytes(b)) as unknown, - } as ParsedSecurity; - }; - - if (deepCompareConfig(normalizeSecurity(config.security), normalizeSecurity(payload), true)) { + if (deepCompareConfig(config.security, payload, true)) { removeChange({ type: "config", variant: "security" }); return; } diff --git a/packages/web/src/core/stores/deviceStore/changeRegistry.ts b/packages/web/src/core/stores/deviceStore/changeRegistry.ts index 2af79bb3..cdfcc0b6 100644 --- a/packages/web/src/core/stores/deviceStore/changeRegistry.ts +++ b/packages/web/src/core/stores/deviceStore/changeRegistry.ts @@ -1,17 +1,15 @@ import type { Types } from "@meshtastic/core"; // Config type discriminators -export type ValidRadioConfigType = "lora" | "security"; - -export type ValidDeviceConfigType = +export type ValidConfigType = | "device" | "position" | "power" | "network" | "display" - | "bluetooth"; - -export type ValidConfigType = ValidRadioConfigType | ValidDeviceConfigType; + | "lora" + | "bluetooth" + | "security"; export type ValidModuleConfigType = | "mqtt" @@ -159,47 +157,6 @@ export function getConfigChangeCount(registry: ChangeRegistry): number { return count; } -/** - * Get count of radio config changes - */ -export function getRadioConfigChangeCount(registry: ChangeRegistry): number { - let count = 0; - for (const keyStr of registry.changes.keys()) { - const key = deserializeKey(keyStr); - if (key.type === "config" && (key.variant === "lora" || key.variant === "security")) { - count++; - } - } - // Channel is displayed under Radio section in UI, so include channel changes in the count - count += getChannelChangeCount(registry); - return count; -} - -/** - * Get count of device config changes - */ -export function getDeviceConfigChangeCount(registry: ChangeRegistry): number { - let count = 0; - for (const keyStr of registry.changes.keys()) { - const key = deserializeKey(keyStr); - if ( - key.type === "config" && - (key.variant === "device" || - key.variant === "position" || - key.variant === "power" || - key.variant === "network" || - key.variant === "display" || - key.variant === "bluetooth") - ) { - count++; - } - } - if (hasUserChange(registry)) { - count++; - } - return count; -} - /** * Get count of module config changes */ diff --git a/packages/web/src/core/stores/deviceStore/deviceStore.mock.ts b/packages/web/src/core/stores/deviceStore/deviceStore.mock.ts index e36deafe..eb67f7bf 100644 --- a/packages/web/src/core/stores/deviceStore/deviceStore.mock.ts +++ b/packages/web/src/core/stores/deviceStore/deviceStore.mock.ts @@ -91,8 +91,6 @@ export const mockDeviceStore: Device = { hasChannelChange: vi.fn().mockReturnValue(false), hasUserChange: vi.fn().mockReturnValue(false), getConfigChangeCount: vi.fn().mockReturnValue(0), - getRadioConfigChangeCount: vi.fn().mockReturnValue(0), - getDeviceConfigChangeCount: vi.fn().mockReturnValue(0), getModuleConfigChangeCount: vi.fn().mockReturnValue(0), getChannelChangeCount: vi.fn().mockReturnValue(0), getAllConfigChanges: vi.fn().mockReturnValue([]), diff --git a/packages/web/src/core/stores/deviceStore/index.ts b/packages/web/src/core/stores/deviceStore/index.ts index d8f52c93..a226d7af 100644 --- a/packages/web/src/core/stores/deviceStore/index.ts +++ b/packages/web/src/core/stores/deviceStore/index.ts @@ -15,8 +15,6 @@ import { getAllModuleConfigChanges, getChannelChangeCount, getConfigChangeCount, - getRadioConfigChangeCount, - getDeviceConfigChangeCount, getModuleConfigChangeCount, hasChannelChange, hasConfigChange, @@ -121,8 +119,6 @@ export interface Device extends DeviceData { hasChannelChange: (index: Types.ChannelNumber) => boolean; hasUserChange: () => boolean; getConfigChangeCount: () => number; - getRadioConfigChangeCount: () => number; - getDeviceConfigChangeCount: () => number; getModuleConfigChangeCount: () => number; getChannelChangeCount: () => number; getAllConfigChanges: () => Protobuf.Config.Config[]; @@ -821,24 +817,6 @@ function deviceFactory( return getConfigChangeCount(device.changeRegistry); }, - getRadioConfigChangeCount: () => { - const device = get().devices.get(id); - if (!device) { - return 0; - } - - return getRadioConfigChangeCount(device.changeRegistry); - }, - - getDeviceConfigChangeCount: () => { - const device = get().devices.get(id); - if (!device) { - return 0; - } - - return getDeviceConfigChangeCount(device.changeRegistry); - }, - getModuleConfigChangeCount: () => { const device = get().devices.get(id); if (!device) { diff --git a/packages/web/src/core/utils/deepCompareConfig.ts b/packages/web/src/core/utils/deepCompareConfig.ts index 5e12c59d..cc9bcdb8 100644 --- a/packages/web/src/core/utils/deepCompareConfig.ts +++ b/packages/web/src/core/utils/deepCompareConfig.ts @@ -6,13 +6,6 @@ function isUint8Array(v: unknown): v is Uint8Array { return v instanceof Uint8Array; } -export function normalizeBytes(value: unknown): unknown { - if (value instanceof Uint8Array && value.byteLength === 0) { - return undefined; - } - return value; -} - function bytesEqual(a: Uint8Array, b: Uint8Array): boolean { if (a.byteLength !== b.byteLength) { return false; diff --git a/packages/web/src/pages/Settings/index.tsx b/packages/web/src/pages/Settings/index.tsx index 1becf159..2df577ed 100644 --- a/packages/web/src/pages/Settings/index.tsx +++ b/packages/web/src/pages/Settings/index.tsx @@ -36,8 +36,6 @@ const ConfigPage = () => { setModuleConfig, addChannel, getConfigChangeCount, - getRadioConfigChangeCount, - getDeviceConfigChangeCount, getModuleConfigChangeCount, getChannelChangeCount, getAdminMessageChangeCount, @@ -52,9 +50,9 @@ const ConfigPage = () => { const routerState = useRouterState(); const { t } = useTranslation("config"); - const radioConfigChangeCount = getRadioConfigChangeCount(); - const deviceConfigChangeCount = getDeviceConfigChangeCount(); + const configChangeCount = getConfigChangeCount(); const moduleConfigChangeCount = getModuleConfigChangeCount(); + const channelChangeCount = getChannelChangeCount(); const adminMessageChangeCount = getAdminMessageChangeCount(); const sections = useMemo( @@ -64,7 +62,7 @@ const ConfigPage = () => { route: radioRoute, label: t("navigation.radioConfig"), icon: RadioTowerIcon, - changeCount: radioConfigChangeCount, + changeCount: configChangeCount, component: RadioConfig, }, { @@ -72,7 +70,7 @@ const ConfigPage = () => { route: deviceRoute, label: t("navigation.deviceConfig"), icon: RouterIcon, - changeCount: deviceConfigChangeCount, + changeCount: moduleConfigChangeCount, component: DeviceConfig, }, { @@ -80,11 +78,11 @@ const ConfigPage = () => { route: moduleRoute, label: t("navigation.moduleConfig"), icon: LayersIcon, - changeCount: moduleConfigChangeCount, + changeCount: channelChangeCount, component: ModuleConfig, }, ], - [t, radioConfigChangeCount, deviceConfigChangeCount, moduleConfigChangeCount], + [t, configChangeCount, moduleConfigChangeCount, channelChangeCount], ); const activeSection = @@ -265,7 +263,7 @@ const ConfigPage = () => { getModuleConfigChangeCount() > 0 || getChannelChangeCount() > 0 || adminMessageChangeCount > 0; - const hasPending = hasDrafts; + const hasPending = hasDrafts || rhfState.isDirty; const buttonOpacity = hasPending ? "opacity-100" : "opacity-0"; const saveDisabled = isSaving || !rhfState.isValid || !hasPending;