diff --git a/desktop/src/auto-update.js b/desktop/src/auto-update.js new file mode 100644 --- /dev/null +++ b/desktop/src/auto-update.js @@ -0,0 +1,27 @@ +// @flow + +import { app, ipcMain, autoUpdater } from 'electron/main'; + +const getUpdateUrl = version => + `https://electron-update.commtechnologies.org/update/${process.platform}/${version}`; + +export function initAutoUpdate(): void { + autoUpdater.setFeedURL({ url: getUpdateUrl(app.getVersion()) }); + + // Check for new updates every 10 minutes + const updateIntervalMs = 10 * 60_000; + const scheduleCheckForUpdates = () => { + setTimeout(() => autoUpdater.checkForUpdates(), updateIntervalMs); + }; + + scheduleCheckForUpdates(); + + autoUpdater.on('update-not-available', scheduleCheckForUpdates); + + autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { + autoUpdater.setFeedURL({ url: getUpdateUrl(releaseName) }); + scheduleCheckForUpdates(); + }); + + ipcMain.on('update-to-new-version', () => autoUpdater.quitAndInstall()); +} diff --git a/desktop/src/main.js b/desktop/src/main.js --- a/desktop/src/main.js +++ b/desktop/src/main.js @@ -7,10 +7,12 @@ Menu, ipcMain, systemPreferences, + autoUpdater, } from 'electron/main'; import fs from 'fs'; import path from 'path'; +import { initAutoUpdate } from './auto-update'; import { handleSquirrelEvent } from './handle-squirrel-event'; const isDev = process.env.ENV === 'dev'; @@ -140,9 +142,15 @@ }; ipcMain.on('double-click-top-bar', doubleClickTopBar); + const updateDownloaded = (event, releaseNotes, releaseName) => { + win.webContents.send('on-new-version-available', releaseName); + }; + autoUpdater.on('update-downloaded', updateDownloaded); + win.on('closed', () => { ipcMain.removeListener('clear-history', clearHistory); ipcMain.removeListener('double-click-top-bar', doubleClickTopBar); + autoUpdater.removeListener('update-downloaded', updateDownloaded); }); win.webContents.setWindowOpenHandler(({ url: openURL }) => { @@ -237,11 +245,22 @@ (async () => { await app.whenReady(); + if (app.isPackaged) { + try { + initAutoUpdate(); + } catch (error) { + console.error(error); + } + } + ipcMain.on('set-badge', (event, value) => { if (isMac) { app.dock.setBadge(value?.toString() ?? ''); } }); + ipcMain.on('get-version', event => { + event.returnValue = app.getVersion().toString(); + }); show(); diff --git a/desktop/src/preload.js b/desktop/src/preload.js --- a/desktop/src/preload.js +++ b/desktop/src/preload.js @@ -13,6 +13,14 @@ clearHistory: () => ipcRenderer.send('clear-history'), doubleClickTopBar: () => ipcRenderer.send('double-click-top-bar'), setBadge: value => ipcRenderer.send('set-badge', value), + version: ipcRenderer.sendSync('get-version'), + onNewVersionAvailable: callback => { + const withEvent = (event, ...args) => callback(...args); + ipcRenderer.on('on-new-version-available', withEvent); + return () => + ipcRenderer.removeListener('on-new-version-available', withEvent); + }, + updateToNewVersion: () => ipcRenderer.send('update-to-new-version'), }; contextBridge.exposeInMainWorld('electronContextBridge', bridge); diff --git a/lib/types/electron-types.js b/lib/types/electron-types.js --- a/lib/types/electron-types.js +++ b/lib/types/electron-types.js @@ -5,10 +5,16 @@ +canGoForward: boolean, }) => void; +type OnNewVersionAvailableListener = (version: string) => void; + export type ElectronBridge = { // Returns a callback that you can call to remove the listener +onNavigate: OnNavigateListener => () => void, +clearHistory: () => void, +doubleClickTopBar: () => void, +setBadge: (number | string | null) => void, + +version?: string, + // Returns a callback that you can call to remove the listener + +onNewVersionAvailable?: OnNewVersionAvailableListener => () => void, + +updateToNewVersion?: () => void, };