-
Notifications
You must be signed in to change notification settings - Fork 285
Expand file tree
/
Copy pathupdater.ts
More file actions
148 lines (126 loc) · 4.67 KB
/
updater.ts
File metadata and controls
148 lines (126 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { dialog, type MessageBoxOptions } from 'electron';
import log from 'electron-log';
import { autoUpdater } from 'electron-updater';
import type { Menubar } from 'menubar';
import { APPLICATION } from '../shared/constants';
import { logError, logInfo } from '../shared/logger';
import type MenuBuilder from './menu';
/**
* Updater class for handling application updates.
*
* Supports scheduled and manual updates for all platforms.
*
* Documentation: https://www.electron.build/auto-update
*
* NOTE: previously used update-electron-app (Squirrel-focused, no Linux + NSIS). electron-updater gives cross-platform support.
* Caller guarantees app is ready before initialize() is invoked.
*/
export default class AppUpdater {
private readonly menubar: Menubar;
private readonly menuBuilder: MenuBuilder;
private started = false;
constructor(menubar: Menubar, menuBuilder: MenuBuilder) {
this.menubar = menubar;
this.menuBuilder = menuBuilder;
autoUpdater.logger = log;
}
async start(): Promise<void> {
if (this.started) {
return; // idempotent
}
if (!this.menubar.app.isPackaged) {
logInfo(
'app updater',
'Skipping updater since app is in development mode',
);
return;
}
logInfo('app updater', 'Starting updater');
this.registerListeners();
await this.performInitialCheck();
this.schedulePeriodicChecks();
this.started = true;
}
private registerListeners() {
autoUpdater.on('checking-for-update', () => {
logInfo('auto updater', 'Checking for update');
this.menuBuilder.setCheckForUpdatesMenuEnabled(false);
this.menuBuilder.setNoUpdateAvailableMenuVisibility(false);
});
autoUpdater.on('update-available', () => {
logInfo('auto updater', 'Update available');
this.setTooltipWithStatus('A new update is available');
this.menuBuilder.setUpdateAvailableMenuVisibility(true);
});
autoUpdater.on('download-progress', (progressObj) => {
this.setTooltipWithStatus(
`Downloading update: ${progressObj.percent.toFixed(2)}%`,
);
});
autoUpdater.on('update-downloaded', (event) => {
logInfo('auto updater', 'Update downloaded');
this.setTooltipWithStatus('A new update is ready to install');
this.menuBuilder.setUpdateAvailableMenuVisibility(false);
this.menuBuilder.setUpdateReadyForInstallMenuVisibility(true);
this.showUpdateReadyDialog(event.releaseName);
});
autoUpdater.on('update-not-available', () => {
logInfo('auto updater', 'Update not available');
this.menuBuilder.setCheckForUpdatesMenuEnabled(true);
this.menuBuilder.setNoUpdateAvailableMenuVisibility(true);
this.menuBuilder.setUpdateAvailableMenuVisibility(false);
this.menuBuilder.setUpdateReadyForInstallMenuVisibility(false);
});
autoUpdater.on('update-cancelled', () => {
logInfo('auto updater', 'Update cancelled');
this.resetState();
});
autoUpdater.on('error', (err) => {
logError('auto updater', 'Error checking for update', err);
this.resetState();
});
}
private async performInitialCheck() {
try {
logInfo('app updater', 'Checking for updates on application launch');
await autoUpdater.checkForUpdatesAndNotify();
} catch (e) {
logError('auto updater', 'Initial check failed', e as Error);
}
}
private schedulePeriodicChecks() {
setInterval(async () => {
try {
logInfo('app updater', 'Checking for updates on a periodic schedule');
await autoUpdater.checkForUpdatesAndNotify();
} catch (e) {
logError('auto updater', 'Scheduled check failed', e as Error);
}
}, APPLICATION.UPDATE_CHECK_INTERVAL_MS);
}
private setTooltipWithStatus(status: string) {
this.menubar.tray.setToolTip(`${APPLICATION.NAME}\n${status}`);
}
private resetState() {
this.menubar.tray.setToolTip(APPLICATION.NAME);
this.menuBuilder.setCheckForUpdatesMenuEnabled(true);
this.menuBuilder.setNoUpdateAvailableMenuVisibility(false);
this.menuBuilder.setUpdateAvailableMenuVisibility(false);
this.menuBuilder.setUpdateReadyForInstallMenuVisibility(false);
}
private showUpdateReadyDialog(releaseName: string) {
const dialogOpts: MessageBoxOptions = {
type: 'info',
buttons: ['Restart', 'Later'],
title: 'Application Update',
message: `${APPLICATION.NAME} ${releaseName} has been downloaded`,
detail:
'Restart to apply the update. You can also restart later from the tray menu.',
};
dialog.showMessageBox(dialogOpts).then((returnValue) => {
if (returnValue.response === 0) {
autoUpdater.quitAndInstall();
}
});
}
}