diff --git a/src/components/Dashboard/BluetoothSection.tsx b/src/components/Dashboard/BluetoothSection.tsx
index bfd8838b..e2f0ba69 100644
--- a/src/components/Dashboard/BluetoothSection.tsx
+++ b/src/components/Dashboard/BluetoothSection.tsx
@@ -61,7 +61,7 @@ export const BluetoothSection = ({ onConnect }: BluetoothSectionProps) => {
onConnect?.();
} catch (error) {
console.error("Bluetooth connection error:", error);
- setConnectionError(`Failed to connect to ${bleDevice.name || "device"}`);
+ setConnectionError(`Failed to connect to ${bleDevice.name ?? "device"}`);
} finally {
setConnectingToDevice(null);
}
@@ -102,11 +102,11 @@ export const BluetoothSection = ({ onConnect }: BluetoothSectionProps) => {
return (
);
- } else if (isConnected) {
+ }
+ if (isConnected) {
return ;
- } else {
- return ;
}
+ return ;
};
const getStatusText = (device: BluetoothDevice) => {
@@ -148,7 +148,7 @@ export const BluetoothSection = ({ onConnect }: BluetoothSectionProps) => {
- {device.name || "Unknown Device"}
+ {device.name ?? "Unknown Device"}
diff --git a/src/components/Dashboard/HTTPServerSection.tsx b/src/components/Dashboard/HTTPServerSection.tsx
index 683bc842..cdb44290 100644
--- a/src/components/Dashboard/HTTPServerSection.tsx
+++ b/src/components/Dashboard/HTTPServerSection.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useState } from "react";
+import { useState } from "react";
import { Button } from "@components/UI/Button.tsx";
import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx";
@@ -18,14 +18,12 @@ import { TransportHTTP } from "@meshtastic/transport-http";
import { useForm } from "react-hook-form";
import {
AlertTriangle,
- Circle,
Clock,
Lock,
LockOpen,
Plus,
Server,
Trash2,
- Users,
Wifi,
} from "lucide-react";
import { useMessageStore } from "@core/stores/messageStore/index.ts";
@@ -56,7 +54,6 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
addSavedServer,
removeSavedServer,
clearSavedServers,
- updateServerStatus,
getSavedServers,
} = useAppStore();
@@ -77,48 +74,6 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
const secureValue = watch("secure");
- // Auto-check server status on component mount
- useEffect(() => {
- savedServers.forEach((server) => {
- if (server.status !== "checking") {
- checkServerStatus(server);
- }
- });
- }, []);
-
- const checkServerStatus = async (server: SavedServer) => {
- updateServerStatus(server.url, "checking");
-
- try {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 8000);
-
- const response = await fetch(`${server.url}/api/v1/fromradio?all=false`, {
- method: "GET",
- mode: "cors",
- signal: controller.signal,
- });
-
- clearTimeout(timeoutId);
-
- if (response.ok) {
- updateServerStatus(server.url, "online");
- } else {
- const fallbackResponse = await fetch(`${server.url}/`, {
- method: "GET",
- mode: "cors",
- signal: AbortSignal.timeout(3000),
- });
- updateServerStatus(
- server.url,
- fallbackResponse.ok ? "online" : "offline",
- );
- }
- } catch {
- updateServerStatus(server.url, "offline");
- }
- };
-
const connectToServer = async (server: SavedServer) => {
setConnectingToServer(server.url);
setConnectionError(null);
@@ -137,13 +92,11 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
subscribeAll(device, connection, messageStore);
addSavedServer(server.host, server.protocol);
- updateServerStatus(server.url, "online");
onConnect?.();
} catch (error) {
console.error("Connection error:", error);
setConnectionError(`Failed to connect to ${server.host}`);
- updateServerStatus(server.url, "offline");
} finally {
setConnectingToServer(null);
}
@@ -177,40 +130,12 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
}
});
- const getStatusIcon = (status?: string) => {
- switch (status) {
- case "online":
- return
;
- case "offline":
- return
;
- case "checking":
- return (
-
- );
- default:
- return
;
- }
- };
-
const getSecurityIcon = (protocol: "http" | "https") => {
return protocol === "https"
?
:
;
};
- const getStatusText = (status?: string) => {
- switch (status) {
- case "online":
- return "Online";
- case "offline":
- return "Offline";
- case "checking":
- return "Checking...";
- default:
- return "Unknown";
- }
- };
-
return (
<>
@@ -249,11 +174,6 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
key={server.url}
className="flex items-center gap-3 p-3 rounded-lg bg-white dark:bg-slate-700 border border-slate-200 dark:border-slate-600 hover:shadow-sm transition-shadow"
>
- {/* Status */}
-
- {getStatusIcon(server.status)}
-
-
{/* Server Info */}
@@ -262,29 +182,6 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
{getSecurityIcon(server.protocol)}
-
-
- {getStatusText(server.status)}
-
- {server.deviceInfo?.model && (
- <>
- •
-
- {server.deviceInfo.model}
-
- >
- )}
-
- {server.deviceInfo?.nodeCount && (
- <>
- •
-
-
- {server.deviceInfo.nodeCount}
-
- >
- )}
-
{/* Actions */}
diff --git a/src/components/Dashboard/SerialSection.tsx b/src/components/Dashboard/SerialSection.tsx
index 8b9a8060..0bef441f 100644
--- a/src/components/Dashboard/SerialSection.tsx
+++ b/src/components/Dashboard/SerialSection.tsx
@@ -27,7 +27,7 @@ export const SerialSection = ({ onConnect }: SerialSectionProps) => {
const updateSerialPortList = useCallback(async () => {
try {
- setSerialPorts(await navigator?.serial?.getPorts() || []);
+ setSerialPorts((await navigator?.serial?.getPorts()) ?? []);
} catch (error) {
console.error("Error getting serial ports:", error);
}
@@ -111,11 +111,11 @@ export const SerialSection = ({ onConnect }: SerialSectionProps) => {
return (
);
- } else if (isConnected) {
+ }
+ if (isConnected) {
return
;
- } else {
- return
;
}
+ return
;
};
const getStatusText = (port: SerialPort) => {
@@ -145,7 +145,9 @@ export const SerialSection = ({ onConnect }: SerialSectionProps) => {
: (
serialPorts.map((port, index) => (
{/* Status */}
diff --git a/src/components/PageComponents/Connect/HTTP.test.tsx b/src/components/PageComponents/Connect/HTTP.test.tsx
index 4422c949..197b4bad 100644
--- a/src/components/PageComponents/Connect/HTTP.test.tsx
+++ b/src/components/PageComponents/Connect/HTTP.test.tsx
@@ -5,7 +5,13 @@ import { TransportHTTP } from "@meshtastic/transport-http";
import { describe, expect, it, vi } from "vitest";
vi.mock("@core/stores/appStore.ts", () => ({
- useAppStore: vi.fn(() => ({ setSelectedDevice: vi.fn() })),
+ useAppStore: vi.fn(() => ({
+ setSelectedDevice: vi.fn(),
+ addSavedServer: vi.fn(),
+ removeSavedServer: vi.fn(),
+ clearSavedServers: vi.fn(),
+ getSavedServers: vi.fn(() => []),
+ })),
}));
vi.mock("@core/stores/deviceStore.ts", () => ({
@@ -14,10 +20,18 @@ vi.mock("@core/stores/deviceStore.ts", () => ({
})),
}));
+vi.mock("@core/stores/messageStore/index.ts", () => ({
+ useMessageStore: vi.fn(() => ({})),
+}));
+
vi.mock("@core/utils/randId.ts", () => ({
randId: vi.fn(() => "mock-id"),
}));
+vi.mock("@core/subscriptions.ts", () => ({
+ subscribeAll: vi.fn(),
+}));
+
vi.mock("@meshtastic/transport-http", () => ({
TransportHTTP: {
create: vi.fn(() => Promise.resolve({})),
@@ -33,34 +47,35 @@ vi.mock("@meshtastic/core", () => ({
describe("HTTP Component", () => {
it("renders correctly", () => {
render();
- expect(screen.getByText("IP Address/Hostname")).toBeInTheDocument();
- expect(screen.getByRole("textbox")).toBeInTheDocument();
- expect(screen.getByPlaceholderText("000.000.000.000 / meshtastic.local"))
- .toBeInTheDocument();
- expect(screen.getByText("Use HTTPS")).toBeInTheDocument();
- expect(screen.getByRole("button", { name: "Connect" })).toBeInTheDocument();
+ expect(screen.getByText("Meshtastic Servers")).toBeInTheDocument();
+ expect(screen.getByText("Add New Server")).toBeInTheDocument();
+ expect(screen.getByText("No saved servers yet")).toBeInTheDocument();
});
- it("allows input field to be updated", () => {
+ it("opens dialog when add new server is clicked", () => {
render();
- const inputField = screen.getByRole("textbox");
- fireEvent.change(inputField, { target: { value: "meshtastic.local" } });
- expect(screen.getByPlaceholderText("000.000.000.000 / meshtastic.local"))
+ const addButton = screen.getByText("Add New Server");
+ fireEvent.click(addButton);
+ expect(screen.getByText("Hostname or IP Address")).toBeInTheDocument();
+ expect(screen.getByPlaceholderText("meshtastic.local or 192.168.1.100"))
.toBeInTheDocument();
});
- it("toggles HTTPS switch and updates prefix", () => {
+ it("toggles HTTPS switch in dialog", () => {
render();
+ // Open the dialog first
+ const addButton = screen.getByText("Add New Server");
+ fireEvent.click(addButton);
+
const switchInput = screen.getByRole("switch");
- expect(screen.getByText("http://")).toBeInTheDocument();
+ expect(screen.getByText("Use HTTPS (Secure)")).toBeInTheDocument();
fireEvent.click(switchInput);
- expect(screen.getByText("https://")).toBeInTheDocument();
+ expect(switchInput).toBeChecked();
fireEvent.click(switchInput);
expect(switchInput).not.toBeChecked();
- expect(screen.getByText("http://")).toBeInTheDocument();
});
it("enables HTTPS toggle when location protocol is https", () => {
@@ -71,10 +86,12 @@ describe("HTTP Component", () => {
render();
+ // Open the dialog first
+ const addButton = screen.getByText("Add New Server");
+ fireEvent.click(addButton);
+
const switchInput = screen.getByRole("switch");
expect(switchInput).toBeChecked();
-
- expect(screen.getByText("https://")).toBeInTheDocument();
});
it.skip("submits form and triggers connection process", async () => {
diff --git a/src/components/PageComponents/Connect/HTTP.tsx b/src/components/PageComponents/Connect/HTTP.tsx
index b8ff9e6c..9db11fab 100644
--- a/src/components/PageComponents/Connect/HTTP.tsx
+++ b/src/components/PageComponents/Connect/HTTP.tsx
@@ -15,7 +15,7 @@ import { subscribeAll } from "@core/subscriptions.ts";
import { randId } from "@core/utils/randId.ts";
import { MeshDevice } from "@meshtastic/core";
import { TransportHTTP } from "@meshtastic/transport-http";
-import { useCallback, useEffect, useState } from "react";
+import { useState } from "react";
import { useForm } from "react-hook-form";
import {
AlertTriangle,
@@ -54,7 +54,6 @@ export const HTTP = ({ closeDialog }: TabElementProps) => {
addSavedServer,
removeSavedServer,
clearSavedServers,
- updateServerStatus,
getSavedServers,
} = useAppStore();
@@ -75,80 +74,6 @@ export const HTTP = ({ closeDialog }: TabElementProps) => {
const secureValue = watch("secure");
- const checkServerStatus = useCallback(
- async (server: SavedServer) => {
- updateServerStatus(server.url, "checking");
-
- try {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 8000);
-
- // Try to get actual device info from fromradio endpoint
- const response = await fetch(
- `${server.url}/api/v1/fromradio?all=false`,
- {
- method: "GET",
- mode: "cors",
- signal: controller.signal,
- },
- );
-
- clearTimeout(timeoutId);
-
- if (response.ok) {
- // Try to get basic device info if available
- try {
- // First check if we can get a simple status response
- const statusResponse = await fetch(
- `${server.url}/api/v1/fromradio`,
- {
- method: "GET",
- mode: "cors",
- signal: AbortSignal.timeout(3000),
- },
- );
-
- if (statusResponse.ok) {
- // For now, just mark as online - we could parse protobuf later for device details
- updateServerStatus(server.url, "online");
-
- // TODO: Parse protobuf response to get device info
- // This would require proper protobuf decoding which is complex
- // For now, we'll show it as online and get device info after connection
- } else {
- updateServerStatus(server.url, "offline");
- }
- } catch {
- // If fromradio fails, try the simpler status endpoint
- const fallbackResponse = await fetch(`${server.url}/`, {
- method: "GET",
- mode: "cors",
- signal: AbortSignal.timeout(3000),
- });
- updateServerStatus(
- server.url,
- fallbackResponse.ok ? "online" : "offline",
- );
- }
- } else {
- updateServerStatus(server.url, "offline");
- }
- } catch {
- updateServerStatus(server.url, "offline");
- }
- },
- [updateServerStatus],
- );
-
- // Auto-check server status on component mount
- useEffect(() => {
- for (const server of savedServers) {
- if (server.status !== "checking") {
- checkServerStatus(server);
- }
- }
- }, [savedServers, checkServerStatus]);
-
const connectToServer = async (server: SavedServer) => {
setConnectingToServer(server.url);
setConnectionError(null);
@@ -168,13 +93,11 @@ export const HTTP = ({ closeDialog }: TabElementProps) => {
// Update last used time
addSavedServer(server.host, server.protocol);
- updateServerStatus(server.url, "online");
closeDialog();
} catch (error) {
console.error("Connection error:", error);
setConnectionError(`Failed to connect to ${server.host}`);
- updateServerStatus(server.url, "offline");
} finally {
setConnectingToServer(null);
}