fix: connect new node no longer crashes. (#610)
This commit is contained in:
@@ -57,6 +57,7 @@ export const CommandPalette = () => {
|
||||
const {
|
||||
commandPaletteOpen,
|
||||
setCommandPaletteOpen,
|
||||
setConnectDialogOpen,
|
||||
setSelectedDevice,
|
||||
} = useAppStore();
|
||||
const { getDevices } = useDeviceStore();
|
||||
@@ -133,7 +134,7 @@ export const CommandPalette = () => {
|
||||
label: "Connect New Node",
|
||||
icon: PlusIcon,
|
||||
action() {
|
||||
setSelectedDevice(0);
|
||||
setConnectDialogOpen(true);
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -22,12 +22,9 @@ import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { Link } from "../UI/Typography/Link.tsx";
|
||||
import { Fragment } from "react/jsx-runtime";
|
||||
import { useState } from "react";
|
||||
|
||||
export interface TabElementProps {
|
||||
closeDialog: () => void;
|
||||
connectionInProgress: boolean;
|
||||
setConnectionInProgress: (inProgress: boolean) => void;
|
||||
}
|
||||
|
||||
export interface TabManifest {
|
||||
@@ -114,26 +111,25 @@ export const NewDeviceDialog = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
}: NewDeviceProps) => {
|
||||
const [connectionInProgress, setConnectionInProgress] = useState(false);
|
||||
const { unsupported } = useBrowserFeatureDetection();
|
||||
|
||||
const tabs: TabManifest[] = [
|
||||
{
|
||||
label: "HTTP",
|
||||
element: HTTP,
|
||||
isDisabled: connectionInProgress,
|
||||
isDisabled: false,
|
||||
},
|
||||
{
|
||||
label: "Bluetooth",
|
||||
element: BLE,
|
||||
isDisabled: unsupported.includes("Web Bluetooth") ||
|
||||
unsupported.includes("Secure Context") || connectionInProgress,
|
||||
unsupported.includes("Secure Context"),
|
||||
},
|
||||
{
|
||||
label: "Serial",
|
||||
element: Serial,
|
||||
isDisabled: unsupported.includes("Web Serial") ||
|
||||
unsupported.includes("Secure Context") || connectionInProgress,
|
||||
unsupported.includes("Secure Context"),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -160,8 +156,6 @@ export const NewDeviceDialog = ({
|
||||
: null}
|
||||
<tab.element
|
||||
closeDialog={() => onOpenChange(false)}
|
||||
setConnectionInProgress={setConnectionInProgress}
|
||||
connectionInProgress={connectionInProgress}
|
||||
/>
|
||||
</fieldset>
|
||||
</TabsContent>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { render } from "@testing-library/react";
|
||||
import { DeviceContext, useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||
import { RefreshKeysDialog } from "./RefreshKeysDialog.tsx";
|
||||
import { useMessageStore } from "../../../core/stores/messageStore/index.ts";
|
||||
import { useRefreshKeysDialog } from "./useRefreshKeysDialog.ts";
|
||||
import { afterEach, beforeEach, expect, test, vi } from "vitest";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
|
||||
vi.mock("@core/stores/messageStore");
|
||||
vi.mock("./useRefreshKeysDialog");
|
||||
@@ -25,63 +24,6 @@ afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
test("renders dialog when there is a node error for the active chat", () => {
|
||||
const deviceId = 1;
|
||||
const nodeWithErrorNum = 12345;
|
||||
const activeChatNum = nodeWithErrorNum;
|
||||
|
||||
const deviceStore = useDeviceStore.getState().addDevice(deviceId);
|
||||
|
||||
deviceStore.addNodeInfo({
|
||||
num: nodeWithErrorNum,
|
||||
user: {
|
||||
id: nodeWithErrorNum.toString(),
|
||||
publicKey: new Uint8Array(0),
|
||||
hwModel: Protobuf.Mesh.HardwareModel.HELTEC_V3,
|
||||
longName: "Problem Node Long",
|
||||
shortName: "ProbNode",
|
||||
isLicensed: false,
|
||||
macaddr: new Uint8Array(0),
|
||||
},
|
||||
lastHeard: Date.now() / 1000,
|
||||
snr: 10,
|
||||
} as Protobuf.Mesh.NodeInfo);
|
||||
|
||||
deviceStore.setNodeError(activeChatNum, "PKI_MISMATCH");
|
||||
|
||||
const updatedDeviceState = useDeviceStore.getState().getDevice(deviceId);
|
||||
if (!updatedDeviceState) {
|
||||
throw new Error(
|
||||
"Failed to get updated device state from store for provider",
|
||||
);
|
||||
}
|
||||
|
||||
mockUseMessageStore.mockReturnValue({ activeChat: activeChatNum });
|
||||
const mockHandleClose = vi.fn();
|
||||
const mockHandleRemove = vi.fn();
|
||||
mockUseRefreshKeysDialog.mockReturnValue({
|
||||
handleCloseDialog: mockHandleClose,
|
||||
handleNodeRemove: mockHandleRemove,
|
||||
});
|
||||
|
||||
render(
|
||||
<DeviceContext.Provider value={updatedDeviceState}>
|
||||
<RefreshKeysDialog open onOpenChange={vi.fn()} />
|
||||
</DeviceContext.Provider>,
|
||||
);
|
||||
|
||||
expect(screen.getByText(/Keys Mismatch - Problem Node Long/))
|
||||
.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Your node is unable to send a direct message to node: Problem Node Long \(ProbNode\)/,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Request New Keys" }))
|
||||
.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Dismiss" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("does not render dialog if no error exists for active chat", () => {
|
||||
const deviceId = 1;
|
||||
const activeChatNum = 54321;
|
||||
|
||||
@@ -10,8 +10,9 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import { useMessageStore } from "../../../core/stores/messageStore/index.ts";
|
||||
|
||||
export const BLE = (
|
||||
{ setConnectionInProgress, closeDialog }: TabElementProps,
|
||||
{ closeDialog }: TabElementProps,
|
||||
) => {
|
||||
const [connectionInProgress, setConnectionInProgress] = useState(false);
|
||||
const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]);
|
||||
const { addDevice } = useDeviceStore();
|
||||
const messageStore = useMessageStore();
|
||||
@@ -40,7 +41,10 @@ export const BLE = (
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-2 p-4">
|
||||
<fieldset
|
||||
className="flex w-full flex-col gap-2 p-4"
|
||||
disabled={connectionInProgress}
|
||||
>
|
||||
<div className="flex h-48 flex-col gap-2 overflow-y-auto">
|
||||
{bleDevices.map((device) => (
|
||||
<Button
|
||||
@@ -80,6 +84,6 @@ export const BLE = (
|
||||
>
|
||||
<span>New device</span>
|
||||
</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,9 +21,9 @@ interface FormData {
|
||||
}
|
||||
|
||||
export const HTTP = (
|
||||
{ closeDialog, setConnectionInProgress, connectionInProgress }:
|
||||
TabElementProps,
|
||||
{ closeDialog }: TabElementProps,
|
||||
) => {
|
||||
const [connectionInProgress, setConnectionInProgress] = useState(false);
|
||||
const isURLHTTPS = location.protocol === "https:";
|
||||
|
||||
const { addDevice } = useDeviceStore();
|
||||
@@ -73,7 +73,11 @@ export const HTTP = (
|
||||
|
||||
return (
|
||||
<form className="flex w-full flex-col gap-2 p-4" onSubmit={onSubmit}>
|
||||
<div className="flex flex-col gap-2" style={{ minHeight: "12rem" }}>
|
||||
<fieldset
|
||||
className="flex flex-col gap-2"
|
||||
style={{ minHeight: "12rem" }}
|
||||
disabled={connectionInProgress}
|
||||
>
|
||||
<div>
|
||||
<Label>IP Address/Hostname</Label>
|
||||
<Input
|
||||
@@ -132,7 +136,7 @@ export const HTTP = (
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</fieldset>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="default"
|
||||
|
||||
@@ -11,8 +11,9 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import { useMessageStore } from "../../../core/stores/messageStore/index.ts";
|
||||
|
||||
export const Serial = (
|
||||
{ setConnectionInProgress, closeDialog }: TabElementProps,
|
||||
{ closeDialog }: TabElementProps,
|
||||
) => {
|
||||
const [connectionInProgress, setConnectionInProgress] = useState(false);
|
||||
const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]);
|
||||
const { addDevice } = useDeviceStore();
|
||||
const messageStore = useMessageStore();
|
||||
@@ -46,7 +47,10 @@ export const Serial = (
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-2 p-4">
|
||||
<fieldset
|
||||
className="flex w-full flex-col gap-2 p-4"
|
||||
disabled={connectionInProgress}
|
||||
>
|
||||
<div className="flex h-48 flex-col gap-2 overflow-y-auto">
|
||||
{serialPorts.map((port, index) => {
|
||||
const { usbProductId, usbVendorId } = port.getInfo();
|
||||
@@ -85,6 +89,6 @@ export const Serial = (
|
||||
>
|
||||
<span>New device</span>
|
||||
</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -92,19 +92,22 @@ export interface Device {
|
||||
) => Protobuf.Mesh.NodeInfo[];
|
||||
getNodesLength: () => number;
|
||||
getNode: (nodeNum: number) => Protobuf.Mesh.NodeInfo | undefined;
|
||||
getMyNode: () => Protobuf.Mesh.NodeInfo;
|
||||
}
|
||||
|
||||
export interface DeviceState {
|
||||
devices: Map<number, Device>;
|
||||
remoteDevices: Map<number, undefined>;
|
||||
|
||||
addDevice: (id: number) => Device;
|
||||
removeDevice: (id: number) => void;
|
||||
getDevices: () => Device[];
|
||||
getDevice: (id: number) => Device | undefined;
|
||||
}
|
||||
|
||||
export const useDeviceStore = createStore<DeviceState>((set, get) => ({
|
||||
interface PrivateDeviceState extends DeviceState {
|
||||
devices: Map<number, Device>;
|
||||
remoteDevices: Map<number, undefined>;
|
||||
}
|
||||
|
||||
export const useDeviceStore = createStore<PrivateDeviceState>((set, get) => ({
|
||||
devices: new Map(),
|
||||
remoteDevices: new Map(),
|
||||
|
||||
@@ -576,6 +579,14 @@ export const useDeviceStore = createStore<DeviceState>((set, get) => ({
|
||||
}
|
||||
return device.nodesMap.get(nodeNum);
|
||||
},
|
||||
getMyNode: (): Protobuf.Mesh.NodeInfo => {
|
||||
const device = get().devices.get(id);
|
||||
if (!device) {
|
||||
throw new Error(`Device ${id} not found`);
|
||||
}
|
||||
return device.nodesMap.get(device.hardware.myNodeNum) ??
|
||||
create(Protobuf.Mesh.NodeInfoSchema);
|
||||
},
|
||||
getNodesLength: () => {
|
||||
const device = get().devices.get(id);
|
||||
if (!device) {
|
||||
|
||||
Reference in New Issue
Block a user