Commit b51bbe4a authored by tom's avatar tom

name domains pages

parent f566c916
...@@ -5,12 +5,12 @@ import React from 'react'; ...@@ -5,12 +5,12 @@ import React from 'react';
import type { Props } from 'nextjs/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
// const NameDomain = dynamic(() => import('ui/pages/NameDomain'), { ssr: false }); const NameDomain = dynamic(() => import('ui/pages/NameDomain'), { ssr: false });
const Page: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageNextJs pathname="/name-domains/[name]" query={ props.query }> <PageNextJs pathname="/name-domains/[name]" query={ props.query }>
{ /* <NameDomain/> */ } <NameDomain/>
</PageNextJs> </PageNextJs>
); );
}; };
......
...@@ -4,12 +4,12 @@ import React from 'react'; ...@@ -4,12 +4,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
// const NameDomains = dynamic(() => import('ui/pages/NameDomains'), { ssr: false }); const NameDomains = dynamic(() => import('ui/pages/NameDomains'), { ssr: false });
const Page: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageNextJs pathname="/name-domains"> <PageNextJs pathname="/name-domains">
{ /* <NameDomains/> */ } <NameDomains/>
</PageNextJs> </PageNextJs>
); );
}; };
......
...@@ -26,11 +26,12 @@ export interface LinkProps extends Pick<NextLinkProps, 'shallow' | 'prefetch' | ...@@ -26,11 +26,12 @@ export interface LinkProps extends Pick<NextLinkProps, 'shallow' | 'prefetch' |
external?: boolean; external?: boolean;
iconColor?: ChakraLinkProps['color']; iconColor?: ChakraLinkProps['color'];
noIcon?: boolean; noIcon?: boolean;
disabled?: boolean;
} }
export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>( export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
function Link(props, ref) { function Link(props, ref) {
const { external, loading, href, children, scroll = true, iconColor, noIcon, shallow, prefetch = false, ...rest } = props; const { external, loading, href, children, scroll = true, iconColor, noIcon, shallow, prefetch = false, disabled, ...rest } = props;
if (external) { if (external) {
return ( return (
...@@ -41,6 +42,7 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>( ...@@ -41,6 +42,7 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
className="group" className="group"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
{ ...(disabled ? { 'data-disabled': true } : {}) }
{ ...rest } { ...rest }
> >
{ children } { children }
...@@ -52,7 +54,12 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>( ...@@ -52,7 +54,12 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
return ( return (
<Skeleton loading={ loading } asChild> <Skeleton loading={ loading } asChild>
<ChakraLink asChild ref={ ref } { ...rest }> <ChakraLink
asChild
ref={ ref }
{ ...(disabled ? { 'data-disabled': true } : {}) }
{ ...rest }
>
{ href ? ( { href ? (
<NextLink <NextLink
href={ href as NextLinkProps['href'] } href={ href as NextLinkProps['href'] }
......
...@@ -3,6 +3,9 @@ import { defineRecipe } from '@chakra-ui/react'; ...@@ -3,6 +3,9 @@ import { defineRecipe } from '@chakra-ui/react';
export const recipe = defineRecipe({ export const recipe = defineRecipe({
base: { base: {
gap: 0, gap: 0,
_disabled: {
cursor: 'not-allowed',
},
}, },
variants: { variants: {
variant: { variant: {
...@@ -12,6 +15,9 @@ export const recipe = defineRecipe({ ...@@ -12,6 +15,9 @@ export const recipe = defineRecipe({
textDecoration: 'none', textDecoration: 'none',
color: 'link.primary.hover', color: 'link.primary.hover',
}, },
_disabled: {
color: 'text.secondary',
},
}, },
secondary: { secondary: {
color: 'link.secondary', color: 'link.secondary',
......
import { Grid, Tooltip, Flex } from '@chakra-ui/react'; import { Grid, Flex } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -10,12 +10,13 @@ import config from 'configs/app'; ...@@ -10,12 +10,13 @@ import config from 'configs/app';
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 stripTrailingSlash from 'lib/stripTrailingSlash'; import stripTrailingSlash from 'lib/stripTrailingSlash';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import NftEntity from 'ui/shared/entities/nft/NftEntity'; import NftEntity from 'ui/shared/entities/nft/NftEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import NameDomainDetailsAlert from './details/NameDomainDetailsAlert'; import NameDomainDetailsAlert from './details/NameDomainDetailsAlert';
...@@ -45,7 +46,7 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -45,7 +46,7 @@ const NameDomainDetails = ({ query }: Props) => {
</DetailedInfo.ItemLabel> </DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue> <DetailedInfo.ItemValue>
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/> <IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 }/>
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px"> <Skeleton loading={ isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="20px">
{ dayjs(query.data.registration_date).format('llll') } { dayjs(query.data.registration_date).format('llll') }
</Skeleton> </Skeleton>
</DetailedInfo.ItemValue> </DetailedInfo.ItemValue>
...@@ -65,17 +66,17 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -65,17 +66,17 @@ const NameDomainDetails = ({ query }: Props) => {
<IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/> <IconSvg name="clock" boxSize={ 5 } color="gray.500" verticalAlign="middle" isLoading={ isLoading } mr={ 2 } mt="-2px"/>
{ hasExpired && ( { hasExpired && (
<> <>
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px"> <Skeleton loading={ isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
{ dayjs(query.data.expiry_date).fromNow() } { dayjs(query.data.expiry_date).fromNow() }
</Skeleton> </Skeleton>
<TextSeparator color="gray.500"/> <TextSeparator color="gray.500"/>
</> </>
) } ) }
<Skeleton isLoaded={ !isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px"> <Skeleton loading={ isLoading } display="inline" whiteSpace="pre-wrap" lineHeight="24px">
{ dayjs(query.data.expiry_date).format('llll') } { dayjs(query.data.expiry_date).format('llll') }
</Skeleton> </Skeleton>
<TextSeparator color="gray.500"/> <TextSeparator color="gray.500"/>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline"> <Skeleton loading={ isLoading } color="text_secondary" display="inline">
<NameDomainExpiryStatus date={ query.data?.expiry_date }/> <NameDomainExpiryStatus date={ query.data?.expiry_date }/>
</Skeleton> </Skeleton>
</DetailedInfo.ItemValue> </DetailedInfo.ItemValue>
...@@ -116,14 +117,14 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -116,14 +117,14 @@ const NameDomainDetails = ({ query }: Props) => {
address={ query.data.registrant } address={ query.data.registrant }
isLoading={ isLoading } isLoading={ isLoading }
/> />
<Tooltip label="Lookup for related domain names"> <Tooltip content="Lookup for related domain names">
<LinkInternal <Link
flexShrink={ 0 } flexShrink={ 0 }
display="inline-flex" display="inline-flex"
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.registrant.hash } }) } href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.registrant.hash } }) }
> >
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </Link>
</Tooltip> </Tooltip>
</DetailedInfo.ItemValue> </DetailedInfo.ItemValue>
</> </>
...@@ -145,14 +146,14 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -145,14 +146,14 @@ const NameDomainDetails = ({ query }: Props) => {
address={ query.data.owner } address={ query.data.owner }
isLoading={ isLoading } isLoading={ isLoading }
/> />
<Tooltip label="Lookup for related domain names"> <Tooltip content="Lookup for related domain names">
<LinkInternal <Link
flexShrink={ 0 } flexShrink={ 0 }
display="inline-flex" display="inline-flex"
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.owner.hash } }) } href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.owner.hash } }) }
> >
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </Link>
</Tooltip> </Tooltip>
</DetailedInfo.ItemValue> </DetailedInfo.ItemValue>
</> </>
...@@ -174,14 +175,14 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -174,14 +175,14 @@ const NameDomainDetails = ({ query }: Props) => {
address={ query.data.wrapped_owner } address={ query.data.wrapped_owner }
isLoading={ isLoading } isLoading={ isLoading }
/> />
<Tooltip label="Lookup for related domain names"> <Tooltip content="Lookup for related domain names">
<LinkInternal <Link
flexShrink={ 0 } flexShrink={ 0 }
display="inline-flex" display="inline-flex"
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: query.data.wrapped_owner.hash } }) } 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 }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </Link>
</Tooltip> </Tooltip>
</DetailedInfo.ItemValue> </DetailedInfo.ItemValue>
</> </>
...@@ -229,7 +230,7 @@ const NameDomainDetails = ({ query }: Props) => { ...@@ -229,7 +230,7 @@ const NameDomainDetails = ({ query }: Props) => {
> >
{ otherAddresses.map(([ type, address ]) => ( { otherAddresses.map(([ type, address ]) => (
<Flex key={ type } columnGap={ 2 } minW="0" w="100%" overflow="hidden"> <Flex key={ type } columnGap={ 2 } minW="0" w="100%" overflow="hidden">
<Skeleton isLoaded={ !isLoading }>{ type }</Skeleton> <Skeleton loading={ isLoading }>{ type }</Skeleton>
<AddressEntity <AddressEntity
address={{ hash: address }} address={{ hash: address }}
isLoading={ isLoading } isLoading={ isLoading }
......
import { Box, Hide, Show } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -22,7 +22,7 @@ const NameDomainHistory = ({ domain }: Props) => { ...@@ -22,7 +22,7 @@ const NameDomainHistory = ({ domain }: Props) => {
const router = useRouter(); const router = useRouter();
const domainName = getQueryParamString(router.query.name); const domainName = getQueryParamString(router.query.name);
const [ sort, setSort ] = React.useState<Sort>(); const [ sort, setSort ] = React.useState<Sort>('default');
const { isPlaceholderData, isError, data } = useApiQuery('domain_events', { const { isPlaceholderData, isError, data } = useApiQuery('domain_events', {
pathParams: { name: domainName, chainId: config.chain.id }, pathParams: { name: domainName, chainId: config.chain.id },
...@@ -44,8 +44,7 @@ const NameDomainHistory = ({ domain }: Props) => { ...@@ -44,8 +44,7 @@ const NameDomainHistory = ({ domain }: Props) => {
const content = ( const content = (
<> <>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
<Box>
{ data?.items.map((item, index) => ( { data?.items.map((item, index) => (
<NameDomainHistoryListItem <NameDomainHistoryListItem
key={ index } key={ index }
...@@ -55,8 +54,7 @@ const NameDomainHistory = ({ domain }: Props) => { ...@@ -55,8 +54,7 @@ const NameDomainHistory = ({ domain }: Props) => {
/> />
)) } )) }
</Box> </Box>
</Show> <Box hideBelow="lg">
<Hide below="lg" ssr={ false }>
<NameDomainHistoryTable <NameDomainHistoryTable
history={ data } history={ data }
domain={ domain } domain={ domain }
...@@ -64,17 +62,18 @@ const NameDomainHistory = ({ domain }: Props) => { ...@@ -64,17 +62,18 @@ const NameDomainHistory = ({ domain }: Props) => {
sort={ sort } sort={ sort }
onSortToggle={ handleSortToggle } onSortToggle={ handleSortToggle }
/> />
</Hide> </Box>
</> </>
); );
return ( return (
<DataListDisplay <DataListDisplay
isError={ isError } isError={ isError }
items={ data?.items } itemsNum={ data?.items.length }
emptyText="There are no events for this domain." emptyText="There are no events for this domain."
content={ content } >
/> { content }
</DataListDisplay>
); );
}; };
......
import { Alert } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
import LinkExternal from 'ui/shared/links/LinkExternal'; import { Alert } from 'toolkit/chakra/alert';
import { Link } from 'toolkit/chakra/link';
interface Props { interface Props {
data: bens.DetailedDomain | undefined; data: bens.DetailedDomain | undefined;
...@@ -18,9 +18,9 @@ const NameDomainDetailsAlert = ({ data }: Props) => { ...@@ -18,9 +18,9 @@ const NameDomainDetailsAlert = ({ data }: Props) => {
return ( return (
<Alert status="info" colorScheme="gray" display="inline-block" whiteSpace="pre-wrap" mb={ 6 }> <Alert status="info" colorScheme="gray" display="inline-block" whiteSpace="pre-wrap" mb={ 6 }>
<span>The domain name is resolved offchain using </span> <span>The domain name is resolved offchain using </span>
{ data.stored_offchain && <LinkExternal href="https://eips.ethereum.org/EIPS/eip-3668">EIP-3668: CCIP Read</LinkExternal> } { data.stored_offchain && <Link external href="https://eips.ethereum.org/EIPS/eip-3668">EIP-3668: CCIP Read</Link> }
{ data.stored_offchain && data.resolved_with_wildcard && <span> & </span> } { data.stored_offchain && data.resolved_with_wildcard && <span> & </span> }
{ data.resolved_with_wildcard && <LinkExternal href="https://eips.ethereum.org/EIPS/eip-2544">EIP-2544: Wildcard Resolution</LinkExternal> } { data.resolved_with_wildcard && <Link external href="https://eips.ethereum.org/EIPS/eip-2544">EIP-2544: Wildcard Resolution</Link> }
</Alert> </Alert>
); );
}; };
......
...@@ -6,7 +6,7 @@ import { route } from 'nextjs-routes'; ...@@ -6,7 +6,7 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import stripTrailingSlash from 'lib/stripTrailingSlash';
import Tag from 'ui/shared/chakra/Tag'; import { Badge } from 'toolkit/chakra/badge';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
...@@ -58,7 +58,7 @@ const NameDomainHistoryListItem = ({ isLoading, domain, event }: Props) => { ...@@ -58,7 +58,7 @@ const NameDomainHistoryListItem = ({ isLoading, domain, event }: Props) => {
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>Method</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Method</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
<Tag colorScheme="gray" isLoading={ isLoading }>{ event.action }</Tag> <Badge colorPalette="gray" loading={ isLoading }>{ event.action }</Badge>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
</> </>
) } ) }
......
import { Table, Tbody, Tr, Th, Link } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { default as Thead } from 'ui/shared/TheadSticky';
import NameDomainHistoryTableItem from './NameDomainHistoryTableItem'; import NameDomainHistoryTableItem from './NameDomainHistoryTableItem';
import type { Sort } from './utils'; import type { Sort } from './utils';
...@@ -22,11 +22,11 @@ const NameDomainHistoryTable = ({ history, domain, isLoading, sort, onSortToggle ...@@ -22,11 +22,11 @@ const NameDomainHistoryTable = ({ history, domain, isLoading, sort, onSortToggle
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)'; const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return ( return (
<Table> <TableRoot>
<Thead top={ 0 }> <TableHeaderSticky top={ 0 }>
<Tr> <TableRow>
<Th width="25%">Txn hash</Th> <TableColumnHeader width="25%">Txn hash</TableColumnHeader>
<Th width="25%" pl={ 9 }> <TableColumnHeader width="25%" pl={ 9 }>
<Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="timestamp" onClick={ onSortToggle }> <Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="timestamp" onClick={ onSortToggle }>
{ sort?.includes('timestamp') && ( { sort?.includes('timestamp') && (
<IconSvg <IconSvg
...@@ -41,20 +41,20 @@ const NameDomainHistoryTable = ({ history, domain, isLoading, sort, onSortToggle ...@@ -41,20 +41,20 @@ const NameDomainHistoryTable = ({ history, domain, isLoading, sort, onSortToggle
) } ) }
<span>Age</span> <span>Age</span>
</Link> </Link>
</Th> </TableColumnHeader>
<Th width="25%">From</Th> <TableColumnHeader width="25%">From</TableColumnHeader>
<Th width="25%">Method</Th> <TableColumnHeader width="25%">Method</TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ {
history?.items history?.items
.slice() .slice()
.sort(sortFn(sort)) .sort(sortFn(sort))
.map((item, index) => <NameDomainHistoryTableItem key={ index } event={ item } domain={ domain } isLoading={ isLoading }/>) .map((item, index) => <NameDomainHistoryTableItem key={ index } event={ item } domain={ domain } isLoading={ isLoading }/>)
} }
</Tbody> </TableBody>
</Table> </TableRoot>
); );
}; };
......
import { Tr, Td } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
...@@ -7,7 +6,8 @@ import { route } from 'nextjs-routes'; ...@@ -7,7 +6,8 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import stripTrailingSlash from 'lib/stripTrailingSlash';
import Tag from 'ui/shared/chakra/Tag'; import { Badge } from 'toolkit/chakra/badge';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip'; import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
...@@ -29,8 +29,8 @@ const NameDomainHistoryTableItem = ({ isLoading, event, domain }: Props) => { ...@@ -29,8 +29,8 @@ const NameDomainHistoryTableItem = ({ isLoading, event, domain }: Props) => {
}; };
return ( return (
<Tr> <TableRow>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
<TxEntity <TxEntity
{ ...txEntityProps } { ...txEntityProps }
hash={ event.transaction_hash } hash={ event.transaction_hash }
...@@ -39,22 +39,22 @@ const NameDomainHistoryTableItem = ({ isLoading, event, domain }: Props) => { ...@@ -39,22 +39,22 @@ const NameDomainHistoryTableItem = ({ isLoading, event, domain }: Props) => {
noIcon noIcon
truncation="constant_long" truncation="constant_long"
/> />
</Td> </TableCell>
<Td pl={ 9 } verticalAlign="middle"> <TableCell pl={ 9 } verticalAlign="middle">
<TimeAgoWithTooltip <TimeAgoWithTooltip
timestamp={ event.timestamp } timestamp={ event.timestamp }
isLoading={ isLoading } isLoading={ isLoading }
color="text_secondary" color="text_secondary"
display="inline-block" display="inline-block"
/> />
</Td> </TableCell>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
{ event.from_address && <AddressEntity address={ event.from_address } isLoading={ isLoading } truncation="constant"/> } { event.from_address && <AddressEntity address={ event.from_address } isLoading={ isLoading } truncation="constant"/> }
</Td> </TableCell>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
{ event.action && <Tag colorScheme="gray" isLoading={ isLoading }>{ event.action }</Tag> } { event.action && <Badge colorPalette="gray" loading={ isLoading }>{ event.action }</Badge> }
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
...@@ -3,10 +3,10 @@ import type * as bens from '@blockscout/bens-types'; ...@@ -3,10 +3,10 @@ import type * as bens from '@blockscout/bens-types';
import getNextSortValueShared from 'ui/shared/sort/getNextSortValue'; import getNextSortValueShared from 'ui/shared/sort/getNextSortValue';
export type SortField = 'timestamp'; export type SortField = 'timestamp';
export type Sort = `${ SortField }-asc` | `${ SortField }-desc`; export type Sort = `${ SortField }-asc` | `${ SortField }-desc` | 'default';
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = { const SORT_SEQUENCE: Record<SortField, Array<Sort>> = {
timestamp: [ 'timestamp-desc', 'timestamp-asc', undefined ], timestamp: [ 'timestamp-desc', 'timestamp-asc', 'default' ],
}; };
export const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE); export const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE);
......
import { Box, Checkbox, CheckboxGroup, Flex, HStack, Image, Link, Text, VStack, chakra } from '@chakra-ui/react'; import { Box, CheckboxGroup, Fieldset, Flex, HStack, Text, chakra, createListCollection } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
...@@ -6,6 +6,9 @@ import type { EnsDomainLookupFiltersOptions } from 'types/api/ens'; ...@@ -6,6 +6,9 @@ import type { EnsDomainLookupFiltersOptions } from 'types/api/ens';
import type { PaginationParams } from 'ui/shared/pagination/types'; import type { PaginationParams } from 'ui/shared/pagination/types';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import FilterInput from 'ui/shared/filters/FilterInput'; import FilterInput from 'ui/shared/filters/FilterInput';
import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PopoverFilter from 'ui/shared/filters/PopoverFilter';
...@@ -16,6 +19,8 @@ import Sort from 'ui/shared/sort/Sort'; ...@@ -16,6 +19,8 @@ import Sort from 'ui/shared/sort/Sort';
import type { Sort as TSort } from './utils'; import type { Sort as TSort } from './utils';
import { SORT_OPTIONS } from './utils'; import { SORT_OPTIONS } from './utils';
const sortCollection = createListCollection({ items: SORT_OPTIONS });
interface Props { interface Props {
pagination: PaginationParams; pagination: PaginationParams;
searchTerm: string | undefined; searchTerm: string | undefined;
...@@ -25,8 +30,8 @@ interface Props { ...@@ -25,8 +30,8 @@ interface Props {
protocolsData: Array<bens.ProtocolInfo> | undefined; protocolsData: Array<bens.ProtocolInfo> | undefined;
protocolsFilterValue: Array<string>; protocolsFilterValue: Array<string>;
onProtocolsFilterChange: (nextValue: Array<string>) => void; onProtocolsFilterChange: (nextValue: Array<string>) => void;
sort: TSort | undefined; sort: TSort;
onSortChange: (nextValue: TSort | undefined) => void; onSortChange: (nextValue: TSort) => void;
isLoading: boolean; isLoading: boolean;
isAddressSearch: boolean; isAddressSearch: boolean;
} }
...@@ -51,11 +56,11 @@ const NameDomainsActionBar = ({ ...@@ -51,11 +56,11 @@ const NameDomainsActionBar = ({
<FilterInput <FilterInput
w={{ base: '100%', lg: '360px' }} w={{ base: '100%', lg: '360px' }}
minW={{ base: 'auto', lg: '250px' }} minW={{ base: 'auto', lg: '250px' }}
size="xs" size="sm"
onChange={ onSearchChange } onChange={ onSearchChange }
placeholder="Search by name or address" placeholder="Search by name or address"
initialValue={ searchTerm } initialValue={ searchTerm }
isLoading={ isInitialLoading } loading={ isInitialLoading }
/> />
); );
...@@ -63,6 +68,14 @@ const NameDomainsActionBar = ({ ...@@ -63,6 +68,14 @@ const NameDomainsActionBar = ({
onProtocolsFilterChange([]); onProtocolsFilterChange([]);
}, [ onProtocolsFilterChange ]); }, [ onProtocolsFilterChange ]);
const handleSortChange = React.useCallback(({ value }: { value: Array<string> }) => {
onSortChange(value[0] as TSort);
}, [ onSortChange ]);
const handleFilterValueChange = React.useCallback((value: Array<string>) => {
onFilterValueChange(value as EnsDomainLookupFiltersOptions);
}, [ onFilterValueChange ]);
const filterGroupDivider = <Box w="100%" borderBottomWidth="1px" borderBottomColor="border.divider" my={ 4 }/>; const filterGroupDivider = <Box w="100%" borderBottomWidth="1px" borderBottomColor="border.divider" my={ 4 }/>;
const appliedFiltersNum = filterValue.length + (protocolsData && protocolsData.length > 1 ? protocolsFilterValue.length : 0); const appliedFiltersNum = filterValue.length + (protocolsData && protocolsData.length > 1 ? protocolsFilterValue.length : 0);
...@@ -72,20 +85,18 @@ const NameDomainsActionBar = ({ ...@@ -72,20 +85,18 @@ const NameDomainsActionBar = ({
<div> <div>
{ protocolsData && protocolsData.length > 1 && ( { protocolsData && protocolsData.length > 1 && (
<> <>
<Flex justifyContent="space-between" fontSize="sm" mb={ 3 }> <Flex justifyContent="space-between" textStyle="sm" mb={ 3 }>
<Text fontWeight={ 600 } variant="secondary">Protocol</Text> <Text fontWeight={ 600 } color="text.secondary">Protocol</Text>
<Link <Link
onClick={ handleProtocolReset } onClick={ handleProtocolReset }
color={ protocolsData.length > 0 ? 'link' : 'text_secondary' } disabled={ protocolsFilterValue.length === 0 }
_hover={{
color: protocolsData.length > 0 ? 'link_hovered' : 'text_secondary',
}}
> >
Reset Reset
</Link> </Link>
</Flex> </Flex>
<CheckboxGroup size="lg" value={ protocolsFilterValue } defaultValue={ protocolsFilterValue } onChange={ onProtocolsFilterChange }> <Fieldset.Root>
<VStack gap={ 5 } alignItems="flex-start"> <CheckboxGroup defaultValue={ protocolsFilterValue } onValueChange={ onProtocolsFilterChange } value={ protocolsFilterValue } name="token_type">
<Fieldset.Content gap={ 5 } alignItems="flex-start">
{ protocolsData.map((protocol) => { { protocolsData.map((protocol) => {
const topLevelDomains = protocol.tld_list.map((domain) => `.${ domain }`).join(' '); const topLevelDomains = protocol.tld_list.map((domain) => `.${ domain }`).join(' ');
return ( return (
...@@ -98,7 +109,7 @@ const NameDomainsActionBar = ({ ...@@ -98,7 +109,7 @@ const NameDomainsActionBar = ({
mr={ 2 } mr={ 2 }
alt={ `${ protocol.title } protocol icon` } alt={ `${ protocol.title } protocol icon` }
fallback={ <IconSvg name="ENS_slim" boxSize={ 5 } mr={ 2 }/> } fallback={ <IconSvg name="ENS_slim" boxSize={ 5 } mr={ 2 }/> }
fallbackStrategy={ protocol.icon_url ? 'onError' : 'beforeLoadOrError' } // fallbackStrategy={ protocol.icon_url ? 'onError' : 'beforeLoadOrError' }
/> />
<span>{ protocol.short_name }</span> <span>{ protocol.short_name }</span>
<chakra.span color="text_secondary" whiteSpace="pre"> { topLevelDomains }</chakra.span> <chakra.span color="text_secondary" whiteSpace="pre"> { topLevelDomains }</chakra.span>
...@@ -106,30 +117,34 @@ const NameDomainsActionBar = ({ ...@@ -106,30 +117,34 @@ const NameDomainsActionBar = ({
</Checkbox> </Checkbox>
); );
}) } }) }
</VStack> </Fieldset.Content>
</CheckboxGroup> </CheckboxGroup>
</Fieldset.Root>
{ filterGroupDivider } { filterGroupDivider }
</> </>
) } ) }
<CheckboxGroup size="lg" onChange={ onFilterValueChange } value={ filterValue } defaultValue={ filterValue }> <Fieldset.Root>
<Text variant="secondary" fontWeight={ 600 } mb={ 3 } fontSize="sm">Address</Text> <CheckboxGroup defaultValue={ filterValue } onValueChange={ handleFilterValueChange } value={ filterValue } name="token_type">
<Checkbox value="owned_by" isDisabled={ !isAddressSearch } display="block"> <Fieldset.Content gap={ 0 }>
<Text color="text.secondary" fontWeight={ 600 } mb={ 3 } textStyle="sm">Address</Text>
<Checkbox value="owned_by" disabled={ !isAddressSearch }>
Owned by Owned by
</Checkbox> </Checkbox>
<Checkbox <Checkbox
value="resolved_to" value="resolved_to"
mt={ 5 } mt={ 5 }
isDisabled={ !isAddressSearch } disabled={ !isAddressSearch }
display="block"
> >
Resolved to address Resolved to address
</Checkbox> </Checkbox>
{ filterGroupDivider } { filterGroupDivider }
<Text variant="secondary" fontWeight={ 600 } mb={ 3 } fontSize="sm">Status</Text> <Text color="text.secondary" fontWeight={ 600 } mb={ 3 } textStyle="sm">Status</Text>
<Checkbox value="with_inactive" display="block"> <Checkbox value="with_inactive">
Include expired Include expired
</Checkbox> </Checkbox>
</Fieldset.Content>
</CheckboxGroup> </CheckboxGroup>
</Fieldset.Root>
</div> </div>
</PopoverFilter> </PopoverFilter>
); );
...@@ -137,16 +152,16 @@ const NameDomainsActionBar = ({ ...@@ -137,16 +152,16 @@ const NameDomainsActionBar = ({
const sortButton = ( const sortButton = (
<Sort <Sort
name="name_domains_sorting" name="name_domains_sorting"
defaultValue={ sort } defaultValue={ [ sort ] }
options={ SORT_OPTIONS } collection={ sortCollection }
onChange={ onSortChange } onValueChange={ handleSortChange }
isLoading={ isInitialLoading } isLoading={ isInitialLoading }
/> />
); );
return ( return (
<> <>
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}> <HStack gap={ 3 } mb={ 6 } hideFrom="lg">
{ filter } { filter }
{ sortButton } { sortButton }
{ searchInput } { searchInput }
...@@ -155,7 +170,7 @@ const NameDomainsActionBar = ({ ...@@ -155,7 +170,7 @@ const NameDomainsActionBar = ({
mt={ -6 } mt={ -6 }
display={{ base: pagination.isVisible ? 'flex' : 'none', lg: 'flex' }} display={{ base: pagination.isVisible ? 'flex' : 'none', lg: 'flex' }}
> >
<HStack spacing={ 3 } display={{ base: 'none', lg: 'flex' }}> <HStack gap={ 3 } hideBelow="lg">
{ filter } { filter }
{ searchInput } { searchInput }
</HStack> </HStack>
......
...@@ -3,8 +3,8 @@ import React from 'react'; ...@@ -3,8 +3,8 @@ import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import { Skeleton } from 'toolkit/chakra/skeleton';
import NameDomainExpiryStatus from 'ui/nameDomain/NameDomainExpiryStatus'; import NameDomainExpiryStatus from 'ui/nameDomain/NameDomainExpiryStatus';
import Skeleton from 'ui/shared/chakra/Skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
...@@ -41,7 +41,7 @@ const NameDomainsListItem = ({ ...@@ -41,7 +41,7 @@ const NameDomainsListItem = ({
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>Registered on</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Registered on</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading }> <Skeleton loading={ isLoading }>
<div>{ dayjs(registrationDate).format('lll') }</div> <div>{ dayjs(registrationDate).format('lll') }</div>
<div> { dayjs(registrationDate).fromNow() }</div> <div> { dayjs(registrationDate).fromNow() }</div>
</Skeleton> </Skeleton>
...@@ -53,7 +53,7 @@ const NameDomainsListItem = ({ ...@@ -53,7 +53,7 @@ const NameDomainsListItem = ({
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>Expiration date</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Expiration date</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value> <ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } whiteSpace="pre-wrap"> <Skeleton loading={ isLoading } whiteSpace="pre-wrap">
<div>{ dayjs(expiryDate).format('lll') } </div> <div>{ dayjs(expiryDate).format('lll') } </div>
<NameDomainExpiryStatus date={ expiryDate }/> <NameDomainExpiryStatus date={ expiryDate }/>
</Skeleton> </Skeleton>
......
import { Table, Tbody, Tr, Th, Link } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { default as Thead } from 'ui/shared/TheadSticky';
import NameDomainsTableItem from './NameDomainsTableItem'; import NameDomainsTableItem from './NameDomainsTableItem';
import { type Sort } from './utils'; import { type Sort } from './utils';
...@@ -13,7 +13,7 @@ import { type Sort } from './utils'; ...@@ -13,7 +13,7 @@ import { type Sort } from './utils';
interface Props { interface Props {
data: bens.LookupDomainNameResponse | undefined; data: bens.LookupDomainNameResponse | undefined;
isLoading?: boolean; isLoading?: boolean;
sort: Sort | undefined; sort: Sort;
onSortToggle: (event: React.MouseEvent) => void; onSortToggle: (event: React.MouseEvent) => void;
} }
...@@ -21,12 +21,12 @@ const NameDomainsTable = ({ data, isLoading, sort, onSortToggle }: Props) => { ...@@ -21,12 +21,12 @@ const NameDomainsTable = ({ data, isLoading, sort, onSortToggle }: Props) => {
const sortIconTransform = sort?.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)'; const sortIconTransform = sort?.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return ( return (
<Table> <TableRoot>
<Thead top={ ACTION_BAR_HEIGHT_DESKTOP }> <TableHeaderSticky top={ ACTION_BAR_HEIGHT_DESKTOP }>
<Tr> <TableRow>
<Th width="25%">Domain</Th> <TableColumnHeader width="25%">Domain</TableColumnHeader>
<Th width="25%">Address</Th> <TableColumnHeader width="25%">Address</TableColumnHeader>
<Th width="25%" pl={ 9 }> <TableColumnHeader width="25%" pl={ 9 }>
<Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="registration_date" onClick={ onSortToggle }> <Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="registration_date" onClick={ onSortToggle }>
{ sort?.includes('registration_date') && ( { sort?.includes('registration_date') && (
<IconSvg <IconSvg
...@@ -41,14 +41,14 @@ const NameDomainsTable = ({ data, isLoading, sort, onSortToggle }: Props) => { ...@@ -41,14 +41,14 @@ const NameDomainsTable = ({ data, isLoading, sort, onSortToggle }: Props) => {
) } ) }
<span>Registered on</span> <span>Registered on</span>
</Link> </Link>
</Th> </TableColumnHeader>
<Th width="25%">Expiration date</Th> <TableColumnHeader width="25%">Expiration date</TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ data?.items.map((item, index) => <NameDomainsTableItem key={ index } { ...item } isLoading={ isLoading }/>) } { data?.items.map((item, index) => <NameDomainsTableItem key={ index } { ...item } isLoading={ isLoading }/>) }
</Tbody> </TableBody>
</Table> </TableRoot>
); );
}; };
......
import { chakra, Tr, Td } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type * as bens from '@blockscout/bens-types'; import type * as bens from '@blockscout/bens-types';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import NameDomainExpiryStatus from 'ui/nameDomain/NameDomainExpiryStatus'; import NameDomainExpiryStatus from 'ui/nameDomain/NameDomainExpiryStatus';
import Skeleton from 'ui/shared/chakra/Skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
...@@ -23,30 +24,30 @@ const NameDomainsTableItem = ({ ...@@ -23,30 +24,30 @@ const NameDomainsTableItem = ({
}: Props) => { }: Props) => {
return ( return (
<Tr> <TableRow>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
<EnsEntity domain={ name } protocol={ protocol } isLoading={ isLoading } fontWeight={ 600 }/> <EnsEntity domain={ name } protocol={ protocol } isLoading={ isLoading } fontWeight={ 600 }/>
</Td> </TableCell>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
{ resolvedAddress && <AddressEntity address={ resolvedAddress } isLoading={ isLoading } fontWeight={ 500 }/> } { resolvedAddress && <AddressEntity address={ resolvedAddress } isLoading={ isLoading } fontWeight={ 500 }/> }
</Td> </TableCell>
<Td verticalAlign="middle" pl={ 9 }> <TableCell verticalAlign="middle" pl={ 9 }>
{ registrationDate && ( { registrationDate && (
<Skeleton isLoaded={ !isLoading }> <Skeleton loading={ isLoading }>
{ dayjs(registrationDate).format('lll') } { dayjs(registrationDate).format('lll') }
<chakra.span color="text_secondary"> { dayjs(registrationDate).fromNow() }</chakra.span> <chakra.span color="text_secondary"> { dayjs(registrationDate).fromNow() }</chakra.span>
</Skeleton> </Skeleton>
) } ) }
</Td> </TableCell>
<Td verticalAlign="middle"> <TableCell verticalAlign="middle">
{ expiryDate && ( { expiryDate && (
<Skeleton isLoaded={ !isLoading } whiteSpace="pre-wrap"> <Skeleton loading={ isLoading }>
<span>{ dayjs(expiryDate).format('lll') } </span> <span>{ dayjs(expiryDate).format('lll') } </span>
<NameDomainExpiryStatus date={ expiryDate }/> <NameDomainExpiryStatus date={ expiryDate }/>
</Skeleton> </Skeleton>
) } ) }
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
import type { EnsLookupSorting } from 'types/api/ens'; import type { EnsLookupSorting } from 'types/api/ens';
import type { SelectOption } from 'toolkit/chakra/select';
import type { SelectOption } from 'toolkit/chakra/select';
import getNextSortValueShared from 'ui/shared/sort/getNextSortValue'; import getNextSortValueShared from 'ui/shared/sort/getNextSortValue';
export type SortField = EnsLookupSorting['sort']; export type SortField = EnsLookupSorting['sort'];
export type Sort = `${ EnsLookupSorting['sort'] }-${ EnsLookupSorting['order'] }`; export type Sort = `${ EnsLookupSorting['sort'] }-${ EnsLookupSorting['order'] }` | 'default';
export const SORT_OPTIONS: Array<SelectOption<Sort>> = [ export const SORT_OPTIONS: Array<SelectOption<Sort>> = [
{ label: 'Default', value: undefined }, { label: 'Default', value: 'default' },
{ label: 'Registered on descending', value: 'registration_date-DESC' }, { label: 'Registered on descending', value: 'registration_date-DESC' },
{ label: 'Registered on ascending', value: 'registration_date-ASC' }, { label: 'Registered on ascending', value: 'registration_date-ASC' },
]; ];
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = { const SORT_SEQUENCE: Record<SortField, Array<Sort>> = {
registration_date: [ 'registration_date-DESC', 'registration_date-ASC', undefined ], registration_date: [ 'registration_date-DESC', 'registration_date-ASC', 'default' ],
}; };
export const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE); export const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE);
import { Flex, Tooltip } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
...@@ -11,17 +11,18 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -11,17 +11,18 @@ import useApiQuery from 'lib/api/useApiQuery';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { ENS_DOMAIN } from 'stubs/ENS'; import { ENS_DOMAIN } from 'stubs/ENS';
import { Link } from 'toolkit/chakra/link';
import { Tooltip } from 'toolkit/chakra/tooltip';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
import useActiveTabFromQuery from 'toolkit/components/RoutedTabs/useActiveTabFromQuery';
import NameDomainDetails from 'ui/nameDomain/NameDomainDetails'; import NameDomainDetails from 'ui/nameDomain/NameDomainDetails';
import NameDomainHistory from 'ui/nameDomain/NameDomainHistory'; import NameDomainHistory from 'ui/nameDomain/NameDomainHistory';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
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 PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery';
const NameDomain = () => { const NameDomain = () => {
const router = useRouter(); const router = useRouter();
...@@ -34,12 +35,12 @@ const NameDomain = () => { ...@@ -34,12 +35,12 @@ const NameDomain = () => {
}, },
}); });
const tabs: Array<RoutedTab> = [ const tabs: Array<TabItemRegular> = [
{ id: 'details', title: 'Details', component: <NameDomainDetails query={ infoQuery }/> }, { id: 'details', title: 'Details', component: <NameDomainDetails query={ infoQuery }/> },
{ id: 'history', title: 'History', component: <NameDomainHistory domain={ infoQuery.data }/> }, { id: 'history', title: 'History', component: <NameDomainHistory domain={ infoQuery.data }/> },
]; ];
const tabIndex = useTabIndexFromQuery(tabs); const activeTab = useActiveTabFromQuery(tabs);
throwOnResourceLoadError(infoQuery); throwOnResourceLoadError(infoQuery);
...@@ -69,14 +70,14 @@ const NameDomain = () => { ...@@ -69,14 +70,14 @@ const NameDomain = () => {
address={ infoQuery.data?.resolved_address } address={ infoQuery.data?.resolved_address }
isLoading={ isLoading } isLoading={ isLoading }
/> />
<Tooltip label="Lookup for related domain names"> <Tooltip content="Lookup for related domain names">
<LinkInternal <Link
flexShrink={ 0 } flexShrink={ 0 }
display="inline-flex" display="inline-flex"
href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: infoQuery.data?.resolved_address?.hash } }) } href={ route({ pathname: '/name-domains', query: { owned_by: 'true', resolved_to: 'true', address: infoQuery.data?.resolved_address?.hash } }) }
> >
<IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/> <IconSvg name="search" boxSize={ 5 } isLoading={ isLoading }/>
</LinkInternal> </Link>
</Tooltip> </Tooltip>
</Flex> </Flex>
) } ) }
...@@ -89,8 +90,8 @@ const NameDomain = () => { ...@@ -89,8 +90,8 @@ const NameDomain = () => {
<PageTitle title="Name details" secondRow={ titleSecondRow }/> <PageTitle title="Name details" secondRow={ titleSecondRow }/>
{ infoQuery.isPlaceholderData ? ( { infoQuery.isPlaceholderData ? (
<> <>
<TabsSkeleton tabs={ tabs } mt={ 6 }/> <RoutedTabsSkeleton tabs={ tabs } mt={ 6 }/>
{ tabs[tabIndex]?.component } { activeTab?.component }
</> </>
) : <RoutedTabs tabs={ tabs }/> } ) : <RoutedTabs tabs={ tabs }/> }
</> </>
......
import { Box, Hide, Show } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -41,7 +41,7 @@ const NameDomains = () => { ...@@ -41,7 +41,7 @@ const NameDomains = () => {
const [ searchTerm, setSearchTerm ] = React.useState<string>(q || ''); const [ searchTerm, setSearchTerm ] = React.useState<string>(q || '');
const [ filterValue, setFilterValue ] = React.useState<EnsDomainLookupFiltersOptions>(initialFilters); const [ filterValue, setFilterValue ] = React.useState<EnsDomainLookupFiltersOptions>(initialFilters);
const [ sort, setSort ] = React.useState<Sort | undefined>(initialSort); const [ sort, setSort ] = React.useState<Sort>(initialSort ?? 'default');
const [ protocolsFilter, setProtocolsFilter ] = React.useState<Array<string>>(protocols); const [ protocolsFilter, setProtocolsFilter ] = React.useState<Array<string>>(protocols);
const debouncedSearchTerm = useDebounce(searchTerm, 300); const debouncedSearchTerm = useDebounce(searchTerm, 300);
...@@ -192,8 +192,7 @@ const NameDomains = () => { ...@@ -192,8 +192,7 @@ const NameDomains = () => {
const content = ( const content = (
<> <>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
<Box>
{ data?.items.map((item, index) => ( { data?.items.map((item, index) => (
<NameDomainsListItem <NameDomainsListItem
key={ item.id + (isLoading ? index : '') } key={ item.id + (isLoading ? index : '') }
...@@ -202,15 +201,14 @@ const NameDomains = () => { ...@@ -202,15 +201,14 @@ const NameDomains = () => {
/> />
)) } )) }
</Box> </Box>
</Show> <Box hideBelow="lg">
<Hide below="lg" ssr={ false }>
<NameDomainsTable <NameDomainsTable
data={ data } data={ data }
isLoading={ isLoading } isLoading={ isLoading }
sort={ sort } sort={ sort }
onSortToggle={ handleSortToggle } onSortToggle={ handleSortToggle }
/> />
</Hide> </Box>
</> </>
); );
...@@ -239,15 +237,16 @@ const NameDomains = () => { ...@@ -239,15 +237,16 @@ const NameDomains = () => {
/> />
<DataListDisplay <DataListDisplay
isError={ isError } isError={ isError }
items={ data?.items } itemsNum={ data?.items.length }
emptyText="There are no name domains." emptyText="There are no name domains."
filterProps={{ filterProps={{
emptyFilteredText: `Couldn${ apos }t find name domains that match your filter query.`, emptyFilteredText: `Couldn${ apos }t find name domains that match your filter query.`,
hasActiveFilters, hasActiveFilters,
}} }}
content={ content }
actionBar={ actionBar } actionBar={ actionBar }
/> >
{ content }
</DataListDisplay>
</> </>
); );
}; };
......
...@@ -34,11 +34,7 @@ const TokenTypeFilter = <T extends TokenType | NFTTokenType>({ nftOnly, onChange ...@@ -34,11 +34,7 @@ const TokenTypeFilter = <T extends TokenType | NFTTokenType>({ nftOnly, onChange
<Text fontWeight={ 600 } color="text.secondary">Type</Text> <Text fontWeight={ 600 } color="text.secondary">Type</Text>
<Link <Link
onClick={ handleReset } onClick={ handleReset }
cursor={ value.length > 0 ? 'pointer' : 'unset' } disabled={ value.length === 0 }
color={ value.length > 0 ? 'link' : 'text_secondary' }
_hover={{
color: value.length > 0 ? 'link_hovered' : 'text_secondary',
}}
> >
Reset Reset
</Link> </Link>
......
...@@ -38,10 +38,7 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => { ...@@ -38,10 +38,7 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => {
<Text fontWeight={ 600 } color="text.secondary">Show bridged tokens from</Text> <Text fontWeight={ 600 } color="text.secondary">Show bridged tokens from</Text>
<Link <Link
onClick={ handleReset } onClick={ handleReset }
color={ value.length > 0 ? 'link' : 'text_secondary' } disabled={ value.length === 0 }
_hover={{
color: value.length > 0 ? 'link_hovered' : 'text_secondary',
}}
> >
Reset Reset
</Link> </Link>
......
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