From 6770e8d510bc20d8bb101459a5b2716d95e92a44 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 22 Jan 2026 21:31:45 +0700 Subject: [PATCH] fix(app): remove test connection button that broke host status The Test button in settings was creating a separate DaemonClientV2 instance without the Tauri WebSocket transport. In the desktop app, this caused origin rejection (tauri://localhost) which then overwrote the connection status to offline, even though the main session connection was working fine. Removed the redundant test functionality since: - The daemon client auto-reconnects - Connection status is already tracked and displayed via SessionProvider - The test was actively harmful by creating a rogue client Also simplified waitForDaemonRestart to rely on auto-reconnect instead of polling with a separate connection. --- packages/app/src/app/settings.tsx | 155 +++--------------------------- 1 file changed, 12 insertions(+), 143 deletions(-) diff --git a/packages/app/src/app/settings.tsx b/packages/app/src/app/settings.tsx index bf2e7fc76..9c5e07bed 100644 --- a/packages/app/src/app/settings.tsx +++ b/packages/app/src/app/settings.tsx @@ -22,8 +22,7 @@ import { formatConnectionStatus, getConnectionStatusTone } from "@/utils/daemons import { theme as defaultTheme } from "@/styles/theme"; import { MenuHeader } from "@/components/headers/menu-header"; import { useSessionStore } from "@/stores/session-store"; -import { DaemonClientV2 } from "@server/client/daemon-client-v2"; -import { buildDaemonWebSocketUrl, normalizeHostPort } from "@/utils/daemon-endpoints"; +import { normalizeHostPort } from "@/utils/daemon-endpoints"; const delay = (ms: number) => new Promise((resolve) => { @@ -374,10 +373,6 @@ const styles = StyleSheet.create((theme) => ({ }, })); -type DaemonTestState = { - status: "idle" | "testing" | "success" | "error"; - message?: string; -}; export default function SettingsScreen() { const { settings, isLoading: settingsLoading, updateSettings, resetSettings } = useAppSettings(); @@ -400,7 +395,6 @@ export default function SettingsScreen() { offerUrl: string; }>({ id: null, label: "", endpoint: "", offerUrl: "" }); const [isSavingDaemon, setIsSavingDaemon] = useState(false); - const [daemonTestStates, setDaemonTestStates] = useState>(() => new Map()); const isLoading = settingsLoading || daemonLoading; const isMountedRef = useRef(true); @@ -427,40 +421,6 @@ export default function SettingsScreen() { [] ); - const testServerConnection = useCallback(async (url: string, timeoutMs = 5000) => { - const client = new DaemonClientV2({ - url, - suppressSendErrors: true, - reconnect: { enabled: false }, - }); - let timeoutId: ReturnType | null = null; - - try { - await new Promise((resolve, reject) => { - timeoutId = setTimeout(() => { - reject(new Error("Connection timeout - server did not respond")); - }, timeoutMs); - - client - .connect() - .then(resolve) - .catch((error) => { - const message = - error instanceof Error - ? error.message - : "Connection failed - check URL and network"; - console.error("[Settings] Daemon test error", { url, message }); - reject(new Error(message)); - }); - }); - } finally { - if (timeoutId !== null) { - clearTimeout(timeoutId); - } - await client.close(); - } - }, []); - const handleOpenDaemonForm = useCallback((profile?: DaemonProfile) => { if (profile) { setDaemonFormMode("manual"); @@ -591,37 +551,6 @@ export default function SettingsScreen() { [removeDaemon] ); - const updateDaemonTestState = useCallback((daemonId: string, state: { status: "idle" | "testing" | "success" | "error"; message?: string }) => { - setDaemonTestStates((prev) => { - const next = new Map(prev); - next.set(daemonId, state); - return next; - }); - }, []); - - const handleTestDaemonConnection = useCallback( - async (profile: DaemonProfile) => { - const endpoint = profile.endpoints?.[0] ?? null; - if (!endpoint) { - Alert.alert("Missing host", "This host has no endpoints configured."); - return; - } - const url = buildDaemonWebSocketUrl(endpoint); - updateDaemonTestState(profile.id, { status: "testing" }); - updateConnectionStatus(profile.id, { status: "connecting" }); - try { - await testServerConnection(url, 4000); - updateDaemonTestState(profile.id, { status: "success", message: "Reachable" }); - updateConnectionStatus(profile.id, { status: "online", lastOnlineAt: new Date().toISOString() }); - } catch (error) { - const message = error instanceof Error ? error.message : "Connection failed"; - updateDaemonTestState(profile.id, { status: "error", message }); - updateConnectionStatus(profile.id, { status: "offline", lastError: message }); - } - }, - [testServerConnection, updateConnectionStatus, updateDaemonTestState] - ); - const handleToggleUseSpeaker = useCallback( (value: boolean) => { void updateSettings({ ...settings, useSpeaker: value }); @@ -707,20 +636,16 @@ export default function SettingsScreen() { const connection = connectionStates.get(daemon.id); const connectionStatus = connection?.status ?? "idle"; const lastConnectionError = connection?.lastError ?? null; - const testState = daemonTestStates.get(daemon.id); return ( ); @@ -969,13 +894,10 @@ interface DaemonCardProps { daemon: DaemonProfile; connectionStatus: ConnectionStatus; lastError: string | null; - testState?: DaemonTestState; - onTestConnection: (daemon: DaemonProfile) => void; onEdit: (daemon: DaemonProfile) => void; onRemove: (daemon: DaemonProfile) => void; restartConfirmationMessage: string; waitForCondition: (predicate: () => boolean, timeoutMs: number, intervalMs?: number) => Promise; - testServerConnection: (url: string, timeoutMs?: number) => Promise; isScreenMountedRef: MutableRefObject; } @@ -983,17 +905,13 @@ function DaemonCard({ daemon, connectionStatus, lastError, - testState, - onTestConnection, onEdit, onRemove, restartConfirmationMessage, waitForCondition, - testServerConnection, isScreenMountedRef, }: DaemonCardProps) { const { theme } = useUnistyles(); - const directWsUrl = buildDaemonWebSocketUrl(daemon.endpoints?.[0] ?? "localhost:6767"); const statusLabel = formatConnectionStatus(connectionStatus); const statusTone = getConnectionStatusTone(connectionStatus); const statusColor = @@ -1013,56 +931,33 @@ function DaemonCard({ const [isRestarting, setIsRestarting] = useState(false); const isConnected = daemonConnection?.isConnected ?? false; const isConnectedRef = useRef(isConnected); - const isTesting = testState?.status === "testing"; useEffect(() => { isConnectedRef.current = isConnected; }, [isConnected]); const waitForDaemonRestart = useCallback(async () => { - const maxAttempts = 12; - const retryDelayMs = 2500; const disconnectTimeoutMs = 7000; - const reconnectTimeoutMs = 10000; + const reconnectTimeoutMs = 30000; + // Wait for disconnect first if (isConnectedRef.current) { await waitForCondition(() => !isConnectedRef.current, disconnectTimeoutMs); } - for (let attempt = 1; attempt <= maxAttempts; attempt++) { - try { - await testServerConnection(directWsUrl); - const reconnected = await waitForCondition(() => isConnectedRef.current, reconnectTimeoutMs); + // Wait for auto-reconnect + const reconnected = await waitForCondition(() => isConnectedRef.current, reconnectTimeoutMs); - if (isScreenMountedRef.current) { - setIsRestarting(false); - if (!reconnected) { - Alert.alert( - "Host reachable", - `${daemon.label} came back online but Paseo has not reconnected yet.` - ); - } - } - return; - } catch (error) { - console.warn( - `[Settings] Restart poll attempt ${attempt}/${maxAttempts} failed for ${daemon.label}`, - error + if (isScreenMountedRef.current) { + setIsRestarting(false); + if (!reconnected) { + Alert.alert( + "Unable to reconnect", + `${daemon.label} did not come back online. Please verify it restarted.` ); - if (attempt === maxAttempts) { - if (isScreenMountedRef.current) { - setIsRestarting(false); - Alert.alert( - "Unable to reconnect", - `${daemon.label} did not come back online. Please verify it restarted.` - ); - } - return; - } - await delay(retryDelayMs); } } - }, [daemon.label, directWsUrl, isScreenMountedRef, testServerConnection, waitForCondition]); + }, [daemon.label, isScreenMountedRef, waitForCondition]); const beginServerRestart = useCallback(() => { if (!restartServerFn) { @@ -1141,13 +1036,6 @@ function DaemonCard({ ? "rgba(248, 113, 113, 0.1)" : "rgba(161, 161, 170, 0.1)"; - const testResultColor = - testState?.status === "success" - ? theme.colors.palette.green[400] - : testState?.status === "error" - ? theme.colors.palette.red[300] - : theme.colors.foregroundMuted; - return ( @@ -1160,27 +1048,8 @@ function DaemonCard({ {daemon.endpoints?.[0] ?? ""} {connectionError ? {connectionError} : null} - {testState && testState.status !== "idle" ? ( - - {testState.message ?? (testState.status === "success" ? "Reachable" : "Testing...")} - - ) : null} - onTestConnection(daemon)} - disabled={isTesting} - > - - {isTesting ? "Testing..." : "Test"} - - -