feat: onChain send and receive

This commit is contained in:
AP 2019-12-10 06:13:42 +01:00
parent 9705073959
commit acc3d9ef8d
5 changed files with 354 additions and 11 deletions

View file

@ -34,7 +34,7 @@ export const CreateInvoiceCard = ({ color }: { color: string }) => {
<Input
color={color}
type={'number'}
onChange={e => setAmount(10000)}
onChange={e => setAmount(parseInt(e.target.value))}
/>
<ColorButton
color={color}

View file

@ -0,0 +1,104 @@
import React, { useState, useContext, useEffect } from 'react';
import {
Card,
ColorButton,
NoWrapTitle,
DarkSubTitle,
Separation,
} from '../../generic/Styled';
import { useMutation } from '@apollo/react-hooks';
import { CREATE_ADDRESS } from '../../../graphql/mutation';
import { Edit, Circle } from '../../generic/Icons';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { AccountContext } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
const SingleLine = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;
const RadioText = styled.div`
margin-left: 10px;
`;
const ButtonRow = styled.div`
width: auto;
display: flex;
`;
const TitleWithSpacing = styled(NoWrapTitle)`
margin-right: 10px;
`;
export const ReceiveOnChainCard = ({ color }: { color: string }) => {
const [nested, setNested] = useState(false);
const [received, setReceived] = useState(false);
const { host, read, cert } = useContext(AccountContext);
const auth = getAuthString(host, read, cert);
const [createAddress, { data }] = useMutation(CREATE_ADDRESS, {
onError: error => toast.error(getErrorContent(error)),
});
useEffect(() => {
data && data.createAddress && setReceived(true);
}, [data]);
return (
<Card>
<SingleLine>
<ButtonRow>
<TitleWithSpacing>Type of Address:</TitleWithSpacing>
<ColorButton
color={color}
onClick={() => {
setNested(false);
}}
>
<Circle
size={'10px'}
fillcolor={nested ? '' : 'white'}
/>
<RadioText>P2WPKH</RadioText>
</ColorButton>
<ColorButton
color={color}
onClick={() => {
setNested(true);
}}
>
<Circle
size={'10px'}
fillcolor={nested ? 'white' : ''}
/>
<RadioText>NP2WPKH</RadioText>
</ColorButton>
</ButtonRow>
<ColorButton
color={color}
disabled={received}
onClick={() => {
createAddress({ variables: { auth, nested } });
}}
>
<Edit />
Create Address
</ColorButton>
</SingleLine>
{data && data.createAddress && (
<>
<Separation />
<SingleLine>
<DarkSubTitle bottom={'0px'}>New Address:</DarkSubTitle>
{data.createAddress}
</SingleLine>
</>
)}
</Card>
);
};

View file

@ -0,0 +1,226 @@
import React, { useState, useContext, useEffect } from 'react';
import {
Card,
Input,
ColorButton,
NoWrapTitle,
DarkSubTitle,
} from '../../generic/Styled';
import { useMutation } from '@apollo/react-hooks';
import { PAY_ADDRESS } from '../../../graphql/mutation';
import { Send, Circle } from '../../generic/Icons';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { AccountContext } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { SettingsContext } from '../../../context/SettingsContext';
import { getValue } from '../../../helpers/Helpers';
const SingleLine = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;
const RadioText = styled.div`
margin-left: 10px;
`;
const ButtonRow = styled.div`
width: auto;
display: flex;
justify-content: center;
align-items: center;
`;
const SmallInput = styled(Input)`
max-width: 150px;
`;
const RightButton = styled(ColorButton)`
margin-left: auto;
`;
export const SendOnChainCard = ({ color }: { color: string }) => {
const [address, setAddress] = useState('');
const [tokens, setTokens] = useState(0);
const [type, setType] = useState('none');
const [amount, setAmount] = useState(0);
const [sendAll, setSendAll] = useState(false);
const [isSent, setIsSent] = useState(false);
const { price, symbol, currency } = useContext(SettingsContext);
const { host, read, cert } = useContext(AccountContext);
const auth = getAuthString(host, read, cert);
const [payAddress, { data }] = useMutation(PAY_ADDRESS, {
onError: error => toast.error(getErrorContent(error)),
});
const priceProps = { price, symbol, currency };
const getFormat = (amount: number) =>
getValue({
amount,
...priceProps,
});
const feeFormat = (amount: number) => {
if (type === 'fee') {
return getFormat(amount);
} else {
return `${amount} blocks`;
}
};
const typeAmount =
type === 'fee'
? { fee: amount }
: type === 'target'
? { target: amount }
: {};
const tokenAmount = sendAll ? { sendAll } : { tokens };
useEffect(() => {
if (data && data.sendToAddress && data.sendToAddress.id) {
setIsSent(true);
toast.success('On Chain Payment Sent!');
}
}, [data]);
if (isSent && data && data.sendToAddress && data.sendToAddress.id) {
return (
<Card>
<SingleLine>
<DarkSubTitle>Payment Id:</DarkSubTitle>
{data.sendToAddress.id}
</SingleLine>
</Card>
);
}
return (
<Card>
<SingleLine>
<NoWrapTitle>Send to Address:</NoWrapTitle>
<Input
color={color}
onChange={e => setAddress(e.target.value)}
/>
</SingleLine>
<SingleLine>
<NoWrapTitle>Send All:</NoWrapTitle>
<ButtonRow>
<ColorButton
color={color}
onClick={() => {
setSendAll(true);
}}
>
<Circle
size={'10px'}
fillcolor={sendAll ? 'white' : ''}
/>
<RadioText>Yes</RadioText>
</ColorButton>
<ColorButton
color={color}
onClick={() => {
setSendAll(false);
}}
>
<Circle
size={'10px'}
fillcolor={!sendAll ? 'white' : ''}
/>
<RadioText>No</RadioText>
</ColorButton>
</ButtonRow>
</SingleLine>
{!sendAll && (
<SingleLine>
<NoWrapTitle>Amount:</NoWrapTitle>
<ButtonRow>
<DarkSubTitle>{`(${getFormat(tokens)})`}</DarkSubTitle>
<SmallInput
color={color}
type={'number'}
onChange={e => setTokens(parseInt(e.target.value))}
/>
</ButtonRow>
</SingleLine>
)}
<SingleLine>
<NoWrapTitle>Fee:</NoWrapTitle>
<ButtonRow>
<ColorButton
color={color}
onClick={() => {
setType('none');
}}
>
<Circle
size={'10px'}
fillcolor={type === 'none' ? 'white' : ''}
/>
<RadioText>Auto</RadioText>
</ColorButton>
<ColorButton
color={color}
onClick={() => {
setType('fee');
}}
>
<Circle
size={'10px'}
fillcolor={type === 'fee' ? 'white' : ''}
/>
<RadioText>{`Fee (Sats/Byte)`}</RadioText>
</ColorButton>
<ColorButton
color={color}
onClick={() => {
setType('target');
}}
>
<Circle
size={'10px'}
fillcolor={type === 'target' ? 'white' : ''}
/>
<RadioText>{`Target Confirmations`}</RadioText>
</ColorButton>
</ButtonRow>
{type !== 'none' && (
<ButtonRow>
<DarkSubTitle bottom={'0px'}>
{`(${feeFormat(amount)})`}
</DarkSubTitle>
<SmallInput
color={color}
type={'number'}
onChange={e => setAmount(parseInt(e.target.value))}
/>
</ButtonRow>
)}
</SingleLine>
<RightButton
color={color}
onClick={() => {
payAddress({
variables: {
auth,
address,
...typeAmount,
...tokenAmount,
},
});
}}
>
<Send />
Send To Address
</RightButton>
</Card>
);
};

View file

@ -47,9 +47,9 @@ export const PAY_ADDRESS = gql`
mutation PayAddress(
$auth: String!
$address: String!
$tokens: Number!
$fee: Number
$target: Number
$tokens: Int
$fee: Int
$target: Int
$sendAll: Boolean
) {
sendToAddress(
@ -59,7 +59,13 @@ export const PAY_ADDRESS = gql`
fee: $fee
target: $target
sendAll: $sendAll
)
) {
confirmationCount
id
isConfirmed
isOutgoing
tokens
}
}
`;

View file

@ -5,12 +5,19 @@ export const getErrorContent = (error: ApolloError): ReactNode => {
const errors = error.graphQLErrors.map(x => x.message);
const renderMessage = errors.map((error, i) => {
if (error === 'rateLimitReached') {
return 'Rate Limit Reached.';
}
try {
const errorMsg = JSON.parse(error);
return (
<div
key={i}
>{`${errorMsg.details} [${errorMsg.msg}/${errorMsg.code}]`}</div>
);
} catch (e) {
console.log('JSON parsing error:', e);
}
});
return <div>{renderMessage}</div>;