Commit fbfd2d73 authored by tom's avatar tom

verified addresses modal

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