From 0e6012ad4561102c72bbdcb2c29c300a57927f4e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 12:50:47 +0100 Subject: [PATCH 01/19] absorb updater.js into the Platforms, gets rid of pointless setInterval in Electron Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> (cherry picked from commit 24e8a30) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/index.js | 7 +++--- src/vector/platform/VectorBasePlatform.js | 6 +++++ src/vector/platform/WebPlatform.js | 7 ++++++ src/vector/updater.js | 30 ----------------------- 4 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 src/vector/updater.js diff --git a/src/vector/index.js b/src/vector/index.js index 9f16205164..d26e480e30 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -59,7 +59,6 @@ var sdk = require("matrix-react-sdk"); const PlatformPeg = require("matrix-react-sdk/lib/PlatformPeg"); sdk.loadSkin(require('../component-index')); var VectorConferenceHandler = require('../VectorConferenceHandler'); -var UpdateChecker = require("./updater"); var q = require('q'); var request = require('browser-request'); import * as UserSettingsStore from 'matrix-react-sdk/lib/UserSettingsStore'; @@ -277,7 +276,9 @@ async function loadApp() { Unable to load config file: please refresh the page to try again. , document.getElementById('matrixchat')); } else if (validBrowser) { - UpdateChecker.start(); + const platform = PlatformPeg.get(); + platform.startUpdater(); + const MatrixChat = sdk.getComponent('structures.MatrixChat'); window.matrixChat = ReactDOM.render( , document.getElementById('matrixchat') ); diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js index 76707d1d58..8e998402c4 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.js @@ -74,6 +74,12 @@ export default class VectorBasePlatform extends BasePlatform { this._updateFavicon(); } + /** + * Begin update polling, if applicable + */ + startUpdater() { + } + /** * Check for the availability of an update to the version of the * app that's currently running. diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js index 8397a7f703..c589af38b8 100644 --- a/src/vector/platform/WebPlatform.js +++ b/src/vector/platform/WebPlatform.js @@ -26,6 +26,8 @@ import q from 'q'; import url from 'url'; import UAParser from 'ua-parser-js'; +var POKE_RATE_MS = 10 * 60 * 1000; // 10 min + export default class WebPlatform extends VectorBasePlatform { constructor() { super(); @@ -132,6 +134,11 @@ export default class WebPlatform extends VectorBasePlatform { return this._getVersion(); } + startUpdater() { + this.pollForUpdate(); + setInterval(this.pollForUpdate, POKE_RATE_MS); + } + pollForUpdate() { this._getVersion().done((ver) => { if (this.runningVersion === null) { diff --git a/src/vector/updater.js b/src/vector/updater.js deleted file mode 100644 index 19d40b4f38..0000000000 --- a/src/vector/updater.js +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 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. -*/ - -import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; - -var POKE_RATE_MS = 10 * 60 * 1000; // 10 min - -module.exports = { - start: function() { - module.exports.poll(); - setInterval(module.exports.poll, POKE_RATE_MS); - }, - - poll: function() { - PlatformPeg.get().pollForUpdate(); - } -}; From efc68c078ebe80ec2b499cb644c8eccda33a973d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 15:12:46 +0100 Subject: [PATCH 02/19] basic manual update stuff + update check bar Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron_app/src/electron-main.js | 1 + .../views/globals/UpdateCheckBar.js | 89 +++++++++++++++++++ src/vector/platform/ElectronPlatform.js | 29 ++++-- src/vector/platform/VectorBasePlatform.js | 18 +++- src/vector/platform/WebPlatform.js | 13 +-- 5 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 src/components/views/globals/UpdateCheckBar.js diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js index ef0d173c00..f1a9e37a73 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 0000000000..65fe84c989 --- /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 fa0f999cb8..1ed1c0d8c7 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 8e998402c4..5ae620cb01 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 c589af38b8..318a3fe9a1 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; }); } From b95ad701afcf4cada8c209de505eb53e19bc1e5f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 15:17:58 +0100 Subject: [PATCH 03/19] match ENUM style to others in project Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> (cherry picked from commit a871815) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/UpdateCheckBar.js | 8 ++++---- src/vector/platform/ElectronPlatform.js | 6 +++--- src/vector/platform/VectorBasePlatform.js | 8 ++++---- src/vector/platform/WebPlatform.js | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index 65fe84c989..95c78cf634 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -39,18 +39,18 @@ export default React.createClass({ console.log('checkForUpdate done, ', state); // We will be replaced by NewVersionBar - if (state === updateStateEnum.Ready) return; + if (state === updateStateEnum.READY) return; let done = true; let message; switch (state) { - case updateStateEnum.Error: + case updateStateEnum.ERROR: message = 'Error encountered when checking for an update'; break; - case updateStateEnum.NotAvailable: + case updateStateEnum.NOTAVAILABLE: message = 'No update found'; break; - case updateStateEnum.Downloading: + case updateStateEnum.DOWNLOADING: message = 'Update is being downloaded'; done = false; break; diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index 1ed1c0d8c7..b4f17d6e42 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -143,12 +143,12 @@ export default class ElectronPlatform extends VectorBasePlatform { const _onUpdateAvailable = function() { electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); - deferred.resolve(updateStateEnum.Downloading); + deferred.resolve(updateStateEnum.DOWNLOADING); } const _onUpdateNotAvailable = function() { electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); - deferred.resolve(updateStateEnum.NotAvailable); + deferred.resolve(updateStateEnum.NOTAVAILABLE); } electron.autoUpdater.once('update-available', _onUpdateAvailable); @@ -158,7 +158,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return deferred.promise.timeout(10000).catch(() => { electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); - return updateStateEnum.Error; + return updateStateEnum.ERROR; }); } diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js index 5ae620cb01..0bbcb8f3e7 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.js @@ -23,10 +23,10 @@ import { _t } from 'matrix-react-sdk/lib/languageHandler'; import Favico from 'favico.js'; export const updateStateEnum = { - Error: -1, - NotAvailable: 0, - Downloading: 1, - Ready: 2, + ERROR: 'ERROR', + NOTAVAILABLE: 'NOTAVAILABLE', + DOWNLOADING: 'DOWNLOADING', + READY: 'READY', }; /** diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js index 318a3fe9a1..dd6b91de91 100644 --- a/src/vector/platform/WebPlatform.js +++ b/src/vector/platform/WebPlatform.js @@ -149,12 +149,12 @@ export default class WebPlatform extends VectorBasePlatform { currentVersion: this.runningVersion, newVersion: ver, }); - return updateStateEnum.Ready; + return updateStateEnum.READY; } - return updateStateEnum.NotAvailable; + return updateStateEnum.NOTAVAILABLE; }, (err) => { console.error("Failed to poll for update", err); - return updateStateEnum.Error; + return updateStateEnum.ERROR; }); } From 3ebd90565c37f07c29c23d1eb217a853c2a3a83b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 15:31:08 +0100 Subject: [PATCH 04/19] add clearer concept of timeout vs error Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> (cherry picked from commit 104c804) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/UpdateCheckBar.js | 9 ++++++--- src/vector/platform/ElectronPlatform.js | 2 +- src/vector/platform/VectorBasePlatform.js | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index 95c78cf634..c39754c85f 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -45,13 +45,16 @@ export default React.createClass({ let message; switch (state) { case updateStateEnum.ERROR: - message = 'Error encountered when checking for an update'; + message = 'Error encountered when checking for an update.'; + break; + case updateStateEnum.TIMEOUT: + message = 'Update Check timed out, try again later.'; break; case updateStateEnum.NOTAVAILABLE: - message = 'No update found'; + message = 'No update found.'; break; case updateStateEnum.DOWNLOADING: - message = 'Update is being downloaded'; + message = 'Update is being downloaded.'; done = false; break; } diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index b4f17d6e42..f84fe8d570 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -158,7 +158,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return deferred.promise.timeout(10000).catch(() => { electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); - return updateStateEnum.ERROR; + return updateStateEnum.TIMEOUT; }); } diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js index 0bbcb8f3e7..074be91c83 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.js @@ -24,6 +24,7 @@ import Favico from 'favico.js'; export const updateStateEnum = { ERROR: 'ERROR', + TIMEOUT: 'TIMEOUT', NOTAVAILABLE: 'NOTAVAILABLE', DOWNLOADING: 'DOWNLOADING', READY: 'READY', From f5ba6fa95293b973505b56685b031226f56ef4c8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 15:36:03 +0100 Subject: [PATCH 05/19] i18n things Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> (cherry picked from commit d878c72) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/UpdateCheckBar.js | 10 +++++----- src/i18n/strings/en_EN.json | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index c39754c85f..0a9029222a 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -27,7 +27,7 @@ export default React.createClass({ getInitialState: function() { return { - message: 'Checking for an update...', + message: _t('Checking for an update...'), done: false, }; }, @@ -45,16 +45,16 @@ export default React.createClass({ let message; switch (state) { case updateStateEnum.ERROR: - message = 'Error encountered when checking for an update.'; + message = _t('Error encountered when checking for an update.'); break; case updateStateEnum.TIMEOUT: - message = 'Update Check timed out, try again later.'; + message = _t('Update Check timed out, try again later.'); break; case updateStateEnum.NOTAVAILABLE: - message = 'No update found.'; + message = _t('No update found.'); break; case updateStateEnum.DOWNLOADING: - message = 'Update is being downloaded.'; + message = _t('Update is being downloaded.'); done = false; break; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e148248cc6..2bce407061 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -158,6 +158,11 @@ "Today": "Today", "Yesterday": "Yesterday", "OK": "OK", + "Checking for an update...": "Checking for an update...", + "Error encountered when checking for an update.": "Error encountered when checking for an update.", + "Update Check timed out, try again later.": "Update Check timed out, try again later.", + "No update found.": "No update found.", + "Update is being downloaded.": "Update is being downloaded.", "You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.", "Welcome page": "Welcome page", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!" From a4c1aee5eac715b779e0ccef1f420b767d8e8e15 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 16:00:49 +0100 Subject: [PATCH 06/19] correct file header Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/UpdateCheckBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index 0a9029222a..8d7cc281da 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -1,5 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 5981887705ba324177810f6b48ef24cf05d905fb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 16:04:01 +0100 Subject: [PATCH 07/19] fix UpdateCheckBar image alt Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/UpdateCheckBar.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index 8d7cc281da..d72ccefff4 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -75,11 +75,16 @@ export default React.createClass({ }, render: function() { - const imgSrc = this.state.done ? 'img/warning.svg' : 'img/spinner.gif'; + let image; + if (this.state.done) { + image = /!\; + } else { + image = {this.state.message}/; + } return (
- /!\ + {image}
{this.state.message}
From 93f148fca33c2c1a89a7a8fc407828ea90aa68d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 3 Jun 2017 16:22:14 +0100 Subject: [PATCH 08/19] fix type electron vs remote don't break when running a non Squirrel Windows build that has an update url Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/platform/ElectronPlatform.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index f84fe8d570..519cadc09d 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -142,22 +142,30 @@ export default class ElectronPlatform extends VectorBasePlatform { const deferred = q.defer(); const _onUpdateAvailable = function() { - electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); + remote.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); + remote.autoUpdater.removeListener('error', _onError); deferred.resolve(updateStateEnum.DOWNLOADING); } - const _onUpdateNotAvailable = function() { - electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); + remote.autoUpdater.removeListener('update-available', _onUpdateAvailable); + remote.autoUpdater.removeListener('error', _onError); deferred.resolve(updateStateEnum.NOTAVAILABLE); } + const _onError = function() { + remote.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); + remote.autoUpdater.removeListener('update-available', _onUpdateAvailable); + deferred.resolve(updateStateEnum.ERROR); + } - electron.autoUpdater.once('update-available', _onUpdateAvailable); - electron.autoUpdater.once('update-not-available', _onUpdateNotAvailable); + remote.autoUpdater.once('update-available', _onUpdateAvailable); + remote.autoUpdater.once('update-not-available', _onUpdateNotAvailable); + remote.autoUpdater.once('error', _onError); - electron.ipcRenderer.send('checkForUpdates'); + remote.ipcRenderer.send('checkForUpdates'); return deferred.promise.timeout(10000).catch(() => { - electron.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); - electron.autoUpdater.removeListener('update-available', _onUpdateAvailable); + remote.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); + remote.autoUpdater.removeListener('update-available', _onUpdateAvailable); + remote.autoUpdater.removeListener('error', _onError); return updateStateEnum.TIMEOUT; }); } From 6592526109dcfab9d484408a83d405211a70acae Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 11 Jun 2017 13:45:37 +0100 Subject: [PATCH 09/19] change /!\ to Warning for accessibility (plus weird escaping?) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/MatrixToolbar.js | 2 +- src/components/views/globals/NewVersionBar.js | 2 +- src/components/views/globals/UpdateCheckBar.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/globals/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js index 488b5def47..06bfa36e9e 100644 --- a/src/components/views/globals/MatrixToolbar.js +++ b/src/components/views/globals/MatrixToolbar.js @@ -35,7 +35,7 @@ module.exports = React.createClass({ render: function() { return (
- /!\ + Warning
{ _t('You are not receiving desktop notifications') } { _t('Enable them now') }
diff --git a/src/components/views/globals/NewVersionBar.js b/src/components/views/globals/NewVersionBar.js index 219ef02a9a..d25fa3a6f2 100644 --- a/src/components/views/globals/NewVersionBar.js +++ b/src/components/views/globals/NewVersionBar.js @@ -96,7 +96,7 @@ export default React.createClass({ } return (
- /!\ + Warning
{_t("A new version of Riot is available.")}
diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index d72ccefff4..4949c4426d 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -77,7 +77,7 @@ export default React.createClass({ render: function() { let image; if (this.state.done) { - image = /!\; + image = Warning; } else { image = {this.state.message}/; } From 4c8ff0955db14c78d73a2db43d7ca7a8de7a3b6c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 11 Jun 2017 16:43:20 +0100 Subject: [PATCH 10/19] move electron update logic into own file, tidy tidy tidy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron_app/src/electron-main.js | 72 +++---------------------------- electron_app/src/updater.js | 65 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 67 deletions(-) create mode 100644 electron_app/src/updater.js diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js index f1a9e37a73..d793ab9be9 100644 --- a/electron_app/src/electron-main.js +++ b/electron_app/src/electron-main.js @@ -28,6 +28,7 @@ const AutoLaunch = require('auto-launch'); const tray = require('./tray'); const vectorMenu = require('./vectormenu'); const webContentsHandler = require('./webcontents-handler'); +const updater = require('./updater'); const windowStateKeeper = require('electron-window-state'); @@ -45,69 +46,9 @@ try { // Continue with the defaults (ie. an empty config) } -const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; -const INITIAL_UPDATE_DELAY_MS = 30 * 1000; - let mainWindow = null; -let appQuitting = false; +global.appQuitting = false; -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); - } -} - -function startAutoUpdate(updateBaseUrl) { - if (updateBaseUrl.slice(-1) !== '/') { - updateBaseUrl = updateBaseUrl + '/'; - } - 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') { - // include the current version in the URL we hit. Electron doesn't add - // it anywhere (apart from the User-Agent) so it's up to us. We could - // (and previously did) just use the User-Agent, but this doesn't - // rely on NSURLConnection setting the User-Agent to what we expect, - // and also acts as a convenient cache-buster to ensure that when the - // app updates it always gets a fresh value to avoid update-looping. - electron.autoUpdater.setFeedURL( - `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(electron.app.getVersion())}`); - - } else if (process.platform === 'win32') { - electron.autoUpdater.setFeedURL(`${updateBaseUrl}win32/${process.arch}/`); - } 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. - // We also wait a short time before checking for updates the first time because - // of squirrel on windows and it taking a small amount of time to release a - // lock file. - setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS); - setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS); - } catch (err) { - // will fail if running in debug mode - console.log('Couldn\'t enable update checking', err); - } -} // handle uncaught errors otherwise it displays // stack traces in popup dialogs, which is terrible (which @@ -119,9 +60,6 @@ process.on('uncaughtException', function(error) { console.log('Unhandled exception', error); }); -electron.ipcMain.on('install_update', installUpdate); -electron.ipcMain.on('checkForUpdates', pollForUpdates); - let focusHandlerAttached = false; electron.ipcMain.on('setBadgeCount', function(ev, count) { electron.app.setBadgeCount(count); @@ -219,7 +157,7 @@ electron.ipcMain.on('settings_set', function(ev, key, value) { electron.app.on('ready', () => { if (vectorConfig.update_base_url) { console.log(`Starting auto update with base URL: ${vectorConfig.update_base_url}`); - startAutoUpdate(vectorConfig.update_base_url); + updater.start(vectorConfig.update_base_url) } else { console.log('No update_base_url is defined: auto update is disabled'); } @@ -265,7 +203,7 @@ electron.app.on('ready', () => { mainWindow = null; }); mainWindow.on('close', (e) => { - if (!appQuitting && (tray.hasTray() || process.platform === 'darwin')) { + if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) { // On Mac, closing the window just hides it // (this is generally how single-window Mac apps // behave, eg. Mail.app) @@ -288,7 +226,7 @@ electron.app.on('activate', () => { }); electron.app.on('before-quit', () => { - appQuitting = true; + global.appQuitting = true; }); // Set the App User Model ID to match what the squirrel diff --git a/electron_app/src/updater.js b/electron_app/src/updater.js new file mode 100644 index 0000000000..ea7b259fb4 --- /dev/null +++ b/electron_app/src/updater.js @@ -0,0 +1,65 @@ +const { app, autoUpdater, ipcMain } = require('electron'); + +const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000; +const INITIAL_UPDATE_DELAY_MS = 30 * 1000; + +function installUpdate() { + // for some reason, quitAndInstall does not fire the + // before-quit event, so we need to set the flag here. + global.appQuitting = true; + autoUpdater.quitAndInstall(); +} + +function pollForUpdates() { + try { + autoUpdater.checkForUpdates(); + } catch (e) { + console.log('Couldn\'t check for update', e); + } +} + +module.exports = {}; +module.exports.start = function startAutoUpdate(updateBaseUrl) { + if (updateBaseUrl.slice(-1) !== '/') { + updateBaseUrl = updateBaseUrl + '/'; + } + 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') { + // include the current version in the URL we hit. Electron doesn't add + // it anywhere (apart from the User-Agent) so it's up to us. We could + // (and previously did) just use the User-Agent, but this doesn't + // rely on NSURLConnection setting the User-Agent to what we expect, + // and also acts as a convenient cache-buster to ensure that when the + // app updates it always gets a fresh value to avoid update-looping. + autoUpdater.setFeedURL(`${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`); + + } else if (process.platform === 'win32') { + autoUpdater.setFeedURL(`${updateBaseUrl}win32/${process.arch}/`); + } 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. + // We also wait a short time before checking for updates the first time because + // of squirrel on windows and it taking a small amount of time to release a + // lock file. + setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS); + setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS); + } catch (err) { + // will fail if running in debug mode + console.log('Couldn\'t enable update checking', err); + } +} + +ipcMain.on('install_update', installUpdate); +ipcMain.on('checkForUpdates', pollForUpdates); From c4fd139586f14449bb830f2dc62c6b053c41dcdd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 11 Jun 2017 16:46:47 +0100 Subject: [PATCH 11/19] get rid of pointless interval and timeout on linux Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron_app/src/updater.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/electron_app/src/updater.js b/electron_app/src/updater.js index ea7b259fb4..e585dc977d 100644 --- a/electron_app/src/updater.js +++ b/electron_app/src/updater.js @@ -24,6 +24,7 @@ module.exports.start = function startAutoUpdate(updateBaseUrl) { updateBaseUrl = updateBaseUrl + '/'; } try { + let url; // 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 @@ -36,25 +37,29 @@ module.exports.start = function startAutoUpdate(updateBaseUrl) { // rely on NSURLConnection setting the User-Agent to what we expect, // and also acts as a convenient cache-buster to ensure that when the // app updates it always gets a fresh value to avoid update-looping. - autoUpdater.setFeedURL(`${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`); + url = `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`; } else if (process.platform === 'win32') { - autoUpdater.setFeedURL(`${updateBaseUrl}win32/${process.arch}/`); + url = `${updateBaseUrl}win32/${process.arch}/`; } 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. - // We also wait a short time before checking for updates the first time because - // of squirrel on windows and it taking a small amount of time to release a - // lock file. - setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS); - setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS); + + if (url) { + autoUpdater.setFeedURL(url); + // 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. + // We also wait a short time before checking for updates the first time because + // of squirrel on windows and it taking a small amount of time to release a + // lock file. + setTimeout(pollForUpdates, INITIAL_UPDATE_DELAY_MS); + setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS); + } } catch (err) { // will fail if running in debug mode console.log('Couldn\'t enable update checking', err); From a520f0bfedade23ecd532c2293d677bb790d1170 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 11 Jun 2017 19:19:17 +0100 Subject: [PATCH 12/19] move all logic, make bar more generic pass through actual errors and tidy needs testing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron_app/src/updater.js | 22 +++++- .../views/globals/UpdateCheckBar.js | 74 ++++++------------- src/i18n/strings/en_EN.json | 5 +- src/vector/platform/ElectronPlatform.js | 70 ++++++++++-------- src/vector/platform/VectorBasePlatform.js | 29 +++++++- src/vector/platform/WebPlatform.js | 34 +++++++-- 6 files changed, 138 insertions(+), 96 deletions(-) diff --git a/electron_app/src/updater.js b/electron_app/src/updater.js index e585dc977d..106e3d2f23 100644 --- a/electron_app/src/updater.js +++ b/electron_app/src/updater.js @@ -67,4 +67,24 @@ module.exports.start = function startAutoUpdate(updateBaseUrl) { } ipcMain.on('install_update', installUpdate); -ipcMain.on('checkForUpdates', pollForUpdates); + +let ipcChannel; +ipcMain.on('check_updates', function(event) { + ipcChannel = event.sender; + pollForUpdates(); + // event.sender.send('check_updates') // true/false/error = available(downloading)/notAvailable/error +}); + +function ipcChannelSendUpdateStatus(status) { + if (ipcChannel) { + ipcChannel.send('check_updates', status); + } +} + +autoUpdater.on('update-available', function() { + ipcChannelSendUpdateStatus(true); +}).on('update-not-available', function() { + ipcChannelSendUpdateStatus(false); +}).on('error', function(error) { + ipcChannelSendUpdateStatus(error.message); +}); diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index 4949c4426d..557698c711 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -17,76 +17,50 @@ 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 {updateCheckStatusEnum} from '../../../vector/platform/VectorBasePlatform'; import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton'; +const statusText = { + CHECKING: 'Checking for an update...', + ERROR: 'Error encountered (%(errorDetail)s).', + NOTAVAILABLE: 'No update available.', + DOWNLOADING: 'Downloading update...', +}; + +const doneStatuses = [ + updateCheckStatusEnum.ERROR, + updateCheckStatusEnum.NOTAVAILABLE, +]; + export default React.createClass({ - - getInitialState: function() { - return { - message: _t('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 = _t('Error encountered when checking for an update.'); - break; - case updateStateEnum.TIMEOUT: - message = _t('Update Check timed out, try again later.'); - break; - case updateStateEnum.NOTAVAILABLE: - message = _t('No update found.'); - break; - case updateStateEnum.DOWNLOADING: - message = _t('Update is being downloaded.'); - done = false; - break; - } - - this.setState({message, done}); - }); - }, - - componentWillUnmount: function() { - this._unmounted = true; + propTypes: { + status: React.PropTypes.oneOf(Object.values(updateCheckStatusEnum)).isRequired, + // Currently for error detail but will be usable for download progress + // once that is a thing that squirrel passes through electron. + detail: React.PropTypes.string, }, hideToolbar: function() { - dis.dispatch({ - action: 'check_updates', - value: false, - }); + PlatformPeg.get().stopUpdateCheck(); }, render: function() { + const message = _t(statusText[this.props.status], { errorDetail: this.props.detail }); + let image; - if (this.state.done) { + if (doneStatuses.includes(this.props.status)) { image = Warning; } else { - image = {this.state.message}/; + image = {message}/; } return (
{image}
- {this.state.message} + {message}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2bce407061..1755fd620b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -159,8 +159,9 @@ "Yesterday": "Yesterday", "OK": "OK", "Checking for an update...": "Checking for an update...", - "Error encountered when checking for an update.": "Error encountered when checking for an update.", - "Update Check timed out, try again later.": "Update Check timed out, try again later.", + "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", + "No update available.": "No update available.", + "Downloading update...": "Downloading update...", "No update found.": "No update found.", "Update is being downloaded.": "Update is being downloaded.", "You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.", diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index 519cadc09d..7a1bf58292 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -17,11 +17,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import VectorBasePlatform, {updateStateEnum} from './VectorBasePlatform'; +import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform'; import dis from 'matrix-react-sdk/lib/dispatcher'; import { _t } from 'matrix-react-sdk/lib/languageHandler'; import q from 'q'; -import electron, {remote, ipcRenderer} from 'electron'; +import {remote, ipcRenderer} from 'electron'; remote.autoUpdater.on('update-downloaded', onUpdateDownloaded); @@ -62,11 +62,42 @@ function _onAction(payload: Object) { } } +function getUpdateCheckStatus(status) { + if (status === true) { + return { status: updateCheckStatusEnum.DOWNLOADING }; + } else if (status === false) { + return { status: updateCheckStatusEnum.NOTAVAILABLE }; + } else { + return { + status: updateCheckStatusEnum.ERROR, + detail: status, + }; + } +} + export default class ElectronPlatform extends VectorBasePlatform { constructor() { super(); dis.register(_onAction); this.updatable = Boolean(remote.autoUpdater.getFeedURL()); + + /* + IPC Call `check_updates` returns: + true if there is an update available + false if there is not + or the error if one is encountered + */ + ipcRenderer.on('check_updates', (event, status) => { + if (!this.showUpdateCheck) return; + dis.dispatch({ + action: 'check_updates', + value: getUpdateCheckStatus(status), + }); + this.showUpdateCheck = false; + }); + + this.startUpdateCheck = this.startUpdateCheck.bind(this); + this.stopUpdateCheck = this.stopUpdateCheck.bind(this); } getHumanReadableName(): string { @@ -138,43 +169,18 @@ export default class ElectronPlatform extends VectorBasePlatform { return q(remote.app.getVersion()); } - checkForUpdate() { // manual update check for this platform - const deferred = q.defer(); + startUpdateCheck() { + if (this.showUpdateCheck) return; + super.startUpdateCheck(); - const _onUpdateAvailable = function() { - remote.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); - remote.autoUpdater.removeListener('error', _onError); - deferred.resolve(updateStateEnum.DOWNLOADING); - } - const _onUpdateNotAvailable = function() { - remote.autoUpdater.removeListener('update-available', _onUpdateAvailable); - remote.autoUpdater.removeListener('error', _onError); - deferred.resolve(updateStateEnum.NOTAVAILABLE); - } - const _onError = function() { - remote.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); - remote.autoUpdater.removeListener('update-available', _onUpdateAvailable); - deferred.resolve(updateStateEnum.ERROR); - } - - remote.autoUpdater.once('update-available', _onUpdateAvailable); - remote.autoUpdater.once('update-not-available', _onUpdateNotAvailable); - remote.autoUpdater.once('error', _onError); - - remote.ipcRenderer.send('checkForUpdates'); - return deferred.promise.timeout(10000).catch(() => { - remote.autoUpdater.removeListener('update-not-available', _onUpdateNotAvailable); - remote.autoUpdater.removeListener('update-available', _onUpdateAvailable); - remote.autoUpdater.removeListener('error', _onError); - return updateStateEnum.TIMEOUT; - }); + ipcRenderer.send('check_updates'); } 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'); + ipcRenderer.send('install_update'); } getDefaultDeviceDisplayName(): string { diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js index 074be91c83..0e84e5fcd5 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.js @@ -19,12 +19,13 @@ limitations under the License. import BasePlatform from 'matrix-react-sdk/lib/BasePlatform'; import { _t } from 'matrix-react-sdk/lib/languageHandler'; +import dis from 'matrix-react-sdk/lib/dispatcher'; import Favico from 'favico.js'; -export const updateStateEnum = { +export const updateCheckStatusEnum = { + CHECKING: 'CHECKING', ERROR: 'ERROR', - TIMEOUT: 'TIMEOUT', NOTAVAILABLE: 'NOTAVAILABLE', DOWNLOADING: 'DOWNLOADING', READY: 'READY', @@ -42,8 +43,12 @@ export default class VectorBasePlatform extends BasePlatform { // and we set the state each time, even if the value hasn't changed, // so we'd need to fix that if enabling the animation. this.favicon = new Favico({animation: 'none'}); + this.showUpdateCheck = false; this._updateFavicon(); this.updatable = true; + + this.startUpdateCheck = this.startUpdateCheck.bind(this); + this.stopUpdateCheck = this.stopUpdateCheck.bind(this); } getHumanReadableName(): string { @@ -96,14 +101,30 @@ export default class VectorBasePlatform extends BasePlatform { return this.updatable; } + startUpdateCheck() { + this.showUpdateCheck = true; + dis.dispatch({ + action: 'check_updates', + value: { status: updateCheckStatusEnum.CHECKING }, + }); + } + + stopUpdateCheck() { + this.showUpdateCheck = false; + dis.dispatch({ + action: 'check_updates', + value: false, + }) + } + /** * 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 + * @returns Promise */ - checkForUpdate(): Promise { + pollForUpdate(): Promise { } /** diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js index dd6b91de91..02632ba049 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, {updateStateEnum} from './VectorBasePlatform'; +import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform'; import request from 'browser-request'; import dis from 'matrix-react-sdk/lib/dispatcher.js'; import { _t } from 'matrix-react-sdk/lib/languageHandler'; @@ -32,6 +32,9 @@ export default class WebPlatform extends VectorBasePlatform { constructor() { super(); this.runningVersion = null; + + this.startUpdateCheck = this.startUpdateCheck.bind(this); + this.stopUpdateCheck = this.stopUpdateCheck.bind(this); } getHumanReadableName(): string { @@ -135,11 +138,11 @@ export default class WebPlatform extends VectorBasePlatform { } startUpdater() { - this.checkForUpdate(); - setInterval(this.checkForUpdate, POKE_RATE_MS); + this.pollForUpdate(); + setInterval(this.pollForUpdate.bind(this), POKE_RATE_MS); } - checkForUpdate() { + pollForUpdate() { return this._getVersion().then((ver) => { if (this.runningVersion === null) { this.runningVersion = ver; @@ -149,12 +152,29 @@ export default class WebPlatform extends VectorBasePlatform { currentVersion: this.runningVersion, newVersion: ver, }); - return updateStateEnum.READY; + // Return to skip a MatrixChat state update + return; } - return updateStateEnum.NOTAVAILABLE; + return { status: updateCheckStatusEnum.NOTAVAILABLE }; }, (err) => { console.error("Failed to poll for update", err); - return updateStateEnum.ERROR; + return { + status: updateCheckStatusEnum.ERROR, + detail: err.message || err.status ? err.status.toString() : 'Unknown Error', + }; + }); + } + + startUpdateCheck() { + if (this.showUpdateCheck) return; + super.startUpdateCheck(); + this.pollForUpdate().then((updateState) => { + if (!this.showUpdateCheck) return; + if (!updateState) return; + dis.dispatch({ + action: 'check_updates', + value: updateState, + }); }); } From 89533706a8cfc6f2bd1a6749dc2dbb9473fec3e6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 11 Jun 2017 23:39:21 +0100 Subject: [PATCH 13/19] pollForUpdate is now internal/private to a Platform. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/platform/VectorBasePlatform.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.js index 0e84e5fcd5..c3df04f5b7 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.js @@ -117,16 +117,6 @@ export default class VectorBasePlatform extends BasePlatform { }) } - /** - * 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(): Promise { - } - /** * Update the currently running app to the latest available * version and replace this instance of the app with the From a32ce4fbf6208294d506d3b2dc3d4bcb76875e28 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 11 Jun 2017 23:57:03 +0100 Subject: [PATCH 14/19] remove unused string Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a06c443989..751249a3bc 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -165,7 +165,6 @@ "No update available.": "No update available.", "Downloading update...": "Downloading update...", "No update found.": "No update found.", - "Update is being downloaded.": "Update is being downloaded.", "You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.", "Welcome page": "Welcome page", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!", From 9493f4a8720432c66acb9a7b45f6c3f6e6641c2b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jun 2017 10:42:44 +0100 Subject: [PATCH 15/19] fallback from undefined for interpolation because _t called with undefined interpolation name: errorDetail even though when its undef its not used to sprinf-js would have been fine... Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/globals/UpdateCheckBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index 557698c711..d19a21cb8f 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -47,7 +47,7 @@ export default React.createClass({ }, render: function() { - const message = _t(statusText[this.props.status], { errorDetail: this.props.detail }); + const message = _t(statusText[this.props.status], { errorDetail: this.props.detail || '' }); let image; if (doneStatuses.includes(this.props.status)) { From 53e2ce33b878e61913958b2faced44f43eb4731a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jun 2017 13:39:35 +0100 Subject: [PATCH 16/19] remove unused i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 751249a3bc..135c6dabda 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -164,7 +164,6 @@ "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "No update available.": "No update available.", "Downloading update...": "Downloading update...", - "No update found.": "No update found.", "You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.", "Welcome page": "Welcome page", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!", From b6d2ba2019ed4ca5a9f75bfb065ddbe8509ec02a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jun 2017 13:47:29 +0100 Subject: [PATCH 17/19] store window ref globally so when splitting files its easier to access. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron_app/src/electron-main.js | 6 +++--- electron_app/src/tray.js | 18 +++++++++--------- electron_app/src/updater.js | 12 +++--------- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js index b0d4de4c21..5f9598afaf 100644 --- a/electron_app/src/electron-main.js +++ b/electron_app/src/electron-main.js @@ -185,7 +185,7 @@ electron.app.on('ready', () => { defaultHeight: 768, }); - mainWindow = new electron.BrowserWindow({ + mainWindow = global.mainWindow = new electron.BrowserWindow({ icon: iconPath, show: false, autoHideMenuBar: true, @@ -203,7 +203,7 @@ electron.app.on('ready', () => { mainWindow.hide(); // Create trayIcon icon - tray.create(mainWindow, { + tray.create({ icon_path: iconPath, brand: vectorConfig.brand || 'Riot', }); @@ -215,7 +215,7 @@ electron.app.on('ready', () => { } mainWindow.on('closed', () => { - mainWindow = null; + mainWindow = global.mainWindow = null; }); mainWindow.on('close', (e) => { if (!global.appQuitting && (tray.hasTray() || process.platform === 'darwin')) { diff --git a/electron_app/src/tray.js b/electron_app/src/tray.js index 039e7133fa..4225e1f6f3 100644 --- a/electron_app/src/tray.js +++ b/electron_app/src/tray.js @@ -26,17 +26,17 @@ exports.hasTray = function hasTray() { return (trayIcon !== null); }; -exports.create = function(win, config) { +exports.create = function(config) { // no trays on darwin if (process.platform === 'darwin' || trayIcon) return; const toggleWin = function() { - if (win.isVisible() && !win.isMinimized()) { - win.hide(); + if (global.mainWindow.isVisible() && !global.mainWindow.isMinimized()) { + global.mainWindow.hide(); } else { - if (win.isMinimized()) win.restore(); - if (!win.isVisible()) win.show(); - win.focus(); + if (global.mainWindow.isMinimized()) global.mainWindow.restore(); + if (!global.mainWindow.isVisible()) global.mainWindow.show(); + global.mainWindow.focus(); } }; @@ -60,7 +60,7 @@ exports.create = function(win, config) { trayIcon.on('click', toggleWin); let lastFavicon = null; - win.webContents.on('page-favicon-updated', async function(ev, favicons) { + global.mainWindow.webContents.on('page-favicon-updated', async function(ev, favicons) { let newFavicon = config.icon_path; if (favicons && favicons.length > 0 && favicons[0].startsWith('data:')) { newFavicon = favicons[0]; @@ -85,10 +85,10 @@ exports.create = function(win, config) { } trayIcon.setImage(newFavicon); - win.setIcon(newFavicon); + global.mainWindow.setIcon(newFavicon); }); - win.webContents.on('page-title-updated', function(ev, title) { + global.mainWindow.webContents.on('page-title-updated', function(ev, title) { trayIcon.setToolTip(title); }); }; diff --git a/electron_app/src/updater.js b/electron_app/src/updater.js index 106e3d2f23..49fa4e0419 100644 --- a/electron_app/src/updater.js +++ b/electron_app/src/updater.js @@ -67,17 +67,11 @@ module.exports.start = function startAutoUpdate(updateBaseUrl) { } ipcMain.on('install_update', installUpdate); - -let ipcChannel; -ipcMain.on('check_updates', function(event) { - ipcChannel = event.sender; - pollForUpdates(); - // event.sender.send('check_updates') // true/false/error = available(downloading)/notAvailable/error -}); +ipcMain.on('check_updates', pollForUpdates); function ipcChannelSendUpdateStatus(status) { - if (ipcChannel) { - ipcChannel.send('check_updates', status); + if (global.mainWindow) { + global.mainWindow.webContents.send('check_updates', status); } } From 499b1c2e70197f12ad73ecf2b20d9eb02cb0ccff Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Tue, 20 Jun 2017 14:05:17 +0100 Subject: [PATCH 18/19] add missing semicolon --- electron_app/src/electron-main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electron_app/src/electron-main.js b/electron_app/src/electron-main.js index 5f9598afaf..c8dd6266a3 100644 --- a/electron_app/src/electron-main.js +++ b/electron_app/src/electron-main.js @@ -172,7 +172,7 @@ electron.app.on('ready', () => { if (vectorConfig.update_base_url) { console.log(`Starting auto update with base URL: ${vectorConfig.update_base_url}`); - updater.start(vectorConfig.update_base_url) + updater.start(vectorConfig.update_base_url); } else { console.log('No update_base_url is defined: auto update is disabled'); } From e33e0effa3aa88f2e3c4b344cbc78167c2dde40b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 22 Jun 2017 15:05:13 +0100 Subject: [PATCH 19/19] use `_t` on string literals and i18n 'warning' Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/globals/UpdateCheckBar.js | 32 +++++++++++++------ src/i18n/strings/en_EN.json | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js index d19a21cb8f..926ccbcccf 100644 --- a/src/components/views/globals/UpdateCheckBar.js +++ b/src/components/views/globals/UpdateCheckBar.js @@ -22,13 +22,6 @@ import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; import {updateCheckStatusEnum} from '../../../vector/platform/VectorBasePlatform'; import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton'; -const statusText = { - CHECKING: 'Checking for an update...', - ERROR: 'Error encountered (%(errorDetail)s).', - NOTAVAILABLE: 'No update available.', - DOWNLOADING: 'Downloading update...', -}; - const doneStatuses = [ updateCheckStatusEnum.ERROR, updateCheckStatusEnum.NOTAVAILABLE, @@ -42,16 +35,37 @@ export default React.createClass({ detail: React.PropTypes.string, }, + getDefaultProps: function() { + return { + detail: '', + } + }, + + getStatusText: function() { + switch(this.props.status) { + case updateCheckStatusEnum.ERROR: + return _t('Error encountered (%(errorDetail)s).', { errorDetail: this.props.detail }); + case updateCheckStatusEnum.CHECKING: + return _t('Checking for an update...'); + case updateCheckStatusEnum.NOTAVAILABLE: + return _t('No update available.'); + case updateCheckStatusEnum.DOWNLOADING: + return _t('Downloading update...'); + } + } + , + hideToolbar: function() { PlatformPeg.get().stopUpdateCheck(); }, render: function() { - const message = _t(statusText[this.props.status], { errorDetail: this.props.detail || '' }); + const message = this.getStatusText(); + const warning = _t('Warning'); let image; if (doneStatuses.includes(this.props.status)) { - image = Warning; + image = {warning}/; } else { image = {message}/; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a535ced6ce..10c21a62d7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -159,6 +159,7 @@ "Today": "Today", "Yesterday": "Yesterday", "OK": "OK", + "Warning": "Warning", "Checking for an update...": "Checking for an update...", "Error encountered (%(errorDetail)s).": "Error encountered (%(errorDetail)s).", "No update available.": "No update available.",