mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
skeletons for verified contracts
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import type { NextPage } from 'next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
|
||||
import getNetworkTitle from 'lib/networks/getNetworkTitle';
|
||||
import VerifiedContracts from 'ui/pages/VerifiedContracts';
|
||||
import Page from 'ui/shared/Page/Page';
|
||||
const VerifiedContracts = dynamic(() => import('ui/pages/VerifiedContracts'), { ssr: false });
|
||||
|
||||
const VerifiedContractsPage: NextPage = () => {
|
||||
const title = getNetworkTitle();
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { SmartContract } from 'types/api/contract';
|
||||
import type { VerifiedContract } from 'types/api/contracts';
|
||||
|
||||
import { ADDRESS_PARAMS } from './addressParams';
|
||||
|
||||
export const CONTRACT_CODE_UNVERIFIED = {
|
||||
creation_bytecode: '0x60806040526e',
|
||||
@@ -38,3 +41,15 @@ export const CONTRACT_CODE_VERIFIED = {
|
||||
source_code: 'source_code',
|
||||
verified_at: '2023-02-21T14:39:16.906760Z',
|
||||
} as unknown as SmartContract;
|
||||
|
||||
export const VERIFIED_CONTRACT_INFO: VerifiedContract = {
|
||||
address: { ...ADDRESS_PARAMS, name: 'StubContract' },
|
||||
coin_balance: '30319033612988277',
|
||||
compiler_version: 'v0.8.17+commit.8df45f5f',
|
||||
has_constructor_args: true,
|
||||
language: 'solidity',
|
||||
market_cap: null,
|
||||
optimization_enabled: false,
|
||||
tx_count: 565058,
|
||||
verified_at: '2023-04-10T13:16:33.884921Z',
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
|
||||
import { apos } from 'lib/html-entities';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { VERIFIED_CONTRACT_INFO } from 'stubs/contract';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import ActionBar from 'ui/shared/ActionBar';
|
||||
import DataListDisplay from 'ui/shared/DataListDisplay';
|
||||
import FilterInput from 'ui/shared/filters/FilterInput';
|
||||
@@ -32,9 +34,21 @@ const VerifiedContracts = () => {
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const { isError, isLoading, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({
|
||||
const { isError, isPlaceholderData, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({
|
||||
resourceName: 'verified_contracts',
|
||||
filters: { q: debouncedSearchTerm, filter: type },
|
||||
options: {
|
||||
placeholderData: generateListStub<'verified_contracts'>(
|
||||
VERIFIED_CONTRACT_INFO,
|
||||
50,
|
||||
{
|
||||
next_page_params: {
|
||||
items_count: '50',
|
||||
smart_contract_id: '50',
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const handleSearchTermChange = React.useCallback((value: string) => {
|
||||
@@ -107,10 +121,10 @@ const VerifiedContracts = () => {
|
||||
const content = sortedData ? (
|
||||
<>
|
||||
<Show below="lg" ssr={ false }>
|
||||
<VerifiedContractsList data={ sortedData }/>
|
||||
<VerifiedContractsList data={ sortedData } isLoading={ isPlaceholderData }/>
|
||||
</Show>
|
||||
<Hide below="lg" ssr={ false }>
|
||||
<VerifiedContractsTable data={ sortedData } sort={ sort } onSortToggle={ handleSortToggle }/>
|
||||
<VerifiedContractsTable data={ sortedData } sort={ sort } onSortToggle={ handleSortToggle } isLoading={ isPlaceholderData }/>
|
||||
</Hide>
|
||||
</>
|
||||
) : null;
|
||||
@@ -121,7 +135,7 @@ const VerifiedContracts = () => {
|
||||
<VerifiedContractsCounters/>
|
||||
<DataListDisplay
|
||||
isError={ isError }
|
||||
isLoading={ isLoading }
|
||||
isLoading={ false }
|
||||
items={ data?.items }
|
||||
skeletonProps={{ skeletonDesktopColumns: [ '50%', '130px', '130px', '50%', '80px', '110px' ] }}
|
||||
emptyText="There are no verified contracts."
|
||||
|
||||
@@ -5,10 +5,16 @@ import type { VerifiedContract } from 'types/api/contracts';
|
||||
|
||||
import VerifiedContractsListItem from './VerifiedContractsListItem';
|
||||
|
||||
const VerifiedContractsList = ({ data }: { data: Array<VerifiedContract>}) => {
|
||||
const VerifiedContractsList = ({ data, isLoading }: { data: Array<VerifiedContract>; isLoading: boolean }) => {
|
||||
return (
|
||||
<Box>
|
||||
{ data.map((item) => <VerifiedContractsListItem key={ item.address.hash } data={ item }/>) }
|
||||
{ data.map((item, index) => (
|
||||
<VerifiedContractsListItem
|
||||
key={ item.address.hash + (isLoading ? index : '') }
|
||||
data={ item }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
)) }
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Flex, Icon } from '@chakra-ui/react';
|
||||
import { Box, Flex, Skeleton } from '@chakra-ui/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
@@ -12,14 +12,16 @@ import dayjs from 'lib/date/dayjs';
|
||||
import Address from 'ui/shared/address/Address';
|
||||
import AddressIcon from 'ui/shared/address/AddressIcon';
|
||||
import AddressLink from 'ui/shared/address/AddressLink';
|
||||
import Icon from 'ui/shared/chakra/Icon';
|
||||
import HashStringShorten from 'ui/shared/HashStringShorten';
|
||||
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
|
||||
|
||||
interface Props {
|
||||
data: VerifiedContract;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const VerifiedContractsListItem = ({ data }: Props) => {
|
||||
const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
|
||||
const balance = data.coin_balance && data.coin_balance !== '0' ?
|
||||
BigNumber(data.coin_balance).div(10 ** appConfig.network.currency.decimals).dp(6).toFormat() :
|
||||
'0';
|
||||
@@ -27,50 +29,50 @@ const VerifiedContractsListItem = ({ data }: Props) => {
|
||||
return (
|
||||
<ListItemMobile rowGap={ 3 }>
|
||||
<Address columnGap={ 2 } overflow="hidden" w="100%">
|
||||
<AddressIcon address={ data.address }/>
|
||||
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name }/>
|
||||
<Box color="text_secondary" ml="auto">
|
||||
<AddressIcon address={ data.address } isLoading={ isLoading }/>
|
||||
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name } isLoading={ isLoading }/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" ml="auto">
|
||||
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
|
||||
</Box>
|
||||
</Skeleton>
|
||||
</Address>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Box fontWeight={ 500 }>Balance { appConfig.network.currency.symbol }</Box>
|
||||
<Box color="text_secondary">
|
||||
{ balance }
|
||||
</Box>
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Balance { appConfig.network.currency.symbol }</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>{ balance }</span>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Box fontWeight={ 500 }>Txs count</Box>
|
||||
<Box color="text_secondary">
|
||||
{ data.tx_count ? data.tx_count.toLocaleString() : '0' }
|
||||
</Box>
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Txs count</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>{ data.tx_count ? data.tx_count.toLocaleString() : '0' }</span>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Box fontWeight={ 500 } flexShrink="0">Compiler</Box>
|
||||
<Flex flexWrap="wrap">
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 } flexShrink="0">Compiler</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } display="flex" flexWrap="wrap">
|
||||
<Box textTransform="capitalize">{ data.language }</Box>
|
||||
<Box color="text_secondary" wordBreak="break-all" whiteSpace="pre-wrap"> ({ data.compiler_version })</Box>
|
||||
</Flex>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Box fontWeight={ 500 }>Optimization</Box>
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Optimization</Skeleton>
|
||||
{ data.optimization_enabled ?
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer"/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer"/> }
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer" isLoading={ isLoading }/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer" isLoading={ isLoading }/> }
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Box fontWeight={ 500 }>Constructor args</Box>
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Constructor args</Skeleton>
|
||||
{ data.has_constructor_args ?
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer"/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer"/> }
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer" isLoading={ isLoading }/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer" isLoading={ isLoading }/> }
|
||||
</Flex>
|
||||
<Flex columnGap={ 3 }>
|
||||
<Box fontWeight={ 500 }>Verified</Box>
|
||||
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Verified</Skeleton>
|
||||
<Flex alignItems="center" columnGap={ 2 }>
|
||||
<Icon as={ iconSuccess } boxSize={ 4 } color="green.500"/>
|
||||
<Box color="text_secondary">
|
||||
{ dayjs(data.verified_at).fromNow() }
|
||||
</Box>
|
||||
<Icon as={ iconSuccess } boxSize={ 4 } color="green.500" isLoading={ isLoading }/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>{ dayjs(data.verified_at).fromNow() }</span>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{ /* <Flex columnGap={ 3 }>
|
||||
|
||||
@@ -14,9 +14,10 @@ interface Props {
|
||||
data: Array<VerifiedContract>;
|
||||
sort: Sort | undefined;
|
||||
onSortToggle: (field: SortField) => () => void;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const VerifiedContractsTable = ({ data, sort, onSortToggle }: Props) => {
|
||||
const VerifiedContractsTable = ({ data, sort, onSortToggle, isLoading }: Props) => {
|
||||
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
|
||||
|
||||
return (
|
||||
@@ -25,13 +26,13 @@ const VerifiedContractsTable = ({ data, sort, onSortToggle }: Props) => {
|
||||
<Tr>
|
||||
<Th width="50%">Contract</Th>
|
||||
<Th width="130px" isNumeric>
|
||||
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('balance') } columnGap={ 1 }>
|
||||
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ isLoading ? undefined : 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 }>
|
||||
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ isLoading ? undefined : onSortToggle('txs') } columnGap={ 1 }>
|
||||
{ sort?.includes('txs') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
|
||||
Txs
|
||||
</Link>
|
||||
@@ -43,7 +44,12 @@ const VerifiedContractsTable = ({ data, sort, onSortToggle }: Props) => {
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{ data.map((item) => <VerifiedContractsTableItem key={ item.address.hash } data={ item }/>) }
|
||||
{ data.map((item, index) => (
|
||||
<VerifiedContractsTableItem
|
||||
key={ item.address.hash + (isLoading ? index : '') }
|
||||
data={ item }
|
||||
isLoading={ isLoading }/>
|
||||
)) }
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Tr, Td, Icon, Box, Flex, chakra, Tooltip } from '@chakra-ui/react';
|
||||
import { Tr, Td, Flex, chakra, Tooltip, Skeleton } from '@chakra-ui/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
@@ -11,13 +11,15 @@ 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 Icon from 'ui/shared/chakra/Icon';
|
||||
import HashStringShorten from 'ui/shared/HashStringShorten';
|
||||
|
||||
interface Props {
|
||||
data: VerifiedContract;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const VerifiedContractsTableItem = ({ data }: Props) => {
|
||||
const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
|
||||
const balance = data.coin_balance && data.coin_balance !== '0' ?
|
||||
BigNumber(data.coin_balance).div(10 ** appConfig.network.currency.decimals).dp(6).toFormat() :
|
||||
'0';
|
||||
@@ -26,52 +28,58 @@ const VerifiedContractsTableItem = ({ data }: Props) => {
|
||||
<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">
|
||||
<AddressIcon address={ data.address } isLoading={ isLoading }/>
|
||||
<Flex columnGap={ 2 } flexWrap="wrap" w="calc(100% - 32px)">
|
||||
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name } isLoading={ isLoading } my={ 1 }/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" my={ 1 }>
|
||||
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
|
||||
</Box>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td isNumeric lineHeight={ 6 }>
|
||||
{ balance }
|
||||
<Td isNumeric>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block" my={ 1 }>
|
||||
{ balance }
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td isNumeric lineHeight={ 6 }>
|
||||
{ data.tx_count ? data.tx_count.toLocaleString() : '0' }
|
||||
<Td isNumeric>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block" my={ 1 }>
|
||||
{ data.tx_count ? data.tx_count.toLocaleString() : '0' }
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td lineHeight={ 6 }>
|
||||
<Td>
|
||||
<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>
|
||||
<Skeleton isLoaded={ !isLoading } textTransform="capitalize" my={ 1 }>{ data.language }</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" wordBreak="break-all" my={ 1 }>
|
||||
<span>{ data.compiler_version }</span>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>
|
||||
<Tooltip label="Optimization">
|
||||
<span>
|
||||
<Tooltip label={ isLoading ? undefined : 'Optimization' }>
|
||||
<chakra.span display="inline-block">
|
||||
{ data.optimization_enabled ?
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer"/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer"/> }
|
||||
</span>
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer" isLoading={ isLoading }/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer" isLoading={ isLoading }/> }
|
||||
</chakra.span>
|
||||
</Tooltip>
|
||||
<Tooltip label="Constructor args">
|
||||
<chakra.span ml={ 3 }>
|
||||
<Tooltip label={ isLoading ? undefined : 'Constructor args' }>
|
||||
<chakra.span display="inline-block" 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"/> }
|
||||
<Icon as={ iconCheck } boxSize={ 6 } color="green.500" cursor="pointer" isLoading={ isLoading }/> :
|
||||
<Icon as={ iconCross } boxSize={ 6 } color="red.600" cursor="pointer" isLoading={ isLoading }/> }
|
||||
</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>
|
||||
<Td>
|
||||
<Flex alignItems="center" columnGap={ 2 } my={ 1 }>
|
||||
<Icon as={ iconSuccess } boxSize={ 4 } color="green.500" isLoading={ isLoading }/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>{ dayjs(data.verified_at).fromNow() }</span>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
</Td>
|
||||
{ /* <Td lineHeight={ 6 }>
|
||||
{ /* <Td>
|
||||
N/A
|
||||
</Td> */ }
|
||||
</Tr>
|
||||
|
||||
Reference in New Issue
Block a user