diff --git a/.babelrc b/.babelrc
index ba11916..b8a0eba 100644
--- a/.babelrc
+++ b/.babelrc
@@ -6,7 +6,10 @@
"stage-2",
],
"plugins": [
+ "transform-async-to-generator",
"transform-class-properties",
+ "transform-es2015-classes",
"transform-function-bind",
+ "transform-object-rest-spread",
]
}
diff --git a/.eslintrc.js b/.eslintrc.js
index b10fd46..c1e1590 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,6 +1,12 @@
module.exports = {
extends: 'eslint-config-airbnb',
+ parser: 'babel-eslint',
+ parserOptions: {
+ sourceType: 'module',
+ ecmaVersion: 8,
+ },
rules: {
+ 'react/sort-comp': 0,
'import/extensions': 0,
'import/no-extraneous-dependencies': 0,
'import/no-unresolved': [2, {ignore: ['electron']}],
diff --git a/package.json b/package.json
index 388c2e7..65462a3 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"@commitlint/travis-cli": "^8.0.0",
"@semantic-release/github": "^5.4.0",
"babel-cli": "^6.26.0",
+ "babel-eslint": "10.0.2",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
diff --git a/src/app.jsx b/src/app.jsx
index 3ad109c..f153a6d 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -1,17 +1,21 @@
import Container from '@material-ui/core/Container';
import CssBaseline from '@material-ui/core/CssBaseline';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import FormGroup from '@material-ui/core/FormGroup';
import Grid from '@material-ui/core/Grid';
import LinearProgress from '@material-ui/core/LinearProgress';
import PropTypes from 'prop-types';
import React from 'react';
import Slider from '@material-ui/lab/Slider';
+import Switch from '@material-ui/core/Switch';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import log from 'electron-log';
import {Connection} from '@solana/web3.js';
-import {withStyles} from '@material-ui/core/styles';
-import {ReactThemes, ReactTerminal} from 'react-terminal-component';
import {EmulatorState, OutputFactory, Outputs} from 'javascript-terminal';
+import {ReactThemes, ReactTerminal} from 'react-terminal-component';
+import {withStyles} from '@material-ui/core/styles';
+import Store from 'electron-store';
import {url} from './url';
import {Replicator} from './replicator';
@@ -38,11 +42,11 @@ const styles = theme => ({
marginTop: theme.spacing(5),
},
progressBar: {
- marginTop: theme.spacing(1),
+ marginTop: theme.spacing(2),
},
footer: {
- marginTop: 'auto',
- backgroundColor: 'white',
+ marginTop: theme.spacing(1),
+ backgroundColor: 'lightgray',
},
});
@@ -51,10 +55,19 @@ class App extends React.Component {
super(props);
this.terminalHeight = 25;
+ const storeSchema = {
+ enabled: {
+ type: 'boolean',
+ default: false,
+ },
+ };
+ this.store = new Store({schema: storeSchema});
+
this.state = {
transactionCount: 0,
totalMined: 0,
totalSupply: 0,
+ enabled: this.store.get('enabled'),
};
this.connection = new Connection(url);
log.info('connection:', url);
@@ -63,7 +76,11 @@ class App extends React.Component {
this.addTerminalText(`Cluster entrypoint: ${url}...`);
this.replicator = new Replicator(this.connection, this);
- this.replicator.start();
+ if (this.state.enabled) {
+ this.replicator.start();
+ } else {
+ this.addTerminalText('Mining disabled');
+ }
}
componentDidMount() {
@@ -150,6 +167,17 @@ class App extends React.Component {
}
}
+ onEnabledSwitch = event => {
+ const enabled = event.target.checked;
+ this.store.set('enabled', enabled);
+ this.setState({enabled});
+ if (enabled) {
+ this.replicator.start();
+ } else {
+ this.replicator.stop();
+ }
+ };
+
render() {
const {classes} = this.props;
@@ -181,6 +209,19 @@ class App extends React.Component {
+
+
+ }
+ label="Enable"
+ />
+
+
Gigabytes of ledger to store:
@@ -229,7 +270,11 @@ class App extends React.Component {
-
+
{
const devModeExtra = isDevMode ? 200 : 0;
mainWindow = new BrowserWindow({
width: 1000 + devModeExtra,
- height: 750,
+ height: 800,
resizable: isDevMode,
});
diff --git a/src/replicator.js b/src/replicator.js
index 7de4dae..55d2047 100644
--- a/src/replicator.js
+++ b/src/replicator.js
@@ -11,6 +11,8 @@ export class Replicator {
this.connection = connection;
this.terminalOutput = terminalOutput;
this.running = false;
+ this.mainPromise = Promise.resolve();
+ this.cmdCancel = () => undefined;
const userDataPath = electron.remote.app.getPath('userData');
this.solanaInstallBinDir = path.join(
@@ -30,29 +32,31 @@ export class Replicator {
this.solanaInstallDataDir = path.join(userDataPath, 'install');
}
- start() {
+ async start() {
if (this.running) {
log.warn('Replicator already running, ignoring start()');
return;
}
+ await this.mainPromise;
this.running = true;
- this.main();
+ this.mainPromise = this.main();
}
- stop() {
+ async stop() {
if (!this.running) {
return;
}
- this.terminalOutput.addTerminalText('^C');
this.running = false;
- // TODO: signal main to exit...
+ this.terminalOutput.addTerminalText('^C');
+ this.cmdCancel();
+ await this.mainPromise;
}
- restart() {
+ async restart() {
if (!this.running) {
return;
}
- this.stop();
+ await this.stop();
this.start();
}
@@ -63,6 +67,7 @@ export class Replicator {
this.terminalOutput.addTerminalCommand(`${command} ${args.join(' ')}`);
const env = Object.assign({}, {RUST_LOG: 'solana=info'}, process.env);
const child = spawn(command, args, {env});
+ log.info(`pid ${child.pid}`);
child.stdout.on('data', data =>
this.terminalOutput.addTerminalText(data.toString()),
@@ -70,7 +75,16 @@ export class Replicator {
child.stderr.on('data', data =>
this.terminalOutput.addTerminalText(data.toString()),
);
- return child;
+ return Promise.race([
+ child,
+ new Promise((_, reject) => {
+ this.cmdCancel = () => {
+ log.info(`cmd cancelled, killing pid ${child.pid}`);
+ child.kill();
+ reject(new Error('User abort'));
+ };
+ }),
+ ]);
}
/**
@@ -142,5 +156,12 @@ export class Replicator {
} catch (err) {
this.terminalOutput.addTerminalError(err.message);
}
+
+ if (!this.running) {
+ log.info('main: not running anymore');
+ return Promise.resolve();
+ }
+ log.info('main: still running, restarting');
+ return this.main();
}
}
diff --git a/yarn.lock b/yarn.lock
index 76df6c3..2115afc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1331,6 +1331,18 @@ babel-core@^6.13.2, babel-core@^6.26.0:
slash "^1.0.0"
source-map "^0.5.7"
+babel-eslint@10.0.2:
+ version "10.0.2"
+ resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456"
+ integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/parser" "^7.0.0"
+ "@babel/traverse" "^7.0.0"
+ "@babel/types" "^7.0.0"
+ eslint-scope "3.7.1"
+ eslint-visitor-keys "^1.0.0"
+
babel-generator@^6.26.0:
version "6.26.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
@@ -4449,6 +4461,14 @@ eslint-restricted-globals@^0.1.1:
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=
+eslint-scope@3.7.1:
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+ integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
+ dependencies:
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
eslint-scope@^3.7.1:
version "3.7.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535"