Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Send eth #253

Merged
merged 12 commits into from
Jul 9, 2022
2 changes: 1 addition & 1 deletion extension/source/Confirm/Confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Button from '../components/Button';
import CompactQuillHeading from '../components/CompactQuillHeading';
import { DEFAULT_CHAIN_ID_HEX } from '../env';
import { useInputDecode } from '../hooks/useInputDecode';
import formatCompactAddress from '../Popup/helpers/formatCompactAddress';
import formatCompactAddress from '../helpers/formatCompactAddress';

const Confirm: FunctionComponent = () => {
const [id, setId] = useState<string>();
Expand Down
57 changes: 57 additions & 0 deletions extension/source/Home/Wallet/Wallets/SendDetail/AmountSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ethers } from 'ethers';
import { FunctionComponent, useMemo } from 'react';
import Display from '../../../../cells/components/Display';

import TextBox from '../../../../cells/components/TextBox';
import { IReadableCell } from '../../../../cells/ICell';
import MemoryCell from '../../../../cells/MemoryCell';
import TransformCell from '../../../../cells/TransformCell';
import Button from '../../../../components/Button';

const AmountSelector: FunctionComponent<{
selectedAsset: IReadableCell<string | undefined>;
onSend: (amountWei: string) => void;
}> = ({ selectedAsset, onSend }) => {
const amount = useMemo(() => new MemoryCell(''), []);

const amountValid = useMemo(
() =>
new TransformCell(
amount,
($amount) => $amount || '0',
($amount, $newAmount) =>
Number.isFinite(Number($newAmount)) ? $newAmount : $amount,
),
[amount],
);

return (
<div className="flex flex-col gap-4">
<div className="text-body">Select Amount</div>
<div className="flex justify-end gap-2">
<TextBox
value={amountValid}
className="text-right"
style={{ maxWidth: '10rem' }}
/>
<div className="self-center">
<Display cell={selectedAsset} />
</div>
</div>
<div className="flex justify-end">
<Button
className="btn-primary"
onPress={async () =>
onSend(
ethers.utils.parseEther(await amountValid.read()).toHexString(),
)
}
>
Send
</Button>
</div>
</div>
);
};

export default AmountSelector;
41 changes: 41 additions & 0 deletions extension/source/Home/Wallet/Wallets/SendDetail/AssetSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ArrowRight } from 'phosphor-react';
import { FunctionComponent } from 'react';
import ICell from '../../../../cells/ICell';

import useCell from '../../../../cells/useCell';
import Loading from '../../../../components/Loading';
import onAction from '../../../../helpers/onAction';
import { useQuill } from '../../../QuillContext';
import Balance from '../Balance';

const AssetSelector: FunctionComponent<{
selectedAsset: ICell<string | undefined>;
}> = ({ selectedAsset }) => {
const quill = useQuill();
const selectedAddress = useCell(quill.cells.selectedAddress);

return (
<div className="flex flex-col gap-4">
<div className="text-body">Select Asset</div>
<div className="grid grid-cols-2 gap-4">
<div
className={[
'flex flex-row p-4 gap-4 rounded-lg bg-white border',
'border-grey-400 shadow-md cursor-pointer active:bg-grey-200',
'select-none cursor-pointer',
].join(' ')}
{...onAction(() => selectedAsset.write('ETH'))}
>
<div className="grow">{/* TODO: icon */}Ether</div>
<div>
{selectedAddress && <Balance address={selectedAddress} />}
{!selectedAddress && <Loading />}
</div>
<ArrowRight className="self-center" size={20} />
</div>
</div>
</div>
);
};

export default AssetSelector;
74 changes: 74 additions & 0 deletions extension/source/Home/Wallet/Wallets/SendDetail/BigSendButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { CaretLeft, X } from 'phosphor-react';
import { FunctionComponent } from 'react';
import { useNavigate } from 'react-router-dom';

import useCell from '../../../../cells/useCell';
import formatCompactAddress from '../../../../helpers/formatCompactAddress';
import onAction from '../../../../helpers/onAction';
import type { SendDetailCells } from './SendDetail';

const roundFieldClasses = [
'text-[8pt] bg-blue-100 bg-opacity-40 leading-normal',
'rounded-full px-2 py-1 flex place-items-center gap-2',
].join(' ');

const BigSendButton: FunctionComponent<{
cells: SendDetailCells;
}> = ({ cells }) => {
const navigate = useNavigate();

const selectedAsset = useCell(cells.selectedAsset);
const recipient = useCell(cells.recipient);
const amountWei = useCell(cells.amountWei);

const visibility = amountWei === undefined ? '' : 'invisible';

return (
<div className="btn-primary-outer justify-center grow leading-10">
<div className="flex grow justify-center">
<div
className={`${visibility} btn-primary-inner flex flex-row gap-2`}
{...onAction(async () => {
if (recipient !== undefined) {
await cells.recipient.write(undefined);
} else if (selectedAsset !== undefined) {
await cells.selectedAsset.write(undefined);
} else {
navigate('/wallets');
}
})}
>
<CaretLeft className="self-center" size={20} />
<div>Back</div>
</div>
<div className="flex grow justify-center py-2 gap-2">
<div>Send</div>
{selectedAsset !== undefined && (
<div className="flex flex-col justify-center">
<div className={roundFieldClasses}>{selectedAsset}</div>
</div>
)}
{recipient !== undefined && (
<>
<div>to</div>
<div className="flex flex-col justify-center">
<div className={roundFieldClasses}>
{formatCompactAddress(recipient)}
</div>
</div>
</>
)}
</div>
<div
className={`${visibility} btn-primary-inner flex flex-row gap-2`}
{...onAction(() => navigate('/wallets'))}
>
<div>Cancel</div>
<X className="self-center" size={20} />
</div>
</div>
</div>
);
};

export default BigSendButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ethers } from 'ethers';
import { ArrowRight } from 'phosphor-react';
import { FunctionComponent, useMemo } from 'react';
import Blockies from 'react-blockies';
import TextBox from '../../../../cells/components/TextBox';

import ICell from '../../../../cells/ICell';
import MemoryCell from '../../../../cells/MemoryCell';
import useCell from '../../../../cells/useCell';
import Loading from '../../../../components/Loading';
import onAction from '../../../../helpers/onAction';
import { useQuill } from '../../../QuillContext';
import Balance from '../Balance';

const RecipientSelector: FunctionComponent<{
recipient: ICell<string | undefined>;
}> = ({ recipient }) => {
const quill = useQuill();
const selectedAddress = useCell(quill.cells.selectedAddress);
const keyring = useCell(quill.cells.keyring);

const searchText = useMemo(() => new MemoryCell(''), []);
const searchTextValue = useCell(searchText);

const searchTextLowercase = (searchTextValue ?? '').toLowerCase();

const recipients = (() => {
if (searchTextValue && ethers.utils.isAddress(searchTextValue)) {
return [{ address: searchTextValue, name: 'Custom Recipient' }];
}

return (keyring?.wallets ?? [])
.map((wallet, i) => ({
address: wallet.address,
name: `Wallet ${i}`,
}))
.filter((r) => r.address !== selectedAddress)
.filter(
(r) =>
r.address.toLowerCase().includes(searchTextLowercase) ||
r.name.toLowerCase().includes(searchTextLowercase),
);
})();

return (
<div className="flex flex-col gap-4">
<div className="text-body">Select Recipient</div>
<div>
<TextBox value={searchText} placeholder="Search" />
</div>
{recipients.length === 0 && 'No recipients found'}
<div className="grid grid-cols-2 gap-4">
{recipients.map((r) => {
if (r === undefined) {
return <div />;
}

return (
<div
key={`${r.name}:${r.address}`}
className={[
'flex flex-row p-4 gap-4 rounded-lg bg-white border',
'border-grey-400 shadow-md cursor-pointer active:bg-grey-200',
'select-none cursor-pointer',
].join(' ')}
{...onAction(() => recipient.write(r.address))}
>
<Blockies
seed={r.address}
className="rounded-md self-center"
size={5}
scale={8}
/>
<div className="grow self-center">{r.name}</div>
<div className="self-center">
{selectedAddress && <Balance address={r.address} />}
{!selectedAddress && <Loading />}
</div>
<ArrowRight className="self-center" size={20} />
</div>
);
})}
</div>
</div>
);
};

export default RecipientSelector;
Loading