Merge pull request #41 from andrerfneves/feature/transaction-confirm-modal
Feature/transaction confirm modal
This commit is contained in:
commit
f949cb226d
|
@ -50,54 +50,64 @@ type Props = {
|
|||
renderTrigger: (() => void) => Element<*>,
|
||||
title: string,
|
||||
onConfirm: () => void,
|
||||
onClose?: () => void,
|
||||
showButtons?: boolean,
|
||||
width?: number,
|
||||
isLoading?: boolean,
|
||||
children: Element<*>,
|
||||
children: (() => void) => Element<*>,
|
||||
};
|
||||
|
||||
export const ConfirmDialogComponent = ({
|
||||
children,
|
||||
title,
|
||||
onConfirm,
|
||||
onClose,
|
||||
renderTrigger,
|
||||
showButtons,
|
||||
isLoading,
|
||||
width,
|
||||
}: Props) => (
|
||||
<ModalComponent
|
||||
renderTrigger={renderTrigger}
|
||||
closeOnBackdropClick={false}
|
||||
closeOnEsc={false}
|
||||
>
|
||||
{toggle => (
|
||||
<Wrapper width={width}>
|
||||
<CloseIconWrapper>
|
||||
<CloseIconImg src={CloseIcon} onClick={toggle} />
|
||||
</CloseIconWrapper>
|
||||
<TitleWrapper>
|
||||
<TextComponent value={title} align='center' />
|
||||
</TitleWrapper>
|
||||
<Divider />
|
||||
{React.Children.map(children, _ => _)}
|
||||
{showButtons && (
|
||||
<>
|
||||
<Btn label='Confirm' onClick={onConfirm} isLoading={isLoading} />
|
||||
<Btn
|
||||
label='Cancel'
|
||||
onClick={toggle}
|
||||
variant='secondary'
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Wrapper>
|
||||
)}
|
||||
</ModalComponent>
|
||||
);
|
||||
}: Props) => {
|
||||
const handleClose = toggle => () => {
|
||||
toggle();
|
||||
if (onClose) onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalComponent
|
||||
renderTrigger={renderTrigger}
|
||||
closeOnBackdropClick={false}
|
||||
closeOnEsc={false}
|
||||
>
|
||||
{toggle => (
|
||||
<Wrapper width={width}>
|
||||
<CloseIconWrapper>
|
||||
<CloseIconImg src={CloseIcon} onClick={handleClose(toggle)} />
|
||||
</CloseIconWrapper>
|
||||
<TitleWrapper>
|
||||
<TextComponent value={title} align='center' />
|
||||
</TitleWrapper>
|
||||
<Divider opacity={0.3} />
|
||||
{children(handleClose(toggle))}
|
||||
{showButtons && (
|
||||
<>
|
||||
<Btn label='Confirm' onClick={onConfirm} isLoading={isLoading} />
|
||||
<Btn
|
||||
label='Cancel'
|
||||
onClick={handleClose(toggle)}
|
||||
variant='secondary'
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Wrapper>
|
||||
)}
|
||||
</ModalComponent>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmDialogComponent.defaultProps = {
|
||||
showButtons: true,
|
||||
width: 460,
|
||||
isLoading: false,
|
||||
onClose: () => {},
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ import { DoczWrapper } from '../theme.js'
|
|||
onConfirm={() => alert('Confirm')}
|
||||
renderTrigger={toggle => <button onClick={toggle}> Open! </button>}
|
||||
>
|
||||
<div>Confirm content</div>
|
||||
{toggle => <div>Confirm content</div>}
|
||||
</ConfirmDialogComponent>
|
||||
)}
|
||||
</DoczWrapper>
|
||||
|
|
|
@ -6,4 +6,6 @@ export const Divider = styled.div`
|
|||
height: 1px;
|
||||
background-color: ${props => props.color || props.theme.colors.text};
|
||||
opacity: ${props => props.opacity || 1};
|
||||
margin-bottom: ${props => props.marginBottom || 0};
|
||||
margin-top: ${props => props.marginTop || 0};
|
||||
`;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import React, { type Element } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import theme from '../theme';
|
||||
|
@ -11,6 +11,7 @@ const getDefaultStyles = t => styled[t]`
|
|||
props => props.bgColor || props.theme.colors.inputBackground};
|
||||
color: ${props => props.theme.colors.text};
|
||||
padding: 15px;
|
||||
padding-right: ${props => (props.withRightElement ? '85px' : '15px')};
|
||||
width: 100%;
|
||||
outline: none;
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
|
@ -20,6 +21,16 @@ const getDefaultStyles = t => styled[t]`
|
|||
}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const RightElement = styled.div`
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
`;
|
||||
|
||||
const Input = getDefaultStyles('input');
|
||||
const Textarea = getDefaultStyles('textarea');
|
||||
|
||||
|
@ -32,6 +43,7 @@ type Props = {
|
|||
disabled?: boolean,
|
||||
type?: string,
|
||||
step?: number,
|
||||
renderRight?: () => Element<*> | null,
|
||||
bgColor?: string,
|
||||
};
|
||||
|
||||
|
@ -39,12 +51,16 @@ export const InputComponent = ({
|
|||
inputType,
|
||||
bgColor,
|
||||
onChange = () => {},
|
||||
renderRight = () => null,
|
||||
...props
|
||||
}: Props) => {
|
||||
const rightElement = renderRight();
|
||||
|
||||
const inputTypes = {
|
||||
input: () => (
|
||||
<Input
|
||||
onChange={evt => onChange(evt.target.value)}
|
||||
withRightElement={Boolean(rightElement)}
|
||||
bgColor={bgColor}
|
||||
{...props}
|
||||
/>
|
||||
|
@ -62,7 +78,12 @@ export const InputComponent = ({
|
|||
throw new Error(`Invalid input type: ${String(inputType)}`);
|
||||
}
|
||||
|
||||
return inputTypes[inputType || 'input']();
|
||||
return (
|
||||
<Wrapper>
|
||||
{inputTypes[inputType || 'input']()}
|
||||
{rightElement && <RightElement>{rightElement}</RightElement>}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
InputComponent.defaultProps = {
|
||||
|
@ -70,5 +91,9 @@ InputComponent.defaultProps = {
|
|||
rows: 4,
|
||||
disabled: false,
|
||||
type: 'text',
|
||||
renderRight: () => null,
|
||||
onChange: () => {},
|
||||
onFocus: () => {},
|
||||
step: 1,
|
||||
bgColor: theme.colors.inputBackground,
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// @flow
|
||||
|
||||
export default {
|
||||
LOW: 1,
|
||||
MEDIUM: 5,
|
||||
HIGH: 9,
|
||||
LOW: 0.001,
|
||||
MEDIUM: 0.005,
|
||||
HIGH: 0.009,
|
||||
CUSTOM: 'custom',
|
||||
};
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
sendTransactionSuccess,
|
||||
sendTransactionError,
|
||||
resetSendTransaction,
|
||||
validateAddressSuccess,
|
||||
validateAddressError,
|
||||
} from '../redux/modules/send';
|
||||
|
||||
import filterObjectNullKeys from '../utils/filterObjectNullKeys';
|
||||
|
@ -33,6 +35,7 @@ const mapStateToProps = ({ walletSummary, sendStatus }: AppState) => ({
|
|||
error: sendStatus.error,
|
||||
isSending: sendStatus.isSending,
|
||||
operationId: sendStatus.operationId,
|
||||
isToAddressValid: sendStatus.isToAddressValid,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||
|
@ -61,12 +64,67 @@ 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 }) => {
|
||||
if (address.startsWith('z')) {
|
||||
const [, validationResult] = await eres(rpc.z_validateaddress(address));
|
||||
|
||||
return dispatch(
|
||||
validateAddressSuccess({
|
||||
isValid: Boolean(validationResult && validationResult.isvalid),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const [, validationResult] = await eres(rpc.validateaddress(address));
|
||||
|
||||
if (validationResult) {
|
||||
return dispatch(
|
||||
validateAddressSuccess({
|
||||
isValid: Boolean(validationResult && validationResult.isvalid),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return dispatch(validateAddressError());
|
||||
},
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
|
|
|
@ -5,6 +5,8 @@ export const SEND_TRANSACTION = 'SEND_TRANSACTION';
|
|||
export const SEND_TRANSACTION_SUCCESS = 'SEND_TRANSACTION_SUCCESS';
|
||||
export const SEND_TRANSACTION_ERROR = 'SEND_TRANSACTION_ERROR';
|
||||
export const RESET_SEND_TRANSACTION = 'RESET_SEND_TRANSACTION';
|
||||
export const VALIDATE_ADDRESS_SUCCESS = 'VALIDATE_ADDRESS_SUCCESS';
|
||||
export const VALIDATE_ADDRESS_ERROR = 'VALIDATE_ADDRESS_SUCCESS';
|
||||
|
||||
export const sendTransaction = () => ({
|
||||
type: SEND_TRANSACTION,
|
||||
|
@ -34,34 +36,65 @@ export const resetSendTransaction = () => ({
|
|||
payload: {},
|
||||
});
|
||||
|
||||
export const validateAddressSuccess = ({ isValid }: { isValid: boolean }) => ({
|
||||
type: VALIDATE_ADDRESS_SUCCESS,
|
||||
payload: {
|
||||
isValid,
|
||||
},
|
||||
});
|
||||
|
||||
export const validateAddressError = () => ({
|
||||
type: VALIDATE_ADDRESS_ERROR,
|
||||
payload: {},
|
||||
});
|
||||
|
||||
export type State = {
|
||||
isSending: boolean,
|
||||
isToAddressValid: boolean,
|
||||
error: string | null,
|
||||
operationId: string | null,
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
const initialState: State = {
|
||||
isSending: false,
|
||||
error: null,
|
||||
operationId: null,
|
||||
isToAddressValid: false,
|
||||
};
|
||||
|
||||
export default (state: State = initialState, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case SEND_TRANSACTION:
|
||||
return { isSending: true, error: null, operationId: null };
|
||||
return {
|
||||
...state,
|
||||
isSending: true,
|
||||
error: null,
|
||||
operationId: null,
|
||||
};
|
||||
case SEND_TRANSACTION_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
isSending: false,
|
||||
error: null,
|
||||
operationId: action.payload.operationId,
|
||||
};
|
||||
case SEND_TRANSACTION_ERROR:
|
||||
return {
|
||||
...state,
|
||||
isSending: false,
|
||||
error: action.payload.error,
|
||||
operationId: null,
|
||||
};
|
||||
case VALIDATE_ADDRESS_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
isToAddressValid: action.payload.isValid,
|
||||
};
|
||||
case VALIDATE_ADDRESS_ERROR:
|
||||
return {
|
||||
...state,
|
||||
isToAddressValid: false,
|
||||
};
|
||||
case RESET_SEND_TRANSACTION:
|
||||
return initialState;
|
||||
default:
|
||||
|
|
|
@ -73,6 +73,7 @@ const appTheme = {
|
|||
selectButtonShadow,
|
||||
transactionsDetailsLabel,
|
||||
statusPillLabel,
|
||||
modalItemLabel: transactionsDate,
|
||||
blackTwo,
|
||||
buttonBorderColor,
|
||||
},
|
||||
|
|
|
@ -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';
|
||||
|
@ -13,15 +13,39 @@ import { RowComponent } from '../components/row';
|
|||
import { ColumnComponent } from '../components/column';
|
||||
import { Divider } from '../components/divider';
|
||||
import { Button } from '../components/button';
|
||||
import MenuIcon from '../assets/images/menu_icon.svg';
|
||||
import { ConfirmDialogComponent } from '../components/confirm-dialog';
|
||||
|
||||
import formatNumber from '../utils/formatNumber';
|
||||
|
||||
import type { SendTransactionInput } from '../containers/send';
|
||||
import type { State as SendState } from '../redux/modules/send';
|
||||
|
||||
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%;
|
||||
|
@ -134,10 +158,75 @@ 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%;
|
||||
`;
|
||||
|
||||
const ItemLabel = styled(TextComponent)`
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
font-size: ${props => props.theme.fontSize.small};
|
||||
color: ${props => props.color || props.theme.colors.modalItemLabel};
|
||||
margin-bottom: 3.5px;
|
||||
`;
|
||||
|
||||
const SendZECValue = styled(TextComponent)`
|
||||
color: ${props => props.theme.colors.transactionSent};
|
||||
font-size: ${props => `${props.theme.fontSize.large}em`};
|
||||
font-weight: ${props => props.theme.fontWeight.bold};
|
||||
`;
|
||||
|
||||
const SendUSDValue = styled(TextComponent)`
|
||||
opacity: 0.5;
|
||||
font-weight: ${props => props.theme.fontWeight.light};
|
||||
font-size: ${props => `${props.theme.fontSize.medium}em`};
|
||||
`;
|
||||
|
||||
const Icon = styled.img`
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
margin-left: 15px;
|
||||
`;
|
||||
|
||||
const ValidateStatusIcon = styled.img`
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
margin-right: 7px;
|
||||
`;
|
||||
|
||||
const ShowFeeButton = styled.button`
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
color: ${props => props.theme.colors.text};
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const SeeMoreIcon = styled.img`
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border: 1px solid ${props => props.theme.colors.text};
|
||||
border-radius: 100%;
|
||||
margin-right: 11.5px;
|
||||
`;
|
||||
|
||||
type Props = SendState & {
|
||||
|
@ -146,6 +235,7 @@ type Props = SendState & {
|
|||
addresses: string[],
|
||||
sendTransaction: SendTransactionInput => void,
|
||||
resetSendView: () => void,
|
||||
validateAddress: ({ address: string }) => void,
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -173,31 +263,35 @@ export class SendView extends PureComponent<Props, State> {
|
|||
|
||||
componentDidMount() {
|
||||
const { resetSendView } = this.props;
|
||||
|
||||
resetSendView();
|
||||
}
|
||||
|
||||
handleChange = (field: string) => (value: string) => {
|
||||
this.setState(() => ({ [field]: value }));
|
||||
const { validateAddress } = this.props;
|
||||
|
||||
if (field === 'to') {
|
||||
// eslint-disable-next-line max-len
|
||||
this.setState({ [field]: value }, () => validateAddress({ address: value }));
|
||||
} else {
|
||||
this.setState(() => ({ [field]: value }));
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeFeeType = (value: string) => {
|
||||
this.setState(
|
||||
{
|
||||
feeType: value,
|
||||
if (value === FEES.CUSTOM) {
|
||||
this.setState({
|
||||
feeType: FEES.CUSTOM,
|
||||
fee: null,
|
||||
},
|
||||
() => {
|
||||
if (
|
||||
value === String(FEES.LOW)
|
||||
|| value === String(FEES.MEDIUM)
|
||||
|| value === String(FEES.HIGH)
|
||||
) {
|
||||
this.setState(() => ({
|
||||
fee: Number(value),
|
||||
}));
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
const fee = new BigNumber(value);
|
||||
|
||||
this.setState({
|
||||
feeType: fee.toString(),
|
||||
fee: fee.toNumber(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
|
@ -217,12 +311,133 @@ export class SendView extends PureComponent<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
showModal = (toggle: void => void) => () => {
|
||||
const {
|
||||
from, amount, to, fee,
|
||||
} = this.state;
|
||||
const { isToAddressValid } = this.props;
|
||||
|
||||
if (!from || !amount || !to || !fee || !isToAddressValid) return;
|
||||
|
||||
toggle();
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
const { resetSendView } = this.props;
|
||||
|
||||
this.setState(initialState, () => resetSendView());
|
||||
};
|
||||
|
||||
getFeeText = () => {
|
||||
const { fee } = this.state;
|
||||
|
||||
const feeValue = new BigNumber(fee);
|
||||
|
||||
if (feeValue.isEqualTo(FEES.LOW)) return `Low ZEC ${feeValue.toString()}`;
|
||||
// eslint-disable-next-line max-len
|
||||
if (feeValue.isEqualTo(FEES.MEDIUM)) return `Medium ZEC ${feeValue.toString()}`;
|
||||
if (feeValue.isEqualTo(FEES.HIGH)) return `High ZEC ${feeValue.toString()}`;
|
||||
|
||||
return `Custom ZEC ${feeValue.toString()}`;
|
||||
};
|
||||
|
||||
renderValidationStatus = () => {
|
||||
const { isToAddressValid } = this.props;
|
||||
|
||||
return isToAddressValid ? (
|
||||
<RowComponent alignItems='center'>
|
||||
<ValidateStatusIcon src={ValidIcon} />
|
||||
<ItemLabel value='VALID' color={theme.colors.transactionReceived} />
|
||||
</RowComponent>
|
||||
) : (
|
||||
<RowComponent alignItems='center'>
|
||||
<ValidateStatusIcon src={InvalidIcon} />
|
||||
<ItemLabel value='INVALID' color={theme.colors.transactionSent} />
|
||||
</RowComponent>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
|
@ -233,13 +448,7 @@ export class SendView extends PureComponent<Props, State> {
|
|||
operationId,
|
||||
} = this.props;
|
||||
const {
|
||||
showFee,
|
||||
from,
|
||||
amount,
|
||||
to,
|
||||
memo,
|
||||
fee,
|
||||
feeType,
|
||||
showFee, from, amount, to, memo, fee, feeType,
|
||||
} = this.state;
|
||||
|
||||
const isEmpty = amount === '';
|
||||
|
@ -260,20 +469,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')}
|
||||
|
@ -297,6 +495,7 @@ export class SendView extends PureComponent<Props, State> {
|
|||
onChange={this.handleChange('to')}
|
||||
value={to}
|
||||
placeholder='Enter Address'
|
||||
renderRight={to ? this.renderValidationStatus : () => null}
|
||||
/>
|
||||
<InputLabelComponent value='Memo' />
|
||||
<InputComponent
|
||||
|
@ -309,9 +508,7 @@ export class SendView extends PureComponent<Props, State> {
|
|||
onClick={() => this.setState(state => ({ showFee: !state.showFee }))
|
||||
}
|
||||
>
|
||||
<SeeMoreButton isOpen={showFee}>
|
||||
<SeeMoreIcon src={MenuIcon} alt='' />
|
||||
</SeeMoreButton>
|
||||
<SeeMoreIcon src={MenuIcon} alt='Show more icon' />
|
||||
<TextComponent
|
||||
value={`${showFee ? 'Hide' : 'Show'} Additional Options`}
|
||||
/>
|
||||
|
@ -355,20 +552,33 @@ export class SendView extends PureComponent<Props, State> {
|
|||
<TextComponent value={zecBalance} size={1.25} isBold />
|
||||
<InfoCardUSD value={zecBalanceInUsd} size={0.84375} />
|
||||
</InfoContent>
|
||||
<Divider opacity={0.5} />
|
||||
<Divider opacity={0.3} />
|
||||
<InfoContent>
|
||||
<InfoCardLabel value='Sending' />
|
||||
<TextComponent value={valueSent} size={1.25} isBold />
|
||||
<InfoCardUSD value={valueSentInUsd} size={0.84375} />
|
||||
</InfoContent>
|
||||
</InfoCard>
|
||||
<FormButton
|
||||
label='Send'
|
||||
variant='secondary'
|
||||
focused
|
||||
onClick={this.handleSubmit}
|
||||
isLoading={isSending}
|
||||
/>
|
||||
<ConfirmDialogComponent
|
||||
title='Please Confirm Transaction Details'
|
||||
onConfirm={this.handleSubmit}
|
||||
renderTrigger={toggle => (
|
||||
<FormButton
|
||||
label='Send'
|
||||
variant='secondary'
|
||||
focused
|
||||
onClick={this.showModal(toggle)}
|
||||
/>
|
||||
)}
|
||||
showButtons={!isSending && !error && !operationId}
|
||||
onClose={this.reset}
|
||||
>
|
||||
{toggle => (
|
||||
<ModalContent>
|
||||
{this.renderModalContent({ valueSent, valueSentInUsd, toggle })}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
<FormButton label='Cancel' variant='secondary' />
|
||||
</SendWrapper>
|
||||
</RowComponent>
|
||||
|
|
|
@ -224,26 +224,28 @@ export class SettingsView extends PureComponent<Props, State> {
|
|||
showButtons={!successExportViewKeys}
|
||||
width={750}
|
||||
>
|
||||
<ModalContent>
|
||||
{successExportViewKeys ? (
|
||||
viewKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
{() => (
|
||||
<ModalContent>
|
||||
{successExportViewKeys ? (
|
||||
viewKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
|
||||
<ConfirmDialogComponent
|
||||
|
@ -259,26 +261,28 @@ export class SettingsView extends PureComponent<Props, State> {
|
|||
showButtons={!successExportPrivateKeys}
|
||||
width={750}
|
||||
>
|
||||
<ModalContent>
|
||||
{successExportPrivateKeys ? (
|
||||
privateKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
{() => (
|
||||
<ModalContent>
|
||||
{successExportPrivateKeys ? (
|
||||
privateKeys.map(({ zAddress, key }) => (
|
||||
<>
|
||||
<InputLabelComponent value={zAddress} />
|
||||
<RowComponent alignItems='center'>
|
||||
<InputComponent
|
||||
value={key}
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.select();
|
||||
}}
|
||||
/>
|
||||
<ClipboardButton text={key} />
|
||||
</RowComponent>
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' />
|
||||
)}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
|
||||
<ConfirmDialogComponent
|
||||
|
@ -295,22 +299,25 @@ export class SettingsView extends PureComponent<Props, State> {
|
|||
width={900}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<ModalContent>
|
||||
<InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' />
|
||||
<InputComponent
|
||||
value={importedPrivateKeys}
|
||||
onChange={value => this.setState({ importedPrivateKeys: value })}
|
||||
inputType='textarea'
|
||||
rows={10}
|
||||
/>
|
||||
{successImportPrivateKeys && (
|
||||
<TextComponent
|
||||
value='Private keys imported in your node'
|
||||
align='center'
|
||||
{() => (
|
||||
<ModalContent>
|
||||
<InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' />
|
||||
<InputComponent
|
||||
value={importedPrivateKeys}
|
||||
onChange={value => this.setState({ importedPrivateKeys: value })
|
||||
}
|
||||
inputType='textarea'
|
||||
rows={10}
|
||||
/>
|
||||
)}
|
||||
{error && <TextComponent value={error} align='center' />}
|
||||
</ModalContent>
|
||||
{successImportPrivateKeys && (
|
||||
<TextComponent
|
||||
value='Private keys imported in your node'
|
||||
align='center'
|
||||
/>
|
||||
)}
|
||||
{error && <TextComponent value={error} align='center' />}
|
||||
</ModalContent>
|
||||
)}
|
||||
</ConfirmDialogComponent>
|
||||
|
||||
<SettingWrapper>
|
||||
|
|
|
@ -746,8 +746,8 @@ export type APIMethods = {
|
|||
z_exportwallet: (filename: string) => Promise<string>,
|
||||
z_getbalance: (address: string, minconf?: number) => Promise<number>,
|
||||
z_getnewaddress: (type: ?string) => Promise<string>,
|
||||
z_getoperationresult: (operationid?: string[]) => Promise<Object[]>,
|
||||
z_getoperationstatus: (operationid?: string[]) => Promise<Object[]>,
|
||||
z_getoperationresult: (operationid?: string) => Promise<Object[]>,
|
||||
z_getoperationstatus: (operationids?: string) => Promise<Object[]>,
|
||||
z_gettotalbalance: (
|
||||
minconf?: number,
|
||||
includeWatchonly?: boolean,
|
||||
|
|
Loading…
Reference in New Issue