James Prado 0d5d0cea9a Wallet Loading States & Spinner Update (#334)
* Add disclaimer modal to footer

* Remove duplicate code & unnecessary styles

* Fix formatting noise

* remove un-used css style

* Fix tslint error & add media query for modals

* Nest Media Query

* Replace '???' with Spinner & update spinner

* Add loading states for wallet balances

* Update wallet test

* Remove excess data passed to wallet balance reducer & Fix wallet balance types

* Merge 'develop' into 'loading-indicator'

* Add 'light' prop to Spinner

* Only show spinners when fetching data

* Remove format diff

* Apply naming conventions

* Remove network name when offline
2017-11-14 21:44:55 -06:00

243 lines
7.1 KiB
TypeScript

import Identicon from 'components/ui/Identicon';
import Modal, { IButton } from 'components/ui/Modal';
import Spinner from 'components/ui/Spinner';
import { NetworkConfig, NodeConfig } from 'config/data';
import EthTx from 'ethereumjs-tx';
import {
BroadcastTransactionStatus,
getTransactionFields,
decodeTransaction
} from 'libs/transaction';
import React from 'react';
import { connect } from 'react-redux';
import {
getLanguageSelection,
getNetworkConfig,
getNodeConfig
} from 'selectors/config';
import { getTokens, getTxFromState, MergedToken } from 'selectors/wallet';
import translate, { translateRaw } from 'translations';
import { UnitDisplay } from 'components/ui';
import './ConfirmationModal.scss';
interface Props {
signedTx: string;
transaction: EthTx;
node: NodeConfig;
token: MergedToken;
network: NetworkConfig;
lang: string;
broadCastTxStatus: BroadcastTransactionStatus;
decimal: number;
onConfirm(signedTx: string): void;
onClose(): void;
}
interface State {
timeToRead: number;
hasBroadCasted: boolean;
}
class ConfirmationModal extends React.Component<Props, State> {
public state = {
timeToRead: 5,
hasBroadCasted: false
};
private readTimer = 0;
public componentDidUpdate() {
if (
this.state.hasBroadCasted &&
this.props.broadCastTxStatus &&
!this.props.broadCastTxStatus.isBroadcasting
) {
this.props.onClose();
}
}
// Count down 5 seconds before allowing them to confirm
public componentDidMount() {
this.readTimer = window.setInterval(() => {
if (this.state.timeToRead > 0) {
this.setState({ timeToRead: this.state.timeToRead - 1 });
} else {
window.clearInterval(this.readTimer);
}
}, 1000);
}
public render() {
const {
node,
token,
network,
onClose,
broadCastTxStatus,
transaction,
decimal
} = this.props;
const { timeToRead } = this.state;
const { toAddress, value, gasPrice, data, from, nonce } = decodeTransaction(
transaction,
token
);
const buttonPrefix = timeToRead > 0 ? `(${timeToRead}) ` : '';
const buttons: IButton[] = [
{
text: buttonPrefix + translateRaw('SENDModal_Yes'),
type: 'primary',
disabled: timeToRead > 0,
onClick: this.confirm
},
{
text: translateRaw('SENDModal_No'),
type: 'default',
onClick: onClose
}
];
const symbol = token ? token.symbol : network.unit;
const isBroadcasting =
broadCastTxStatus && broadCastTxStatus.isBroadcasting;
return (
<div className="ConfModalWrap">
<Modal
title="Confirm Your Transaction"
buttons={buttons}
handleClose={onClose}
disableButtons={isBroadcasting}
isOpen={true}
>
<div className="ConfModal">
{isBroadcasting ? (
<div className="ConfModal-loading">
<Spinner size="x5" />
</div>
) : (
<div>
<div className="ConfModal-summary">
<div className="ConfModal-summary-icon ConfModal-summary-icon--from">
<Identicon size="100%" address={from} />
</div>
<div className="ConfModal-summary-amount">
<div className="ConfModal-summary-amount-arrow" />
<div className="ConfModal-summary-amount-currency">
<UnitDisplay
decimal={decimal}
value={value}
symbol={symbol}
/>
</div>
</div>
<div className="ConfModal-summary-icon ConfModal-summary-icon--to">
<Identicon size="100%" address={toAddress} />
</div>
</div>
<ul className="ConfModal-details">
<li className="ConfModal-details-detail">
You are sending from <code>{from}</code>
</li>
<li className="ConfModal-details-detail">
You are sending to <code>{toAddress}</code>
</li>
<li className="ConfModal-details-detail">
You are sending with a nonce of <code>{nonce}</code>
</li>
<li className="ConfModal-details-detail">
You are sending{' '}
<strong>
<UnitDisplay
decimal={decimal}
value={value}
symbol={symbol}
/>
</strong>{' '}
with a gas price of{' '}
<strong>
<UnitDisplay
unit={'gwei'}
value={gasPrice}
symbol={'gwei'}
/>
</strong>
</li>
<li className="ConfModal-details-detail">
You are interacting with the <strong>{node.network}</strong>{' '}
network provided by <strong>{node.service}</strong>
</li>
{!token && (
<li className="ConfModal-details-detail">
{data ? (
<span>
You are sending the following data:{' '}
<textarea
className="form-control"
value={data}
rows={3}
disabled={true}
/>
</span>
) : (
'There is no data attached to this transaction'
)}
</li>
)}
</ul>
<div className="ConfModal-confirm">
{translate('SENDModal_Content_3')}
</div>
</div>
)}
</div>
</Modal>
</div>
);
}
public componentWillUnmount() {
window.clearInterval(this.readTimer);
}
private confirm = () => {
if (this.state.timeToRead < 1) {
this.props.onConfirm(this.props.signedTx);
this.setState({ hasBroadCasted: true });
}
};
}
function mapStateToProps(state, props) {
// Convert the signedTx to an EthTx transaction
const transaction = new EthTx(props.signedTx);
// Network config for defaults
const network = getNetworkConfig(state);
const node = getNodeConfig(state);
const lang = getLanguageSelection(state);
const broadCastTxStatus = getTxFromState(state, props.signedTx);
// Determine if we're sending to a token from the transaction to address
const { to, data } = getTransactionFields(transaction);
const tokens = getTokens(state);
const token = data && tokens.find(t => t.address === to);
return {
node,
broadCastTxStatus,
transaction,
token,
network,
lang
};
}
export default connect(mapStateToProps)(ConfirmationModal);