mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
skeletons for internal txs
This commit is contained in:
@@ -9,6 +9,8 @@ import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
|
||||
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
|
||||
import { apos } from 'lib/html-entities';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { INTERNAL_TX } from 'stubs/internalTx';
|
||||
import { generateListStub } from 'stubs/utils';
|
||||
import AddressIntTxsTable from 'ui/address/internals/AddressIntTxsTable';
|
||||
import ActionBar from 'ui/shared/ActionBar';
|
||||
import DataListDisplay from 'ui/shared/DataListDisplay';
|
||||
@@ -26,15 +28,28 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
|
||||
|
||||
const hash = getQueryParamString(router.query.hash);
|
||||
|
||||
const { data, isLoading, isError, pagination, onFilterChange, isPaginationVisible } = useQueryWithPages({
|
||||
const { data, isPlaceholderData, isError, pagination, onFilterChange, isPaginationVisible } = useQueryWithPages({
|
||||
resourceName: 'address_internal_txs',
|
||||
pathParams: { hash },
|
||||
filters: { filter: filterValue },
|
||||
scrollRef,
|
||||
options: {
|
||||
placeholderData: generateListStub<'address_internal_txs'>(
|
||||
INTERNAL_TX,
|
||||
50,
|
||||
{
|
||||
next_page_params: {
|
||||
block_number: 8987561,
|
||||
index: 2,
|
||||
items_count: 50,
|
||||
transaction_index: 67,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const handleFilterChange = React.useCallback((val: string | Array<string>) => {
|
||||
|
||||
const newVal = getFilterValue(val);
|
||||
setFilterValue(newVal);
|
||||
onFilterChange({ filter: newVal });
|
||||
@@ -43,22 +58,23 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
|
||||
const content = data?.items ? (
|
||||
<>
|
||||
<Show below="lg" ssr={ false }>
|
||||
<AddressIntTxsList data={ data.items } currentAddress={ hash }/>
|
||||
<AddressIntTxsList data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
|
||||
</Show>
|
||||
<Hide below="lg" ssr={ false }>
|
||||
<AddressIntTxsTable data={ data.items } currentAddress={ hash }/>
|
||||
<AddressIntTxsTable data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
|
||||
</Hide>
|
||||
</>
|
||||
) : null ;
|
||||
|
||||
const actionBar = (
|
||||
<ActionBar mt={ -6 } justifyContent="left" showShadow={ isLoading }>
|
||||
<ActionBar mt={ -6 } justifyContent="left">
|
||||
<AddressTxsFilter
|
||||
defaultFilter={ filterValue }
|
||||
onFilterChange={ handleFilterChange }
|
||||
isActive={ Boolean(filterValue) }
|
||||
isLoading={ pagination.isLoading }
|
||||
/>
|
||||
<AddressCsvExportLink address={ hash } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/>
|
||||
<AddressCsvExportLink address={ hash } isLoading={ pagination.isLoading } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/>
|
||||
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
|
||||
</ActionBar>
|
||||
);
|
||||
@@ -66,7 +82,7 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
|
||||
return (
|
||||
<DataListDisplay
|
||||
isError={ isError }
|
||||
isLoading={ isLoading }
|
||||
isLoading={ false }
|
||||
items={ data?.items }
|
||||
skeletonProps={{ isLongSkeleton: true, skeletonDesktopColumns: [ '15%', '15%', '10%', '20%', '20%', '20%' ] }}
|
||||
filterProps={{ emptyFilteredText: `Couldn${ apos }t find any transaction that matches your query.`, hasActiveFilters: Boolean(filterValue) }}
|
||||
|
||||
@@ -8,12 +8,20 @@ import AddressIntTxsListItem from 'ui/address/internals/AddressIntTxsListItem';
|
||||
type Props = {
|
||||
data: Array<InternalTransaction>;
|
||||
currentAddress: string;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const AddressIntTxsList = ({ data, currentAddress }: Props) => {
|
||||
const AddressIntTxsList = ({ data, currentAddress, isLoading }: Props) => {
|
||||
return (
|
||||
<Box>
|
||||
{ data.map((item) => <AddressIntTxsListItem key={ item.transaction_hash } { ...item } currentAddress={ currentAddress }/>) }
|
||||
{ data.map((item, index) => (
|
||||
<AddressIntTxsListItem
|
||||
key={ item.transaction_hash + '_' + index }
|
||||
{ ...item }
|
||||
currentAddress={ currentAddress }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
)) }
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Flex, Tag, Icon, Box, HStack, Text } from '@chakra-ui/react';
|
||||
import { Flex, Box, HStack, Skeleton } from '@chakra-ui/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { route } from 'nextjs-routes';
|
||||
import React from 'react';
|
||||
@@ -11,6 +11,8 @@ 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 Tag from 'ui/shared/chakra/Tag';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import InOutTag from 'ui/shared/InOutTag';
|
||||
import LinkInternal from 'ui/shared/LinkInternal';
|
||||
@@ -18,7 +20,7 @@ import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
|
||||
import TxStatus from 'ui/shared/TxStatus';
|
||||
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
|
||||
|
||||
type Props = InternalTransaction & { currentAddress: string };
|
||||
type Props = InternalTransaction & { currentAddress: string; isLoading?: boolean };
|
||||
|
||||
const TxInternalsListItem = ({
|
||||
type,
|
||||
@@ -32,6 +34,7 @@ const TxInternalsListItem = ({
|
||||
block,
|
||||
timestamp,
|
||||
currentAddress,
|
||||
isLoading,
|
||||
}: Props) => {
|
||||
const typeTitle = TX_INTERNALS_ITEMS.find(({ id }) => id === type)?.title;
|
||||
const toData = to ? to : createdContract;
|
||||
@@ -41,41 +44,45 @@ const TxInternalsListItem = ({
|
||||
|
||||
return (
|
||||
<ListItemMobile rowGap={ 3 }>
|
||||
<Flex>
|
||||
{ typeTitle && <Tag colorScheme="cyan" mr={ 2 }>{ typeTitle }</Tag> }
|
||||
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error }/>
|
||||
<Flex columnGap={ 2 }>
|
||||
{ typeTitle && <Tag colorScheme="cyan" isLoading={ isLoading }>{ typeTitle }</Tag> }
|
||||
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
|
||||
</Flex>
|
||||
<Flex justifyContent="space-between" width="100%">
|
||||
<AddressLink fontWeight="700" hash={ txnHash } truncation="constant" type="transaction"/>
|
||||
<Text variant="secondary" fontWeight="400" fontSize="sm">{ dayjs(timestamp).fromNow() }</Text>
|
||||
<AddressLink fontWeight="700" hash={ txnHash } truncation="constant" type="transaction" isLoading={ isLoading }/>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight="400" fontSize="sm">
|
||||
<span>{ dayjs(timestamp).fromNow() }</span>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
<HStack spacing={ 1 }>
|
||||
<Text fontSize="sm" fontWeight={ 500 }>Block</Text>
|
||||
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: block.toString() } }) }>{ block }</LinkInternal>
|
||||
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Block</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: block.toString() } }) }>{ block }</LinkInternal>
|
||||
</Skeleton>
|
||||
</HStack>
|
||||
<Box w="100%" display="flex" columnGap={ 3 }>
|
||||
<Address width="calc((100% - 48px) / 2)">
|
||||
<AddressIcon address={ from }/>
|
||||
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ isOut }/>
|
||||
{ isIn && <CopyToClipboard text={ from.hash }/> }
|
||||
<AddressIcon address={ from } isLoading={ isLoading }/>
|
||||
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ isOut } isLoading={ isLoading }/>
|
||||
{ isIn && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
|
||||
</Address>
|
||||
{ (isIn || isOut) ?
|
||||
<InOutTag isIn={ isIn } isOut={ isOut }/> :
|
||||
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/>
|
||||
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> :
|
||||
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
|
||||
}
|
||||
{ toData && (
|
||||
<Address width="calc((100% - 48px) / 2)">
|
||||
<AddressIcon address={ toData }/>
|
||||
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ toData.hash } isDisabled={ isIn }/>
|
||||
{ isOut && <CopyToClipboard text={ toData.hash }/> }
|
||||
<AddressIcon address={ toData } isLoading={ isLoading }/>
|
||||
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ toData.hash } isDisabled={ isIn } isLoading={ isLoading }/>
|
||||
{ isOut && <CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> }
|
||||
</Address>
|
||||
) }
|
||||
</Box>
|
||||
<HStack spacing={ 3 }>
|
||||
<Text fontSize="sm" fontWeight={ 500 }>Value { appConfig.network.currency.symbol }</Text>
|
||||
<Text fontSize="sm" variant="secondary">
|
||||
{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() }
|
||||
</Text>
|
||||
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Value { appConfig.network.currency.symbol }</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary" minW={ 6 }>
|
||||
<span>{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() }</span>
|
||||
</Skeleton>
|
||||
</HStack>
|
||||
</ListItemMobile>
|
||||
);
|
||||
|
||||
@@ -11,9 +11,10 @@ import AddressIntTxsTableItem from './AddressIntTxsTableItem';
|
||||
interface Props {
|
||||
data: Array<InternalTransaction>;
|
||||
currentAddress: string;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const AddressIntTxsTable = ({ data, currentAddress }: Props) => {
|
||||
const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
|
||||
return (
|
||||
<Table variant="simple" size="sm">
|
||||
<Thead top={ 80 }>
|
||||
@@ -30,8 +31,13 @@ const AddressIntTxsTable = ({ data, currentAddress }: Props) => {
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{ data.map((item) => (
|
||||
<AddressIntTxsTableItem key={ item.transaction_hash } { ...item } currentAddress={ currentAddress }/>
|
||||
{ data.map((item, index) => (
|
||||
<AddressIntTxsTableItem
|
||||
key={ item.transaction_hash + '_' + index }
|
||||
{ ...item }
|
||||
currentAddress={ currentAddress }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
)) }
|
||||
</Tbody>
|
||||
</Table>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Tr, Td, Tag, Icon, Box, Flex, Text } from '@chakra-ui/react';
|
||||
import { Tr, Td, Box, Flex, Skeleton } from '@chakra-ui/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { route } from 'nextjs-routes';
|
||||
import React from 'react';
|
||||
@@ -11,13 +11,15 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
|
||||
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 Tag from 'ui/shared/chakra/Tag';
|
||||
import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import InOutTag from 'ui/shared/InOutTag';
|
||||
import LinkInternal from 'ui/shared/LinkInternal';
|
||||
import TxStatus from 'ui/shared/TxStatus';
|
||||
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
|
||||
|
||||
type Props = InternalTransaction & { currentAddress: string }
|
||||
type Props = InternalTransaction & { currentAddress: string; isLoading?: boolean }
|
||||
|
||||
const AddressIntTxsTableItem = ({
|
||||
type,
|
||||
@@ -31,6 +33,7 @@ const AddressIntTxsTableItem = ({
|
||||
block,
|
||||
timestamp,
|
||||
currentAddress,
|
||||
isLoading,
|
||||
}: Props) => {
|
||||
const typeTitle = TX_INTERNALS_ITEMS.find(({ id }) => id === type)?.title;
|
||||
const toData = to ? to : createdContract;
|
||||
@@ -44,47 +47,66 @@ const AddressIntTxsTableItem = ({
|
||||
<Tr alignItems="top">
|
||||
<Td verticalAlign="middle">
|
||||
<Flex rowGap={ 3 } flexWrap="wrap">
|
||||
<AddressLink fontWeight="700" hash={ txnHash } type="transaction"/>
|
||||
{ timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
|
||||
<AddressLink fontWeight="700" hash={ txnHash } type="transaction" isLoading={ isLoading }/>
|
||||
{ timestamp && (
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight="400" fontSize="sm">
|
||||
<span>{ timeAgo }</span>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td verticalAlign="middle">
|
||||
<Flex rowGap={ 2 } flexWrap="wrap">
|
||||
{ typeTitle && (
|
||||
<Box w="126px" display="inline-block">
|
||||
<Tag colorScheme="cyan" mr={ 5 }>{ typeTitle }</Tag>
|
||||
<Tag colorScheme="cyan" mr={ 5 } isLoading={ isLoading }>{ typeTitle }</Tag>
|
||||
</Box>
|
||||
) }
|
||||
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error }/>
|
||||
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td verticalAlign="middle">
|
||||
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: block.toString() } }) }>{ block }</LinkInternal>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block">
|
||||
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: block.toString() } }) }>
|
||||
{ block }
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td verticalAlign="middle">
|
||||
<Address display="inline-flex" maxW="100%">
|
||||
<AddressIcon address={ from }/>
|
||||
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } alias={ from.name } flexGrow={ 1 } isDisabled={ isOut }/>
|
||||
{ isIn && <CopyToClipboard text={ from.hash }/> }
|
||||
<AddressIcon address={ from } isLoading={ isLoading }/>
|
||||
<AddressLink
|
||||
type="address"
|
||||
ml={ 2 }
|
||||
fontWeight="500"
|
||||
hash={ from.hash }
|
||||
alias={ from.name }
|
||||
flexGrow={ 1 }
|
||||
isDisabled={ isOut }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
{ isIn && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
|
||||
</Address>
|
||||
</Td>
|
||||
<Td px={ 0 } verticalAlign="middle">
|
||||
{ (isIn || isOut) ?
|
||||
<InOutTag isIn={ isIn } isOut={ isOut }/> :
|
||||
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500"/>
|
||||
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> :
|
||||
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
|
||||
}
|
||||
</Td>
|
||||
<Td verticalAlign="middle">
|
||||
{ toData && (
|
||||
<Address display="inline-flex" maxW="100%">
|
||||
<AddressIcon address={ toData }/>
|
||||
<AddressLink type="address" hash={ toData.hash } alias={ toData.name } fontWeight="500" ml={ 2 } isDisabled={ isIn }/>
|
||||
{ isOut && <CopyToClipboard text={ toData.hash }/> }
|
||||
<AddressIcon address={ toData } isLoading={ isLoading }/>
|
||||
<AddressLink type="address" hash={ toData.hash } alias={ toData.name } fontWeight="500" ml={ 2 } isDisabled={ isIn } isLoading={ isLoading }/>
|
||||
{ isOut && <CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> }
|
||||
</Address>
|
||||
) }
|
||||
</Td>
|
||||
<Td isNumeric verticalAlign="middle">
|
||||
{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() }
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block" minW={ 6 }>
|
||||
{ BigNumber(value).div(BigNumber(10 ** appConfig.network.currency.decimals)).toFormat() }
|
||||
</Skeleton>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user