Skip to content

Commit 206f571

Browse files
authored
feat(menu): auto-hide no updates available (#2390)
* feat(menu): no updates available auto-hide Signed-off-by: Adam Setch <adam.setch@outlook.com> * feat(menu): no updates available auto-hide Signed-off-by: Adam Setch <adam.setch@outlook.com> --------- Signed-off-by: Adam Setch <adam.setch@outlook.com>
1 parent 606df87 commit 206f571

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

src/main/updater.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,66 @@ describe('main/updater.ts', () => {
183183
).toHaveBeenCalledWith(false);
184184
});
185185

186+
it('auto-hides "No updates available" after configured timeout', async () => {
187+
jest.useFakeTimers();
188+
try {
189+
await updater.start();
190+
(
191+
menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock
192+
).mockClear();
193+
194+
emit('update-not-available');
195+
// Immediately shows the message
196+
expect(
197+
menuBuilder.setNoUpdateAvailableMenuVisibility,
198+
).toHaveBeenCalledWith(true);
199+
200+
// Then hides it after the configured timeout
201+
jest.advanceTimersByTime(APPLICATION.UPDATE_NOT_AVAILABLE_DISPLAY_MS);
202+
expect(
203+
menuBuilder.setNoUpdateAvailableMenuVisibility,
204+
).toHaveBeenLastCalledWith(false);
205+
} finally {
206+
jest.useRealTimers();
207+
}
208+
});
209+
210+
it('clears pending hide timer when a new check starts', async () => {
211+
jest.useFakeTimers();
212+
try {
213+
await updater.start();
214+
(
215+
menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock
216+
).mockClear();
217+
218+
emit('update-not-available');
219+
// Message shown
220+
expect(
221+
menuBuilder.setNoUpdateAvailableMenuVisibility,
222+
).toHaveBeenCalledWith(true);
223+
224+
// New check should hide immediately and clear pending timeout
225+
emit('checking-for-update');
226+
expect(
227+
menuBuilder.setNoUpdateAvailableMenuVisibility,
228+
).toHaveBeenLastCalledWith(false);
229+
230+
const callsBefore = (
231+
menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock
232+
).mock.calls.length;
233+
jest.advanceTimersByTime(
234+
APPLICATION.UPDATE_NOT_AVAILABLE_DISPLAY_MS * 2,
235+
);
236+
// No additional hide call due to cleared timeout
237+
expect(
238+
(menuBuilder.setNoUpdateAvailableMenuVisibility as jest.Mock).mock
239+
.calls.length,
240+
).toBe(callsBefore);
241+
} finally {
242+
jest.useRealTimers();
243+
}
244+
});
245+
186246
it('handles update-cancelled (reset state)', async () => {
187247
await updater.start();
188248
emit('update-cancelled');

src/main/updater.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default class AppUpdater {
2222
private readonly menubar: Menubar;
2323
private readonly menuBuilder: MenuBuilder;
2424
private started = false;
25+
private noUpdateMessageTimeout?: NodeJS.Timeout;
2526

2627
constructor(menubar: Menubar, menuBuilder: MenuBuilder) {
2728
this.menubar = menubar;
@@ -56,6 +57,9 @@ export default class AppUpdater {
5657
logInfo('auto updater', 'Checking for update');
5758
this.menuBuilder.setCheckForUpdatesMenuEnabled(false);
5859
this.menuBuilder.setNoUpdateAvailableMenuVisibility(false);
60+
61+
// Clear any existing timeout when starting a new check
62+
this.clearNoUpdateTimeout();
5963
});
6064

6165
autoUpdater.on('update-available', () => {
@@ -84,6 +88,12 @@ export default class AppUpdater {
8488
this.menuBuilder.setNoUpdateAvailableMenuVisibility(true);
8589
this.menuBuilder.setUpdateAvailableMenuVisibility(false);
8690
this.menuBuilder.setUpdateReadyForInstallMenuVisibility(false);
91+
92+
// Auto-hide the "no updates available" message
93+
this.clearNoUpdateTimeout();
94+
this.noUpdateMessageTimeout = setTimeout(() => {
95+
this.menuBuilder.setNoUpdateAvailableMenuVisibility(false);
96+
}, APPLICATION.UPDATE_NOT_AVAILABLE_DISPLAY_MS);
8797
});
8898

8999
autoUpdater.on('update-cancelled', () => {
@@ -121,12 +131,22 @@ export default class AppUpdater {
121131
this.menubar.tray.setToolTip(`${APPLICATION.NAME}\n${status}`);
122132
}
123133

134+
private clearNoUpdateTimeout() {
135+
if (this.noUpdateMessageTimeout) {
136+
clearTimeout(this.noUpdateMessageTimeout);
137+
this.noUpdateMessageTimeout = undefined;
138+
}
139+
}
140+
124141
private resetState() {
125142
this.menubar.tray.setToolTip(APPLICATION.NAME);
126143
this.menuBuilder.setCheckForUpdatesMenuEnabled(true);
127144
this.menuBuilder.setNoUpdateAvailableMenuVisibility(false);
128145
this.menuBuilder.setUpdateAvailableMenuVisibility(false);
129146
this.menuBuilder.setUpdateReadyForInstallMenuVisibility(false);
147+
148+
// Clear any pending timeout
149+
this.clearNoUpdateTimeout();
130150
}
131151

132152
private showUpdateReadyDialog(releaseName: string) {

src/shared/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ export const APPLICATION = {
1616
NOTIFICATION_SOUND: 'clearly.mp3',
1717

1818
UPDATE_CHECK_INTERVAL_MS: 24 * 60 * 60 * 1000, // 24 hours
19+
20+
UPDATE_NOT_AVAILABLE_DISPLAY_MS: 60 * 1000, // 60 seconds
1921
};

0 commit comments

Comments
 (0)