no auto-update (WIP)

This commit is contained in:
Antelle 2015-10-25 22:26:33 +03:00
parent 417cfc73ee
commit 73a51db7cf
8 changed files with 195 additions and 13 deletions

View File

@ -4,11 +4,13 @@ var AppModel = require('./models/app-model'),
AppView = require('./views/app-view'),
KeyHandler = require('./comp/key-handler'),
Alerts = require('./comp/alerts'),
DropboxLink = require('./comp/dropbox-link');
DropboxLink = require('./comp/dropbox-link'),
Updater = require('./comp/updater');
$(function() {
require('./mixins/view');
Updater.check();
if (location.href.indexOf('state=') >= 0) {
DropboxLink.receive();
return;

View File

@ -4,6 +4,7 @@ var Backbone = require('backbone');
var Launcher;
if (window.process && window.process.versions && window.process.versions.electron) {
/* jshint node:true */
Launcher = {
name: 'electron',
version: window.process.versions.electron,
@ -35,6 +36,27 @@ if (window.process && window.process.versions && window.process.versions.electro
},
fileExists: function(path) {
return this.req('fs').existsSync(path);
},
httpGet: function(config) {
var http = require(config.url.lastIndexOf('https', 0) === 0 ? 'https' : 'http');
http.get(config.url, function(res) {
var data = [];
res.on('data', function (chunk) { data.push(chunk); });
res.on('end', function() {
console.log('data', data);
data = Buffer.concat(data);
console.log('data', data);
if (config.utf8) {
data = data.toString('utf8');
}
console.log('data', data);
if (config.complete) {
config.copmlete(null, data);
}
});
}).on('error', function(err) {
if (config.complete) { config.complete(err); }
});
}
};
window.launcherOpen = function(path) {

View File

@ -0,0 +1,50 @@
'use strict';
var RuntimeInfo = require('./runtime-info'),
Links = require('../const/links'),
Launcher = require('../comp/launcher');
var Updater = {
lastCheckDate: null,
lastVersion: null,
lastVersionReleaseDate: null,
needUpdate: null,
status: 'ready',
check: function(complete) {
if (!Launcher) {
return;
}
this.status = 'checking';
Launcher.httpGet({
url: Links.WebApp + 'manifest.appcache',
utf8: true,
complete: (function (err, data) {
if (err) {
this.status = 'err';
if (complete) {
complete(err);
}
return;
}
var match = data.match('#\s*(\d+\-\d+\-\d+):v([\d+\.\w]+)');
if (!match) {
this.status = 'err';
if (complete) {
complete(err);
}
return;
}
this.lastVersionReleaseDate = new Date(match[1]);
this.lastVersion = match[2];
this.lastCheckDate = new Date();
this.status = 'ok';
this.needUpdate = this.lastVersion === RuntimeInfo.version;
if (complete) {
complete();
}
}).bind(this)
});
}
};
module.exports = Updater;

View File

@ -5,7 +5,8 @@ var Backbone = require('backbone');
var AppSettingsModel = Backbone.Model.extend({
defaults: {
theme: 'd',
lastOpenFile: ''
lastOpenFile: '',
autoUpdate: true
},
initialize: function() {

View File

@ -2,13 +2,16 @@
var Backbone = require('backbone'),
Launcher = require('../../comp/launcher'),
Updater = require('../../comp/updater'),
Format = require('../../util/format'),
AppSettingsModel = require('../../models/app-settings-model');
var SettingsGeneralView = Backbone.View.extend({
template: require('templates/settings/settings-general.html'),
events: {
'change .settings__general-theme': 'changeTheme',
'change #settings__general-theme': 'changeTheme',
'change #settings__general-auto-update': 'changeAutoUpdate',
'click .settings__general-dev-tools-link': 'openDevTools'
},
@ -19,10 +22,30 @@ var SettingsGeneralView = Backbone.View.extend({
},
render: function() {
var activeTheme = AppSettingsModel.instance.get('theme');
var lastUpdateCheck;
switch (Updater.status) {
case 'checking':
lastUpdateCheck = 'Checking...';
break;
case 'err':
lastUpdateCheck = 'Error checking';
break;
case 'ok':
lastUpdateCheck = Format.dtStr(Updater.lastCheckDate) + ': ' +
(Updater.needUpdate ? 'New version available: ' + Updater.lastVersion +
' (released ' + Format.dStr(Updater.lastVersionReleaseDate) + ')'
: 'You are using the latest version');
break;
default:
lastUpdateCheck = 'Never';
break;
}
this.renderTemplate({
themes: this.allThemes,
activeTheme: activeTheme,
activeTheme: AppSettingsModel.instance.get('theme'),
autoUpdate: AppSettingsModel.instance.get('autoUpdate'),
canAutoUpdate: !!Launcher,
lastUpdateCheck: lastUpdateCheck,
devTools: Launcher && Launcher.devTools
});
},
@ -32,6 +55,14 @@ var SettingsGeneralView = Backbone.View.extend({
AppSettingsModel.instance.set('theme', theme);
},
changeAutoUpdate: function(e) {
var autoUpdate = e.target.checked;
AppSettingsModel.instance.set('autoUpdate', autoUpdate);
if (autoUpdate) {
Updater.check();
}
},
openDevTools: function() {
if (Launcher) {
Launcher.openDevTools();

View File

@ -3,12 +3,20 @@
<h2>Appearance</h2>
<div>
<label for="settings__general-theme">Theme:</label>
<select class="settings__general-theme settings__select" id="settings__general-theme">
<select class="settings__select" id="settings__general-theme">
<% _.forEach(themes, function(name, key) { %>
<option value="<%= key %>" <%= key === activeTheme ? 'selected' : '' %>><%- name %></option>
<% }); %>
</select>
</div>
<% if (canAutoUpdate) { %>
<h2>Function</h2>
<div>
<input type="checkbox" class="settings__input" id="settings__general-auto-update" <%- autoUpdate ? 'checked' : '' %> />
<label for="settings__general-auto-update">Automatic updates</label>
<div>Last update check: <%- lastUpdateCheck %></div>
</div>
<% } %>
<% if (devTools) { %>
<h2>Advanced</h2>
<a class="settings__general-dev-tools-link">Show dev tools</a>

32
electron/loading.html Normal file
View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>KeeWeb</title>
<style type="text/css">
html, body {
font-family: -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;
background: #342F2E;
color: #FFEAE9;
text-align: center;
width: 100vw;
height: 100vh;
user-select: none;
-webkit-user-select: none;
cursor: default;
}
h1 {
display: block;
margin: 45vh 20px 0;
}
</style>
<script>
function setTitle(title) {
document.getElementById('title').innerHTML = title;
}
</script>
</head>
<body>
<h1 id="title">Loading... Please wait</h1>
</body>
</html>

View File

@ -10,12 +10,13 @@ var app = require('app'),
var mainWindow = null,
openFile = process.argv.filter(function(arg) { return /\.kdbx$/i.test(arg); })[0],
ready = false;
ready = false,
htmlPath = path.join(app.getPath('userData'), 'index.html');
htmlPath = path.join(__dirname, '../tmp/index.html');
app.on('window-all-closed', function() { app.quit(); });
app.on('ready', function() {
var htmlPath = path.join(app.getPath('userData'), 'index.html');
mainWindow = new BrowserWindow({
show: false,
width: 1000, height: 700, 'min-width': 600, 'min-height': 300,
@ -25,12 +26,14 @@ app.on('ready', function() {
if (fs.existsSync(htmlPath)) {
mainWindow.loadUrl('file://' + htmlPath);
} else {
mainWindow.loadUrl('https://antelle.github.io/keeweb/index.html');
downloadFile();
}
mainWindow.webContents.on('dom-ready', function() {
mainWindow.show();
ready = true;
notifyOpenFile();
setTimeout(function() {
mainWindow.show();
ready = true;
notifyOpenFile();
}, 50);
});
mainWindow.on('closed', function() { mainWindow = null; });
});
@ -48,3 +51,36 @@ function notifyOpenFile() {
openFile = null;
}
}
function downloadFile() {
console.log('Downloading file...');
mainWindow.loadUrl('file://' + path.join(__dirname, 'loading.html'));
var fileData = [];
require('https').get('https://antelle.github.io/keeweb/index.html', function(res) {
res.on('data', function (chunk) {
fileData.push(chunk);
});
res.on('end', function() {
fileData = Buffer.concat(fileData);
var fileDataStr = fileData.toString('utf8');
if (/^\s*<![\s\S]*<\/html>\s*$/.test(fileDataStr) && fileData.byteLength > 100000) {
fs.writeFileSync(htmlPath, fileData);
if (mainWindow) {
mainWindow.loadUrl('file://' + htmlPath);
}
} else {
showDownloadError('Invalid file downloaded');
}
});
}).on('error', function(err) {
showDownloadError(err);
});
}
function showDownloadError(err) {
console.error(err);
if (mainWindow) {
mainWindow.webContents.executeJavaScript('setTitle("Failed to download the app. Please restart me.<br/>' +
'This app requires Internet connection to start for the first time.")');
}
}