Skip to content

Commit

Permalink
Merge pull request #4176 from vector-im/t3chguy/updating_stuff
Browse files Browse the repository at this point in the history
improve update polling electron and provide a manual check for updates button
  • Loading branch information
dbkr authored Jun 22, 2017
2 parents a1132ca + e33e0ef commit fab50bc
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 127 deletions.
77 changes: 8 additions & 69 deletions electron_app/src/electron-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,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');

Expand All @@ -46,69 +47,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
Expand All @@ -120,8 +61,6 @@ process.on('uncaughtException', function(error) {
console.log('Unhandled exception', error);
});

electron.ipcMain.on('install_update', installUpdate);

let focusHandlerAttached = false;
electron.ipcMain.on('setBadgeCount', function(ev, count) {
electron.app.setBadgeCount(count);
Expand Down Expand Up @@ -233,7 +172,7 @@ 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');
}
Expand All @@ -246,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,
Expand All @@ -264,7 +203,7 @@ electron.app.on('ready', () => {
mainWindow.hide();

// Create trayIcon icon
tray.create(mainWindow, {
tray.create({
icon_path: iconPath,
brand: vectorConfig.brand || 'Riot',
});
Expand All @@ -276,10 +215,10 @@ electron.app.on('ready', () => {
}

mainWindow.on('closed', () => {
mainWindow = null;
mainWindow = global.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)
Expand All @@ -302,7 +241,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
Expand Down
18 changes: 9 additions & 9 deletions electron_app/src/tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
};

Expand All @@ -62,7 +62,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) {
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) {
if (lastFavicon !== null) {
win.setIcon(defaultIcon);
Expand Down Expand Up @@ -90,10 +90,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);
});
};
84 changes: 84 additions & 0 deletions electron_app/src/updater.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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 {
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
// 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.
url = `${updateBaseUrl}macos/?localVersion=${encodeURIComponent(app.getVersion())}`;

} else if (process.platform === 'win32') {
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');
}

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

ipcMain.on('install_update', installUpdate);
ipcMain.on('check_updates', pollForUpdates);

function ipcChannelSendUpdateStatus(status) {
if (global.mainWindow) {
global.mainWindow.webContents.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);
});
2 changes: 1 addition & 1 deletion src/components/views/globals/MatrixToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = React.createClass({
render: function() {
return (
<div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="Warning"/>
<div className="mx_MatrixToolbar_content">
{ _t('You are not receiving desktop notifications') } <a className="mx_MatrixToolbar_link" onClick={ this.onClick }> { _t('Enable them now') }</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/globals/NewVersionBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default React.createClass({
}
return (
<div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="Warning"/>
<div className="mx_MatrixToolbar_content">
{_t("A new version of Riot is available.")}
</div>
Expand Down
85 changes: 85 additions & 0 deletions src/components/views/globals/UpdateCheckBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright 2017 Michael Telatynski <[email protected]>
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 { _t } from 'matrix-react-sdk/lib/languageHandler';
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 doneStatuses = [
updateCheckStatusEnum.ERROR,
updateCheckStatusEnum.NOTAVAILABLE,
];

export default React.createClass({
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,
},

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 = this.getStatusText();
const warning = _t('Warning');

let image;
if (doneStatuses.includes(this.props.status)) {
image = <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt={warning}/>;
} else {
image = <img className="mx_MatrixToolbar_warning" src="img/spinner.gif" width="24" height="23" alt={message}/>;
}

return (
<div className="mx_MatrixToolbar">
{image}
<div className="mx_MatrixToolbar_content">
{message}
</div>
<AccessibleButton className="mx_MatrixToolbar_close" onClick={this.hideToolbar}>
<img src="img/cancel.svg" width="18" height="18" />
</AccessibleButton>
</div>
);
}
});
5 changes: 5 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@
"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.",
"Downloading update...": "Downloading update...",
"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!",
Expand Down
Loading

0 comments on commit fab50bc

Please sign in to comment.