Commit 9477b5f6 authored by tom's avatar tom

domain history tab

parent 5d19b2a1
......@@ -635,7 +635,8 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'l2_output_roots' | 'l2_withdrawals' | 'l2_txn_batches' | 'l2_deposits' |
'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' |
'withdrawals' | 'address_withdrawals' | 'block_withdrawals' |
'watchlist' | 'private_tags_address' | 'private_tags_tx';
'watchlist' | 'private_tags_address' | 'private_tags_tx' |
'domain_events';
export type PaginatedResponse<Q extends PaginatedResources> = ResourcePayload<Q>;
......
import type { EnsDomainDetailed } from 'types/api/ens';
import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams';
import { TX_HASH } from './tx';
export const ENS_DOMAIN: EnsDomainDetailed = {
id: '0x126d74db13895f8d3a1d362410212731d1e1d9be8add83e388385f93d84c8c84',
......@@ -15,3 +16,10 @@ export const ENS_DOMAIN: EnsDomainDetailed = {
ETH: ADDRESS_HASH,
},
};
export const ENS_DOMAIN_EVENT = {
transactionHash: TX_HASH,
timestamp: '2022-06-06T08:43:15.000000Z',
fromAddress: ADDRESS_PARAMS,
action: '0xf7a16963',
};
import { Box } from '@chakra-ui/react';
import { Box, Hide, Show } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';
import { ENS_DOMAIN_EVENT } from 'stubs/ENS';
import { generateListStub } from 'stubs/utils';
import DataListDisplay from 'ui/shared/DataListDisplay';
import NameDomainHistoryListItem from './history/NameDomainHistoryListItem';
import NameDomainHistoryTable from './history/NameDomainHistoryTable';
import { getNextSortValue, type Sort, type SortField } from './history/utils';
const NameDomainHistory = () => {
return <Box>History</Box>;
const router = useRouter();
const domainName = getQueryParamString(router.query.name);
const [ sort, setSort ] = React.useState<Sort>();
const { isPlaceholderData, isError, data } = useApiQuery('domain_events', {
pathParams: { name: domainName, chainId: config.chain.id },
queryOptions: {
placeholderData: generateListStub<'domain_events'>(ENS_DOMAIN_EVENT, 4, { totalRecords: 4 }),
},
});
const handleSortToggle = React.useCallback((event: React.MouseEvent) => {
if (isPlaceholderData) {
return;
}
const field = (event.currentTarget as HTMLDivElement).getAttribute('data-field') as SortField | undefined;
if (field) {
setSort(getNextSortValue(field));
}
}, [ isPlaceholderData ]);
const content = (
<>
<Show below="lg" ssr={ false }>
<Box>
{ data?.items.map((item, index) => <NameDomainHistoryListItem key={ index } { ...item } isLoading={ isPlaceholderData }/>) }
</Box>
</Show>
<Hide below="lg" ssr={ false }>
<NameDomainHistoryTable
data={ data }
isLoading={ isPlaceholderData }
sort={ sort }
onSortToggle={ handleSortToggle }
/>
</Hide>
</>
);
return (
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no events for this domain."
content={ content }
/>
);
};
export default React.memo(NameDomainHistory);
import { Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { EnsDomainEvent } from 'types/api/ens';
import dayjs from 'lib/date/dayjs';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
type Props = EnsDomainEvent & {
isLoading?: boolean;
}
const NameDomainHistoryListItem = ({ isLoading, transactionHash, timestamp, fromAddress, action }: Props) => {
return (
<ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<TxEntity hash={ transactionHash } isLoading={ isLoading } fontWeight={ 500 }/>
</ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Age</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block">
<span>{ dayjs(timestamp).fromNow() }</span>
</Skeleton>
</ListItemMobileGrid.Value>
{ fromAddress && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>From</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<AddressEntity address={ fromAddress } isLoading={ isLoading }/>
</ListItemMobileGrid.Value>
</>
) }
{ action && (
<>
<ListItemMobileGrid.Label isLoading={ isLoading }>Method</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<Tag colorScheme="gray" isLoading={ isLoading }>{ action }</Tag>
</ListItemMobileGrid.Value>
</>
) }
</ListItemMobileGrid.Container>
);
};
export default React.memo(NameDomainHistoryListItem);
import { Table, Tbody, Tr, Th, Link, Icon } from '@chakra-ui/react';
import React from 'react';
import type { EnsDomainEventsResponse } from 'types/api/ens';
import arrowIcon from 'icons/arrows/east.svg';
import { default as Thead } from 'ui/shared/TheadSticky';
import NameDomainHistoryTableItem from './NameDomainHistoryTableItem';
import type { Sort } from './utils';
import { sortFn } from './utils';
interface Props {
data: EnsDomainEventsResponse | undefined;
isLoading?: boolean;
sort: Sort | undefined;
onSortToggle: (event: React.MouseEvent) => void;
}
const NameDomainHistoryTable = ({ data, isLoading, sort, onSortToggle }: Props) => {
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return (
<Table variant="simple" size="sm">
<Thead top={ 0 }>
<Tr>
<Th width="25%">Txn hash</Th>
<Th width="25%" pl={ 9 }>
<Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="timestamp" onClick={ onSortToggle }>
{ sort?.includes('timestamp') && (
<Icon
as={ arrowIcon }
boxSize={ 4 }
transform={ sortIconTransform }
color="link"
position="absolute"
left={ -5 }
top={ 0 }
/>
) }
<span>Age</span>
</Link>
</Th>
<Th width="25%">From</Th>
<Th width="25%">Method</Th>
</Tr>
</Thead>
<Tbody>
{
data?.items
.slice()
.sort(sortFn(sort))
.map((item, index) => <NameDomainHistoryTableItem key={ index } { ...item } isLoading={ isLoading }/>)
}
</Tbody>
</Table>
);
};
export default React.memo(NameDomainHistoryTable);
import { Tr, Td, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { EnsDomainEvent } from 'types/api/ens';
import dayjs from 'lib/date/dayjs';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
type Props = EnsDomainEvent & {
isLoading?: boolean;
}
const NameDomainHistoryTableItem = ({ isLoading, transactionHash, fromAddress, action, timestamp }: Props) => {
return (
<Tr>
<Td>
<TxEntity hash={ transactionHash } isLoading={ isLoading } fontWeight={ 700 }/>
</Td>
<Td pl={ 9 }>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block">
<span>{ dayjs(timestamp).fromNow() }</span>
</Skeleton>
</Td>
<Td>
{ fromAddress && <AddressEntity address={ fromAddress } isLoading={ isLoading }/> }
</Td>
<Td>
{ action && <Tag colorScheme="gray" isLoading={ isLoading }>{ action }</Tag> }
</Td>
</Tr>
);
};
export default React.memo(NameDomainHistoryTableItem);
import type { EnsDomainEvent } from 'types/api/ens';
import getNextSortValueShared from 'ui/shared/sort/getNextSortValue';
export type SortField = 'timestamp';
export type Sort = `${ SortField }-asc` | `${ SortField }-desc`;
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = {
timestamp: [ 'timestamp-desc', 'timestamp-asc', undefined ],
};
export const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE);
export const sortFn = (sort: Sort | undefined) => (a: EnsDomainEvent, b: EnsDomainEvent) => {
switch (sort) {
case 'timestamp-asc': {
return b.timestamp.localeCompare(a.timestamp);
}
case 'timestamp-desc': {
return a.timestamp.localeCompare(b.timestamp);
}
default:
return 0;
}
};
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