mirror of
https://github.com/hanzonet/explore.git
synced 2026-10-30 18:16:57 +00:00
* first version of the pack * single entry point * add custom components to the package * move form components to toolkit * add more utils and hooks to the package * minor fixes * add CI workflow * update readme and package.json * fix ts * fix build of env-validator * fix docker build * fix icons size * test fixes * more test fixes
251 lines
9.6 KiB
TypeScript
251 lines
9.6 KiB
TypeScript
import { Grid, Flex } from '@chakra-ui/react';
|
|
import type { UseQueryResult } from '@tanstack/react-query';
|
|
import React from 'react';
|
|
|
|
import * as bens from '@blockscout/bens-types';
|
|
|
|
import { route } from 'nextjs-routes';
|
|
|
|
import config from 'configs/app';
|
|
import type { ResourceError } from 'lib/api/resources';
|
|
import dayjs from 'lib/date/dayjs';
|
|
import { Link } from 'toolkit/chakra/link';
|
|
import { Skeleton } from 'toolkit/chakra/skeleton';
|
|
import { Tooltip } from 'toolkit/chakra/tooltip';
|
|
import { stripTrailingSlash } from 'toolkit/utils/url';
|
|
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
|
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
|
import NftEntity from 'ui/shared/entities/nft/NftEntity';
|
|
import IconSvg from 'ui/shared/IconSvg';
|
|
import TextSeparator from 'ui/shared/TextSeparator';
|
|
|
|
import NameDomainDetailsAlert from './details/NameDomainDetailsAlert';
|
|
import NameDomainExpiryStatus from './NameDomainExpiryStatus';
|
|
|
|
interface Props {
|
|
query: UseQueryResult<bens.DetailedDomain, ResourceError<unknown>>;
|
|
}
|
|
|
|
const NameDomainDetails = ({ query }: Props) => {
|
|
const isLoading = query.isPlaceholderData;
|
|
|
|
const otherAddresses = Object.entries(query.data?.other_addresses ?? {});
|
|
const hasExpired = query.data?.expiry_date && dayjs(query.data.expiry_date).isBefore(dayjs());
|
|
|
|
return (
|
|
<>
|
|
<NameDomainDetailsAlert data={ query.data }/>
|
|
<Grid columnGap={ 8 } rowGap={ 3 } templateColumns={{ base: 'minmax(0, 1fr)', lg: 'max-content minmax(728px, auto)' }}>
|
|
{ query.data?.registration_date && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
hint="The date the name was registered"
|
|
isLoading={ isLoading }
|
|
>
|
|
Registration date
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue>
|
|
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/>
|
|
<Skeleton loading={ isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px">
|
|
{ dayjs(query.data.registration_date).format('llll') }
|
|
</Skeleton>
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
|
|
{ query.data?.expiry_date && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
// eslint-disable-next-line max-len
|
|
hint="The date the name expires, upon which there is a grace period for the owner to renew. After grace period ends, the name is released to the market"
|
|
isLoading={ isLoading }
|
|
>
|
|
Expiration date
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue>
|
|
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/>
|
|
{ hasExpired && (
|
|
<>
|
|
<Skeleton loading={ isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
|
|
{ dayjs(query.data.expiry_date).fromNow() }
|
|
</Skeleton>
|
|
<TextSeparator color="gray.500"/>
|
|
</>
|
|
) }
|
|
<Skeleton loading={ isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
|
|
{ dayjs(query.data.expiry_date).format('llll') }
|
|
</Skeleton>
|
|
<TextSeparator color="gray.500"/>
|
|
<Skeleton loading={ isLoading } color="text.secondary" display="inline">
|
|
<NameDomainExpiryStatus date={ query.data?.expiry_date }/>
|
|
</Skeleton>
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
|
|
{ query.data?.resolver_address && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
hint="The resolver contract provides information about a domain name"
|
|
isLoading={ isLoading }
|
|
>
|
|
Resolver
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue
|
|
>
|
|
<AddressEntity
|
|
address={ query.data.resolver_address }
|
|
isLoading={ isLoading }
|
|
/>
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
|
|
{ query.data?.registrant && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
hint="The account that owns the domain name and has the rights to edit its ownership and records"
|
|
isLoading={ isLoading }
|
|
>
|
|
Registrant
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue
|
|
columnGap={ 2 }
|
|
flexWrap="nowrap"
|
|
>
|
|
<AddressEntity
|
|
address={ query.data.registrant }
|
|
isLoading={ isLoading }
|
|
/>
|
|
<Tooltip content="Lookup for related domain names">
|
|
<Link
|
|
flexShrink={ 0 }
|
|
display="inline-flex"
|
|
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.registrant.hash } }) }
|
|
>
|
|
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
|
</Link>
|
|
</Tooltip>
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
|
|
{ query.data?.owner && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
hint="The account that owns the rights to edit the records of this domain name"
|
|
isLoading={ isLoading }
|
|
>
|
|
Owner
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue
|
|
columnGap={ 2 }
|
|
flexWrap="nowrap"
|
|
>
|
|
<AddressEntity
|
|
address={ query.data.owner }
|
|
isLoading={ isLoading }
|
|
/>
|
|
<Tooltip content="Lookup for related domain names">
|
|
<Link
|
|
flexShrink={ 0 }
|
|
display="inline-flex"
|
|
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.owner.hash } }) }
|
|
>
|
|
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
|
</Link>
|
|
</Tooltip>
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
|
|
{ query.data?.wrapped_owner && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
hint="Owner of this NFT domain in NameWrapper contract"
|
|
isLoading={ isLoading }
|
|
>
|
|
Manager
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue
|
|
columnGap={ 2 }
|
|
flexWrap="nowrap"
|
|
>
|
|
<AddressEntity
|
|
address={ query.data.wrapped_owner }
|
|
isLoading={ isLoading }
|
|
/>
|
|
<Tooltip content="Lookup for related domain names">
|
|
<Link
|
|
flexShrink={ 0 }
|
|
display="inline-flex"
|
|
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.wrapped_owner.hash } }) }
|
|
>
|
|
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
|
|
</Link>
|
|
</Tooltip>
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
|
|
{ query.data?.tokens.map((token) => {
|
|
const isProtocolBaseChain = stripTrailingSlash(query.data.protocol?.deployment_blockscout_base_url ?? '') === config.app.baseUrl;
|
|
const entityProps = {
|
|
isExternal: !isProtocolBaseChain ? true : false,
|
|
href: !isProtocolBaseChain ? (
|
|
stripTrailingSlash(query.data.protocol?.deployment_blockscout_base_url ?? '') +
|
|
route({ pathname: '/token/[hash]/instance/[id]', query: { hash: token.contract_hash, id: token.id } })
|
|
) : undefined,
|
|
};
|
|
|
|
return (
|
|
<React.Fragment key={ token.type }>
|
|
<DetailedInfo.ItemLabel
|
|
hint={ `The ${ token.type === bens.TokenType.WRAPPED_DOMAIN_TOKEN ? 'wrapped ' : '' }token ID of this domain name NFT` }
|
|
isLoading={ isLoading }
|
|
>
|
|
{ token.type === bens.TokenType.WRAPPED_DOMAIN_TOKEN ? 'Wrapped token ID' : 'Token ID' }
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue
|
|
wordBreak="break-all"
|
|
whiteSpace="pre-wrap"
|
|
>
|
|
<NftEntity { ...entityProps } hash={ token.contract_hash } id={ token.id } isLoading={ isLoading } noIcon/>
|
|
</DetailedInfo.ItemValue>
|
|
</React.Fragment>
|
|
);
|
|
}) }
|
|
|
|
{ otherAddresses.length > 0 && (
|
|
<>
|
|
<DetailedInfo.ItemLabel
|
|
hint="Other cryptocurrency addresses added to this domain name"
|
|
isLoading={ isLoading }
|
|
>
|
|
Other addresses
|
|
</DetailedInfo.ItemLabel>
|
|
<DetailedInfo.ItemValue
|
|
flexDir="column"
|
|
alignItems="flex-start"
|
|
>
|
|
{ otherAddresses.map(([ type, address ]) => (
|
|
<Flex key={ type } columnGap={ 2 } minW="0" w="100%" overflow="hidden">
|
|
<Skeleton loading={ isLoading }>{ type }</Skeleton>
|
|
<AddressEntity
|
|
address={{ hash: address }}
|
|
isLoading={ isLoading }
|
|
noLink
|
|
noIcon
|
|
/>
|
|
</Flex>
|
|
)) }
|
|
</DetailedInfo.ItemValue>
|
|
</>
|
|
) }
|
|
</Grid>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default React.memo(NameDomainDetails);
|