mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-14 04:39:54 +00:00
[client] Fix tray flicker and stuck Connecting during management retry
The status snapshot tore down on every management retry because state.Status() blanks the status when an error is wrapped, and the SubscribeStatus stream propagated that as FailedPrecondition. The UI treated any stream error as "daemon not running" and flickered the tray to Not running between retries. Disconnect was also unresponsive: Down set Idle before the retry goroutine exited, which then overwrote it with Set(Connecting) on the next attempt; the backoff sleep (up to 15s) wasn't context-aware, so the goroutine kept running long after actCancel. - buildStatusResponse falls back to the underlying status (via new state.CurrentStatus) instead of breaking the stream on wrapped errors. - UI only flips to DaemonUnavailable on codes.Unavailable / non-status errors, so a live daemon returning FailedPrecondition is not reported as down. - connect retry uses backoff.WithContext so actCancel interrupts the inter-attempt sleep, and skips Wrap(err) when the dial fails due to ctx cancellation. - Down sets Idle after waiting for giveUpChan, so the retry goroutine can no longer race the disconnect. - Tray hides Connect during Connecting and keeps Disconnect enabled so the user can abort an in-flight connection attempt.
This commit is contained in:
@@ -479,6 +479,7 @@ func (t *Tray) applyStatus(st services.Status) {
|
||||
strings.EqualFold(st.Status, statusSessionExpired) ||
|
||||
strings.EqualFold(st.Status, statusLoginFailed)
|
||||
daemonUnavailable := strings.EqualFold(st.Status, services.StatusDaemonUnavailable)
|
||||
connecting := strings.EqualFold(st.Status, statusConnecting)
|
||||
if t.statusItem != nil {
|
||||
// When the daemon needs re-authentication the status row turns
|
||||
// into the actionable Login entry — Connect would only fail.
|
||||
@@ -496,12 +497,19 @@ func (t *Tray) applyStatus(st services.Status) {
|
||||
t.applyStatusIndicator(st.Status)
|
||||
}
|
||||
if t.upItem != nil {
|
||||
t.upItem.SetHidden(connected || needsLogin || daemonUnavailable)
|
||||
t.upItem.SetEnabled(!connected && !needsLogin && !daemonUnavailable)
|
||||
// Hide Connect whenever an Up action would be a no-op or would
|
||||
// only fail: tunnel already up, daemon mid-connect (Disconnect
|
||||
// takes over the slot so the user can abort), login required,
|
||||
// or daemon socket unreachable.
|
||||
t.upItem.SetHidden(connected || connecting || needsLogin || daemonUnavailable)
|
||||
t.upItem.SetEnabled(!connected && !connecting && !needsLogin && !daemonUnavailable)
|
||||
}
|
||||
if t.downItem != nil {
|
||||
t.downItem.SetHidden(!connected)
|
||||
t.downItem.SetEnabled(connected)
|
||||
// Disconnect is the abort path while the daemon is still
|
||||
// retrying the management dial — without it the user has no
|
||||
// way to stop the loop short of killing the daemon.
|
||||
t.downItem.SetHidden(!connected && !connecting)
|
||||
t.downItem.SetEnabled(connected || connecting)
|
||||
}
|
||||
// Exit Node and Resources surface tunnel-routed state, so only
|
||||
// expose them while the tunnel is up. Settings/Debug-Bundle just
|
||||
|
||||
Reference in New Issue
Block a user