2018-02-20 09:30:36 +00:00
|
|
|
/* @flow */
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import React, { Component } from 'react';
|
|
|
|
import Select from 'react-select';
|
|
|
|
import AdvancedForm from './AdvancedForm';
|
2018-03-08 16:10:53 +00:00
|
|
|
import PendingTransactions from './PendingTransactions';
|
2018-02-20 09:30:36 +00:00
|
|
|
import { FeeSelectValue, FeeSelectOption } from './FeeSelect';
|
2018-05-18 16:38:02 +00:00
|
|
|
import { Notification } from '~/js/common/Notification';
|
2018-05-18 16:26:45 +00:00
|
|
|
import AbstractAccount from '../AbstractAccount';
|
2018-05-18 16:38:02 +00:00
|
|
|
import { findAccountTokens } from '~/js/reducers/TokensReducer';
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-04-16 21:19:50 +00:00
|
|
|
import type { Props } from './index';
|
2018-05-18 16:26:45 +00:00
|
|
|
import type { AccountState } from '../AbstractAccount';
|
2018-04-16 21:19:50 +00:00
|
|
|
|
|
|
|
export default class Send extends AbstractAccount<Props> {
|
2018-02-20 09:30:36 +00:00
|
|
|
render() {
|
2018-04-16 21:19:50 +00:00
|
|
|
return super.render() || _render(this.props, this.state);
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 21:19:50 +00:00
|
|
|
const _render = (props: Props, state: AccountState): React$Element<string> => {
|
|
|
|
|
|
|
|
const {
|
|
|
|
device,
|
|
|
|
account,
|
2018-05-05 11:52:03 +00:00
|
|
|
discovery,
|
2018-04-16 21:19:50 +00:00
|
|
|
deviceStatusNotification
|
|
|
|
} = state;
|
2018-05-10 16:36:25 +00:00
|
|
|
const abstractAccount = props.abstractAccount;
|
2018-04-16 21:19:50 +00:00
|
|
|
|
2018-05-10 16:36:25 +00:00
|
|
|
if (!device || !account || !discovery || !abstractAccount) return <section></section>;
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-05-10 10:39:49 +00:00
|
|
|
const tokens = findAccountTokens(props.tokens, account);
|
2018-05-10 16:36:25 +00:00
|
|
|
const { network } = abstractAccount;
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
const {
|
|
|
|
address,
|
|
|
|
amount,
|
|
|
|
setMax,
|
2018-03-08 16:10:53 +00:00
|
|
|
coinSymbol,
|
2018-05-10 10:39:49 +00:00
|
|
|
selectedCurrency,
|
2018-02-20 09:30:36 +00:00
|
|
|
feeLevels,
|
|
|
|
selectedFeeLevel,
|
|
|
|
gasPriceNeedsUpdate,
|
|
|
|
total,
|
|
|
|
errors,
|
|
|
|
warnings,
|
|
|
|
infos,
|
|
|
|
advanced,
|
|
|
|
sending,
|
|
|
|
} = props.sendForm;
|
|
|
|
|
|
|
|
const {
|
|
|
|
onAddressChange,
|
|
|
|
onAmountChange,
|
|
|
|
onSetMax,
|
|
|
|
onCurrencyChange,
|
|
|
|
onFeeLevelChange,
|
|
|
|
updateFeeLevels,
|
|
|
|
onSend,
|
|
|
|
} = props.sendFormActions;
|
|
|
|
|
2018-05-10 16:36:25 +00:00
|
|
|
const selectedCoin = abstractAccount.coin;
|
2018-04-11 13:21:43 +00:00
|
|
|
const fiatRate = props.fiat.find(f => f.network === network);
|
2018-03-08 16:10:53 +00:00
|
|
|
|
2018-05-10 10:39:49 +00:00
|
|
|
const tokensSelectData = tokens.map(t => {
|
2018-02-20 09:30:36 +00:00
|
|
|
return { value: t.symbol, label: t.symbol };
|
|
|
|
});
|
2018-05-10 10:39:49 +00:00
|
|
|
tokensSelectData.unshift({ value: selectedCoin.symbol, label: selectedCoin.symbol });
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max';
|
|
|
|
|
|
|
|
let updateFeeLevelsButton = null;
|
|
|
|
if (gasPriceNeedsUpdate) {
|
|
|
|
updateFeeLevelsButton = (
|
|
|
|
<span className="update-fee-levels">Recommended fees updated. <a onClick={ updateFeeLevels }>Click here to use them</a></span>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
let addressClassName: ?string;
|
|
|
|
if (errors.address) {
|
|
|
|
addressClassName = 'not-valid';
|
|
|
|
} else if (warnings.address) {
|
|
|
|
addressClassName = 'warning';
|
|
|
|
} else if (address.length > 0) {
|
|
|
|
addressClassName = 'valid';
|
|
|
|
}
|
|
|
|
|
2018-03-08 16:10:53 +00:00
|
|
|
let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
|
2018-02-20 09:30:36 +00:00
|
|
|
let buttonLabel: string = 'Send';
|
2018-05-10 10:39:49 +00:00
|
|
|
if (coinSymbol !== selectedCurrency && amount.length > 0 && !errors.amount) {
|
|
|
|
buttonLabel += ` ${amount} ${ selectedCurrency.toUpperCase() }`
|
|
|
|
} else if (coinSymbol === selectedCurrency && total !== '0') {
|
2018-03-08 16:10:53 +00:00
|
|
|
buttonLabel += ` ${total} ${ selectedCoin.symbol }`;
|
2018-02-20 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-14 08:45:04 +00:00
|
|
|
if (!device.connected){
|
|
|
|
buttonLabel = 'Device is not connected';
|
|
|
|
buttonDisabled = true;
|
|
|
|
} else if (!device.available) {
|
|
|
|
buttonLabel = 'Device is unavailable';
|
|
|
|
buttonDisabled = true;
|
|
|
|
} else if (!discovery.completed) {
|
|
|
|
buttonLabel = 'Loading accounts';
|
|
|
|
buttonDisabled = true;
|
|
|
|
}
|
2018-05-17 12:08:22 +00:00
|
|
|
|
2018-02-20 09:30:36 +00:00
|
|
|
let notification = null;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<section className="send-form">
|
|
|
|
|
2018-04-11 10:06:46 +00:00
|
|
|
{ deviceStatusNotification }
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
<h2>Send Ethereum or tokens</h2>
|
|
|
|
<div className="row address-input">
|
|
|
|
<label>Address</label>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
autoComplete="off"
|
|
|
|
autoCorrect="off"
|
|
|
|
autoCapitalize="off"
|
|
|
|
spellCheck="false"
|
|
|
|
value={ address }
|
|
|
|
className={ addressClassName }
|
|
|
|
onChange={ event => onAddressChange(event.target.value) } />
|
|
|
|
<span className="input-icon"></span>
|
|
|
|
{ errors.address ? (<span className="error">{ errors.address }</span>) : null }
|
|
|
|
{ warnings.address ? (<span className="warning">{ warnings.address }</span>) : null }
|
|
|
|
{ infos.address ? (<span className="info">{ infos.address }</span>) : null }
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="row">
|
|
|
|
<label>Amount</label>
|
|
|
|
<div className="amount-input">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
autoComplete="off"
|
|
|
|
autoCorrect="off"
|
|
|
|
autoCapitalize="off"
|
|
|
|
spellCheck="false"
|
|
|
|
value={ amount }
|
|
|
|
className={ errors.amount ? 'not-valid' : null }
|
|
|
|
onChange={ event => onAmountChange(event.target.value) } />
|
|
|
|
|
|
|
|
<a className={ setMaxClassName } onClick={ onSetMax }>Set max</a>
|
|
|
|
|
|
|
|
<Select
|
|
|
|
name="currency"
|
|
|
|
className="currency"
|
|
|
|
searchable={ false }
|
|
|
|
clearable= { false }
|
|
|
|
multi={ false }
|
2018-05-10 10:39:49 +00:00
|
|
|
value={ selectedCurrency }
|
|
|
|
disabled={ tokensSelectData.length < 2 }
|
2018-02-20 09:30:36 +00:00
|
|
|
onChange={ onCurrencyChange }
|
2018-05-10 10:39:49 +00:00
|
|
|
options={ tokensSelectData } />
|
2018-02-20 09:30:36 +00:00
|
|
|
</div>
|
|
|
|
{ errors.amount ? (<span className="error">{ errors.amount }</span>) : null }
|
|
|
|
{ warnings.amount ? (<span className="warning">{ warnings.amount }</span>) : null }
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="row">
|
|
|
|
<label>Fee{ updateFeeLevelsButton }</label>
|
|
|
|
<Select
|
|
|
|
name="fee"
|
|
|
|
className="fee"
|
|
|
|
searchable={ false }
|
|
|
|
clearable= { false }
|
|
|
|
value={ selectedFeeLevel }
|
|
|
|
onChange={ onFeeLevelChange }
|
|
|
|
valueComponent={ FeeSelectValue }
|
|
|
|
optionComponent={ FeeSelectOption }
|
|
|
|
optionClassName="fee-option"
|
|
|
|
options={ feeLevels } />
|
|
|
|
</div>
|
|
|
|
|
2018-05-18 16:26:45 +00:00
|
|
|
<AdvancedForm
|
|
|
|
abstractAccount={ props.abstractAccount }
|
|
|
|
sendForm={ props.sendForm }
|
|
|
|
sendFormActions={ props.sendFormActions }>
|
2018-02-20 09:30:36 +00:00
|
|
|
<button disabled={ buttonDisabled } onClick={ event => onSend() }>{ buttonLabel }</button>
|
|
|
|
</AdvancedForm>
|
2018-03-08 16:10:53 +00:00
|
|
|
|
2018-04-16 21:19:50 +00:00
|
|
|
<PendingTransactions
|
2018-05-18 16:26:45 +00:00
|
|
|
pending={ props.pending }
|
|
|
|
tokens={ props.tokens }
|
2018-04-16 21:19:50 +00:00
|
|
|
account={ account }
|
|
|
|
selectedCoin={ selectedCoin } />
|
2018-02-20 09:30:36 +00:00
|
|
|
|
|
|
|
</section>
|
|
|
|
);
|
|
|
|
}
|