mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:24:18 -04:00
[client] Fix Exit Node submenu separator accumulation on Windows (#5691)
* client/ui: fix Exit Node submenu separator accumulation on Windows On Windows the tray uses a background poller (every 10s) instead of TrayOpenedCh to keep the Exit Node menu fresh. Each poll that has a selected exit node called s.mExitNode.AddSeparator() before the "Deselect All" item. Because AddSeparator() returns no handle the separator was never removed in the cleanup pass of recreateExitNodeMenu(), while every other item (exit node checkboxes and the "Deselect All" entry) was properly tracked and removed. After the client has been running for a while with an exit node selected this leaves hundreds of separator lines stacked in the submenu, filling the screen height with blank entries (#4702). On Linux/FreeBSD this is masked because the parent mExitNode item itself is removed and recreated each cycle, wiping all children including orphaned separators. Fix: replace the untracked AddSeparator() call with a regular disabled sub-menu item that is stored in mExitNodeSeparator and removed at the start of each recreateExitNodeMenu() call alongside mExitNodeDeselectAll. Fixes #4702 * client/ui: extract addExitNodeDeselectAll to reduce cognitive complexity Move the separator + deselect-all creation and its goroutine listener out of recreateExitNodeMenu into a dedicated helper, bringing the function's cognitive complexity back under the SonarCloud threshold.
This commit is contained in:
@@ -324,6 +324,7 @@ type serviceClient struct {
|
||||
exitNodeMu sync.Mutex
|
||||
mExitNodeItems []menuHandler
|
||||
exitNodeRetryCancel context.CancelFunc
|
||||
mExitNodeSeparator *systray.MenuItem
|
||||
mExitNodeDeselectAll *systray.MenuItem
|
||||
logFile string
|
||||
wLoginURL fyne.Window
|
||||
|
||||
@@ -421,6 +421,10 @@ func (s *serviceClient) recreateExitNodeMenu(exitNodes []*proto.Network) {
|
||||
node.Remove()
|
||||
}
|
||||
s.mExitNodeItems = nil
|
||||
if s.mExitNodeSeparator != nil {
|
||||
s.mExitNodeSeparator.Remove()
|
||||
s.mExitNodeSeparator = nil
|
||||
}
|
||||
if s.mExitNodeDeselectAll != nil {
|
||||
s.mExitNodeDeselectAll.Remove()
|
||||
s.mExitNodeDeselectAll = nil
|
||||
@@ -453,31 +457,37 @@ func (s *serviceClient) recreateExitNodeMenu(exitNodes []*proto.Network) {
|
||||
}
|
||||
|
||||
if showDeselectAll {
|
||||
s.mExitNode.AddSeparator()
|
||||
deselectAllItem := s.mExitNode.AddSubMenuItem("Deselect All", "Deselect All")
|
||||
s.mExitNodeDeselectAll = deselectAllItem
|
||||
go func() {
|
||||
for {
|
||||
_, ok := <-deselectAllItem.ClickedCh
|
||||
if !ok {
|
||||
// channel closed: exit the goroutine
|
||||
return
|
||||
}
|
||||
exitNodes, err := s.handleExitNodeMenuDeselectAll()
|
||||
if err != nil {
|
||||
log.Warnf("failed to handle deselect all exit nodes: %v", err)
|
||||
} else {
|
||||
s.exitNodeMu.Lock()
|
||||
s.recreateExitNodeMenu(exitNodes)
|
||||
s.exitNodeMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
s.addExitNodeDeselectAll()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *serviceClient) addExitNodeDeselectAll() {
|
||||
sep := s.mExitNode.AddSubMenuItem("───────────────", "")
|
||||
sep.Disable()
|
||||
s.mExitNodeSeparator = sep
|
||||
|
||||
deselectAllItem := s.mExitNode.AddSubMenuItem("Deselect All", "Deselect All")
|
||||
s.mExitNodeDeselectAll = deselectAllItem
|
||||
|
||||
go func() {
|
||||
for {
|
||||
_, ok := <-deselectAllItem.ClickedCh
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
exitNodes, err := s.handleExitNodeMenuDeselectAll()
|
||||
if err != nil {
|
||||
log.Warnf("failed to handle deselect all exit nodes: %v", err)
|
||||
} else {
|
||||
s.exitNodeMu.Lock()
|
||||
s.recreateExitNodeMenu(exitNodes)
|
||||
s.exitNodeMu.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *serviceClient) getExitNodes(conn proto.DaemonServiceClient) ([]*proto.Network, error) {
|
||||
ctx, cancel := context.WithTimeout(s.ctx, defaultFailTimeout)
|
||||
defer cancel()
|
||||
|
||||
Reference in New Issue
Block a user