Commit 78c4fe54 authored by tom's avatar tom

skeleton for token transfers

parent 8b4ee616
import type { TokenCounters, TokenHolder, TokenInfo } from 'types/api/token';
import type { TokenCounters, TokenHolder, TokenInfo, TokenType } from 'types/api/token';
import type { TokenTransfer, TokenTransferResponse } from 'types/api/tokenTransfer';
export const TOKEN_INFO: TokenInfo<'ERC-20'> = {
const ADDRESS_PARAMS = {
hash: '0x2B51Ae4412F79c3c1cB12AA40Ea4ECEb4e80511a',
implementation_name: null,
is_contract: false,
is_verified: null,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
};
export const TOKEN_INFO_ERC_20: TokenInfo<'ERC-20'> = {
address: '0x2B51Ae4412F79c3c1cB12AA40Ea4ECEb4e80511a',
decimals: '18',
exchange_rate: null,
......@@ -11,21 +23,67 @@ export const TOKEN_INFO: TokenInfo<'ERC-20'> = {
type: 'ERC-20',
};
export const TOKEN_INFO_ERC_721: TokenInfo<'ERC-721'> = {
...TOKEN_INFO_ERC_20,
type: 'ERC-721',
};
export const TOKEN_INFO_ERC_1155: TokenInfo<'ERC-1155'> = {
...TOKEN_INFO_ERC_20,
type: 'ERC-1155',
};
export const TOKEN_COUNTERS: TokenCounters = {
token_holders_count: '123456',
transfers_count: '123456',
};
export const TOKEN_HOLDER: TokenHolder = {
address: {
hash: '0x2B51Ae4412F79c3c1cB12AA40Ea4ECEb4e80511a',
implementation_name: null,
is_contract: false,
is_verified: null,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
address: ADDRESS_PARAMS,
value: '1021378038331138520668',
};
export const TOKEN_TRANSFER_ERC_20: TokenTransfer = {
block_hash: '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70',
from: ADDRESS_PARAMS,
log_index: '4',
method: 'addLiquidity',
timestamp: '2022-06-24T10:22:11.000000Z',
to: ADDRESS_PARAMS,
token: TOKEN_INFO_ERC_20,
total: {
decimals: '18',
value: '9851351626684503',
},
tx_hash: '0x3ed9d81e7c1001bdda1caa1dc62c0acbbe3d2c671cdc20dc1e65efdaa4186967',
type: 'token_minting',
};
export const TOKEN_TRANSFER_ERC_721: TokenTransfer = {
...TOKEN_TRANSFER_ERC_20,
total: {
token_id: '35870',
},
token: TOKEN_INFO_ERC_721,
};
export const TOKEN_TRANSFER_ERC_1155: TokenTransfer = {
...TOKEN_TRANSFER_ERC_20,
total: {
token_id: '35870',
value: '123',
decimals: '18',
},
token: TOKEN_INFO_ERC_1155,
};
export const getTokenTransfersStub = (type?: TokenType): TokenTransferResponse => {
switch (type) {
case 'ERC-721':
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_721), next_page_params: null };
case 'ERC-1155':
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_1155), next_page_params: null };
default:
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_20), next_page_params: null };
}
};
......@@ -42,7 +42,7 @@ const TokenPageContent = () => {
const tokenQuery = useApiQuery('token', {
pathParams: { hash: hashString },
queryOptions: { enabled: Boolean(router.query.hash), placeholderData: stubs.TOKEN_INFO },
queryOptions: { enabled: Boolean(router.query.hash), placeholderData: stubs.TOKEN_INFO_ERC_20 },
});
useEffect(() => {
......@@ -66,6 +66,7 @@ const TokenPageContent = () => {
scrollRef,
options: {
enabled: Boolean(router.query.hash && (!router.query.tab || router.query.tab === 'token_transfers') && tokenQuery.data),
placeholderData: stubs.getTokenTransfersStub(tokenQuery.data?.type),
},
});
......@@ -101,10 +102,10 @@ const TokenPageContent = () => {
(tokenQuery.data?.type === 'ERC-1155' || tokenQuery.data?.type === 'ERC-721') ?
{ id: 'inventory', title: 'Inventory', component: <TokenInventory inventoryQuery={ inventoryQuery }/> } :
undefined,
contractQuery.data?.is_contract ? {
{
id: 'contract',
title: () => {
if (contractQuery.data.is_verified) {
if (contractQuery.data?.is_verified) {
return (
<>
<span>Contract</span>
......@@ -117,7 +118,7 @@ const TokenPageContent = () => {
},
component: <AddressContract tabs={ contractTabs } addressHash={ hashString }/>,
subTabs: contractTabs.map(tab => tab.id),
} : undefined,
},
].filter(Boolean);
let hasPagination;
......
import { Alert, Link, Text, chakra, useTheme, useColorModeValue } from '@chakra-ui/react';
import { Alert, Link, Text, chakra, useTheme, useColorModeValue, Skeleton } from '@chakra-ui/react';
import { transparentize } from '@chakra-ui/theme-tools';
import React from 'react';
......@@ -13,9 +13,10 @@ interface Props {
url: string;
alert?: string;
num?: number;
isLoading?: boolean;
}
const SocketNewItemsNotice = ({ children, className, url, num, alert, type = 'transaction' }: Props) => {
const SocketNewItemsNotice = ({ children, className, url, num, alert, type = 'transaction', isLoading }: Props) => {
const theme = useTheme();
const alertContent = (() => {
......@@ -37,7 +38,10 @@ const SocketNewItemsNotice = ({ children, className, url, num, alert, type = 'tr
);
})();
const content = (
const color = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const bgColor = useColorModeValue('orange.50', transparentize('orange.200', 0.16)(theme));
const content = !isLoading ? (
<Alert
className={ className }
status="warning"
......@@ -45,12 +49,12 @@ const SocketNewItemsNotice = ({ children, className, url, num, alert, type = 'tr
py="6px"
fontWeight={ 400 }
fontSize="sm"
bgColor={ useColorModeValue('orange.50', transparentize('orange.200', 0.16)(theme)) }
color={ useColorModeValue('blackAlpha.800', 'whiteAlpha.800') }
bgColor={ bgColor }
color={ color }
>
{ alertContent }
</Alert>
);
) : <Skeleton className={ className } h="33px"/>;
return children ? children({ content }) : content;
};
......
import { Box, Icon, chakra } from '@chakra-ui/react';
import { Box, Icon, chakra, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
......@@ -13,9 +13,10 @@ interface Props {
className?: string;
isDisabled?: boolean;
truncation?: 'dynamic' | 'constant';
isLoading?: boolean;
}
const TokenTransferNft = ({ hash, id, className, isDisabled, truncation = 'dynamic' }: Props) => {
const TokenTransferNft = ({ hash, id, className, isDisabled, isLoading, truncation = 'dynamic' }: Props) => {
const Component = isDisabled ? Box : LinkInternal;
return (
......@@ -29,9 +30,9 @@ const TokenTransferNft = ({ hash, id, className, isDisabled, truncation = 'dynam
className={ className }
>
<Icon as={ nftPlaceholder } boxSize="30px" mr={ 1 } color="inherit"/>
<Box maxW="calc(100% - 34px)">
<Skeleton isLoaded={ !isLoading } maxW="calc(100% - 34px)">
{ truncation === 'constant' ? <HashStringShorten hash={ id }/> : <HashStringShortenDynamic hash={ id } fontWeight={ 500 }/> }
</Box>
</Skeleton>
</Component>
);
};
......
......@@ -30,7 +30,7 @@ type Props = {
const TokenTransfer = ({ transfersQuery, tokenId }: Props) => {
const isMobile = useIsMobile();
const router = useRouter();
const { isError, isLoading, data, pagination, isPaginationVisible } = transfersQuery;
const { isError, isLoading, isPlaceholderData, data, pagination, isPaginationVisible } = transfersQuery;
const [ newItemsCount, setNewItemsCount ] = useGradualIncrement(0);
const [ socketAlert, setSocketAlert ] = React.useState('');
......@@ -72,6 +72,7 @@ const TokenTransfer = ({ transfersQuery, tokenId }: Props) => {
socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount }
tokenId={ tokenId }
isLoading={ isPlaceholderData }
/>
</Hide>
<Show below="lg" ssr={ false }>
......@@ -82,9 +83,10 @@ const TokenTransfer = ({ transfersQuery, tokenId }: Props) => {
alert={ socketAlert }
type="token_transfer"
borderBottomRadius={ 0 }
isLoading={ isPlaceholderData }
/>
) }
<TokenTransferList data={ items } tokenId={ tokenId }/>
<TokenTransferList data={ items } tokenId={ tokenId } isLoading={ isPlaceholderData }/>
</Show>
</>
) : null;
......@@ -98,7 +100,7 @@ const TokenTransfer = ({ transfersQuery, tokenId }: Props) => {
return (
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
isLoading={ !isPlaceholderData && isLoading }
items={ data?.items }
skeletonProps={{
isLongSkeleton: true,
......
......@@ -8,9 +8,10 @@ import TokenTransferListItem from 'ui/token/TokenTransfer/TokenTransferListItem'
interface Props {
data: Array<TokenTransfer>;
tokenId?: string;
isLoading?: boolean;
}
const TokenTransferList = ({ data, tokenId }: Props) => {
const TokenTransferList = ({ data, tokenId, isLoading }: Props) => {
return (
<Box>
{ data.map((item, index) => (
......@@ -18,6 +19,7 @@ const TokenTransferList = ({ data, tokenId }: Props) => {
key={ index }
{ ...item }
tokenId={ tokenId }
isLoading={ isLoading }
/>
)) }
</Box>
......
import { Text, Flex, Tag, Icon, useColorModeValue } from '@chakra-ui/react';
import { Text, Flex, Tag, Icon, useColorModeValue, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
......@@ -14,7 +14,7 @@ import AddressLink from 'ui/shared/address/AddressLink';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
type Props = TokenTransfer & {tokenId?: string};
type Props = TokenTransfer & { tokenId?: string; isLoading?: boolean };
const TokenTransferListItem = ({
token,
......@@ -25,6 +25,7 @@ const TokenTransferListItem = ({
method,
timestamp,
tokenId,
isLoading,
}: Props) => {
const value = (() => {
if (!('value' in total)) {
......@@ -54,32 +55,47 @@ const TokenTransferListItem = ({
type="transaction"
fontWeight="700"
truncation="constant"
isLoading={ isLoading }
/>
</Address>
</Flex>
{ timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
{ timestamp && (
<Text variant="secondary" fontWeight="400" fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ timeAgo }
</Skeleton>
</Text>
) }
</Flex>
{ method && <Tag colorScheme="gray">{ method }</Tag> }
{ method && <Skeleton isLoaded={ !isLoading } borderRadius="sm"><Tag colorScheme="gray">{ method }</Tag></Skeleton> }
<Flex w="100%" columnGap={ 3 }>
<Address width="50%">
<AddressIcon address={ from }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from.hash } type="address_token" tokenHash={ token.address }/>
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from.hash } type="address_token" tokenHash={ token.address } isLoading={ isLoading }/>
</Address>
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/>
<Address width="50%">
<AddressIcon address={ to }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ to.hash } type="address_token" tokenHash={ token.address }/>
<AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ to.hash } type="address_token" tokenHash={ token.address } isLoading={ isLoading }/>
</Address>
</Flex>
{ value && (token.type === 'ERC-20' || token.type === 'ERC-1155') && (
<Flex columnGap={ 2 } w="100%">
<Text fontWeight={ 500 } flexShrink={ 0 }>Value</Text>
<Text variant="secondary">{ value }</Text>
<Text>{ trimTokenSymbol(token.symbol) }</Text>
<Skeleton isLoaded={ !isLoading } variant="secondary">
{ value }
</Skeleton>
<Skeleton isLoaded={ !isLoading }>{ trimTokenSymbol(token.symbol) }</Skeleton>
</Flex>
) }
{ 'token_id' in total && (token.type === 'ERC-721' || token.type === 'ERC-1155') &&
<TokenTransferNft hash={ token.address } id={ total.token_id } isDisabled={ Boolean(tokenId && tokenId === total.token_id) }/> }
{ 'token_id' in total && (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
<TokenTransferNft
hash={ token.address }
id={ total.token_id }
isDisabled={ Boolean(tokenId && tokenId === total.token_id) }
isLoading={ isLoading }
/>
) }
</ListItemMobile>
);
};
......
import { Table, Tbody, Tr, Th, Td } from '@chakra-ui/react';
import { Table, Tbody, Tr, Th, Td, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer';
......@@ -15,9 +15,10 @@ interface Props {
socketInfoAlert?: string;
socketInfoNum?: number;
tokenId?: string;
isLoading?: boolean;
}
const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socketInfoNum, tokenId }: Props) => {
const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socketInfoNum, tokenId, isLoading }: Props) => {
const tokenType = data[0].token.type;
const tokenSymbol = data[0].token.symbol;
......@@ -38,6 +39,9 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket
{ showSocketInfo && (
<Tr>
<Td colSpan={ 10 } p={ 0 }>
{ isLoading ? (
<Skeleton my="6px" mx="10px" h={ 4 } maxW="215px" w="100%" borderRadius="sm"/>
) : (
<SocketNewItemsNotice
borderRadius={ 0 }
pl="10px"
......@@ -46,11 +50,12 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket
num={ socketInfoNum }
type="token_transfer"
/>
) }
</Td>
</Tr>
) }
{ data.map((item, index) => (
<TokenTransferTableItem key={ index } { ...item } tokenId={ tokenId }/>
<TokenTransferTableItem key={ index } { ...item } tokenId={ tokenId } isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
......
import { Tr, Td, Tag, Text, Icon, Grid } from '@chakra-ui/react';
import { Tr, Td, Tag, Text, Icon, Grid, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
......@@ -11,7 +11,7 @@ import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
type Props = TokenTransfer & { tokenId?: string }
type Props = TokenTransfer & { tokenId?: string; isLoading?: boolean }
const TokenTransferTableItem = ({
token,
......@@ -22,6 +22,7 @@ const TokenTransferTableItem = ({
method,
timestamp,
tokenId,
isLoading,
}: Props) => {
const value = (() => {
if (!('value' in total)) {
......@@ -37,18 +38,26 @@ const TokenTransferTableItem = ({
<Tr alignItems="top">
<Td>
<Grid alignItems="center" gridTemplateColumns="auto 130px" width="fit-content">
<Address display="inline-flex" fontWeight={ 600 } lineHeight="30px">
<AddressLink type="transaction" hash={ txHash }/>
<Address display="inline-flex" fontWeight={ 600 }>
<AddressLink type="transaction" hash={ txHash } isLoading={ isLoading }/>
</Address>
{ timestamp && <Text color="gray.500" fontWeight="400" ml="10px">{ timeAgo }</Text> }
{ timestamp && (
<Text color="gray.500" fontWeight="400" ml="10px">
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ timeAgo }
</Skeleton>
</Text>
) }
</Grid>
</Td>
<Td>
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm">
{ method ? <Tag colorScheme="gray">{ method }</Tag> : '-' }
</Skeleton>
</Td>
<Td>
<Address display="inline-flex" maxW="100%" lineHeight="30px">
<AddressIcon address={ from }/>
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink
ml={ 2 }
flexGrow={ 1 }
......@@ -58,6 +67,7 @@ const TokenTransferTableItem = ({
alias={ from.name }
tokenHash={ token.address }
truncation="constant"
isLoading={ isLoading }
/>
</Address>
</Td>
......@@ -65,8 +75,8 @@ const TokenTransferTableItem = ({
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/>
</Td>
<Td>
<Address display="inline-flex" maxW="100%" lineHeight="30px">
<AddressIcon address={ to }/>
<Address display="inline-flex" maxW="100%">
<AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink
ml={ 2 }
flexGrow={ 1 }
......@@ -76,25 +86,29 @@ const TokenTransferTableItem = ({
alias={ to.name }
tokenHash={ token.address }
truncation="constant"
isLoading={ isLoading }
/>
</Address>
</Td>
{ (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
<Td lineHeight="30px">
<Td>
{ 'token_id' in total ? (
<TokenTransferNft
hash={ token.address }
id={ total.token_id }
justifyContent={ token.type === 'ERC-721' ? 'end' : 'start' }
isDisabled={ Boolean(tokenId && tokenId === total.token_id) }
isLoading={ isLoading }
/>
) : '-'
}
</Td>
) }
{ (token.type === 'ERC-20' || token.type === 'ERC-1155') && (
<Td isNumeric verticalAlign="top" lineHeight="30px">
<Td isNumeric verticalAlign="top">
<Skeleton isLoaded={ !isLoading }>
{ value || '-' }
</Skeleton>
</Td>
) }
</Tr>
......
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