diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index bcc235d36b..963323044f 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -27,6 +27,8 @@ const PERMITTED_URL_SCHEMES = [ 'mailto:', ]; +const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; + let mainWindow = null; let appQuitting = false; @@ -66,14 +68,57 @@ function onLinkContextMenu(ev, params) { ev.preventDefault(); } +function installUpdate() { + // for some reason, quitAndInstall does not fire the + // before-quit event, so we need to set the flag here. + appQuitting = true; + electron.autoUpdater.quitAndInstall(); +} + +function pollForUpdates() { + try { + electron.autoUpdater.checkForUpdates(); + } catch (e) { + console.log("Couldn't check for update", e); + } +} + +electron.ipcMain.on('install_update', installUpdate); + electron.app.on('ready', () => { - // Enable auto-update once we can codesign on OS X - //electron.autoUpdater.setFeedURL("http://localhost:8888/"); + try { + // For reasons best known to Squirrel, the way it checks for updates + // is completely different between macOS and windows. On macOS, it + // hits a URL that either gives it a 200 with some json or + // 204 No Content. On windows it takes a base path and looks for + // files under that path. + if (process.platform == 'darwin') { + electron.autoUpdater.setFeedURL("https://riot.im/autoupdate/desktop/"); + } else if (process.platform == 'win32') { + electron.autoUpdater.setFeedURL("https://riot.im/download/desktop/win32/"); + } else { + // Squirrel / electron only supports auto-update on these two platforms. + // I'm not even going to try to guess which feed style they'd use if they + // implemented it on Linux, or if it would be different again. + console.log("Auto update not supported on this platform"); + } + // We check for updates ourselves rather than using 'updater' because we need to + // do it in the main process (and we don't really need to check every 10 minutes: + // every hour should be just fine for a desktop app) + // However, we still let the main window listen for the update events. + pollForUpdates(); + setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS); + } catch (err) { + // will fail if running in debug mode + console.log("Couldn't enable update checking", err); + } mainWindow = new electron.BrowserWindow({ icon: `${__dirname}/../../vector/img/logo.png`, width: 1024, height: 768, }); + // A useful one to uncomment for debugging + //mainWindow.webContents.openDevTools(); mainWindow.loadURL(`file://${__dirname}/../../vector/index.html`); electron.Menu.setApplicationMenu(VectorMenu); diff --git a/src/components/views/globals/NewVersionBar.js b/src/components/views/globals/NewVersionBar.js index 90b30f727b..3ae9580411 100644 --- a/src/components/views/globals/NewVersionBar.js +++ b/src/components/views/globals/NewVersionBar.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var sdk = require('matrix-react-sdk'); import Modal from 'matrix-react-sdk/lib/Modal'; +import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; /** * Check a version string is compatible with the Changelog @@ -31,30 +32,50 @@ function checkVersion(ver) { export default function NewVersionBar(props) { const onChangelogClicked = () => { - const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog'); - - Modal.createDialog(ChangelogDialog, { - version: props.version, - newVersion: props.newVersion, - onFinished: (update) => { - if(update) { - window.location.reload(); + if (props.releaseNotes) { + const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); + Modal.createDialog(QuestionDialog, { + title: "What's New", + description:
{props.releaseNotes}
, + button: "Update", + onFinished: (update) => { + if(update && PlatformPeg.get()) { + PlatformPeg.get().installUpdate(); + } } - } - }); + }); + } else { + const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog'); + Modal.createDialog(ChangelogDialog, { + version: props.version, + newVersion: props.newVersion, + releaseNotes: releaseNotes, + onFinished: (update) => { + if(update && PlatformPeg.get()) { + PlatformPeg.get().installUpdate(); + } + } + }); + } }; - let changelog_button; - if (checkVersion(props.version) && checkVersion(props.newVersion)) { - changelog_button = ; + const onUpdateClicked = () => { + PlatformPeg.get().installUpdate(); + }; + + let action_button; + if (props.releaseNotes || (checkVersion(props.version) && checkVersion(props.newVersion))) { + action_button = ; + } else if (PlatformPeg.get()) { + action_button = ; } return (
/!\
- A new version of Riot is available. Refresh your browser. + A new version of Riot is available.
- {changelog_button} + {action_button}
); } @@ -62,4 +83,5 @@ export default function NewVersionBar(props) { NewVersionBar.propTypes = { version: React.PropTypes.string.isRequired, newVersion: React.PropTypes.string.isRequired, + releaseNotes: React.PropTypes.string, }; diff --git a/src/skins/vector/css/common.css b/src/skins/vector/css/common.css index 5165e9c57c..bb00bbd8c0 100644 --- a/src/skins/vector/css/common.css +++ b/src/skins/vector/css/common.css @@ -288,3 +288,7 @@ textarea { cursor: pointer; display: inline; } + +.changelog_text { + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; +} diff --git a/src/vector/index.js b/src/vector/index.js index 4c965ef317..bf764a499d 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -112,10 +112,6 @@ function onHashChange(ev) { routeUrl(window.location); } -function onVersion(current, latest) { - window.matrixChat.onVersion(current, latest); -} - var loaded = false; var lastLoadedScreen = null; @@ -164,8 +160,7 @@ window.onload = function() { if (!validBrowser) { return; } - UpdateChecker.setVersionListener(onVersion); - UpdateChecker.run(); + UpdateChecker.start(); routeUrl(window.location); loaded = true; if (lastLoadedScreen) { diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index da901b923a..cb0e343afb 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -17,11 +17,22 @@ limitations under the License. */ import BasePlatform from './BasePlatform'; +import dis from 'matrix-react-sdk/lib/dispatcher'; + +function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) { + dis.dispatch({ + action: 'new_version', + currentVersion: electron.remote.app.getVersion(), + newVersion: ver, + releaseNotes: releaseNotes, + }); +} // index.js imports us unconditionally, so we need this check here as well let electron = null, remote = null; if (window && window.process && window.process && window.process.type === 'renderer') { electron = require('electron'); + electron.remote.autoUpdater.on('update-downloaded', onUpdateDownloaded); remote = electron.remote; } @@ -56,4 +67,17 @@ export default class ElectronPlatform extends BasePlatform { clearNotification(notif: Notification) { notif.close(); } + + pollForUpdate() { + // In electron we control the update process ourselves, since + // it needs to run in the main process, so we just run the timer + // loop in the main electron process instead. + } + + installUpdate() { + // IPC to the main process to install the update, since quitAndInstall + // doesn't fire the before-quit event so the main process needs to know + // it should exit. + electron.ipcRenderer.send('install_update'); + } } diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js index d1ae01d630..8a57a62ced 100644 --- a/src/vector/platform/WebPlatform.js +++ b/src/vector/platform/WebPlatform.js @@ -18,10 +18,14 @@ limitations under the License. import BasePlatform from './BasePlatform'; import Favico from 'favico.js'; +import request from 'browser-request'; +import dis from 'matrix-react-sdk/lib/dispatcher.js'; +import q from 'q'; export default class WebPlatform extends BasePlatform { constructor() { super(); + this.runningVersion = null; // The 'animations' are really low framerate and look terrible. // Also it re-starts the animationb every time you set the badge, // and we set the state each time, even if the value hasn't changed, @@ -85,4 +89,43 @@ export default class WebPlatform extends BasePlatform { notification.close(); }, 5 * 1000); } + + _getVersion() { + const deferred = q.defer(); + request( + { method: "GET", url: "version" }, + (err, response, body) => { + if (err || response.status < 200 || response.status >= 300) { + if (err == null) err = { status: response.status }; + deferred.reject(err); + return; + } + + const ver = body.trim(); + deferred.resolve(ver); + } + ); + return deferred.promise; + } + + pollForUpdate() { + this._getVersion().done((ver) => { + if (this.runningVersion == null) { + this.runningVersion = ver; + } else if (this.runningVersion != ver) { + dis.dispatch({ + action: 'new_version', + currentVersion: this.runningVersion, + newVersion: ver, + releaseNotes: "Theses are some release notes innit\n * Do a thing\n * Do some more things", + }); + } + }, (err) => { + console.error("Failed to poll for update", err); + }); + } + + installUpdate() { + window.location.reload(); + } } diff --git a/src/vector/updater.js b/src/vector/updater.js index e8d6830d02..fc556d9f89 100644 --- a/src/vector/updater.js +++ b/src/vector/updater.js @@ -13,48 +13,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -var POKE_RATE_MS = 10 * 60 * 1000; // 10 min -var currentVersion = null; -var latestVersion = null; -var listener = function(){}; // NOP + +import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; + +var POKE_RATE_MS = 1 * 6 * 1000; // 10 min module.exports = { - setVersionListener: function(fn) { // invoked with fn(currentVer, newVer) - listener = fn; + start: function() { + module.exports.poll(); + setInterval(module.exports.poll, POKE_RATE_MS); }, - run: function() { - var req = new XMLHttpRequest(); - req.addEventListener("load", function() { - if (!req.responseText) { - return; - } - var ver = req.responseText.trim(); - if (!currentVersion) { - currentVersion = ver; - listener(currentVersion, currentVersion); - } - - if (ver !== latestVersion) { - latestVersion = ver; - if (module.exports.hasNewVersion()) { - console.log("Current=%s Latest=%s", currentVersion, latestVersion); - listener(currentVersion, latestVersion); - } - } - }); - var cacheBuster = "?ts=" + new Date().getTime(); - req.open("GET", "version" + cacheBuster); - req.send(); // can't suppress 404s from being logged. - - setTimeout(module.exports.run, POKE_RATE_MS); - }, - - getCurrentVersion: function() { - return currentVersion; - }, - - hasNewVersion: function() { - return currentVersion && latestVersion && (currentVersion !== latestVersion); + poll: function() { + PlatformPeg.get().pollForUpdate(); } };