Allow editing of gasPrice & gas in Signer (#3777)
* Rework gas display (maintainable) * Move GasPriceSelector to ui * Allow opening of gas component (WIP) * Merge * Consistency * Adjust for Signer display * Set maximum height based on screen size * Gas editor displays in-place * Cleanups * Merge * Style fixes * Fixup stash mishap (again) * Add store test * Allow edited values to refrect on the display * Fix properties * Adjust styling to show different rows * git mv * git mv * Style fixes * Style updates * Pass gas & gasPrice with confirmation * Fix build (case) * Style fixes * Basic GasPriceEditor smoketest * manual move 1 * manual move 2 * manual move 1 * manual move 2 * NODE_ENV=test ace fix * UI smoketests * Style * Format options via formatter * Initial version * Re-add even/odd class * re-add gasLimit to embedded passing * style * Updated for passing gas & price to store * Allow gas/price overrides when none available * Fix slider value, pass as number
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
border-radius: 4px 4px 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 19em;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.expanded .content {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { addressLink } from '~/3rdparty/etherscan/links';
|
||||
import styles from './AccountLink.css';
|
||||
import styles from './accountLink.css';
|
||||
|
||||
export default class AccountLink extends Component {
|
||||
static propTypes = {
|
||||
@@ -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 './AccountLink';
|
||||
export default from './accountLink';
|
||||
|
||||
@@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { IdentityIcon, IdentityName } from '~/ui';
|
||||
import AccountLink from './AccountLink';
|
||||
|
||||
import styles from './Account.css';
|
||||
import styles from './account.css';
|
||||
|
||||
export default class Account extends Component {
|
||||
static propTypes = {
|
||||
@@ -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 './Account';
|
||||
export default from './account';
|
||||
|
||||
@@ -21,21 +21,26 @@ import SignRequest from '../SignRequest';
|
||||
|
||||
export default class RequestPending extends Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
date: PropTypes.instanceOf(Date).isRequired,
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
id: PropTypes.object.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onReject: PropTypes.func.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
date: PropTypes.instanceOf(Date).isRequired,
|
||||
payload: PropTypes.oneOfType([
|
||||
PropTypes.shape({ signTransaction: PropTypes.object.isRequired }),
|
||||
PropTypes.shape({ sendTransaction: PropTypes.object.isRequired }),
|
||||
PropTypes.shape({ sign: PropTypes.object.isRequired })
|
||||
PropTypes.shape({ sign: PropTypes.object.isRequired }),
|
||||
PropTypes.shape({ signTransaction: PropTypes.object.isRequired })
|
||||
]).isRequired,
|
||||
className: PropTypes.string,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isSending: false
|
||||
};
|
||||
|
||||
onConfirm = data => {
|
||||
const { onConfirm, payload } = this.props;
|
||||
|
||||
@@ -44,24 +49,23 @@ export default class RequestPending extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { payload, id, className, isSending, date, onReject, isTest, store } = this.props;
|
||||
const { className, date, gasLimit, id, isSending, isTest, onReject, payload, store } = this.props;
|
||||
|
||||
if (payload.sign) {
|
||||
const { sign } = payload;
|
||||
|
||||
return (
|
||||
<SignRequest
|
||||
address={ sign.address }
|
||||
className={ className }
|
||||
hash={ sign.hash }
|
||||
id={ id }
|
||||
isFinished={ false }
|
||||
isSending={ isSending }
|
||||
isTest={ isTest }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ onReject }
|
||||
isSending={ isSending }
|
||||
isFinished={ false }
|
||||
id={ id }
|
||||
address={ sign.address }
|
||||
hash={ sign.hash }
|
||||
isTest={ isTest }
|
||||
store={ store }
|
||||
/>
|
||||
store={ store } />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,19 +74,19 @@ export default class RequestPending extends Component {
|
||||
return (
|
||||
<TransactionPending
|
||||
className={ className }
|
||||
date={ date }
|
||||
gasLimit={ gasLimit }
|
||||
id={ id }
|
||||
isSending={ isSending }
|
||||
isTest={ isTest }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ onReject }
|
||||
isSending={ isSending }
|
||||
id={ id }
|
||||
transaction={ transaction }
|
||||
date={ date }
|
||||
isTest={ isTest }
|
||||
store={ store }
|
||||
/>
|
||||
transaction={ transaction } />
|
||||
);
|
||||
}
|
||||
|
||||
// Unknown payload
|
||||
console.error('RequestPending: Unknown payload', payload);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 './SignRequest';
|
||||
export default from './signRequest';
|
||||
|
||||
@@ -21,7 +21,7 @@ import Account from '../Account';
|
||||
import TransactionPendingForm from '../TransactionPendingForm';
|
||||
import TxHashLink from '../TxHashLink';
|
||||
|
||||
import styles from './SignRequest.css';
|
||||
import styles from './signRequest.css';
|
||||
|
||||
@observer
|
||||
export default class SignRequest extends Component {
|
||||
@@ -49,7 +49,7 @@ export default class SignRequest extends Component {
|
||||
const { className } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className || ''}` }>
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
{ this.renderDetails() }
|
||||
{ this.renderActions() }
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import SignRequest from './';
|
||||
|
||||
const store = {
|
||||
balances: {},
|
||||
fetchBalance: sinon.stub()
|
||||
};
|
||||
|
||||
describe('views/Signer/components/SignRequest', () => {
|
||||
it('renders', () => {
|
||||
expect(shallow(
|
||||
<SignRequest store={ store } />,
|
||||
)).to.be.ok;
|
||||
});
|
||||
});
|
||||
@@ -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 './TransactionMainDetails';
|
||||
export default from './transactionMainDetails';
|
||||
|
||||
@@ -17,20 +17,25 @@
|
||||
|
||||
@import '../../_layout.css';
|
||||
|
||||
.transaction {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.transaction > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.account {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contractIcon {
|
||||
background: #eee;
|
||||
width: 50px !important;
|
||||
height: 50px !important;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
padding: 13px;
|
||||
}
|
||||
|
||||
.editButtonRow {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.from {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
vertical-align: top;
|
||||
|
||||
@@ -47,6 +52,7 @@
|
||||
}
|
||||
|
||||
.method {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
vertical-align: top;
|
||||
line-height: 1em;
|
||||
@@ -66,11 +72,7 @@
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.contractIcon {
|
||||
background: #eee;
|
||||
width: 50px !important;
|
||||
height: 50px !important;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
padding: 13px;
|
||||
.transaction {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -14,52 +14,43 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import MapsLocalGasStation from 'material-ui/svg-icons/maps/local-gas-station';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import { MethodDecoding } from '~/ui';
|
||||
import { Button, MethodDecoding } from '~/ui';
|
||||
|
||||
import * as tUtil from '../util/transaction';
|
||||
import Account from '../Account';
|
||||
import styles from './TransactionMainDetails.css';
|
||||
import styles from './transactionMainDetails.css';
|
||||
|
||||
export default class TransactionMainDetails extends Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.object.isRequired,
|
||||
children: PropTypes.node,
|
||||
from: PropTypes.string.isRequired,
|
||||
fromBalance: PropTypes.object, // eth BigNumber, not required since it might take time to fetch
|
||||
value: PropTypes.object.isRequired, // wei hex
|
||||
totalValue: PropTypes.object.isRequired, // wei BigNumber
|
||||
fromBalance: PropTypes.object,
|
||||
gasStore: PropTypes.object,
|
||||
id: PropTypes.object.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
totalValue: PropTypes.object.isRequired,
|
||||
transaction: PropTypes.object.isRequired,
|
||||
children: PropTypes.node
|
||||
value: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { value, totalValue } = this.props;
|
||||
const { totalValue, value } = this.props;
|
||||
|
||||
this.updateDisplayValues(value, totalValue);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { value, totalValue } = nextProps;
|
||||
const { totalValue, value } = nextProps;
|
||||
|
||||
this.updateDisplayValues(value, totalValue);
|
||||
}
|
||||
|
||||
updateDisplayValues (value, totalValue) {
|
||||
this.setState({
|
||||
feeEth: tUtil.calcFeeInEth(totalValue, value),
|
||||
valueDisplay: tUtil.getValueDisplay(value),
|
||||
valueDisplayWei: tUtil.getValueDisplayWei(value),
|
||||
totalValueDisplay: tUtil.getTotalValueDisplay(totalValue),
|
||||
totalValueDisplayWei: tUtil.getTotalValueDisplayWei(totalValue)
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, from, fromBalance, transaction, isTest } = this.props;
|
||||
const { children, from, fromBalance, gasStore, isTest, transaction } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.transaction }>
|
||||
@@ -74,29 +65,74 @@ export default class TransactionMainDetails extends Component {
|
||||
<div className={ styles.method }>
|
||||
<MethodDecoding
|
||||
address={ from }
|
||||
transaction={ transaction }
|
||||
historic={ false } />
|
||||
historic={ false }
|
||||
transaction={
|
||||
gasStore
|
||||
? gasStore.overrideTransaction(transaction)
|
||||
: transaction
|
||||
} />
|
||||
{ this.renderEditGas() }
|
||||
</div>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEditGas () {
|
||||
const { gasStore } = this.props;
|
||||
|
||||
if (!gasStore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.editButtonRow }>
|
||||
<Button
|
||||
icon={ <MapsLocalGasStation /> }
|
||||
label='Edit gas/gasPrice'
|
||||
onClick={ this.toggleGasEditor } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTotalValue () {
|
||||
const { id } = this.props;
|
||||
const { feeEth, totalValueDisplay, totalValueDisplayWei } = this.state;
|
||||
const labelId = `totalValue${id}`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={ styles.total }
|
||||
data-effect='solid'
|
||||
data-for={ labelId }
|
||||
data-place='bottom'
|
||||
data-tip>
|
||||
{ totalValueDisplay } <small>ETH</small>
|
||||
</div>
|
||||
<ReactTooltip id={ labelId }>
|
||||
The value of the transaction including the mining fee is <strong>{ totalValueDisplayWei }</strong> <small>WEI</small>. <br />
|
||||
(This includes a mining fee of <strong>{ feeEth }</strong> <small>ETH</small>)
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderValue () {
|
||||
const { id } = this.props;
|
||||
const { valueDisplay, valueDisplayWei } = this.state;
|
||||
const labelId = `value${id}`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
data-tip
|
||||
data-for={ 'value' + id }
|
||||
data-effect='solid'
|
||||
>
|
||||
data-for={ labelId }
|
||||
data-tip>
|
||||
<strong>{ valueDisplay } </strong>
|
||||
<small>ETH</small>
|
||||
</div>
|
||||
<ReactTooltip id={ 'value' + id }>
|
||||
<ReactTooltip id={ labelId }>
|
||||
The value of the transaction.<br />
|
||||
<strong>{ valueDisplayWei }</strong> <small>WEI</small>
|
||||
</ReactTooltip>
|
||||
@@ -104,25 +140,17 @@ export default class TransactionMainDetails extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderTotalValue () {
|
||||
const { id } = this.props;
|
||||
const { totalValueDisplay, totalValueDisplayWei, feeEth } = this.state;
|
||||
updateDisplayValues (value, totalValue) {
|
||||
this.setState({
|
||||
feeEth: tUtil.calcFeeInEth(totalValue, value),
|
||||
totalValueDisplay: tUtil.getTotalValueDisplay(totalValue),
|
||||
totalValueDisplayWei: tUtil.getTotalValueDisplayWei(totalValue),
|
||||
valueDisplay: tUtil.getValueDisplay(value),
|
||||
valueDisplayWei: tUtil.getValueDisplayWei(value)
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
data-tip
|
||||
data-for={ 'totalValue' + id }
|
||||
data-effect='solid'
|
||||
data-place='bottom'
|
||||
className={ styles.total }>
|
||||
{ totalValueDisplay } <small>ETH</small>
|
||||
</div>
|
||||
<ReactTooltip id={ 'totalValue' + id }>
|
||||
The value of the transaction including the mining fee is <strong>{ totalValueDisplayWei }</strong> <small>WEI</small>. <br />
|
||||
(This includes a mining fee of <strong>{ feeEth }</strong> <small>ETH</small>)
|
||||
</ReactTooltip>
|
||||
</div>
|
||||
);
|
||||
toggleGasEditor = () => {
|
||||
this.props.gasStore.setEditing(true);
|
||||
}
|
||||
}
|
||||
@@ -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 './TransactionPending';
|
||||
export default from './transactionPending';
|
||||
|
||||
@@ -19,13 +19,9 @@
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
padding: 1em 0 1em;
|
||||
padding: 1.5em 1em 1.5em 0;
|
||||
|
||||
& > * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.container+.container {
|
||||
padding-top: 2em;
|
||||
}
|
||||
@@ -17,89 +17,130 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import { Button, GasPriceEditor } from '~/ui';
|
||||
|
||||
import TransactionMainDetails from '../TransactionMainDetails';
|
||||
import TransactionPendingForm from '../TransactionPendingForm';
|
||||
|
||||
import styles from './TransactionPending.css';
|
||||
import styles from './transactionPending.css';
|
||||
|
||||
import * as tUtil from '../util/transaction';
|
||||
|
||||
@observer
|
||||
export default class TransactionPending extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
id: PropTypes.object.isRequired,
|
||||
transaction: PropTypes.shape({
|
||||
from: PropTypes.string.isRequired,
|
||||
value: PropTypes.object.isRequired, // wei hex
|
||||
gasPrice: PropTypes.object.isRequired, // wei hex
|
||||
gas: PropTypes.object.isRequired, // hex
|
||||
data: PropTypes.string, // hex
|
||||
to: PropTypes.string // undefined if it's a contract
|
||||
}).isRequired,
|
||||
className: PropTypes.string,
|
||||
date: PropTypes.instanceOf(Date).isRequired,
|
||||
gasLimit: PropTypes.object,
|
||||
id: PropTypes.object.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
nonce: PropTypes.number,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onReject: PropTypes.func.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
className: PropTypes.string,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
store: PropTypes.object.isRequired,
|
||||
transaction: PropTypes.shape({
|
||||
data: PropTypes.string,
|
||||
from: PropTypes.string.isRequired,
|
||||
gas: PropTypes.object.isRequired,
|
||||
gasPrice: PropTypes.object.isRequired,
|
||||
to: PropTypes.string,
|
||||
value: PropTypes.object.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isSending: false
|
||||
};
|
||||
gasStore = new GasPriceEditor.Store(this.context.api, {
|
||||
gas: this.props.transaction.gas.toFixed(),
|
||||
gasLimit: this.props.gasLimit,
|
||||
gasPrice: this.props.transaction.gasPrice.toFixed()
|
||||
});
|
||||
|
||||
componentWillMount () {
|
||||
const { transaction, store } = this.props;
|
||||
const { gas, gasPrice, value, from, to } = transaction;
|
||||
const { store, transaction } = this.props;
|
||||
const { from, gas, gasPrice, to, value } = transaction;
|
||||
|
||||
const fee = tUtil.getFee(gas, gasPrice); // BigNumber object
|
||||
const totalValue = tUtil.getTotalValue(fee, value);
|
||||
const gasPriceEthmDisplay = tUtil.getEthmFromWeiDisplay(gasPrice);
|
||||
const gasToDisplay = tUtil.getGasDisplay(gas);
|
||||
const totalValue = tUtil.getTotalValue(fee, value);
|
||||
|
||||
this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay });
|
||||
this.gasStore.setEthValue(value);
|
||||
store.fetchBalances([from, to]);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, id, transaction, store, isTest } = this.props;
|
||||
const { from, value } = transaction;
|
||||
return this.gasStore.isEditing
|
||||
? this.renderGasEditor()
|
||||
: this.renderTransaction();
|
||||
}
|
||||
|
||||
renderTransaction () {
|
||||
const { className, id, isSending, isTest, store, transaction } = this.props;
|
||||
const { totalValue } = this.state;
|
||||
const { from, value } = transaction;
|
||||
|
||||
const fromBalance = store.balances[from];
|
||||
|
||||
return (
|
||||
<div className={ `${styles.container} ${className || ''}` }>
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
<TransactionMainDetails
|
||||
id={ id }
|
||||
value={ value }
|
||||
from={ from }
|
||||
isTest={ isTest }
|
||||
fromBalance={ fromBalance }
|
||||
className={ styles.transactionDetails }
|
||||
from={ from }
|
||||
fromBalance={ fromBalance }
|
||||
gasStore={ this.gasStore }
|
||||
id={ id }
|
||||
isTest={ isTest }
|
||||
totalValue={ totalValue }
|
||||
transaction={ transaction }
|
||||
totalValue={ totalValue } />
|
||||
value={ value } />
|
||||
<TransactionPendingForm
|
||||
address={ from }
|
||||
isSending={ this.props.isSending }
|
||||
isSending={ isSending }
|
||||
onConfirm={ this.onConfirm }
|
||||
onReject={ this.onReject }
|
||||
/>
|
||||
onReject={ this.onReject } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onConfirm = data => {
|
||||
const { id, transaction } = this.props;
|
||||
const { gasPrice } = transaction;
|
||||
const { password, wallet } = data;
|
||||
renderGasEditor () {
|
||||
const { className } = this.props;
|
||||
|
||||
this.props.onConfirm({ id, password, wallet, gasPrice });
|
||||
return (
|
||||
<div className={ `${styles.container} ${className}` }>
|
||||
<GasPriceEditor
|
||||
store={ this.gasStore }>
|
||||
<Button
|
||||
label='view transaction'
|
||||
onClick={ this.toggleGasEditor } />
|
||||
</GasPriceEditor>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onConfirm = (data) => {
|
||||
const { id, transaction } = this.props;
|
||||
const { password, wallet } = data;
|
||||
const { gas, gasPrice } = this.gasStore.overrideTransaction(transaction);
|
||||
|
||||
this.props.onConfirm({
|
||||
gas,
|
||||
gasPrice,
|
||||
id,
|
||||
password,
|
||||
wallet
|
||||
});
|
||||
}
|
||||
|
||||
onReject = () => {
|
||||
this.props.onReject(this.props.id);
|
||||
}
|
||||
|
||||
toggleGasEditor = () => {
|
||||
this.gasStore.setEditing(false);
|
||||
}
|
||||
}
|
||||
@@ -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 './TransactionPendingFormConfirm';
|
||||
export default from './transactionPendingFormConfirm';
|
||||
|
||||
@@ -22,7 +22,7 @@ import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import { Form, Input, IdentityIcon } from '~/ui';
|
||||
|
||||
import styles from './TransactionPendingFormConfirm.css';
|
||||
import styles from './transactionPendingFormConfirm.css';
|
||||
|
||||
class TransactionPendingFormConfirm extends Component {
|
||||
static propTypes = {
|
||||
@@ -35,14 +35,14 @@ class TransactionPendingFormConfirm extends Component {
|
||||
id = Math.random(); // for tooltip
|
||||
|
||||
state = {
|
||||
walletError: null,
|
||||
password: '',
|
||||
wallet: null,
|
||||
password: ''
|
||||
walletError: null
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, address, isSending } = this.props;
|
||||
const { password, walletError, wallet } = this.state;
|
||||
const { password, wallet, walletError } = this.state;
|
||||
const account = accounts[address] || {};
|
||||
const isExternal = !account.uuid;
|
||||
|
||||
@@ -51,37 +51,54 @@ class TransactionPendingFormConfirm extends Component {
|
||||
: null;
|
||||
|
||||
const isWalletOk = !isExternal || (walletError === null && wallet !== null);
|
||||
const keyInput = isExternal ? this.renderKeyInput() : null;
|
||||
const keyInput = isExternal
|
||||
? this.renderKeyInput()
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={ styles.confirmForm }>
|
||||
<Form>
|
||||
{ keyInput }
|
||||
<Input
|
||||
hint={
|
||||
isExternal
|
||||
? 'decrypt the key'
|
||||
: 'unlock the account'
|
||||
}
|
||||
label={
|
||||
isExternal
|
||||
? 'Key Password'
|
||||
: 'Account Password'
|
||||
}
|
||||
onChange={ this.onModifyPassword }
|
||||
onKeyDown={ this.onKeyDown }
|
||||
label={ isExternal ? 'Key Password' : 'Account Password' }
|
||||
hint={ isExternal ? 'decrypt the key' : 'unlock the account' }
|
||||
type='password'
|
||||
value={ password } />
|
||||
<div className={ styles.passwordHint }>
|
||||
{ passwordHint }
|
||||
</div>
|
||||
<div
|
||||
data-tip
|
||||
data-place='bottom'
|
||||
data-for={ 'transactionConfirmForm' + this.id }
|
||||
data-effect='solid'
|
||||
>
|
||||
data-for={ `transactionConfirmForm${this.id}` }
|
||||
data-place='bottom'
|
||||
data-tip>
|
||||
<RaisedButton
|
||||
onTouchTap={ this.onConfirm }
|
||||
className={ styles.confirmButton }
|
||||
fullWidth
|
||||
primary
|
||||
disabled={ isSending || !isWalletOk }
|
||||
icon={ <IdentityIcon address={ address } button className={ styles.signerIcon } /> }
|
||||
label={ isSending ? 'Confirming...' : 'Confirm Transaction' }
|
||||
/>
|
||||
fullWidth
|
||||
icon={
|
||||
<IdentityIcon
|
||||
address={ address }
|
||||
button
|
||||
className={ styles.signerIcon } />
|
||||
}
|
||||
label={
|
||||
isSending
|
||||
? 'Confirming...'
|
||||
: 'Confirm Transaction'
|
||||
}
|
||||
onTouchTap={ this.onConfirm }
|
||||
primary />
|
||||
</div>
|
||||
{ this.renderTooltip() }
|
||||
</Form>
|
||||
@@ -95,11 +112,10 @@ class TransactionPendingFormConfirm extends Component {
|
||||
return (
|
||||
<Input
|
||||
className={ styles.fileInput }
|
||||
onChange={ this.onKeySelect }
|
||||
error={ walletError }
|
||||
label='Select Local Key'
|
||||
type='file'
|
||||
/>
|
||||
onChange={ this.onKeySelect }
|
||||
type='file' />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,34 +125,37 @@ class TransactionPendingFormConfirm extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactTooltip id={ 'transactionConfirmForm' + this.id }>
|
||||
<ReactTooltip id={ `transactionConfirmForm${this.id}` }>
|
||||
Please provide a password for this account
|
||||
</ReactTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
onKeySelect = evt => {
|
||||
onKeySelect = (event) => {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = e => {
|
||||
|
||||
fileReader.onload = (e) => {
|
||||
try {
|
||||
const wallet = JSON.parse(e.target.result);
|
||||
|
||||
this.setState({
|
||||
walletError: null,
|
||||
wallet: wallet
|
||||
wallet,
|
||||
walletError: null
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
this.setState({
|
||||
walletError: 'Given wallet file is invalid.',
|
||||
wallet: null
|
||||
wallet: null,
|
||||
walletError: 'Given wallet file is invalid.'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsText(evt.target.files[0]);
|
||||
fileReader.readAsText(event.target.files[0]);
|
||||
}
|
||||
|
||||
onModifyPassword = evt => {
|
||||
const password = evt.target.value;
|
||||
onModifyPassword = (event) => {
|
||||
const password = event.target.value;
|
||||
|
||||
this.setState({
|
||||
password
|
||||
});
|
||||
@@ -150,8 +169,8 @@ class TransactionPendingFormConfirm extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
onKeyDown = evt => {
|
||||
if (evt.which !== 13) {
|
||||
onKeyDown = (event) => {
|
||||
if (event.which !== 13) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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 './TransactionPendingFormReject';
|
||||
export default from './transactionPendingFormReject';
|
||||
|
||||
@@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
|
||||
import styles from './TransactionPendingFormReject.css';
|
||||
import styles from './transactionPendingFormReject.css';
|
||||
|
||||
export default class TransactionPendingFormReject extends Component {
|
||||
|
||||
@@ -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 './TransactionPendingForm';
|
||||
export default from './transactionPendingForm';
|
||||
|
||||
@@ -20,10 +20,9 @@ import BackIcon from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
|
||||
import TransactionPendingFormConfirm from './TransactionPendingFormConfirm';
|
||||
import TransactionPendingFormReject from './TransactionPendingFormReject';
|
||||
import styles from './TransactionPendingForm.css';
|
||||
import styles from './transactionPendingForm.css';
|
||||
|
||||
export default class TransactionPendingForm extends Component {
|
||||
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
isSending: PropTypes.bool.isRequired,
|
||||
@@ -60,8 +59,8 @@ export default class TransactionPendingForm extends Component {
|
||||
return (
|
||||
<TransactionPendingFormConfirm
|
||||
address={ address }
|
||||
onConfirm={ onConfirm }
|
||||
isSending={ isSending } />
|
||||
isSending={ isSending }
|
||||
onConfirm={ onConfirm } />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,14 +71,13 @@ export default class TransactionPendingForm extends Component {
|
||||
if (!isRejectOpen) {
|
||||
html = <span>reject transaction</span>;
|
||||
} else {
|
||||
html = <span><BackIcon />I've changed my mind</span>;
|
||||
html = <span><BackIcon />{ "I've changed my mind" }</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
onClick={ this.onToggleReject }
|
||||
className={ styles.rejectToggle }
|
||||
>
|
||||
onClick={ this.onToggleReject }>
|
||||
{ html }
|
||||
</a>
|
||||
);
|
||||
@@ -87,7 +85,9 @@ export default class TransactionPendingForm extends Component {
|
||||
|
||||
onToggleReject = () => {
|
||||
const { isRejectOpen } = this.state;
|
||||
this.setState({ isRejectOpen: !isRejectOpen });
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isRejectOpen: !isRejectOpen
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 './TxHashLink';
|
||||
export default from './txHashLink';
|
||||
|
||||
@@ -19,22 +19,21 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { txLink } from '~/3rdparty/etherscan/links';
|
||||
|
||||
export default class TxHashLink extends Component {
|
||||
|
||||
static propTypes = {
|
||||
txHash: PropTypes.string.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string
|
||||
className: PropTypes.string,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
txHash: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, txHash, className, isTest } = this.props;
|
||||
const { children, className, isTest, txHash } = this.props;
|
||||
|
||||
return (
|
||||
<a
|
||||
className={ className }
|
||||
href={ txLink(txHash, isTest) }
|
||||
target='_blank'
|
||||
className={ className }>
|
||||
target='_blank'>
|
||||
{ children || txHash }
|
||||
</a>
|
||||
);
|
||||
@@ -17,16 +17,22 @@
|
||||
|
||||
@import '../../_layout.css';
|
||||
|
||||
.signer {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
width: $embedWidth;
|
||||
.info {
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
.none {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 1em 0;
|
||||
.request {
|
||||
&:nth-child(even) {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
.signer {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
width: $embedWidth;
|
||||
}
|
||||
|
||||
@@ -33,15 +33,16 @@ class Embedded extends Component {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
signer: PropTypes.shape({
|
||||
pending: PropTypes.array.isRequired,
|
||||
finished: PropTypes.array.isRequired
|
||||
}).isRequired,
|
||||
actions: PropTypes.shape({
|
||||
startConfirmRequest: PropTypes.func.isRequired,
|
||||
startRejectRequest: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
isTest: PropTypes.bool.isRequired
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
signer: PropTypes.shape({
|
||||
finished: PropTypes.array.isRequired,
|
||||
pending: PropTypes.array.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
store = new Store(this.context.api);
|
||||
@@ -78,20 +79,21 @@ class Embedded extends Component {
|
||||
}
|
||||
|
||||
renderPending = (data) => {
|
||||
const { actions, isTest } = this.props;
|
||||
const { payload, id, isSending, date } = data;
|
||||
const { actions, gasLimit, isTest } = this.props;
|
||||
const { date, id, isSending, payload } = data;
|
||||
|
||||
return (
|
||||
<RequestPending
|
||||
className={ styles.request }
|
||||
date={ date }
|
||||
gasLimit={ gasLimit }
|
||||
id={ id }
|
||||
isSending={ isSending }
|
||||
isTest={ isTest }
|
||||
key={ id }
|
||||
onConfirm={ actions.startConfirmRequest }
|
||||
onReject={ actions.startRejectRequest }
|
||||
isSending={ isSending || false }
|
||||
key={ id }
|
||||
id={ id }
|
||||
payload={ payload }
|
||||
date={ date }
|
||||
isTest={ isTest }
|
||||
store={ this.store }
|
||||
/>
|
||||
);
|
||||
@@ -103,13 +105,14 @@ class Embedded extends Component {
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { isTest } = state.nodeStatus;
|
||||
const { gasLimit, isTest } = state.nodeStatus;
|
||||
const { actions, signer } = state;
|
||||
|
||||
return {
|
||||
actions,
|
||||
signer,
|
||||
isTest
|
||||
gasLimit,
|
||||
isTest,
|
||||
signer
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 './RequestsPage';
|
||||
export default from './requestsPage';
|
||||
|
||||
@@ -18,3 +18,9 @@
|
||||
.noRequestsMsg {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.request {
|
||||
&:nth-child(odd) {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import { Container, Page, TxList } from '~/ui';
|
||||
|
||||
import RequestPending from '../../components/RequestPending';
|
||||
|
||||
import styles from './RequestsPage.css';
|
||||
import styles from './requestsPage.css';
|
||||
|
||||
@observer
|
||||
class RequestsPage extends Component {
|
||||
@@ -35,15 +35,16 @@ class RequestsPage extends Component {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
signer: PropTypes.shape({
|
||||
pending: PropTypes.array.isRequired,
|
||||
finished: PropTypes.array.isRequired
|
||||
}).isRequired,
|
||||
actions: PropTypes.shape({
|
||||
startConfirmRequest: PropTypes.func.isRequired,
|
||||
startRejectRequest: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
isTest: PropTypes.bool.isRequired
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
signer: PropTypes.shape({
|
||||
pending: PropTypes.array.isRequired,
|
||||
finished: PropTypes.array.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
store = new Store(this.context.api, true);
|
||||
@@ -104,19 +105,21 @@ class RequestsPage extends Component {
|
||||
}
|
||||
|
||||
renderPending = (data) => {
|
||||
const { actions, isTest } = this.props;
|
||||
const { payload, id, isSending, date } = data;
|
||||
const { actions, gasLimit, isTest } = this.props;
|
||||
const { date, id, isSending, payload } = data;
|
||||
|
||||
return (
|
||||
<RequestPending
|
||||
className={ styles.request }
|
||||
date={ date }
|
||||
gasLimit={ gasLimit }
|
||||
id={ id }
|
||||
isSending={ isSending }
|
||||
isTest={ isTest }
|
||||
key={ id }
|
||||
onConfirm={ actions.startConfirmRequest }
|
||||
onReject={ actions.startRejectRequest }
|
||||
isSending={ isSending || false }
|
||||
key={ id }
|
||||
id={ id }
|
||||
payload={ payload }
|
||||
date={ date }
|
||||
isTest={ isTest }
|
||||
store={ this.store }
|
||||
/>
|
||||
);
|
||||
@@ -124,13 +127,14 @@ class RequestsPage extends Component {
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { isTest } = state.nodeStatus;
|
||||
const { gasLimit, isTest } = state.nodeStatus;
|
||||
const { actions, signer } = state;
|
||||
|
||||
return {
|
||||
actions,
|
||||
signer,
|
||||
isTest
|
||||
gasLimit,
|
||||
isTest,
|
||||
signer
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user