diff --git a/src/main/updater.test.ts b/src/main/updater.test.ts index 7c460aa09..b455d3c1a 100644 --- a/src/main/updater.test.ts +++ b/src/main/updater.test.ts @@ -183,6 +183,66 @@ describe('main/updater.ts', () => { ).toHaveBeenCalledWith(false); }); + it('auto-hides "No updates available" after configured timeout', async () => { + jest.useFakeTimers(); + try { + await updater.start(); + ( + menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock + ).mockClear(); + + emit('update-not-available'); + // Immediately shows the message + expect( + menuBuilder.setNoUpdateAvailableMenuVisibility, + ).toHaveBeenCalledWith(true); + + // Then hides it after the configured timeout + jest.advanceTimersByTime(APPLICATION.UPDATE_NOT_AVAILABLE_DISPLAY_MS); + expect( + menuBuilder.setNoUpdateAvailableMenuVisibility, + ).toHaveBeenLastCalledWith(false); + } finally { + jest.useRealTimers(); + } + }); + + it('clears pending hide timer when a new check starts', async () => { + jest.useFakeTimers(); + try { + await updater.start(); + ( + menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock + ).mockClear(); + + emit('update-not-available'); + // Message shown + expect( + menuBuilder.setNoUpdateAvailableMenuVisibility, + ).toHaveBeenCalledWith(true); + + // New check should hide immediately and clear pending timeout + emit('checking-for-update'); + expect( + menuBuilder.setNoUpdateAvailableMenuVisibility, + ).toHaveBeenLastCalledWith(false); + + const callsBefore = ( + menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock + ).mock.calls.length; + jest.advanceTimersByTime( + APPLICATION.UPDATE_NOT_AVAILABLE_DISPLAY_MS * 2, + ); + // No additional hide call due to cleared timeout + expect( + (menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock).mock + .calls.length, + ).toBe(callsBefore); + } finally { + jest.useRealTimers(); + } + }); + it('handles update-cancelled (reset state)', async () => { await updater.start(); emit('update-cancelled'); diff --git a/src/main/updater.ts b/src/main/updater.ts index fd37bea02..e2ce02372 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -22,6 +22,7 @@ export default class AppUpdater { private readonly menubar: Menubar; private readonly menuBuilder: MenuBuilder; private started = false; + private noUpdateMessageTimeout?: NodeJS.Timeout; constructor(menubar: Menubar, menuBuilder: MenuBuilder) { this.menubar = menubar; @@ -56,6 +57,9 @@ export default class AppUpdater { logInfo('auto updater', 'Checking for update'); this.menuBuilder.setCheckForUpdatesMenuEnabled(false); this.menuBuilder.setNoUpdateAvailableMenuVisibility(false); + + // Clear any existing timeout when starting a new check + this.clearNoUpdateTimeout(); }); autoUpdater.on('update-available', () => { @@ -84,6 +88,12 @@ export default class AppUpdater { this.menuBuilder.setNoUpdateAvailableMenuVisibility(true); this.menuBuilder.setUpdateAvailableMenuVisibility(false); this.menuBuilder.setUpdateReadyForInstallMenuVisibility(false); + + // Auto-hide the "no updates available" message + this.clearNoUpdateTimeout(); + this.noUpdateMessageTimeout = setTimeout(() => { + this.menuBuilder.setNoUpdateAvailableMenuVisibility(false); + }, APPLICATION.UPDATE_NOT_AVAILABLE_DISPLAY_MS); }); autoUpdater.on('update-cancelled', () => { @@ -121,12 +131,22 @@ export default class AppUpdater { this.menubar.tray.setToolTip(`${APPLICATION.NAME}\n${status}`); } + private clearNoUpdateTimeout() { + if (this.noUpdateMessageTimeout) { + clearTimeout(this.noUpdateMessageTimeout); + this.noUpdateMessageTimeout = undefined; + } + } + private resetState() { this.menubar.tray.setToolTip(APPLICATION.NAME); this.menuBuilder.setCheckForUpdatesMenuEnabled(true); this.menuBuilder.setNoUpdateAvailableMenuVisibility(false); this.menuBuilder.setUpdateAvailableMenuVisibility(false); this.menuBuilder.setUpdateReadyForInstallMenuVisibility(false); + + // Clear any pending timeout + this.clearNoUpdateTimeout(); } private showUpdateReadyDialog(releaseName: string) { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index f3c05e48e..0315b8720 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -16,4 +16,6 @@ export const APPLICATION = { NOTIFICATION_SOUND: 'clearly.mp3', UPDATE_CHECK_INTERVAL_MS: 24 * 60 * 60 * 1000, // 24 hours + + UPDATE_NOT_AVAILABLE_DISPLAY_MS: 60 * 1000, // 60 seconds };