diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js index ef0d173c..f1a9e37a 100644 --- a/electron_app/src/electron-main.js +++ b/electron_app/src/electron-main.js @@ -120,6 +120,7 @@ process.on('uncaughtException', function(error) { }); electron.ipcMain.on('install_update', installUpdate); +electron.ipcMain.on('checkForUpdates', pollForUpdates); let focusHandlerAttached = false; electron.ipcMain.on('setBadgeCount', function(ev, count) { diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js new file mode 100644 index 00000000..65fe84c9 --- /dev/null +++ b/src/components/views/globals/UpdateCheckBar.js @@ -0,0 +1,89 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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. +*/ + +'use strict'; + +import React from 'react'; +import dis from 'matrix-react-sdk/lib/dispatcher'; +import { _t } from 'matrix-react-sdk/lib/languageHandler'; +import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; +import {updateStateEnum} from '../../../vector/platform/VectorBasePlatform'; +import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton'; + +export default React.createClass({ + + getInitialState: function() { + return { + message: 'Checking for an update...', + done: false, + }; + }, + + componentWillMount: function() { + PlatformPeg.get().checkForUpdate().done((state) => { + if (this._unmounted) return; + + console.log('checkForUpdate done, ', state); + + // We will be replaced by NewVersionBar + if (state === updateStateEnum.Ready) return; + + let done = true; + let message; + switch (state) { + case updateStateEnum.Error: + message = 'Error encountered when checking for an update'; + break; + case updateStateEnum.NotAvailable: + message = 'No update found'; + break; + case updateStateEnum.Downloading: + message = 'Update is being downloaded'; + done = false; + break; + } + + this.setState({message, done}); + }); + }, + + componentWillUnmount: function() { + this._unmounted = true; + }, + + hideToolbar: function() { + dis.dispatch({ + action: 'check_updates', + value: false, + }); + }, + + render: function() { + const imgSrc = this.state.done ? 'img/warning.svg' : 'img/spinner.gif'; + + return ( +
+ /!\ +
+ {this.state.message} +
+ + + +
+ ); + } +}); diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index fa0f999c..1ed1c0d8 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -17,7 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import VectorBasePlatform from './VectorBasePlatform'; +import VectorBasePlatform, {updateStateEnum} from './VectorBasePlatform'; import dis from 'matrix-react-sdk/lib/dispatcher'; import { _t } from 'matrix-react-sdk/lib/languageHandler'; import q from 'q'; @@ -66,6 +66,7 @@ export default class ElectronPlatform extends VectorBasePlatform { constructor() { super(); dis.register(_onAction); + this.updatable = Boolean(remote.autoUpdater.getFeedURL()); } getHumanReadableName(): string { @@ -137,10 +138,28 @@ export default class ElectronPlatform extends VectorBasePlatform { return q(remote.app.getVersion()); } - 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. + checkForUpdate() { // manual update check for this platform + const deferred = q.defer(); + + const _onUpdateAvailable = function() { + electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); + deferred.resolve(updateStateEnum.Downloading); + } + + const _onUpdateNotAvailable = function() { + electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); + deferred.resolve(updateStateEnum.NotAvailable); + } + + electron.autoUpdater.once('update-available', _onUpdateAvailable); + electron.autoUpdater.once('update-not-available', _onUpdateNotAvailable); + + electron.ipcRenderer.send('checkForUpdates'); + return deferred.promise.timeout(10000).catch(() => { + electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); + electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); + return updateStateEnum.Error; + }); } installUpdate() { diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js index 8e998402..5ae620cb 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.js @@ -22,6 +22,13 @@ import { _t } from 'matrix-react-sdk/lib/languageHandler'; import Favico from 'favico.js'; +export const updateStateEnum = { + Error: -1, + NotAvailable: 0, + Downloading: 1, + Ready: 2, +}; + /** * Vector-specific extensions to the BasePlatform template */ @@ -35,6 +42,7 @@ export default class VectorBasePlatform extends BasePlatform { // so we'd need to fix that if enabling the animation. this.favicon = new Favico({animation: 'none'}); this._updateFavicon(); + this.updatable = true; } getHumanReadableName(): string { @@ -80,13 +88,21 @@ export default class VectorBasePlatform extends BasePlatform { startUpdater() { } + /** + * Whether we can call checkForUpdate on this platform build + */ + canSelfUpdate(): boolean { + return this.updatable; + } + /** * Check for the availability of an update to the version of the * app that's currently running. * If an update is available, this function should dispatch the * 'new_version' action. + * @returns Promise */ - pollForUpdate() { + checkForUpdate(): Promise { } /** diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js index c589af38..318a3fe9 100644 --- a/src/vector/platform/WebPlatform.js +++ b/src/vector/platform/WebPlatform.js @@ -17,7 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import VectorBasePlatform from './VectorBasePlatform'; +import VectorBasePlatform, {updateStateEnum} from './VectorBasePlatform'; import request from 'browser-request'; import dis from 'matrix-react-sdk/lib/dispatcher.js'; import { _t } from 'matrix-react-sdk/lib/languageHandler'; @@ -135,12 +135,12 @@ export default class WebPlatform extends VectorBasePlatform { } startUpdater() { - this.pollForUpdate(); - setInterval(this.pollForUpdate, POKE_RATE_MS); + this.checkForUpdate(); + setInterval(this.checkForUpdate, POKE_RATE_MS); } - pollForUpdate() { - this._getVersion().done((ver) => { + checkForUpdate() { + return this._getVersion().then((ver) => { if (this.runningVersion === null) { this.runningVersion = ver; } else if (this.runningVersion !== ver) { @@ -149,9 +149,12 @@ export default class WebPlatform extends VectorBasePlatform { currentVersion: this.runningVersion, newVersion: ver, }); + return updateStateEnum.Ready; } + return updateStateEnum.NotAvailable; }, (err) => { console.error("Failed to poll for update", err); + return updateStateEnum.Error; }); }