diff --git a/core/internal/server/sysupdate/manager.go b/core/internal/server/sysupdate/manager.go index ce3681e5d..9f300ac97 100644 --- a/core/internal/server/sysupdate/manager.go +++ b/core/internal/server/sysupdate/manager.go @@ -183,13 +183,17 @@ func (m *Manager) Close() { }) } +// Zero turns scheduled checks off; manual and startup checks still run. func (m *Manager) SetInterval(seconds int) { - if seconds < minIntervalSeconds { + if seconds > 0 && seconds < minIntervalSeconds { seconds = minIntervalSeconds } m.mu.Lock() - m.state.IntervalSeconds = seconds - m.state.NextCheckUnix = m.nextCheckLocked() + m.state.IntervalSeconds = max(seconds, 0) + m.state.NextCheckUnix = 0 + if seconds > 0 { + m.state.NextCheckUnix = m.nextCheckLocked() + } m.mu.Unlock() m.wake() m.markDirty() @@ -284,7 +288,11 @@ func (m *Manager) wake() { func (m *Manager) scheduler() { defer m.schedulerWG.Done() for { - if !m.held() { + m.mu.RLock() + interval := m.state.IntervalSeconds + next := m.state.NextCheckUnix + m.mu.RUnlock() + if !m.held() || interval == 0 { select { case <-m.stopChan: return @@ -293,10 +301,6 @@ func (m *Manager) scheduler() { continue } - m.mu.RLock() - interval := m.state.IntervalSeconds - next := m.state.NextCheckUnix - m.mu.RUnlock() if interval < minIntervalSeconds { interval = minIntervalSeconds } diff --git a/core/internal/server/sysupdate/shell_test.go b/core/internal/server/sysupdate/shell_test.go index c83d5fb39..b7813d00b 100644 --- a/core/internal/server/sysupdate/shell_test.go +++ b/core/internal/server/sysupdate/shell_test.go @@ -222,6 +222,11 @@ func TestSetIntervalSchedulesFromLastCheck(t *testing.T) { if next := m.GetState().NextCheckUnix; next < now+startupGraceSeconds-5 || next > now+startupGraceSeconds+5 { t.Errorf("overdue next = now+%d, want now+%d", next-now, startupGraceSeconds) } + + m.SetInterval(0) + if state := m.GetState(); state.IntervalSeconds != 0 || state.NextCheckUnix != 0 { + t.Errorf("never: interval = %d, next = %d, want both 0", state.IntervalSeconds, state.NextCheckUnix) + } } func TestRefreshWithoutBackendsAdvancesDeadline(t *testing.T) { diff --git a/quickshell/Modules/Settings/SoftwareUpdatesTab.qml b/quickshell/Modules/Settings/SoftwareUpdatesTab.qml index b62c33cd8..70dbf281c 100644 --- a/quickshell/Modules/Settings/SoftwareUpdatesTab.qml +++ b/quickshell/Modules/Settings/SoftwareUpdatesTab.qml @@ -43,6 +43,10 @@ Item { { label: I18n.tr("Once a day"), seconds: 86400 + }, + { + label: I18n.tr("Never"), + seconds: 0 } ] diff --git a/quickshell/Services/SystemUpdateService.qml b/quickshell/Services/SystemUpdateService.qml index e8a400073..ff130dc94 100644 --- a/quickshell/Services/SystemUpdateService.qml +++ b/quickshell/Services/SystemUpdateService.qml @@ -186,7 +186,7 @@ Singleton { distributionPretty = data.distroPretty || ""; distributionSupported = (backends.length > 0); recentLog = data.recentLog || []; - intervalSeconds = data.intervalSeconds || 86400; + intervalSeconds = data.intervalSeconds ?? 86400; const checked = data.lastCheckUnix || 0; const freshCheck = _stateSeeded && checked > lastCheckUnix; _stateSeeded = true;