Skip to content

feat(desktop): add configurable transport reconnect hook#1059

Open
wpfleger96 wants to merge 8 commits into
mainfrom
duncan/relay-reconnect-hook
Open

feat(desktop): add configurable transport reconnect hook#1059
wpfleger96 wants to merge 8 commits into
mainfrom
duncan/relay-reconnect-hook

Conversation

@wpfleger96

Copy link
Copy Markdown
Collaborator

Adds a build-time-configured transport reconnect hook so internal builds can recover the underlying transport (e.g. WARP VPN) before the relay reconnect, while OSS builds stay a pure no-op.

The generic "Reconnect to relay" button is transport-agnostic; internal users behind a VPN need the transport re-established first. A build-time env var BUZZ_BUILD_RELAY_RECONNECT_CMD (set only in squareup/buzz-releases) carries a JSON config validated into a typed Rust struct at compile time, so the OSS binary ships with zero VPN knowledge.

  • New Tauri command relay_reconnect_hook runs structured fixed-argv steps plus a readiness probe, wrapped in tokio::task::spawn_blocking; non-fatal end-to-end so any failure falls through to preconnect().
  • ReconnectHookConfig lives in one dep-free source file include!'d by both build.rs and the runtime command, so the compile-time validation and runtime parse cannot drift.
  • useReconnectRelay.ts invokes the hook before relay preconnect, guarded so a hook rejection cannot abort the reconnect.
  • When BUZZ_BUILD_RELAY_RECONNECT_CMD is unset (OSS), the command compiles to an early-return no-op.

@wpfleger96 wpfleger96 force-pushed the duncan/relay-reconnect-hook branch from ba254b1 to 16191d3 Compare June 15, 2026 22:10
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 4 commits June 15, 2026 18:55
Build-time env var BUZZ_BUILD_RELAY_RECONNECT_CMD carries a JSON config
blob validated into a typed ReconnectHookConfig struct at compile time.
If unset (OSS builds), the Tauri command is a pure no-op. If set, the
frontend invokes it before relay preconnect() to run transport-layer
recovery steps (e.g. warp-cli connect + access-reauth) and poll for
readiness. All subprocess steps are non-fatal — timeout or failure falls
through to the existing relay reconnect.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Rust Lint CI (cargo fmt --check) flagged the from_value closure in
build.rs and the Command builder chain in relay_reconnect.rs. Let
cargo fmt produce the canonical reflow so CI matches byte-for-byte.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
std::process::Command::output() blocks until the child exits, so a wedged
warp-cli (the degraded-transport case this hook targets) would hang the
spawn_blocking task forever — the frontend invoke never resolves, the
Reconnect button spins, and a blocking-pool thread is pinned. timeout_ms
only gated the poll loop between probe attempts, not any single subprocess.

Route the step loop and the probe through a spawn + try_wait + kill-on-deadline
helper modeled on media_transcode.rs run_ffmpeg_with_timeout, so each child is
killed and reaped at the cap. Steps reuse timeout_ms per step; the probe phase
stays a single timeout_ms ceiling by capping each attempt at the remaining
budget. No config-schema change.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Stdio::null() replaces Stdio::piped() for stderr since the helper never
reads it — removes a latent deadlock if a future command floods the OS
pipe buffer. Doc comment on timeout_ms now accurately describes its
broader role as a per-process wall-clock cap.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the duncan/relay-reconnect-hook branch from 16191d3 to 01680d9 Compare June 15, 2026 22:55
@wpfleger96 wpfleger96 enabled auto-merge (squash) June 15, 2026 23:41
@wpfleger96 wpfleger96 disabled auto-merge June 15, 2026 23:41
@wpfleger96 wpfleger96 enabled auto-merge (squash) June 15, 2026 23:41
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 3 commits June 15, 2026 19:42
The sidebar reconnect prompt only appeared once channelsQuery errored,
which lags 60-120s behind a dropped socket (React Query staleTime +
refetchInterval 60s plus one retry). OR-in the live, debounced
connection state already powering ConnectionBanner so the prompt
surfaces within ~2s of degradation, regardless of query error timing.
Cached channels stay visible alongside the prompt.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The Reconnect to relay item in the profile popover rendered unconditionally
because SidebarProfileCard always passed a truthy onReconnect. Gate it on the
same isRelayConnectionDegraded live-state check the sidebar prompt uses so the
popover hides the item when healthy. The || isPending guard keeps it visible
while a reconnect is in flight so it does not vanish mid-click.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Captures the profile-popover reconnect item and the sidebar relay-unreachable block in both healthy and degraded relay states, driving the real connectionStateEmitter via the E2E bridge. Each test asserts the failable gate condition before screenshotting so a regression breaks the capture, not just the image. Registers the spec in the smoke testMatch allowlist (both Playwright projects use explicit allowlists; an unlisted spec yields no tests).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jun 16, 2026
The 03/04 sidebar screenshots were visually identical: the
sidebar-relay-unreachable block renders at the bottom of the scroll
region, painted under the absolute z-30 profile footer, so it was
occluded even though toBeVisible passed (it is in-DOM). The prior
scroll helper matched the wrong overflowing descendant and no-opped.

Scroll the exact [data-sidebar="content"] container fully down before
capture so the block clears the footer, applied symmetrically to 03
(absent) and 04 (present).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jun 16, 2026
@wpfleger96

Copy link
Copy Markdown
Collaborator Author

Relay reconnect affordances — degraded-state gating

Profile popover — healthy (item hidden)

01-profile-popover-healthy
The Reconnect to relay item is absent when the relay is connected.

Profile popover — degraded (item shown)

02-profile-popover-degraded
Driving disconnected surfaces the reconnect item reactively.

Sidebar — healthy (no prompt)

03-sidebar-healthy
No relay-unreachable block when the connection is healthy.

Sidebar — degraded (prompt + channels)

04-sidebar-degraded
The dual-signal block surfaces the reconnect prompt while the channel list stays visible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant