Revert "fix(settings): fix unsaved change detection for non-admin-message con…" (#1100)

This reverts commit 87043f35cb.
This commit is contained in:
Dan Ditomaso
2026-06-14 20:03:50 -04:00
committed by GitHub
parent 87043f35cb
commit 7a8529b841
6 changed files with 13 additions and 102 deletions

View File

@@ -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;
}

View File

@@ -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
*/

View File

@@ -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([]),

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;