mirror of
https://github.com/hanzonet/explore.git
synced 2026-10-30 18:16:57 +00:00
148 lines
5.3 KiB
TypeScript
148 lines
5.3 KiB
TypeScript
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';
|
|
|
|
import type * as bens from '@blockscout/bens-types';
|
|
|
|
import { route } from 'nextjs-routes';
|
|
|
|
import type { ResourceError } from 'lib/api/resources';
|
|
import dayjs from 'lib/date/dayjs';
|
|
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';
|
|
|
|
interface Props {
|
|
query: UseQueryResult<bens.LookupAddressResponse, ResourceError<unknown>>;
|
|
addressHash: string;
|
|
mainDomainName: string | null | undefined;
|
|
}
|
|
|
|
const DomainsGrid = ({ data }: { data: Array<bens.Domain> }) => {
|
|
return (
|
|
<Grid
|
|
templateColumns={{ base: `repeat(${ clamp(data.length, 1, 2) }, 1fr)`, lg: `repeat(${ clamp(data.length, 1, 3) }, 1fr)` }}
|
|
columnGap={ 8 }
|
|
rowGap={ 4 }
|
|
mt={ 2 }
|
|
>
|
|
{ data.slice(0, 9).map((domain) => <EnsEntity key={ domain.id } domain={ domain.name } protocol={ domain.protocol } noCopy/>) }
|
|
</Grid>
|
|
);
|
|
};
|
|
|
|
const AddressEnsDomains = ({ query, addressHash, mainDomainName }: Props) => {
|
|
const { data, isPending, isError } = query;
|
|
|
|
if (isError) {
|
|
return null;
|
|
}
|
|
|
|
if (isPending) {
|
|
return <Skeleton loading h={ 8 } w={{ base: '50px', xl: '120px' }} borderRadius="base"/>;
|
|
}
|
|
|
|
if (data.items.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const mainDomain = data.items.find((domain) => mainDomainName && domain.name === mainDomainName);
|
|
const ownedDomains = data.items.filter((domain) => {
|
|
if (mainDomainName && domain.name === mainDomainName) {
|
|
return false;
|
|
}
|
|
|
|
// exclude resolved address
|
|
if (domain.resolved_address && domain.resolved_address.hash.toLowerCase() === addressHash.toLowerCase()) {
|
|
return false;
|
|
}
|
|
|
|
if (domain.owner && domain.owner.hash.toLowerCase() === addressHash.toLowerCase()) {
|
|
return true;
|
|
}
|
|
|
|
// include wrapped owner
|
|
if (domain.wrapped_owner?.hash.toLowerCase() === addressHash.toLowerCase()) {
|
|
return !domain.resolved_address || domain.resolved_address.hash.toLowerCase() !== addressHash.toLowerCase();
|
|
}
|
|
|
|
return false;
|
|
});
|
|
const resolvedDomains = data.items.filter((domain) =>
|
|
domain.resolved_address &&
|
|
domain.resolved_address.hash.toLowerCase() === addressHash.toLowerCase() &&
|
|
domain.name !== mainDomainName,
|
|
);
|
|
|
|
const totalRecords = data.items.length > 40 ? '40+' : data.items.length;
|
|
|
|
return (
|
|
<PopoverRoot>
|
|
<Tooltip content="List of names resolved or owned by this address" disableOnMobile>
|
|
<div>
|
|
<PopoverTrigger>
|
|
<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>
|
|
</PopoverTrigger>
|
|
</div>
|
|
</Tooltip>
|
|
<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" 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> }
|
|
</Flex>
|
|
</Box>
|
|
) }
|
|
{ ownedDomains.length > 0 && (
|
|
<div>
|
|
<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" textStyle="xs">Resolved to this address</chakra.span>
|
|
<DomainsGrid data={ resolvedDomains }/>
|
|
</div>
|
|
) }
|
|
{ (ownedDomains.length > 9 || resolvedDomains.length > 9) && (
|
|
<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>
|
|
</Link>
|
|
) }
|
|
{ mainDomain && (
|
|
<chakra.span fontSize="xs" mt={ -1 }>
|
|
*A domain name is not necessarily held by a person popularly associated with the name
|
|
</chakra.span>
|
|
) }
|
|
</PopoverBody>
|
|
</PopoverContent>
|
|
</PopoverRoot>
|
|
);
|
|
};
|
|
|
|
export default React.memo(AddressEnsDomains);
|