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, + }); }); }