Merge pull request #2532 from vector-im/dbkr/platform_version

Move 'new version' support into Platform
This commit is contained in:
David Baker 2016-11-03 13:30:19 +00:00 committed by GitHub
commit efd0dab316
6 changed files with 155 additions and 75 deletions

View File

@ -16,9 +16,10 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); import React from 'react';
var sdk = require('matrix-react-sdk'); import sdk from 'matrix-react-sdk';
import Modal from 'matrix-react-sdk/lib/Modal'; import Modal from 'matrix-react-sdk/lib/Modal';
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
/** /**
* Check a version string is compatible with the Changelog * Check a version string is compatible with the Changelog
@ -29,37 +30,65 @@ function checkVersion(ver) {
return parts[0] == 'vector' && parts[2] == 'react' && parts[4] == 'js'; return parts[0] == 'vector' && parts[2] == 'react' && parts[4] == 'js';
} }
export default function NewVersionBar(props) { export default React.createClass({
const onChangelogClicked = () => { propTypes: {
const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog'); version: React.PropTypes.string.isRequired,
newVersion: React.PropTypes.string.isRequired,
releaseNotes: React.PropTypes.string,
},
Modal.createDialog(ChangelogDialog, { displayReleaseNotes: function(releaseNotes) {
version: props.version, const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
newVersion: props.newVersion, Modal.createDialog(QuestionDialog, {
title: "What's New",
description: <pre className="changelog_text">{releaseNotes}</pre>,
button: "Update",
onFinished: (update) => { onFinished: (update) => {
if(update) { if(update && PlatformPeg.get()) {
window.location.reload(); PlatformPeg.get().installUpdate();
} }
} }
}); });
}; },
let changelog_button; displayChangelog: function() {
if (checkVersion(props.version) && checkVersion(props.newVersion)) { const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog');
changelog_button = <button className="mx_MatrixToolbar_action" onClick={onChangelogClicked}>Changelog</button>; Modal.createDialog(ChangelogDialog, {
version: this.props.version,
newVersion: this.props.newVersion,
onFinished: (update) => {
if(update && PlatformPeg.get()) {
PlatformPeg.get().installUpdate();
}
}
});
},
onUpdateClicked: function() {
PlatformPeg.get().installUpdate();
},
render: function() {
let action_button;
// If we have release notes to display, we display them. Otherwise,
// we display the Changelog Dialog which takes two versions and
// automatically tells you what's changed (provided the versions
// are in the right format)
if (this.props.releaseNotes) {
action_button = <button className="mx_MatrixToolbar_action" onClick={this.displayReleaseNotes}>What's new?</button>;
} else if (checkVersion(this.props.version) && checkVersion(this.props.newVersion)) {
action_button = <button className="mx_MatrixToolbar_action" onClick={this.displayChangelog}>What's new?</button>;
} else if (PlatformPeg.get()) {
action_button = <button className="mx_MatrixToolbar_action" onClick={this.onUpdateClicked}>Update</button>;
} }
return ( return (
<div className="mx_MatrixToolbar"> <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="/!\"/>
<div className="mx_MatrixToolbar_content"> <div className="mx_MatrixToolbar_content">
A new version of Riot is available. Refresh your browser. A new version of Riot is available.
</div> </div>
{changelog_button} {action_button}
</div> </div>
); );
} }
});
NewVersionBar.propTypes = {
version: React.PropTypes.string.isRequired,
newVersion: React.PropTypes.string.isRequired,
};

View File

@ -288,3 +288,7 @@ textarea {
cursor: pointer; cursor: pointer;
display: inline; display: inline;
} }
.changelog_text {
font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
}

View File

@ -113,10 +113,6 @@ function onHashChange(ev) {
routeUrl(window.location); routeUrl(window.location);
} }
function onVersion(current, latest) {
window.matrixChat.onVersion(current, latest);
}
var loaded = false; var loaded = false;
var lastLoadedScreen = null; var lastLoadedScreen = null;
@ -165,8 +161,7 @@ window.onload = function() {
if (!validBrowser) { if (!validBrowser) {
return; return;
} }
UpdateChecker.setVersionListener(onVersion); UpdateChecker.start();
UpdateChecker.run();
routeUrl(window.location); routeUrl(window.location);
loaded = true; loaded = true;
if (lastLoadedScreen) { if (lastLoadedScreen) {

View File

@ -0,0 +1,42 @@
// @flow
/*
Copyright 2016 Aviral Dasgupta
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 BasePlatform from 'matrix-react-sdk/lib/BasePlatform'
/**
* Vector-specific extensions to the BasePlatform template
*/
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.
*/
pollForUpdate() {
}
/**
* Update the currently running app to the latest available
* version and replace this instance of the app with the
* new version.
*/
installUpdate() {
}
}

View File

@ -17,14 +17,16 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import BasePlatform from 'matrix-react-sdk/lib/BasePlatform'; import VectorBasePlatform from './VectorBasePlatform';
import Favico from 'favico.js'; import Favico from 'favico.js';
import request from 'browser-request';
import dis from 'matrix-react-sdk/lib/dispatcher.js'; import dis from 'matrix-react-sdk/lib/dispatcher.js';
import q from 'q'; import q from 'q';
export default class WebPlatform extends BasePlatform { export default class WebPlatform extends VectorBasePlatform {
constructor() { constructor() {
super(); super();
this.runningVersion = null;
// The 'animations' are really low framerate and look terrible. // The 'animations' are really low framerate and look terrible.
// Also it re-starts the animationb every time you set the badge, // Also it re-starts the animationb every time you set the badge,
// and we set the state each time, even if the value hasn't changed, // and we set the state each time, even if the value hasn't changed,
@ -123,4 +125,42 @@ export default class WebPlatform extends BasePlatform {
notification.close(); notification.close();
}, 5 * 1000); }, 5 * 1000);
} }
_getVersion() {
const deferred = q.defer();
request(
{ method: "GET", url: "version" },
(err, response, body) => {
if (err || response.status < 200 || response.status >= 300) {
if (err == null) err = { status: response.status };
deferred.reject(err);
return;
}
const ver = body.trim();
deferred.resolve(ver);
}
);
return deferred.promise;
}
pollForUpdate() {
this._getVersion().done((ver) => {
if (this.runningVersion == null) {
this.runningVersion = ver;
} else if (this.runningVersion != ver) {
dis.dispatch({
action: 'new_version',
currentVersion: this.runningVersion,
newVersion: ver,
});
}
}, (err) => {
console.error("Failed to poll for update", err);
});
}
installUpdate() {
window.location.reload();
}
} }

View File

@ -13,48 +13,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
var POKE_RATE_MS = 10 * 60 * 1000; // 10 min var POKE_RATE_MS = 10 * 60 * 1000; // 10 min
var currentVersion = null;
var latestVersion = null;
var listener = function(){}; // NOP
module.exports = { module.exports = {
setVersionListener: function(fn) { // invoked with fn(currentVer, newVer) start: function() {
listener = fn; module.exports.poll();
setInterval(module.exports.poll, POKE_RATE_MS);
}, },
run: function() { poll: function() {
var req = new XMLHttpRequest(); PlatformPeg.get().pollForUpdate();
req.addEventListener("load", function() {
if (!req.responseText) {
return;
}
var ver = req.responseText.trim();
if (!currentVersion) {
currentVersion = ver;
listener(currentVersion, currentVersion);
}
if (ver !== latestVersion) {
latestVersion = ver;
if (module.exports.hasNewVersion()) {
console.log("Current=%s Latest=%s", currentVersion, latestVersion);
listener(currentVersion, latestVersion);
}
}
});
var cacheBuster = "?ts=" + new Date().getTime();
req.open("GET", "version" + cacheBuster);
req.send(); // can't suppress 404s from being logged.
setTimeout(module.exports.run, POKE_RATE_MS);
},
getCurrentVersion: function() {
return currentVersion;
},
hasNewVersion: function() {
return currentVersion && latestVersion && (currentVersion !== latestVersion);
} }
}; };