Commit fbfd2d73 authored by tom's avatar tom

verified addresses modal

parent 6f894977
...@@ -4,12 +4,12 @@ import React from 'react'; ...@@ -4,12 +4,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
// const VerifiedAddresses = dynamic(() => import('ui/pages/VerifiedAddresses'), { ssr: false }); const VerifiedAddresses = dynamic(() => import('ui/pages/VerifiedAddresses'), { ssr: false });
const Page: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageNextJs pathname="/account/verified-addresses"> <PageNextJs pathname="/account/verified-addresses">
{ /* <VerifiedAddresses/> */ } <VerifiedAddresses/>
</PageNextJs> </PageNextJs>
); );
}; };
......
import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react'; import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
import * as React from 'react'; import * as React from 'react';
import ButtonBackTo from 'ui/shared/buttons/ButtonBackTo';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
interface DialogContentProps extends ChakraDialog.ContentProps { interface DialogContentProps extends ChakraDialog.ContentProps {
...@@ -51,13 +53,16 @@ export const DialogCloseTrigger = React.forwardRef< ...@@ -51,13 +53,16 @@ export const DialogCloseTrigger = React.forwardRef<
export interface DialogHeaderProps extends ChakraDialog.HeaderProps { export interface DialogHeaderProps extends ChakraDialog.HeaderProps {
startElement?: React.ReactNode; startElement?: React.ReactNode;
onBackToClick?: () => void;
} }
export const DialogHeader = React.forwardRef< export const DialogHeader = React.forwardRef<
HTMLDivElement, HTMLDivElement,
DialogHeaderProps DialogHeaderProps
>(function DialogHeader(props, ref) { >(function DialogHeader(props, ref) {
const { startElement, ...rest } = props; const { startElement: startElementProp, onBackToClick, ...rest } = props;
const startElement = startElementProp ?? (onBackToClick && <ButtonBackTo onClick={ onBackToClick }/>);
return ( return (
<ChakraDialog.Header ref={ ref } { ...rest }> <ChakraDialog.Header ref={ ref } { ...rest }>
......
import type { ThemingConfig } from '@chakra-ui/react'; const colors = {
import type { ExcludeUndefined } from 'types/utils';
const colors: ExcludeUndefined<ThemingConfig['tokens']>['colors'] = {
green: { green: {
'50': { value: '#F0FFF4' }, '50': { value: '#F0FFF4' },
'100': { value: '#C6F6D5' }, '100': { value: '#C6F6D5' },
......
import { Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Link } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess } from './types'; import type { AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess } from './types';
...@@ -6,7 +5,7 @@ import type { VerifiedAddress } from 'types/api/account'; ...@@ -6,7 +5,7 @@ import type { VerifiedAddress } from 'types/api/account';
import config from 'configs/app'; import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import IconSvg from 'ui/shared/IconSvg'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
import Web3ModalProvider from 'ui/shared/Web3ModalProvider'; import Web3ModalProvider from 'ui/shared/Web3ModalProvider';
import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress'; import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress';
...@@ -16,8 +15,8 @@ import AddressVerificationStepSuccess from './steps/AddressVerificationStepSucce ...@@ -16,8 +15,8 @@ import AddressVerificationStepSuccess from './steps/AddressVerificationStepSucce
type StateData = AddressVerificationFormFirstStepFields & AddressCheckStatusSuccess & { isToken?: boolean }; type StateData = AddressVerificationFormFirstStepFields & AddressCheckStatusSuccess & { isToken?: boolean };
interface Props { interface Props {
isOpen: boolean; open: boolean;
onClose: () => void; onOpenChange: ({ open }: { open: boolean }) => void;
onSubmit: (address: VerifiedAddress) => void; onSubmit: (address: VerifiedAddress) => void;
onAddTokenInfoClick: (address: string) => void; onAddTokenInfoClick: (address: string) => void;
onShowListClick: () => void; onShowListClick: () => void;
...@@ -25,16 +24,16 @@ interface Props { ...@@ -25,16 +24,16 @@ interface Props {
pageType: string; pageType: string;
} }
const AddressVerificationModal = ({ defaultAddress, isOpen, onClose, onSubmit, onAddTokenInfoClick, onShowListClick, pageType }: Props) => { const AddressVerificationModal = ({ defaultAddress, open, onOpenChange, onSubmit, onAddTokenInfoClick, onShowListClick, pageType }: Props) => {
const [ stepIndex, setStepIndex ] = React.useState(0); const [ stepIndex, setStepIndex ] = React.useState(0);
const [ data, setData ] = React.useState<StateData>({ address: '', signingMessage: '' }); const [ data, setData ] = React.useState<StateData>({ address: '', signingMessage: '' });
React.useEffect(() => { React.useEffect(() => {
isOpen && mixpanel.logEvent( open && mixpanel.logEvent(
mixpanel.EventTypes.VERIFY_ADDRESS, mixpanel.EventTypes.VERIFY_ADDRESS,
{ Action: 'Form opened', 'Page type': pageType }, { Action: 'Form opened', 'Page type': pageType },
); );
}, [ isOpen, pageType ]); }, [ open, pageType ]);
const handleGoToSecondStep = React.useCallback((firstStepResult: typeof data) => { const handleGoToSecondStep = React.useCallback((firstStepResult: typeof data) => {
setData(firstStepResult); setData(firstStepResult);
...@@ -59,16 +58,18 @@ const AddressVerificationModal = ({ defaultAddress, isOpen, onClose, onSubmit, o ...@@ -59,16 +58,18 @@ const AddressVerificationModal = ({ defaultAddress, isOpen, onClose, onSubmit, o
setStepIndex((prev) => prev - 1); setStepIndex((prev) => prev - 1);
}, []); }, []);
const handleClose = React.useCallback(() => { const handleOpenChange = React.useCallback(({ open }: { open: boolean }) => {
onClose(); onOpenChange({ open });
if (!open) {
setStepIndex(0); setStepIndex(0);
setData({ address: '', signingMessage: '' }); setData({ address: '', signingMessage: '' });
}, [ onClose ]); }
}, [ onOpenChange ]);
const handleAddTokenInfoClick = React.useCallback(() => { const handleAddTokenInfoClick = React.useCallback(() => {
onAddTokenInfoClick(data.address); onAddTokenInfoClick(data.address);
handleClose(); handleOpenChange({ open: false });
}, [ handleClose, data.address, onAddTokenInfoClick ]); }, [ handleOpenChange, data.address, onAddTokenInfoClick ]);
const steps = [ const steps = [
{ {
...@@ -100,25 +101,26 @@ const AddressVerificationModal = ({ defaultAddress, isOpen, onClose, onSubmit, o ...@@ -100,25 +101,26 @@ const AddressVerificationModal = ({ defaultAddress, isOpen, onClose, onSubmit, o
const step = steps[stepIndex]; const step = steps[stepIndex];
return ( return (
<Modal isOpen={ isOpen } onClose={ handleClose } size={{ base: 'full', lg: 'md' }}> <DialogRoot
<ModalOverlay/> open={ open }
<ModalContent> onOpenChange={ handleOpenChange }
<ModalHeader fontWeight="500" textStyle="h3" mb={ 6 }> size={{ lgDown: 'full', lg: 'md' }}
{ stepIndex !== 0 && ( closeOnInteractOutside={ false }
<Link mr={ 3 } onClick={ handleGoToPrevStep }> modal={ false }
<IconSvg name="arrows/east" boxSize={ 6 } transform="rotate(180deg)" verticalAlign="middle" color="gray.400"/> trapFocus={ false }
</Link> preventScroll={ false }
) } >
<span>{ step.title }</span> <DialogContent>
</ModalHeader> <DialogHeader onBackToClick={ stepIndex !== 0 ? handleGoToPrevStep : undefined }>
<ModalCloseButton/> { step.title }
<ModalBody mb={ 0 }> </DialogHeader>
<DialogBody mb={ 0 }>
<Web3ModalProvider> <Web3ModalProvider>
{ step.content } { step.content }
</Web3ModalProvider> </Web3ModalProvider>
</ModalBody> </DialogBody>
</ModalContent> </DialogContent>
</Modal> </DialogRoot>
); );
}; };
......
import { Alert, Box, Button, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
...@@ -16,9 +16,12 @@ import { route } from 'nextjs-routes'; ...@@ -16,9 +16,12 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import { Alert } from 'toolkit/chakra/alert';
import { Button } from 'toolkit/chakra/button';
import { Link } from 'toolkit/chakra/link'; import { Link } from 'toolkit/chakra/link';
import FormFieldAddress from 'ui/shared/forms/fields/FormFieldAddress'; import FormFieldAddress from 'ui/shared/forms/fields/FormFieldAddress';
import AdminSupportText from 'ui/shared/texts/AdminSupportText'; import AdminSupportText from 'ui/shared/texts/AdminSupportText';
type Fields = RootFields & AddressVerificationFormFirstStepFields; type Fields = RootFields & AddressVerificationFormFirstStepFields;
interface Props { interface Props {
...@@ -105,13 +108,13 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) = ...@@ -105,13 +108,13 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) =
{ rootError && <Alert status="warning" mt={ 3 }>{ rootError }</Alert> } { rootError && <Alert status="warning" mt={ 3 }>{ rootError }</Alert> }
<FormFieldAddress<Fields> <FormFieldAddress<Fields>
name="address" name="address"
isRequired required
bgColor="dialog.bg" bgColor="dialog.bg"
placeholder="Smart contract address (0x...)" placeholder="Smart contract address (0x...)"
mt={ 8 } mt={ 8 }
/> />
<Flex alignItems={{ base: 'flex-start', lg: 'center' }} mt={ 8 } columnGap={ 5 } rowGap={ 2 } flexDir={{ base: 'column', lg: 'row' }}> <Flex alignItems={{ base: 'flex-start', lg: 'center' }} mt={ 8 } columnGap={ 5 } rowGap={ 2 } flexDir={{ base: 'column', lg: 'row' }}>
<Button size="lg" type="submit" isLoading={ formState.isSubmitting } loadingText="Continue" flexShrink={ 0 }> <Button size="lg" type="submit" loading={ formState.isSubmitting } loadingText="Continue" flexShrink={ 0 }>
Continue Continue
</Button> </Button>
<AdminSupportText/> <AdminSupportText/>
......
import { Alert, Box, Button, chakra, Flex, Link, Radio, RadioGroup } from '@chakra-ui/react'; import { Box, chakra, Flex } from '@chakra-ui/react';
import { useAppKit } from '@reown/appkit/react'; import { useAppKit } from '@reown/appkit/react';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
...@@ -18,6 +18,10 @@ import type { VerifiedAddress } from 'types/api/account'; ...@@ -18,6 +18,10 @@ import type { VerifiedAddress } from 'types/api/account';
import config from 'configs/app'; import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import shortenString from 'lib/shortenString'; import shortenString from 'lib/shortenString';
import { Alert } from 'toolkit/chakra/alert';
import { Button } from 'toolkit/chakra/button';
import { Link } from 'toolkit/chakra/link';
import { Radio, RadioGroup } from 'toolkit/chakra/radio';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import FormFieldText from 'ui/shared/forms/fields/FormFieldText'; import FormFieldText from 'ui/shared/forms/fields/FormFieldText';
import { SIGNATURE_REGEXP } from 'ui/shared/forms/validators/signature'; import { SIGNATURE_REGEXP } from 'ui/shared/forms/validators/signature';
...@@ -81,8 +85,8 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre ...@@ -81,8 +85,8 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
const { signMessage, isPending: isSigning } = useSignMessage(); const { signMessage, isPending: isSigning } = useSignMessage();
const handleSignMethodChange = React.useCallback((value: typeof signMethod) => { const handleSignMethodChange = React.useCallback(({ value }: { value: string }) => {
setSignMethod(value); setSignMethod(value as SignMethod);
clearErrors('root'); clearErrors('root');
}, [ clearErrors ]); }, [ clearErrors ]);
...@@ -121,7 +125,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre ...@@ -121,7 +125,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
<Button <Button
size="lg" size="lg"
onClick={ handleManualSignClick } onClick={ handleManualSignClick }
isLoading={ formState.isSubmitting } loading={ formState.isSubmitting }
loadingText="Verifying" loadingText="Verifying"
> >
Verify Verify
...@@ -133,7 +137,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre ...@@ -133,7 +137,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
<Button <Button
size="lg" size="lg"
onClick={ isConnected ? handleWeb3SignClick : handleOpenWeb3Modal } onClick={ isConnected ? handleWeb3SignClick : handleOpenWeb3Modal }
isLoading={ formState.isSubmitting || isSigning } loading={ formState.isSubmitting || isSigning }
loadingText={ isSigning ? 'Signing' : 'Verifying' } loadingText={ isSigning ? 'Signing' : 'Verifying' }
> >
{ isConnected ? 'Sign and verify' : 'Connect wallet' } { isConnected ? 'Sign and verify' : 'Connect wallet' }
...@@ -217,15 +221,23 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre ...@@ -217,15 +221,23 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
<FormFieldText<Fields> <FormFieldText<Fields>
name="message" name="message"
placeholder="Message to sign" placeholder="Message to sign"
isRequired required
asComponent="Textarea" asComponent="Textarea"
isReadOnly readOnly
maxH={{ base: '140px', lg: '80px' }} inputProps={{
bgColor="dialog.bg" h: { base: '180px', lg: '130px' },
minH: 'auto',
}}
/> />
</div> </div>
{ !noWeb3Provider && ( { !noWeb3Provider && (
<RadioGroup onChange={ handleSignMethodChange } value={ signMethod } display="flex" flexDir="column" rowGap={ 4 }> <RadioGroup
onValueChange={ handleSignMethodChange }
value={ signMethod }
display="flex"
flexDir="column"
rowGap={ 4 }
>
<Radio value="wallet">Sign via Web3 wallet</Radio> <Radio value="wallet">Sign via Web3 wallet</Radio>
<Radio value="manual">Sign manually</Radio> <Radio value="manual">Sign manually</Radio>
</RadioGroup> </RadioGroup>
...@@ -234,7 +246,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre ...@@ -234,7 +246,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre
<FormFieldText<Fields> <FormFieldText<Fields>
name="signature" name="signature"
placeholder="Signature hash" placeholder="Signature hash"
isRequired required
rules={{ pattern: SIGNATURE_REGEXP }} rules={{ pattern: SIGNATURE_REGEXP }}
bgColor="dialog.bg" bgColor="dialog.bg"
/> />
......
import { Alert, Box, Button, chakra, Flex } from '@chakra-ui/react'; import { Box, chakra, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
import { Button } from 'toolkit/chakra/button';
interface Props { interface Props {
onShowListClick: () => void; onShowListClick: () => void;
onAddTokenInfoClick: () => void; onAddTokenInfoClick: () => void;
...@@ -11,7 +14,7 @@ interface Props { ...@@ -11,7 +14,7 @@ interface Props {
const AddressVerificationStepSuccess = ({ onAddTokenInfoClick, onShowListClick, isToken, address }: Props) => { const AddressVerificationStepSuccess = ({ onAddTokenInfoClick, onShowListClick, isToken, address }: Props) => {
return ( return (
<Box> <Box>
<Alert status="success" flexWrap="wrap" whiteSpace="pre-wrap" wordBreak="break-word" mb={ 3 } display="inline-block"> <Alert status="success" descriptionProps={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }} mb={ 3 } display="inline-block">
<span>The address ownership for </span> <span>The address ownership for </span>
<chakra.span fontWeight={ 700 }>{ address }</chakra.span> <chakra.span fontWeight={ 700 }>{ address }</chakra.span>
<span> is verified.</span> <span> is verified.</span>
......
import { OrderedList, ListItem, chakra, Button, useDisclosure, Show, Hide, Link } from '@chakra-ui/react'; import { List, chakra, Box } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -10,9 +10,12 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; ...@@ -10,9 +10,12 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType'; import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { TOKEN_INFO_APPLICATION, VERIFIED_ADDRESS } from 'stubs/account'; import { TOKEN_INFO_APPLICATION, VERIFIED_ADDRESS } from 'stubs/account';
import { Button } from 'toolkit/chakra/button';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal'; import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription'; import AccountPageDescription from 'ui/shared/AccountPageDescription';
import Skeleton from 'ui/shared/chakra/Skeleton';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import AdminSupportText from 'ui/shared/texts/AdminSupportText'; import AdminSupportText from 'ui/shared/texts/AdminSupportText';
...@@ -111,18 +114,16 @@ const VerifiedAddresses = () => { ...@@ -111,18 +114,16 @@ const VerifiedAddresses = () => {
const addButton = (() => { const addButton = (() => {
if (userWithoutEmail) { if (userWithoutEmail) {
return ( return (
<Button size="lg" isDisabled mt={ 8 }> <Button size="lg" disabled mt={ 8 }>
Add address Add address
</Button> </Button>
); );
} }
return ( return (
<Skeleton mt={ 8 } isLoaded={ !isLoading } display="inline-block"> <Button size="lg" onClick={ modalProps.onOpen } loadingSkeleton={ isLoading } mt={ 8 }>
<Button size="lg" onClick={ modalProps.onOpen }>
Add address Add address
</Button> </Button>
</Skeleton>
); );
})(); })();
...@@ -143,12 +144,12 @@ const VerifiedAddresses = () => { ...@@ -143,12 +144,12 @@ const VerifiedAddresses = () => {
return ( return (
<> <>
<PageTitle title="Token info application form" backLink={ backLink }/> <PageTitle title="Token info application form" backLink={ backLink }/>
<TokenInfoForm { /* <TokenInfoForm
address={ selectedAddress } address={ selectedAddress }
tokenName={ tokenName } tokenName={ tokenName }
application={ applicationsQuery.data?.submissions.find(({ tokenAddress }) => tokenAddress.toLowerCase() === selectedAddress.toLowerCase()) } application={ applicationsQuery.data?.submissions.find(({ tokenAddress }) => tokenAddress.toLowerCase() === selectedAddress.toLowerCase()) }
onSubmit={ handleApplicationSubmit } onSubmit={ handleApplicationSubmit }
/> /> */ }
</> </>
); );
} }
...@@ -161,7 +162,7 @@ const VerifiedAddresses = () => { ...@@ -161,7 +162,7 @@ const VerifiedAddresses = () => {
if (addressesQuery.data?.verifiedAddresses) { if (addressesQuery.data?.verifiedAddresses) {
return ( return (
<> <>
<Show below="lg" key="content-mobile" ssr={ false }> <Box hideFrom="lg" key="content-mobile">
{ addressesQuery.data.verifiedAddresses.map((item, index) => ( { addressesQuery.data.verifiedAddresses.map((item, index) => (
<VerifiedAddressesListItem <VerifiedAddressesListItem
key={ item.contractAddress + (isLoading ? index : '') } key={ item.contractAddress + (isLoading ? index : '') }
...@@ -175,8 +176,8 @@ const VerifiedAddresses = () => { ...@@ -175,8 +176,8 @@ const VerifiedAddresses = () => {
isLoading={ isLoading } isLoading={ isLoading }
/> />
)) } )) }
</Show> </Box>
<Hide below="lg" key="content-desktop" ssr={ false }> <Box hideBelow="lg" key="content-desktop">
<VerifiedAddressesTable <VerifiedAddressesTable
data={ addressesQuery.data.verifiedAddresses } data={ addressesQuery.data.verifiedAddresses }
applications={ applicationsQuery.data?.submissions } applications={ applicationsQuery.data?.submissions }
...@@ -184,7 +185,7 @@ const VerifiedAddresses = () => { ...@@ -184,7 +185,7 @@ const VerifiedAddresses = () => {
onItemAdd={ handleItemAdd } onItemAdd={ handleItemAdd }
isLoading={ isLoading } isLoading={ isLoading }
/> />
</Hide> </Box>
</> </>
); );
} }
...@@ -206,14 +207,14 @@ const VerifiedAddresses = () => { ...@@ -206,14 +207,14 @@ const VerifiedAddresses = () => {
<chakra.p fontWeight={ 600 } mt={ 5 }> <chakra.p fontWeight={ 600 } mt={ 5 }>
Before starting, make sure that: Before starting, make sure that:
</chakra.p> </chakra.p>
<OrderedList ml={ 6 }> <List.Root ml={ 6 } as="ol">
<ListItem>The source code for the smart contract is deployed on “{ config.chain.name }”.</ListItem> <List.Item _marker={{ color: 'inherit' }}>The source code for the smart contract is deployed on “{ config.chain.name }”.</List.Item>
<ListItem> <List.Item _marker={{ color: 'inherit' }}>
<span>The source code is verified (if not yet verified, you can use </span> <span>The source code is verified (if not yet verified, you can use </span>
<Link href="https://docs.blockscout.com/for-users/verifying-a-smart-contract" target="_blank">this tool</Link> <Link href="https://docs.blockscout.com/for-users/verifying-a-smart-contract" target="_blank">this tool</Link>
<span>).</span> <span>).</span>
</ListItem> </List.Item>
</OrderedList> </List.Root>
<chakra.div mt={ 5 }> <chakra.div mt={ 5 }>
Once these steps are complete, click the Add address button below to get started. Once these steps are complete, click the Add address button below to get started.
</chakra.div> </chakra.div>
...@@ -221,15 +222,16 @@ const VerifiedAddresses = () => { ...@@ -221,15 +222,16 @@ const VerifiedAddresses = () => {
</AccountPageDescription> </AccountPageDescription>
<DataListDisplay <DataListDisplay
isError={ profileQuery.isError || addressesQuery.isError || applicationsQuery.isError } isError={ profileQuery.isError || addressesQuery.isError || applicationsQuery.isError }
items={ addressesQuery.data?.verifiedAddresses } itemsNum={ addressesQuery.data?.verifiedAddresses.length }
content={ content }
emptyText="" emptyText=""
/> >
{ content }
</DataListDisplay>
{ addButton } { addButton }
<AddressVerificationModal <AddressVerificationModal
pageType={ PAGE_TYPE_DICT['/account/verified-addresses'] } pageType={ PAGE_TYPE_DICT['/account/verified-addresses'] }
isOpen={ modalProps.isOpen } open={ modalProps.open }
onClose={ modalProps.onClose } onOpenChange={ modalProps.onOpenChange }
onSubmit={ handleAddressSubmit } onSubmit={ handleAddressSubmit }
onAddTokenInfoClick={ handleItemAdd } onAddTokenInfoClick={ handleItemAdd }
onShowListClick={ modalProps.onClose } onShowListClick={ modalProps.onClose }
......
...@@ -4,12 +4,11 @@ import React from 'react'; ...@@ -4,12 +4,11 @@ import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { Heading } from 'toolkit/chakra/heading'; import { Heading } from 'toolkit/chakra/heading';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip'; import { Tooltip } from 'toolkit/chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import IconSvg from 'ui/shared/IconSvg'; import ButtonBackTo from 'ui/shared/buttons/ButtonBackTo';
type BackLinkProp = { label: string; url: string } | { label: string; onClick: () => void }; type BackLinkProp = { label: string; url: string } | { label: string; onClick: () => void };
...@@ -27,37 +26,6 @@ type Props = { ...@@ -27,37 +26,6 @@ type Props = {
const TEXT_MAX_LINES = 1; const TEXT_MAX_LINES = 1;
const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
if (!props) {
return null;
}
if (props.isLoading) {
return (
<Skeleton
boxSize={ 6 }
display="inline-block"
flexShrink={ 0 }
borderRadius="base"
mr={ 3 }
my={ 2 }
verticalAlign="text-bottom"
loading
/>
);
}
const icon = <IconSvg name="arrows/east" boxSize={ 6 } transform="rotate(180deg)" margin="auto" color="gray.400" flexShrink={ 0 }/>;
return (
<Tooltip content={ props.label }>
<Link display="inline-flex" href={ 'url' in props ? props.url : undefined } onClick={ 'onClick' in props ? props.onClick : undefined } h="40px" mr={ 3 }>
{ icon }
</Link>
</Tooltip>
);
};
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading = false, afterTitle, beforeTitle, secondRow }: Props) => { const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading = false, afterTitle, beforeTitle, secondRow }: Props) => {
const tooltip = useDisclosure(); const tooltip = useDisclosure();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
...@@ -113,7 +81,15 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa ...@@ -113,7 +81,15 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
alignItems="center" alignItems="center"
> >
<Flex h={{ base: 'auto', lg: isLoading ? 10 : 'auto' }} maxW="100%" alignItems="center"> <Flex h={{ base: 'auto', lg: isLoading ? 10 : 'auto' }} maxW="100%" alignItems="center">
{ backLink && <BackLink { ...backLink } isLoading={ isLoading }/> } { backLink && (
<ButtonBackTo
hint={ backLink.label }
href={ 'url' in backLink ? backLink.url : undefined }
onClick={ 'onClick' in backLink ? backLink.onClick : undefined }
loadingSkeleton={ isLoading }
mr={ 3 }
/>
) }
{ beforeTitle } { beforeTitle }
<Skeleton <Skeleton
loading={ isLoading } loading={ isLoading }
......
...@@ -3,13 +3,13 @@ import { createAppKit, useAppKitTheme } from '@reown/appkit/react'; ...@@ -3,13 +3,13 @@ import { createAppKit, useAppKitTheme } from '@reown/appkit/react';
import React from 'react'; import React from 'react';
import { WagmiProvider } from 'wagmi'; import { WagmiProvider } from 'wagmi';
import config from 'configs/app';
import { currentChain, parentChain } from 'lib/web3/chains';
import wagmiConfig from 'lib/web3/wagmiConfig';
import { useColorMode } from 'toolkit/chakra/color-mode'; import { useColorMode } from 'toolkit/chakra/color-mode';
import colors from 'toolkit/theme/foundations/colors'; import colors from 'toolkit/theme/foundations/colors';
import { BODY_TYPEFACE } from 'toolkit/theme/foundations/typography'; import { BODY_TYPEFACE } from 'toolkit/theme/foundations/typography';
import zIndex from 'toolkit/theme/foundations/zIndex'; import zIndex from 'toolkit/theme/foundations/zIndex';
import config from 'configs/app';
import { currentChain, parentChain } from 'lib/web3/chains';
import wagmiConfig from 'lib/web3/wagmiConfig';
const feature = config.features.blockchainInteraction; const feature = config.features.blockchainInteraction;
......
...@@ -18,15 +18,15 @@ type AdData = { ...@@ -18,15 +18,15 @@ type AdData = {
}; };
}; };
const MOCK: AdData = { // const MOCK: AdData = {
ad: { // ad: {
url: 'https://unsplash.com/s/photos/cute-kitten', // url: 'https://unsplash.com/s/photos/cute-kitten',
thumbnail: 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/gnosis.svg', // thumbnail: 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/gnosis.svg',
name: 'All about kitties', // name: 'All about kitties',
description_short: 'To see millions picture of cute kitties', // description_short: 'To see millions picture of cute kitties',
cta_button: 'click here', // cta_button: 'click here',
}, // },
}; // };
const CoinzillaTextAd = ({ className }: { className?: string }) => { const CoinzillaTextAd = ({ className }: { className?: string }) => {
const [ adData, setAdData ] = React.useState<AdData | null>(null); const [ adData, setAdData ] = React.useState<AdData | null>(null);
...@@ -44,7 +44,7 @@ const CoinzillaTextAd = ({ className }: { className?: string }) => { ...@@ -44,7 +44,7 @@ const CoinzillaTextAd = ({ className }: { className?: string }) => {
} }
}) })
.finally(() => { .finally(() => {
setAdData(MOCK); // setAdData(MOCK);
setIsLoading(false); setIsLoading(false);
}); });
} }
......
import React from 'react'; import React from 'react';
import type { ButtonProps } from 'toolkit/chakra/button';
import { IconButton } from 'toolkit/chakra/icon-button'; import { IconButton } from 'toolkit/chakra/icon-button';
import { Link } from 'toolkit/chakra/link';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props extends ButtonProps {
onClick: () => void; href?: string;
hint?: string;
} }
const ButtonBackTo = ({ onClick }: Props) => { const ButtonBackTo = ({ href, hint, ...rest }: Props) => {
return (
<IconButton> const button = (
<IconButton { ...rest }>
<IconSvg <IconSvg
name="arrows/east" name="arrows/east"
boxSize={ 6 } boxSize={ 6 }
transform="rotate(180deg)" transform="rotate(180deg)"
color="icon.backTo" color="icon.backTo"
_hover={{ color: 'link.primary.hover' }} _hover={{ color: 'link.primary.hover' }}
onClick={ onClick }
/> />
</IconButton> </IconButton>
); );
return (
<Tooltip content={ hint } disabled={ !hint }>
{ href ? <Link href={ href } asChild>{ button }</Link> : button }
</Tooltip>
);
}; };
export default React.memo(ButtonBackTo); export default React.memo(ButtonBackTo);
import { Box, Link, chakra } from '@chakra-ui/react'; import { Box, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Link } from 'toolkit/chakra/link';
interface Props { interface Props {
className?: string; className?: string;
} }
......
import { IconButton, Link, Tooltip } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account'; import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { IconButton } from 'toolkit/chakra/icon-button';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -37,7 +39,7 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading ...@@ -37,7 +39,7 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
const tokenInfo = (() => { const tokenInfo = (() => {
if (isLoading) { if (isLoading) {
return <Skeleton height={ 6 } width="140px"/>; return <Skeleton loading height={ 6 } width="140px"/>;
} }
if (!item.metadata.tokenName) { if (!item.metadata.tokenName) {
...@@ -64,16 +66,16 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading ...@@ -64,16 +66,16 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
noCopy noCopy
noSymbol noSymbol
/> />
<Tooltip label="Edit"> <Tooltip content="Edit" disabled={ isLoading }>
<IconButton <IconButton
aria-label="edit" aria-label="edit"
variant="simple"
boxSize={ 5 } boxSize={ 5 }
borderRadius="none" borderRadius="none"
flexShrink={ 0 } flexShrink={ 0 }
onClick={ handleEditClick } onClick={ handleEditClick }
icon={ <IconSvg name="edit" boxSize={ 4 } flexShrink={ 0 }/> } >
/> <IconSvg name="edit" boxSize={ 4 } flexShrink={ 0 }/>
</IconButton>
</Tooltip> </Tooltip>
</> </>
); );
...@@ -103,7 +105,7 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading ...@@ -103,7 +105,7 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
<VerifiedAddressesStatus status={ application.status }/> <VerifiedAddressesStatus status={ application.status }/>
</Skeleton> </Skeleton>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
...@@ -114,7 +116,7 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading ...@@ -114,7 +116,7 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>Date</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Date</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ dayjs(application.updatedAt).format('MMM DD, YYYY') } { dayjs(application.updatedAt).format('MMM DD, YYYY') }
</Skeleton> </Skeleton>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
......
import { Table, Tbody, Th, Thead, Tr } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account'; import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table';
import VerifiedAddressesTableItem from './VerifiedAddressesTableItem'; import VerifiedAddressesTableItem from './VerifiedAddressesTableItem';
interface Props { interface Props {
...@@ -15,17 +16,17 @@ interface Props { ...@@ -15,17 +16,17 @@ interface Props {
const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd, isLoading }: Props) => { const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd, isLoading }: Props) => {
return ( return (
<Table> <TableRoot>
<Thead> <TableHeader>
<Tr> <TableRow>
<Th>Address</Th> <TableColumnHeader>Address</TableColumnHeader>
<Th w="168px" pr={ 1 }>Token info</Th> <TableColumnHeader w="168px" pr={ 1 }>Token info</TableColumnHeader>
<Th w="36px" pl="0"></Th> <TableColumnHeader w="36px" pl="0"></TableColumnHeader>
<Th w="160px">Request status</Th> <TableColumnHeader w="160px">Request status</TableColumnHeader>
<Th w="150px">Date</Th> <TableColumnHeader w="150px">Date</TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeader>
<Tbody> <TableBody>
{ data.map((item, index) => ( { data.map((item, index) => (
<VerifiedAddressesTableItem <VerifiedAddressesTableItem
key={ item.contractAddress + (isLoading ? index : '') } key={ item.contractAddress + (isLoading ? index : '') }
...@@ -36,8 +37,8 @@ const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd, isL ...@@ -36,8 +37,8 @@ const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd, isL
isLoading={ isLoading } isLoading={ isLoading }
/> />
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
); );
}; };
......
import { Td, Tr, Link, Tooltip, IconButton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account'; import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { IconButton } from 'toolkit/chakra/icon-button';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import { Tooltip } from 'toolkit/chakra/tooltip';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -37,7 +40,7 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin ...@@ -37,7 +40,7 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin
const tokenInfo = (() => { const tokenInfo = (() => {
if (isLoading) { if (isLoading) {
return <Skeleton height={ 6 } width="140px"/>; return <Skeleton loading height={ 6 } width="140px"/>;
} }
if (!item.metadata.tokenName) { if (!item.metadata.tokenName) {
...@@ -67,43 +70,43 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin ...@@ -67,43 +70,43 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin
})(); })();
return ( return (
<Tr> <TableRow>
<Td> <TableCell>
<AddressEntity <AddressEntity
address={{ hash: item.contractAddress, is_contract: true }} address={{ hash: item.contractAddress, is_contract: true }}
isLoading={ isLoading } isLoading={ isLoading }
fontWeight="600" fontWeight="600"
/> />
</Td> </TableCell>
<Td fontSize="sm" verticalAlign="middle" pr={ 1 }> <TableCell fontSize="sm" verticalAlign="middle" pr={ 1 }>
{ tokenInfo } { tokenInfo }
</Td> </TableCell>
<Td pl="0"> <TableCell pl="0">
{ item.metadata.tokenName && application && !isLoading ? ( { item.metadata.tokenName && application && !isLoading ? (
<Tooltip label={ isLoading ? undefined : 'Edit' }> <Tooltip content="Edit" disabled={ isLoading }>
<IconButton <IconButton
aria-label="edit" aria-label="edit"
variant="simple"
boxSize={ 5 } boxSize={ 5 }
borderRadius="none" borderRadius="none"
flexShrink={ 0 } flexShrink={ 0 }
onClick={ handleEditClick } onClick={ handleEditClick }
icon={ <IconSvg name="edit" boxSize={ 4 } flexShrink={ 0 }/> } >
/> <IconSvg name="edit" boxSize={ 4 } flexShrink={ 0 }/>
</IconButton>
</Tooltip> </Tooltip>
) : null } ) : null }
</Td> </TableCell>
<Td fontSize="sm"> <TableCell fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
<VerifiedAddressesStatus status={ item.metadata.tokenName ? application?.status : undefined }/> <VerifiedAddressesStatus status={ item.metadata.tokenName ? application?.status : undefined }/>
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td fontSize="sm" color="text_secondary"> <TableCell fontSize="sm" color="text_secondary">
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ item.metadata.tokenName && application ? dayjs(application.updatedAt).format('MMM DD, YYYY') : null } { item.metadata.tokenName && application ? dayjs(application.updatedAt).format('MMM DD, YYYY') : null }
</Skeleton> </Skeleton>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment