feat(send): add transaction status modal on send
This commit is contained in:
parent
f514457fe4
commit
9c1897fa30
|
@ -64,10 +64,42 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
|
|||
),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line max-len
|
||||
if (sendErr || !operationId) return dispatch(sendTransactionError({ error: sendErr.message }));
|
||||
|
||||
dispatch(sendTransactionSuccess({ operationId }));
|
||||
/**
|
||||
Output is a list of operation status objects.
|
||||
[
|
||||
{“operationid”: “opid-12ee…”,
|
||||
“status”: “queued”},
|
||||
{“operationid”: “opd-098a…”, “status”: ”executing”},
|
||||
{“operationid”: “opid-9876”, “status”: ”failed”}
|
||||
]
|
||||
|
||||
When the operation succeeds, the status object will also include the result.
|
||||
{“operationid”: “opid-0e0e”, “status”:”success”, “execution_time”:”25”, “result”: {“txid”:”af3887654…”,...}}
|
||||
|
||||
then the promise will only be resolved when a "success" or "failure" status is obtained
|
||||
*/
|
||||
const interval = setInterval(async () => {
|
||||
const [, status] = await eres(rpc.z_getoperationstatus());
|
||||
|
||||
const operationStatus = status.find(({ id }) => operationId === id);
|
||||
|
||||
if (operationStatus && operationStatus.status === 'success') {
|
||||
clearInterval(interval);
|
||||
dispatch(
|
||||
sendTransactionSuccess({ operationId: operationStatus.result.txid }),
|
||||
);
|
||||
}
|
||||
|
||||
if (operationStatus && operationStatus.status === 'failed') {
|
||||
clearInterval(interval);
|
||||
dispatch(
|
||||
sendTransactionError({ error: operationStatus.error.message }),
|
||||
);
|
||||
}
|
||||
}, 2000);
|
||||
},
|
||||
resetSendView: () => dispatch(resetSendTransaction()),
|
||||
validateAddress: async ({ address }: { address: string }) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React, { PureComponent } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
import FEES from '../constants/fees';
|
||||
|
@ -24,9 +24,28 @@ import SentIcon from '../assets/images/transaction_sent_icon.svg';
|
|||
import MenuIcon from '../assets/images/menu_icon.svg';
|
||||
import ValidIcon from '../assets/images/green_check.png';
|
||||
import InvalidIcon from '../assets/images/error_icon.png';
|
||||
import LoadingIcon from '../assets/images/sync_icon.png';
|
||||
|
||||
import theme from '../theme';
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`;
|
||||
|
||||
const Loader = styled.img`
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
animation: 2s linear infinite;
|
||||
animation-name: ${rotate};
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
margin-top: ${props => props.theme.layoutContentPaddingTop};
|
||||
width: 71%;
|
||||
|
@ -73,20 +92,19 @@ const FormButton = styled(Button)`
|
|||
}
|
||||
`;
|
||||
|
||||
const SuccessWrapper = styled(ColumnComponent)`
|
||||
const ModalContent = styled(ColumnComponent)`
|
||||
min-height: 400px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
p {
|
||||
word-break: break-all;
|
||||
}
|
||||
`;
|
||||
|
||||
const ConfirmItemWrapper = styled(RowComponent)`
|
||||
padding: 22.5px 33.5px;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
p {
|
||||
word-break: break-all;
|
||||
}
|
||||
`;
|
||||
|
||||
const ItemLabel = styled(TextComponent)`
|
||||
|
@ -179,19 +197,14 @@ export class SendView extends PureComponent<Props, State> {
|
|||
|
||||
componentDidMount() {
|
||||
const { resetSendView } = this.props;
|
||||
|
||||
resetSendView();
|
||||
}
|
||||
|
||||
handleChange = (field: string) => (value: string) => {
|
||||
const { validateAddress } = this.props;
|
||||
|
||||
if (field === 'amount') {
|
||||
if (value !== '') {
|
||||
this.setState(() => ({
|
||||
[field]: value,
|
||||
}));
|
||||
}
|
||||
} else if (field === 'to') {
|
||||
if (field === 'to') {
|
||||
// eslint-disable-next-line max-len
|
||||
this.setState({ [field]: value }, () => validateAddress({ address: value }));
|
||||
} else {
|
||||
|
@ -236,8 +249,9 @@ export class SendView extends PureComponent<Props, State> {
|
|||
const {
|
||||
from, amount, to, fee,
|
||||
} = this.state;
|
||||
const { isToAddressValid } = this.props;
|
||||
|
||||
if (!from || !amount || !to || !fee) return;
|
||||
if (!from || !amount || !to || !fee || !isToAddressValid) return;
|
||||
|
||||
toggle();
|
||||
};
|
||||
|
@ -277,6 +291,87 @@ export class SendView extends PureComponent<Props, State> {
|
|||
);
|
||||
};
|
||||
|
||||
renderModalContent = ({
|
||||
valueSent,
|
||||
valueSentInUsd,
|
||||
toggle,
|
||||
}: {
|
||||
valueSent: string,
|
||||
valueSentInUsd: string,
|
||||
toggle: () => void,
|
||||
}) => {
|
||||
const { operationId, isSending, error } = this.props;
|
||||
const { from, to } = this.state;
|
||||
|
||||
if (isSending) {
|
||||
return (
|
||||
<>
|
||||
<Loader src={LoadingIcon} />
|
||||
<TextComponent value='Processing transaction...' />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (operationId) {
|
||||
return (
|
||||
<>
|
||||
<TextComponent
|
||||
value={`Transaction ID: ${operationId}`}
|
||||
align='center'
|
||||
/>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
this.reset();
|
||||
toggle();
|
||||
}}
|
||||
>
|
||||
Send again!
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) return <TextComponent value={error} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='AMOUNT' />
|
||||
<SendZECValue value={`-${valueSent}`} />
|
||||
<SendUSDValue value={`-${valueSentInUsd}`} />
|
||||
</ColumnComponent>
|
||||
<ColumnComponent>
|
||||
<Icon src={SentIcon} alt='Transaction Type Icon' />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='FEE' />
|
||||
<TextComponent value={this.getFeeText()} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='FROM' />
|
||||
<TextComponent value={from} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='TO' />
|
||||
<TextComponent value={to} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} marginBottom='27.5px' />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
addresses,
|
||||
|
@ -304,20 +399,9 @@ export class SendView extends PureComponent<Props, State> {
|
|||
append: 'USD $',
|
||||
});
|
||||
|
||||
return operationId ? (
|
||||
<SuccessWrapper>
|
||||
<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>
|
||||
) : (
|
||||
return (
|
||||
<RowComponent justifyContent='space-between'>
|
||||
<FormWrapper>
|
||||
{error && <TextComponent value={error} />}
|
||||
<InputLabelComponent value='From' />
|
||||
<SelectComponent
|
||||
onChange={this.handleChange('from')}
|
||||
|
@ -408,44 +492,16 @@ export class SendView extends PureComponent<Props, State> {
|
|||
variant='secondary'
|
||||
focused
|
||||
onClick={this.showModal(toggle)}
|
||||
isLoading={isSending}
|
||||
/>
|
||||
)}
|
||||
showButtons={!isSending && !error && !operationId}
|
||||
onClose={this.reset}
|
||||
>
|
||||
<>
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='AMOUNT' />
|
||||
<SendZECValue value={`-${valueSent}`} />
|
||||
<SendUSDValue value={`-${valueSentInUsd}`} />
|
||||
</ColumnComponent>
|
||||
<ColumnComponent>
|
||||
<Icon src={SentIcon} alt='Transaction Type Icon' />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='FEE' />
|
||||
<TextComponent value={this.getFeeText()} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='FROM' />
|
||||
<TextComponent value={from} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
<ConfirmItemWrapper alignItems='center'>
|
||||
<ColumnComponent>
|
||||
<ItemLabel value='TO' />
|
||||
<TextComponent value={to} />
|
||||
</ColumnComponent>
|
||||
</ConfirmItemWrapper>
|
||||
<Divider opacity={0.3} marginBottom='27.5px' />
|
||||
</>
|
||||
{toggle => (
|
||||
<ModalContent>
|
||||
{this.renderModalContent({ valueSent, valueSentInUsd, toggle })}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
<FormButton label='Cancel' variant='secondary' />
|
||||
</SendWrapper>
|
||||
|
|
Loading…
Reference in New Issue