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

@ -17,16 +17,14 @@
import { newError } from '~/ui/Errors/actions'; import { newError } from '~/ui/Errors/actions';
import { setAddressImage } from './providers/imagesActions'; import { setAddressImage } from './providers/imagesActions';
import { openSnackbar, showSnackbar } from './providers/snackbarActions'; import { openSnackbar, showSnackbar } from './providers/snackbarActions';
import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from './providers/statusActions'; import { toggleStatusRefresh } from './providers/statusActions';
import { toggleView } from '~/views/Settings/actions'; import { toggleView } from '~/views/Settings/actions';
export { export {
newError, newError,
clearStatusLogs,
setAddressImage, setAddressImage,
openSnackbar, openSnackbar,
showSnackbar, showSnackbar,
toggleStatusLogs,
toggleStatusRefresh, toggleStatusRefresh,
toggleView toggleView
}; };

View File

@ -20,7 +20,6 @@ import ErrorsMiddleware from '~/ui/Errors/middleware';
import SettingsMiddleware from '~/views/Settings/middleware'; import SettingsMiddleware from '~/views/Settings/middleware';
import SignerMiddleware from './providers/signerMiddleware'; import SignerMiddleware from './providers/signerMiddleware';
import statusMiddleware from '~/views/Status/middleware';
import CertificationsMiddleware from './providers/certifications/middleware'; import CertificationsMiddleware from './providers/certifications/middleware';
import ChainMiddleware from './providers/chainMiddleware'; import ChainMiddleware from './providers/chainMiddleware';
import RegistryMiddleware from './providers/registry/middleware'; import RegistryMiddleware from './providers/registry/middleware';
@ -44,8 +43,7 @@ export default function (api, browserHistory, forEmbed = false) {
middleware.push(certifications, registry); middleware.push(certifications, registry);
} }
const status = statusMiddleware();
const routeMiddleware = browserHistory ? routerMiddleware(browserHistory) : []; const routeMiddleware = browserHistory ? routerMiddleware(browserHistory) : [];
return middleware.concat(status, routeMiddleware, thunk); return middleware.concat(routeMiddleware, thunk);
} }

View File

@ -20,7 +20,7 @@ import { LOG_KEYS, getLogger } from '~/config';
import UpgradeStore from '~/modals/UpgradeParity/store'; import UpgradeStore from '~/modals/UpgradeParity/store';
import BalancesProvider from './balances'; import BalancesProvider from './balances';
import { statusBlockNumber, statusCollection, statusLogs } from './statusActions'; import { statusBlockNumber, statusCollection } from './statusActions';
const log = getLogger(LOG_KEYS.Signer); const log = getLogger(LOG_KEYS.Signer);
let instance = null; let instance = null;
@ -59,6 +59,14 @@ export default class Status {
return instance; return instance;
} }
static get () {
if (!instance) {
throw new Error('The Status Provider has not been initialized yet');
}
return instance;
}
start () { start () {
log.debug('status::start'); log.debug('status::start');
@ -66,7 +74,6 @@ export default class Status {
.all([ .all([
this._subscribeBlockNumber(), this._subscribeBlockNumber(),
this._pollLogs(),
this._pollLongStatus(), this._pollLongStatus(),
this._pollStatus() this._pollStatus()
]) ])
@ -187,25 +194,12 @@ export default class Status {
return Promise.resolve(); return Promise.resolve();
} }
const { refreshStatus } = this._store.getState().nodeStatus; const statusPromises = [ this._api.eth.syncing(), this._api.parity.netPeers() ];
const statusPromises = [ this._api.eth.syncing() ];
if (refreshStatus) {
statusPromises.push(this._api.parity.netPeers());
statusPromises.push(this._api.eth.hashrate());
}
return Promise return Promise
.all(statusPromises) .all(statusPromises)
.then(([ syncing, ...statusResults ]) => { .then(([ syncing, netPeers ]) => {
const status = statusResults.length === 0 const status = { netPeers, syncing };
? { syncing }
: {
syncing,
netPeers: statusResults[0],
hashrate: statusResults[1]
};
if (!isEqual(status, this._status)) { if (!isEqual(status, this._status)) {
this._store.dispatch(statusCollection(status)); this._store.dispatch(statusCollection(status));
@ -220,39 +214,6 @@ export default class Status {
}); });
} }
/**
* 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.minGasPrice(),
this._api.parity.gasFloorTarget()
])
.then(([
coinbase, extraData, minGasPrice, gasFloorTarget
]) => {
const minerSettings = {
coinbase,
extraData,
minGasPrice,
gasFloorTarget
};
if (!isEqual(minerSettings, this._minerSettings)) {
this._store.dispatch(statusCollection(minerSettings));
this._minerSettings = minerSettings;
}
})
.catch((error) => {
console.error('_pollMinerSettings', error);
});
}
/** /**
* The data fetched here should not change * The data fetched here should not change
* unless Parity is restarted. They are thus * unless Parity is restarted. They are thus
@ -272,23 +233,16 @@ export default class Status {
this._timeoutIds.longStatus = setTimeout(() => this._pollLongStatus(), timeout); this._timeoutIds.longStatus = setTimeout(() => this._pollLongStatus(), timeout);
}; };
// Poll Miner settings just in case return Promise
const minerPromise = this._pollMinerSettings();
const mainPromise = Promise
.all([ .all([
this._api.parity.netPeers(), this._api.parity.netPeers(),
this._api.web3.clientVersion(), this._api.web3.clientVersion(),
this._api.net.version(), this._api.net.version(),
this._api.parity.defaultExtraData(),
this._api.parity.netChain(), this._api.parity.netChain(),
this._api.parity.netPort(),
this._api.parity.rpcSettings(),
this._api.parity.enode().then((enode) => enode).catch(() => '-'),
this._upgradeStore.checkUpgrade() this._upgradeStore.checkUpgrade()
]) ])
.then(([ .then(([
netPeers, clientVersion, netVersion, defaultExtraData, netChain, netPort, rpcSettings, enode, upgradeStatus netPeers, clientVersion, netVersion, netChain, upgradeStatus
]) => { ]) => {
const isTest = [ const isTest = [
'2', // morden '2', // morden
@ -299,13 +253,9 @@ export default class Status {
const longStatus = { const longStatus = {
netPeers, netPeers,
clientVersion, clientVersion,
defaultExtraData,
netChain, netChain,
netPort,
netVersion, netVersion,
rpcSettings, isTest
isTest,
enode
}; };
if (!isEqual(longStatus, this._longStatus)) { if (!isEqual(longStatus, this._longStatus)) {
@ -319,42 +269,5 @@ export default class Status {
.then(() => { .then(() => {
nextTimeout(60000); nextTimeout(60000);
}); });
return Promise.all([ minerPromise, mainPromise ]);
}
_pollLogs = () => {
const nextTimeout = (timeout = 1000) => {
if (this._timeoutIds.logs) {
clearTimeout(this._timeoutIds.logs);
}
this._timeoutIds.logs = setTimeout(this._pollLogs, timeout);
};
const { devLogsEnabled } = this._store.getState().nodeStatus;
if (!devLogsEnabled) {
nextTimeout();
return Promise.resolve();
}
return Promise
.all([
this._api.parity.devLogs(),
this._api.parity.devLogsLevels()
])
.then(([devLogs, devLogsLevels]) => {
this._store.dispatch(statusLogs({
devLogs: devLogs.slice(-1024),
devLogsLevels
}));
})
.catch((error) => {
console.error('_pollLogs', error);
})
.then(() => {
return nextTimeout();
});
} }
} }

View File

@ -27,30 +27,3 @@ export function statusCollection (collection) {
collection collection
}; };
} }
export function statusLogs (logInfo) {
return {
type: 'statusLogs',
logInfo
};
}
export function toggleStatusLogs (devLogsEnabled) {
return {
type: 'toggleStatusLogs',
devLogsEnabled
};
}
export function clearStatusLogs () {
return {
type: 'clearStatusLogs'
};
}
export function toggleStatusRefresh (refreshStatus) {
return {
type: 'toggleStatusRefresh',
refreshStatus
};
}

View File

@ -21,32 +21,20 @@ const DEFAULT_NETCHAIN = '(unknown)';
const initialState = { const initialState = {
blockNumber: new BigNumber(0), blockNumber: new BigNumber(0),
blockTimestamp: new Date(), blockTimestamp: new Date(),
devLogs: [],
devLogsLevels: null,
devLogsEnabled: false,
clientVersion: '', clientVersion: '',
coinbase: '',
defaultExtraData: '',
enode: '',
extraData: '',
gasFloorTarget: new BigNumber(0),
gasLimit: new BigNumber(0), gasLimit: new BigNumber(0),
hashrate: new BigNumber(0),
minGasPrice: new BigNumber(0),
netChain: DEFAULT_NETCHAIN, netChain: DEFAULT_NETCHAIN,
netPeers: { netPeers: {
active: new BigNumber(0), active: new BigNumber(0),
connected: new BigNumber(0), connected: new BigNumber(0),
max: new BigNumber(0) max: new BigNumber(0),
peers: []
}, },
netPort: new BigNumber(0),
netVersion: '0', netVersion: '0',
rpcSettings: {},
syncing: true, syncing: true,
isConnected: false, isConnected: false,
isConnecting: false, isConnecting: false,
isTest: undefined, isTest: undefined,
refreshStatus: false,
traceMode: undefined traceMode: undefined
}; };
@ -61,28 +49,6 @@ export default handleActions({
const { collection } = action; const { collection } = action;
return Object.assign({}, state, collection); return Object.assign({}, state, collection);
},
statusLogs (state, action) {
const { logInfo } = action;
return Object.assign({}, state, logInfo);
},
toggleStatusLogs (state, action) {
const { devLogsEnabled } = action;
return Object.assign({}, state, { devLogsEnabled });
},
clearStatusLogs (state, action) {
return Object.assign({}, state, { devLogs: [] });
},
toggleStatusRefresh (state, action) {
const { refreshStatus } = action;
return Object.assign({}, state, { refreshStatus });
} }
}, initialState); }, initialState);

View File

@ -56,10 +56,25 @@
} }
} }
.inputAddress { .copy {
position: relative; margin-right: 0.5em;
}
&:hover, *:hover { .inputAddressContainer {
display: flex;
flex-direction: row;
align-items: baseline;
position: relative;
}
.inputAddress {
flex: 1;
&:focus {
outline: none;
}
> *:hover {
cursor: text !important; cursor: text !important;
} }
} }
@ -67,10 +82,6 @@
.main { .main {
position: relative; position: relative;
left: 0; left: 0;
&:focus {
outline: none;
}
} }
.title { .title {

View File

@ -25,6 +25,7 @@ import TextFieldUnderline from 'material-ui/TextField/TextFieldUnderline';
import apiutil from '~/api/util'; import apiutil from '~/api/util';
import AccountCard from '~/ui/AccountCard'; import AccountCard from '~/ui/AccountCard';
import CopyToClipboard from '~/ui/CopyToClipboard';
import InputAddress from '~/ui/Form/InputAddress'; import InputAddress from '~/ui/Form/InputAddress';
import Loading from '~/ui/Loading'; import Loading from '~/ui/Loading';
import Portal from '~/ui/Portal'; import Portal from '~/ui/Portal';
@ -107,18 +108,8 @@ class AddressSelect extends Component {
const input = this.renderInput(); const input = this.renderInput();
const content = this.renderContent(); const content = this.renderContent();
const classes = [ styles.main ];
return ( return (
<div <div className={ styles.main }>
className={ classes.join(' ') }
onBlur={ this.handleMainBlur }
onClick={ this.handleFocus }
onFocus={ this.handleMainFocus }
onKeyDown={ this.handleInputAddresKeydown }
ref='inputAddress'
tabIndex={ 0 }
>
{ input } { input }
{ content } { content }
</div> </div>
@ -151,8 +142,37 @@ class AddressSelect extends Component {
} }
return ( return (
<div className={ styles.inputAddress }> <div className={ styles.inputAddressContainer }>
{ input } { this.renderCopyButton() }
<div
className={ styles.inputAddress }
onBlur={ this.handleMainBlur }
onClick={ this.handleFocus }
onFocus={ this.handleMainFocus }
onKeyDown={ this.handleInputAddresKeydown }
ref='inputAddress'
tabIndex={ 0 }
>
{ input }
</div>
</div>
);
}
renderCopyButton () {
const { allowCopy, value } = this.props;
if (!allowCopy) {
return null;
}
const text = typeof allowCopy === 'string'
? allowCopy
: value.toString();
return (
<div className={ styles.copy }>
<CopyToClipboard data={ text } />
</div> </div>
); );
} }

View File

@ -59,11 +59,15 @@ export default class Input extends Component {
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
children: PropTypes.node, children: PropTypes.node,
className: PropTypes.string, className: PropTypes.string,
defaultValue: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
error: nodeOrStringProptype(), error: nodeOrStringProptype(),
escape: PropTypes.oneOf([
'default',
'initial'
]),
focused: PropTypes.bool, focused: PropTypes.bool,
readOnly: PropTypes.bool, readOnly: PropTypes.bool,
floatCopy: PropTypes.bool,
hint: nodeOrStringProptype(), hint: nodeOrStringProptype(),
hideUnderline: PropTypes.bool, hideUnderline: PropTypes.bool,
label: nodeOrStringProptype(), label: nodeOrStringProptype(),
@ -92,8 +96,8 @@ export default class Input extends Component {
static defaultProps = { static defaultProps = {
allowCopy: false, allowCopy: false,
escape: 'initial',
hideUnderline: false, hideUnderline: false,
floatCopy: false,
onBlur: noop, onBlur: noop,
onFocus: noop, onFocus: noop,
onChange: noop, onChange: noop,
@ -124,8 +128,8 @@ export default class Input extends Component {
render () { render () {
const { value } = this.state; const { value } = this.state;
const { autoFocus, children, className, hideUnderline, disabled, error, focused, label } = this.props; const { autoFocus, children, className, defaultValue, hideUnderline, disabled, error } = this.props;
const { hint, onClick, multiLine, rows, type, min, max, step, style, tabIndex } = this.props; const { focused, label, hint, onClick, multiLine, rows, type, min, max, step, style, tabIndex } = this.props;
const readOnly = this.props.readOnly || disabled; const readOnly = this.props.readOnly || disabled;
@ -159,6 +163,7 @@ export default class Input extends Component {
autoComplete='off' autoComplete='off'
autoFocus={ autoFocus } autoFocus={ autoFocus }
className={ className } className={ className }
defaultValue={ defaultValue }
errorText={ error } errorText={ error }
floatingLabelFixed floatingLabelFixed
floatingLabelText={ label } floatingLabelText={ label }
@ -275,14 +280,22 @@ export default class Input extends Component {
* if we only want to revert to initial value * if we only want to revert to initial value
*/ */
onKeyUp = (event) => { onKeyUp = (event) => {
const { escape } = this.props;
const codeName = keycode(event); const codeName = keycode(event);
if (codeName === 'esc' && !this.pressedEsc && this.intialValue !== undefined) { if (codeName === 'esc' && !this.pressedEsc) {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
this.pressedEsc = true; this.pressedEsc = true;
this.onChange(event, this.intialValue);
if (escape === 'initial' && this.intialValue !== undefined) {
return this.onChange(event, this.intialValue);
}
if (escape === 'default' && this.props.defaultValue !== undefined) {
return this.onSubmit(this.props.defaultValue);
}
} else if (this.pressedEsc) { } else if (this.pressedEsc) {
this.pressedEsc = false; this.pressedEsc = false;
} }

View File

@ -16,5 +16,4 @@
import { createAction } from 'redux-actions'; import { createAction } from 'redux-actions';
export const error = createAction('error');
export const syncRpcStateFromLocalStorage = createAction('sync rpcStateFromLocalStorage'); export const syncRpcStateFromLocalStorage = createAction('sync rpcStateFromLocalStorage');

View File

@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { createAction } from 'redux-actions'; import rpc from './rpc';
import logger from './logger';
export const initAppAction = createAction('init app'); export {
rpc,
logger
};

View File

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

View File

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

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

View File

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

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,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,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
};
}

Some files were not shown because too many files have changed in this diff Show More