refactor: device connection logic, added nonce to get config only (#946)
* refactor: device connection logic, added nonce to get config only on connect. * Update packages/web/src/core/services/MeshService.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/src/pages/Connections/useConnections.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * code review fixes * fixes from code review * ui fixes * refactored meshService, moved code into deviceStore. Fixed some connnection issues * formatting fixes --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -52,9 +52,17 @@ type DeviceData = {
|
||||
waypoints: WaypointWithMetadata[];
|
||||
neighborInfo: Map<number, Protobuf.Mesh.NeighborInfo>;
|
||||
};
|
||||
export type ConnectionPhase =
|
||||
| "disconnected"
|
||||
| "connecting"
|
||||
| "configuring"
|
||||
| "configured";
|
||||
|
||||
export interface Device extends DeviceData {
|
||||
// Ephemeral state (not persisted)
|
||||
status: Types.DeviceStatusEnum;
|
||||
connectionPhase: ConnectionPhase;
|
||||
connectionId: ConnectionId | null;
|
||||
channels: Map<Types.ChannelNumber, Protobuf.Channel.Channel>;
|
||||
config: Protobuf.LocalOnly.LocalConfig;
|
||||
moduleConfig: Protobuf.LocalOnly.LocalModuleConfig;
|
||||
@@ -70,6 +78,8 @@ export interface Device extends DeviceData {
|
||||
clientNotifications: Protobuf.Mesh.ClientNotification[];
|
||||
|
||||
setStatus: (status: Types.DeviceStatusEnum) => void;
|
||||
setConnectionPhase: (phase: ConnectionPhase) => void;
|
||||
setConnectionId: (id: ConnectionId | null) => void;
|
||||
setConfig: (config: Protobuf.Config.Config) => void;
|
||||
setModuleConfig: (config: Protobuf.ModuleConfig.ModuleConfig) => void;
|
||||
getEffectiveConfig<K extends ValidConfigType>(
|
||||
@@ -153,6 +163,16 @@ export interface deviceState {
|
||||
) => void;
|
||||
removeSavedConnection: (id: ConnectionId) => void;
|
||||
getSavedConnections: () => Connection[];
|
||||
|
||||
// Active connection tracking
|
||||
activeConnectionId: ConnectionId | null;
|
||||
setActiveConnectionId: (id: ConnectionId | null) => void;
|
||||
getActiveConnectionId: () => ConnectionId | null;
|
||||
|
||||
// Helper selectors for connection ↔ device relationships
|
||||
getActiveConnection: () => Connection | undefined;
|
||||
getDeviceForConnection: (id: ConnectionId) => Device | undefined;
|
||||
getConnectionForDevice: (deviceId: number) => Connection | undefined;
|
||||
}
|
||||
|
||||
interface PrivateDeviceState extends deviceState {
|
||||
@@ -185,6 +205,8 @@ function deviceFactory(
|
||||
neighborInfo,
|
||||
|
||||
status: Types.DeviceStatusEnum.DeviceDisconnected,
|
||||
connectionPhase: "disconnected",
|
||||
connectionId: null,
|
||||
channels: new Map(),
|
||||
config: create(Protobuf.LocalOnly.LocalConfigSchema),
|
||||
moduleConfig: create(Protobuf.LocalOnly.LocalModuleConfigSchema),
|
||||
@@ -227,6 +249,26 @@ function deviceFactory(
|
||||
}),
|
||||
);
|
||||
},
|
||||
setConnectionPhase: (phase: ConnectionPhase) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
const device = draft.devices.get(id);
|
||||
if (device) {
|
||||
device.connectionPhase = phase;
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
setConnectionId: (connectionId: ConnectionId | null) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
const device = draft.devices.get(id);
|
||||
if (device) {
|
||||
device.connectionId = connectionId;
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
setConfig: (config: Protobuf.Config.Config) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
@@ -907,6 +949,7 @@ export const deviceStoreInitializer: StateCreator<PrivateDeviceState> = (
|
||||
) => ({
|
||||
devices: new Map(),
|
||||
savedConnections: [],
|
||||
activeConnectionId: null,
|
||||
|
||||
addDevice: (id) => {
|
||||
const existing = get().devices.get(id);
|
||||
@@ -972,6 +1015,33 @@ export const deviceStoreInitializer: StateCreator<PrivateDeviceState> = (
|
||||
);
|
||||
},
|
||||
getSavedConnections: () => get().savedConnections,
|
||||
|
||||
setActiveConnectionId: (id) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
draft.activeConnectionId = id;
|
||||
}),
|
||||
);
|
||||
},
|
||||
getActiveConnectionId: () => get().activeConnectionId,
|
||||
|
||||
getActiveConnection: () => {
|
||||
const activeId = get().activeConnectionId;
|
||||
if (!activeId) {
|
||||
return undefined;
|
||||
}
|
||||
return get().savedConnections.find((c) => c.id === activeId);
|
||||
},
|
||||
getDeviceForConnection: (id) => {
|
||||
const connection = get().savedConnections.find((c) => c.id === id);
|
||||
if (!connection?.meshDeviceId) {
|
||||
return undefined;
|
||||
}
|
||||
return get().devices.get(connection.meshDeviceId);
|
||||
},
|
||||
getConnectionForDevice: (deviceId) => {
|
||||
return get().savedConnections.find((c) => c.meshDeviceId === deviceId);
|
||||
},
|
||||
});
|
||||
|
||||
const persistOptions: PersistOptions<PrivateDeviceState, DevicePersisted> = {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import type { Device } from "./index.ts";
|
||||
import { useDeviceStore } from "./index.ts";
|
||||
import type { Connection, ConnectionId } from "./types.ts";
|
||||
|
||||
/**
|
||||
* Hook to get the currently active connection
|
||||
*/
|
||||
export function useActiveConnection(): Connection | undefined {
|
||||
return useDeviceStore((s) => s.getActiveConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the HTTP connection marked as default
|
||||
*/
|
||||
export function useDefaultConnection(): Connection | undefined {
|
||||
return useDeviceStore((s) => s.savedConnections.find((c) => c.isDefault));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the first saved connection
|
||||
*/
|
||||
export function useFirstSavedConnection(): Connection | undefined {
|
||||
return useDeviceStore((s) => s.savedConnections.at(0));
|
||||
}
|
||||
|
||||
export function useAddSavedConnection() {
|
||||
return useDeviceStore((s) => s.addSavedConnection);
|
||||
}
|
||||
|
||||
export function useUpdateSavedConnection() {
|
||||
return useDeviceStore((s) => s.updateSavedConnection);
|
||||
}
|
||||
|
||||
export function useRemoveSavedConnection() {
|
||||
return useDeviceStore((s) => s.removeSavedConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the active connection ID
|
||||
*/
|
||||
export function useActiveConnectionId(): ConnectionId | null {
|
||||
return useDeviceStore((s) => s.activeConnectionId);
|
||||
}
|
||||
|
||||
export function useSetActiveConnectionId() {
|
||||
return useDeviceStore((s) => s.setActiveConnectionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get a specific connection's status
|
||||
*/
|
||||
export function useConnectionStatus(id: ConnectionId): string | undefined {
|
||||
return useDeviceStore(
|
||||
(s) => s.savedConnections.find((c) => c.id === id)?.status,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get a device for a specific connection
|
||||
*/
|
||||
export function useDeviceForConnection(id: ConnectionId): Device | undefined {
|
||||
return useDeviceStore((s) => s.getDeviceForConnection(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get a connection for a specific device
|
||||
*/
|
||||
export function useConnectionForDevice(
|
||||
deviceId: number,
|
||||
): Connection | undefined {
|
||||
return useDeviceStore((s) => s.getConnectionForDevice(deviceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to check if any connection is currently connecting
|
||||
*/
|
||||
export function useIsConnecting(): boolean {
|
||||
return useDeviceStore((s) =>
|
||||
s.savedConnections.some(
|
||||
(c) => c.status === "connecting" || c.status === "configuring",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get error message for a specific connection
|
||||
*/
|
||||
export function useConnectionError(id: ConnectionId): string | null {
|
||||
return useDeviceStore(
|
||||
(s) => s.savedConnections.find((c) => c.id === id)?.error ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get all saved connections
|
||||
*/
|
||||
export function useSavedConnections(): Connection[] {
|
||||
return useDeviceStore((s) => s.savedConnections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to check if a connection is connected
|
||||
*/
|
||||
export function useIsConnected(id: ConnectionId): boolean {
|
||||
return useDeviceStore((s) => {
|
||||
const status = s.savedConnections.find((c) => c.id === id)?.status;
|
||||
return status === "connected" || status === "configured";
|
||||
});
|
||||
}
|
||||
@@ -14,6 +14,22 @@ export {
|
||||
} from "@core/hooks/useDeviceContext";
|
||||
export { useAppStore } from "@core/stores/appStore/index.ts";
|
||||
export { type Device, useDeviceStore } from "@core/stores/deviceStore/index.ts";
|
||||
export {
|
||||
useActiveConnection,
|
||||
useActiveConnectionId,
|
||||
useAddSavedConnection,
|
||||
useConnectionError,
|
||||
useConnectionForDevice,
|
||||
useConnectionStatus,
|
||||
useDefaultConnection,
|
||||
useDeviceForConnection,
|
||||
useFirstSavedConnection,
|
||||
useIsConnected,
|
||||
useIsConnecting,
|
||||
useRemoveSavedConnection,
|
||||
useSavedConnections,
|
||||
useUpdateSavedConnection,
|
||||
} from "@core/stores/deviceStore/selectors.ts";
|
||||
export type {
|
||||
Page,
|
||||
ValidConfigType,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { featureFlags } from "@core/services/featureFlags";
|
||||
import { validateIncomingNode } from "@core/stores/nodeDBStore/nodeValidation";
|
||||
import { evictOldestEntries } from "@core/stores/utils/evictOldestEntries.ts";
|
||||
import { createStorage } from "@core/stores/utils/indexDB.ts";
|
||||
import { Protobuf, type Types } from "@meshtastic/core";
|
||||
import { produce } from "immer";
|
||||
@@ -15,7 +14,7 @@ import type { NodeError, NodeErrorType, ProcessPacketParams } from "./types.ts";
|
||||
|
||||
const IDB_KEY_NAME = "meshtastic-nodedb-store";
|
||||
const CURRENT_STORE_VERSION = 0;
|
||||
const NODEDB_RETENTION_NUM = 10;
|
||||
const NODE_RETENTION_DAYS = 14; // Remove nodes not heard from in 14 days
|
||||
|
||||
type NodeDBData = {
|
||||
// Persisted data
|
||||
@@ -30,6 +29,7 @@ export interface NodeDB extends NodeDBData {
|
||||
addNode: (nodeInfo: Protobuf.Mesh.NodeInfo) => void;
|
||||
removeNode: (nodeNum: number) => void;
|
||||
removeAllNodes: (keepMyNode?: boolean) => void;
|
||||
pruneStaleNodes: () => number;
|
||||
processPacket: (data: ProcessPacketParams) => void;
|
||||
addUser: (user: Types.PacketMetadata<Protobuf.Mesh.User>) => void;
|
||||
addPosition: (position: Types.PacketMetadata<Protobuf.Mesh.Position>) => void;
|
||||
@@ -90,6 +90,11 @@ function nodeDBFactory(
|
||||
if (!nodeDB) {
|
||||
throw new Error(`No nodeDB found (id: ${id})`);
|
||||
}
|
||||
|
||||
// Check if node already exists
|
||||
const existing = nodeDB.nodeMap.get(node.num);
|
||||
const isNew = !existing;
|
||||
|
||||
// Use validation to check the new node before adding
|
||||
const next = validateIncomingNode(
|
||||
node,
|
||||
@@ -105,7 +110,30 @@ function nodeDBFactory(
|
||||
return;
|
||||
}
|
||||
|
||||
nodeDB.nodeMap = new Map(nodeDB.nodeMap).set(node.num, next);
|
||||
// Merge with existing node data if it exists
|
||||
const merged = existing
|
||||
? {
|
||||
...existing,
|
||||
...next,
|
||||
// Preserve existing fields if new node doesn't have them
|
||||
user: next.user ?? existing.user,
|
||||
position: next.position ?? existing.position,
|
||||
deviceMetrics: next.deviceMetrics ?? existing.deviceMetrics,
|
||||
}
|
||||
: next;
|
||||
|
||||
// Use the validated node's num to ensure consistency
|
||||
nodeDB.nodeMap = new Map(nodeDB.nodeMap).set(merged.num, merged);
|
||||
|
||||
if (isNew) {
|
||||
console.log(
|
||||
`[NodeDB] Adding new node from NodeInfo packet: ${merged.num} (${merged.user?.longName || "unknown"})`,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`[NodeDB] Updating existing node from NodeInfo packet: ${merged.num} (${merged.user?.longName || "unknown"})`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -145,6 +173,56 @@ function nodeDBFactory(
|
||||
}),
|
||||
),
|
||||
|
||||
pruneStaleNodes: () => {
|
||||
const nodeDB = get().nodeDBs.get(id);
|
||||
if (!nodeDB) {
|
||||
throw new Error(`No nodeDB found (id: ${id})`);
|
||||
}
|
||||
|
||||
const nowSec = Math.floor(Date.now() / 1000);
|
||||
const cutoffSec = nowSec - NODE_RETENTION_DAYS * 24 * 60 * 60;
|
||||
let prunedCount = 0;
|
||||
|
||||
set(
|
||||
produce<PrivateNodeDBState>((draft) => {
|
||||
const nodeDB = draft.nodeDBs.get(id);
|
||||
if (!nodeDB) {
|
||||
throw new Error(`No nodeDB found (id: ${id})`);
|
||||
}
|
||||
|
||||
const newNodeMap = new Map<number, Protobuf.Mesh.NodeInfo>();
|
||||
|
||||
for (const [nodeNum, node] of nodeDB.nodeMap) {
|
||||
// Keep myNode regardless of lastHeard
|
||||
// Keep nodes that have been heard recently
|
||||
// Keep nodes without lastHeard (just in case)
|
||||
if (
|
||||
nodeNum === nodeDB.myNodeNum ||
|
||||
!node.lastHeard ||
|
||||
node.lastHeard >= cutoffSec
|
||||
) {
|
||||
newNodeMap.set(nodeNum, node);
|
||||
} else {
|
||||
prunedCount++;
|
||||
console.log(
|
||||
`[NodeDB] Pruning stale node ${nodeNum} (last heard ${Math.floor((nowSec - node.lastHeard) / 86400)} days ago)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
nodeDB.nodeMap = newNodeMap;
|
||||
}),
|
||||
);
|
||||
|
||||
if (prunedCount > 0) {
|
||||
console.log(
|
||||
`[NodeDB] Pruned ${prunedCount} stale node(s) older than ${NODE_RETENTION_DAYS} days`,
|
||||
);
|
||||
}
|
||||
|
||||
return prunedCount;
|
||||
},
|
||||
|
||||
setNodeError: (nodeNum, error) =>
|
||||
set(
|
||||
produce<PrivateNodeDBState>((draft) => {
|
||||
@@ -220,11 +298,20 @@ function nodeDBFactory(
|
||||
if (!nodeDB) {
|
||||
throw new Error(`No nodeDB found (id: ${id})`);
|
||||
}
|
||||
const current =
|
||||
nodeDB.nodeMap.get(user.from) ??
|
||||
create(Protobuf.Mesh.NodeInfoSchema);
|
||||
const updated = { ...current, user: user.data, num: user.from };
|
||||
const current = nodeDB.nodeMap.get(user.from);
|
||||
const isNew = !current;
|
||||
const updated = {
|
||||
...(current ?? create(Protobuf.Mesh.NodeInfoSchema)),
|
||||
user: user.data,
|
||||
num: user.from,
|
||||
};
|
||||
nodeDB.nodeMap = new Map(nodeDB.nodeMap).set(user.from, updated);
|
||||
|
||||
if (isNew) {
|
||||
console.log(
|
||||
`[NodeDB] Adding new node from user packet: ${user.from} (${user.data.longName || "unknown"})`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -235,15 +322,20 @@ function nodeDBFactory(
|
||||
if (!nodeDB) {
|
||||
throw new Error(`No nodeDB found (id: ${id})`);
|
||||
}
|
||||
const current =
|
||||
nodeDB.nodeMap.get(position.from) ??
|
||||
create(Protobuf.Mesh.NodeInfoSchema);
|
||||
const current = nodeDB.nodeMap.get(position.from);
|
||||
const isNew = !current;
|
||||
const updated = {
|
||||
...current,
|
||||
...(current ?? create(Protobuf.Mesh.NodeInfoSchema)),
|
||||
position: position.data,
|
||||
num: position.from,
|
||||
};
|
||||
nodeDB.nodeMap = new Map(nodeDB.nodeMap).set(position.from, updated);
|
||||
|
||||
if (isNew) {
|
||||
console.log(
|
||||
`[NodeDB] Adding new node from position packet: ${position.from}`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -411,6 +503,8 @@ export const nodeDBInitializer: StateCreator<PrivateNodeDBState> = (
|
||||
addNodeDB: (id) => {
|
||||
const existing = get().nodeDBs.get(id);
|
||||
if (existing) {
|
||||
// Prune stale nodes when accessing existing nodeDB
|
||||
existing.pruneStaleNodes();
|
||||
return existing;
|
||||
}
|
||||
|
||||
@@ -418,12 +512,12 @@ export const nodeDBInitializer: StateCreator<PrivateNodeDBState> = (
|
||||
set(
|
||||
produce<PrivateNodeDBState>((draft) => {
|
||||
draft.nodeDBs = new Map(draft.nodeDBs).set(id, nodeDB);
|
||||
|
||||
// Enforce retention limit
|
||||
evictOldestEntries(draft.nodeDBs, NODEDB_RETENTION_NUM);
|
||||
}),
|
||||
);
|
||||
|
||||
// Prune stale nodes on creation (useful when rehydrating from storage)
|
||||
nodeDB.pruneStaleNodes();
|
||||
|
||||
return nodeDB;
|
||||
},
|
||||
removeNodeDB: (id) => {
|
||||
|
||||
@@ -15,9 +15,12 @@ export const mockNodeDBStore: NodeDB = {
|
||||
addUser: vi.fn(),
|
||||
addPosition: vi.fn(),
|
||||
removeNode: vi.fn(),
|
||||
removeAllNodes: vi.fn(),
|
||||
pruneStaleNodes: vi.fn().mockReturnValue(0),
|
||||
processPacket: vi.fn(),
|
||||
setNodeError: vi.fn(),
|
||||
clearNodeError: vi.fn(),
|
||||
removeAllNodeErrors: vi.fn(),
|
||||
getNodeError: vi.fn().mockReturnValue(undefined),
|
||||
hasNodeError: vi.fn().mockReturnValue(false),
|
||||
getNodes: vi.fn().mockReturnValue([]),
|
||||
@@ -27,6 +30,4 @@ export const mockNodeDBStore: NodeDB = {
|
||||
updateFavorite: vi.fn(),
|
||||
updateIgnore: vi.fn(),
|
||||
setNodeNum: vi.fn(),
|
||||
removeAllNodeErrors: vi.fn(),
|
||||
removeAllNodes: vi.fn(),
|
||||
};
|
||||
|
||||
@@ -66,10 +66,10 @@ describe("NodeDB store", () => {
|
||||
|
||||
const db1 = useNodeDBStore.getState().addNodeDB(123);
|
||||
const db2 = useNodeDBStore.getState().addNodeDB(123);
|
||||
expect(db1).toBe(db2);
|
||||
expect(db1).toStrictEqual(db2);
|
||||
|
||||
const got = useNodeDBStore.getState().getNodeDB(123);
|
||||
expect(got).toBe(db1);
|
||||
expect(got).toStrictEqual(db1);
|
||||
|
||||
expect(useNodeDBStore.getState().getNodeDBs().length).toBe(1);
|
||||
});
|
||||
@@ -204,15 +204,18 @@ describe("NodeDB store", () => {
|
||||
expect(filtered.map((n) => n.num).sort()).toEqual([12]); // still excludes 11
|
||||
});
|
||||
|
||||
it("when exceeding cap, evicts earliest inserted, not the newly added", async () => {
|
||||
it("will prune nodes after 14 days of inactivitiy", async () => {
|
||||
const { useNodeDBStore } = await freshStore();
|
||||
const st = useNodeDBStore.getState();
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
st.addNodeDB(i);
|
||||
}
|
||||
st.addNodeDB(11);
|
||||
expect(st.getNodeDB(1)).toBeUndefined();
|
||||
expect(st.getNodeDB(11)).toBeDefined();
|
||||
st.addNodeDB(1).addNode(
|
||||
makeNode(1, { lastHeard: Date.now() / 1000 - 15 * 24 * 3600 }),
|
||||
); // 15 days ago
|
||||
st.addNodeDB(1).addNode(
|
||||
makeNode(2, { lastHeard: Date.now() / 1000 - 7 * 24 * 3600 }),
|
||||
); // 7 days ago
|
||||
|
||||
st.getNodeDB(1)!.pruneStaleNodes();
|
||||
expect(st.getNodeDB(1)?.getNode(2)).toBeDefined();
|
||||
});
|
||||
|
||||
it("removeNodeDB persists removal across reload", async () => {
|
||||
@@ -401,28 +404,6 @@ describe("NodeDB – merge semantics, PKI checks & extras", () => {
|
||||
expect(newDB.getNodeError(2)!.error).toBe("NEW_ERR"); // new added
|
||||
});
|
||||
|
||||
it("eviction still honors cap after merge", async () => {
|
||||
const { useNodeDBStore } = await freshStore();
|
||||
const st = useNodeDBStore.getState();
|
||||
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
st.addNodeDB(i);
|
||||
}
|
||||
const oldDB = st.addNodeDB(100);
|
||||
oldDB.setNodeNum(12345);
|
||||
oldDB.addNode(makeNode(2000));
|
||||
|
||||
const newDB = st.addNodeDB(101);
|
||||
newDB.setNodeNum(12345); // merges + deletes 100
|
||||
|
||||
// adding another to trigger eviction of earliest non-merged entry (which was 1)
|
||||
st.addNodeDB(102);
|
||||
|
||||
expect(st.getNodeDB(1)).toBeUndefined(); // evicted
|
||||
expect(st.getNodeDB(101)).toBeDefined(); // merged entry exists
|
||||
expect(st.getNodeDB(101)!.getNode(2000)).toBeTruthy(); // carried over
|
||||
});
|
||||
|
||||
it("removeAllNodes (optionally keeping my node) and removeAllNodeErrors persist across reload", async () => {
|
||||
{
|
||||
const { useNodeDBStore } = await freshStore(true); // with persistence
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ensureDefaultUser } from "@core/dto/NodeNumToNodeInfoDTO.ts";
|
||||
import PacketToMessageDTO from "@core/dto/PacketToMessageDTO.ts";
|
||||
import { useNewNodeNum } from "@core/hooks/useNewNodeNum";
|
||||
import {
|
||||
@@ -68,11 +67,11 @@ export const subscribeAll = (
|
||||
nodeDB.addPosition(position);
|
||||
});
|
||||
|
||||
// NOTE: Node handling is managed by the nodeDB
|
||||
// Nodes are added via subscriptions.ts and stored in nodeDB
|
||||
// Configuration is handled directly by meshDevice.configure() in useConnections
|
||||
connection.events.onNodeInfoPacket.subscribe((nodeInfo) => {
|
||||
const nodeWithUser = ensureDefaultUser(nodeInfo);
|
||||
|
||||
// PKI sanity check is handled inside nodeDB.addNode
|
||||
nodeDB.addNode(nodeWithUser);
|
||||
nodeDB.addNode(nodeInfo);
|
||||
});
|
||||
|
||||
connection.events.onChannelPacket.subscribe((channel) => {
|
||||
|
||||
Reference in New Issue
Block a user