[UI] Fixup advanced Settings

This commit is contained in:
riccardom
2026-06-08 18:06:02 +02:00
parent 6509174400
commit 2be7ef4929
+41 -33
View File
@@ -323,6 +323,12 @@ type serviceClient struct {
isEnforcedUpdate bool isEnforcedUpdate bool
lastNotifiedVersion string lastNotifiedVersion string
settingsEnabled bool settingsEnabled bool
// advancedSettingsLocked caches the last applied MDM lock state for
// the "Advanced Settings" submenu item. Calling Enable/Disable +
// SetTitle on a systray item every 2 s drops the ClickedCh
// subscription, so we only touch the item on transition. Zero value
// (false) matches the default enabled state at AddSubMenuItem time.
advancedSettingsLocked bool
profilesEnabled bool profilesEnabled bool
networksEnabled bool networksEnabled bool
showNetworks bool showNetworks bool
@@ -455,15 +461,12 @@ func (s *serviceClient) updateIcon() {
} }
func (s *serviceClient) showSettingsUI() { func (s *serviceClient) showSettingsUI() {
// Check if update settings are disabled by daemon // DisableUpdateSettings no longer gates the window from opening:
features, err := s.getFeatures() // the daemon blocks every actual mutation at SetConfig / Login,
if err != nil { // so the window is safe to show as a read-only view. The previous
log.Errorf("failed to get features from daemon: %v", err) // early-return also blocked Advanced Settings whenever update
// Continue with default behavior if features can't be retrieved // editing was off, which conflated two distinct kill switches
} else if features != nil && features.DisableUpdateSettings { // (see comment in checkAndUpdateFeatures).
log.Warn("Update settings are disabled by daemon")
return
}
// add settings window UI elements. // add settings window UI elements.
s.wSettings = s.app.NewWindow("NetBird Settings") s.wSettings = s.app.NewWindow("NetBird Settings")
@@ -1069,18 +1072,17 @@ func (s *serviceClient) onTrayReady() {
s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr) s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr)
s.loadSettings() s.loadSettings()
// Disable settings menu if update settings are disabled by daemon // Disable profile menu if profiles are disabled by daemon.
// DisableUpdateSettings is enforced at the daemon's SetConfig /
// Login gates, not by hiding the UI — so the Settings menu (and
// its Advanced Settings submenu, which has its own kill switch)
// stays visible and the user can still inspect current values.
features, err := s.getFeatures() features, err := s.getFeatures()
if err != nil { if err != nil {
log.Errorf("failed to get features from daemon: %v", err) log.Errorf("failed to get features from daemon: %v", err)
// Continue with default behavior if features can't be retrieved // Continue with default behavior if features can't be retrieved
} else { } else if features != nil && features.DisableProfiles {
if features != nil && features.DisableUpdateSettings { s.mProfile.setEnabled(false)
s.setSettingsEnabled(false)
}
if features != nil && features.DisableProfiles {
s.mProfile.setEnabled(false)
}
} }
s.exitNodeMu.Lock() s.exitNodeMu.Lock()
@@ -1261,24 +1263,30 @@ func (s *serviceClient) checkAndUpdateFeatures() {
s.updateIndicationLock.Lock() s.updateIndicationLock.Lock()
defer s.updateIndicationLock.Unlock() defer s.updateIndicationLock.Unlock()
// Update settings menu based on current features // DisableUpdateSettings is enforced server-side by the daemon gates
settingsEnabled := features == nil || !features.DisableUpdateSettings // on SetConfig + Login: any attempt to mutate config from UI or
if s.settingsEnabled != settingsEnabled { // CLI is rejected at that layer. The UI deliberately keeps the
s.settingsEnabled = settingsEnabled // Settings menu visible so the user can still inspect current
s.setSettingsEnabled(settingsEnabled) // values — read-only by virtue of the daemon refusing edits.
} // Hiding the menu here would also hide the Advanced Settings
// submenu, which has its own dedicated kill switch
// (DisableAdvancedSettings); the two concerns must not be coupled.
// MDM kill switch on the Advanced Settings submenu item: when the // MDM kill switch on the Advanced Settings submenu item.
// daemon reports disableAdvancedSettings=true the user must not be // Only flip on transition — calling Enable/SetTitle on every 2 s
// able to open the settings window at all. Title gets the "(MDM)" // loop tick drops the systray ClickedCh subscription on some
// suffix to match the convention used for individual locked fields. // platforms, breaking the click handler entirely.
if s.mAdvancedSettings != nil { if s.mAdvancedSettings != nil {
if features != nil && features.DisableAdvancedSettings { desired := features != nil && features.DisableAdvancedSettings
s.mAdvancedSettings.SetTitle("Advanced Settings (MDM)") if desired != s.advancedSettingsLocked {
s.mAdvancedSettings.Disable() s.advancedSettingsLocked = desired
} else { if desired {
s.mAdvancedSettings.SetTitle("Advanced Settings") s.mAdvancedSettings.SetTitle("Advanced Settings (MDM)")
s.mAdvancedSettings.Enable() s.mAdvancedSettings.Disable()
} else {
s.mAdvancedSettings.SetTitle("Advanced Settings")
s.mAdvancedSettings.Enable()
}
} }
} }