2018-11-23 15:23:41 -08:00
|
|
|
// @flow
|
2018-12-20 06:33:00 -08:00
|
|
|
import React, { PureComponent } from 'react';
|
2018-12-19 13:41:43 -08:00
|
|
|
import styled from 'styled-components';
|
2019-01-09 05:14:02 -08:00
|
|
|
import { BigNumber } from 'bignumber.js';
|
2018-11-23 15:23:41 -08:00
|
|
|
|
2018-12-20 09:18:45 -08:00
|
|
|
import FEES from '../constants/fees';
|
|
|
|
|
2018-12-19 13:41:43 -08:00
|
|
|
import { InputLabelComponent } from '../components/input-label';
|
|
|
|
import { InputComponent } from '../components/input';
|
|
|
|
import { TextComponent } from '../components/text';
|
|
|
|
import { SelectComponent } from '../components/select';
|
2018-12-20 06:10:44 -08:00
|
|
|
import { RowComponent } from '../components/row';
|
|
|
|
import { ColumnComponent } from '../components/column';
|
|
|
|
import { Divider } from '../components/divider';
|
|
|
|
import { Button } from '../components/button';
|
2018-11-23 15:23:41 -08:00
|
|
|
|
2018-12-20 11:00:35 -08:00
|
|
|
import formatNumber from '../utils/formatNumber';
|
|
|
|
|
2018-12-21 07:44:29 -08:00
|
|
|
import type { SendTransactionInput } from '../containers/send';
|
2019-01-10 06:51:32 -08:00
|
|
|
import type { State as SendState } from '../redux/modules/send';
|
2018-12-21 07:44:29 -08:00
|
|
|
|
2018-12-20 06:10:44 -08:00
|
|
|
const FormWrapper = styled.div`
|
2018-12-19 13:41:43 -08:00
|
|
|
margin-top: ${props => props.theme.layoutContentPaddingTop};
|
2019-01-10 19:30:46 -08:00
|
|
|
width: 71%;
|
2018-12-20 06:10:44 -08:00
|
|
|
`;
|
|
|
|
|
|
|
|
const SendWrapper = styled(ColumnComponent)`
|
|
|
|
margin-top: 60px;
|
2019-01-10 19:30:46 -08:00
|
|
|
width: 25%;
|
2018-12-19 13:41:43 -08:00
|
|
|
`;
|
|
|
|
|
|
|
|
const ShowFeeButton = styled.button`
|
|
|
|
background: none;
|
|
|
|
border: none;
|
|
|
|
cursor: pointer;
|
|
|
|
width: 100%;
|
|
|
|
color: ${props => props.theme.colors.text};
|
|
|
|
outline: none;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
text-decoration: underline;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
2018-12-20 06:10:44 -08:00
|
|
|
const InfoCard = styled.div`
|
|
|
|
width: 100%;
|
|
|
|
background-color: ${props => props.theme.colors.cardBackgroundColor};
|
|
|
|
border-radius: ${props => props.theme.boxBorderRadius};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const InfoContent = styled.div`
|
|
|
|
padding: 15px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const InfoCardLabel = styled(TextComponent)`
|
|
|
|
opacity: 0.5;
|
|
|
|
margin-bottom: 10px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const InfoCardUSD = styled(TextComponent)`
|
|
|
|
opacity: 0.5;
|
|
|
|
margin-top: 2.5px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const FormButton = styled(Button)`
|
|
|
|
margin: 10px 0;
|
|
|
|
`;
|
|
|
|
|
2019-01-10 06:51:32 -08:00
|
|
|
const SuccessWrapper = styled(ColumnComponent)`
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
height: 100%;
|
|
|
|
`;
|
|
|
|
|
|
|
|
type Props = SendState & {
|
2018-12-20 11:00:35 -08:00
|
|
|
balance: number,
|
|
|
|
zecPrice: number,
|
|
|
|
addresses: string[],
|
2018-12-21 07:44:29 -08:00
|
|
|
sendTransaction: SendTransactionInput => void,
|
2019-01-10 06:51:32 -08:00
|
|
|
resetSendView: () => void,
|
2018-12-20 11:00:35 -08:00
|
|
|
};
|
2019-01-10 06:51:32 -08:00
|
|
|
|
2018-12-19 13:41:43 -08:00
|
|
|
type State = {
|
|
|
|
showFee: boolean,
|
|
|
|
from: string,
|
2019-01-07 10:59:27 -08:00
|
|
|
amount: string,
|
2018-12-19 13:41:43 -08:00
|
|
|
to: string,
|
2018-12-20 09:18:45 -08:00
|
|
|
feeType: string | number,
|
|
|
|
fee: number | null,
|
2018-12-19 13:41:43 -08:00
|
|
|
memo: string,
|
|
|
|
};
|
|
|
|
|
2019-01-10 06:51:32 -08:00
|
|
|
const initialState = {
|
|
|
|
showFee: false,
|
|
|
|
from: '',
|
|
|
|
amount: '0',
|
|
|
|
to: '',
|
|
|
|
feeType: FEES.LOW,
|
|
|
|
fee: FEES.LOW,
|
|
|
|
memo: '',
|
|
|
|
};
|
|
|
|
|
2018-12-19 13:41:43 -08:00
|
|
|
export class SendView extends PureComponent<Props, State> {
|
2019-01-10 06:51:32 -08:00
|
|
|
state = initialState;
|
2018-12-19 13:41:43 -08:00
|
|
|
|
2019-01-10 10:16:23 -08:00
|
|
|
componentDidMount() {
|
|
|
|
const { resetSendView } = this.props;
|
|
|
|
resetSendView();
|
|
|
|
}
|
2018-12-19 13:41:43 -08:00
|
|
|
|
|
|
|
handleChange = (field: string) => (value: string) => {
|
2019-01-10 19:38:57 -08:00
|
|
|
if (field === 'amount') {
|
|
|
|
if (value !== '') {
|
|
|
|
this.setState(() => ({
|
|
|
|
[field]: value,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.setState(() => ({ [field]: value }));
|
|
|
|
}
|
2018-12-19 13:41:43 -08:00
|
|
|
};
|
|
|
|
|
2018-12-20 09:18:45 -08:00
|
|
|
handleChangeFeeType = (value: string) => {
|
|
|
|
this.setState(
|
|
|
|
{
|
|
|
|
feeType: value,
|
|
|
|
fee: null,
|
|
|
|
},
|
|
|
|
() => {
|
|
|
|
if (
|
|
|
|
value === String(FEES.LOW)
|
|
|
|
|| value === String(FEES.MEDIUM)
|
|
|
|
|| value === String(FEES.HIGH)
|
|
|
|
) {
|
|
|
|
this.setState(() => ({
|
|
|
|
fee: Number(value),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2018-12-21 07:44:29 -08:00
|
|
|
handleSubmit = () => {
|
|
|
|
const {
|
|
|
|
from, amount, to, memo, fee,
|
|
|
|
} = this.state;
|
|
|
|
const { sendTransaction } = this.props;
|
|
|
|
|
2019-01-07 10:59:27 -08:00
|
|
|
if (!from || !amount || !to || !fee) return;
|
2018-12-21 07:44:29 -08:00
|
|
|
|
|
|
|
sendTransaction({
|
|
|
|
from,
|
|
|
|
to,
|
|
|
|
amount,
|
|
|
|
fee,
|
|
|
|
memo,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2019-01-10 06:51:32 -08:00
|
|
|
reset = () => {
|
|
|
|
const { resetSendView } = this.props;
|
|
|
|
|
|
|
|
this.setState(initialState, () => resetSendView());
|
|
|
|
};
|
|
|
|
|
2018-12-19 13:41:43 -08:00
|
|
|
render() {
|
2018-12-21 07:44:29 -08:00
|
|
|
const {
|
2019-01-10 06:51:32 -08:00
|
|
|
addresses,
|
|
|
|
balance,
|
|
|
|
zecPrice,
|
|
|
|
isSending,
|
|
|
|
error,
|
|
|
|
operationId,
|
2018-12-21 07:44:29 -08:00
|
|
|
} = this.props;
|
2018-12-19 13:41:43 -08:00
|
|
|
const {
|
2019-01-23 09:04:11 -08:00
|
|
|
showFee, from, amount, to, memo, fee, feeType,
|
2018-12-19 13:41:43 -08:00
|
|
|
} = this.state;
|
|
|
|
|
2018-12-20 11:00:35 -08:00
|
|
|
const zecBalance = formatNumber({ value: balance, append: 'ZEC ' });
|
|
|
|
const zecBalanceInUsd = formatNumber({
|
2019-01-10 19:30:46 -08:00
|
|
|
value: new BigNumber(balance).times(zecPrice).toNumber(),
|
2018-12-20 11:00:35 -08:00
|
|
|
append: 'USD $',
|
|
|
|
});
|
2019-01-07 10:59:27 -08:00
|
|
|
const valueSent = formatNumber({
|
2019-01-09 05:14:02 -08:00
|
|
|
value: new BigNumber(amount).toNumber(),
|
2019-01-07 10:59:27 -08:00
|
|
|
append: 'ZEC ',
|
|
|
|
});
|
2018-12-20 11:00:35 -08:00
|
|
|
const valueSentInUsd = formatNumber({
|
2019-01-10 19:30:46 -08:00
|
|
|
value: new BigNumber(amount).times(zecPrice).toNumber(),
|
2018-12-20 11:00:35 -08:00
|
|
|
append: 'USD $',
|
|
|
|
});
|
|
|
|
|
2019-01-10 06:51:32 -08:00
|
|
|
return operationId ? (
|
2019-01-23 09:04:11 -08:00
|
|
|
<SuccessWrapper id='send-success-wrapper'>
|
2019-01-10 06:51:32 -08:00
|
|
|
<TextComponent value={`Processing operation: ${operationId}`} />
|
|
|
|
<TextComponent value={`Amount: ${amount}`} />
|
|
|
|
<TextComponent value={`From: ${from}`} />
|
|
|
|
<TextComponent value={`To: ${to}`} />
|
|
|
|
<button type='button' onClick={this.reset}>
|
|
|
|
Send again!
|
|
|
|
</button>
|
|
|
|
</SuccessWrapper>
|
|
|
|
) : (
|
2019-01-23 09:04:11 -08:00
|
|
|
<RowComponent id='send-wrapper' justifyContent='space-between'>
|
2018-12-20 06:10:44 -08:00
|
|
|
<FormWrapper>
|
2019-01-23 11:37:35 -08:00
|
|
|
{error && <TextComponent id='send-error-text' value={error} />}
|
2018-12-20 06:10:44 -08:00
|
|
|
<InputLabelComponent value='From' />
|
|
|
|
<SelectComponent
|
|
|
|
onChange={this.handleChange('from')}
|
|
|
|
value={from}
|
|
|
|
placeholder='Select a address'
|
2018-12-20 11:00:35 -08:00
|
|
|
options={addresses.map(addr => ({ value: addr, label: addr }))}
|
2018-12-20 06:10:44 -08:00
|
|
|
/>
|
|
|
|
<InputLabelComponent value='Amount' />
|
|
|
|
<InputComponent
|
2019-01-23 09:04:11 -08:00
|
|
|
name='amount'
|
2018-12-20 06:10:44 -08:00
|
|
|
type='number'
|
|
|
|
onChange={this.handleChange('amount')}
|
|
|
|
value={String(amount)}
|
2019-01-10 09:51:51 -08:00
|
|
|
placeholder='ZEC 0.0'
|
2019-01-08 06:35:23 -08:00
|
|
|
step={0.01}
|
2018-12-20 06:10:44 -08:00
|
|
|
/>
|
|
|
|
<InputLabelComponent value='To' />
|
|
|
|
<InputComponent
|
|
|
|
onChange={this.handleChange('to')}
|
|
|
|
value={to}
|
2019-01-10 09:51:51 -08:00
|
|
|
placeholder='Enter Address'
|
2019-01-23 09:04:11 -08:00
|
|
|
name='to'
|
2018-12-19 13:41:43 -08:00
|
|
|
/>
|
2018-12-20 06:10:44 -08:00
|
|
|
<InputLabelComponent value='Memo' />
|
|
|
|
<InputComponent
|
|
|
|
onChange={this.handleChange('memo')}
|
|
|
|
value={memo}
|
|
|
|
inputType='textarea'
|
|
|
|
placeholder='Enter a text here'
|
2019-01-23 09:04:11 -08:00
|
|
|
name='memo'
|
2018-12-20 06:10:44 -08:00
|
|
|
/>
|
|
|
|
<ShowFeeButton
|
2019-01-23 09:04:11 -08:00
|
|
|
id='send-show-additional-options-button'
|
2018-12-20 06:10:44 -08:00
|
|
|
onClick={() => this.setState(state => ({ showFee: !state.showFee }))
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<TextComponent
|
2019-01-10 19:30:46 -08:00
|
|
|
paddingTop='10px'
|
2018-12-20 06:10:44 -08:00
|
|
|
value={`${showFee ? 'Hide' : 'Show'} Additional Options`}
|
|
|
|
align='right'
|
2018-12-19 13:41:43 -08:00
|
|
|
/>
|
2018-12-20 06:10:44 -08:00
|
|
|
</ShowFeeButton>
|
|
|
|
{showFee && (
|
2019-01-23 09:04:11 -08:00
|
|
|
<RowComponent
|
|
|
|
id='send-fee-wrapper'
|
|
|
|
alignItems='flex-end'
|
|
|
|
justifyContent='space-between'
|
|
|
|
>
|
2018-12-20 06:33:00 -08:00
|
|
|
<ColumnComponent width='74%'>
|
|
|
|
<InputLabelComponent value='Fee' />
|
|
|
|
<InputComponent
|
|
|
|
type='number'
|
|
|
|
onChange={this.handleChange('fee')}
|
|
|
|
value={String(fee)}
|
2018-12-20 09:18:45 -08:00
|
|
|
disabled={feeType !== FEES.CUSTOM}
|
2019-01-23 09:04:11 -08:00
|
|
|
name='fee'
|
2018-12-20 06:33:00 -08:00
|
|
|
/>
|
|
|
|
</ColumnComponent>
|
|
|
|
<ColumnComponent width='25%'>
|
|
|
|
<SelectComponent
|
2018-12-20 09:18:45 -08:00
|
|
|
onChange={this.handleChangeFeeType}
|
|
|
|
value={String(feeType)}
|
|
|
|
options={Object.keys(FEES).map(cur => ({
|
|
|
|
label: cur.toLowerCase(),
|
|
|
|
value: String(FEES[cur]),
|
|
|
|
}))}
|
|
|
|
placement='top'
|
2018-12-20 06:33:00 -08:00
|
|
|
/>
|
|
|
|
</ColumnComponent>
|
|
|
|
</RowComponent>
|
2018-12-20 06:10:44 -08:00
|
|
|
)}
|
2018-12-20 11:04:00 -08:00
|
|
|
{feeType === FEES.CUSTOM && (
|
|
|
|
<TextComponent value='Custom fees may compromise your privacy since fees are transparent' />
|
|
|
|
)}
|
2018-12-20 06:10:44 -08:00
|
|
|
</FormWrapper>
|
|
|
|
<SendWrapper>
|
|
|
|
<InfoCard>
|
|
|
|
<InfoContent>
|
|
|
|
<InfoCardLabel value='Available Funds:' />
|
2019-01-10 09:51:51 -08:00
|
|
|
<TextComponent value={zecBalance} size={1.25} isBold />
|
|
|
|
<InfoCardUSD value={zecBalanceInUsd} size={0.84375} />
|
2018-12-20 06:10:44 -08:00
|
|
|
</InfoContent>
|
|
|
|
<Divider opacity={0.5} />
|
|
|
|
<InfoContent>
|
|
|
|
<InfoCardLabel value='Sending' />
|
2019-01-10 09:51:51 -08:00
|
|
|
<TextComponent value={valueSent} size={1.25} isBold />
|
|
|
|
<InfoCardUSD value={valueSentInUsd} size={0.84375} />
|
2018-12-20 06:10:44 -08:00
|
|
|
</InfoContent>
|
|
|
|
</InfoCard>
|
2018-12-21 07:44:29 -08:00
|
|
|
<FormButton
|
2019-01-23 09:04:11 -08:00
|
|
|
id='send-submit-button'
|
2018-12-21 07:44:29 -08:00
|
|
|
label='Send'
|
|
|
|
variant='secondary'
|
|
|
|
focused
|
|
|
|
onClick={this.handleSubmit}
|
|
|
|
isLoading={isSending}
|
2019-01-23 09:04:11 -08:00
|
|
|
disabled={!from || !amount || !to || !fee}
|
2018-12-21 07:44:29 -08:00
|
|
|
/>
|
2018-12-20 06:10:44 -08:00
|
|
|
<FormButton label='Cancel' variant='secondary' />
|
|
|
|
</SendWrapper>
|
|
|
|
</RowComponent>
|
2018-12-19 13:41:43 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|