Browse Source

Remove HTTP server status checking

- Remove automatic server status checking from HTTP components
  - Eliminate status tracking overhead in HTTPServerSection and HTTP tab
  - Simplify persistent server list by removing status indicators
  - Clean up unused imports and status-related functions
  - Replace || with ?? for nullish coalescing consistency
pull/645/head
tpaulsoncdw 1 year ago
parent
commit
523cdbc8f9
  1. 10
      src/components/Dashboard/BluetoothSection.tsx
  2. 105
      src/components/Dashboard/HTTPServerSection.tsx
  3. 12
      src/components/Dashboard/SerialSection.tsx
  4. 51
      src/components/PageComponents/Connect/HTTP.test.tsx
  5. 79
      src/components/PageComponents/Connect/HTTP.tsx

10
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 (
<Circle className="h-3 w-3 fill-yellow-500 text-yellow-500 animate-spin" />
);
} else if (isConnected) {
}
if (isConnected) {
return <Circle className="h-3 w-3 fill-blue-500 text-blue-500" />;
} else {
return <Circle className="h-3 w-3 fill-slate-300 text-slate-300" />;
}
return <Circle className="h-3 w-3 fill-slate-300 text-slate-300" />;
};
const getStatusText = (device: BluetoothDevice) => {
@ -148,7 +148,7 @@ export const BluetoothSection = ({ onConnect }: BluetoothSectionProps) => {
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium text-slate-900 dark:text-slate-100 truncate">
{device.name || "Unknown Device"}
{device.name ?? "Unknown Device"}
</span>
<Bluetooth className="h-4 w-4 text-blue-500" />
</div>

105
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 <Circle className="h-3 w-3 fill-green-500 text-green-500" />;
case "offline":
return <Circle className="h-3 w-3 fill-red-500 text-red-500" />;
case "checking":
return (
<Circle className="h-3 w-3 fill-yellow-500 text-yellow-500 animate-spin" />
);
default:
return <Circle className="h-3 w-3 fill-slate-300 text-slate-300" />;
}
};
const getSecurityIcon = (protocol: "http" | "https") => {
return protocol === "https"
? <Lock className="h-4 w-4 text-green-600" />
: <LockOpen className="h-4 w-4 text-yellow-600" />;
};
const getStatusText = (status?: string) => {
switch (status) {
case "online":
return "Online";
case "offline":
return "Offline";
case "checking":
return "Checking...";
default:
return "Unknown";
}
};
return (
<>
<div className="space-y-3">
@ -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 */}
<div className="flex-shrink-0">
{getStatusIcon(server.status)}
</div>
{/* Server Info */}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
@ -262,29 +182,6 @@ export const HTTPServerSection = ({ onConnect }: HTTPServerSectionProps) => {
</span>
{getSecurityIcon(server.protocol)}
</div>
<div className="flex items-center gap-3 text-xs text-slate-500 dark:text-slate-400 mt-1">
<span>{getStatusText(server.status)}</span>
{server.deviceInfo?.model && (
<>
<span></span>
<span className="font-mono bg-slate-200 dark:bg-slate-600 px-1 rounded">
{server.deviceInfo.model}
</span>
</>
)}
{server.deviceInfo?.nodeCount && (
<>
<span></span>
<span className="flex items-center gap-1">
<Users className="h-3 w-3" />
{server.deviceInfo.nodeCount}
</span>
</>
)}
</div>
</div>
{/* Actions */}

12
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 (
<Circle className="h-3 w-3 fill-yellow-500 text-yellow-500 animate-spin" />
);
} else if (isConnected) {
}
if (isConnected) {
return <Circle className="h-3 w-3 fill-green-500 text-green-500" />;
} else {
return <Circle className="h-3 w-3 fill-slate-400 text-slate-400" />;
}
return <Circle className="h-3 w-3 fill-slate-400 text-slate-400" />;
};
const getStatusText = (port: SerialPort) => {
@ -145,7 +145,9 @@ export const SerialSection = ({ onConnect }: SerialSectionProps) => {
: (
serialPorts.map((port, index) => (
<div
key={`${port}-${index}`}
key={`serial-${port.getInfo().usbVendorId ?? "unknown"}-${
port.getInfo().usbProductId ?? "unknown"
}`}
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 */}

51
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(<Http closeDialog={vi.fn()} />);
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(<Http closeDialog={vi.fn()} />);
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(<Http closeDialog={vi.fn()} />);
// 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(<Http closeDialog={vi.fn()} />);
// 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 () => {

79
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);
}

Loading…
Cancel
Save