Skip to content

Commit

Permalink
feat: isolate button component
Browse files Browse the repository at this point in the history
  • Loading branch information
davidecarpini committed Oct 27, 2023
1 parent f1044af commit 1019587
Show file tree
Hide file tree
Showing 15 changed files with 469 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import type { HTMLChakraProps } from '@chakra-ui/react';
import { Button, Icon, useDisclosure } from '@chakra-ui/react';
import { WalletIcon } from '@heroicons/react/24/solid';
import { useDisclosure } from '@chakra-ui/react';
import React from 'react';
import { useWallet } from '@vechain/react-wallet-kit';
import { ConnectWalletButton, useWallet } from '@vechain/react-wallet-kit';
import { AccountDetailModal } from './AccountDetailModal';
import { AddressButton } from './AddressButton';
import { ConnectWalletModal } from './ConnectWalletModal';

interface ConnectWalletButtonProps {
interface SwitchWalletButtonProps {
buttonProps?: HTMLChakraProps<'button'>;
}

export const ConnectWalletButton: React.FC<ConnectWalletButtonProps> = ({
export const SwitchWalletButton: React.FC<SwitchWalletButtonProps> = ({
buttonProps,
}): React.ReactElement => {
const { isOpen, onOpen, onClose } = useDisclosure();
Expand All @@ -38,16 +36,5 @@ export const ConnectWalletButton: React.FC<ConnectWalletButtonProps> = ({
</>
);

return (
<>
<ConnectWalletModal isOpen={isOpen} onClose={onClose} />
<Button
{...buttonProps}
leftIcon={<Icon as={WalletIcon} />}
onClick={onOpen}
>
Connect Wallet
</Button>
</>
);
return <ConnectWalletButton buttonProps={buttonProps} />;
};
2 changes: 1 addition & 1 deletion apps/sample-react-app/src/Components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ export * from './AccountDetailBody';
export * from './AccountDetailModal';
export * from './AddressButton';
export * from './AddressIcon';
export * from './ConnectWalletButton';
export * from './SwitchWalletButton';
export * from './ConnectWalletModal';
export * from './WalletSourceRadio';
6 changes: 3 additions & 3 deletions apps/sample-react-app/src/Components/layout/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { Bars3Icon } from '@heroicons/react/24/solid';
import { useWallet } from '@vechain/react-wallet-kit';
import { VechainLogo } from '../../Logos';
import { AccountDetailBody } from '../AccountDetailBody';
import { ConnectWalletButton } from '../ConnectWalletButton';
import { SwitchWalletButton } from '../SwitchWalletButton';

export const NavBar = (): JSX.Element => {
const bg = useColorModeValue('gray.50', 'gray.900');
Expand Down Expand Up @@ -101,7 +101,7 @@ const MobileNavBarDrawer = ({
source={accountState.source}
/>
) : (
<ConnectWalletButton />
<SwitchWalletButton />
)}
</VStack>
</VStack>
Expand All @@ -114,7 +114,7 @@ const MobileNavBarDrawer = ({
const NavBarWalletConnect = (): JSX.Element => {
return (
<HStack spacing={4}>
<ConnectWalletButton />
<SwitchWalletButton />
</HStack>
);
};
4 changes: 2 additions & 2 deletions apps/sample-react-app/src/Screens/components/Welcome.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button, Heading, HStack, Link, Text, VStack } from '@chakra-ui/react';
import type { JSX } from 'react';
import { StyledCard } from '../../Components/shared';
import { ConnectWalletButton } from '../../Components';
import { SwitchWalletButton } from '../../Components';

export const Welcome = (): JSX.Element => {
return (
Expand All @@ -18,7 +18,7 @@ export const Welcome = (): JSX.Element => {
w="full"
wrap="wrap"
>
<ConnectWalletButton />
<SwitchWalletButton />
<Link
href="https://github.com/vechainfoundation/veworld-dapp"
isExternal
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
],
"scripts": {
"build": "turbo run build",
"build-react-kit": "turbo run build --filter='@vechain/react-wallet-kit'",
"build:deps": "turbo build --no-daemon --filter='@vechain/*'",
"clean": "npx turbo@latest run clean && rm -rf node_modules .turbo",
"dev": "turbo run dev --no-daemon",
Expand Down
3 changes: 3 additions & 0 deletions packages/react-wallet-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
"lint": "eslint src --ext .js,.jsx,.ts,.tsx"
},
"dependencies": {
"@chakra-ui/react": "^2.8.1",
"@heroicons/react": "^2.0.18",
"@vechain/connex": "2.1.0",
"@vechain/connex-framework": "2.1.0",
"@vechain/picasso": "^2.1.1",
"@vechain/wallet-connect": "*",
"@vechain/wallet-kit": "*",
"thor-devkit": "^2.0.9"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import {
Alert,
AlertIcon,
Box,
Button,
Flex,
HStack,
Icon,
Spinner,
Text,
useToast,
VStack,
} from '@chakra-ui/react';
import { LinkIcon, WalletIcon } from '@heroicons/react/24/solid';
import React, { useCallback, useState } from 'react';
import { Certificate } from 'thor-devkit';
import { Dialog } from './Dialog';
import { WalletSourceRadio } from './WalletSourceRadio';
import { useConnex, useWallet } from '../../ConnexProvider';

Check warning on line 19 in packages/react-wallet-kit/src/ConnectWalletButton/Components/ConnectWalletModal.tsx

View workflow job for this annotation

GitHub Actions / Lint, Build & Test

`../../ConnexProvider` import should occur before import of `./Dialog`

interface ConnectedWalletDialogProps {
isOpen: boolean;
onClose: () => void;
}

export const ConnectWalletModal: React.FC<ConnectedWalletDialogProps> = ({
isOpen,
onClose,
}) => {
const header = (
<HStack spacing={2}>
<Icon as={WalletIcon} />
<Text>Connect Wallet</Text>
</HStack>
);

return (
<Dialog
body={<ConnectedWalletBody onClose={onClose} />}
header={header}
isOpen={isOpen}
onClose={onClose}
/>
);
};

interface ConnectedWalletBodyProps {
onClose: () => void;
}

const ConnectedWalletBody: React.FC<ConnectedWalletBodyProps> = ({
onClose,
}) => {
const toast = useToast();
const { setAccount } = useWallet();
const { vendor } = useConnex();

const [connectionLoading, setConnectionLoading] = useState(false);
const [connectionError, setConnectionError] = useState('');

const connectToWalletHandler =
useCallback(async (): Promise<Certificate> => {
const message: Connex.Vendor.CertMessage = {
purpose: 'identification',
payload: {
type: 'text',
content: 'Sign a certificate to prove your identity',
},
};

if (!vendor) throw new Error('Vendor not available');

const certResponse = await vendor.sign('cert', message).request();

const cert: Certificate = {
purpose: message.purpose,
payload: message.payload,
domain: certResponse.annex.domain,
timestamp: certResponse.annex.timestamp,
signer: certResponse.annex.signer,
signature: certResponse.signature,
};

Certificate.verify(cert);

return cert;
}, [vendor]);

const onSuccessfullConnection = useCallback(
(cert: Certificate): void => {
setAccount(cert.signer);
onClose();
toast({
title: 'Wallet connected.',
description: `You've succesfully connected with wallet ${cert.signer}`,
status: 'success',
position: 'bottom-left',
duration: 5000,
isClosable: true,
});
},
[toast, setAccount, onClose],
);

const connectHandler = useCallback(async () => {
try {
setConnectionError('');
setConnectionLoading(true);

const cert = await connectToWalletHandler();

onSuccessfullConnection(cert);
} catch (e) {
if (e instanceof Error) {
setConnectionError(e.message);
} else {
setConnectionError('Failed to connect to wallet');
}
} finally {
setConnectionLoading(false);
}
}, [
onSuccessfullConnection,
setConnectionError,
setConnectionLoading,
connectToWalletHandler,
]);

const connect = useCallback(() => {
connectHandler().catch((e) => {
throw e;
});
}, [connectHandler]);

return (
<>
<Flex direction="column" gap={8}>
<Box>
<Text mb="8px">Wallet</Text>
<WalletSourceRadio />
</Box>
</Flex>
<VStack mt={8} spacing={4} w="full">
{connectionLoading ? (
<Alert status="warning">
<AlertIcon />
Waiting for wallet approval...
</Alert>
) : null}
{connectionError ? (
<Alert status="error">
<AlertIcon />
{connectionError}
</Alert>
) : null}

<Button
colorScheme="blue"
disabled={connectionLoading}
leftIcon={
connectionLoading ? <Spinner /> : <Icon as={LinkIcon} />
}
onClick={connect}
w="full"
>
{connectionLoading ? 'Connecting...' : 'Connect'}
</Button>
</VStack>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { HTMLChakraProps } from '@chakra-ui/react';
import {
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from '@chakra-ui/react';
import React from 'react';

interface DialogProps {
isOpen: boolean;
onClose: () => void;
header?: React.ReactNode;
headerStyle?: HTMLChakraProps<'header'>;
body?: React.ReactNode;
footer?: React.ReactNode;
showCloseButton?: boolean;
closeButtonStyle?: HTMLChakraProps<'button'>;
}

export const Dialog: React.FC<DialogProps> = ({
isOpen,
onClose,
header,
headerStyle = {},
body,
footer,
showCloseButton = true,
closeButtonStyle = {},
}) => {
return (
<Modal isOpen={isOpen} onClose={onClose} trapFocus={false}>
<ModalOverlay />
<ModalContent>
{header ? (
<ModalHeader {...headerStyle}>{header}</ModalHeader>
) : null}
{showCloseButton ? (
<ModalCloseButton {...closeButtonStyle} />
) : null}
{body ? <ModalBody>{body}</ModalBody> : null}
{footer ? <ModalFooter>{footer}</ModalFooter> : null}
</ModalContent>
</Modal>
);
};
Loading

0 comments on commit 1019587

Please sign in to comment.