mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
address subheading refactoring
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
/* 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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
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 }
|
||||
/>
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
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>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+46
-46
@@ -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 }/> }
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
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>
|
||||
);
|
||||
|
||||
@@ -1,48 +1,43 @@
|
||||
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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
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>
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
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>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user