Cleanup the Status View (#5317)

* Better view of Settings and Mining Settings

* Cleanup Status view

* Node Logs refactoring

* Cleanup Status

* Move RPC Calls files

* Basic Peers view

* Add Peers table

* style table header
This commit is contained in:
Nicolas Gotchac
2017-03-29 14:38:07 +02:00
committed by Gav Wood
parent 8930f510fc
commit 5fa088114c
101 changed files with 771 additions and 869 deletions

View File

@@ -43,6 +43,7 @@
.log {
font-family: monospace;
font-size: 0.9em;
white-space: pre-line;
word-wrap: break-word;
color: #aaa;

View File

@@ -14,30 +14,30 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Container } from '~/ui';
import { PauseIcon, PlayIcon, ReorderIcon, ReplayIcon } from '~/ui/Icons';
import DebugStore from './store';
import styles from './debug.css';
@observer
export default class Debug extends Component {
static propTypes = {
actions: PropTypes.shape({
clearStatusLogs: PropTypes.func.isRequired,
toggleStatusLogs: PropTypes.func.isRequired
}).isRequired,
nodeStatus: PropTypes.object.isRequired
}
static contextTypes = {
api: PropTypes.object.isRequired
};
state = {
reversed: true
debugStore = new DebugStore(this.context.api);
componentWillUnmount () {
this.debugStore.stopPolling();
}
render () {
const { nodeStatus } = this.props;
const { devLogsLevels } = nodeStatus;
const { logsLevels } = this.debugStore;
return (
<Container
@@ -50,7 +50,7 @@ export default class Debug extends Component {
>
{ this.renderActions() }
<h2 className={ styles.subheader }>
{ devLogsLevels || '-' }
{ logsLevels || '-' }
</h2>
{ this.renderToggle() }
{ this.renderLogs() }
@@ -59,9 +59,9 @@ export default class Debug extends Component {
}
renderToggle () {
const { devLogsEnabled } = this.props.nodeStatus;
const { logsEnabled } = this.debugStore;
if (devLogsEnabled) {
if (logsEnabled) {
return null;
}
@@ -76,36 +76,18 @@ export default class Debug extends Component {
}
renderLogs () {
const { nodeStatus } = this.props;
const { reversed } = this.state;
const { devLogs } = nodeStatus;
const { logs } = this.debugStore;
const dateRegex = /^(\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2})(.*)$/i;
if (!devLogs) {
if (logs.length === 0) {
return null;
}
const logs = reversed
? [].concat(devLogs).reverse()
: [].concat(devLogs);
const text = logs
.map((log, index) => {
const logDate = dateRegex.exec(log);
if (!logDate) {
return (
<p key={ index } className={ styles.log }>
{ log }
</p>
);
}
return (
<p key={ index } className={ styles.log }>
<span className={ styles.logDate }>{ logDate[1] }</span>
<span className={ styles.logText }>{ logDate[2] }</span>
<span className={ styles.logDate }>[{ log.date.toLocaleString() }]</span>
<span className={ styles.logText }>{ log.log }</span>
</p>
);
});
@@ -118,8 +100,8 @@ export default class Debug extends Component {
}
renderActions () {
const { devLogsEnabled } = this.props.nodeStatus;
const toggleButton = devLogsEnabled
const { logsEnabled } = this.debugStore;
const toggleButton = logsEnabled
? <PauseIcon />
: <PlayIcon />;
@@ -143,21 +125,14 @@ export default class Debug extends Component {
}
clear = () => {
const { clearStatusLogs } = this.props.actions;
clearStatusLogs();
}
this.debugStore.clearLogs();
};
toggle = () => {
const { devLogsEnabled } = this.props.nodeStatus;
const { toggleStatusLogs } = this.props.actions;
toggleStatusLogs(!devLogsEnabled);
}
this.debugStore.toggle();
};
reverse = () => {
const { reversed } = this.state;
this.setState({ reversed: !reversed });
}
this.debugStore.reverse();
};
}

View File

@@ -0,0 +1,128 @@
// Copyright 2015-2017 Parity Technologies (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 { action, observable, transaction } from 'mobx';
const LOG_DATE_REGEX = /^(\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2})(.*)$/i;
const MAX_LOGS = 25;
export default class DebugStore {
@observable logs = [];
@observable logsLevels = null;
@observable logsEnabled = false;
@observable reversed = false;
api = null;
_lastLogAdded = null;
_timeoutId = null;
constructor (api) {
this.api = api;
}
@action clearLogs () {
this.logs = [];
}
@action setLogs (logs, logsLevels) {
let newLogs = [];
if (this._lastLogAdded) {
const sliceIndex = logs.findIndex((log) => log === this._lastLogAdded);
newLogs = logs.slice(0, sliceIndex);
} else {
newLogs = logs.slice();
}
this._lastLogAdded = logs[0];
const parsedLogs = newLogs
.map((log) => {
const logDate = LOG_DATE_REGEX.exec(log);
if (!logDate) {
return null;
}
return {
date: new Date(logDate[1]),
log: logDate[2]
};
})
.filter((log) => log);
transaction(() => {
if (!this.reversed) {
this.logs = [].concat(parsedLogs, this.logs.slice()).slice(0, MAX_LOGS);
} else {
parsedLogs.reverse();
this.logs = [].concat(this.logs.slice(), parsedLogs).slice(-1 * MAX_LOGS);
}
this.logsLevels = logsLevels;
});
}
@action toggle () {
this.logsEnabled = !this.logsEnabled;
if (this.logsEnabled) {
this.initPolling();
} else {
this.stopPolling();
}
}
@action reverse () {
transaction(() => {
this.reversed = !this.reversed;
this.logs = this.logs.reverse();
});
}
initPolling () {
this._pollLogs();
}
stopPolling () {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
}
_pollLogs = () => {
const nextTimeout = (timeout = 1000) => {
this.stopPolling();
this._timeoutId = setTimeout(this._pollLogs, timeout);
};
return Promise
.all([
this.api.parity.devLogs(),
this.api.parity.devLogsLevels()
])
.then(([ devLogs, devLogsLevels ]) => {
this.setLogs(devLogs, devLogsLevels);
})
.catch((error) => {
console.error('_pollLogs', error);
})
.then(() => {
return nextTimeout();
});
}
}

View File

@@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import formatNumber from 'format-number';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import formatNumber from 'format-number';
import { ContainerTitle, Input } from '~/ui';
import { ContainerTitle, Input, TypedInput } from '~/ui';
import { numberFromString } from './numberFromString';
import { decodeExtraData } from './decodeExtraData';
@@ -28,21 +28,23 @@ const toNiceNumber = formatNumber();
export default class MiningSettings extends Component {
static contextTypes = {
api: PropTypes.object
}
};
static propTypes = {
nodeStatus: PropTypes.object
}
coinbase: PropTypes.string,
defaultExtraData: PropTypes.string,
extraData: PropTypes.string,
gasFloorTarget: PropTypes.object,
minGasPrice: PropTypes.object,
onUpdateSetting: PropTypes.func.isRequired
};
render () {
const { nodeStatus } = this.props;
const { coinbase, defaultExtraData, extraData, gasFloorTarget, minGasPrice } = nodeStatus;
const extradata = extraData
const { coinbase, defaultExtraData, extraData, gasFloorTarget, minGasPrice } = this.props;
const decodedExtraData = extraData
? decodeExtraData(extraData)
: '';
const defaultExtradata = defaultExtraData
const decodedDefaultExtraData = defaultExtraData
? decodeExtraData(defaultExtraData)
: '';
@@ -56,7 +58,7 @@ export default class MiningSettings extends Component {
/>
}
/>
<Input
<TypedInput
label={
<FormattedMessage
id='status.miningSettings.input.author.label'
@@ -69,14 +71,15 @@ export default class MiningSettings extends Component {
defaultMessage='the mining author'
/>
}
param='address'
value={ coinbase }
onSubmit={ this.onAuthorChange }
onChange={ this.onAuthorChange }
allowCopy
floatCopy
{ ...this._test('author') }
/>
<Input
defaultValue={ decodedDefaultExtraData }
escape='default'
label={
<FormattedMessage
id='status.miningSettings.input.extradata.label'
@@ -89,12 +92,9 @@ export default class MiningSettings extends Component {
defaultMessage='extra data for mined blocks'
/>
}
value={ extradata }
value={ decodedExtraData }
onSubmit={ this.onExtraDataChange }
defaultValue={ defaultExtradata }
allowCopy
floatCopy
{ ...this._test('extra-data') }
/>
<Input
@@ -113,8 +113,6 @@ export default class MiningSettings extends Component {
value={ toNiceNumber(minGasPrice) }
onSubmit={ this.onMinGasPriceChange }
allowCopy={ minGasPrice.toString() }
floatCopy
{ ...this._test('min-gas-price') }
/>
<Input
@@ -133,8 +131,6 @@ export default class MiningSettings extends Component {
value={ toNiceNumber(gasFloorTarget) }
onSubmit={ this.onGasFloorTargetChange }
allowCopy={ gasFloorTarget.toString() }
floatCopy
{ ...this._test('gas-floor-target') }
/>
</div>
);
@@ -143,29 +139,36 @@ export default class MiningSettings extends Component {
onMinGasPriceChange = (newVal) => {
const { api } = this.context;
api.parity.setMinGasPrice(numberFromString(newVal));
api.parity
.setMinGasPrice(numberFromString(newVal))
.then(() => this.updateMiningSettings());
};
onExtraDataChange = (newVal, isResetToDefault) => {
onExtraDataChange = (value) => {
const { api } = this.context;
const { nodeStatus } = this.props;
// In case of resetting to default we are just using raw bytes from defaultExtraData
// When user sets new value we can safely send a string that will be converted to hex by formatter.
const val = isResetToDefault ? nodeStatus.defaultExtraData : newVal;
api.parity.setExtraData(val);
api.parity
.setExtraData(value)
.then(() => this.updateMiningSettings());
};
onAuthorChange = (newVal) => {
const { api } = this.context;
api.parity.setAuthor(newVal);
api.parity
.setAuthor(newVal)
.then(() => this.updateMiningSettings());
};
onGasFloorTargetChange = (newVal) => {
const { api } = this.context;
api.parity.setGasFloorTarget(numberFromString(newVal));
api.parity
.setGasFloorTarget(numberFromString(newVal))
.then(() => this.updateMiningSettings());
};
updateMiningSettings () {
this.props.onUpdateSetting();
}
}

View File

@@ -14,4 +14,4 @@
// 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 './Calls';
export default from './peers';

View File

@@ -14,31 +14,34 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.historyInfo {
margin-top: 0;
text-transform: initial;
}
.header {
margin: 0;
}
.removeIcon {
float: right;
color: #bbb;
cursor: pointer;
transition: opacity ease-in-out 0.2s;
transition-delay: 0.2s;
opacity: 0;
}
div:hover > .removeIcon {
opacity: 1.0;
}
.history {
margin-top: calc(1em + 18.4px);
padding-top: 0;
.peers {
margin-top: 0.5em;
overflow: auto;
max-height: 60vh;
width: 100%;
table {
border-collapse: collapse;
width: 100%;
}
th {
padding: 0.5em;
text-align: left;
white-space: nowrap;
}
}
.peer {
&:nth-child(odd) {
background-color: rgba(200, 200, 200, 0.1);
}
td {
border-top: 1px solid #333;
font-size: 0.9em;
overflow: hidden;
padding: 0.5em 0.25em;
white-space: nowrap;
}
}

View File

@@ -0,0 +1,163 @@
// Copyright 2015-2017 Parity Technologies (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 { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Container, ContainerTitle, ScrollableText, ShortenedHash } from '~/ui';
import styles from './peers.css';
class Peers extends Component {
static propTypes = {
peers: PropTypes.array.isRequired
};
render () {
const { peers } = this.props;
return (
<Container>
<ContainerTitle
title={
<FormattedMessage
id='status.peers.title'
defaultMessage='network peers'
/>
}
/>
<div className={ styles.peers }>
<table>
<thead>
<tr>
<th />
<th>
<FormattedMessage
id='status.peers.table.header.id'
defaultMessage='ID'
/>
</th>
<th>
<FormattedMessage
id='status.peers.table.header.remoteAddress'
defaultMessage='Remote Address'
/>
</th>
<th>
<FormattedMessage
id='status.peers.table.header.name'
defaultMessage='Name'
/>
</th>
<th>
<FormattedMessage
id='status.peers.table.header.ethHeader'
defaultMessage='Header (ETH)'
/>
</th>
<th>
<FormattedMessage
id='status.peers.table.header.ethDiff'
defaultMessage='Difficulty (ETH)'
/>
</th>
<th>
<FormattedMessage
id='status.peers.table.header.caps'
defaultMessage='Capabilities'
/>
</th>
</tr>
</thead>
<tbody>
{ this.renderPeers(peers) }
</tbody>
</table>
</div>
</Container>
);
}
renderPeers (peers) {
return peers.map((peer, index) => this.renderPeer(peer, index));
}
renderPeer (peer, index) {
const { caps, id, name, network, protocols } = peer;
return (
<tr
className={ styles.peer }
key={ id }
>
<td>
{ index + 1 }
</td>
<td>
<ScrollableText small text={ id } />
</td>
<td>
{ network.remoteAddress }
</td>
<td>
{ name }
</td>
<td>
{
protocols.eth
? <ShortenedHash data={ protocols.eth.head } />
: null
}
</td>
<td>
{
protocols.eth && protocols.eth.difficulty.gt(0)
? protocols.eth.difficulty.toExponential(16)
: null
}
</td>
<td>
{
caps && caps.length > 0
? caps.join(' - ')
: null
}
</td>
</tr>
);
}
}
function mapStateToProps (state) {
const handshakeRegex = /handshake/i;
const { netPeers } = state.nodeStatus;
const { peers = [] } = netPeers;
const realPeers = peers
.filter((peer) => peer.id)
.filter((peer) => !handshakeRegex.test(peer.network.remoteAddress))
.filter((peer) => peer.protocols.eth && peer.protocols.eth.head)
.sort((peerA, peerB) => {
const idComp = peerA.id.localeCompare(peerB.id);
return idComp;
});
return { peers: realPeers };
}
export default connect(mapStateToProps)(Peers);

View File

@@ -18,28 +18,46 @@ import bytes from 'bytes';
import moment from 'moment';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Container, ContainerTitle, Input } from '~/ui';
import MiningSettings from '../MiningSettings';
import StatusStore from './store';
import styles from './status.css';
export default class Status extends Component {
class Status extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
};
static propTypes = {
nodeStatus: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
blockNumber: PropTypes.object,
blockTimestamp: PropTypes.object,
netChain: PropTypes.string,
netPeers: PropTypes.object
};
statusStore = new StatusStore(this.context.api);
componentWillMount () {
this.statusStore.startPolling();
}
componentWillUnmount () {
this.statusStore.stopPolling();
}
render () {
const { nodeStatus } = this.props;
const { netPeers } = nodeStatus;
const { blockNumber, blockTimestamp, netPeers } = this.props;
const { hashrate } = this.statusStore;
if (!netPeers || !nodeStatus.blockNumber) {
if (!netPeers || !blockNumber) {
return null;
}
const hashrate = bytes(nodeStatus.hashrate.toNumber()) || 0;
const hashrateValue = bytes(hashrate.toNumber()) || 0;
const peers = `${netPeers.active}/${netPeers.connected}/${netPeers.max}`;
return (
@@ -56,11 +74,11 @@ export default class Status extends Component {
/>
}
/>
<div { ...this._test('best-block') } className={ styles.blockInfo }>
#{ nodeStatus.blockNumber.toFormat() }
<div className={ styles.blockInfo }>
#{ blockNumber.toFormat() }
</div>
<div className={ styles.blockByline }>
{ moment(nodeStatus.blockTimestamp).calendar() }
{ moment(blockTimestamp).calendar() }
</div>
</div>
<div className={ `${styles.col12} ${styles.padBottom}` }>
@@ -72,7 +90,7 @@ export default class Status extends Component {
/>
}
/>
<div { ...this._test('peers') } className={ styles.blockInfo }>
<div className={ styles.blockInfo }>
{ peers }
</div>
</div>
@@ -85,23 +103,19 @@ export default class Status extends Component {
/>
}
/>
<div { ...this._test('hashrate') } className={ styles.blockInfo }>
<div className={ styles.blockInfo }>
<FormattedMessage
id='status.status.hashrate'
defaultMessage='{hashrate} H/s'
values={ {
hashrate
hashrate: hashrateValue
} }
/>
</div>
</div>
</div>
<div className={ styles.col4_5 }>
<MiningSettings
{ ...this._test('mining') }
nodeStatus={ nodeStatus }
actions={ this.props.actions }
/>
{ this.renderMiningSettings() }
</div>
<div className={ styles.col4_5 }>
{ this.renderSettings() }
@@ -112,12 +126,27 @@ export default class Status extends Component {
);
}
renderMiningSettings () {
const { coinbase, defaultExtraData, extraData, gasFloorTarget, minGasPrice } = this.statusStore;
return (
<MiningSettings
coinbase={ coinbase }
defaultExtraData={ defaultExtraData }
extraData={ extraData }
gasFloorTarget={ gasFloorTarget }
minGasPrice={ minGasPrice }
onUpdateSetting={ this.statusStore.handleUpdateSetting }
/>
);
}
renderNodeName () {
const { nodeStatus } = this.props;
const { nodeName } = this.statusStore;
return (
<span>
{ nodeStatus.nodeName || (
{ nodeName || (
<FormattedMessage
id='status.status.title.node'
defaultMessage='Node'
@@ -128,9 +157,8 @@ export default class Status extends Component {
}
renderSettings () {
const { nodeStatus } = this.props;
const { rpcSettings, netPeers, netPort = '' } = nodeStatus;
const peers = `${netPeers.active}/${netPeers.connected}/${netPeers.max}`;
const { netChain } = this.props;
const { enode, rpcSettings, netPort = '' } = this.statusStore;
if (!rpcSettings) {
return null;
@@ -139,7 +167,7 @@ export default class Status extends Component {
const rpcPort = rpcSettings.port || '';
return (
<div { ...this._test('settings') }>
<div>
<ContainerTitle
title={
<FormattedMessage
@@ -157,8 +185,7 @@ export default class Status extends Component {
defaultMessage='chain'
/>
}
value={ nodeStatus.netChain }
{ ...this._test('chain') }
value={ netChain }
/>
<div className={ styles.row }>
<div className={ styles.col6 }>
@@ -167,12 +194,25 @@ export default class Status extends Component {
readOnly
label={
<FormattedMessage
id='status.status.input.peers'
defaultMessage='peers'
id='status.status.input.rpcEnabled'
defaultMessage='rpc enabled'
/>
}
value={ peers }
{ ...this._test('peers') }
value={
rpcSettings.enabled
? (
<FormattedMessage
id='status.status.input.yes'
defaultMessage='yes'
/>
)
: (
<FormattedMessage
id='status.status.input.no'
defaultMessage='no'
/>
)
}
/>
</div>
<div className={ styles.col6 }>
@@ -186,37 +226,10 @@ export default class Status extends Component {
/>
}
value={ netPort.toString() }
{ ...this._test('network-port') }
/>
</div>
</div>
<Input
allowCopy
readOnly
label={
<FormattedMessage
id='status.status.input.rpcEnabled'
defaultMessage='rpc enabled'
/>
}
value={
rpcSettings.enabled
? (
<FormattedMessage
id='status.status.input.yes'
defaultMessage='yes'
/>
)
: (
<FormattedMessage
id='status.status.input.no'
defaultMessage='no'
/>
)
}
{ ...this._test('rpc-enabled') }
/>
<div className={ styles.row }>
<div className={ styles.col6 }>
<Input
@@ -229,7 +242,6 @@ export default class Status extends Component {
/>
}
value={ rpcSettings.interface }
{ ...this._test('rpc-interface') }
/>
</div>
<div className={ styles.col6 }>
@@ -243,7 +255,6 @@ export default class Status extends Component {
/>
}
value={ rpcPort.toString() }
{ ...this._test('rpc-port') }
/>
</div>
</div>
@@ -259,8 +270,7 @@ export default class Status extends Component {
defaultMessage='enode'
/>
}
value={ nodeStatus.enode }
{ ...this._test('node-enode') }
value={ enode }
/>
</div>
</div>
@@ -268,3 +278,24 @@ export default class Status extends Component {
);
}
}
function mapStateToProps (state) {
const {
blockNumber,
blockTimestamp,
netChain,
netPeers
} = state.nodeStatus;
return {
blockNumber,
blockTimestamp,
netChain,
netPeers
};
}
export default connect(
mapStateToProps,
null
)(Status);

View File

@@ -0,0 +1,160 @@
// Copyright 2015-2017 Parity Technologies (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 { action, observable, transaction } from 'mobx';
export default class StatusStore {
@observable defaultExtraData = '';
@observable enode = '';
@observable hashrate = new BigNumber(0);
@observable netPort = new BigNumber(0);
@observable nodeName = '';
@observable rpcSettings = {};
@observable coinbase = '';
@observable extraData = '';
@observable gasFloorTarget = new BigNumber(0);
@observable minGasPrice = new BigNumber(0);
api = null;
_timeoutIds = {};
constructor (api) {
this.api = api;
}
@action setLongStatus ({ defaultExtraData, enode, netPort, rpcSettings }) {
transaction(() => {
this.defaultExtraData = defaultExtraData;
this.enode = enode;
this.netPort = netPort;
this.rpcSettings = rpcSettings;
});
}
@action setStatus ({ hashrate }) {
transaction(() => {
this.hashrate = hashrate;
});
}
@action setMinerSettings ({ coinbase, extraData, gasFloorTarget, minGasPrice }) {
transaction(() => {
this.coinbase = coinbase;
this.extraData = extraData;
this.gasFloorTarget = gasFloorTarget;
this.minGasPrice = minGasPrice;
});
}
startPolling () {
this._pollStatus();
this._pollLongStatus();
}
stopPolling () {
Object.keys(this._timeoutIds).forEach((key) => clearTimeout(this._timeoutIds[key]));
}
/**
* Miner settings should never changes unless
* Parity is restarted, or if the values are changed
* from the UI
*/
_pollMinerSettings () {
return Promise
.all([
this.api.eth.coinbase(),
this.api.parity.extraData(),
this.api.parity.gasFloorTarget(),
this.api.parity.minGasPrice()
])
.then(([
coinbase, extraData, gasFloorTarget, minGasPrice
]) => {
const minerSettings = {
coinbase,
extraData,
gasFloorTarget,
minGasPrice
};
this.setMinerSettings(minerSettings);
})
.catch((error) => {
console.error('_pollMinerSettings', error);
});
}
_pollStatus () {
const nextTimeout = (timeout = 1000) => {
clearTimeout(this._timeoutIds.short);
this._timeoutIds.short = setTimeout(() => this._pollStatus(), timeout);
};
return Promise
.all([
this.api.eth.hashrate()
])
.then(([
hashrate
]) => {
this.setStatus({
hashrate
});
})
.catch((error) => {
console.error('_pollStatus', error);
})
.then(() => {
nextTimeout();
});
}
_pollLongStatus () {
const nextTimeout = (timeout = 30000) => {
clearTimeout(this._timeoutIds.long);
this._timeoutIds.long = setTimeout(() => this._pollLongStatus(), timeout);
};
this._pollMinerSettings();
return Promise
.all([
this.api.parity.defaultExtraData(),
this.api.parity.enode().then((enode) => enode).catch(() => '-'),
this.api.parity.netPort(),
this.api.parity.rpcSettings()
])
.then(([
defaultExtraData, enode, netPort, rpcSettings
]) => {
this.setLongStatus({
defaultExtraData, enode, netPort, rpcSettings
});
})
.catch((error) => {
console.error('_pollLongStatus', error);
})
.then(() => {
nextTimeout();
});
}
handleUpdateSetting = () => {
return this._pollMinerSettings();
};
}

View File

@@ -1,19 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
export const initAppAction = createAction('init app');

View File

@@ -1,22 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
import { identity } from '../util';
import { withError } from '~/redux/util';
export const copyToClipboard = createAction('copy toClipboard', identity, withError(identity));

View File

@@ -1,23 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
export const error = createAction('error');
export const updateDevLogs = createAction('update devLogs');
export const removeDevLogs = createAction('remove devLogs');
export const updateDevLogging = createAction('update devLogging');
export const updateDevLogsLevels = createAction('update devLogsLevels');

View File

@@ -1,20 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
export const error = createAction('error');
export const syncRpcStateFromLocalStorage = createAction('sync rpcStateFromLocalStorage');

View File

@@ -1,24 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
import { identity } from '../util';
import { withError } from '~/redux/util';
export const updateLogging = createAction(
'update logging', identity, withError(flag => `logging updated to ${flag}`)
);

View File

@@ -1,24 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
export const error = createAction('error');
export const updateAuthor = createAction('update author');
export const updateMinGasPrice = createAction('update minGasPrice');
export const updateGasFloorTarget = createAction('update gasFloorTarget');
export const updateExtraData = createAction('update extraData');
export const updateDefaultExtraData = createAction('update defaultExtraData');

View File

@@ -1,23 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
export const modifyMinGasPrice = createAction('modify minGasPrice');
export const modifyGasFloorTarget = createAction('modify gasFloorTarget');
export const modifyAuthor = createAction('modify author');
export const modifyExtraData = createAction('modify extraData');
export const resetExtraData = createAction('reset extraData');

View File

@@ -1,28 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
import { identity } from '../util';
import { withError } from '~/redux/util';
export const error = createAction('error rpc', identity,
withError(() => 'error processing rpc call. check console for details', 'error')
);
export const fireRpc = createAction('fire rpc');
export const addRpcReponse = createAction('add rpcResponse');
export const selectRpcMethod = createAction('select rpcMethod');
export const resetRpcPrevCalls = createAction('reset rpcPrevCalls');

View File

@@ -1,29 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { createAction } from 'redux-actions';
export const error = createAction('error');
export const updateHashrate = createAction('update hashrate');
export const updateBlockNumber = createAction('update blockNumber');
export const updateVersion = createAction('update version');
export const updatePeerCount = createAction('update peerCount');
export const updateNetPeers = createAction('update netPeers');
export const updateNetChain = createAction('update netChain');
export const updateNetPort = createAction('update netPort');
export const updateRpcSettings = createAction('update rpcSettings');
export const updateNodeName = createAction('update nodeName');
export const updateAccounts = createAction('update accounts');

View File

@@ -1,42 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 AutoComplete from 'material-ui/AutoComplete';
export default class WrappedAutoComplete extends Component {
render () {
return (
<AutoComplete { ...this.props } />
);
}
static defaultProps = {
openOnFocus: true,
filter: (searchText, key) => searchText === '' || key.toLowerCase().indexOf(searchText.toLowerCase()) !== -1
}
static propTypes = {
dataSource: PropTypes.array.isRequired,
filter: PropTypes.func,
name: PropTypes.string.isRequired,
openOnFocus: PropTypes.bool
}
static contextTypes = {
muiTheme: PropTypes.object.isRequired
}
}

View File

@@ -1,44 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 getMuiTheme from 'material-ui/styles/getMuiTheme';
import WrappedAutoComplete from './AutoComplete';
describe('views/Status/components/AutoComplete', () => {
describe('rendering', () => {
let rendered;
beforeEach(() => {
const dataSource = ['abc', 'def', 'ghi'];
const component =
<WrappedAutoComplete
dataSource={ dataSource }
name='testComponent'
/>;
rendered = shallow(component, { context: { muiTheme: getMuiTheme({}) } });
});
it('renders the material AutoComplete component', () => {
expect(rendered).to.be.ok;
expect(rendered).to.have.exactly(1).descendants('AutoComplete');
});
});
});

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './AutoComplete';

View File

@@ -1,45 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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';
export default class Box extends Component {
renderValue () {
if (!this.props.value) {
return;
}
return (
<h1>{ this.props.value }</h1>
);
}
render () {
return (
<div className='dapp-box'>
<h2>{ this.props.title }</h2>
{ this.renderValue() }
{ this.props.children }
</div>
);
}
static propTypes = {
title: PropTypes.string.isRequired,
value: PropTypes.string,
children: PropTypes.element
}
}

View File

@@ -1,70 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 Box from './Box';
describe('views/Status/components/Box', () => {
describe('rendering', () => {
const title = 'test title';
let rendered;
beforeEach(() => {
rendered = shallow(<Box title={ title } />);
});
it('renders the component', () => {
expect(rendered).to.be.ok;
expect(rendered).to.have.className('dapp-box');
});
it('renders the title', () => {
expect(rendered.find('h2')).to.have.text(title);
});
it('renders no default value', () => {
expect(rendered).to.not.have.descendants('h1');
});
});
describe('contents', () => {
const value = 'test value';
const child = 'this is the child value';
let rendered;
beforeEach(() => {
rendered = shallow(
<Box
title='title'
value={ value }
>
<pre>{ child }</pre>
</Box>
);
});
it('renders the value', () => {
expect(rendered.find('h1')).to.have.text(value);
});
it('wraps the children', () => {
expect(rendered.find('pre')).to.have.text(child);
});
});
});

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './Box';

View File

@@ -1,40 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.call {
position: relative;
border-bottom: 2px solid #CCC6C6;
margin-bottom: 18.4px;
background: #f5f4f2;
}
.call:first-child {
border-color: #6691C2;
}
.call pre {
margin: 0;
}
.call pre:nth-child(3) {
padding-top: 0;
color: #bbb;
}
.callNo {
float: right;
color: #ccc;
}

View File

@@ -1,68 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 Response from '../Response';
import styles from './Call.css';
export default class Call extends Component {
render () {
let { callNo, name, params, response } = this.props.call;
params = this.formatParams(params);
return (
<div
onMouseEnter={ this.setActiveCall }
ref={ this.setElement }
className={ styles.call }
{ ...this._test(`call-${callNo}`) }
>
<span className={ styles.callNo } { ...this._test('callNo') }>#{ callNo }</span>
<pre { ...this._test('name') }>{ name }({ params })</pre>
<Response response={ response } />
</div>
);
}
setElement = el => {
this.element = el;
}
setActiveCall = () => {
this.props.setActiveCall(this.props.call, this.element);
}
formatParams (params) {
return params.reduce((str, p) => {
if (str !== '') {
str += ', ';
}
if (p === undefined) {
return str;
}
if (typeof p === 'object' || typeof p === 'string') {
p = JSON.stringify(p);
}
return str + p;
}, '');
}
static propTypes = {
call: PropTypes.object.isRequired,
setActiveCall: PropTypes.func.isRequired
}
}

View File

@@ -1,92 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 sinon from 'sinon';
import '../../../../environment/tests';
import Call from './Call';
describe('views/Status/components/Call', () => {
const call = { callIdx: 123, callNo: 456, name: 'eth_call', params: [{ name: '123' }], response: '' };
const element = 'dummyElement';
let rendered;
let instance;
let setActiveCall = sinon.stub();
beforeEach(() => {
rendered = shallow(
<Call
call={ call }
setActiveCall={ setActiveCall }
/>
);
instance = rendered.instance();
});
describe('rendering', () => {
it('renders the component', () => {
expect(rendered).to.be.ok;
expect(rendered).to.have.exactly(1).descendants(`div[data-test="Call-call-${call.callNo}"]`);
});
it('adds onMouseEnter to setActiveElement', () => {
expect(rendered.find('div').first()).to.have.prop('onMouseEnter', instance.setActiveCall);
});
});
describe('actions', () => {
it('sets the element via setElement', () => {
expect(instance.element).to.not.be.ok;
instance.setElement(element);
expect(instance.element).to.equal(element);
});
it('calls parent setActive call on setActiveCall', () => {
instance.setElement(element);
instance.setActiveCall();
expect(setActiveCall).to.be.calledWith(call, element);
});
});
describe('utility', () => {
describe('.formatParams', () => {
it('correctly returns a single parameter', () => {
expect(instance.formatParams([1])).to.equal('1');
});
it('correctly joins 2 parameters', () => {
expect(instance.formatParams([1, 2])).to.equal('1, 2');
});
it('stringifies a string object', () => {
expect(instance.formatParams(['1'])).to.equal('"1"');
});
it('stringifies an object object', () => {
expect(instance.formatParams([{ name: '1' }])).to.equal('{"name":"1"}');
});
it('skips an undefined value', () => {
expect(instance.formatParams(['1', undefined, 3])).to.equal('"1", , 3');
});
});
});
});

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './Call';

View File

@@ -1,138 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { FormattedMessage } from 'react-intl';
import Call from '../Call';
import CallsToolbar from '../CallsToolbar';
import styles from './Calls.css';
export default class Calls extends Component {
state = {
activeCall: null,
activeChild: null
}
render () {
return (
<div
className='calls-container'
onMouseLeave={ this.clearActiveCall }
{ ...this._test('container') }
>
{ this.renderClear() }
<h2 className={ styles.header }>
<FormattedMessage
id='status.calls.title'
defaultMessage='History'
/>
</h2>
<div className={ `${styles.history} row` } ref={ this.setCallsHistory }>
{ this.renderNoCallsMsg() }
{ this.renderCalls() }
</div>
<CallsToolbar
call={ this.state.activeCall }
callEl={ this.state.activeChild }
containerEl={ this._callsHistory }
actions={ this.props.actions }
/>
</div>
);
}
renderClear () {
if (!this.props.calls.length) {
return;
}
return (
<a
{ ...this._test('remove') }
title={
<FormattedMessage
id='status.calls.clearHistory'
defaultMessage='Clear RPC calls history'
/>
}
onClick={ this.clearHistory }
className={ styles.removeIcon }
>
<i className='icon-trash' />
</a>
);
}
renderNoCallsMsg () {
if (this.props.calls.length) {
return;
}
return (
<div { ...this._test('empty-wrapper') }>
<h3 className={ styles.historyInfo } { ...this._test('empty') }>
<FormattedMessage
id='status.calls.rpcResults'
defaultMessage='Fire up some calls and the results will be here.'
/>
</h3>
</div>
);
}
renderCalls () {
const { calls } = this.props;
if (!calls.length) {
return;
}
return calls.map((call, idx) => (
<Call
key={ calls.length - idx }
call={ call }
setActiveCall={ this.setActiveCall }
/>
));
}
clearActiveCall = () => {
this.setState({ activeCall: null, activeChild: null });
}
setActiveCall = (call, el) => {
this.setState({ activeCall: call, activeChild: el });
}
setCallsHistory = el => {
this._callsHistory = el;
}
clearHistory = () => {
this.props.reset();
}
static propTypes = {
calls: PropTypes.arrayOf(PropTypes.object).isRequired,
actions: PropTypes.shape({
fireRpc: PropTypes.func.isRequired,
copyToClipboard: PropTypes.func.isRequired,
selectRpcMethod: PropTypes.func.isRequired
}).isRequired,
reset: PropTypes.func
}
}

View File

@@ -1,135 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 Calls from './Calls';
describe('views/Status/components/Calls', () => {
describe('rendering (no calls)', () => {
let rendered;
before(() => {
const calls = [];
rendered = shallow(<Calls calls={ calls } />);
});
it('renders the component and container', () => {
expect(rendered).to.be.ok;
expect(rendered).to.have.className('calls-container');
});
it('renders no calls', () => {
expect(rendered.find('div[data-test="Calls-empty-wrapper"]')).to.have.exactly(1).descendants('h3');
});
it('renders no clear button', () => {
expect(rendered.find('a[data-test="Calls-remove"]')).to.not.exist;
});
it('renders an attached CallsToolbar', () => {
expect(rendered).to.have.exactly(1).descendants('CallsToolbar');
});
});
describe('rendering (calls supplied)', () => {
const calls = [
{ callNo: 0, name: 'eth_call', params: '', response: '' },
{ callNo: 1, name: 'eth_sendTransaction', params: '', response: '' }
];
const actions = { action1: true, action2: true };
let rendered;
let instance;
before(() => {
rendered = shallow(<Calls calls={ calls } actions={ actions } />);
instance = rendered.instance();
});
it('renders the clear button', () => {
expect(rendered).to.have.exactly(1).descendants('a[data-test="Calls-remove"]');
});
it('renders calls', () => {
expect(rendered.find('div[data-test="Calls-empty-wrapper"]')).to.not.exist;
expect(rendered.find('div.row div')).to.have.exactly(2).descendants('Call');
});
it('passes the correct properties to Call', () => {
const call = rendered.find('Call').first();
expect(call).to.have.prop('setActiveCall', instance.setActiveCall);
expect(call).to.have.prop('call').deep.equal(calls[0]);
});
it('passes the correct properties to CallsToolbar', () => {
const child = { offsetTop: 0 };
const container = { scrollTop: 0 };
instance.setCallsHistory(container);
rendered.setState({ activeCall: 'dummyActiveCall', activeChild: child });
const toolbar = rendered.find('CallsToolbar').first();
expect(toolbar).to.have.prop('call', 'dummyActiveCall');
expect(toolbar).to.have.prop('actions').deep.equal(actions);
expect(toolbar).to.have.prop('callEl').deep.equal(child);
expect(toolbar).to.have.prop('containerEl').deep.equal(container);
});
});
describe('actions', () => {
let rendered;
let instance;
before(() => {
const calls = [
{ callNo: 0, name: 'eth_call', params: '', response: '' },
{ callNo: 1, name: 'eth_sendTransaction', params: '', response: '' }
];
rendered = shallow(<Calls calls={ calls } />);
instance = rendered.instance();
});
it('sets the element via setCallsHistory', () => {
instance.setCallsHistory('dummyElement');
expect(instance._callsHistory).to.equal('dummyElement');
});
it('sets state via setActiveCall', () => {
instance.setActiveCall('dummyActiveCall', 'dummyActiveChild');
expect(rendered).to.have.state('activeCall', 'dummyActiveCall');
expect(rendered).to.have.state('activeChild', 'dummyActiveChild');
});
it('clears state via clearActiveCall', () => {
instance.setActiveCall('dummyActiveCall', 'dummyActiveChild');
expect(rendered).to.have.state('activeCall', 'dummyActiveCall');
instance.clearActiveCall();
expect(rendered).to.have.state('activeCall', null);
expect(rendered).to.have.state('activeChild', null);
});
});
});

View File

@@ -1,66 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.callActionsWrap {
position: absolute;
animation: fadein .5s;
right: 0;
height: 28px;
}
.callActionsButton {
padding: 0 !important;
height: 24px !important;
}
.callActionsWrap:hover .callActionsButton {
display: none !important;
}
.callActions {
display: none;
height: 100%;
margin-top: 1px;
margin-right: 10px;
}
.callActionsWrap:hover .callActions {
display: block;
}
.callAction {
float: right;
transition: opacity ease-in-out .2s;
transition-delay: .2s;
opacity: 0;
padding: 3px !important;
height: 22px !important;
width: 22px !important;
}
.callActions:hover .callAction {
opacity: 1;
}
.callActionIcon {
height: 100% !important;
width: 100% !important;
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}

View File

@@ -1,153 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 CopyToClipboard from 'react-copy-to-clipboard';
import { FormattedMessage } from 'react-intl';
import { sortBy, find, extend } from 'lodash';
import IconButton from 'material-ui/IconButton';
import MoreHorizIcon from 'material-ui/svg-icons/navigation/more-horiz';
import CallIcon from 'material-ui/svg-icons/communication/call';
import AssignmentIcon from 'material-ui/svg-icons/action/assignment';
import InputIcon from 'material-ui/svg-icons/action/input';
import { SCROLLBAR_WIDTH } from '../../constants';
import styles from './CallsToolbar.css';
import rpcData from '../../data/rpc.json';
const rpcMethods = sortBy(rpcData.methods, 'name');
export default class CallsToolbar extends Component {
render () {
const { call, callEl, containerEl } = this.props;
if (!call) {
return null;
}
const wrapStyle = { top: callEl.offsetTop - SCROLLBAR_WIDTH - containerEl.scrollTop };
if (this.hasScrollbar(containerEl)) {
wrapStyle.right = 13;
}
return (
<div
className={ styles.callActionsWrap }
style={ wrapStyle }
>
<IconButton
className={ styles.callActionsButton }
{ ...this._test('button-more') }
>
<MoreHorizIcon />
</IconButton>
<div className={ styles.callActions } { ...this._test('button-container') }>
<IconButton
className={ styles.callAction }
onTouchTap={ this.setCall }
tooltip={
<FormattedMessage
id='status.callsToolbar.tooltip.set'
defaultMessage='Set'
/>
}
tooltipPosition='top-left'
{ ...this._test('button-setCall') }
>
<InputIcon className={ styles.callActionIcon } />
</IconButton>
<IconButton
className={ styles.callAction }
onTouchTap={ this.makeCall }
tooltip={
<FormattedMessage
id='status.callsToolbar.tooltip.fireAgain'
defaultMessage='Fire again'
/>
}
tooltipPosition='top-left'
{ ...this._test('button-makeCall') }
>
<CallIcon className={ styles.callActionIcon } />
</IconButton>
<CopyToClipboard
text={ JSON.stringify(call) }
onCopy={ this.copyToClipboard }
>
<IconButton
className={ styles.callAction }
tooltip={
<FormattedMessage
id='status.callsToolbar.tooltip.copy'
defaultMessage='Copy to clipboard'
/>
}
tooltipPosition='top-left'
{ ...this._test('copyCallToClipboard') }
>
<AssignmentIcon className={ styles.callActionIcon } />
</IconButton>
</CopyToClipboard>
</div>
</div>
);
}
setCall = () => {
const { call } = this.props;
let method = find(rpcMethods, { name: call.name });
this.props.actions.selectRpcMethod(extend({}, method, { paramsValues: call.params }));
}
makeCall = () => {
const { call } = this.props;
let method = find(rpcMethods, { name: call.name });
this.setCall();
this.props.actions.fireRpc({
method: method.name,
outputFormatter: method.outputFormatter,
inputFormatters: method.inputFormatters,
params: call.params
});
}
copyToClipboard = () => {
this.props.actions.copyToClipboard(
<FormattedMessage
id='status.callsToolbar.copied'
defaultMessage='method copied to clipboard'
/>
);
}
hasScrollbar (el) {
return el.clientHeight < el.scrollHeight;
}
}
CallsToolbar.propTypes = {
call: PropTypes.object.isRequired,
callEl: PropTypes.node.isRequired,
containerEl: PropTypes.node.isRequired,
actions: PropTypes.shape({
fireRpc: PropTypes.func.isRequired,
copyToClipboard: PropTypes.func.isRequired,
selectRpcMethod: PropTypes.func.isRequired
}).isRequired
};

View File

@@ -1,118 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 sinon from 'sinon';
import { shallow } from 'enzyme';
import '../../../../environment/tests';
import CallsToolbar from './CallsToolbar';
describe('views/Status/components/CallsToolbar', () => {
const callEl = { offsetTop: 0 };
const containerEl = { scrollTop: 0, clientHeight: 0, scrollHeight: 999 };
describe('rendering (no call)', () => {
let rendered;
before(() => {
const call = null;
rendered = shallow(<CallsToolbar call={ call } callEl={ callEl } containerEl={ containerEl } />);
});
it('does not render the component', () => {
expect(rendered).to.not.have.descendants('[data-test="CallsToolbar-button-more"]');
});
});
describe('rendering', () => {
const call = { callNo: 456, name: 'eth_call', params: '', response: '' };
let rendered;
let btncontainer;
before(() => {
rendered = shallow(<CallsToolbar call={ call } callEl={ callEl } containerEl={ containerEl } />);
btncontainer = rendered.find('[data-test="CallsToolbar-button-container"]');
});
it('renders the More button', () => {
expect(rendered).to.have.descendants('[data-test="CallsToolbar-button-more"]');
});
it('renders the Set button', () => {
expect(btncontainer).to.have.descendants('[data-test="CallsToolbar-button-setCall"]');
});
it('renders the Fire button', () => {
expect(btncontainer).to.have.descendants('[data-test="CallsToolbar-button-makeCall"]');
});
it('renders the Copy button', () => {
expect(btncontainer).to.have.descendants('[data-test="CallsToolbar-copyCallToClipboard"]');
});
});
describe('actions', () => {
const call = { callNo: 456, name: 'eth_call', params: '', response: '' };
const actions = { fireRpc: sinon.stub(), copyToClipboard: sinon.stub(), selectRpcMethod: sinon.stub() };
let rendered;
let instance;
before(() => {
rendered = shallow(<CallsToolbar call={ call } callEl={ callEl } containerEl={ containerEl } actions={ actions } />);
instance = rendered.instance();
});
it('calls copyToClipboard with action copyToClipboard', () => {
instance.copyToClipboard();
expect(actions.copyToClipboard).to.be.calledOnce;
});
it('calls setCall with action selectRpcMethod', () => {
instance.setCall();
expect(actions.selectRpcMethod).to.be.calledOnce;
});
it('calls makeCall with action fireRpc', () => {
instance.makeCall();
expect(actions.fireRpc).to.be.calledOnce;
});
});
describe('utility', () => {
const call = { callNo: 456, name: 'eth_call', params: '', response: '' };
let rendered;
let instance;
before(() => {
rendered = shallow(<CallsToolbar call={ call } callEl={ callEl } containerEl={ containerEl } />);
instance = rendered.instance();
});
describe('.hasScrollbar', () => {
it('correctly returns true when scrollbar', () => {
expect(instance.hasScrollbar({ clientHeight: 123, scrollHeight: 456 })).to.be.true;
});
it('correctly returns false when no scrollbar', () => {
expect(instance.hasScrollbar({ clientHeight: 456, scrollHeight: 123 })).to.be.false;
});
});
});
});

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './CallsToolbar';

View File

@@ -1,74 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.input {
margin-top: 0;
width: 100%;
padding-left: 10px;
padding-right: 50px;
}
.icon, .iconSuccess, .firstIcon {
display: block;
cursor: pointer;
flex: 1;
margin: 3px 3px;
color: #bbb;
}
.success, .iconSuccess {
color: #8bc34a;
}
.icon i, .iconSuccess i, .firstIcon i {
vertical-align: middle;
}
.icons, .iconsVisible, .firstIcon {
z-index: 10;
position: absolute;
right: 0;
top: 0;
bottom: 0;
display: flex;
opacity: 0;
transition: opacity ease-in-out .2s;
transition-delay: 0.2s;
}
.iconsVisible {
opacity: 1.0;
}
.container:hover .icons, .container:hover .firstIcon {
opacity: 1.0;
}
.firstIcon {
position: absolute;
left: -10px;
right: auto;
top: 0;
bottom: 0;
margin: 3px 0;
display: block;
}
.autocomplete input {
margin-top: 0;
padding-left: 10px !important;
padding-right: 50px !important;
}

View File

@@ -1,204 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { FormattedMessage } from 'react-intl';
import AutoComplete from 'material-ui/AutoComplete';
import styles from './EditableValue.css';
import valueStyles from '../Value/Value.css';
export default class EditableValue extends Component {
state = {
value: this.props.value,
inEditMode: false
}
componentWillReceiveProps (newProps) {
if (newProps.value === this.state.value || this.state.inEditMode) {
return;
}
this.setState({
value: newProps.value
});
}
onChange = value => {
this.setState({
value: value
});
}
onOpenEdit = evt => {
this.setState({
inEditMode: true
});
if (!this._input) {
return;
}
this._input.focus();
}
onCancel = evt => {
this.setState({
inEditMode: false,
value: this.props.value
});
}
onSubmit = () => {
this.setState({
inEditMode: false
});
this.props.onSubmit(this.state.value, false);
}
onResetToDefault = () => {
this.props.onSubmit(this.props.defaultValue, true);
}
render () {
return (
<form
className={ `${valueStyles.valueContainer} ${styles.container}` }
onSubmit={ this.onSubmit }
{ ...this._testInherit() }
>
{ this.renderResetButton() }
<div className={ this.state.inEditMode ? styles.iconsVisible : styles.icons }>
{ this.props.children }
{ this.renderButtons() }
</div>
{ this.renderInput() }
</form>
);
}
renderInput () {
const { inEditMode, value } = this.state;
const setInput = el => { this._input = el; };
const onChange = evt => this.onChange(evt.target.value);
if (!inEditMode || !this.props.autocomplete) {
return (
<input
className={ inEditMode ? styles.input : valueStyles.value }
type='text'
value={ value }
onClick={ this.onOpenEdit }
ref={ setInput }
onChange={ onChange }
readOnly={ !inEditMode }
/>
);
}
return (
<AutoComplete
name='EditableValueAutoComplete' // avoid Material Ui warning
className={ styles.autocomplete }
fullWidth
searchText={ value }
dataSource={ this.props.dataSource }
onUpdateInput={ this.onChange }
onNewRequest={ this.onChange }
openOnFocus
filter={ AutoComplete.noFilter }
/>
);
}
renderResetButton () {
if (this.state.inEditMode) {
return;
}
if (!this.props.defaultValue || this.state.value === this.props.defaultValue) {
return;
}
return (
<a
key='reset'
className={ `${styles.icon} ${styles.firstIcon}` }
onClick={ this.onResetToDefault }
title={
<FormattedMessage
id='status.editableValue.reset'
defaultMessage='Reset to {defaultVaule}'
values={ {
defaultVaule: this.props.defaultValue
} }
/>
}
{ ...this._testInherit('reset') }
>
<i className='icon-anchor' />
</a>
);
}
renderButtons () {
if (this.state.inEditMode) {
return [
<a
key='submit'
className={ styles.iconSuccess }
onClick={ this.onSubmit }
{ ...this._testInherit('submit') }
>
<i className='icon-check' />
</a>,
<a
key='cancel'
className={ styles.icon }
onClick={ this.onCancel }
{ ...this._testInherit('cancel') }
>
<i className='icon-close' />
</a>
];
}
return (
<a
key='edit'
className={ styles.icon }
onClick={ this.onOpenEdit }
title={
<FormattedMessage
id='status.editableValue.edit'
defaultMessage='Edit'
/>
}
{ ...this._testInherit('edit') }
>
<i className='icon-pencil' />
</a>
);
}
static propTypes = {
onSubmit: PropTypes.func.isRequired,
value: PropTypes.string,
defaultValue: PropTypes.string,
children: PropTypes.element,
autocomplete: PropTypes.bool,
dataSource: PropTypes.arrayOf(PropTypes.string)
}
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './EditableValue';

View File

@@ -1,28 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.editor {
width: 100%;
height: 200px;
}
.error {
border: 1px solid red !important;
}
.errorMsg {
color: red;
}

View File

@@ -1,99 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { isEqual } from 'lodash';
import formatJson from 'format-json';
import styles from './JsonEditor.css';
export default class JsonEditor extends Component {
constructor (...args) {
super(...args);
let { value } = this.props;
value = formatJson.plain(value);
this.state = { value };
}
componentDidMount () {
const mockedEvt = { target: { value: this.state.value } };
this.onChange(mockedEvt);
}
componentWillReceiveProps (nextProps) {
let { value } = nextProps;
if (!isEqual(value, this.props.value)) {
value = formatJson.plain(value);
this.setState({ value });
}
}
render () {
let errorClass = this.state.error ? styles.error : '';
return (
<div className='row'>
<textarea
onChange={ this.onChange }
className={ `${styles.editor} ${errorClass}` }
value={ this.state.value }
/>
{ this.renderError() }
</div>
);
}
renderError () {
const { error } = this.state;
if (!error) {
return;
}
return (
<div className={ styles.errorMsg }>{ error }</div>
);
}
onChange = evt => {
const { value } = evt.target;
let parsed;
let error;
try {
parsed = JSON.parse(value);
error = null;
} catch (err) {
parsed = null;
error = 'invalid json';
}
this.setState({
value,
error
});
this.props.onChange(parsed, error);
}
static propTypes = {
onChange: PropTypes.func.isRequired,
value: PropTypes.object
}
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './JsonEditor';

View File

@@ -1,20 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.container li {
display: list-item;
line-height: 1.6;
}

View File

@@ -1,57 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 marked from 'marked';
import React, { Component, PropTypes } from 'react';
import styles from './Markdown.css';
export default class Marked extends Component {
state = {}
render () {
let { parsed } = this.state;
if (!parsed) {
return null;
}
return <div className={ styles.container } style={ this.props.style } dangerouslySetInnerHTML={ { __html: parsed } } />;
}
componentWillMount () {
this.setState({ parsed: this.parse(this.props.val) });
}
componentWillReceiveProps (newProps) {
if (newProps.val === this.props.val) {
return;
}
this.setState({ parsed: this.parse(newProps.val) });
}
parse (val) {
try {
val = marked(val);
} catch (err) {
console.error(`Marked error when parsing ${val}: ${err}`);
}
return val;
}
static propTypes = {
val: PropTypes.any,
style: PropTypes.object
}
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './Markdown';

View File

@@ -1,20 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.response {
white-space: nowrap;
overflow-x: auto;
}

View File

@@ -1,65 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { isArray, isPlainObject } from 'lodash';
import styles from './Response.css';
export default class Response extends Component {
render () {
let { response } = this.props;
let formatted;
if (isArray(response)) {
formatted = this.renderArray();
}
if (isPlainObject(response)) {
formatted = this.renderObject();
}
return <pre className={ styles.response }>{ formatted || response }</pre>;
}
renderArray () {
let { response } = this.props;
return response.map((r, idx) => (
<span key={ idx }>
{ idx === 0 ? '[' : ',' }
{ idx === 0 ? '' : <br /> }
{ r }
{ idx === response.length - 1 ? ']' : '' }
</span>
));
}
renderObject () {
let { response } = this.props;
const arr = JSON.stringify(response, null, 1).split('\n');
return arr.map((any, idx) => (
<span key={ idx }>
{ any }
{ idx !== 0 && idx !== arr.length - 1 ? <br /> : '' }
</span>
));
}
static propTypes = {
response: PropTypes.any.isRequired
}
}

View File

@@ -1,47 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 Response from './Response';
describe('views/Status/components/Response', () => {
describe('rendering', () => {
it('renders non-arrays/non-objects exactly as received', () => {
const TEST = '1234567890';
const rendered = shallow(<Response response={ TEST } />);
expect(rendered).to.have.html(`<pre>${TEST}</pre>`);
});
it('renders arrays properly with index and value', () => {
const TEST = ['123', '456'];
const rendered = shallow(<Response response={ TEST } />);
expect(rendered).to.have.html('<pre><span>[123</span><span>,<br/>456]</span></pre>');
});
it('renders objects properly with key and value', () => {
const TEST = { foo: '123', bar: '456' };
const rendered = shallow(<Response response={ TEST } />);
expect(rendered).to.have.html('<pre><span>{</span><span> &quot;foo&quot;: &quot;123&quot;,<br/></span><span> &quot;bar&quot;: &quot;456&quot;<br/></span><span>}</span></pre>');
});
});
});

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './Response';

View File

@@ -1,32 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.header {
margin: 0;
}
.input {
width: 100%;
}
h3 + label > input {
margin-top: 0;
}
.jsonToggle {
width: auto !important;
float: right;
}

View File

@@ -1,373 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { FormattedMessage } from 'react-intl';
import _ from 'lodash';
import Toggle from 'material-ui/Toggle/Toggle';
import TextField from 'material-ui/TextField';
import { RpcAutoComplete } from 'dapps-react-components';
import { formatRpcMd } from '../../util/rpc-md';
import AnimateChildren from '../../components-compositors/Animated/children';
import JsonEditor from '../JsonEditor';
import Calls from '../Calls';
import Markdown from '../Markdown';
import styles from './RpcCalls.css';
import rpcData from '../../data/rpc.json';
import RpcNav from '../RpcNav';
const rpcMethods = _.sortBy(rpcData.methods, 'name');
export default class RpcCalls extends Component {
state = {};
componentWillReceiveProps (nextProps) {
const { paramsValues, params } = nextProps.rpc.selectedMethod;
if (paramsValues) {
params.map((p, index) => {
// todo [adgo] 01.05.2016 - make sure this works
// not sure idx is the same for paramsValues and params
this.setState({
[this.paramKey(p)]: paramsValues[index]
});
});
if (this.state.jsonMode) {
this.setJsonEditorValue();
}
}
}
render () {
return (
<div className='dapp-flex-content'>
<main className='dapp-content'>
<div className='dapp-container'>
<div className='row'>
<div className='col col-6'>
<h1>
<FormattedMessage
id='status.rpcCalls.requests'
defaultMessage='RPC Requests'
/>
</h1>
</div>
<div className='col col-6'>
<RpcNav />
</div>
</div>
</div>
<div style={ { clear: 'both' } } />
<div className='dapp-container'>
<div className='row'>
<div className='col col-6 mobile-full'>
{ this.renderForm() }
</div>
<div className='col col-6 mobile-full'>
<Calls
calls={ this.props.rpc.prevCalls }
reset={ this.props.actions.resetRpcPrevCalls }
actions={ this.props.actions }
/>
</div>
</div>
</div>
</main>
</div>
);
}
renderForm () {
return (
<div>
<Toggle
className={ styles.jsonToggle }
onToggle={ this.onJsonToggle }
label={
<FormattedMessage
id='status.rpcCalls.json'
defaultMessage='JSON'
/>
}
/>
<h2 className={ styles.header }>
<label htmlFor='selectedMethod'>
<FormattedMessage
id='status.rpcCalls.callMethod'
defaultMessage='Call Method'
/>
</label>
</h2>
<AnimateChildren absolute>
{ this.renderJsonForm() }
{ this.renderInputForm() }
</AnimateChildren>
</div>
);
}
renderInputForm () {
if (this.state.jsonMode) {
return;
}
const { returns } = this.props.rpc.selectedMethod;
return (
<div className='row'>
{ this.renderMethodList() }
<h3>
<FormattedMessage
id='status.rpcCalls.parameters'
defaultMessage='Parameters'
/>
</h3>
{ this.renderInputs() }
<h3>
<FormattedMessage
id='status.rpcCalls.returns'
defaultMessage='Returns'
/>
</h3>
<Markdown val={ formatRpcMd(returns) } />
{ this.renderFormButton() }
</div>
);
}
renderMethodList () {
const { desc } = this.props.rpc.selectedMethod;
return (
<div>
<RpcAutoComplete
style={ { marginTop: 0 } }
onNewRequest={ this.handleMethodChange }
{ ...this._test('rpcAutoComplete') }
/>
<div>
<Markdown val={ desc } />
</div>
</div>
);
}
handleMethodChange = name => {
const method = rpcMethods.find(m => m.name === name);
this.props.actions.selectRpcMethod(method);
}
onRpcFire = () => {
if (this.state.jsonMode) {
return this.onCustomRpcFire();
}
let { name, params, outputFormatter, inputFormatters } = this.props.rpc.selectedMethod;
params = params.map(this.jsonParamValue);
this.props.actions.fireRpc({
method: name,
outputFormatter,
inputFormatters,
params
});
}
onCustomRpcFire () {
const { method, params } = this.state.jsonEditorParsedValue;
this.props.actions.fireRpc({ method, params });
}
renderInputs () {
let { params, name } = this.props.rpc.selectedMethod;
if (!params || !params.length) {
return (
<FormattedMessage
id='status.rpcCalls.none'
defaultMessage='none'
/>
);
}
return _.find(rpcMethods, { name })
.params.map(
p => {
const onChange = evt => this.setState({
[this.paramKey(p)]: evt.target.value
});
if (_.isPlainObject(p)) {
return this.renderObjInputs(p);
}
return (
<TextField
key={ p }
inputStyle={ { marginTop: 0 } }
fullWidth
hintText={ p }
title={ p }
hintStyle={ { maxWidth: '100%', overflow: 'hidden', whiteSpace: 'nowrap' } }
value={ this.paramValue(p) }
onChange={ onChange }
{ ...this._test(this.paramKey(p)) }
/>
);
}
);
}
renderObjInputs (param) {
const { description, details } = param;
return (
<div>
<Markdown val={ description } />
<ul>
{ Object.keys(details).map(k => {
const onChange = evt => this.setState({
[this.paramKey(`${description}.${k}`)]: evt.target.value
});
return (
<li key={ k }>
<TextField
inputStyle={ { marginTop: 0 } }
fullWidth
title={ `${k}: ${details[k]}` }
hintText={ `${k}: ${details[k]}` }
hintStyle={ { maxWidth: '100%', overflow: 'hidden', whiteSpace: 'nowrap' } }
value={ this.paramValue(`${description}.${k}`) }
onChange={ onChange }
{ ...this._test(this.paramKey(k)) }
/>
</li>
);
}) }
</ul>
</div>
);
}
setJsonEditorValue () {
const { name, params } = this.props.rpc.selectedMethod;
const json = {
method: name,
params: params.map(this.jsonParamValue)
};
this.setState({
jsonEditorValue: json
});
}
onJsonToggle = () => {
if (!this.state.jsonMode) {
this.setJsonEditorValue();
}
this.setState({ jsonMode: !this.state.jsonMode });
}
renderJsonForm () {
if (!this.state.jsonMode) {
return;
}
return (
<div>
<JsonEditor
onChange={ this.onJsonEditorChange }
value={ this.state.jsonEditorValue }
/>
{ this.renderFormButton() }
</div>
);
}
renderFormButton () {
return (
<button
{ ...this._test('fireRpc') }
className={ 'dapp-block-button' }
disabled={ this.state.jsonEditorError }
onClick={ this.onRpcFire }
>
<FormattedMessage
id='status.rpcCalls.fireButton'
defaultMessage='Fire'
/>
</button>
);
}
onJsonEditorChange = (jsonEditorParsedValue, jsonEditorError) => {
this.setState({
jsonEditorParsedValue,
jsonEditorError
});
}
jsonParamValue = p => {
if (_.isPlainObject(p)) {
const { description, details } = p;
return Object.keys(details).reduce((obj, key) => {
obj[key] = this.paramValue(`${description}.${key}`);
return obj;
}, {});
}
return this.paramValue(p);
}
paramValue (p) {
return this.state[this.paramKey(p)];
}
paramKey (p) {
return `params_${p}`;
}
selectedMethodChanged (nextProps) {
return nextProps.rpc.selectedMethod.name !== this.props.rpc.selectedMethod.name;
}
stateChanged (nextState) {
return !_.isEqual(nextState, this.state);
}
prevCallsChanged (nextProps) {
return nextProps.rpc.prevCalls.length !== this.props.rpc.prevCalls.length;
}
static propTypes = {
rpc: PropTypes.shape({
prevCalls: PropTypes.array.isRequired,
selectedMethod: PropTypes.object.isRequired
}).isRequired,
actions: PropTypes.shape({
fireRpc: PropTypes.func.isRequired,
selectRpcMethod: PropTypes.func.isRequired,
resetRpcPrevCalls: PropTypes.func.isRequired
}).isRequired
}
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './RpcCalls';

View File

@@ -1,35 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.autocomplete {
margin-top: 0 !important;
}
.headline {
text-transform: none;
}
.returnsTitle {
display: 'inline';
}
.returnsDesc {
display: inline-block;
}
hr {
margin-bottom: 0;
}

View File

@@ -1,142 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 } from 'react';
import ReactDOM from 'react-dom';
import { FormattedMessage } from 'react-intl';
import { sortBy } from 'lodash';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import AutoComplete from '../AutoComplete';
import { formatRpcMd } from '../../util/rpc-md';
import ScrollTopButton from '../ScrollTopButton';
import styles from './RpcDocs.css';
import Markdown from '../Markdown';
import rpcData from '../../data/rpc.json';
import RpcNav from '../RpcNav';
const rpcMethods = sortBy(rpcData.methods, 'name');
class RpcDocs extends Component {
render () {
return (
<div className='dapp-flex-content'>
<main className='dapp-content'>
<div className='dapp-container'>
<div className='row'>
<div className='col col-6'>
<h1>
<FormattedMessage
id='status.rpcDocs.title'
defaultMessage='RPC Docs'
/>
</h1>
</div>
<div className='col col-6'>
<RpcNav />
</div>
</div>
</div>
<div style={ { clear: 'both' } } />
<div className='dapp-container'>
<div className='row'>
<div className='col col-12'>
<AutoComplete
floatingLabelText={
<FormattedMessage
id='status.rpcDocs.methodName'
defaultMessage='Method name'
/>
}
className={ styles.autocomplete }
dataSource={ rpcMethods.map(m => m.name) }
onNewRequest={ this.handleMethodChange }
{ ...this._test('autocomplete') }
/>
{ this.renderData() }
</div>
</div>
</div>
<ScrollTopButton />
</main>
</div>
);
}
renderData () {
const methods = rpcMethods.map((m, idx) => {
const setMethod = el => { this[`_method-${m.name}`] = el; };
return (
<ListItem
key={ m.name }
disabled
ref={ setMethod }
>
<h3 className={ styles.headline }>{ m.name }</h3>
<Markdown val={ m.desc } />
<p>
<FormattedMessage
id='status.rpcDocs.params'
defaultMessage='Params {params}'
vaules={ {
params: !m.params.length
? (
<FormattedMessage
id='status.rpcDocs.paramsNone'
defaultMessage=' - none'
/>
)
: ''
} }
/>
</p>
{
m.params.map((p, idx) => {
return (
<Markdown
key={ `${m.name}-${idx}` }
val={ formatRpcMd(p) }
/>
);
})
}
<p className={ styles.returnsTitle }>
<FormattedMessage
id='status.rpcDocs.returns'
defaultMessage='Returns - '
/>
</p>
<Markdown className={ styles.returnsDesc } val={ formatRpcMd(m.returns) } />
{ idx !== rpcMethods.length - 1 ? <hr /> : '' }
</ListItem>
);
});
return (
<List>
{ methods }
</List>
);
}
handleMethodChange = name => {
ReactDOM.findDOMNode(this[`_method-${name}`]).scrollIntoViewIfNeeded();
}
}
export default RpcDocs;

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './RpcDocs';

View File

@@ -1,28 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.nav {
font-size: 20px;
float: right;
}
.activeNav {
color: #b7a6a6 !important;
}
.nav a:not(:first-child) {
margin-left: 30px;
}

View File

@@ -1,34 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 } from 'react';
import { Link } from 'react-router';
import styles from './RpcNav.css';
export default class RpcNav extends Component {
render () {
return (
<div className={ styles.nav }>
<Link to={ '/rpc/calls' } activeClassName={ styles.activeNav } { ...this._test('rpc-calls-link') }>
<i className='icon-call-out' />
</Link>
<Link to={ '/rpc/docs' } activeClassName={ styles.activeNav } { ...this._test('rpc-docs-link') }>
<i className='icon-docs' />
</Link>
</div>
);
}
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './RpcNav';

View File

@@ -1,41 +0,0 @@
/* Copyright 2015-2017 Parity Technologies (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/>.
*/
.scrollButton {
position: fixed !important;
bottom: 35px !important;
right: 0;
left: 0;
margin-right: auto;
margin-left: auto;
animation: fadein .5s;
background: #eee !important;
border-radius: 50%;
border: 1px solid #6691C2 !important;
z-index: 10;
padding: 5px !important;
width: 34px !important;
height: 34px !important;
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
.hidden {
display: none !important;
}

View File

@@ -1,70 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 } from 'react';
import IconButton from 'material-ui/IconButton';
import ArrowUpwardIcon from 'material-ui/svg-icons/navigation/arrow-upward';
import { scrollTo } from './util';
import styles from './ScrollTopButton.css';
const scrollTopThreshold = 600;
export default class ScrollTopButton extends Component {
state = {}
componentDidMount () {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount () {
window.removeEventListener('scroll', this.handleScroll);
}
_scrollToTop () {
scrollTo(document.body, 0, 500);
}
render () {
let hiddenClass = !this.state.showScrollButton ? styles.hidden : '';
return (
<IconButton
className={ `${styles.scrollButton} ${hiddenClass}` }
onTouchTap={ this._scrollToTop }
>
<ArrowUpwardIcon />
</IconButton>
);
}
handleScroll = event => {
let { scrollTop } = event.srcElement.body;
let { showScrollButton } = this.state;
if (!showScrollButton && scrollTop > scrollTopThreshold) {
this.setState({
showScrollButton: true
});
}
if (showScrollButton && scrollTop < scrollTopThreshold) {
this.setState({
showScrollButton: false
});
}
}
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './ScrollTopButton';

View File

@@ -1,48 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 function scrollTo (element, to, duration) {
let start = element.scrollTop;
let change = to - start;
let increment = 50;
let animateScroll = elapsedTime => {
elapsedTime += increment;
let position = easeInOut(elapsedTime, start, change, duration);
element.scrollTop = position;
if (elapsedTime < duration) {
setTimeout(() => {
// stop if user scrolled
if (element.scrollTop !== parseInt(position, 10)) {
return;
}
animateScroll(elapsedTime);
}, increment);
}
};
animateScroll(0);
}
export function easeInOut (currentTime, start, change, duration) {
currentTime /= duration / 2;
if (currentTime < 1) {
return change / 2 * currentTime * currentTime + start;
}
currentTime -= 1;
return -change / 2 * (currentTime * (currentTime - 2) - 1) + start;
}

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 const SCROLLBAR_WIDTH = 22;

View File

@@ -1,57 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { extend } from 'lodash';
import * as RpcActions from '../../actions/rpc';
import { updateLogging } from '../../actions/logger';
import { copyToClipboard } from '../../actions/clipboard';
class RpcPage extends Component {
static propTypes = {
children: PropTypes.object.isRequired,
rpc: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
}
render () {
return (
<div>
{ this.props.children && React.cloneElement(this.props.children, {
...this.props
}) }
</div>
);
}
}
function mapStateToProps (state) {
return state;
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(extend({}, RpcActions, { copyToClipboard }, { updateLogging }), dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(RpcPage);

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './RpcPage';

View File

@@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 './statusPage';

View File

@@ -1,69 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from '~/redux/actions';
import Debug from '../../components/Debug';
import Status from '../../components/Status';
import styles from './statusPage.css';
class StatusPage extends Component {
static propTypes = {
nodeStatus: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
}
componentWillMount () {
this.props.actions.toggleStatusRefresh(true);
}
componentWillUnmount () {
this.props.actions.toggleStatusRefresh(false);
}
render () {
return (
<div className={ styles.body }>
<Status { ...this.props } />
<Debug { ...this.props } />
</div>
);
}
}
function mapStateToProps (state) {
return state;
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators({
clearStatusLogs,
toggleStatusLogs,
toggleStatusRefresh
}, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(StatusPage);

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 LocalStorage from './localstorage';
export default function () {
const localstorage = new LocalStorage();
return [
localstorage.toMiddleware()
];
}

View File

@@ -1,75 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 localStore from 'store';
import { syncRpcStateFromLocalStorage } from '../actions/localstorage';
import rpcMetods from '../data/rpc.json';
export default class localStorageMiddleware {
toMiddleware () {
return store => next => action => {
let delegate;
switch (action.type) {
case 'add rpcResponse': delegate = ::this.onAddRpcResponse; break;
case 'reset rpcPrevCalls': delegate = ::this.onResetRpcCalls; break;
case 'init app': delegate = ::this.onInitApp; break;
default:
next(action);
return;
}
if (!delegate) {
return;
}
delegate(store, next, action);
};
}
onInitApp (store, next, action) {
const prevCalls = localStore.get('rpcPrevCalls');
if (!(prevCalls && prevCalls.length)) {
return next(action);
}
store.dispatch(syncRpcStateFromLocalStorage({
prevCalls: prevCalls,
callNo: prevCalls.length ? prevCalls[0].callNo + 1 : 1,
selectedMethod: rpcMetods.methods.find(m => m.name === prevCalls[0].name)
}));
return next(action);
}
onAddRpcResponse (store, next, action) {
action.payload.callNo = store.getState().rpc.callNo;
this.unshift('rpcPrevCalls', action.payload);
return next(action);
}
onResetRpcCalls (store, next, action) {
localStore.set('rpcPrevCalls', []);
return next(action);
}
// TODO [adgo] 20.04.2016 remove if/when PR is accepted: https://github.com/marcuswestin/store.js/pull/153
unshift (key, value) {
const newArr = [value].concat(localStore.get(key) || []);
localStore.set(key, newArr);
}
}

View File

@@ -1,205 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 sinon from 'sinon';
import localStore from 'store';
import { syncRpcStateFromLocalStorage } from '../actions/localstorage';
import rpcData from '../data/rpc.json';
import LocalStorageMiddleware from './localstorage';
describe('views/Status/middleware/localstorage', () => {
let cut;
let state;
beforeEach('mock cut', () => {
cut = new LocalStorageMiddleware();
sinon.spy(cut, 'onAddRpcResponse');
sinon.spy(cut, 'onResetRpcCalls');
sinon.spy(cut, 'onInitApp');
sinon.spy(cut, 'unshift');
state = {
rpc: {
callNo: 1
}
};
});
it('should call onAddRpcResponse when respected action is dispatched', () => {
// given
const store = { getState: () => state };
const next = sinon.spy();
const middleware = cut.toMiddleware()(store)(next);
const action = { type: 'add rpcResponse', payload: {} };
expect(middleware).to.be.a('function');
expect(action).to.be.an('object');
// when
middleware(action);
// then
expect(cut.onAddRpcResponse.calledWith(store, next, action)).to.be.true;
});
it('should call onResetRpcCalls when respected action is dispactched', () => {
// given
const store = {};
const next = sinon.spy();
const middleware = cut.toMiddleware()(store)(next);
const action = { type: 'reset rpcPrevCalls', payload: {} };
expect(middleware).to.be.a('function');
expect(action).to.be.an('object');
// when
middleware(action);
// then
expect(cut.onResetRpcCalls.calledWith(store, next, action)).to.be.true;
});
it('should call onInitApp when respected action is dispatched', () => {
// given
const store = { dispatch: sinon.spy() };
const next = sinon.spy();
const middleware = cut.toMiddleware()(store)(next);
const action = { type: 'init app' };
cut.onInitApp = sinon.spy();
expect(middleware).to.be.a('function');
expect(action).to.be.an('object');
// when
middleware(action);
// then
expect(cut.onInitApp.calledWith(store, next, action)).to.be.true;
});
it('should not call onAddRpcResponse or onInitApp when a non-respected action is dispatched', () => {
// given
const store = {};
const next = sinon.spy();
const middleware = cut.toMiddleware()(store)(next);
const action = { type: 'testAction' };
expect(middleware).to.be.a('function');
expect(action).to.be.an('object');
// when
middleware(action);
// then
expect(cut.onAddRpcResponse.called).to.be.false;
expect(cut.onInitApp.called).to.be.false;
expect(next.calledWith(action)).to.be.true;
});
describe('RPC', () => {
it('should dispatch syncRpcStateFromLocalStorage when there are rpc calls in localStorage', () => {
// given
const store = { dispatch: sinon.spy() };
const next = sinon.spy();
const action = {};
const key = 'rpcPrevCalls';
const prevCalls = [rpcData.methods[0]];
prevCalls[0].callNo = 1;
localStore.remove(key);
localStore.set(key, prevCalls);
// when
cut.onInitApp(store, next, action);
// then
expect(store.dispatch.calledWith(syncRpcStateFromLocalStorage({
prevCalls: prevCalls,
callNo: 2,
selectedMethod: prevCalls[0]
}))).to.be.true;
expect(next.calledWith(action)).to.be.true;
});
it('should not dispatch syncRpcStateFromLocalStorage when there are no rpc calls in localStorage', () => {
// given
const store = { dispatch: sinon.spy() };
const next = sinon.spy();
const action = {};
localStore.remove('rpcPrevCalls');
// when
cut.onInitApp(store, next, action);
// then
expect(store.dispatch.notCalled).to.be.true;
expect(next.calledWith(action)).to.be.true;
});
});
it('should call unshift and next', () => {
// given
const store = { getState: () => state };
const next = sinon.spy();
const action = { payload: {} };
// when
cut.onAddRpcResponse(store, next, action);
// then
expect(cut.unshift.calledWith('rpcPrevCalls', action.payload)).to.be.true;
expect(action.payload.callNo).to.equal(1);
expect(next.calledWith(action)).to.be.true;
});
describe('UNSHIFT', () => {
// TODO [adgo] 20.04.2016 remove if/when PR is accepted: https://github.com/marcuswestin/store.js/pull/153
it('should create array in local storage by key and unshift item to it', () => {
// given
const key = 'foo';
const val = 'bar';
localStore.remove(key);
// when
cut.unshift(key, val);
// then
expect(localStore.get(key)[0]).to.equal(val);
expect(localStore.get(key).length).to.equal(1);
});
// TODO [adgo] 20.04.2016 remove if/when PR is accepted: https://github.com/marcuswestin/store.js/pull/153
it('should unshift item to an existing array in local storage by key', () => {
// given
const key = 'foo';
const val = 'bar';
const newVal = 'bazz';
localStore.remove(key);
localStore.set(key, [val]);
expect(localStore.get(key)).to.be.defined;
// when
cut.unshift(key, newVal);
// then
expect(localStore.get(key)[0]).to.equal(newVal);
expect(localStore.get(key).length).to.equal(2);
});
});
});

View File

@@ -1,66 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { handleActions } from 'redux-actions';
import { union } from 'lodash';
const initialState = {
levels: '',
logging: true,
logs: []
};
const maxLogs = 1024;
export const actionHandlers = {
'update devLogsLevels' (state, action) {
return {
...state,
levels: `${action.payload}`
};
},
'remove devLogs' (state, action) {
return {
...state,
logs: []
};
},
'update devLogging' (state, action) {
return {
...state,
logging: action.payload
};
},
'update devLogs' (state, action) {
if (!state.logging) {
return { ...state };
}
let newLogs = union(state.logs, action.payload.reverse());
return {
...state,
logs: newLogs.slice(newLogs.length - maxLogs)
};
}
};
export default handleActions(actionHandlers, initialState);

View File

@@ -1,31 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 status from './status';
import settings from './settings';
import mining from './mining';
import debug from './debug';
import rpc from './rpc';
import logger from './logger';
export {
status,
settings,
mining,
rpc,
logger,
debug
};

View File

@@ -1,32 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { handleActions } from 'redux-actions';
const isProd = process.env.NODE_ENV === 'production';
const initialState = {
logging: false && !isProd
};
export default handleActions({
'update logging' (state, action) {
return {
logging: action.payload
};
}
}, initialState);

View File

@@ -1,66 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { handleActions } from 'redux-actions';
const initialState = {
author: 'loading...',
extraData: 'loading...',
defaultExtraData: '0x01',
minGasPrice: 'loading...',
gasFloorTarget: 'loading...'
};
export const actionHandlers = {
'update author' (state, action) {
return {
...state,
author: `${action.payload}`
};
},
'update minGasPrice' (state, action) {
return {
...state,
minGasPrice: `${action.payload}`
};
},
'update gasFloorTarget' (state, action) {
return {
...state,
gasFloorTarget: `${action.payload}`
};
},
'update extraData' (state, action) {
return {
...state,
extraData: `${action.payload}`
};
},
'update defaultExtraData' (state, action) {
return {
...state,
defaultExtraData: `${action.payload}`
};
}
};
export default handleActions(actionHandlers, initialState);

View File

@@ -1,65 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { handleActions } from 'redux-actions';
import rpcMetods from '../data/rpc.json';
const initialState = {
prevCalls: [],
callNo: 1,
selectedMethod: rpcMetods.methods[0]
};
export const actionHandlers = {
'add rpcResponse' (state, action) {
const calls = [action.payload].concat(state.prevCalls);
const maxCalls = 64;
return {
...state,
callNo: state.callNo + 1,
prevCalls: calls.slice(0, maxCalls)
};
},
'sync rpcStateFromLocalStorage' (state, action) {
return {
...state,
prevCalls: action.payload.prevCalls,
callNo: action.payload.callNo,
selectedMethod: action.payload.selectedMethod
};
},
'reset rpcPrevCalls' (state, action) {
return {
...state,
callNo: 1,
prevCalls: []
};
},
'select rpcMethod' (state, action) {
return {
...state,
selectedMethod: action.payload
};
}
};
export default handleActions(actionHandlers, initialState);

View File

@@ -1,60 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { handleActions } from 'redux-actions';
const initialState = {
chain: 'loading...',
networkPort: 0,
maxPeers: 0,
rpcEnabled: false,
rpcInterface: '-',
rpcPort: 0
};
export default handleActions({
'update netChain' (state, action) {
return {
...state,
chain: action.payload
};
},
'update netPort' (state, action) {
return {
...state,
networkPort: action.payload
};
},
'update netPeers' (state, action) {
return {
...state,
maxPeers: action.payload.max
};
},
'update rpcSettings' (state, action) {
const rpc = action.payload;
return {
...state,
rpcEnabled: rpc.enabled,
rpcInterface: rpc.interface,
rpcPort: rpc.port
};
}
}, initialState);

View File

@@ -1,92 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { handleActions } from 'redux-actions';
const initialState = {
error: false,
noOfErrors: 0,
name: 'My node',
bestBlock: 'loading...',
hashrate: 'loading...',
connectedPeers: 0,
activePeers: 0,
peers: 0,
accounts: [],
version: '-'
};
export default handleActions({
error (state, action) {
return {
...state,
disconnected: (action.payload.message === 'Invalid JSON RPC response: ""'),
noOfErrors: state.noOfErrors + 1
};
},
'update blockNumber' (state, action) {
return {
...resetError(state),
bestBlock: `${action.payload}`
};
},
'update hashrate' (state, action) {
return {
...resetError(state),
hashrate: `${action.payload}`
};
},
'update netPeers' (state, action) {
return {
...state,
connectedPeers: action.payload.connected,
activePeers: action.payload.active
};
},
'update version' (state, action) {
return {
...resetError(state),
version: action.payload
};
},
'update accounts' (state, action) {
return {
...resetError(state),
accounts: action.payload
};
},
'update nodeName' (state, action) {
return {
...resetError(state),
name: action.payload || ' '
};
}
}, initialState);
function resetError (state) {
return {
...state,
disconnected: false,
noOfErrors: 0
};
}

View File

@@ -14,26 +14,30 @@
// 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 } from 'react';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Page } from '~/ui';
import StatusPage from './containers/StatusPage';
import Debug from './Debug';
import Peers from './Peers';
import Status from './Status';
export default class Status extends Component {
render () {
return (
<Page
title={
<FormattedMessage
id='status.title'
defaultMessage='Status'
/>
}
>
<StatusPage />
</Page>
);
}
}
import styles from './status.css';
export default () => (
<Page
title={
<FormattedMessage
id='status.title'
defaultMessage='Status'
/>
}
>
<div className={ styles.body }>
<Status />
<Peers />
<Debug />
</div>
</Page>
);

View File

@@ -1,30 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 function filterErrors (xs) {
return xs.filter(isError);
}
export function hasErrors (xs) {
if (!xs) {
return;
}
return !!xs.find(isError);
}
export function isError (x) {
return x instanceof Error;
}

View File

@@ -1,109 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 sinon from 'sinon';
import * as ErrorUtil from './error';
describe('views/Status/util/error', () => {
beforeEach('spy on isError', () => {
sinon.spy(ErrorUtil, 'isError');
});
afterEach('spy on isError', () => {
ErrorUtil.isError.restore();
});
describe('filterErrors', () => {
const ERROR1 = new Error('abc');
const ERROR2 = new Error('def');
const INPUT = [ERROR1, 'ghi', ERROR2, 'jkl'];
const ERRORS = [ERROR1, ERROR2];
it('should return errors in the array', () => {
expect(ErrorUtil.filterErrors(INPUT)).to.deep.equal(ERRORS);
});
});
describe('hasErrors', () => {
it('should return undefined and not invoke isError when null is passed', () => {
// given
const xs = null;
// when
const res = ErrorUtil.hasErrors(xs);
// then
expect(ErrorUtil.isError.called).to.be.false;
expect(res).to.be.undefined;
});
it('should return true and invoke isError when at least one error object is passed', () => {
// given
const arg1 = 'test string';
const arg2 = new Error();
const xs = [arg1, arg2];
// when
const res = ErrorUtil.hasErrors(xs);
// then
// todo [adgo] - 30.04.2016 - fix and uncomment
// expect(ErrorUtil.isError.calledWith(arg1)).to.be.true;
// expect(ErrorUtil.isError.calledWith(arg2)).to.be.true;
expect(res).to.be.true;
});
it('should return false and invoke isError when non error objects are passed', () => {
// given
const arg1 = 'test string';
const arg2 = 123;
const xs = [arg1, arg2];
// when
const res = ErrorUtil.hasErrors(xs);
// then
// todo [adgo] - 30.04.2016 - fix and uncomment
// expect(ErrorUtil.isError.calledWith(arg1)).to.be.true;
// expect(ErrorUtil.isError.calledWith(arg2)).to.be.true;
expect(res).to.be.false;
});
});
describe('isError', () => {
it('should return false when non error object is passed', () => {
// given
const arg = '';
// when
const res = ErrorUtil.isError(arg);
// then
expect(res).to.be.false;
});
it('should return true when error object is passed', () => {
// given
const arg = new Error();
// when
const res = ErrorUtil.isError(arg);
// then
expect(res).to.be.true;
});
});
});

View File

@@ -1,38 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 function toPromise (fn) {
return new Promise((resolve, reject) => {
fn((err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
}
export function stringifyIfObject (any) {
if (typeof any === 'object') {
any = JSON.stringify(any);
}
return any;
}
export function identity (x) {
return x;
}

View File

@@ -1,51 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { toPromise, identity } from './';
describe('views/Status/util', () => {
describe('toPromise', () => {
it('rejects on error result', () => {
const ERROR = new Error();
const FN = function (callback) {
callback(ERROR);
};
return toPromise(FN).catch(err => {
expect(err).to.equal(ERROR);
});
});
it('resolves on success result', () => {
const SUCCESS = 'ok, we are good';
const FN = function (callback) {
callback(null, SUCCESS);
};
return toPromise(FN).then(success => {
expect(success).to.equal(SUCCESS);
});
});
});
describe('identity', () => {
it('returns the value passed in', () => {
const TEST = { abc: 'def' };
expect(identity(TEST)).to.deep.equal(TEST);
});
});
});

View File

@@ -1,21 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { isValidElement } from 'react';
export function isReactComponent (componentOrElem) {
return isValidElement(componentOrElem) && typeof componentOrElem.type === 'function';
}

View File

@@ -1,27 +0,0 @@
// Copyright 2015-2017 Parity Technologies (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 { isPlainObject } from 'lodash';
export function formatRpcMd (val) {
if (!isPlainObject(val)) {
return val;
}
return val.description + Object.keys(val.details)
.map(key => `- \`${key}\`: ${val.details[key]}`)
.join('\n');
}