Commit 98dcb80b authored by tom's avatar tom

skeletons for token transfers

parent 172791f3
import type { TokenCounters, TokenHolder, TokenHolders, TokenInfo, TokenInstance, TokenInventoryResponse, TokenType } from 'types/api/token'; import type { TokenCounters, TokenHolder, TokenHolders, TokenInfo, TokenInstance, TokenInventoryResponse, TokenType } from 'types/api/token';
import type { TokenTransfer, TokenTransferResponse } from 'types/api/tokenTransfer'; import type { TokenTransfer, TokenTransferPagination, TokenTransferResponse } from 'types/api/tokenTransfer';
import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams'; import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams';
import { BLOCK_HASH } from './block'; import { BLOCK_HASH } from './block';
...@@ -72,14 +72,14 @@ export const TOKEN_TRANSFER_ERC_1155: TokenTransfer = { ...@@ -72,14 +72,14 @@ export const TOKEN_TRANSFER_ERC_1155: TokenTransfer = {
token: TOKEN_INFO_ERC_1155, token: TOKEN_INFO_ERC_1155,
}; };
export const getTokenTransfersStub = (type?: TokenType): TokenTransferResponse => { export const getTokenTransfersStub = (type?: TokenType, pagination: TokenTransferPagination | null = null): TokenTransferResponse => {
switch (type) { switch (type) {
case 'ERC-721': case 'ERC-721':
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_721), next_page_params: null }; return { items: Array(50).fill(TOKEN_TRANSFER_ERC_721), next_page_params: pagination };
case 'ERC-1155': case 'ERC-1155':
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_1155), next_page_params: null }; return { items: Array(50).fill(TOKEN_TRANSFER_ERC_1155), next_page_params: pagination };
default: default:
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_20), next_page_params: null }; return { items: Array(50).fill(TOKEN_TRANSFER_ERC_20), next_page_params: pagination };
} }
}; };
......
...@@ -20,11 +20,12 @@ import getQueryParamString from 'lib/router/getQueryParamString'; ...@@ -20,11 +20,12 @@ import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import TOKEN_TYPE from 'lib/token/tokenTypes'; import TOKEN_TYPE from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
...@@ -81,11 +82,18 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -81,11 +82,18 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
}, },
); );
const { isError, isLoading, data, pagination, onFilterChange, isPaginationVisible } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange, isPaginationVisible } = useQueryWithPages({
resourceName: 'address_token_transfers', resourceName: 'address_token_transfers',
pathParams: { hash: currentAddress }, pathParams: { hash: currentAddress },
filters: tokenFilter ? { token: tokenFilter } : filters, filters: tokenFilter ? { token: tokenFilter } : filters,
scrollRef, scrollRef,
options: {
placeholderData: getTokenTransfersStub(undefined, {
block_number: 7793535,
index: 46,
items_count: 50,
}),
},
}); });
const handleTypeFilterChange = React.useCallback((nextValue: Array<TokenType>) => { const handleTypeFilterChange = React.useCallback((nextValue: Array<TokenType>) => {
...@@ -172,16 +180,17 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -172,16 +180,17 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
showSocketInfo={ pagination.page === 1 && !tokenFilter } showSocketInfo={ pagination.page === 1 && !tokenFilter }
socketInfoAlert={ socketAlert } socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount } socketInfoNum={ newItemsCount }
isLoading={ isPlaceholderData }
/> />
</Hide> </Hide>
<Show below="lg" ssr={ false }> <Show below="lg" ssr={ false }>
{ pagination.page === 1 && !tokenFilter && ( { pagination.page === 1 && !tokenFilter && (
<SocketNewItemsNotice <SocketNewItemsNotice.Mobile
url={ window.location.href } url={ window.location.href }
num={ newItemsCount } num={ newItemsCount }
alert={ socketAlert } alert={ socketAlert }
type="token_transfer" type="token_transfer"
borderBottomRadius={ 0 } isLoading={ isPlaceholderData }
/> />
) } ) }
<TokenTransferList <TokenTransferList
...@@ -189,6 +198,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -189,6 +198,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
baseAddress={ currentAddress } baseAddress={ currentAddress }
showTxInfo showTxInfo
enableTimeIncrement enableTimeIncrement
isLoading={ isPlaceholderData }
/> />
</Show> </Show>
</> </>
...@@ -221,7 +231,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -221,7 +231,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
<> <>
{ isMobile && tokenFilterComponent } { isMobile && tokenFilterComponent }
{ !isActionBarHidden && ( { !isActionBarHidden && (
<ActionBar mt={ -6 } showShadow={ isLoading }> <ActionBar mt={ -6 }>
{ !isMobile && tokenFilterComponent } { !isMobile && tokenFilterComponent }
{ !tokenFilter && ( { !tokenFilter && (
<TokenTransferFilter <TokenTransferFilter
...@@ -231,9 +241,17 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -231,9 +241,17 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
withAddressFilter withAddressFilter
onAddressFilterChange={ handleAddressFilterChange } onAddressFilterChange={ handleAddressFilterChange }
defaultAddressFilter={ filters.filter } defaultAddressFilter={ filters.filter }
isLoading={ isPlaceholderData }
/>
) }
{ currentAddress && (
<AddressCsvExportLink
address={ currentAddress }
type="token-transfers"
ml={{ base: 2, lg: 'auto' }}
isLoading={ isPlaceholderData }
/> />
) } ) }
{ currentAddress && <AddressCsvExportLink address={ currentAddress } type="token-transfers" ml={{ base: 2, lg: 'auto' }}/> }
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> } { isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
</ActionBar> </ActionBar>
) } ) }
...@@ -243,7 +261,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -243,7 +261,7 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
return ( return (
<DataListDisplay <DataListDisplay
isError={ isError } isError={ isError }
isLoading={ isLoading } isLoading={ false }
items={ data?.items } items={ data?.items }
skeletonProps={{ skeletonProps={{
isLongSkeleton: true, isLongSkeleton: true,
......
...@@ -21,7 +21,7 @@ const AdditionalInfoButton = ({ isOpen, onClick, className, isLoading }: Props, ...@@ -21,7 +21,7 @@ const AdditionalInfoButton = ({ isOpen, onClick, className, isLoading }: Props,
const infoBgColor = useColorModeValue('blue.50', 'gray.600'); const infoBgColor = useColorModeValue('blue.50', 'gray.600');
if (isLoading) { if (isLoading) {
return <Skeleton boxSize={ 6 } borderRadius="sm"/>; return <Skeleton boxSize={ 6 } borderRadius="sm" flexShrink={ 0 }/>;
} }
return ( return (
......
...@@ -33,7 +33,7 @@ interface Props { ...@@ -33,7 +33,7 @@ interface Props {
const TokenLogo = ({ hash, name, className, isLoading }: Props) => { const TokenLogo = ({ hash, name, className, isLoading }: Props) => {
if (isLoading) { if (isLoading) {
return <Skeleton className={ className } borderRadius="base"/>; return <Skeleton className={ className } borderRadius="base" flexShrink={ 0 }/>;
} }
const logoSrc = appConfig.network.assetsPathname && hash ? [ const logoSrc = appConfig.network.assetsPathname && hash ? [
......
...@@ -11,13 +11,14 @@ interface Props { ...@@ -11,13 +11,14 @@ interface Props {
className?: string; className?: string;
logoSize?: number; logoSize?: number;
isDisabled?: boolean; isDisabled?: boolean;
isLoading?: boolean;
} }
const TokenSnippet = ({ symbol, hash, name, className, logoSize = 6, isDisabled }: Props) => { const TokenSnippet = ({ symbol, hash, name, className, logoSize = 6, isDisabled, isLoading }: Props) => {
return ( return (
<Flex className={ className } alignItems="center" columnGap={ 2 } w="100%"> <Flex className={ className } alignItems="center" columnGap={ 2 } w="100%">
<TokenLogo boxSize={ logoSize } hash={ hash } name={ name }/> <TokenLogo boxSize={ logoSize } hash={ hash } name={ name } isLoading={ isLoading }/>
<AddressLink hash={ hash } alias={ name } type="token" isDisabled={ isDisabled }/> <AddressLink hash={ hash } alias={ name } type="token" isDisabled={ isDisabled } isLoading={ isLoading }/>
{ symbol && <Text variant="secondary">({ symbol })</Text> } { symbol && <Text variant="secondary">({ symbol })</Text> }
</Flex> </Flex>
); );
......
...@@ -19,6 +19,7 @@ interface Props { ...@@ -19,6 +19,7 @@ interface Props {
withAddressFilter?: boolean; withAddressFilter?: boolean;
onAddressFilterChange?: (nextValue: string) => void; onAddressFilterChange?: (nextValue: string) => void;
defaultAddressFilter?: AddressFromToFilter; defaultAddressFilter?: AddressFromToFilter;
isLoading?: boolean;
} }
const TokenTransferFilter = ({ const TokenTransferFilter = ({
...@@ -28,10 +29,11 @@ const TokenTransferFilter = ({ ...@@ -28,10 +29,11 @@ const TokenTransferFilter = ({
withAddressFilter, withAddressFilter,
onAddressFilterChange, onAddressFilterChange,
defaultAddressFilter, defaultAddressFilter,
isLoading,
}: Props) => { }: Props) => {
return ( return (
<PopoverFilter appliedFiltersNum={ appliedFiltersNum } contentProps={{ w: '200px' }}> <PopoverFilter appliedFiltersNum={ appliedFiltersNum } contentProps={{ w: '200px' }} isLoading={ isLoading }>
{ withAddressFilter && ( { withAddressFilter && (
<> <>
<Text variant="secondary" fontWeight={ 600 }>Address</Text> <Text variant="secondary" fontWeight={ 600 }>Address</Text>
......
...@@ -10,18 +10,20 @@ interface Props { ...@@ -10,18 +10,20 @@ interface Props {
baseAddress?: string; baseAddress?: string;
showTxInfo?: boolean; showTxInfo?: boolean;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
isLoading?: boolean;
} }
const TokenTransferList = ({ data, baseAddress, showTxInfo, enableTimeIncrement }: Props) => { const TokenTransferList = ({ data, baseAddress, showTxInfo, enableTimeIncrement, isLoading }: Props) => {
return ( return (
<Box> <Box>
{ data.map((item) => ( { data.map((item, index) => (
<TokenTransferListItem <TokenTransferListItem
key={ item.tx_hash + item.block_hash + item.log_index } key={ item.tx_hash + item.block_hash + item.log_index + (isLoading ? index : '') }
{ ...item } { ...item }
baseAddress={ baseAddress } baseAddress={ baseAddress }
showTxInfo={ showTxInfo } showTxInfo={ showTxInfo }
enableTimeIncrement={ enableTimeIncrement } enableTimeIncrement={ enableTimeIncrement }
isLoading={ isLoading }
/> />
)) } )) }
</Box> </Box>
......
import { Text, Flex, Tag, Icon } from '@chakra-ui/react'; import { Flex, Icon, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
...@@ -10,6 +10,8 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; ...@@ -10,6 +10,8 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
...@@ -17,12 +19,11 @@ import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; ...@@ -17,12 +19,11 @@ import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import CopyToClipboard from '../CopyToClipboard';
type Props = TokenTransfer & { type Props = TokenTransfer & {
baseAddress?: string; baseAddress?: string;
showTxInfo?: boolean; showTxInfo?: boolean;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
isLoading?: boolean;
} }
const TokenTransferListItem = ({ const TokenTransferListItem = ({
...@@ -36,6 +37,7 @@ const TokenTransferListItem = ({ ...@@ -36,6 +37,7 @@ const TokenTransferListItem = ({
type, type,
timestamp, timestamp,
enableTimeIncrement, enableTimeIncrement,
isLoading,
}: Props) => { }: Props) => {
const value = (() => { const value = (() => {
if (!('value' in total)) { if (!('value' in total)) {
...@@ -51,57 +53,63 @@ const TokenTransferListItem = ({ ...@@ -51,57 +53,63 @@ const TokenTransferListItem = ({
return ( return (
<ListItemMobile rowGap={ 3 } isAnimated> <ListItemMobile rowGap={ 3 } isAnimated>
<Flex w="100%" justifyContent="space-between"> <Flex w="100%" justifyContent="space-between">
<Flex flexWrap="wrap" rowGap={ 1 } mr={ showTxInfo && txHash ? 2 : 0 }> <Flex flexWrap="wrap" rowGap={ 1 } mr={ showTxInfo && txHash ? 2 : 0 } columnGap={ 2 }>
<TokenSnippet hash={ token.address } w="auto" maxW="calc(100% - 140px)" name={ token.name || 'Unnamed token' }/> <TokenSnippet hash={ token.address } w="auto" maxW="calc(100% - 140px)" name={ token.name || 'Unnamed token' } isLoading={ isLoading }/>
<Tag flexShrink={ 0 } ml={ 2 } mr={ 2 }>{ token.type }</Tag> <Tag flexShrink={ 0 } isLoading={ isLoading }>{ token.type }</Tag>
<Tag colorScheme="orange">{ getTokenTransferTypeText(type) }</Tag> <Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex> </Flex>
{ showTxInfo && txHash && ( { showTxInfo && txHash && (
<TxAdditionalInfo hash={ txHash } isMobile/> <TxAdditionalInfo hash={ txHash } isMobile isLoading={ isLoading }/>
) } ) }
</Flex> </Flex>
{ 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id }/> } { 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id } isLoading={ isLoading }/> }
{ showTxInfo && txHash && ( { showTxInfo && txHash && (
<Flex justifyContent="space-between" alignItems="center" lineHeight="24px" width="100%"> <Flex justifyContent="space-between" alignItems="center" lineHeight="24px" width="100%">
<Flex> <Flex>
<Icon <Skeleton isLoaded={ !isLoading } boxSize="30px" mr={ 2 } borderRadius="base">
as={ transactionIcon } <Icon
boxSize="30px" as={ transactionIcon }
mr={ 2 } boxSize="30px"
color="link" color="link"
/> />
</Skeleton>
<Address width="100%"> <Address width="100%">
<AddressLink <AddressLink
hash={ txHash } hash={ txHash }
type="transaction" type="transaction"
fontWeight="700" fontWeight="700"
truncation="constant" truncation="constant"
isLoading={ isLoading }
/> />
</Address> </Address>
</Flex> </Flex>
{ timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> } { timestamp && (
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight="400" fontSize="sm">
<span>{ timeAgo }</span>
</Skeleton>
) }
</Flex> </Flex>
) } ) }
<Flex w="100%" columnGap={ 3 }> <Flex w="100%" columnGap={ 3 }>
<Address width={ addressWidth }> <Address width={ addressWidth }>
<AddressIcon address={ from }/> <AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ baseAddress === from.hash }/> <AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ baseAddress === from.hash } isLoading={ isLoading }/>
{ baseAddress !== from.hash && <CopyToClipboard text={ from.hash }/> } { baseAddress !== from.hash && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address> </Address>
{ baseAddress ? { baseAddress ?
<InOutTag isIn={ baseAddress === to.hash } isOut={ baseAddress === from.hash } w="50px" textAlign="center"/> : <InOutTag isIn={ baseAddress === to.hash } isOut={ baseAddress === from.hash } w="50px" textAlign="center" isLoading={ isLoading }/> :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/> <Skeleton isLoaded={ !isLoading } boxSize={ 6 }><Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/></Skeleton>
} }
<Address width={ addressWidth }> <Address width={ addressWidth }>
<AddressIcon address={ to }/> <AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ to.hash } isDisabled={ baseAddress === to.hash }/> <AddressLink type="address" ml={ 2 } fontWeight="500" hash={ to.hash } isDisabled={ baseAddress === to.hash } isLoading={ isLoading }/>
{ baseAddress !== to.hash && <CopyToClipboard text={ to.hash }/> } { baseAddress !== to.hash && <CopyToClipboard text={ to.hash } isLoading={ isLoading }/> }
</Address> </Address>
</Flex> </Flex>
{ value && ( { value && (
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Text fontWeight={ 500 } flexShrink={ 0 }>Value</Text> <Skeleton isLoaded={ !isLoading } fontWeight={ 500 } flexShrink={ 0 }>Value</Skeleton>
<Text variant="secondary">{ value }</Text> <Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ value }</span></Skeleton>
</Flex> </Flex>
) } ) }
</ListItemMobile> </ListItemMobile>
......
import { Table, Tbody, Tr, Th, Td } from '@chakra-ui/react'; import { Table, Tbody, Tr, Th } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import TokenTransferTableItem from 'ui/shared/TokenTransfer/TokenTransferTableItem'; import TokenTransferTableItem from 'ui/shared/TokenTransfer/TokenTransferTableItem';
...@@ -16,6 +16,7 @@ interface Props { ...@@ -16,6 +16,7 @@ interface Props {
showSocketInfo?: boolean; showSocketInfo?: boolean;
socketInfoAlert?: string; socketInfoAlert?: string;
socketInfoNum?: number; socketInfoNum?: number;
isLoading?: boolean;
} }
const TokenTransferTable = ({ const TokenTransferTable = ({
...@@ -27,6 +28,7 @@ const TokenTransferTable = ({ ...@@ -27,6 +28,7 @@ const TokenTransferTable = ({
showSocketInfo, showSocketInfo,
socketInfoAlert, socketInfoAlert,
socketInfoNum, socketInfoNum,
isLoading,
}: Props) => { }: Props) => {
return ( return (
...@@ -45,26 +47,22 @@ const TokenTransferTable = ({ ...@@ -45,26 +47,22 @@ const TokenTransferTable = ({
</Thead> </Thead>
<Tbody> <Tbody>
{ showSocketInfo && ( { showSocketInfo && (
<Tr> <SocketNewItemsNotice.Desktop
<Td colSpan={ 10 } p={ 0 }> url={ window.location.href }
<SocketNewItemsNotice alert={ socketInfoAlert }
borderRadius={ 0 } num={ socketInfoNum }
pl="10px" type="token_transfer"
url={ window.location.href } isLoading={ isLoading }
alert={ socketInfoAlert } />
num={ socketInfoNum }
type="token_transfer"
/>
</Td>
</Tr>
) } ) }
{ data.map((item) => ( { data.map((item, index) => (
<TokenTransferTableItem <TokenTransferTableItem
key={ item.tx_hash + item.block_hash + item.log_index } key={ item.tx_hash + item.block_hash + item.log_index + (isLoading ? index : '') }
{ ...item } { ...item }
baseAddress={ baseAddress } baseAddress={ baseAddress }
showTxInfo={ showTxInfo } showTxInfo={ showTxInfo }
enableTimeIncrement={ enableTimeIncrement } enableTimeIncrement={ enableTimeIncrement }
isLoading={ isLoading }
/> />
)) } )) }
</Tbody> </Tbody>
......
import { Tr, Td, Tag, Flex, Text } from '@chakra-ui/react'; import { Tr, Td, Flex, Skeleton, Box } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
...@@ -8,18 +8,19 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; ...@@ -8,18 +8,19 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import CopyToClipboard from '../CopyToClipboard';
type Props = TokenTransfer & { type Props = TokenTransfer & {
baseAddress?: string; baseAddress?: string;
showTxInfo?: boolean; showTxInfo?: boolean;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
isLoading?: boolean;
} }
const TokenTransferTableItem = ({ const TokenTransferTableItem = ({
...@@ -33,6 +34,7 @@ const TokenTransferTableItem = ({ ...@@ -33,6 +34,7 @@ const TokenTransferTableItem = ({
type, type,
timestamp, timestamp,
enableTimeIncrement, enableTimeIncrement,
isLoading,
}: Props) => { }: Props) => {
const timeAgo = useTimeAgoIncrement(timestamp, enableTimeIncrement); const timeAgo = useTimeAgoIncrement(timestamp, enableTimeIncrement);
...@@ -40,48 +42,81 @@ const TokenTransferTableItem = ({ ...@@ -40,48 +42,81 @@ const TokenTransferTableItem = ({
<Tr alignItems="top"> <Tr alignItems="top">
{ showTxInfo && txHash && ( { showTxInfo && txHash && (
<Td> <Td>
<TxAdditionalInfo hash={ txHash }/> <Box my="3px">
<TxAdditionalInfo hash={ txHash } isLoading={ isLoading }/>
</Box>
</Td> </Td>
) } ) }
<Td> <Td>
<Flex flexDir="column" alignItems="flex-start"> <Flex flexDir="column" alignItems="flex-start" my="3px" rowGap={ 2 }>
<TokenSnippet hash={ token.address } name={ token.name || 'Unnamed token' } lineHeight="30px"/> <TokenSnippet hash={ token.address } name={ token.name || 'Unnamed token' } isLoading={ isLoading }/>
<Tag mt={ 1 }>{ token.type }</Tag> <Tag isLoading={ isLoading }>{ token.type }</Tag>
<Tag colorScheme="orange" mt={ 2 }>{ getTokenTransferTypeText(type) }</Tag> <Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex> </Flex>
</Td> </Td>
<Td lineHeight="30px"> <Td>
{ 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id }/> } { 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id } isLoading={ isLoading }/> }
</Td> </Td>
{ showTxInfo && txHash && ( { showTxInfo && txHash && (
<Td> <Td>
<Address display="inline-flex" maxW="100%" fontWeight={ 600 } lineHeight="30px"> <Address display="inline-flex" maxW="100%" fontWeight={ 600 } mt="7px">
<AddressLink type="transaction" hash={ txHash }/> <AddressLink type="transaction" hash={ txHash } isLoading={ isLoading }/>
</Address> </Address>
{ timestamp && <Text color="gray.500" fontWeight="400" mt="10px">{ timeAgo }</Text> } { timestamp && (
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight="400" mt="10px" display="inline-block">
<span>{ timeAgo }</span>
</Skeleton>
) }
</Td> </Td>
) } ) }
<Td> <Td>
<Address display="inline-flex" maxW="100%" lineHeight="30px"> <Address display="inline-flex" maxW="100%" my="3px">
<AddressIcon address={ from }/> <AddressIcon address={ from } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } alias={ from.name } flexGrow={ 1 } isDisabled={ baseAddress === from.hash }/> <AddressLink
{ baseAddress !== from.hash && <CopyToClipboard text={ from.hash }/> } type="address" ml={ 2 }
fontWeight="500"
hash={ from.hash }
alias={ from.name }
flexGrow={ 1 }
isDisabled={ baseAddress === from.hash }
isLoading={ isLoading }
/>
{ baseAddress !== from.hash && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address> </Address>
</Td> </Td>
{ baseAddress && ( { baseAddress && (
<Td px={ 0 }> <Td px={ 0 }>
<InOutTag isIn={ baseAddress === to.hash } isOut={ baseAddress === from.hash } w="50px" textAlign="center" mt="3px"/> <Box mt="3px">
<InOutTag
isIn={ baseAddress === to.hash }
isOut={ baseAddress === from.hash }
w="50px"
textAlign="center"
isLoading={ isLoading }
/>
</Box>
</Td> </Td>
) } ) }
<Td> <Td>
<Address display="inline-flex" maxW="100%" lineHeight="30px"> <Address display="inline-flex" maxW="100%" my="3px">
<AddressIcon address={ to }/> <AddressIcon address={ to } isLoading={ isLoading }/>
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ to.hash } alias={ to.name } flexGrow={ 1 } isDisabled={ baseAddress === to.hash }/> <AddressLink
{ baseAddress !== to.hash && <CopyToClipboard text={ to.hash }/> } type="address"
ml={ 2 }
fontWeight="500"
hash={ to.hash }
alias={ to.name }
flexGrow={ 1 }
isDisabled={ baseAddress === to.hash }
isLoading={ isLoading }
/>
{ baseAddress !== to.hash && <CopyToClipboard text={ to.hash } isLoading={ isLoading }/> }
</Address> </Address>
</Td> </Td>
<Td isNumeric verticalAlign="top" lineHeight="30px"> <Td isNumeric verticalAlign="top">
{ 'value' in total && BigNumber(total.value).div(BigNumber(10 ** Number(total.decimals))).dp(8).toFormat() } <Skeleton isLoaded={ !isLoading } display="inline-block" my="7px">
{ 'value' in total && BigNumber(total.value).div(BigNumber(10 ** Number(total.decimals))).dp(8).toFormat() }
</Skeleton>
</Td> </Td>
</Tr> </Tr>
); );
......
...@@ -15,9 +15,10 @@ interface Props { ...@@ -15,9 +15,10 @@ interface Props {
isActive?: boolean; isActive?: boolean;
children: React.ReactNode; children: React.ReactNode;
contentProps?: PopoverContentProps; contentProps?: PopoverContentProps;
isLoading?: boolean;
} }
const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isActive }: Props) => { const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isActive, isLoading }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
return ( return (
...@@ -27,6 +28,7 @@ const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isActive }: ...@@ -27,6 +28,7 @@ const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isActive }:
isActive={ isOpen || isActive || Number(appliedFiltersNum) > 0 } isActive={ isOpen || isActive || Number(appliedFiltersNum) > 0 }
onClick={ onToggle } onClick={ onToggle }
appliedFiltersNum={ appliedFiltersNum } appliedFiltersNum={ appliedFiltersNum }
isLoading={ isLoading }
/> />
</PopoverTrigger> </PopoverTrigger>
<PopoverContent { ...contentProps }> <PopoverContent { ...contentProps }>
......
...@@ -9,6 +9,7 @@ import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; ...@@ -9,6 +9,7 @@ import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import TOKEN_TYPE from 'lib/token/tokenTypes'; import TOKEN_TYPE from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
...@@ -34,7 +35,10 @@ const TxTokenTransfer = () => { ...@@ -34,7 +35,10 @@ const TxTokenTransfer = () => {
const tokenTransferQuery = useQueryWithPages({ const tokenTransferQuery = useQueryWithPages({
resourceName: 'tx_token_transfers', resourceName: 'tx_token_transfers',
pathParams: { hash: txsInfo.data?.hash.toString() }, pathParams: { hash: txsInfo.data?.hash.toString() },
options: { enabled: Boolean(txsInfo.data?.status && txsInfo.data?.hash) }, options: {
enabled: !txsInfo.isPlaceholderData && Boolean(txsInfo.data?.status && txsInfo.data?.hash),
placeholderData: getTokenTransfersStub(),
},
filters: { type: typeFilter }, filters: { type: typeFilter },
}); });
...@@ -43,7 +47,7 @@ const TxTokenTransfer = () => { ...@@ -43,7 +47,7 @@ const TxTokenTransfer = () => {
setTypeFilter(nextValue); setTypeFilter(nextValue);
}, [ tokenTransferQuery ]); }, [ tokenTransferQuery ]);
if (!txsInfo.isLoading && !txsInfo.isError && !txsInfo.data.status) { if (!txsInfo.isLoading && !txsInfo.isPlaceholderData && !txsInfo.isError && !txsInfo.data.status) {
return txsInfo.socketStatus ? <TxSocketAlert status={ txsInfo.socketStatus }/> : <TxPendingAlert/>; return txsInfo.socketStatus ? <TxSocketAlert status={ txsInfo.socketStatus }/> : <TxPendingAlert/>;
} }
...@@ -57,10 +61,10 @@ const TxTokenTransfer = () => { ...@@ -57,10 +61,10 @@ const TxTokenTransfer = () => {
const content = tokenTransferQuery.data?.items ? ( const content = tokenTransferQuery.data?.items ? (
<> <>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
<TokenTransferTable data={ tokenTransferQuery.data?.items } top={ isActionBarHidden ? 0 : 80 }/> <TokenTransferTable data={ tokenTransferQuery.data?.items } top={ isActionBarHidden ? 0 : 80 } isLoading={ tokenTransferQuery.isPlaceholderData }/>
</Hide> </Hide>
<Show below="lg" ssr={ false }> <Show below="lg" ssr={ false }>
<TokenTransferList data={ tokenTransferQuery.data?.items }/> <TokenTransferList data={ tokenTransferQuery.data?.items } isLoading={ tokenTransferQuery.isPlaceholderData }/>
</Show> </Show>
</> </>
) : null; ) : null;
...@@ -71,6 +75,7 @@ const TxTokenTransfer = () => { ...@@ -71,6 +75,7 @@ const TxTokenTransfer = () => {
defaultTypeFilters={ typeFilter } defaultTypeFilters={ typeFilter }
onTypeFilterChange={ handleTypeFilterChange } onTypeFilterChange={ handleTypeFilterChange }
appliedFiltersNum={ numActiveFilters } appliedFiltersNum={ numActiveFilters }
isLoading={ tokenTransferQuery.isPlaceholderData }
/> />
{ tokenTransferQuery.isPaginationVisible && <Pagination ml="auto" { ...tokenTransferQuery.pagination }/> } { tokenTransferQuery.isPaginationVisible && <Pagination ml="auto" { ...tokenTransferQuery.pagination }/> }
</ActionBar> </ActionBar>
...@@ -79,7 +84,7 @@ const TxTokenTransfer = () => { ...@@ -79,7 +84,7 @@ const TxTokenTransfer = () => {
return ( return (
<DataListDisplay <DataListDisplay
isError={ txsInfo.isError || tokenTransferQuery.isError } isError={ txsInfo.isError || tokenTransferQuery.isError }
isLoading={ txsInfo.isLoading || tokenTransferQuery.isLoading } isLoading={ false }
items={ tokenTransferQuery.data?.items } items={ tokenTransferQuery.data?.items }
skeletonProps={{ skeletonProps={{
isLongSkeleton: true, isLongSkeleton: true,
......
...@@ -15,7 +15,7 @@ import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; ...@@ -15,7 +15,7 @@ import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
type Props = InternalTransaction & { isLoading: boolean }; type Props = InternalTransaction & { isLoading?: boolean };
const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit: gasLimit, created_contract: createdContract, isLoading }: Props) => { const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit: gasLimit, created_contract: createdContract, isLoading }: Props) => {
const typeTitle = TX_INTERNALS_ITEMS.find(({ id }) => id === type)?.title; const typeTitle = TX_INTERNALS_ITEMS.find(({ id }) => id === type)?.title;
......
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