Merge branch 'master' into jg-signer-api-queries-2
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parity.js",
|
||||
"version": "0.2.53",
|
||||
"version": "0.2.58",
|
||||
"main": "release/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
@@ -102,9 +102,10 @@
|
||||
"postcss-nested": "^1.0.0",
|
||||
"postcss-simple-vars": "^3.0.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react-addons-test-utils": "^15.3.0",
|
||||
"react-addons-test-utils": "~15.3.2",
|
||||
"react-copy-to-clipboard": "^4.2.3",
|
||||
"react-hot-loader": "^1.3.0",
|
||||
"react-dom": "~15.3.2",
|
||||
"react-hot-loader": "~1.3.0",
|
||||
"rucksack-css": "^0.8.6",
|
||||
"sinon": "^1.17.4",
|
||||
"sinon-as-promised": "^4.0.2",
|
||||
@@ -114,7 +115,7 @@
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-dev-server": "^1.15.2",
|
||||
"webpack-error-notification": "0.1.6",
|
||||
"webpack-hot-middleware": "^2.7.1",
|
||||
"webpack-hot-middleware": "~2.13.2",
|
||||
"websocket": "^1.0.23"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -134,7 +135,7 @@
|
||||
"js-sha3": "^0.5.2",
|
||||
"lodash": "^4.11.1",
|
||||
"marked": "^0.3.6",
|
||||
"material-ui": "^0.16.1",
|
||||
"material-ui": "0.16.1",
|
||||
"material-ui-chip-input": "^0.8.0",
|
||||
"mobx": "^2.6.1",
|
||||
"mobx-react": "^3.5.8",
|
||||
@@ -142,16 +143,16 @@
|
||||
"moment": "^2.14.1",
|
||||
"phoneformat.js": "^1.0.3",
|
||||
"qs": "^6.3.0",
|
||||
"react": "^15.2.1",
|
||||
"react": "~15.3.2",
|
||||
"react-ace": "^4.0.0",
|
||||
"react-addons-css-transition-group": "^15.2.1",
|
||||
"react-addons-css-transition-group": "~15.3.2",
|
||||
"react-chartjs-2": "^1.5.0",
|
||||
"react-dom": "^15.2.1",
|
||||
"react-dom": "~15.3.2",
|
||||
"react-dropzone": "^3.7.3",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-router": "^2.6.1",
|
||||
"react-router-redux": "^4.0.5",
|
||||
"react-tap-event-plugin": "^1.0.0",
|
||||
"react-tap-event-plugin": "~1.0.0",
|
||||
"react-tooltip": "^2.0.3",
|
||||
"recharts": "^0.15.2",
|
||||
"redux": "^3.5.2",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inData, inHex, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outHistogram, outNumber, outPeers } from '../../format/output';
|
||||
import { outAccountInfo, outAddress, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output';
|
||||
|
||||
export default class Parity {
|
||||
constructor (transport) {
|
||||
@@ -117,16 +117,29 @@ export default class Parity {
|
||||
.execute('parity_hashContent', url);
|
||||
}
|
||||
|
||||
importGethAccounts (accounts) {
|
||||
return this._transport
|
||||
.execute('parity_importGethAccounts', (accounts || []).map(inAddress))
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
listGethAccounts () {
|
||||
return this._transport
|
||||
.execute('parity_listGethAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
importGethAccounts (accounts) {
|
||||
localTransactions () {
|
||||
return this._transport
|
||||
.execute('parity_importGethAccounts', (accounts || []).map(inAddress))
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
.execute('parity_localTransactions')
|
||||
.then(transactions => {
|
||||
Object.values(transactions)
|
||||
.filter(tx => tx.transaction)
|
||||
.map(tx => {
|
||||
tx.transaction = outTransaction(tx.transaction);
|
||||
});
|
||||
return transactions;
|
||||
});
|
||||
}
|
||||
|
||||
minGasPrice () {
|
||||
@@ -192,6 +205,17 @@ export default class Parity {
|
||||
.execute('parity_nodeName');
|
||||
}
|
||||
|
||||
pendingTransactions () {
|
||||
return this._transport
|
||||
.execute('parity_pendingTransactions')
|
||||
.then(data => data.map(outTransaction));
|
||||
}
|
||||
|
||||
pendingTransactionsStats () {
|
||||
return this._transport
|
||||
.execute('parity_pendingTransactionsStats');
|
||||
}
|
||||
|
||||
phraseToAddress (phrase) {
|
||||
return this._transport
|
||||
.execute('parity_phraseToAddress', phrase)
|
||||
|
||||
@@ -105,7 +105,7 @@ export function attachInstances () {
|
||||
])
|
||||
.then(([registryAddress, netChain]) => {
|
||||
const registry = api.newContract(abis.registry, registryAddress).instance;
|
||||
isTest = netChain === 'morden' || netChain === 'testnet';
|
||||
isTest = ['morden', 'ropsten', 'testnet'].includes(netChain);
|
||||
|
||||
console.log(`contract was found at registry=${registryAddress}`);
|
||||
console.log(`running on ${netChain}, isTest=${isTest}`);
|
||||
|
||||
@@ -15,12 +15,17 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
.body {
|
||||
text-align: center;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
font-family: 'Roboto';
|
||||
vertical-align: middle;
|
||||
padding: 4em 0;
|
||||
text-align: center;
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
.form {
|
||||
@@ -98,7 +103,7 @@
|
||||
color: #333;
|
||||
background: #eee;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 0.5em;
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
@@ -113,20 +118,29 @@
|
||||
}
|
||||
|
||||
.hashError, .hashWarning, .hashOk {
|
||||
padding-top: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
text-align: center;
|
||||
padding: 1em 0;
|
||||
border: 0.25em solid #333;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.hashError {
|
||||
border-color: #f66;
|
||||
color: #f66;
|
||||
background: rgba(255, 102, 102, 0.25);
|
||||
}
|
||||
|
||||
.hashWarning {
|
||||
border-color: #f80;
|
||||
color: #f80;
|
||||
background: rgba(255, 236, 0, 0.25);
|
||||
}
|
||||
|
||||
.hashOk {
|
||||
opacity: 0.5;
|
||||
border-color: #6f6;
|
||||
color: #6f6;
|
||||
background: rgba(102, 255, 102, 0.25);
|
||||
}
|
||||
|
||||
.typeButtons {
|
||||
|
||||
@@ -19,6 +19,7 @@ import React, { Component } from 'react';
|
||||
import { api } from '../parity';
|
||||
import { attachInterface } from '../services';
|
||||
import Button from '../Button';
|
||||
import Events from '../Events';
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
import Loading from '../Loading';
|
||||
|
||||
@@ -27,6 +28,8 @@ import styles from './application.css';
|
||||
const INVALID_URL_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
let nextEventId = 0;
|
||||
|
||||
export default class Application extends Component {
|
||||
state = {
|
||||
fromAddress: null,
|
||||
@@ -43,7 +46,9 @@ export default class Application extends Component {
|
||||
registerState: '',
|
||||
registerType: 'file',
|
||||
repo: '',
|
||||
repoError: null
|
||||
repoError: null,
|
||||
events: {},
|
||||
eventIds: []
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -75,7 +80,7 @@ export default class Application extends Component {
|
||||
let hashClass = null;
|
||||
if (contentHashError) {
|
||||
hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning;
|
||||
} else {
|
||||
} else if (contentHash) {
|
||||
hashClass = styles.hashOk;
|
||||
}
|
||||
|
||||
@@ -116,29 +121,34 @@ export default class Application extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.form }>
|
||||
<div className={ styles.typeButtons }>
|
||||
<Button
|
||||
disabled={ registerBusy }
|
||||
invert={ registerType !== 'file' }
|
||||
onClick={ this.onClickTypeNormal }>File Link</Button>
|
||||
<Button
|
||||
disabled={ registerBusy }
|
||||
invert={ registerType !== 'content' }
|
||||
onClick={ this.onClickTypeContent }>Content Bundle</Button>
|
||||
</div>
|
||||
<div className={ styles.box }>
|
||||
<div className={ styles.description }>
|
||||
Provide a valid URL to register. The content information can be used in other contracts that allows for reverse lookups, e.g. image registries, dapp registries, etc.
|
||||
<div className={ styles.body }>
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.form }>
|
||||
<div className={ styles.typeButtons }>
|
||||
<Button
|
||||
disabled={ registerBusy }
|
||||
invert={ registerType !== 'file' }
|
||||
onClick={ this.onClickTypeNormal }>File Link</Button>
|
||||
<Button
|
||||
disabled={ registerBusy }
|
||||
invert={ registerType !== 'content' }
|
||||
onClick={ this.onClickTypeContent }>Content Bundle</Button>
|
||||
</div>
|
||||
{ valueInputs }
|
||||
<div className={ hashClass }>
|
||||
{ contentHashError || contentHash }
|
||||
<div className={ styles.box }>
|
||||
<div className={ styles.description }>
|
||||
Provide a valid URL to register. The content information can be used in other contracts that allows for reverse lookups, e.g. image registries, dapp registries, etc.
|
||||
</div>
|
||||
{ valueInputs }
|
||||
<div className={ hashClass }>
|
||||
{ contentHashError || contentHash }
|
||||
</div>
|
||||
{ registerBusy ? this.renderProgress() : this.renderButtons() }
|
||||
</div>
|
||||
{ registerBusy ? this.renderProgress() : this.renderButtons() }
|
||||
</div>
|
||||
</div>
|
||||
<Events
|
||||
eventIds={ this.state.eventIds }
|
||||
events={ this.state.events } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -285,15 +295,29 @@ export default class Application extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
trackRequest (promise) {
|
||||
trackRequest (eventId, promise) {
|
||||
return promise
|
||||
.then((signerRequestId) => {
|
||||
this.setState({ signerRequestId, registerState: 'Transaction posted, Waiting for transaction authorization' });
|
||||
this.setState({
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||
signerRequestId,
|
||||
registerState: 'Transaction posted, Waiting for transaction authorization'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
return api.pollMethod('parity_checkRequest', signerRequestId);
|
||||
})
|
||||
.then((txHash) => {
|
||||
this.setState({ txHash, registerState: 'Transaction authorized, Waiting for network confirmations' });
|
||||
this.setState({
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||
txHash,
|
||||
registerState: 'Transaction authorized, Waiting for network confirmations'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
return api.pollMethod('eth_getTransactionReceipt', txHash, (receipt) => {
|
||||
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
|
||||
@@ -304,27 +328,72 @@ export default class Application extends Component {
|
||||
});
|
||||
})
|
||||
.then((txReceipt) => {
|
||||
this.setState({ txReceipt, registerBusy: false, registerState: 'Network confirmed, Received transaction receipt', url: '', commit: '', repo: '', commitError: null, contentHash: '', contentHashOwner: null, contentHashError: null });
|
||||
this.setState({
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||
txReceipt,
|
||||
registerBusy: false,
|
||||
registerState: 'Network confirmed, Received transaction receipt'
|
||||
})
|
||||
})
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('onSend', error);
|
||||
this.setState({ registerError: error.message });
|
||||
|
||||
this.setState({
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||
registerState: error.message,
|
||||
registerError: true,
|
||||
registerBusy: false
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
registerContent (repo, commit) {
|
||||
registerContent (contentRepo, contentCommit) {
|
||||
const { contentHash, fromAddress, instance } = this.state;
|
||||
contentCommit = contentCommit.substr(0, 2) === '0x' ? contentCommit : `0x${contentCommit}`;
|
||||
|
||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
||||
|
||||
const values = [contentHash, repo, commit.substr(0, 2) === '0x' ? commit : `0x${commit}`];
|
||||
const eventId = nextEventId++;
|
||||
const values = [contentHash, contentRepo, contentCommit];
|
||||
const options = { from: fromAddress };
|
||||
|
||||
this.setState({
|
||||
eventIds: [eventId].concat(this.state.eventIds),
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: {
|
||||
contentHash,
|
||||
contentRepo,
|
||||
contentCommit,
|
||||
fromAddress,
|
||||
registerBusy: true,
|
||||
registerState: 'Estimating gas for the transaction',
|
||||
timestamp: new Date()
|
||||
}
|
||||
}),
|
||||
url: '',
|
||||
commit: '',
|
||||
repo: '',
|
||||
commitError: null,
|
||||
contentHash: '',
|
||||
contentHashOwner: null,
|
||||
contentHashError: null
|
||||
});
|
||||
|
||||
this.trackRequest(
|
||||
instance
|
||||
eventId, instance
|
||||
.hint.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
||||
this.setState({
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||
registerState: 'Gas estimated, Posting transaction to the network'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const gasPassed = gas.mul(1.2);
|
||||
options.gas = gasPassed.toFixed(0);
|
||||
@@ -335,19 +404,45 @@ export default class Application extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
registerUrl (url) {
|
||||
registerUrl (contentUrl) {
|
||||
const { contentHash, fromAddress, instance } = this.state;
|
||||
|
||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
||||
|
||||
const values = [contentHash, url];
|
||||
const eventId = nextEventId++;
|
||||
const values = [contentHash, contentUrl];
|
||||
const options = { from: fromAddress };
|
||||
|
||||
this.setState({
|
||||
eventIds: [eventId].concat(this.state.eventIds),
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: {
|
||||
contentHash,
|
||||
contentUrl,
|
||||
fromAddress,
|
||||
registerBusy: true,
|
||||
registerState: 'Estimating gas for the transaction',
|
||||
timestamp: new Date()
|
||||
}
|
||||
}),
|
||||
url: '',
|
||||
commit: '',
|
||||
repo: '',
|
||||
commitError: null,
|
||||
contentHash: '',
|
||||
contentHashOwner: null,
|
||||
contentHashError: null
|
||||
});
|
||||
|
||||
this.trackRequest(
|
||||
instance
|
||||
eventId, instance
|
||||
.hintURL.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
||||
this.setState({
|
||||
events: Object.assign({}, this.state.events, {
|
||||
[eventId]: Object.assign({}, this.state.events[eventId], {
|
||||
registerState: 'Gas estimated, Posting transaction to the network'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const gasPassed = gas.mul(1.2);
|
||||
options.gas = gasPassed.toFixed(0);
|
||||
|
||||
37
js/src/dapps/githubhint/Events/events.css
Normal file
37
js/src/dapps/githubhint/Events/events.css
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.list {
|
||||
border: none;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
|
||||
tr {
|
||||
&[data-busy="true"] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&[data-error="true"] {
|
||||
color: #f66;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
52
js/src/dapps/githubhint/Events/events.js
Normal file
52
js/src/dapps/githubhint/Events/events.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import moment from 'moment';
|
||||
|
||||
import styles from './events.css';
|
||||
|
||||
export default class Events extends Component {
|
||||
static propTypes = {
|
||||
eventIds: PropTypes.array.isRequired,
|
||||
events: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<table className={ styles.list }>
|
||||
<tbody>
|
||||
{ this.props.eventIds.map((id) => this.renderEvent(id, this.props.events[id])) }
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
renderEvent = (eventId, event) => {
|
||||
return (
|
||||
<tr key={ `event_${eventId}` } data-busy={ event.registerBusy } data-error={ event.registerError }>
|
||||
<td>
|
||||
<div>{ moment(event.timestamp).fromNow() }</div>
|
||||
<div>{ event.registerState }</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>{ event.contentUrl || `${event.contentRepo}/${event.contentCommit}` }</div>
|
||||
<div>{ event.contentHash }</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
17
js/src/dapps/githubhint/Events/index.js
Normal file
17
js/src/dapps/githubhint/Events/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './events';
|
||||
17
js/src/dapps/localtx.html
Normal file
17
js/src/dapps/localtx.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="/parity-logo-black-no-text.png" type="image/png">
|
||||
<title>Local transactions Viewer</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="vendor.js"></script>
|
||||
<script src="commons.js"></script>
|
||||
<script src="/parity-utils/parity.js"></script>
|
||||
<script src="localtx.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
33
js/src/dapps/localtx.js
Normal file
33
js/src/dapps/localtx.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
|
||||
import Application from './localtx/Application';
|
||||
|
||||
import '../../assets/fonts/Roboto/font.css';
|
||||
import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
import './localtx.html';
|
||||
|
||||
ReactDOM.render(
|
||||
<Application />,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
19
js/src/dapps/localtx/Application/application.css
Normal file
19
js/src/dapps/localtx/Application/application.css
Normal file
@@ -0,0 +1,19 @@
|
||||
.container {
|
||||
padding: 1rem 2rem;
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
table {
|
||||
text-align: left;
|
||||
margin: auto;
|
||||
max-width: 90vw;
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
203
js/src/dapps/localtx/Application/application.js
Normal file
203
js/src/dapps/localtx/Application/application.js
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
import styles from './application.css';
|
||||
|
||||
import { Transaction, LocalTransaction } from '../Transaction';
|
||||
|
||||
export default class Application extends Component {
|
||||
state = {
|
||||
loading: true,
|
||||
transactions: [],
|
||||
localTransactions: {},
|
||||
blockNumber: 0
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const poll = () => this.fetchTransactionData().then(poll).catch(poll);
|
||||
this._timeout = setTimeout(poll, 2000);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
|
||||
fetchTransactionData () {
|
||||
return Promise.all([
|
||||
api.parity.pendingTransactions(),
|
||||
api.parity.pendingTransactionsStats(),
|
||||
api.parity.localTransactions(),
|
||||
api.eth.blockNumber()
|
||||
]).then(([pending, stats, local, blockNumber]) => {
|
||||
// Combine results together
|
||||
const transactions = pending.map(tx => {
|
||||
return {
|
||||
transaction: tx,
|
||||
stats: stats[tx.hash],
|
||||
isLocal: !!local[tx.hash]
|
||||
};
|
||||
});
|
||||
|
||||
// Add transaction data to locals
|
||||
transactions
|
||||
.filter(tx => tx.isLocal)
|
||||
.map(data => {
|
||||
const tx = data.transaction;
|
||||
local[tx.hash].transaction = tx;
|
||||
local[tx.hash].stats = data.stats;
|
||||
});
|
||||
|
||||
// Convert local transactions to array
|
||||
const localTransactions = Object.keys(local).map(hash => {
|
||||
const data = local[hash];
|
||||
data.txHash = hash;
|
||||
return data;
|
||||
});
|
||||
|
||||
// Sort local transactions by nonce (move future to the end)
|
||||
localTransactions.sort((a, b) => {
|
||||
a = a.transaction || {};
|
||||
b = b.transaction || {};
|
||||
|
||||
if (a.from && b.from && a.from !== b.from) {
|
||||
return a.from < b.from;
|
||||
}
|
||||
|
||||
if (!a.nonce || !b.nonce) {
|
||||
return !a.nonce ? 1 : -1;
|
||||
}
|
||||
|
||||
return new BigNumber(a.nonce).comparedTo(new BigNumber(b.nonce));
|
||||
});
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
transactions,
|
||||
localTransactions,
|
||||
blockNumber
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { loading } = this.state;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className={ styles.container }>Loading...</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<h1>Your local transactions</h1>
|
||||
{ this.renderLocals() }
|
||||
<h1>Transactions in the queue</h1>
|
||||
{ this.renderQueueSummary() }
|
||||
{ this.renderQueue() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderQueueSummary () {
|
||||
const { transactions } = this.state;
|
||||
if (!transactions.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const count = transactions.length;
|
||||
const locals = transactions.filter(tx => tx.isLocal).length;
|
||||
const fee = transactions
|
||||
.map(tx => tx.transaction)
|
||||
.map(tx => tx.gasPrice.mul(tx.gas))
|
||||
.reduce((sum, fee) => sum.add(fee), new BigNumber(0));
|
||||
|
||||
return (
|
||||
<h3>
|
||||
Count: <strong>{ locals ? `${count} (${locals})` : count }</strong>
|
||||
|
||||
Total Fee: <strong>{ api.util.fromWei(fee).toFixed(3) } ETH</strong>
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
renderQueue () {
|
||||
const { blockNumber, transactions } = this.state;
|
||||
if (!transactions.length) {
|
||||
return (
|
||||
<h3>The queue seems is empty.</h3>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<table cellSpacing='0'>
|
||||
<thead>
|
||||
{ Transaction.renderHeader() }
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
transactions.map((tx, idx) => (
|
||||
<Transaction
|
||||
key={ tx.transaction.hash }
|
||||
idx={ idx + 1 }
|
||||
isLocal={ tx.isLocal }
|
||||
transaction={ tx.transaction }
|
||||
stats={ tx.stats }
|
||||
blockNumber={ blockNumber }
|
||||
/>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
renderLocals () {
|
||||
const { localTransactions } = this.state;
|
||||
if (!localTransactions.length) {
|
||||
return (
|
||||
<h3>You haven't sent any transactions yet.</h3>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<table cellSpacing='0'>
|
||||
<thead>
|
||||
{ LocalTransaction.renderHeader() }
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
localTransactions.map(tx => (
|
||||
<LocalTransaction
|
||||
key={ tx.txHash }
|
||||
hash={ tx.txHash }
|
||||
transaction={ tx.transaction }
|
||||
status={ tx.status }
|
||||
stats={ tx.stats }
|
||||
details={ tx }
|
||||
/>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
32
js/src/dapps/localtx/Application/application.spec.js
Normal file
32
js/src/dapps/localtx/Application/application.spec.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import '../../../environment/tests';
|
||||
|
||||
import Application from './application';
|
||||
|
||||
describe('localtx/Application', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders without crashing', () => {
|
||||
const rendered = shallow(<Application />);
|
||||
|
||||
expect(rendered).to.be.defined;
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/dapps/localtx/Application/index.js
Normal file
17
js/src/dapps/localtx/Application/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './application';
|
||||
17
js/src/dapps/localtx/Transaction/index.js
Normal file
17
js/src/dapps/localtx/Transaction/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export { Transaction, LocalTransaction } from './transaction';
|
||||
31
js/src/dapps/localtx/Transaction/transaction.css
Normal file
31
js/src/dapps/localtx/Transaction/transaction.css
Normal file
@@ -0,0 +1,31 @@
|
||||
.from {
|
||||
white-space: nowrap;
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.transaction {
|
||||
td {
|
||||
padding: 7px 15px;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
padding: 7px 0;
|
||||
}
|
||||
|
||||
&.local {
|
||||
background: #8bc34a;
|
||||
}
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.edit {
|
||||
label, input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
382
js/src/dapps/localtx/Transaction/transaction.js
Normal file
382
js/src/dapps/localtx/Transaction/transaction.js
Normal file
@@ -0,0 +1,382 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
import styles from './transaction.css';
|
||||
|
||||
import IdentityIcon from '../../githubhint/IdentityIcon';
|
||||
|
||||
class BaseTransaction extends Component {
|
||||
|
||||
shortHash (hash) {
|
||||
return `${hash.substr(0, 5)}..${hash.substr(hash.length - 3)}`;
|
||||
}
|
||||
|
||||
renderHash (hash) {
|
||||
return (
|
||||
<code title={ hash }>
|
||||
{ this.shortHash(hash) }
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
renderFrom (transaction) {
|
||||
if (!transaction) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return (
|
||||
<div title={ transaction.from } className={ styles.from }>
|
||||
<IdentityIcon
|
||||
address={ transaction.from }
|
||||
/>
|
||||
0x{ transaction.nonce.toString(16) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderGasPrice (transaction) {
|
||||
if (!transaction) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return (
|
||||
<span title={ `${transaction.gasPrice.toFormat(0)} wei` }>
|
||||
{ api.util.fromWei(transaction.gasPrice, 'shannon').toFormat(2) } shannon
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderGas (transaction) {
|
||||
if (!transaction) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return (
|
||||
<span title={ `${transaction.gas.toFormat(0)} Gas` }>
|
||||
{ transaction.gas.div(10 ** 6).toFormat(3) } MGas
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderPropagation (stats) {
|
||||
const noOfPeers = Object.keys(stats.propagatedTo).length;
|
||||
const noOfPropagations = Object.values(stats.propagatedTo).reduce((sum, val) => sum + val, 0);
|
||||
|
||||
return (
|
||||
<span className={ styles.nowrap }>
|
||||
{ noOfPropagations } ({ noOfPeers } peers)
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Transaction extends BaseTransaction {
|
||||
|
||||
static propTypes = {
|
||||
idx: PropTypes.number.isRequired,
|
||||
transaction: PropTypes.object.isRequired,
|
||||
blockNumber: PropTypes.object.isRequired,
|
||||
isLocal: PropTypes.bool,
|
||||
stats: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isLocal: false,
|
||||
stats: {
|
||||
firstSeen: 0,
|
||||
propagatedTo: {}
|
||||
}
|
||||
};
|
||||
|
||||
static renderHeader () {
|
||||
return (
|
||||
<tr className={ styles.header }>
|
||||
<th></th>
|
||||
<th>
|
||||
Transaction
|
||||
</th>
|
||||
<th>
|
||||
From
|
||||
</th>
|
||||
<th>
|
||||
Gas Price
|
||||
</th>
|
||||
<th>
|
||||
Gas
|
||||
</th>
|
||||
<th>
|
||||
First propagation
|
||||
</th>
|
||||
<th>
|
||||
# Propagated
|
||||
</th>
|
||||
<th>
|
||||
</th>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isLocal, stats, transaction, idx } = this.props;
|
||||
const blockNo = new BigNumber(stats.firstSeen);
|
||||
|
||||
const clazz = classnames(styles.transaction, {
|
||||
[styles.local]: isLocal
|
||||
});
|
||||
|
||||
return (
|
||||
<tr className={ clazz }>
|
||||
<td>
|
||||
{ idx }.
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderHash(transaction.hash) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderFrom(transaction) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderGasPrice(transaction) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderGas(transaction) }
|
||||
</td>
|
||||
<td title={ blockNo.toFormat(0) }>
|
||||
{ this.renderTime(stats.firstSeen) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderPropagation(stats) }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
renderTime (firstSeen) {
|
||||
const { blockNumber } = this.props;
|
||||
if (!firstSeen) {
|
||||
return 'never';
|
||||
}
|
||||
|
||||
const timeInMinutes = blockNumber.sub(firstSeen).mul(14).div(60).toFormat(1);
|
||||
return `${timeInMinutes} minutes ago`;
|
||||
}
|
||||
}
|
||||
|
||||
export class LocalTransaction extends BaseTransaction {
|
||||
|
||||
static propTypes = {
|
||||
hash: PropTypes.string.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
transaction: PropTypes.object,
|
||||
isLocal: PropTypes.bool,
|
||||
stats: PropTypes.object,
|
||||
details: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
stats: {
|
||||
propagatedTo: {}
|
||||
}
|
||||
};
|
||||
|
||||
static renderHeader () {
|
||||
return (
|
||||
<tr className={ styles.header }>
|
||||
<th></th>
|
||||
<th>
|
||||
Transaction
|
||||
</th>
|
||||
<th>
|
||||
From
|
||||
</th>
|
||||
<th>
|
||||
Gas Price / Gas
|
||||
</th>
|
||||
<th>
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
state = {
|
||||
isSending: false,
|
||||
isResubmitting: false,
|
||||
gasPrice: null,
|
||||
gas: null
|
||||
};
|
||||
|
||||
toggleResubmit = () => {
|
||||
const { transaction } = this.props;
|
||||
const { isResubmitting, gasPrice } = this.state;
|
||||
|
||||
this.setState({
|
||||
isResubmitting: !isResubmitting
|
||||
});
|
||||
|
||||
if (gasPrice === null) {
|
||||
this.setState({
|
||||
gasPrice: `0x${transaction.gasPrice.toString(16)}`,
|
||||
gas: `0x${transaction.gas.toString(16)}`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
setGasPrice = el => {
|
||||
this.setState({
|
||||
gasPrice: el.target.value
|
||||
});
|
||||
};
|
||||
|
||||
setGas = el => {
|
||||
this.setState({
|
||||
gas: el.target.value
|
||||
});
|
||||
};
|
||||
|
||||
sendTransaction = () => {
|
||||
const { transaction } = this.props;
|
||||
const { gasPrice, gas } = this.state;
|
||||
|
||||
const newTransaction = {
|
||||
from: transaction.from,
|
||||
to: transaction.to,
|
||||
nonce: transaction.nonce,
|
||||
value: transaction.value,
|
||||
data: transaction.data,
|
||||
gasPrice, gas
|
||||
};
|
||||
|
||||
this.setState({
|
||||
isResubmitting: false,
|
||||
isSending: true
|
||||
});
|
||||
|
||||
const closeSending = () => this.setState({
|
||||
isSending: false,
|
||||
gasPrice: null,
|
||||
gas: null
|
||||
});
|
||||
|
||||
api.eth.sendTransaction(newTransaction)
|
||||
.then(closeSending)
|
||||
.catch(closeSending);
|
||||
};
|
||||
|
||||
render () {
|
||||
if (this.state.isResubmitting) {
|
||||
return this.renderResubmit();
|
||||
}
|
||||
|
||||
const { stats, transaction, hash, status } = this.props;
|
||||
const { isSending } = this.state;
|
||||
|
||||
const resubmit = isSending ? (
|
||||
'sending...'
|
||||
) : (
|
||||
<a href='javascript:void' onClick={ this.toggleResubmit }>
|
||||
resubmit
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<tr className={ styles.transaction }>
|
||||
<td>
|
||||
{ !transaction ? null : resubmit }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderHash(hash) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderFrom(transaction) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderGasPrice(transaction) }
|
||||
<br />
|
||||
{ this.renderGas(transaction) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderStatus() }
|
||||
<br />
|
||||
{ status === 'pending' ? this.renderPropagation(stats) : null }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
renderStatus () {
|
||||
const { details } = this.props;
|
||||
|
||||
let state = {
|
||||
'pending': () => 'In queue: Pending',
|
||||
'future': () => 'In queue: Future',
|
||||
'mined': () => 'Mined',
|
||||
'dropped': () => 'Dropped because of queue limit',
|
||||
'invalid': () => 'Transaction is invalid',
|
||||
'rejected': () => `Rejected: ${details.error}`,
|
||||
'replaced': () => `Replaced by ${this.shortHash(details.hash)}`
|
||||
}[this.props.status];
|
||||
|
||||
return state ? state() : 'unknown';
|
||||
}
|
||||
|
||||
// TODO [ToDr] Gas Price / Gas selection is not needed
|
||||
// when signer supports gasPrice/gas tunning.
|
||||
renderResubmit () {
|
||||
const { transaction } = this.props;
|
||||
const { gasPrice, gas } = this.state;
|
||||
|
||||
return (
|
||||
<tr className={ styles.transaction }>
|
||||
<td>
|
||||
<a href='javascript:void' onClick={ this.toggleResubmit }>
|
||||
cancel
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderHash(transaction.hash) }
|
||||
</td>
|
||||
<td>
|
||||
{ this.renderFrom(transaction) }
|
||||
</td>
|
||||
<td className={ styles.edit }>
|
||||
<input
|
||||
type='text'
|
||||
value={ gasPrice }
|
||||
onChange={ this.setGasPrice }
|
||||
/>
|
||||
<input
|
||||
type='text'
|
||||
value={ gas }
|
||||
onChange={ this.setGas }
|
||||
/>
|
||||
</td>
|
||||
<td colSpan='2'>
|
||||
<a href='javascript:void' onClick={ this.sendTransaction }>
|
||||
Send
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
67
js/src/dapps/localtx/Transaction/transaction.spec.js
Normal file
67
js/src/dapps/localtx/Transaction/transaction.spec.js
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import '../../../environment/tests';
|
||||
import EthApi from '../../../api';
|
||||
|
||||
// Mock API for tests
|
||||
import * as Api from '../parity';
|
||||
Api.api = {
|
||||
util: EthApi.prototype.util
|
||||
};
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { Transaction, LocalTransaction } from './transaction';
|
||||
|
||||
describe('localtx/Transaction', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders without crashing', () => {
|
||||
const transaction = {
|
||||
hash: '0x1234567890',
|
||||
nonce: 15,
|
||||
gasPrice: new BigNumber(10),
|
||||
gas: new BigNumber(10)
|
||||
};
|
||||
const rendered = shallow(
|
||||
<Transaction
|
||||
isLocal={ false }
|
||||
transaction={ transaction }
|
||||
blockNumber={ new BigNumber(0) }
|
||||
/>
|
||||
);
|
||||
|
||||
expect(rendered).to.be.defined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('localtx/LocalTransaction', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders without crashing', () => {
|
||||
const rendered = shallow(
|
||||
<LocalTransaction
|
||||
hash={ '0x1234567890' }
|
||||
status={ 'pending' }
|
||||
/>
|
||||
);
|
||||
|
||||
expect(rendered).to.be.defined;
|
||||
});
|
||||
});
|
||||
});
|
||||
21
js/src/dapps/localtx/parity.js
Normal file
21
js/src/dapps/localtx/parity.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const api = window.parent.secureApi;
|
||||
|
||||
export {
|
||||
api
|
||||
};
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { sha3, toWei } from '../parity.js';
|
||||
import { sha3 } from '../parity.js';
|
||||
|
||||
const alreadyQueued = (queue, action, name) =>
|
||||
!!queue.find((entry) => entry.action === action && entry.name === name);
|
||||
@@ -29,6 +29,8 @@ export const reserve = (name) => (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const account = state.accounts.selected;
|
||||
const contract = state.contract;
|
||||
const fee = state.fee;
|
||||
|
||||
if (!contract || !account) return;
|
||||
if (alreadyQueued(state.names.queue, 'reserve', name)) return;
|
||||
const reserve = contract.functions.find((f) => f.name === 'reserve');
|
||||
@@ -36,7 +38,7 @@ export const reserve = (name) => (dispatch, getState) => {
|
||||
name = name.toLowerCase();
|
||||
const options = {
|
||||
from: account.address,
|
||||
value: toWei(1).toString()
|
||||
value: fee
|
||||
};
|
||||
const values = [ sha3(name) ];
|
||||
|
||||
|
||||
@@ -224,15 +224,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
listGethAccounts: {
|
||||
desc: 'Returns a list of the accounts available from Geth',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Array,
|
||||
desc: '20 Bytes addresses owned by the client.'
|
||||
}
|
||||
},
|
||||
|
||||
importGethAccounts: {
|
||||
desc: 'Imports a list of accounts from geth',
|
||||
params: [
|
||||
@@ -247,6 +238,24 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
listGethAccounts: {
|
||||
desc: 'Returns a list of the accounts available from Geth',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Array,
|
||||
desc: '20 Bytes addresses owned by the client.'
|
||||
}
|
||||
},
|
||||
|
||||
localTransactions: {
|
||||
desc: 'Returns an object of current and past local transactions.',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Object,
|
||||
desc: 'Mapping of `tx hash` into status object.'
|
||||
}
|
||||
},
|
||||
|
||||
minGasPrice: {
|
||||
desc: 'Returns currently set minimal gas price',
|
||||
params: [],
|
||||
@@ -379,6 +388,24 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
pendingTransactions: {
|
||||
desc: 'Returns a list of transactions currently in the queue.',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Array,
|
||||
desc: 'Transactions ordered by priority'
|
||||
}
|
||||
},
|
||||
|
||||
pendingTransactionsStats: {
|
||||
desc: 'Returns propagation stats for transactions in the queue',
|
||||
params: [],
|
||||
returns: {
|
||||
type: Object,
|
||||
desc: 'mapping of `tx hash` into `stats`'
|
||||
}
|
||||
},
|
||||
|
||||
phraseToAddress: {
|
||||
desc: 'Converts a secret phrase into the corresponting address',
|
||||
params: [
|
||||
|
||||
@@ -103,7 +103,7 @@ export default class DetailsStep extends Component {
|
||||
label='contract name'
|
||||
hint='a name for the deployed contract'
|
||||
error={ nameError }
|
||||
value={ name }
|
||||
value={ name || '' }
|
||||
onChange={ this.onNameChange } />
|
||||
|
||||
<Input
|
||||
@@ -169,6 +169,10 @@ export default class DetailsStep extends Component {
|
||||
const contractName = Object.keys(contracts)[index];
|
||||
const contract = contracts[contractName];
|
||||
|
||||
if (!this.props.name || this.props.name.trim() === '') {
|
||||
this.onNameChange(null, contractName);
|
||||
}
|
||||
|
||||
const { abi, bin } = contract;
|
||||
const code = /^0x/.test(bin) ? bin : `0x${bin}`;
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ export default class Status {
|
||||
.then(([
|
||||
clientVersion, defaultExtraData, netChain, netPort, rpcSettings, enode
|
||||
]) => {
|
||||
const isTest = netChain === 'morden' || netChain === 'testnet';
|
||||
const isTest = netChain === 'morden' || netChain === 'ropsten' || netChain === 'testnet';
|
||||
|
||||
const longStatus = {
|
||||
clientVersion,
|
||||
|
||||
@@ -31,7 +31,7 @@ const initialState = {
|
||||
gasLimit: new BigNumber(0),
|
||||
hashrate: new BigNumber(0),
|
||||
minGasPrice: new BigNumber(0),
|
||||
netChain: 'morden',
|
||||
netChain: 'ropsten',
|
||||
netPeers: {
|
||||
active: new BigNumber(0),
|
||||
connected: new BigNumber(0),
|
||||
|
||||
@@ -19,5 +19,5 @@
|
||||
}
|
||||
|
||||
.layout>div {
|
||||
padding-bottom: 0.25em;
|
||||
padding-bottom: 0.75em;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export function validateAbi (abi, api) {
|
||||
try {
|
||||
abiParsed = JSON.parse(abi);
|
||||
|
||||
if (!api.util.isArray(abiParsed) || !abiParsed.length) {
|
||||
if (!api.util.isArray(abiParsed)) {
|
||||
abiError = ERRORS.invalidAbi;
|
||||
return { abi, abiError, abiParsed };
|
||||
}
|
||||
|
||||
@@ -18,3 +18,22 @@
|
||||
.description {
|
||||
margin-top: .5em !important;
|
||||
}
|
||||
|
||||
.list {
|
||||
.background {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
margin: 0 -1.5em;
|
||||
padding: 0.5em 1.5em;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.byline {
|
||||
font-size: 0.75em;
|
||||
padding-top: 0.5em;
|
||||
line-height: 1.5em;
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,16 +51,37 @@ export default class AddDapps extends Component {
|
||||
] }
|
||||
visible
|
||||
scroll>
|
||||
<List>
|
||||
{ store.apps.map(this.renderApp) }
|
||||
</List>
|
||||
<div className={ styles.warning }>
|
||||
</div>
|
||||
{ this.renderList(store.sortedLocal, 'Applications locally available', 'All applications installed locally on the machine by the user for access by the Parity client.') }
|
||||
{ this.renderList(store.sortedBuiltin, 'Applications bundled with Parity', 'Experimental applications developed by the Parity team to show off dapp capabilities, integration, experimental features and to control certain network-wide client behaviour.') }
|
||||
{ this.renderList(store.sortedNetwork, 'Applications on the global network', 'These applications are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.') }
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderList (items, header, byline) {
|
||||
if (!items || !items.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.list }>
|
||||
<div className={ styles.background }>
|
||||
<div className={ styles.header }>{ header }</div>
|
||||
<div className={ styles.byline }>{ byline }</div>
|
||||
</div>
|
||||
<List>
|
||||
{ items.map(this.renderApp) }
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderApp = (app) => {
|
||||
const { store } = this.props;
|
||||
const isHidden = store.hidden.includes(app.id);
|
||||
const isHidden = !store.displayApps[app.id].visible;
|
||||
|
||||
const onCheck = () => {
|
||||
if (isHidden) {
|
||||
store.showApp(app.id);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { Container, ContainerTitle } from '../../../ui';
|
||||
import { Container, ContainerTitle, Tags } from '../../../ui';
|
||||
|
||||
import styles from './summary.css';
|
||||
|
||||
@@ -49,6 +49,7 @@ export default class Summary extends Component {
|
||||
return (
|
||||
<Container className={ styles.container }>
|
||||
{ image }
|
||||
<Tags tags={ [app.type] } />
|
||||
<div className={ styles.description }>
|
||||
<ContainerTitle
|
||||
className={ styles.title }
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"name": "Token Deployment",
|
||||
"description": "Deploy new basic tokens that you are able to send around",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "0xd1adaede68d344519025e2ff574650cd99d3830fe6d274c7a7843cdc00e17938",
|
||||
@@ -13,7 +14,8 @@
|
||||
"name": "Registry",
|
||||
"description": "A global registry of addresses on the network",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "0x0a8048117e51e964628d0f2d26342b3cd915248b59bcce2721e1d05f5cfa2208",
|
||||
@@ -21,7 +23,8 @@
|
||||
"name": "Token Registry",
|
||||
"description": "A registry of transactable tokens on the network",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46",
|
||||
@@ -29,7 +32,8 @@
|
||||
"name": "Method Registry",
|
||||
"description": "A registry of method signatures for lookups on transactions",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"id": "0x058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75",
|
||||
@@ -38,6 +42,16 @@
|
||||
"description": "A mapping of GitHub URLs to hashes for use in contracts as references",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0",
|
||||
"visible": false,
|
||||
"secure": true
|
||||
},
|
||||
{
|
||||
"id": "0xae74ad174b95cdbd01c88ac5b73a296d33e9088fc2a200e76bcedf3a94a7815d",
|
||||
"url": "localtx",
|
||||
"name": "TxQueue Viewer",
|
||||
"description": "Have a peak on internals of transaction queue of your node.",
|
||||
"author": "Parity Team <admin@ethcore.io>",
|
||||
"version": "1.0.0",
|
||||
"secure": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: -0.125em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.list+.list {
|
||||
@@ -29,3 +30,25 @@
|
||||
flex: 0 1 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
bottom: 0.5em;
|
||||
left: -0.125em;
|
||||
position: absolute;
|
||||
right: -0.125em;
|
||||
top: -0.25em;
|
||||
z-index: 100;
|
||||
padding: 1em;
|
||||
|
||||
.body {
|
||||
line-height: 1.5em;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
max-width: 980px;
|
||||
|
||||
&>div:first-child {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Checkbox } from 'material-ui';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import { Actionbar, Page } from '../../ui';
|
||||
@@ -37,6 +38,24 @@ export default class Dapps extends Component {
|
||||
store = new DappsStore(this.context.api);
|
||||
|
||||
render () {
|
||||
let externalOverlay = null;
|
||||
if (this.store.externalOverlayVisible) {
|
||||
externalOverlay = (
|
||||
<div className={ styles.overlay }>
|
||||
<div className={ styles.body }>
|
||||
<div>Applications made available on the network by 3rd-party authors are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each before interacting.</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
className={ styles.accept }
|
||||
label='I understand that these applications are not affiliated with Parity'
|
||||
checked={ false }
|
||||
onCheck={ this.onClickAcceptExternal } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AddDapps store={ this.store } />
|
||||
@@ -53,14 +72,27 @@ export default class Dapps extends Component {
|
||||
] }
|
||||
/>
|
||||
<Page>
|
||||
<div className={ styles.list }>
|
||||
{ this.store.visible.map(this.renderApp) }
|
||||
</div>
|
||||
{ this.renderList(this.store.visibleLocal) }
|
||||
{ this.renderList(this.store.visibleBuiltin) }
|
||||
{ this.renderList(this.store.visibleNetwork, externalOverlay) }
|
||||
</Page>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderList (items, overlay) {
|
||||
if (!items || !items.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.list }>
|
||||
{ overlay }
|
||||
{ items.map(this.renderApp) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderApp = (app) => {
|
||||
return (
|
||||
<div
|
||||
@@ -70,4 +102,8 @@ export default class Dapps extends Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onClickAcceptExternal = () => {
|
||||
this.store.closeExternalOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,39 +14,65 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { action, computed, observable, transaction } from 'mobx';
|
||||
import store from 'store';
|
||||
|
||||
import Contracts from '../../contracts';
|
||||
import { hashToImageUrl } from '../../redux/util';
|
||||
|
||||
import builtinApps from './builtin.json';
|
||||
|
||||
const LS_KEY_HIDDEN = 'hiddenApps';
|
||||
const LS_KEY_EXTERNAL = 'externalApps';
|
||||
const LS_KEY_DISPLAY = 'displayApps';
|
||||
const LS_KEY_EXTERNAL_ACCEPT = 'acceptExternal';
|
||||
|
||||
export default class DappsStore {
|
||||
@observable apps = [];
|
||||
@observable externalApps = [];
|
||||
@observable hiddenApps = [];
|
||||
@observable displayApps = {};
|
||||
@observable modalOpen = false;
|
||||
@observable externalOverlayVisible = true;
|
||||
|
||||
constructor (api) {
|
||||
this._api = api;
|
||||
|
||||
this._readHiddenApps();
|
||||
this._readExternalApps();
|
||||
this.loadExternalOverlay();
|
||||
this.readDisplayApps();
|
||||
|
||||
this._fetchBuiltinApps();
|
||||
this._fetchLocalApps();
|
||||
this._fetchRegistryApps();
|
||||
Promise
|
||||
.all([
|
||||
this._fetchBuiltinApps(),
|
||||
this._fetchLocalApps(),
|
||||
this._fetchRegistryApps()
|
||||
])
|
||||
.then(this.writeDisplayApps);
|
||||
}
|
||||
|
||||
@computed get visible () {
|
||||
return this.apps
|
||||
.filter((app) => {
|
||||
return this.externalApps.includes(app.id) || !this.hiddenApps.includes(app.id);
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
@computed get sortedBuiltin () {
|
||||
return this.apps.filter((app) => app.type === 'builtin');
|
||||
}
|
||||
|
||||
@computed get sortedLocal () {
|
||||
return this.apps.filter((app) => app.type === 'local');
|
||||
}
|
||||
|
||||
@computed get sortedNetwork () {
|
||||
return this.apps.filter((app) => app.type === 'network');
|
||||
}
|
||||
|
||||
@computed get visibleApps () {
|
||||
return this.apps.filter((app) => this.displayApps[app.id] && this.displayApps[app.id].visible);
|
||||
}
|
||||
|
||||
@computed get visibleBuiltin () {
|
||||
return this.visibleApps.filter((app) => app.type === 'builtin');
|
||||
}
|
||||
|
||||
@computed get visibleLocal () {
|
||||
return this.visibleApps.filter((app) => app.type === 'local');
|
||||
}
|
||||
|
||||
@computed get visibleNetwork () {
|
||||
return this.visibleApps.filter((app) => app.type === 'network');
|
||||
}
|
||||
|
||||
@action openModal = () => {
|
||||
@@ -57,14 +83,48 @@ export default class DappsStore {
|
||||
this.modalOpen = false;
|
||||
}
|
||||
|
||||
@action closeExternalOverlay = () => {
|
||||
this.externalOverlayVisible = false;
|
||||
store.set(LS_KEY_EXTERNAL_ACCEPT, true);
|
||||
}
|
||||
|
||||
@action loadExternalOverlay () {
|
||||
this.externalOverlayVisible = !(store.get(LS_KEY_EXTERNAL_ACCEPT) || false);
|
||||
}
|
||||
|
||||
@action hideApp = (id) => {
|
||||
this.hiddenApps = this.hiddenApps.concat(id);
|
||||
this._writeHiddenApps();
|
||||
this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: false } });
|
||||
this.writeDisplayApps();
|
||||
}
|
||||
|
||||
@action showApp = (id) => {
|
||||
this.hiddenApps = this.hiddenApps.filter((_id) => _id !== id);
|
||||
this._writeHiddenApps();
|
||||
this.displayApps = Object.assign({}, this.displayApps, { [id]: { visible: true } });
|
||||
this.writeDisplayApps();
|
||||
}
|
||||
|
||||
@action readDisplayApps = () => {
|
||||
this.displayApps = store.get(LS_KEY_DISPLAY) || {};
|
||||
}
|
||||
|
||||
@action writeDisplayApps = () => {
|
||||
store.set(LS_KEY_DISPLAY, this.displayApps);
|
||||
}
|
||||
|
||||
@action addApps = (apps) => {
|
||||
transaction(() => {
|
||||
this.apps = this.apps
|
||||
.concat(apps || [])
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const visibility = {};
|
||||
apps.forEach((app) => {
|
||||
if (!this.displayApps[app.id]) {
|
||||
visibility[app.id] = { visible: app.visible };
|
||||
}
|
||||
});
|
||||
|
||||
this.displayApps = Object.assign({}, this.displayApps, visibility);
|
||||
});
|
||||
}
|
||||
|
||||
_getHost (api) {
|
||||
@@ -79,13 +139,16 @@ export default class DappsStore {
|
||||
return Promise
|
||||
.all(builtinApps.map((app) => dappReg.getImage(app.id)))
|
||||
.then((imageIds) => {
|
||||
transaction(() => {
|
||||
builtinApps.forEach((app, index) => {
|
||||
this.addApps(
|
||||
builtinApps.map((app, index) => {
|
||||
app.type = 'builtin';
|
||||
app.image = hashToImageUrl(imageIds[index]);
|
||||
this.apps.push(app);
|
||||
});
|
||||
});
|
||||
return app;
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('DappsStore:fetchBuiltinApps', error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,15 +163,12 @@ export default class DappsStore {
|
||||
return apps
|
||||
.map((app) => {
|
||||
app.type = 'local';
|
||||
app.visible = true;
|
||||
return app;
|
||||
})
|
||||
.filter((app) => app.id && !['ui'].includes(app.id));
|
||||
})
|
||||
.then((apps) => {
|
||||
transaction(() => {
|
||||
(apps || []).forEach((app) => this.apps.push(app));
|
||||
});
|
||||
})
|
||||
.then(this.addApps)
|
||||
.catch((error) => {
|
||||
console.warn('DappsStore:fetchLocal', error);
|
||||
});
|
||||
@@ -132,7 +192,9 @@ export default class DappsStore {
|
||||
.then((appsInfo) => {
|
||||
const appIds = appsInfo
|
||||
.map(([appId, owner]) => this._api.util.bytesToHex(appId))
|
||||
.filter((appId) => !builtinApps.find((app) => app.id === appId));
|
||||
.filter((appId) => {
|
||||
return (new BigNumber(appId)).gt(0) && !builtinApps.find((app) => app.id === appId);
|
||||
});
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
@@ -147,7 +209,8 @@ export default class DappsStore {
|
||||
image: hashToImageUrl(imageIds[index]),
|
||||
contentHash: this._api.util.bytesToHex(contentIds[index]).substr(2),
|
||||
manifestHash: this._api.util.bytesToHex(manifestIds[index]).substr(2),
|
||||
type: 'network'
|
||||
type: 'network',
|
||||
visible: true
|
||||
};
|
||||
|
||||
return app;
|
||||
@@ -179,11 +242,7 @@ export default class DappsStore {
|
||||
});
|
||||
});
|
||||
})
|
||||
.then((apps) => {
|
||||
transaction(() => {
|
||||
(apps || []).forEach((app) => this.apps.push(app));
|
||||
});
|
||||
})
|
||||
.then(this.addApps)
|
||||
.catch((error) => {
|
||||
console.warn('DappsStore:fetchRegistry', error);
|
||||
});
|
||||
@@ -201,44 +260,4 @@ export default class DappsStore {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
_readHiddenApps () {
|
||||
const stored = localStorage.getItem(LS_KEY_HIDDEN);
|
||||
|
||||
if (stored) {
|
||||
try {
|
||||
this.hiddenApps = JSON.parse(stored);
|
||||
} catch (error) {
|
||||
console.warn('DappsStore:readHiddenApps', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readExternalApps () {
|
||||
const stored = localStorage.getItem(LS_KEY_EXTERNAL);
|
||||
|
||||
if (stored) {
|
||||
try {
|
||||
this.externalApps = JSON.parse(stored);
|
||||
} catch (error) {
|
||||
console.warn('DappsStore:readExternalApps', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_writeExternalApps () {
|
||||
try {
|
||||
localStorage.setItem(LS_KEY_EXTERNAL, JSON.stringify(this.externalApps));
|
||||
} catch (error) {
|
||||
console.error('DappsStore:writeExternalApps', error);
|
||||
}
|
||||
}
|
||||
|
||||
_writeHiddenApps () {
|
||||
try {
|
||||
localStorage.setItem(LS_KEY_HIDDEN, JSON.stringify(this.hiddenApps));
|
||||
} catch (error) {
|
||||
console.error('DappsStore:writeHiddenApps', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ module.exports = {
|
||||
'githubhint': ['./dapps/githubhint.js'],
|
||||
'registry': ['./dapps/registry.js'],
|
||||
'signaturereg': ['./dapps/signaturereg.js'],
|
||||
'localtx': ['./dapps/localtx.js'],
|
||||
'tokenreg': ['./dapps/tokenreg.js'],
|
||||
// app
|
||||
'index': ['./index.js']
|
||||
|
||||
@@ -26,7 +26,6 @@ const DEST = process.env.BUILD_DEST || '.build';
|
||||
|
||||
module.exports = {
|
||||
context: path.join(__dirname, './src'),
|
||||
target: 'node',
|
||||
entry: {
|
||||
// library
|
||||
'inject': ['./web3.js'],
|
||||
@@ -39,14 +38,7 @@ module.exports = {
|
||||
library: '[name].js',
|
||||
libraryTarget: 'umd'
|
||||
},
|
||||
externals: {
|
||||
'node-fetch': 'node-fetch',
|
||||
'vertx': 'vertx'
|
||||
},
|
||||
module: {
|
||||
noParse: [
|
||||
/babel-polyfill/
|
||||
],
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
|
||||
Reference in New Issue
Block a user