Commit 57b296bd authored by tom's avatar tom

desktop view and sorting

parent 0b4b78da
......@@ -7,7 +7,7 @@ export interface VerifiedContract {
language: 'vyper' | 'yul' | 'solidity';
has_constructor_args: boolean;
optimization_enabled: boolean;
tx_count: number;
tx_count: number | null;
verified_at: string;
market_cap: string | null;
}
......
......@@ -2,8 +2,9 @@ import { Box, Flex, Hide, Show, Text } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { VerifiedContractsFilters } from 'types/api/contracts';
import type { VerifiedContract, VerifiedContractsFilters } from 'types/api/contracts';
import compareBns from 'lib/bigint/compareBns';
import useDebounce from 'lib/hooks/useDebounce';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities';
......@@ -16,12 +17,55 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import type { Sort, SortField } from 'ui/verifiedContracts/utils';
import VerifiedContractsFilter from 'ui/verifiedContracts/VerifiedContractsFilter';
import VerifiedContractsTable from 'ui/verifiedContracts/VerifiedContractsTable';
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = {
balance: [ 'balance-desc', 'balance-asc', undefined ],
txs: [ 'txs-desc', 'txs-asc', undefined ],
};
const getNextSortValue = (field: SortField) => (prevValue: Sort | undefined) => {
const sequence = SORT_SEQUENCE[field];
const curIndex = sequence.findIndex((sort) => sort === prevValue);
const nextIndex = curIndex + 1 > sequence.length - 1 ? 0 : curIndex + 1;
return sequence[nextIndex];
};
const sortFn = (sort: Sort | undefined) => (a: VerifiedContract, b: VerifiedContract) => {
switch (sort) {
case 'balance-desc': {
const result = compareBns(b.coin_balance, a.coin_balance);
return a.coin_balance === b.coin_balance ? 0 : result;
}
case 'balance-asc': {
const result = compareBns(a.coin_balance, b.coin_balance);
return a.coin_balance === b.coin_balance ? 0 : result;
}
case 'txs-desc': {
const result = (a.tx_count || 0) > (b.tx_count || 0) ? -1 : 1;
return a.tx_count === b.tx_count ? 0 : result;
}
case 'txs-asc': {
const result = (a.tx_count || 0) > (b.tx_count || 0) ? 1 : -1;
return a.tx_count === b.tx_count ? 0 : result;
}
default:
return 0;
}
};
const VerifiedContracts = () => {
const router = useRouter();
const [ searchTerm, setSearchTerm ] = React.useState(getQueryParamString(router.query.q) || undefined);
const [ type, setType ] = React.useState(getQueryParamString(router.query.filter) as VerifiedContractsFilters['filter'] || undefined);
const [ sort, setSort ] = React.useState<Sort>();
const debouncedSearchTerm = useDebounce(searchTerm || '', 300);
const { isError, isLoading, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({
......@@ -49,6 +93,12 @@ const VerifiedContracts = () => {
setType(undefined);
}, [ debouncedSearchTerm, onFilterChange ]);
const handleSortToggle = React.useCallback((field: SortField) => {
return () => {
setSort(getNextSortValue(field));
};
}, []);
const typeFilter = <VerifiedContractsFilter onChange={ handleTypeChange } defaultValue={ type } isActive={ Boolean(type) }/>;
const filterInput = (
......@@ -89,13 +139,13 @@ const VerifiedContracts = () => {
<SkeletonList/>
</Show>
<Hide below="lg" ssr={ false }>
<SkeletonTable columns={ [ '15%', '15%', '10%', '20%', '20%', '20%' ] }/>
<SkeletonTable columns={ [ '50%', '130px', '130px', '50%', '80px', '110px', '120px' ] }/>
</Hide>
</>
);
}
if (data.items.length === 0 && !searchTerm) {
if (data.items.length === 0 && !searchTerm && !type) {
return <Text as="span">There are no verified contracts</Text>;
}
......@@ -103,13 +153,15 @@ const VerifiedContracts = () => {
return <EmptySearchResult text={ `Couldn${ apos }t find any contract that matches your query.` }/>;
}
const sortedData = data.items.slice().sort(sortFn(sort));
return (
<>
<Show below="lg" ssr={ false }>
{ '<AddressIntTxsList data={ data.items } currentAddress={ hash }/>' }
</Show>
<Hide below="lg" ssr={ false }>
{ '<AddressIntTxsTable data={ data.items } currentAddress={ hash }/>' }
<VerifiedContractsTable data={ sortedData } sort={ sort } onSortToggle={ handleSortToggle }/>
</Hide>
</>
);
......
import { Table, Tbody, Tr, Th, Link, Icon } from '@chakra-ui/react';
import React from 'react';
import type { VerifiedContract } from 'types/api/contracts';
import appConfig from 'configs/app/config';
import arrowIcon from 'icons/arrows/east.svg';
import { default as Thead } from 'ui/shared/TheadSticky';
import type { Sort, SortField } from './utils';
import VerifiedContractsTableItem from './VerifiedContractsTableItem';
interface Props {
data: Array<VerifiedContract>;
sort: Sort | undefined;
onSortToggle: (field: SortField) => () => void;
}
const VerifiedContractsTable = ({ data, sort, onSortToggle }: Props) => {
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return (
<Table variant="simple" size="sm">
<Thead top={ 80 }>
<Tr>
<Th width="50%">Contract</Th>
<Th width="130px" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('balance') } columnGap={ 1 }>
{ sort?.includes('balance') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Balance { appConfig.network.currency.symbol }
</Link>
</Th>
<Th width="130px" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('txs') } columnGap={ 1 }>
{ sort?.includes('txs') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Txs
</Link>
</Th>
<Th width="50%">Compiler/version</Th>
<Th width="80px">Settings</Th>
<Th width="110px">Verified</Th>
<Th width="120px">Market cap</Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item) => <VerifiedContractsTableItem key={ item.address.hash } data={ item }/>) }
</Tbody>
</Table>
);
};
export default React.memo(VerifiedContractsTable);
import { Tr, Td, Icon, Box, Flex, chakra, Tooltip } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { VerifiedContract } from 'types/api/contracts';
import appConfig from 'configs/app/config';
import iconCheck from 'icons/check.svg';
import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import HashStringShorten from 'ui/shared/HashStringShorten';
interface Props {
data: VerifiedContract;
}
const VerifiedContractsTableItem = ({ data }: Props) => {
const balance = data.coin_balance && data.coin_balance !== '0' ?
BigNumber(data.coin_balance).div(10 ** appConfig.network.currency.decimals).toFormat(2) :
'0';
return (
<Tr>
<Td>
<Flex columnGap={ 2 }>
<AddressIcon address={ data.address }/>
<Flex columnGap={ 2 } flexWrap="wrap" lineHeight={ 6 } w="calc(100% - 32px)">
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name }/>
<Box color="text_secondary">
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
</Box>
</Flex>
</Flex>
</Td>
<Td isNumeric lineHeight={ 6 }>
{ balance }
</Td>
<Td isNumeric lineHeight={ 6 }>
{ data.tx_count ? data.tx_count.toLocaleString() : '0' }
</Td>
<Td lineHeight={ 6 }>
<Flex flexWrap="wrap" columnGap={ 2 }>
<chakra.span textTransform="capitalize">{ data.language }</chakra.span>
<chakra.span color="text_secondary" wordBreak="break-all">{ data.compiler_version }</chakra.span>
</Flex>
</Td>
<Td>
<Tooltip label="Optimization">
<span>
{ data.optimization_enabled ?
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer"/> :
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer"/> }
</span>
</Tooltip>
<Tooltip label="Constructor args">
<chakra.span ml={ 3 }>
{ data.has_constructor_args ?
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer"/> :
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer"/> }
</chakra.span>
</Tooltip>
</Td>
<Td lineHeight={ 6 }>
<Flex alignItems="center" columnGap={ 2 }>
<Icon as={ iconSuccess } boxSize={ 4 } color="green.500"/>
<chakra.span color="text_secondary">
{ dayjs(data.verified_at).fromNow() }
</chakra.span>
</Flex>
</Td>
<Td lineHeight={ 6 }>
N/A
</Td>
</Tr>
);
};
export default React.memo(VerifiedContractsTableItem);
export type Sort = 'balance-asc' | 'balance-desc' | 'txs-asc' | 'txs-desc';
export type SortField = 'balance' | 'txs';
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