feat: add periodic tray menu refresh and normalize tunnel metrics field names

Add background task to refresh tray menu every 5 seconds to keep status display current. Add RawTunnelMetrics type and normalizeTunnelMetrics helper to handle both snake_case and camelCase field names from backend responses. Update refreshTunnelMetrics to normalize metrics before setting state and explicitly cast active status to boolean.
This commit is contained in:
2026-03-18 10:04:55 +01:00
parent 184192e1c2
commit 0fcea99006
2 changed files with 26 additions and 2 deletions

View File

@@ -665,6 +665,14 @@ pub fn run() {
});
refresh_tray_menu(app.handle());
let app_handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
loop {
refresh_tray_menu(&app_handle);
tauri::async_runtime::sleep(std::time::Duration::from_secs(5)).await;
}
});
Ok(())
})
.on_window_event(|window, event| match event {

View File

@@ -17,6 +17,14 @@ type TunnelMetrics = {
txBytes: number;
};
type RawTunnelMetrics = {
active?: boolean;
rxBytes?: number;
txBytes?: number;
rx_bytes?: number;
tx_bytes?: number;
};
function formatInvokeError(err: unknown, fallback: string) {
if (typeof err === "string" && err.trim().length > 0) {
return err;
@@ -67,6 +75,14 @@ function formatDataSize(bytes: number) {
return `${value >= 100 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`;
}
function normalizeTunnelMetrics(value: RawTunnelMetrics | null | undefined): TunnelMetrics {
return {
active: Boolean(value?.active),
rxBytes: Number(value?.rxBytes ?? value?.rx_bytes ?? 0),
txBytes: Number(value?.txBytes ?? value?.tx_bytes ?? 0)
};
}
export function App() {
const [serverUrl, setServerUrl] = useState("http://localhost");
const [username, setUsername] = useState("");
@@ -84,14 +100,14 @@ export function App() {
async function refreshTunnelMetrics() {
try {
const value = await invoke<TunnelMetrics>("tunnel_metrics");
const value = normalizeTunnelMetrics(await invoke<RawTunnelMetrics>("tunnel_metrics"));
setMetrics(value);
setConnected(value.active);
} catch {
try {
const active = await invoke<boolean>("tunnel_status");
setConnected(active);
setMetrics((current) => ({ ...current, active }));
setMetrics((current) => ({ ...current, active: Boolean(active) }));
} catch {
setMetrics({ active: false, rxBytes: 0, txBytes: 0 });
setConnected(false);