Merge branch 'real-upgrades'
* real-upgrades: minor some progress, but needs more work Real update system
This commit is contained in:
commit
0d5ef61944
6 changed files with 193 additions and 30 deletions
60
app/main.js
60
app/main.js
|
@ -1,4 +1,4 @@
|
||||||
const {app, BrowserWindow, ipcMain} = require('electron');
|
const {app, BrowserWindow, ipcMain, shell} = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const jayson = require('jayson');
|
const jayson = require('jayson');
|
||||||
// tree-kill has better cross-platform handling of
|
// tree-kill has better cross-platform handling of
|
||||||
|
@ -27,7 +27,7 @@ function createWindow () {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function lauchDaemon() {
|
function launchDaemon() {
|
||||||
if (subpy) {
|
if (subpy) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ function launchDaemonIfNotRunning() {
|
||||||
function (err, res) {
|
function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('lbrynet daemon needs to be launched')
|
console.log('lbrynet daemon needs to be launched')
|
||||||
lauchDaemon();
|
launchDaemon();
|
||||||
} else {
|
} else {
|
||||||
console.log('lbrynet daemon is already running')
|
console.log('lbrynet daemon is already running')
|
||||||
}
|
}
|
||||||
|
@ -115,23 +115,49 @@ app.on('activate', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function shutdownDaemon() {
|
function shutdownDaemon(evenIfNotStartedByApp = false) {
|
||||||
console.log('Shutdown triggered');
|
if (subpy) {
|
||||||
if (subpy == null) {
|
|
||||||
// TODO: In this case, we didn't start the process so I'm hesitant
|
|
||||||
// to shut it down. We might want to send a stop command
|
|
||||||
// though instead of just letting it run.
|
|
||||||
console.log('Not killing lbrynet daemon because we did not start it')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (win) {
|
|
||||||
win.loadURL(`file://${__dirname}/dist/quit.html`);
|
|
||||||
}
|
|
||||||
quitting = true;
|
|
||||||
console.log('Killing lbrynet-daemon process');
|
console.log('Killing lbrynet-daemon process');
|
||||||
kill(subpy.pid, undefined, (err) => {
|
kill(subpy.pid, undefined, (err) => {
|
||||||
console.log('Killed lbrynet-daemon process');
|
console.log('Killed lbrynet-daemon process');
|
||||||
});
|
});
|
||||||
|
} else if (evenIfNotStartedByApp) {
|
||||||
|
console.log('Killing lbrynet-daemon, even though app did not start it');
|
||||||
|
client.request('daemon_stop', []);
|
||||||
|
// TODO: If the daemon errors or times out when we make this request, find
|
||||||
|
// the process and force quit it.
|
||||||
|
} else {
|
||||||
|
console.log('Not killing lbrynet-daemon because app did not start it')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it safe to start the installer before the daemon finishes running?
|
||||||
|
// If not, we should wait until the daemon is closed before we start the install.
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('shutdown', shutdownDaemon);
|
function shutdown() {
|
||||||
|
if (win) {
|
||||||
|
win.loadURL(`file://${__dirname}/dist/quit.html`);
|
||||||
|
}
|
||||||
|
quitting = true;
|
||||||
|
shutdownDaemon();
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgrade(event, installerPath) {
|
||||||
|
app.on('quit', () => {
|
||||||
|
shell.openItem(installerPath);
|
||||||
|
});
|
||||||
|
if (win) {
|
||||||
|
win.loadURL(`file://${__dirname}/dist/upgrade.html`);
|
||||||
|
}
|
||||||
|
quitting = true;
|
||||||
|
shutdownDaemon(true);
|
||||||
|
// wait for daemon to shut down before upgrading
|
||||||
|
// what to do if no shutdown in a long time?
|
||||||
|
console.log('Update downloaded to ', installerPath);
|
||||||
|
console.log('The app will close, and you will be prompted to install the latest version of LBRY.');
|
||||||
|
console.log('After the install is complete, please reopen the app.');
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.on('upgrade', upgrade);
|
||||||
|
|
||||||
|
ipcMain.on('shutdown', shutdown);
|
|
@ -13,7 +13,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
* Wherever possible, use outpoints for unique IDs instead of names or SD hashes
|
* Wherever possible, use outpoints for unique IDs instead of names or SD hashes
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
*
|
* Update process now easier and more reliable
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
||||||
|
|
37
ui/dist/upgrade.html
vendored
Normal file
37
ui/dist/upgrade.html
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<title>LBRY</title>
|
||||||
|
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
|
||||||
|
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
|
||||||
|
<link href="./js/mediaelement/mediaelementplayer.css" rel="stylesheet" type="text/css" />
|
||||||
|
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
|
||||||
|
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
|
||||||
|
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">
|
||||||
|
<link rel="icon" type="image/png" href="./img/fav/android-chrome-192x192.png" sizes="192x192">
|
||||||
|
<link rel="icon" type="image/png" href="./img/fav/favicon-16x16.png" sizes="16x16">
|
||||||
|
|
||||||
|
<meta name="msapplication-TileColor" content="#155B4A">
|
||||||
|
<meta name="msapplication-TileImage" content="/img/fav/mstile-144x144.png">
|
||||||
|
<meta name="theme-color" content="#155B4A">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: "#155b4a"
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<div id="canvas">
|
||||||
|
<div class="load-screen" style="color: white; min-height: 100vh; min-width: 100vw; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||||||
|
<img src="./img/lbry-white-485x160.png" alt="LBRY">
|
||||||
|
<div style="margin-top: 24px; width: 325px; text-align: center;">
|
||||||
|
<h3>
|
||||||
|
<span>Starting LBRY Upgrade <span class="busy-indicator"></span>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</html>
|
82
ui/js/app.js
82
ui/js/app.js
|
@ -24,6 +24,9 @@ import {Link} from './component/link.js';
|
||||||
|
|
||||||
const {remote, ipcRenderer, shell} = require('electron');
|
const {remote, ipcRenderer, shell} = require('electron');
|
||||||
const {download} = remote.require('electron-dl');
|
const {download} = remote.require('electron-dl');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const app = require('electron').remote.app;
|
||||||
|
|
||||||
|
|
||||||
var App = React.createClass({
|
var App = React.createClass({
|
||||||
|
@ -36,6 +39,19 @@ var App = React.createClass({
|
||||||
data: 'Error data',
|
data: 'Error data',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_upgradeDownloadItem: null,
|
||||||
|
_version: null,
|
||||||
|
|
||||||
|
// Temporary workaround since electron-dl throws errors when you try to get the filename
|
||||||
|
getUpgradeFilename: function() {
|
||||||
|
if (os.platform() == 'darwin') {
|
||||||
|
return `LBRY-${this._version}.dmg`;
|
||||||
|
} else if (os.platform() == 'linux') {
|
||||||
|
return `LBRY-${this._version}.deb`;
|
||||||
|
} else {
|
||||||
|
return `LBRY-${this._version}_amd64.deb`;
|
||||||
|
}
|
||||||
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
// For now, routes are in format ?page or ?page=args
|
// For now, routes are in format ?page or ?page=args
|
||||||
var match, param, val, viewingPage,
|
var match, param, val, viewingPage,
|
||||||
|
@ -53,6 +69,7 @@ var App = React.createClass({
|
||||||
updateUrl: null,
|
updateUrl: null,
|
||||||
isOldOSX: null,
|
isOldOSX: null,
|
||||||
downloadProgress: null,
|
downloadProgress: null,
|
||||||
|
downloadComplete: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
@ -79,6 +96,8 @@ var App = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getVersionInfo((versionInfo) => {
|
lbry.getVersionInfo((versionInfo) => {
|
||||||
|
this._version = versionInfo.lbrynet_version; // temp for building upgrade filename
|
||||||
|
|
||||||
var isOldOSX = false;
|
var isOldOSX = false;
|
||||||
if (versionInfo.os_system == 'Darwin') {
|
if (versionInfo.os_system == 'Darwin') {
|
||||||
var updateUrl = 'https://lbry.io/get/lbry.dmg';
|
var updateUrl = 'https://lbry.io/get/lbry.dmg';
|
||||||
|
@ -91,10 +110,7 @@ var App = React.createClass({
|
||||||
} else if (versionInfo.os_system == 'Linux') {
|
} else if (versionInfo.os_system == 'Linux') {
|
||||||
var updateUrl = 'https://lbry.io/get/lbry.deb';
|
var updateUrl = 'https://lbry.io/get/lbry.deb';
|
||||||
} else if (versionInfo.os_system == 'Windows') {
|
} else if (versionInfo.os_system == 'Windows') {
|
||||||
// A little weird, but for electron, the installer is
|
var updateUrl = 'https://lbry.io/get/lbry.exe';
|
||||||
// actually an exe. Maybe a better url would
|
|
||||||
// be something like /get/windows ?
|
|
||||||
var updateUrl = 'https://lbry.io/get/lbry.msi';
|
|
||||||
} else {
|
} else {
|
||||||
var updateUrl = 'https://lbry.io/get';
|
var updateUrl = 'https://lbry.io/get';
|
||||||
}
|
}
|
||||||
|
@ -123,17 +139,51 @@ var App = React.createClass({
|
||||||
handleUpgradeClicked: function() {
|
handleUpgradeClicked: function() {
|
||||||
// TODO: create a callback for onProgress and have the UI
|
// TODO: create a callback for onProgress and have the UI
|
||||||
// show download progress
|
// show download progress
|
||||||
// TODO: remove the saveAs popup. Thats just me being lazy and having
|
|
||||||
// some indication that the download is happening
|
|
||||||
// TODO: calling lbry.stop() ends up displaying the "daemon
|
// TODO: calling lbry.stop() ends up displaying the "daemon
|
||||||
// unexpectedly stopped" page. Have a better way of shutting down
|
// unexpectedly stopped" page. Have a better way of shutting down
|
||||||
|
let dir = app.getPath('temp');
|
||||||
let options = {
|
let options = {
|
||||||
onProgress: (p) => this.setState({downloadProgress: Math.round(p * 100)}),
|
onProgress: (p) => this.setState({downloadProgress: Math.round(p * 100)}),
|
||||||
}
|
directory: dir,
|
||||||
|
};
|
||||||
download(remote.getCurrentWindow(), this.state.updateUrl, options)
|
download(remote.getCurrentWindow(), this.state.updateUrl, options)
|
||||||
.then(dl => ipcRenderer.send('shutdown'));
|
.then(downloadItem => {
|
||||||
|
/**
|
||||||
|
* TODO: get the download path directly from the download object. It should just be
|
||||||
|
* downloadItem.getSavePath(), but the copy on the main process is being garbage collected
|
||||||
|
* too soon.
|
||||||
|
*/
|
||||||
|
|
||||||
|
this._upgradeDownloadItem = downloadItem;
|
||||||
|
this._upgradeDownloadPath = path.join(dir, this.getUpgradeFilename());
|
||||||
|
this.setState({
|
||||||
|
downloadComplete: true
|
||||||
|
});
|
||||||
|
});
|
||||||
this.setState({modal: 'downloading'});
|
this.setState({modal: 'downloading'});
|
||||||
},
|
},
|
||||||
|
handleStartUpgradeClicked: function() {
|
||||||
|
ipcRenderer.send('upgrade', this._upgradeDownloadPath);
|
||||||
|
},
|
||||||
|
cancelUpgrade: function() {
|
||||||
|
if (this._upgradeDownloadItem) {
|
||||||
|
/*
|
||||||
|
* Right now the remote reference to the download item gets garbage collected as soon as the
|
||||||
|
* the download is over (maybe even earlier), so trying to cancel a finished download may
|
||||||
|
* throw an error.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
this._upgradeDownloadItem.cancel();
|
||||||
|
} catch (err) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
downloadProgress: null,
|
||||||
|
downloadComplete: false,
|
||||||
|
modal: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
handleSkipClicked: function() {
|
handleSkipClicked: function() {
|
||||||
sessionStorage.setItem('upgradeSkipped', true);
|
sessionStorage.setItem('upgradeSkipped', true);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -246,8 +296,22 @@ var App = React.createClass({
|
||||||
|
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={this.state.modal == 'downloading'} contentLabel="Downloading Update" type="custom">
|
<Modal isOpen={this.state.modal == 'downloading'} contentLabel="Downloading Update" type="custom">
|
||||||
Downloading Update: {this.state.downloadProgress}% Complete
|
Downloading Update{this.state.downloadProgress ? `: ${this.state.downloadProgress}%` : null}
|
||||||
<Line percent={this.state.downloadProgress} strokeWidth="4"/>
|
<Line percent={this.state.downloadProgress} strokeWidth="4"/>
|
||||||
|
{this.state.downloadComplete ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<p>Click "Begin Upgrade" to start the upgrade process.</p>
|
||||||
|
<p>The app will close, and you will be prompted to install the latest version of LBRY.</p>
|
||||||
|
<p>After the install is complete, please reopen the app.</p>
|
||||||
|
</div>
|
||||||
|
) : null }
|
||||||
|
<div className="modal__buttons">
|
||||||
|
{this.state.downloadComplete
|
||||||
|
? <Link button="primary" label="Begin Upgrade" className="modal__button" onClick={this.handleStartUpgradeClicked} />
|
||||||
|
: null}
|
||||||
|
<Link button="alt" label="Cancel" className="modal__button" onClick={this.cancelUpgrade} />
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
<ExpandableModal isOpen={this.state.modal == 'error'} contentLabel="Error" className="error-modal"
|
<ExpandableModal isOpen={this.state.modal == 'error'} contentLabel="Error" className="error-modal"
|
||||||
overlayClassName="error-modal-overlay" onConfirmed={this.closeModal}
|
overlayClassName="error-modal-overlay" onConfirmed={this.closeModal}
|
||||||
|
|
|
@ -33,10 +33,10 @@ export const Modal = React.createClass({
|
||||||
{this.props.type == 'custom' // custom modals define their own buttons
|
{this.props.type == 'custom' // custom modals define their own buttons
|
||||||
? null
|
? null
|
||||||
: <div className="modal__buttons">
|
: <div className="modal__buttons">
|
||||||
|
<Link button="primary" label={this.props.confirmButtonLabel} className="modal__button" disabled={this.props.confirmButtonDisabled} onClick={this.props.onConfirmed} />
|
||||||
{this.props.type == 'confirm'
|
{this.props.type == 'confirm'
|
||||||
? <Link button="alt" label={this.props.abortButtonLabel} className="modal__button" disabled={this.props.abortButtonDisabled} onClick={this.props.onAborted} />
|
? <Link button="alt" label={this.props.abortButtonLabel} className="modal__button" disabled={this.props.abortButtonDisabled} onClick={this.props.onAborted} />
|
||||||
: null}
|
: null}
|
||||||
<Link button="primary" label={this.props.confirmButtonLabel} className="modal__button" disabled={this.props.confirmButtonDisabled} onClick={this.props.onConfirmed} />
|
|
||||||
</div>}
|
</div>}
|
||||||
</ReactModal>
|
</ReactModal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FormField from '../component/form.js';
|
import FormField from '../component/form.js';
|
||||||
|
import {Link} from '../component/link.js';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const {ipcRenderer} = require('electron');
|
||||||
|
|
||||||
const DeveloperPage = React.createClass({
|
const DeveloperPage = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -8,6 +12,7 @@ const DeveloperPage = React.createClass({
|
||||||
showDeveloperMenu: lbry.getClientSetting('showDeveloperMenu'),
|
showDeveloperMenu: lbry.getClientSetting('showDeveloperMenu'),
|
||||||
useCustomLighthouseServers: lbry.getClientSetting('useCustomLighthouseServers'),
|
useCustomLighthouseServers: lbry.getClientSetting('useCustomLighthouseServers'),
|
||||||
customLighthouseServers: lbry.getClientSetting('customLighthouseServers').join('\n'),
|
customLighthouseServers: lbry.getClientSetting('customLighthouseServers').join('\n'),
|
||||||
|
upgradePath: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleShowDeveloperMenuChange: function(event) {
|
handleShowDeveloperMenuChange: function(event) {
|
||||||
|
@ -23,6 +28,30 @@ const DeveloperPage = React.createClass({
|
||||||
useCustomLighthouseServers: event.target.checked,
|
useCustomLighthouseServers: event.target.checked,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleUpgradeFileChange: function(event) {
|
||||||
|
this.setState({
|
||||||
|
upgradePath: event.target.files[0].path,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleForceUpgradeClick: function() {
|
||||||
|
let upgradeSent = false;
|
||||||
|
if (!this.state.upgradePath) {
|
||||||
|
alert('Please select a file to upgrade from');
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const stats = fs.lstatSync(this.state.upgradePath);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
console.log('Starting upgrade using ' + this.state.upgradePath);
|
||||||
|
ipcRenderer.send('upgrade', this.state.upgradePath);
|
||||||
|
upgradeSent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
if (!upgradeSent) {
|
||||||
|
alert('Failed to start upgrade. Is "' + this.state.upgradePath + '" a valid path to the upgrade?');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
@ -43,6 +72,13 @@ const DeveloperPage = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
</section>
|
</section>
|
||||||
|
<section className="card">
|
||||||
|
<div className="form-row">
|
||||||
|
<FormField name="file" ref="file" type="file" onChange={this.handleUpgradeFileChange}/>
|
||||||
|
|
||||||
|
<Link label="Force Upgrade" button="alt" onClick={this.handleForceUpgradeClick} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue