add auto update checking

This commit is contained in:
Guillermo Rauch 2016-07-01 13:01:33 -07:00
parent 76886669da
commit db21e8e87d
6 changed files with 122 additions and 6 deletions

View file

@ -63,3 +63,23 @@ header {
.resize-indicator.showing {
opacity: 1;
}
.update-indicator {
background: rgba(255, 51, 76, .8);
padding: 6px 14px;
color: #fff;
font: 11px Menlo;
position: fixed;
bottom: 20px;
right: 20px;
opacity: 0;
transition: opacity 150ms ease-in;
}
.update-indicator a {
color: #fff;
}
.update-indicator.showing {
opacity: 1;
}

View file

@ -6,6 +6,7 @@ import classes from 'classnames';
import getTextMetrics from './text-metrics';
import shallowCompare from 'react-addons-shallow-compare';
import React, { Component } from 'react';
import UpdateChecker from './update-checker';
export default class HyperTerm extends Component {
constructor () {
@ -19,7 +20,8 @@ export default class HyperTerm extends Component {
active: null,
activeMarkers: [],
mac: /Mac/.test(navigator.userAgent),
resizeIndicatorShowing: false
resizeIndicatorShowing: false,
updateVersion: null
};
// we set this to true when the first tab
@ -34,6 +36,7 @@ export default class HyperTerm extends Component {
this.onResize = this.onResize.bind(this);
this.onChange = this.onChange.bind(this);
this.openExternal = this.openExternal.bind(this);
this.focusActive = this.focusActive.bind(this);
this.onHeaderMouseDown = this.onHeaderMouseDown.bind(this);
@ -80,9 +83,19 @@ export default class HyperTerm extends Component {
<div className={classes('resize-indicator', { showing: this.state.resizeIndicatorShowing })}>
{ this.state.cols }x{ this.state.rows }
</div>
<div className={classes('update-indicator', { showing: null !== this.state.updateVersion })}>
Update available (<b>{ this.state.updateVersion }</b>).
{' '}
<a href='https://hyperterm.now.sh' onClick={this.openExternal} target='_blank'>Download</a>
</div>
</div>;
}
openExternal (ev) {
ev.preventDefault();
this.rpc.emit('open external', { url: ev.target.href });
}
requestTab () {
this.rpc.emit('new', this.getDimensions());
}
@ -156,6 +169,7 @@ export default class HyperTerm extends Component {
componentDidMount () {
this.rpc = new RPC();
this.updateChecker = new UpdateChecker(this.onUpdateAvailable.bind(this));
this.setState(this.getDimensions());
// open a new tab upon mounting
@ -218,6 +232,10 @@ export default class HyperTerm extends Component {
Mousetrap.bind('command+alt+right', this.moveRight);
}
onUpdateAvailable (updateVersion) {
this.setState({ updateVersion });
}
moveTo (n) {
if (this.state.sessions[n]) {
this.setActive(n);
@ -359,5 +377,6 @@ export default class HyperTerm extends Component {
this.rpc.destroy();
clearTimeout(this.resizeIndicatorTimeout);
Mousetrap.reset();
this.updateChecker.destroy();
}
}

View file

@ -7,7 +7,9 @@
"mousetrap": "1.6.0",
"classnames": "2.2.5",
"react": "15.1.0",
"react-dom": "15.1.0"
"react-dom": "15.1.0",
"semver-compare": "^1.0.0",
"json-loader": "^0.5.4"
},
"devDependencies": {
"eslint": "2.13.1",
@ -30,10 +32,19 @@
],
"rules": {
"yoda": 0,
"semi": [2, "always"],
"semi": [
2,
"always"
],
"no-unused-vars": 2,
"no-extra-semi": 2,
"semi-spacing": [2, { "before": false, "after": true }],
"semi-spacing": [
2,
{
"before": false,
"after": true
}
],
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1
},
@ -54,4 +65,4 @@
"lint": "eslint *.js",
"build": "NODE_ENV=production webpack"
}
}
}

58
app/update-checker.js Normal file
View file

@ -0,0 +1,58 @@
/*global fetch:false*/
import { version as currentVersion } from '../package';
import compare from 'semver-compare';
export default class UpdateChecker {
constructor (fn, { interval = 5000 } = {}) {
this.callback = fn;
this.interval = interval;
this.check();
this.lastKnown = null;
}
check () {
const done = () => {
this.checkTimer = setTimeout(() => {
this.check();
}, this.interval);
};
console.log('checking for update');
fetch('https://hyperterm.now.sh/data.json')
.then((res) => {
if (200 !== res.status) {
console.error('Update check error. Status (%d)', res.status);
return done();
}
res.json()
.then(({ version }) => {
if (this.lastKnown !== version) {
this.lastKnown = version;
if (1 === compare(version, currentVersion)) {
console.log('update found');
this.callback(version);
} else {
console.log('no update. latest:', version);
}
}
done();
})
.catch((err) => {
console.error('Update JSON parse error', err.stack);
done();
});
}).catch((err) => {
console.error('Update check error', err.stack);
done();
});
}
destroy () {
this.aborted = true;
clearTimeout(this.checkTimer);
}
}

View file

@ -20,6 +20,10 @@ module.exports = {
'babel-loader'
]
},
{
test: /\.json/,
loader: 'json-loader'
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'

View file

@ -1,4 +1,4 @@
const { app, BrowserWindow, Menu } = require('electron');
const { app, BrowserWindow, shell, Menu } = require('electron');
const createRPC = require('./rpc');
const Session = require('./session');
const genUid = require('uid2');
@ -81,6 +81,10 @@ app.on('ready', () => {
sessions.get(uid).write(data);
});
rpc.on('open external', ({ url }) => {
shell.openExternal(url);
});
const deleteSessions = () => {
sessions.forEach((session, key) => {
session.removeAllListeners();