From b0a3b1f18ed9c12eead755681b919a93db1f79b3 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Mon, 25 May 2026 06:59:57 +0400 Subject: [PATCH] fix: 10s timeout on handshake CallAnswer; button stays visible during connect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - handshake.rs: add 10s timeout on recv_signal() waiting for CallAnswer — previously hung forever if relay didn't respond, making join button disappear with no feedback - main.ts: keep join button visible + show "Connecting…" state instead of hiding it before the await; button restores correctly on error Co-Authored-By: Claude Sonnet 4.6 --- crates/wzp-client/src/handshake.rs | 15 +++++++++------ desktop/src/main.ts | 7 +++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/wzp-client/src/handshake.rs b/crates/wzp-client/src/handshake.rs index dd14419..14180eb 100644 --- a/crates/wzp-client/src/handshake.rs +++ b/crates/wzp-client/src/handshake.rs @@ -101,12 +101,15 @@ pub async fn perform_handshake( .await .map_err(HandshakeError::Transport)?; - // 5. Wait for CallAnswer - let answer = transport - .recv_signal() - .await - .map_err(HandshakeError::Transport)? - .ok_or(HandshakeError::ConnectionClosed)?; + // 5. Wait for CallAnswer — 10s timeout guards against relay not responding. + let answer = tokio::time::timeout( + std::time::Duration::from_secs(10), + transport.recv_signal(), + ) + .await + .map_err(|_| HandshakeError::Transport(wzp_proto::TransportError::Timeout { ms: 10_000 }))? + .map_err(HandshakeError::Transport)? + .ok_or(HandshakeError::ConnectionClosed)?; let (callee_identity_pub, callee_ephemeral_pub, callee_signature, _chosen_profile) = match answer { diff --git a/desktop/src/main.ts b/desktop/src/main.ts index 3481718..9161d10 100644 --- a/desktop/src/main.ts +++ b/desktop/src/main.ts @@ -331,7 +331,9 @@ joinVoiceBtn.addEventListener("click", async () => { const s = loadSettings(); if (!relay) { showToast("No relay configured"); return; } connectPending = true; - joinVoiceBtn.classList.add("hidden"); + const origText = joinVoiceBtn.textContent; + joinVoiceBtn.textContent = "Connecting…"; + (joinVoiceBtn as HTMLButtonElement).disabled = true; try { await invoke("connect", { relay: relay.address, @@ -344,9 +346,10 @@ joinVoiceBtn.addEventListener("click", async () => { } catch (e: any) { console.error("connect failed:", e); showToast(`Join failed: ${e}`); - joinVoiceBtn.classList.remove("hidden"); } finally { connectPending = false; + joinVoiceBtn.textContent = origText; + (joinVoiceBtn as HTMLButtonElement).disabled = false; } });