Skip to content

Commit c18fdcf

Browse files
committed
chore: drop #1062 (unsaved-change-detection) to match upstream revert
#1062 was merged to main by accident (per @danditomaso) and is being reverted. Reverse-applied its diff here via 3-way so #1097 stays consistent with where main is headed, while keeping the feature changes layered on the same files (deviceStore/changeRegistry). Build + 131 tests + lint + format green.
1 parent bdb8d58 commit c18fdcf

6 files changed

Lines changed: 13 additions & 102 deletions

File tree

apps/web/src/components/PageComponents/Settings/Security/Security.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { PkiRegenerateDialog } from "@components/Dialog/PkiRegenerateDialog.tsx"
99
import { createZodResolver } from "@components/Form/createZodResolver.ts";
1010
import { DynamicForm, type DynamicFormFormInit } from "@components/Form/DynamicForm.tsx";
1111
import { useDevice } from "@core/stores";
12-
import { deepCompareConfig, normalizeBytes } from "@core/utils/deepCompareConfig.ts";
12+
import { deepCompareConfig } from "@core/utils/deepCompareConfig.ts";
1313
import { getX25519PrivateKey, getX25519PublicKey } from "@core/utils/x25519.ts";
1414
import { fromByteArray, toByteArray } from "base64-js";
1515
import { useEffect, useState } from "react";
@@ -87,20 +87,7 @@ export const Security = ({ onFormInit }: SecurityConfigProps) => {
8787
],
8888
};
8989

90-
// Normalize empty byte arrays -> undefined for comparison so
91-
// empty base64 strings from the form match undefined/empty fields
92-
// in the existing config and allow removeChange to work.
93-
const normalizeSecurity = (s: ParsedSecurity | undefined) => {
94-
if (!s) return s;
95-
return {
96-
...s,
97-
privateKey: normalizeBytes(s.privateKey),
98-
publicKey: normalizeBytes(s.publicKey),
99-
adminKey: s.adminKey?.map((b) => normalizeBytes(b)) as unknown,
100-
} as ParsedSecurity;
101-
};
102-
103-
if (deepCompareConfig(normalizeSecurity(config.security), normalizeSecurity(payload), true)) {
90+
if (deepCompareConfig(config.security, payload, true)) {
10491
removeChange({ type: "config", variant: "security" });
10592
return;
10693
}

apps/web/src/core/stores/deviceStore/changeRegistry.ts

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import type { Types } from "@meshtastic/core";
22

33
// Config type discriminators
4-
export type ValidRadioConfigType = "lora" | "security";
5-
6-
export type ValidDeviceConfigType =
4+
export type ValidConfigType =
75
| "device"
86
| "position"
97
| "power"
108
| "network"
119
| "display"
12-
| "bluetooth";
13-
14-
export type ValidConfigType = ValidRadioConfigType | ValidDeviceConfigType;
10+
| "lora"
11+
| "bluetooth"
12+
| "security";
1513

1614
export type ValidModuleConfigType =
1715
| "mqtt"
@@ -163,47 +161,6 @@ export function getConfigChangeCount(registry: ChangeRegistry): number {
163161
return count;
164162
}
165163

166-
/**
167-
* Get count of radio config changes
168-
*/
169-
export function getRadioConfigChangeCount(registry: ChangeRegistry): number {
170-
let count = 0;
171-
for (const keyStr of registry.changes.keys()) {
172-
const key = deserializeKey(keyStr);
173-
if (key.type === "config" && (key.variant === "lora" || key.variant === "security")) {
174-
count++;
175-
}
176-
}
177-
// Channel is displayed under Radio section in UI, so include channel changes in the count
178-
count += getChannelChangeCount(registry);
179-
return count;
180-
}
181-
182-
/**
183-
* Get count of device config changes
184-
*/
185-
export function getDeviceConfigChangeCount(registry: ChangeRegistry): number {
186-
let count = 0;
187-
for (const keyStr of registry.changes.keys()) {
188-
const key = deserializeKey(keyStr);
189-
if (
190-
key.type === "config" &&
191-
(key.variant === "device" ||
192-
key.variant === "position" ||
193-
key.variant === "power" ||
194-
key.variant === "network" ||
195-
key.variant === "display" ||
196-
key.variant === "bluetooth")
197-
) {
198-
count++;
199-
}
200-
}
201-
if (hasUserChange(registry)) {
202-
count++;
203-
}
204-
return count;
205-
}
206-
207164
/**
208165
* Get count of module config changes
209166
*/

apps/web/src/core/stores/deviceStore/deviceStore.mock.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ export const mockDeviceStore: Device = {
9191
hasChannelChange: vi.fn().mockReturnValue(false),
9292
hasUserChange: vi.fn().mockReturnValue(false),
9393
getConfigChangeCount: vi.fn().mockReturnValue(0),
94-
getRadioConfigChangeCount: vi.fn().mockReturnValue(0),
95-
getDeviceConfigChangeCount: vi.fn().mockReturnValue(0),
9694
getModuleConfigChangeCount: vi.fn().mockReturnValue(0),
9795
getChannelChangeCount: vi.fn().mockReturnValue(0),
9896
getAllConfigChanges: vi.fn().mockReturnValue([]),

apps/web/src/core/stores/deviceStore/index.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import {
1515
getAllModuleConfigChanges,
1616
getChannelChangeCount,
1717
getConfigChangeCount,
18-
getRadioConfigChangeCount,
19-
getDeviceConfigChangeCount,
2018
getModuleConfigChangeCount,
2119
hasChannelChange,
2220
hasConfigChange,
@@ -121,8 +119,6 @@ export interface Device extends DeviceData {
121119
hasChannelChange: (index: Types.ChannelNumber) => boolean;
122120
hasUserChange: () => boolean;
123121
getConfigChangeCount: () => number;
124-
getRadioConfigChangeCount: () => number;
125-
getDeviceConfigChangeCount: () => number;
126122
getModuleConfigChangeCount: () => number;
127123
getChannelChangeCount: () => number;
128124
getAllConfigChanges: () => Protobuf.Config.Config[];
@@ -837,24 +833,6 @@ function deviceFactory(
837833
return getConfigChangeCount(device.changeRegistry);
838834
},
839835

840-
getRadioConfigChangeCount: () => {
841-
const device = get().devices.get(id);
842-
if (!device) {
843-
return 0;
844-
}
845-
846-
return getRadioConfigChangeCount(device.changeRegistry);
847-
},
848-
849-
getDeviceConfigChangeCount: () => {
850-
const device = get().devices.get(id);
851-
if (!device) {
852-
return 0;
853-
}
854-
855-
return getDeviceConfigChangeCount(device.changeRegistry);
856-
},
857-
858836
getModuleConfigChangeCount: () => {
859837
const device = get().devices.get(id);
860838
if (!device) {

apps/web/src/core/utils/deepCompareConfig.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,6 @@ function isUint8Array(v: unknown): v is Uint8Array {
66
return v instanceof Uint8Array;
77
}
88

9-
export function normalizeBytes(value: unknown): unknown {
10-
if (value instanceof Uint8Array && value.byteLength === 0) {
11-
return undefined;
12-
}
13-
return value;
14-
}
15-
169
function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
1710
if (a.byteLength !== b.byteLength) {
1811
return false;

apps/web/src/pages/Settings/index.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ const ConfigPage = () => {
3636
setModuleConfig,
3737
addChannel,
3838
getConfigChangeCount,
39-
getRadioConfigChangeCount,
40-
getDeviceConfigChangeCount,
4139
getModuleConfigChangeCount,
4240
getChannelChangeCount,
4341
getAdminMessageChangeCount,
@@ -52,9 +50,9 @@ const ConfigPage = () => {
5250
const routerState = useRouterState();
5351
const { t } = useTranslation("config");
5452

55-
const radioConfigChangeCount = getRadioConfigChangeCount();
56-
const deviceConfigChangeCount = getDeviceConfigChangeCount();
53+
const configChangeCount = getConfigChangeCount();
5754
const moduleConfigChangeCount = getModuleConfigChangeCount();
55+
const channelChangeCount = getChannelChangeCount();
5856
const adminMessageChangeCount = getAdminMessageChangeCount();
5957

6058
const sections = useMemo(
@@ -64,27 +62,27 @@ const ConfigPage = () => {
6462
route: radioRoute,
6563
label: t("navigation.radioConfig"),
6664
icon: RadioTowerIcon,
67-
changeCount: radioConfigChangeCount,
65+
changeCount: configChangeCount,
6866
component: RadioConfig,
6967
},
7068
{
7169
key: "device",
7270
route: deviceRoute,
7371
label: t("navigation.deviceConfig"),
7472
icon: RouterIcon,
75-
changeCount: deviceConfigChangeCount,
73+
changeCount: moduleConfigChangeCount,
7674
component: DeviceConfig,
7775
},
7876
{
7977
key: "module",
8078
route: moduleRoute,
8179
label: t("navigation.moduleConfig"),
8280
icon: LayersIcon,
83-
changeCount: moduleConfigChangeCount,
81+
changeCount: channelChangeCount,
8482
component: ModuleConfig,
8583
},
8684
],
87-
[t, radioConfigChangeCount, deviceConfigChangeCount, moduleConfigChangeCount],
85+
[t, configChangeCount, moduleConfigChangeCount, channelChangeCount],
8886
);
8987

9088
const activeSection =
@@ -265,7 +263,7 @@ const ConfigPage = () => {
265263
getModuleConfigChangeCount() > 0 ||
266264
getChannelChangeCount() > 0 ||
267265
adminMessageChangeCount > 0;
268-
const hasPending = hasDrafts;
266+
const hasPending = hasDrafts || rhfState.isDirty;
269267
const buttonOpacity = hasPending ? "opacity-100" : "opacity-0";
270268
const saveDisabled = isSaving || !rhfState.isValid || !hasPending;
271269

0 commit comments

Comments
 (0)