diff --git a/_locales/en/messages.json b/_locales/en/messages.json index eae029a62..f409716c3 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1965,7 +1965,7 @@ "message": "There is a new version of Session available." }, "autoUpdateNewVersionInstructions": { - "message": "Press Restart Session to apply the updates." + "message": "Would you like to download the update?" }, "autoUpdateRestartButtonLabel": { "message": "Restart Session" @@ -1973,6 +1973,15 @@ "autoUpdateLaterButtonLabel": { "message": "Later" }, + "autoUpdateDownloadButtonLabel": { + "message": "Download" + }, + "autoUpdateDownloadedMessage": { + "message": "The new update has been downloaded." + }, + "autoUpdateRestartInstructions": { + "message": "Press Restart Session to apply the updates." + }, "leftTheGroup": { "message": "$name$ left the group", "description": diff --git a/ts/updater/common.ts b/ts/updater/common.ts index 1bb73decc..547f01b1f 100644 --- a/ts/updater/common.ts +++ b/ts/updater/common.ts @@ -18,37 +18,57 @@ export type LoggerType = { trace: LogFunction; }; -export async function showUpdateDialog( +export async function showDownloadUpdateDialog( mainWindow: BrowserWindow, messages: MessagesType ): Promise { - const RESTART_BUTTON = 0; + const DOWNLOAD_BUTTON = 0; const LATER_BUTTON = 1; const options = { type: 'info', buttons: [ - messages.autoUpdateRestartButtonLabel.message, + messages.autoUpdateDownloadButtonLabel.message, messages.autoUpdateLaterButtonLabel.message, ], title: messages.autoUpdateNewVersionTitle.message, message: messages.autoUpdateNewVersionMessage.message, detail: messages.autoUpdateNewVersionInstructions.message, defaultId: LATER_BUTTON, - cancelId: RESTART_BUTTON, + cancelId: DOWNLOAD_BUTTON, }; return new Promise(resolve => { dialog.showMessageBox(mainWindow, options, response => { - if (response === RESTART_BUTTON) { - // It's key to delay any install calls here because they don't seem to work inside this - // callback - but only if the message box has a parent window. - // Fixes this: https://github.com/signalapp/Signal-Desktop/issues/1864 - resolve(true); + resolve(response === DOWNLOAD_BUTTON); + }); + }); +} - return; - } +export async function showUpdateDialog( + mainWindow: BrowserWindow, + messages: MessagesType +): Promise { + const RESTART_BUTTON = 0; + const LATER_BUTTON = 1; + const options = { + type: 'info', + buttons: [ + messages.autoUpdateRestartButtonLabel.message, + messages.autoUpdateLaterButtonLabel.message, + ], + title: messages.autoUpdateNewVersionTitle.message, + message: messages.autoUpdateDownloadedMessage.message, + detail: messages.autoUpdateRestartInstructions.message, + defaultId: LATER_BUTTON, + cancelId: RESTART_BUTTON, + }; - resolve(false); + return new Promise(resolve => { + dialog.showMessageBox(mainWindow, options, response => { + // It's key to delay any install calls here because they don't seem to work inside this + // callback - but only if the message box has a parent window. + // Fixes this: https://github.com/signalapp/Signal-Desktop/issues/1864 + resolve(response === RESTART_BUTTON); }); }); } diff --git a/ts/updater/updater.ts b/ts/updater/updater.ts index cd2547d31..abde36b06 100644 --- a/ts/updater/updater.ts +++ b/ts/updater/updater.ts @@ -1,6 +1,6 @@ import * as path from 'path'; import * as fs from 'fs-extra'; -import { autoUpdater } from 'electron-updater'; +import { autoUpdater, UpdateInfo } from 'electron-updater'; import { app, BrowserWindow } from 'electron'; import { markShouldQuit } from '../../app/window_state'; import { @@ -8,10 +8,13 @@ import { LoggerType, MessagesType, showCannotUpdateDialog, + showDownloadUpdateDialog, showUpdateDialog, } from './common'; +import { gt as isVersionGreaterThan, parse as parseVersion } from 'semver'; let isUpdating = false; +let downloadIgnored = false; const SECOND = 1000; const MINUTE = SECOND * 60; @@ -25,6 +28,7 @@ export async function start( logger.info('auto-update: starting checks...'); autoUpdater.logger = logger; + autoUpdater.autoDownload = false; setInterval(async () => { try { @@ -42,7 +46,7 @@ async function checkForUpdates( messages: MessagesType, logger: LoggerType ) { - if (isUpdating) { + if (isUpdating || downloadIgnored) { return; } @@ -51,20 +55,39 @@ async function checkForUpdates( return; } - isUpdating = true; - logger.info('auto-update: checking for update...'); + isUpdating = true; + try { // Get the update using electron-updater + const result = await autoUpdater.checkForUpdates(); + if (!result.updateInfo) { + logger.info('auto-update: no update info received'); + + return; + } + try { - const info = await autoUpdater.checkForUpdates(); - if (!info.downloadPromise) { - logger.info('auto-update: no update to download'); + const hasUpdate = isUpdateAvailable(result.updateInfo); + if (!hasUpdate) { + logger.info('auto-update: no update available'); return; } - await info.downloadPromise; + + logger.info('auto-update: showing download dialog...'); + const shouldDownload = await showDownloadUpdateDialog( + getMainWindow(), + messages + ); + if (!shouldDownload) { + downloadIgnored = true; + + return; + } + + await autoUpdater.downloadUpdate(); } catch (error) { await showCannotUpdateDialog(getMainWindow(), messages); throw error; @@ -85,6 +108,18 @@ async function checkForUpdates( } } +function isUpdateAvailable(updateInfo: UpdateInfo): boolean { + const latestVersion = parseVersion(updateInfo.version); + if (!latestVersion) { + return false; + } + + // We need to convert this to string because typescript won't let us use types across submodules .... + const currentVersion = autoUpdater.currentVersion.toString(); + + return isVersionGreaterThan(latestVersion, currentVersion); +} + /* Check if we have the required files to auto update. These files won't exist inside certain formats such as a linux deb file.