Commit 0a79c372 authored by tom's avatar tom

address subheading refactoring

parent 041f2130
/* eslint-disable no-restricted-imports */
import type { BoxProps, ImageProps as ChakraImageProps } from '@chakra-ui/react';
import { Image as ChakraImage } from '@chakra-ui/react';
import React from 'react';
......
......@@ -11,10 +11,12 @@ export interface TooltipProps extends ChakraTooltip.RootProps {
portalRef?: React.RefObject<HTMLElement>;
content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps;
triggerProps?: ChakraTooltip.TriggerProps;
disabled?: boolean;
disableOnMobile?: boolean;
}
// TODO @tom2drum fix flashing svg icons when use tooltip (disabled) + popover
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(props, ref) {
const {
......@@ -32,6 +34,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
defaultOpen = false,
lazyMount = true,
unmountOnExit = true,
triggerProps,
...rest
} = props;
......@@ -79,6 +82,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
ref={ triggerRef }
asChild
onClick={ isMobile ? handleTriggerClick : undefined }
{ ...triggerProps }
>
{ children }
</ChakraTooltip.Trigger>
......
import { Box, Text, Icon, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
import { Box, Text, Icon } from '@chakra-ui/react';
import React from 'react';
// This icon doesn't work properly when it is in the sprite
......@@ -6,8 +6,9 @@ import React from 'react';
// eslint-disable-next-line no-restricted-imports
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
import useFetchReport from 'lib/solidityScan/useFetchReport';
import Popover from 'ui/shared/chakra/Popover';
import LinkExternal from 'ui/shared/links/LinkExternal';
import { Link } from 'toolkit/chakra/link';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
import SolidityscanReportDetails from 'ui/shared/solidityscanReport/SolidityscanReportDetails';
import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore';
......@@ -17,7 +18,7 @@ interface Props {
}
const SolidityscanReport = ({ hash }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const { open, onOpenChange } = useDisclosure();
const { data, isPlaceholderData, isError } = useFetchReport({ hash });
......@@ -36,17 +37,18 @@ const SolidityscanReport = ({ hash }: Props) => {
const vulnerabilitiesCount = vulnerabilitiesCounts.reduce((acc, val) => acc + val, 0);
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverRoot onOpenChange={ onOpenChange } open={ open }>
<PopoverTrigger>
<SolidityscanReportButton
score={ score }
isLoading={ isPlaceholderData }
onClick={ onToggle }
isActive={ isOpen }
/>
<Box>
<SolidityscanReportButton
score={ score }
isLoading={ isPlaceholderData }
isActive={ open }
/>
</Box>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<PopoverBody textStyle="sm">
<Box mb={ 5 } lineHeight="25px">
Contract analyzed for 240+ vulnerability patterns by
<Icon as={ solidityScanIcon } mr={ 1 } ml="6px" w="23px" h="20px" display="inline-block" verticalAlign="middle"/>
......@@ -55,14 +57,14 @@ const SolidityscanReport = ({ hash }: Props) => {
<SolidityscanReportScore score={ score } mb={ 5 }/>
{ vulnerabilities && vulnerabilitiesCount > 0 && (
<Box mb={ 5 }>
<Text py="7px" variant="secondary" fontSize="xs" fontWeight={ 500 }>Vulnerabilities distribution</Text>
<Text py="7px" color="text.secondary" textStyle="xs" fontWeight={ 500 }>Vulnerabilities distribution</Text>
<SolidityscanReportDetails vulnerabilities={ vulnerabilities } vulnerabilitiesCount={ vulnerabilitiesCount }/>
</Box>
) }
<LinkExternal href={ data.scan_report.scanner_reference_url }>View full report</LinkExternal>
<Link href={ data.scan_report.scanner_reference_url } external>View full report</Link>
</PopoverBody>
</PopoverContent>
</Popover>
</PopoverRoot>
);
};
......
import { chakra, Tooltip, IconButton, useDisclosure } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -7,6 +7,9 @@ import config from 'configs/app';
import { getResourceKey } from 'lib/api/useApiQuery';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import * as mixpanel from 'lib/mixpanel/index';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Tooltip } from 'toolkit/chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg';
import AuthGuard from 'ui/snippets/auth/AuthGuard';
import WatchlistAddModal from 'ui/watchlist/AddressModal/AddressModal';
......@@ -36,14 +39,6 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => {
addModalProps.onClose();
}, [ addModalProps, queryClient, router.query.hash ]);
const handleAddModalClose = React.useCallback(() => {
addModalProps.onClose();
}, [ addModalProps ]);
const handleDeleteModalClose = React.useCallback(() => {
deleteModalProps.onClose();
}, [ deleteModalProps ]);
const formData = React.useMemo(() => {
if (typeof watchListId !== 'number') {
return { address_hash: hash };
......@@ -63,34 +58,30 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => {
<>
<AuthGuard onAuthSuccess={ handleAddToFavorite }>
{ ({ onClick }) => (
<Tooltip label={ `${ watchListId ? 'Remove address from Watch list' : 'Add address to Watch list' }` }>
<Tooltip content={ `${ watchListId ? 'Remove address from Watch list' : 'Add address to Watch list' }` }>
<IconButton
isActive={ Boolean(watchListId) }
className={ className }
aria-label="edit"
variant="outline"
size="sm"
pl="6px"
pr="6px"
flexShrink={ 0 }
onClick={ onClick }
icon={ <IconSvg name={ watchListId ? 'star_filled' : 'star_outline' } boxSize={ 5 }/> }
onFocusCapture={ onFocusCapture }
/>
>
<IconSvg name={ watchListId ? 'star_filled' : 'star_outline' } boxSize={ 5 }/>
</IconButton>
</Tooltip>
) }
</AuthGuard>
<WatchlistAddModal
{ ...addModalProps }
isAdd
onClose={ handleAddModalClose }
onSuccess={ handleAddOrDeleteSuccess }
data={ formData }
/>
{ formData.id && (
<DeleteAddressModal
{ ...deleteModalProps }
onClose={ handleDeleteModalClose }
data={ formData }
onSuccess={ handleAddOrDeleteSuccess }
/>
......
import {
chakra,
Alert,
Modal,
ModalBody,
ModalContent,
ModalCloseButton,
ModalHeader,
ModalOverlay,
LightMode,
Box,
useDisclosure,
Tooltip,
IconButton,
} from '@chakra-ui/react';
import { chakra, Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import QRCode from 'qrcode';
import React from 'react';
import type { Address as AddressType } from 'types/api/address';
import getPageType from 'lib/mixpanel/getPageType';
import * as mixpanel from 'lib/mixpanel/index';
import { useRollbar } from 'lib/rollbar';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Alert } from 'toolkit/chakra/alert';
import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import IconSvg from 'ui/shared/IconSvg';
......@@ -32,12 +21,12 @@ const SVG_OPTIONS = {
interface Props {
className?: string;
address: AddressType;
hash: string;
isLoading?: boolean;
}
const AddressQrCode = ({ address, className, isLoading }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const AddressQrCode = ({ hash, className, isLoading }: Props) => {
const { open, onOpen, onOpenChange } = useDisclosure();
const router = useRouter();
const rollbar = useRollbar();
......@@ -48,8 +37,8 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => {
const pageType = getPageType(router.pathname);
React.useEffect(() => {
if (isOpen) {
QRCode.toString(address.hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => {
if (open) {
QRCode.toString(hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => {
if (error) {
setError('We were unable to generate QR code.');
rollbar?.warn('QR code generation failed');
......@@ -61,58 +50,52 @@ const AddressQrCode = ({ address, className, isLoading }: Props) => {
mixpanel.logEvent(mixpanel.EventTypes.QR_CODE, { 'Page type': pageType });
});
}
}, [ address.hash, isOpen, onClose, pageType, rollbar ]);
}, [ hash, open, pageType, rollbar ]);
if (isLoading) {
return <Skeleton className={ className } w="36px" h="32px" borderRadius="base"/>;
return <Skeleton loading className={ className } w="36px" h="32px" borderRadius="base"/>;
}
return (
<>
<Tooltip label="Click to view QR code">
<Tooltip content="Click to view QR code">
<IconButton
className={ className }
aria-label="Show QR code"
variant="outline"
size="sm"
pl="6px"
pr="6px"
onClick={ onOpen }
icon={ <IconSvg name="qr_code" boxSize={ 5 }/> }
flexShrink={ 0 }
/>
>
<IconSvg name="qr_code" boxSize={ 5 }/>
</IconButton>
</Tooltip>
{ error && (
<Modal isOpen={ isOpen } onClose={ onClose } size={{ base: 'full', lg: 'sm' }}>
<ModalOverlay/>
<ModalContent>
<ModalBody mb={ 0 }>
<DialogRoot open={ open } onOpenChange={ onOpenChange } size={{ lgDown: 'full', lg: 'sm' }}>
<DialogContent>
<DialogBody>
<Alert status="warning">{ error }</Alert>
</ModalBody>
</ModalContent>
</Modal>
</DialogBody>
</DialogContent>
</DialogRoot>
) }
{ !error && (
<LightMode>
<Modal isOpen={ isOpen } onClose={ onClose } size={{ base: 'full', lg: 'sm' }}>
<ModalOverlay/>
<ModalContent>
<ModalHeader fontWeight="500" textStyle="h3" mb={ 4 }>Address QR code</ModalHeader>
<ModalCloseButton/>
<ModalBody mb={ 0 }>
<AddressEntity
mb={ 3 }
fontWeight={ 500 }
color="text"
address={ address }
noLink
/>
<Box p={ 4 } dangerouslySetInnerHTML={{ __html: qr }}/>
</ModalBody>
</ModalContent>
</Modal>
</LightMode>
<DialogRoot open={ open } onOpenChange={ onOpenChange } size={{ lgDown: 'full', lg: 'sm' }}>
<DialogContent className="light">
<DialogHeader>Address QR code</DialogHeader>
<DialogBody>
<AddressEntity
mb={ 3 }
fontWeight={ 500 }
color="text"
address={{ hash }}
noLink
/>
<Box p={ 4 } dangerouslySetInnerHTML={{ __html: qr }}/>
</DialogBody>
</DialogContent>
</DialogRoot>
) }
</>
);
......
import {
Box,
Button,
Flex,
Grid,
Hide,
PopoverBody,
PopoverContent,
PopoverTrigger,
Show,
useDisclosure,
chakra,
} from '@chakra-ui/react';
import { Box, Flex, Grid, chakra } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { clamp } from 'es-toolkit';
import React from 'react';
......@@ -21,12 +9,13 @@ import { route } from 'nextjs-routes';
import type { ResourceError } from 'lib/api/resources';
import dayjs from 'lib/date/dayjs';
import Popover from 'ui/shared/chakra/Popover';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Button } from 'toolkit/chakra/button';
import { Link } from 'toolkit/chakra/link';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import PopoverTriggerTooltip from 'ui/shared/PopoverTriggerTooltip';
interface Props {
query: UseQueryResult<bens.LookupAddressResponse, ResourceError<unknown>>;
......@@ -48,7 +37,7 @@ const DomainsGrid = ({ data }: { data: Array<bens.Domain> }) => {
};
const AddressEnsDomains = ({ query, addressHash, mainDomainName }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
// const { isOpen, onToggle, onClose } = useDisclosure();
const { data, isPending, isError } = query;
......@@ -57,7 +46,7 @@ const AddressEnsDomains = ({ query, addressHash, mainDomainName }: Props) => {
}
if (isPending) {
return <Skeleton h={ 8 } w={{ base: '50px', xl: '120px' }} borderRadius="base"/>;
return <Skeleton loading h={ 8 } w={{ base: '50px', xl: '120px' }} borderRadius="base"/>;
}
if (data.items.length === 0) {
......@@ -95,62 +84,56 @@ const AddressEnsDomains = ({ query, addressHash, mainDomainName }: Props) => {
const totalRecords = data.items.length > 40 ? '40+' : data.items.length;
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverRoot>
<PopoverTrigger>
<PopoverTriggerTooltip label="List of names resolved or owned by this address">
<Button
size="sm"
variant="outline"
colorScheme="gray"
onClick={ onToggle }
isActive={ isOpen }
aria-label="Address domains"
fontWeight={ 500 }
px={ 2 }
h="32px"
flexShrink={ 0 }
>
<IconSvg name="ENS_slim" boxSize={ 5 }/>
<Show above="xl">
<chakra.span ml={ 1 }>{ totalRecords } Domain{ data.items.length > 1 ? 's' : '' }</chakra.span>
</Show>
<Hide above="xl">
<chakra.span ml={ 1 }>{ totalRecords }</chakra.span>
</Hide>
</Button>
</PopoverTriggerTooltip>
<Box>
<Tooltip content="List of names resolved or owned by this address">
<Button
size="sm"
variant="dropdown"
aria-label="Address domains"
fontWeight={ 500 }
flexShrink={ 0 }
columnGap={ 1 }
>
<IconSvg name="ENS_slim" boxSize={ 5 }/>
<chakra.span hideBelow="xl">{ totalRecords } Domain{ data.items.length > 1 ? 's' : '' }</chakra.span>
<chakra.span hideFrom="xl">{ totalRecords }</chakra.span>
</Button>
</Tooltip>
</Box>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '500px' }}>
<PopoverBody px={ 6 } py={ 5 } fontSize="sm" display="flex" flexDir="column" rowGap={ 5 } alignItems="flex-start">
<PopoverContent w={{ lg: '500px' }}>
<PopoverBody textStyle="sm" display="flex" flexDir="column" rowGap={ 5 } alignItems="flex-start">
{ mainDomain && (
<Box w="100%">
<chakra.span color="text_secondary" fontSize="xs">Primary*</chakra.span>
<Flex alignItems="center" fontSize="md" mt={ 2 }>
<chakra.span color="text.secondary" textStyle="xs">Primary*</chakra.span>
<Flex alignItems="center" textStyle="md" mt={ 2 }>
<EnsEntity domain={ mainDomain.name } protocol={ mainDomain.protocol } fontWeight={ 600 } noCopy/>
{ mainDomain.expiry_date &&
<chakra.span color="text_secondary" whiteSpace="pre"> (expires { dayjs(mainDomain.expiry_date).fromNow() })</chakra.span> }
<chakra.span color="text.secondary" whiteSpace="pre"> (expires { dayjs(mainDomain.expiry_date).fromNow() })</chakra.span> }
</Flex>
</Box>
) }
{ ownedDomains.length > 0 && (
<div>
<chakra.span color="text_secondary" fontSize="xs">Owned by this address</chakra.span>
<chakra.span color="text.secondary" textStyle="xs">Owned by this address</chakra.span>
<DomainsGrid data={ ownedDomains }/>
</div>
) }
{ resolvedDomains.length > 0 && (
<div>
<chakra.span color="text_secondary" fontSize="xs">Resolved to this address</chakra.span>
<chakra.span color="text.secondary" textStyle="xs">Resolved to this address</chakra.span>
<DomainsGrid data={ resolvedDomains }/>
</div>
) }
{ (ownedDomains.length > 9 || resolvedDomains.length > 9) && (
<LinkInternal
<Link
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: addressHash } }) }
>
<span> More results</span>
<chakra.span color="text_secondary"> ({ totalRecords })</chakra.span>
</LinkInternal>
<chakra.span color="text.secondary"> ({ totalRecords })</chakra.span>
</Link>
) }
{ mainDomain && (
<chakra.span fontSize="xs" mt={ -1 }>
......@@ -159,7 +142,7 @@ const AddressEnsDomains = ({ query, addressHash, mainDomainName }: Props) => {
) }
</PopoverBody>
</PopoverContent>
</Popover>
</PopoverRoot>
);
};
......
......@@ -366,51 +366,51 @@ const AddressPageContent = () => {
// In this case it returns 404 with empty payload, so we calculate check-summed hash on the client
const checkSummedHash = React.useMemo(() => addressQuery.data?.hash ?? getCheckedSummedAddress(hash), [ hash, addressQuery.data?.hash ]);
// const titleSecondRow = (
// <Flex alignItems="center" w="100%" columnGap={ 2 } rowGap={ 2 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}>
// { addressQuery.data?.ens_domain_name && (
// <EnsEntity
// domain={ addressQuery.data?.ens_domain_name }
// protocol={ !addressEnsDomainsQuery.isPending ? addressMainDomain?.protocol : null }
// fontFamily="heading"
// fontSize="lg"
// fontWeight={ 500 }
// mr={ 1 }
// maxW="300px"
// />
// ) }
// <AddressEntity
// address={{
// ...addressQuery.data,
// hash: checkSummedHash,
// name: '',
// ens_domain_name: '',
// implementations: null,
// }}
// isLoading={ isLoading }
// fontFamily="heading"
// fontSize="lg"
// fontWeight={ 500 }
// noLink
// isSafeAddress={ isSafeAddress }
// icon={{ color: isSafeAddress ? { _light: 'black', _dark: 'white' } : undefined }}
// mr={ 4 }
// />
// { !isLoading && addressQuery.data?.is_contract && addressQuery.data.token &&
// <AddressAddToWallet token={ addressQuery.data.token } variant="button"/> }
// { !isLoading && !addressQuery.data?.is_contract && config.features.account.isEnabled && (
// <AddressFavoriteButton hash={ hash } watchListId={ addressQuery.data?.watchlist_address_id }/>
// ) }
// <AddressQrCode address={{ hash: addressQuery.data?.filecoin?.robust ?? checkSummedHash }} isLoading={ isLoading }/>
// <AccountActionsMenu isLoading={ isLoading }/>
// <HStack ml="auto" gap={ 2 }/>
// { !isLoading && addressQuery.data?.is_contract && addressQuery.data?.is_verified && config.UI.views.address.solidityscanEnabled &&
// <SolidityscanReport hash={ hash }/> }
// { !isLoading && addressEnsDomainsQuery.data && config.features.nameService.isEnabled &&
// <AddressEnsDomains query={ addressEnsDomainsQuery } addressHash={ hash } mainDomainName={ addressQuery.data?.ens_domain_name }/> }
// <NetworkExplorers type="address" pathParam={ hash.toLowerCase() }/>
// </Flex>
// );
const titleSecondRow = (
<Flex alignItems="center" w="100%" columnGap={ 2 } rowGap={ 2 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}>
{ addressQuery.data?.ens_domain_name && (
<EnsEntity
domain={ addressQuery.data?.ens_domain_name }
protocol={ !addressEnsDomainsQuery.isPending ? addressMainDomain?.protocol : null }
fontFamily="heading"
fontSize="lg"
fontWeight={ 500 }
mr={ 1 }
maxW="300px"
/>
) }
<AddressEntity
address={{
...addressQuery.data,
hash: checkSummedHash,
name: '',
ens_domain_name: '',
implementations: null,
}}
isLoading={ isLoading }
fontFamily="heading"
fontSize="lg"
fontWeight={ 500 }
noLink
isSafeAddress={ isSafeAddress }
icon={{ color: isSafeAddress ? { _light: 'black', _dark: 'white' } : undefined }}
mr={ 4 }
/>
{ !isLoading && addressQuery.data?.is_contract && addressQuery.data.token &&
<AddressAddToWallet token={ addressQuery.data.token } variant="button"/> }
{ !isLoading && !addressQuery.data?.is_contract && config.features.account.isEnabled && (
<AddressFavoriteButton hash={ hash } watchListId={ addressQuery.data?.watchlist_address_id }/>
) }
<AddressQrCode hash={ addressQuery.data?.filecoin?.robust ?? checkSummedHash } isLoading={ isLoading }/>
<AccountActionsMenu isLoading={ isLoading }/>
<HStack ml="auto" gap={ 2 }/>
{ !isLoading && addressQuery.data?.is_contract && addressQuery.data?.is_verified && config.UI.views.address.solidityscanEnabled &&
<SolidityscanReport hash={ hash }/> }
{ !isLoading && addressEnsDomainsQuery.data && config.features.nameService.isEnabled &&
<AddressEnsDomains query={ addressEnsDomainsQuery } addressHash={ hash } mainDomainName={ addressQuery.data?.ens_domain_name }/> }
<NetworkExplorers type="address" pathParam={ hash.toLowerCase() }/>
</Flex>
);
return (
<>
......@@ -419,7 +419,7 @@ const AddressPageContent = () => {
title={ `${ addressQuery.data?.is_contract ? 'Contract' : 'Address' } details` }
backLink={ backLink }
contentAfter={ titleContentAfter }
// secondRow={ titleSecondRow }
secondRow={ titleSecondRow }
isLoading={ isLoading }
/>
{ !addressMetadataQuery.isPending &&
......
......@@ -32,7 +32,7 @@ const NativeTokenIcon = ({ isLoading, className, type }: Props) => {
return (
<Image
borderRadius="base"
className={ className }
containerProps={{ className }}
src={ src || undefined }
alt={ `${ config.chain.currency.symbol } logo` }
fallback={ <TokenLogoPlaceholder borderRadius="base" className={ className }/> }
......
import { Box, chakra, IconButton, Tooltip } from '@chakra-ui/react';
import { Box, chakra } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import config from 'configs/app';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel/index';
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
import useProvider from 'lib/web3/useProvider';
import { WALLETS_INFO } from 'lib/web3/wallets';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { toaster } from 'toolkit/chakra/toaster';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
const feature = config.features.web3Wallet;
......@@ -23,7 +25,6 @@ interface Props {
}
const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', iconSize = 6 }: Props) => {
const toast = useToast();
const { provider, wallet } = useProvider();
const addOrSwitchChain = useAddOrSwitchChain();
......@@ -50,13 +51,9 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
});
if (wasAdded) {
toast({
position: 'top-right',
toaster.success({
title: 'Success',
description: 'Successfully added token to your wallet',
status: 'success',
variant: 'subtle',
isClosable: true,
});
mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, {
......@@ -66,23 +63,19 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
});
}
} catch (error) {
toast({
position: 'top-right',
toaster.error({
title: 'Error',
description: (error as Error)?.message || 'Something went wrong',
status: 'error',
variant: 'subtle',
isClosable: true,
});
}
}, [ toast, token, provider, wallet, addOrSwitchChain ]);
}, [ token, provider, wallet, addOrSwitchChain ]);
if (!provider || !wallet) {
return null;
}
if (isLoading) {
return <Skeleton className={ className } boxSize={ iconSize } borderRadius="base"/>;
return <Skeleton loading className={ className } boxSize={ iconSize } borderRadius="base"/>;
}
if (!feature.isEnabled) {
......@@ -91,7 +84,7 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
if (variant === 'button') {
return (
<Tooltip label={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<Tooltip content={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<IconButton
className={ className }
aria-label="Add token to wallet"
......@@ -99,15 +92,16 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
size="sm"
px={ 1 }
onClick={ handleClick }
icon={ <IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ 6 }/> }
flexShrink={ 0 }
/>
>
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ 6 }/>
</IconButton>
</Tooltip>
);
}
return (
<Tooltip label={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<Tooltip content={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<Box className={ className } display="inline-flex" cursor="pointer" onClick={ handleClick } flexShrink={ 0 } aria-label="Add token to wallet">
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ iconSize }/>
</Box>
......
......@@ -141,13 +141,12 @@ export interface EntityProps extends EntityBase.EntityBaseProps {
const EnsEntity = (props: EntityProps) => {
const partsProps = distributeEntityProps(props);
const content = <Content { ...partsProps.content }/>;
return (
<Container { ...partsProps.container }>
<Icon { ...partsProps.icon }/>
<Link { ...partsProps.link }>
<Content { ...partsProps.content }/>
</Link>
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
<Copy { ...partsProps.copy }/>
</Container>
);
......
import { Button, Spinner, Tooltip, useColorModeValue, chakra } from '@chakra-ui/react';
import { Spinner } from '@chakra-ui/react';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import type { ButtonProps } from 'toolkit/chakra/button';
import { Button } from 'toolkit/chakra/button';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
import useScoreLevelAndColor from './useScoreLevelAndColor';
interface Props {
interface Props extends ButtonProps {
score: number;
isLoading?: boolean;
onlyIcon?: boolean;
onClick?: () => void;
isActive?: boolean;
label?: string | React.ReactElement;
isActive: boolean;
className?: string;
}
const SolidityscanReportButton = (
{ score, isLoading, onlyIcon, onClick, label = 'Security score', isActive, className }: Props,
ref: React.ForwardedRef<HTMLButtonElement>,
{ score, isLoading, onlyIcon, isActive, label = 'Security score', ...rest }: Props,
) => {
const { scoreColor } = useScoreLevelAndColor(score);
const colorLoading = useColorModeValue('gray.300', 'gray.600');
const isMobile = useIsMobile();
const colorLoading = { _light: 'gray.300', _dark: 'gray.600' };
const onFocusCapture = usePreventFocusAfterModalClosing();
return (
<Tooltip label={ label } isDisabled={ isMobile } openDelay={ 100 } textAlign="center">
<Tooltip content={ label } disableOnMobile disabled={ isActive }>
<Button
className={ className }
ref={ ref }
color={ isLoading ? colorLoading : scoreColor }
size="sm"
variant="outline"
colorScheme="gray"
onClick={ onClick }
isActive={ isActive }
variant="dropdown"
expanded={ isActive }
aria-label="SolidityScan score"
fontWeight={ 500 }
px="6px"
flexShrink={ 0 }
columnGap={ 1 }
isDisabled={ isLoading }
disabled={ isLoading }
_expanded={{ color: 'link.primary.hover' }}
_disabled={{
opacity: 1,
_hover: {
......@@ -50,6 +45,7 @@ const SolidityscanReportButton = (
},
}}
onFocusCapture={ onFocusCapture }
{ ...rest }
>
<IconSvg name={ score < 80 ? 'score/score-not-ok' : 'score/score-ok' } boxSize={ 5 }/>
{ isLoading && <Spinner size="sm"/> }
......@@ -59,4 +55,4 @@ const SolidityscanReportButton = (
);
};
export default chakra(React.forwardRef(SolidityscanReportButton));
export default SolidityscanReportButton;
import { Box, Flex, Text, Grid, useColorModeValue, chakra } from '@chakra-ui/react';
import { Box, Flex, Text, Grid, chakra } from '@chakra-ui/react';
import React from 'react';
import type { SolidityScanReportSeverityDistribution } from 'lib/solidityScan/schema';
......@@ -30,8 +30,6 @@ type ItemProps = {
};
const SolidityScanReportItem = ({ item, vulnerabilities, vulnerabilitiesCount }: ItemProps) => {
const bgBar = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const yetAnotherGrayColor = useColorModeValue('gray.400', 'gray.500');
const vulnerability = vulnerabilities[item.id];
if (vulnerability === undefined) {
......@@ -43,9 +41,9 @@ const SolidityScanReportItem = ({ item, vulnerabilities, vulnerabilitiesCount }:
<Box w={ 3 } h={ 3 } bg={ item.color } borderRadius="6px" mr={ 2 }></Box>
<Flex justifyContent="space-between" mr={ 3 }>
<Text>{ item.name }</Text>
<Text color={ vulnerability > 0 ? 'text' : yetAnotherGrayColor }>{ vulnerabilities[item.id] }</Text>
<Text color={ vulnerability > 0 ? 'text' : { _light: 'gray.400', _dark: 'gray.500' } }>{ vulnerabilities[item.id] }</Text>
</Flex>
<Box bg={ bgBar } h="10px" borderRadius="8px">
<Box bg={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }} h="10px" borderRadius="8px">
<Box bg={ item.color } w={ vulnerability / vulnerabilitiesCount } h="10px" borderRadius="8px"/>
</Box>
</>
......
import { Box, Flex, Text, chakra, Center, useColorModeValue } from '@chakra-ui/react';
import { Box, Flex, Text, chakra, Center } from '@chakra-ui/react';
import React from 'react';
import IconSvg from 'ui/shared/IconSvg';
......@@ -13,21 +13,22 @@ interface Props {
const SolidityscanReportScore = ({ className, score }: Props) => {
const { scoreLevel, scoreColor } = useScoreLevelAndColor(score);
const chartGrayColor = useColorModeValue('gray.100', 'gray.700');
const yetAnotherGrayColor = useColorModeValue('gray.400', 'gray.500');
const popoverBgColor = useColorModeValue('white', 'gray.900');
const yetAnotherGrayColor = { _light: 'gray.400', _dark: 'gray.500' };
return (
<Flex className={ className } alignItems="center">
<Box
w={ 12 }
h={ 12 }
bgGradient={ `conic-gradient(${ scoreColor } 0, ${ scoreColor } ${ score }%, ${ chartGrayColor } 0, ${ chartGrayColor } 100%)` }
bgGradient={{
_light: `conic-gradient({colors.${ scoreColor._light }} 0, {colors.${ scoreColor._light }} ${ score }%, {colors.gray.100} 0, {colors.gray.100} 100%)`,
_dark: `conic-gradient({colors.${ scoreColor._dark }} 0, {colors.${ scoreColor._dark }} ${ score }%, {colors.gray.700} 0, {colors.gray.700} 100%)`,
}}
borderRadius="24px"
position="relative"
mr={ 3 }
>
<Center position="absolute" w="38px" h="38px" top="5px" right="5px" bg={ popoverBgColor } borderRadius="20px">
<Center position="absolute" w="38px" h="38px" top="5px" right="5px" bg="popover.bg" borderRadius="20px">
<IconSvg name={ score < 80 ? 'score/score-not-ok' : 'score/score-ok' } boxSize={ 5 } color={ scoreColor }/>
</Center>
</Box>
......
import { useColorModeValue } from '@chakra-ui/react';
export default function useScoreLevelAndColor(score: number) {
const greatScoreColor = useColorModeValue('green.600', 'green.400');
const averageScoreColor = useColorModeValue('purple.600', 'purple.400');
const lowScoreColor = useColorModeValue('red.600', 'red.400');
const greatScoreColor = { _light: 'green.600', _dark: 'green.400' };
const averageScoreColor = { _light: 'purple.600', _dark: 'purple.400' };
const lowScoreColor = { _light: 'red.600', _dark: 'red.400' };
let scoreColor;
let scoreLevel;
......
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