[client] Add a Reconnect entry to the tray menu - #6976
Conversation
Collapses the Disconnect -> wait -> Connect sequence users repeat after a sleep/wake, a network roam, or peers reading connected while no traffic flows, into a single tray click. Connect and Disconnect are unchanged. Connection.Reconnect owns the ordering: Down is synchronous, so Up goes out only once the daemon reports the teardown finished, a failed Down aborts instead of stacking an Up on a session that is still up, and a context cancelled between the legs aborts as well. The tray keeps its own share - the in-flight guard, the transitional paint and the failure toast. reconnectCancel doubles as guard and abort handle: a second click cannot send a Down into the in-flight Up, and a Disconnect or quit is not undone by the Up queued behind it. The transitional paint reuses DaemonFeed's switch suppression, which already hides the stale Connected/Idle blink a Down emits and arms the SSO login-watch for the Up that follows.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|



Describe your changes
Adds a Reconnect entry to the desktop tray menu: one click that runs
Downand thenUp, replacing the right-click → Disconnect → wait → right-click → Connect dance users repeat after a sleep/hibernate wake, a Wi-Fi ↔ ethernet ↔ LTE roam, or when peers read as connected but no traffic flows.Existing Connect and Disconnect behaviour is unchanged, and no gRPC, daemon, or CLI surface is touched — the daemon has no reconnect RPC and this does not add one. The sequencing lives in the UI, where
ProfileSwitcheralready does the same thing.client/ui/services/connection.go—Connection.Reconnect(ctx)owns the sequence, next to theUp/Downit composes:Downis synchronous, soUpgoes out only once the daemon reports the teardown finished.Downaborts instead of stacking anUpon a session that is still up.ctxcancelled between the legs aborts too — a Disconnect or a quit racing the round trip must not be undone by a lateUp.client/ui/tray.go— the tray's share: the entry, the in-flight guard, the transitional paint, the failure toast.connected || connecting); with no session to tear down, a reconnect is just Connect.reconnectCancelis non-nil exactly while a round trip is in flight. It does double duty:handleReconnectandrelayoutMenuread it so a second click cannot send aDowninto the in-flightUp, andhandleDisconnect/handleQuitcall it so the queuedUpcannot resurrect the session the user just asked to drop. A click-timeSetEnabled(false)cannot stand in for the first duty —buildMenurecreates every item on relayout, and the status pushes the round trip itself produces are what triggers those relayouts. For the same reason the row is re-enabled by a relayout rather than bySetEnabledon a by-then-detached item.Downcannot grey the entry out for the rest of the session.Down→Up, cancelling one midway can leave the daemon and the CLI's on-disk profile state disagreeing, and thisUpcarries no profile of its own.Transitional feedback reuses
DaemonFeed.BeginProfileSwitch()instead of inventing a "Reconnecting…" state that would have to fight the daemon's status pushes. It already does exactly what aDown→Upneeds: emits an optimisticConnecting, suppresses the staleConnected/Idleblink during teardown, and arms the login-watch so anUplanding inNeedsLogin(an expired session, say) opens browser-login instead of stalling.handleReconnectclearspendingConnectLoginwhen arming it, so the two SSO-handoff mechanisms cannot both fire on oneNeedsLoginpush. On failure the optimistic paint is released and the tray repaints from a freshGet()snapshot — a failedDownleaves the daemon exactly as it was, andSubscribeStatusonly pushes on state changes, so waiting for a push would strand the tray onConnecting.i18n —
tray.menu.reconnectandnotify.error.reconnectadded toenwith descriptions and translated in all nine other bundles, following each locale's existing Connect/Disconnect wording (de"Neu verbinden",ru"Переподключиться",ja"再接続", …) and the phrasing already used bysettings.network.monitor.label.Known gap
The failure repaint above fixes the tray. The React window is fed by the same suppressed stream, so after a failed
Downit can sit on the optimisticConnectinguntil the next real state change. Fixing it properly means aDaemonFeedmethod that clears suppression and re-emits a snapshot — see question 1.Open questions for reviewers
BeginProfileSwitch/CancelProfileSwitchnaming. They are named for the flow they were written for, but the mechanism is theDown→Uptransition this shares, and DaemonFeed's suppression-table doc comment is now describing a flow it no longer exclusively serves. I left the names alone to keep the diff small. If you would rather haveBeginTransition/CancelTransition— ideally with the cancel re-emitting a snapshot, closing the gap above — I am glad to do it in this PR.connectingstate. The entry stays enabled while connecting, so a stuck attempt can be restarted. Say the word if you would rather it wereconnected-only.Issue ticket number and link
Discussion: #6943
Stack
Checklist
Last box left unchecked deliberately: this is a new user-facing feature and discussion #6943 has no maintainer reply yet, which is why the PR is a draft.
client/ui/services/connection_test.gocoversReconnect's three orderings —DownthenUpon the happy path, noUpafter a failedDown, and noUpafter a cancel lands mid-teardown. The tray wiring itself is untested:client/uihas no harness forbuildMenu/handleConnect/handleDisconnect(it needs a liveapplication.App), and the package does not even compile without a builtfrontend/dist. Glad to add coverage there if you have a preferred way to fake the Wails menu.Verified:
gofmt,go build ./client/ui/...,go vet ./client/ui/...,go test ./client/ui/...clean on Windows/amd64, Go 1.26.5.Not verified: no runtime click-through. I have no Wails/pnpm toolchain or running daemon here, so the menu has not been exercised on any of the three platforms — worth a manual pass before this is taken seriously.
Documentation
Select exactly one:
A self-explanatory tray entry with no new setting or flag. If the docs enumerate the tray menu somewhere I missed, point me at it and I will open the matching netbirdio/docs PR.
Docs PR URL (required if "docs added" is checked)
n/a