chore: refactor receive modal to share code better

This commit is contained in:
Pete Watters
2023-08-11 12:27:48 +01:00
parent efa14d094a
commit 77d8704d67
15 changed files with 144 additions and 227 deletions

View File

@@ -0,0 +1,6 @@
import BitcoinStampImg from '@assets/images/bitcoin-stamp.png';
import { Box } from '@stacks/ui';
export function BtcStampsIcon() {
return <Box as="img" src={BitcoinStampImg} width="36px" />;
}

View File

@@ -1,83 +0,0 @@
import toast from 'react-hot-toast';
import { useLocation, useNavigate } from 'react-router-dom';
import BitcoinStampImg from '@assets/images/bitcoin-stamp.png';
import { Box, Stack, useClipboard } from '@stacks/ui';
import { HomePageSelectors } from '@tests/selectors/home.selectors';
import get from 'lodash.get';
import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { OrdinalIcon } from '@app/components/icons/ordinal-icon';
import { useZeroIndexTaprootAddress } from '@app/query/bitcoin/ordinals/use-zero-index-taproot-address';
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { ReceiveCollectibleItem } from './receive-collectible-item';
export function ReceiveCollectible() {
const analytics = useAnalytics();
const location = useLocation();
const navigate = useNavigate();
const accountIndex = get(location.state, 'accountIndex', undefined);
const btcAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero();
const btcAddressTaproot = useZeroIndexTaprootAddress(accountIndex);
// TODO: Reuse later for privacy mode
// const { isLoading, isError, data: btcAddress } = useNextFreshTaprootAddressQuery(accountIndex);
const stxAddress = useCurrentAccountStxAddressState();
const { onCopy: onCopyOrdinal } = useClipboard(btcAddressTaproot);
const { onCopy: onCopyBitcoin } = useClipboard(btcAddressNativeSegwit);
const { onCopy: onCopyStacks } = useClipboard(stxAddress);
function copyBitcoinAddressToClipboard(copyHandler: () => void) {
void analytics.track('select_stamp_to_add_new_collectible');
toast.success('Copied to clipboard!');
copyHandler();
}
function copyStacksAddressToClipboard(copyHandler: () => void) {
void analytics.track('select_nft_to_add_new_collectible');
toast.success('Copied to clipboard!');
copyHandler();
}
if (!btcAddressTaproot) return null;
return (
<Stack spacing="loose" mt="base" mb="extra-loose">
<ReceiveCollectibleItem
address={btcAddressTaproot}
icon={<OrdinalIcon />}
dataTestId={HomePageSelectors.ReceiveBtcTaprootQrCodeBtn}
onCopyAddress={() => copyBitcoinAddressToClipboard(onCopyOrdinal)}
onClickQrCode={() => {
void analytics.track('select_inscription_to_add_new_collectible');
navigate(RouteUrls.ReceiveCollectibleOrdinal, { state: { btcAddressTaproot } });
}}
title="Ordinal inscription"
/>
<ReceiveCollectibleItem
address={btcAddressNativeSegwit}
icon={
<Box>
<img src={BitcoinStampImg} width="36px" />
</Box>
}
onCopyAddress={() => copyBitcoinAddressToClipboard(onCopyBitcoin)}
title="Bitcoin Stamp"
/>
<ReceiveCollectibleItem
address={stxAddress}
icon={<StxAvatar />}
dataTestId={HomePageSelectors.ReceiveStxQrCodeBtn}
onCopyAddress={() => copyStacksAddressToClipboard(onCopyStacks)}
onClickQrCode={() => navigate(RouteUrls.ReceiveStx)}
title="Stacks NFT"
/>
</Stack>
);
}

View File

@@ -1,108 +0,0 @@
import toast from 'react-hot-toast';
import { FiCopy } from 'react-icons/fi';
import { useNavigate } from 'react-router-dom';
import { Box, Button, Flex, Stack, useClipboard } from '@stacks/ui';
import { color, truncateMiddle } from '@stacks/ui-utils';
import { HomePageSelectors } from '@tests/selectors/home.selectors';
import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { BaseDrawer } from '@app/components/drawer/base-drawer';
import { BtcIcon } from '@app/components/icons/btc-icon';
import { Flag } from '@app/components/layout/flag';
import { QrCodeIcon } from '@app/components/qr-code-icon';
import { ReceiveCollectible } from '@app/components/receive/receive-collectible';
import { Caption } from '@app/components/typography';
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
export function ReceiveModal() {
const analytics = useAnalytics();
const navigate = useNavigate();
const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
const stxAddress = useCurrentAccountStxAddressState();
const { onCopy: onCopyBtc } = useClipboard(btcAddress);
const { onCopy: onCopyStacks } = useClipboard(stxAddress);
function copyToClipboard(copyHandler: () => void) {
void analytics.track('copy_address_to_clipboard');
toast.success('Copied to clipboard!');
copyHandler();
}
return (
<BaseDrawer title="Select asset to receive" isShowing onClose={() => navigate('../')}>
<Box mx="extra-loose">
<Caption style={{ fontSize: '14px' }}>Tokens</Caption>
<Stack spacing="loose" mt="base" mb="extra-loose">
<Flag img={<BtcIcon />} spacing="base">
<Flex justifyContent="space-between">
<Box>
Bitcoin
<Caption mt="2px">{truncateMiddle(btcAddress, 6)}</Caption>
</Box>
<Stack>
<Box>
<Button
borderRadius="10px"
mode="tertiary"
onClick={() => copyToClipboard(onCopyBtc)}
>
<FiCopy />
</Button>
<Button
borderRadius="10px"
data-testid={HomePageSelectors.ReceiveBtcNativeSegwitQrCodeBtn}
mode="tertiary"
ml="tight"
onClick={() => navigate(RouteUrls.ReceiveBtc)}
>
<Box color={color('text-caption')} size="14px">
<QrCodeIcon />
</Box>
</Button>
</Box>
</Stack>
</Flex>
</Flag>
<Flag img={<StxAvatar />} spacing="base">
<Flex justifyContent="space-between">
<Box>
Stacks
<Caption mt="2px">{truncateMiddle(stxAddress, 6)}</Caption>
</Box>
<Stack>
<Box>
<Button
borderRadius="10px"
mode="tertiary"
onClick={() => copyToClipboard(onCopyStacks)}
>
<FiCopy />
</Button>
<Button
borderRadius="10px"
data-testid={HomePageSelectors.ReceiveStxQrCodeBtn}
mode="tertiary"
ml="tight"
onClick={() => navigate(RouteUrls.ReceiveStx)}
>
<Box color={color('text-caption')} size="14px">
<QrCodeIcon />
</Box>
</Button>
</Box>
</Stack>
</Flex>
</Flag>
</Stack>
<Caption style={{ fontSize: '14px' }}>Collectibles</Caption>
<ReceiveCollectible />
</Box>
</BaseDrawer>
);
}

View File

@@ -3,12 +3,11 @@ import { FiCopy } from 'react-icons/fi';
import { Box, Button, ButtonProps, Flex, Stack } from '@stacks/ui';
import { color, truncateMiddle } from '@stacks/ui-utils';
import { Caption } from '@app/components//typography';
import { Flag } from '@app/components/layout/flag';
import { QrCodeIcon } from '@app/components/qr-code-icon';
import { Flag } from '../layout/flag';
import { Caption } from '../typography';
interface ReceiveCollectibleItemProps extends ButtonProps {
interface ReceiveItemProps extends ButtonProps {
address: string;
dataTestId?: string;
icon: React.JSX.Element;
@@ -16,15 +15,14 @@ interface ReceiveCollectibleItemProps extends ButtonProps {
onClickQrCode?(): void;
title: string;
}
export function ReceiveCollectibleItem({
export function ReceiveItem({
address,
dataTestId,
icon,
onCopyAddress,
onClickQrCode,
title,
...rest
}: ReceiveCollectibleItemProps) {
}: ReceiveItemProps) {
return (
<Flag img={icon} spacing="base">
<Flex justifyContent="space-between">
@@ -34,7 +32,7 @@ export function ReceiveCollectibleItem({
</Box>
<Stack>
<Box>
<Button borderRadius="10px" mode="tertiary" onClick={onCopyAddress} {...rest}>
<Button borderRadius="10px" mode="tertiary" onClick={onCopyAddress}>
<FiCopy />
</Button>
{onClickQrCode && (
@@ -45,9 +43,7 @@ export function ReceiveCollectibleItem({
ml="tight"
onClick={onClickQrCode}
>
<Box color={color('text-caption')} size="14px">
<QrCodeIcon />
</Box>
<QrCodeIcon color={color('text-caption')} size="14px" />
</Button>
)}
</Box>

View File

@@ -0,0 +1,18 @@
import { ButtonProps, Stack } from '@stacks/ui';
import { Caption } from '@app/components//typography';
interface ReceiveItemListProps extends ButtonProps {
children: React.ReactNode;
title?: string;
}
export function ReceiveItemList({ children, title }: ReceiveItemListProps) {
return (
<>
{title && <Caption>{title}</Caption>}
<Stack spacing="loose" mt="base" mb="extra-loose">
{children}
</Stack>
</>
);
}

View File

@@ -6,7 +6,7 @@ import { useCurrentAccountDisplayName } from '@app/common/hooks/account/use-acco
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { ReceiveTokensLayout } from './components/receive-tokens.layout';
import { ReceiveTokensLayout } from '../components/receive-tokens.layout';
// ts-unused-exports:disable-next-line
export function ReceiveTokens() {

View File

@@ -1,18 +0,0 @@
import { useNavigate } from 'react-router-dom';
import { Box } from '@stacks/ui';
import { BaseDrawer } from '@app/components/drawer/base-drawer';
import { ReceiveCollectible } from '@app/components/receive/receive-collectible';
export function ReceiveCollectibleModal() {
const navigate = useNavigate();
return (
<BaseDrawer title="Add collectible" isShowing onClose={() => navigate('../')}>
<Box mx="extra-loose">
<ReceiveCollectible />
</Box>
</BaseDrawer>
);
}

View File

@@ -0,0 +1,104 @@
import toast from 'react-hot-toast';
import { useLocation, useNavigate } from 'react-router-dom';
import { Box, useClipboard } from '@stacks/ui';
import { HomePageSelectors } from '@tests/selectors/home.selectors';
import get from 'lodash.get';
import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { BaseDrawer } from '@app/components/drawer/base-drawer';
import { BtcIcon } from '@app/components/icons/btc-icon';
import { BtcStampsIcon } from '@app/components/icons/btc-stamps-icon';
import { OrdinalIcon } from '@app/components/icons/ordinal-icon';
import { useZeroIndexTaprootAddress } from '@app/query/bitcoin/ordinals/use-zero-index-taproot-address';
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { ReceiveItem } from './components/receive-item';
import { ReceiveItemList } from './components/receive-items';
type ReceiveModal = 'full' | 'collectible';
interface ReceiveModalProps {
type?: 'full' | 'collectible';
}
export function ReceiveModal({ type = 'full' }: ReceiveModalProps) {
const analytics = useAnalytics();
const location = useLocation();
const navigate = useNavigate();
const btcAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero();
const stxAddress = useCurrentAccountStxAddressState();
const accountIndex = get(location.state, 'accountIndex', undefined);
const btcAddressTaproot = useZeroIndexTaprootAddress(accountIndex);
const { onCopy: onCopyBtc } = useClipboard(btcAddressNativeSegwit);
const { onCopy: onCopyStx } = useClipboard(stxAddress);
const { onCopy: onCopyOrdinal } = useClipboard(btcAddressTaproot);
function copyToClipboard(copyHandler: () => void, tracker = 'copy_address_to_clipboard') {
void analytics.track(tracker);
toast.success('Copied to clipboard!');
copyHandler();
}
const title = type === 'full' ? 'Select asset to receive' : 'Add collectible';
return (
<BaseDrawer title={title} isShowing onClose={() => navigate('../')}>
<Box mx="extra-loose">
{type === 'full' && (
<ReceiveItemList title="Tokens">
<ReceiveItem
address={btcAddressNativeSegwit}
icon={<BtcIcon />}
dataTestId={HomePageSelectors.ReceiveBtcNativeSegwitQrCodeBtn}
onCopyAddress={() => copyToClipboard(onCopyBtc)}
onClickQrCode={() => navigate(RouteUrls.ReceiveBtc)}
title="Bitcoin"
/>
<ReceiveItem
address={stxAddress}
icon={<StxAvatar />}
dataTestId={HomePageSelectors.ReceiveStxQrCodeBtn}
onCopyAddress={() => copyToClipboard(onCopyStx)}
onClickQrCode={() => navigate(RouteUrls.ReceiveStx)}
title="Stacks"
/>
</ReceiveItemList>
)}
<ReceiveItemList title={type === 'full' ? 'Collectibles' : undefined}>
<ReceiveItem
address={btcAddressTaproot}
icon={<OrdinalIcon />}
dataTestId={HomePageSelectors.ReceiveBtcTaprootQrCodeBtn}
onCopyAddress={() =>
copyToClipboard(onCopyOrdinal, 'select_stamp_to_add_new_collectible')
}
onClickQrCode={() => {
void analytics.track('select_inscription_to_add_new_collectible');
navigate(RouteUrls.ReceiveCollectibleOrdinal, { state: { btcAddressTaproot } });
}}
title="Ordinal inscription"
/>
<ReceiveItem
address={btcAddressNativeSegwit}
icon={<BtcStampsIcon />}
onCopyAddress={() => copyToClipboard(onCopyBtc, 'select_stamp_to_add_new_collectible')}
title="Bitcoin Stamp"
/>
<ReceiveItem
address={stxAddress}
icon={<StxAvatar />}
onCopyAddress={() => copyToClipboard(onCopyStx, 'select_nft_to_add_new_collectible')}
onClickQrCode={() => navigate(RouteUrls.ReceiveStx)}
title="Stacks NFT"
/>
</ReceiveItemList>
</Box>
</BaseDrawer>
);
}

View File

@@ -35,11 +35,10 @@ import { BackUpSecretKeyPage } from '@app/pages/onboarding/back-up-secret-key/ba
import { SignIn } from '@app/pages/onboarding/sign-in/sign-in';
import { WelcomePage } from '@app/pages/onboarding/welcome/welcome';
import { PsbtRequest } from '@app/pages/psbt-request/psbt-request';
import { ReceiveBtcModal } from '@app/pages/receive-tokens/receive-btc';
import { ReceiveModal } from '@app/pages/receive-tokens/receive-modal';
import { ReceiveStxModal } from '@app/pages/receive-tokens/receive-stx';
import { ReceiveCollectibleModal } from '@app/pages/receive/receive-collectible/receive-collectible-modal';
import { ReceiveCollectibleOrdinal } from '@app/pages/receive/receive-collectible/receive-collectible-oridinal';
import { ReceiveBtcModal } from '@app/pages/receive/receive-btc';
import { ReceiveCollectibleOrdinal } from '@app/pages/receive/receive-collectible-oridinal';
import { ReceiveModal } from '@app/pages/receive/receive-modal';
import { ReceiveStxModal } from '@app/pages/receive/receive-stx';
import { RequestError } from '@app/pages/request-error/request-error';
import { RpcGetAddresses } from '@app/pages/rpc-get-addresses/rpc-get-addresses';
import { rpcSendTransferRoutes } from '@app/pages/rpc-send-transfer/rpc-send-transfer.routes';
@@ -213,7 +212,10 @@ function useAppRoutes() {
<Route path={RouteUrls.IncreaseFeeSent} element={<IncreaseFeeSentDrawer />} />
<Route path={RouteUrls.Receive} element={<ReceiveModal />} />
<Route path={RouteUrls.ReceiveCollectible} element={<ReceiveCollectibleModal />} />
<Route
path={RouteUrls.ReceiveCollectible}
element={<ReceiveModal type="collectible" />}
/>
<Route
path={RouteUrls.ReceiveCollectibleOrdinal}
element={<ReceiveCollectibleOrdinal />}