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