diff --git a/electron-builder.yml b/electron-builder.yml index 6376682..6494398 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -24,6 +24,8 @@ mac: target: - target: dmg arch: [universal] + - target: zip + arch: [universal] icon: resources/icon.icns category: public.app-category.utilities diff --git a/main/background.ts b/main/background.ts index dd56d96..b4c1d20 100644 --- a/main/background.ts +++ b/main/background.ts @@ -8,6 +8,8 @@ import { createEncryptedStore, initializeStore } from './store'; import { Account, Confirmation, IpcHandlers, MaFileData } from './types'; import { readFile } from 'fs/promises'; import { getConfirmationKey, time } from 'steam-totp'; +import { autoUpdater } from 'electron-updater'; +import { DownloadStatusPage } from './html/download-status'; // Type-safe IPC handler helper function handleIpc( @@ -17,7 +19,6 @@ function handleIpc( ipcMain.handle(channel, handler); } - if (app.isPackaged) { serve({ directory: 'app' }); } else { @@ -27,6 +28,77 @@ if (app.isPackaged) { (async () => { await app.whenReady(); + let updateWindow: Electron.BrowserWindow | null = null; + + const createUpdateWindow = () => { + updateWindow = createWindow('update', { + width: 400, + height: 200, + frame: false, + resizable: false, + }); + + // Load a simple HTML page for the update progress + updateWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(DownloadStatusPage)}`); + + return updateWindow; + }; + + autoUpdater.autoDownload = false; + const result = await autoUpdater.checkForUpdates(); + + // If there's an update available, show the update window and download + if (result?.updateInfo && result.updateInfo.version !== app.getVersion()) { + const choice = dialog.showMessageBoxSync({ + type: 'info', + buttons: ['Download & Install', 'Skip'], + defaultId: 0, + title: 'Update available', + message: `A new version (${app.getVersion()} -> ${result.updateInfo.version}) is available. Download now?`, + detail: 'The app will start after the update is installed.' + }); + + if (choice === 0) { + createUpdateWindow(); + + // Set up progress tracking + autoUpdater.on('download-progress', (progressObj) => { + if (updateWindow && !updateWindow.isDestroyed()) { + updateWindow.webContents.executeJavaScript(` + document.getElementById('progressBar').style.width = '${progressObj.percent}%'; + document.getElementById('progressText').textContent = '${Math.round(progressObj.percent)}% (${Math.round(progressObj.bytesPerSecond / 1024 / 1024)} MB/s)'; + `); + } + }); + + // Wait for download to complete + await new Promise((resolve) => { + autoUpdater.once('update-downloaded', () => { + if (updateWindow && !updateWindow.isDestroyed()) { + updateWindow.close(); + } + + const installChoice = dialog.showMessageBoxSync({ + type: 'question', + buttons: ['Install and Restart', 'Later'], + defaultId: 0, + title: 'Update ready', + message: 'The update has been downloaded. Install and restart now?' + }); + + if (installChoice === 0) { + autoUpdater.quitAndInstall(); + } else { + resolve(); + } + }); + + autoUpdater.downloadUpdate(); + }); + } + } + + // Create main window after update check/download const mainWindow = createWindow('main', { width: 1000, height: 600, @@ -358,4 +430,4 @@ handleIpc('accept-all-confirmations', async () => { resolve(); }); }); -}); \ No newline at end of file +}); diff --git a/main/html/download-status.ts b/main/html/download-status.ts new file mode 100644 index 0000000..9acb190 --- /dev/null +++ b/main/html/download-status.ts @@ -0,0 +1,57 @@ +export const DownloadStatusPage = ` + + + + + + +
+
Downloading update...
+
+
+
+
0%
+
+ + +`; diff --git a/package.json b/package.json index 2e9d125..6669d78 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "electron-serve": "^3.0.0", "electron-store": "^8.2.0", + "electron-updater": "^6.6.2", "steam-totp": "^2.1.2", "steam-user": "^5.2.3", "steamcommunity": "^3.49.0", diff --git a/yarn.lock b/yarn.lock index bd3364a..a77e1a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3398,6 +3398,20 @@ electron-to-chromium@^1.5.227: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.237.tgz#eacf61cef3f6345d0069ab427585c5a04d7084f0" integrity sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg== +electron-updater@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.6.2.tgz#3e65e044f1a99b00d61e200e24de8e709c69ce99" + integrity sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw== + dependencies: + builder-util-runtime "9.3.1" + fs-extra "^10.1.0" + js-yaml "^4.1.0" + lazy-val "^1.0.5" + lodash.escaperegexp "^4.1.2" + lodash.isequal "^4.5.0" + semver "^7.6.3" + tiny-typed-emitter "^2.1.0" + electron@^38.3.0: version "38.3.0" resolved "https://registry.yarnpkg.com/electron/-/electron-38.3.0.tgz#5f8c8bc3153555832a465cbe271b0ae8117f4145" @@ -5153,6 +5167,11 @@ lodash.defaults@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw== + lodash.filter@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" @@ -5168,6 +5187,11 @@ lodash.foreach@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" integrity sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ== +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.map@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" @@ -6365,7 +6389,7 @@ semver@^6.2.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0, semver@^7.7.1, semver@^7.7.2: +semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1, semver@^7.7.2: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==