Commit f11218a1 authored by tom's avatar tom

token transfer table

parent 8e46f14d
This diff is collapsed.
...@@ -7,7 +7,7 @@ import React from 'react'; ...@@ -7,7 +7,7 @@ import React from 'react';
import infoIcon from 'icons/info.svg'; import infoIcon from 'icons/info.svg';
const TxAdditionalInfoButton = ({ isOpen, onClick }: {isOpen?: boolean; onClick?: () => void}, ref: React.ForwardedRef<HTMLDivElement>) => { const AdditionalInfoButton = ({ isOpen, onClick }: {isOpen?: boolean; onClick?: () => void}, ref: React.ForwardedRef<HTMLDivElement>) => {
const infoBgColor = useColorModeValue('blue.50', 'gray.600'); const infoBgColor = useColorModeValue('blue.50', 'gray.600');
const infoColor = useColorModeValue('blue.600', 'blue.300'); const infoColor = useColorModeValue('blue.600', 'blue.300');
...@@ -24,4 +24,4 @@ const TxAdditionalInfoButton = ({ isOpen, onClick }: {isOpen?: boolean; onClick? ...@@ -24,4 +24,4 @@ const TxAdditionalInfoButton = ({ isOpen, onClick }: {isOpen?: boolean; onClick?
); );
}; };
export default React.forwardRef(TxAdditionalInfoButton); export default React.forwardRef(AdditionalInfoButton);
import { Tag, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
baseAddress: string;
addressFrom: string;
className?: string;
}
const InOutTag = ({ baseAddress, addressFrom, className }: Props) => {
const isOut = addressFrom === baseAddress;
const colorScheme = isOut ? 'orange' : 'green';
return <Tag className={ className } colorScheme={ colorScheme }>{ isOut ? 'OUT' : 'IN' }</Tag>;
};
export default React.memo(chakra(InOutTag));
import { Center, Link, Text, chakra } from '@chakra-ui/react'; import { Flex, Text, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import link from 'lib/link/link'; import AddressLink from 'ui/shared/address/AddressLink';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
interface Props { interface Props {
...@@ -12,16 +12,12 @@ interface Props { ...@@ -12,16 +12,12 @@ interface Props {
} }
const TokenSnippet = ({ symbol, hash, name, className }: Props) => { const TokenSnippet = ({ symbol, hash, name, className }: Props) => {
const url = link('token_index', { hash });
return ( return (
<Center className={ className } columnGap={ 1 }> <Flex className={ className } alignItems="center" columnGap={ 1 } w="100%">
<TokenLogo boxSize={ 5 } hash={ hash } name={ name }/> <TokenLogo boxSize={ 5 } borderRadius={ 2 } hash={ hash } name={ name }/>
<Link href={ url } target="_blank"> <AddressLink hash={ hash } alias={ name } type="token"/>
{ name }
</Link>
{ symbol && <Text variant="secondary">({ symbol })</Text> } { symbol && <Text variant="secondary">({ symbol })</Text> }
</Center> </Flex>
); );
}; };
......
import { Box } from '@chakra-ui/react'; import { Alert, Show } from '@chakra-ui/react';
import type { QueryKey } from '@tanstack/react-query'; import type { QueryKey } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import { TokenTransfer } from 'types/api/tokenTransfer';
import type { TokenTransferResponse } from 'types/api/tokenTransfer'; import type { TokenTransferResponse } from 'types/api/tokenTransfer';
import useFetch from 'lib/hooks/useFetch';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import SkeletonTable from 'ui/shared/SkeletonTable';
import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable';
interface Props { interface Props {
isLoading?: boolean; isLoading?: boolean;
isDisabled?: boolean; isDisabled?: boolean;
path: string; path: string;
queryKey: QueryKey; queryKey: QueryKey;
baseAddress?: string;
} }
const TokenTransfer = ({ isLoading: isLoadingProp, isDisabled, queryKey, path }: Props) => { const TokenTransfer = ({ isLoading: isLoadingProp, isDisabled, queryKey, path, baseAddress }: Props) => {
const { isError, isLoading } = useQuery<unknown, unknown, TokenTransferResponse>( const fetch = useFetch();
const { isError, isLoading, data } = useQuery<unknown, unknown, TokenTransferResponse>(
queryKey, queryKey,
async() => await fetch(path), async() => await fetch(path),
{ {
...@@ -24,14 +30,37 @@ const TokenTransfer = ({ isLoading: isLoadingProp, isDisabled, queryKey, path }: ...@@ -24,14 +30,37 @@ const TokenTransfer = ({ isLoading: isLoadingProp, isDisabled, queryKey, path }:
); );
if (isLoading || isLoadingProp) { if (isLoading || isLoadingProp) {
return <span>Loading...</span>; return (
<>
<Show below="lg">loading...</Show>
<Show above="lg">
<SkeletonTable columns={ [ '44px', '185px', '160px', '25%', '25%', '25%', '25%' ] }/>
</Show>
</>
);
} }
if (isError) { if (isError) {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
return <Box>TokenTransfer</Box>; if (!data.items?.length) {
return <Alert>There are no token transfers</Alert>;
}
const items = data.items.reduce((result, item) => {
if (Array.isArray(item.total)) {
item.total.forEach((total) => {
result.push({ ...item, total });
});
} else {
result.push(item);
}
return result;
}, [] as Array<TokenTransfer>);
return <TokenTransferTable data={ items } baseAddress={ baseAddress }/>;
}; };
export default React.memo(TokenTransfer); export default React.memo(TokenTransfer);
import { Table, Tbody, Tr, Th } from '@chakra-ui/react';
import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import { default as Thead } from 'ui/shared/TheadSticky';
import TokenTransferTableItem from 'ui/shared/TokenTransfer/TokenTransferTableItem';
interface Props {
data: Array<TokenTransfer>;
baseAddress?: string;
}
const TxInternalsTable = ({ data, baseAddress }: Props) => {
return (
<Table variant="simple" size="sm" mt={ 6 }>
<Thead top={ 0 }>
<Tr>
<Th width="44px"></Th>
<Th width="185px">Token</Th>
<Th width="160px">Token ID</Th>
<Th width="25%">Txn hash</Th>
<Th width="25%">From</Th>
{ baseAddress && <Th width="50px" px={ 0 }/> }
<Th width="25%">To</Th>
<Th width="25%">Value</Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item, index) => (
<TokenTransferTableItem key={ index } { ...item } baseAddress={ baseAddress }/>
)) }
</Tbody>
</Table>
);
};
export default React.memo(TxInternalsTable);
import { Tr, Td, Tag, Icon, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import nftPlaceholder from 'icons/nft_placeholder.svg';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import InOutTag from 'ui/shared/InOutTag';
import TokenSnippet from 'ui/shared/TokenSnippet';
type Props = TokenTransfer & {
baseAddress?: string;
}
const TxInternalTableItem = ({ token, total, tx_hash: txHash, from, to, baseAddress }: Props) => {
const value = (() => {
if (!('value' in total)) {
return '-';
}
return BigNumber(total.value).div(BigNumber(10 ** Number(total.decimals))).dp(8).toFormat();
})();
return (
<Tr alignItems="top">
<Td>
<AdditionalInfoButton/>
</Td>
<Td>
<Flex flexDir="column" rowGap={ 3 } alignItems="flex-start">
<TokenSnippet hash={ token.address } name={ token.name || 'Unnamed token' }/>
<Tag>{ token.type }</Tag>
</Flex>
</Td>
<Td>
{ 'token_id' in total ? (
<Flex align="center">
<Icon as={ nftPlaceholder } boxSize="30px" mr={ 2 }/>
<AddressLink hash={ token.address } id={ total.token_id } type="token_instance_item"/>
</Flex>
) : '-' }
</Td>
<Td>
<Address display="inline-flex" maxW="100%" fontWeight={ 600 }>
<AddressLink type="transaction" hash={ txHash }/>
</Address>
</Td>
<Td>
<Address display="inline-flex" maxW="100%">
<AddressIcon hash={ from.hash }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ from.hash } alias={ from.name } flexGrow={ 1 }/>
</Address>
</Td>
{ baseAddress && (
<Td px={ 0 }>
<InOutTag baseAddress={ baseAddress } addressFrom={ from.hash } w="50px" textAlign="center"/>
</Td>
) }
<Td>
<Address display="inline-flex" maxW="100%">
<AddressIcon hash={ to.hash }/>
<AddressLink ml={ 2 } fontWeight="500" hash={ to.hash } alias={ to.name } flexGrow={ 1 }/>
</Address>
</Td>
<Td isNumeric verticalAlign="top">
{ value }
</Td>
</Tr>
);
};
export default React.memo(TxInternalTableItem);
...@@ -7,7 +7,7 @@ import HashStringShorten from 'ui/shared/HashStringShorten'; ...@@ -7,7 +7,7 @@ import HashStringShorten from 'ui/shared/HashStringShorten';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props { interface Props {
type?: 'address' | 'transaction' | 'token' | 'block'; type?: 'address' | 'transaction' | 'token' | 'block' | 'token_instance_item';
alias?: string | null; alias?: string | null;
className?: string; className?: string;
hash: string; hash: string;
...@@ -23,6 +23,8 @@ const AddressLink = ({ alias, type, className, truncation = 'dynamic', hash, id, ...@@ -23,6 +23,8 @@ const AddressLink = ({ alias, type, className, truncation = 'dynamic', hash, id,
url = link('tx', { id: id || hash }); url = link('tx', { id: id || hash });
} else if (type === 'token') { } else if (type === 'token') {
url = link('token_index', { hash: id || hash }); url = link('token_index', { hash: id || hash });
} else if (type === 'token_instance_item') {
url = link('token_instance_item', { hash, id });
} else if (type === 'block') { } else if (type === 'block') {
url = link('block', { id: id || hash }); url = link('block', { id: id || hash });
} else { } else {
...@@ -39,11 +41,11 @@ const AddressLink = ({ alias, type, className, truncation = 'dynamic', hash, id, ...@@ -39,11 +41,11 @@ const AddressLink = ({ alias, type, className, truncation = 'dynamic', hash, id,
} }
switch (truncation) { switch (truncation) {
case 'constant': case 'constant':
return <HashStringShorten hash={ hash }/>; return <HashStringShorten hash={ id || hash }/>;
case 'dynamic': case 'dynamic':
return <HashStringShortenDynamic hash={ hash } fontWeight={ fontWeight }/>; return <HashStringShortenDynamic hash={ id || hash } fontWeight={ fontWeight }/>;
case 'none': case 'none':
return <span>{ hash }</span>; return <span>{ id || hash }</span>;
} }
})(); })();
......
...@@ -23,7 +23,15 @@ const TxTokenTransfer = () => { ...@@ -23,7 +23,15 @@ const TxTokenTransfer = () => {
const queryKey = [ QueryKeys.txTokenTransfers, data?.hash ]; const queryKey = [ QueryKeys.txTokenTransfers, data?.hash ];
const path = `/node-api/transactions/${ data?.hash }/token-transfers`; const path = `/node-api/transactions/${ data?.hash }/token-transfers`;
return <TokenTransfer isLoading={ isLoading } isDisabled={ !data?.status || !data?.hash } path={ path } queryKey={ queryKey }/>; return (
<TokenTransfer
isLoading={ isLoading }
isDisabled={ !data?.status || !data?.hash }
path={ path }
queryKey={ queryKey }
// todo_tom delete me
baseAddress="0xd789a607CEac2f0E14867de4EB15b15C9FFB5859"/>
);
}; };
export default TxTokenTransfer; export default TxTokenTransfer;
...@@ -20,12 +20,12 @@ import transactionIcon from 'icons/transactions.svg'; ...@@ -20,12 +20,12 @@ import transactionIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import link from 'lib/link/link'; import link from 'lib/link/link';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
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 TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton';
import TxType from 'ui/txs/TxType'; import TxType from 'ui/txs/TxType';
const TxsListItem = ({ tx }: {tx: Transaction}) => { const TxsListItem = ({ tx }: {tx: Transaction}) => {
...@@ -42,7 +42,7 @@ const TxsListItem = ({ tx }: {tx: Transaction}) => { ...@@ -42,7 +42,7 @@ const TxsListItem = ({ tx }: {tx: Transaction}) => {
{ tx.tx_types.map(item => <TxType key={ item } type={ item }/>) } { tx.tx_types.map(item => <TxType key={ item } type={ item }/>) }
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/> <TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
</HStack> </HStack>
<TxAdditionalInfoButton onClick={ onOpen }/> <AdditionalInfoButton onClick={ onOpen }/>
</Flex> </Flex>
<Flex justifyContent="space-between" lineHeight="24px" mt={ 3 }> <Flex justifyContent="space-between" lineHeight="24px" mt={ 3 }>
<Flex> <Flex>
......
...@@ -22,6 +22,7 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -22,6 +22,7 @@ import type { Transaction } from 'types/api/transaction';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import link from 'lib/link/link'; import link from 'lib/link/link';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
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';
...@@ -29,7 +30,6 @@ import CurrencyValue from 'ui/shared/CurrencyValue'; ...@@ -29,7 +30,6 @@ import CurrencyValue from 'ui/shared/CurrencyValue';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton';
import TxType from './TxType'; import TxType from './TxType';
...@@ -60,7 +60,7 @@ const TxsTableItem = ({ tx }: {tx: Transaction}) => { ...@@ -60,7 +60,7 @@ const TxsTableItem = ({ tx }: {tx: Transaction}) => {
{ ({ isOpen }) => ( { ({ isOpen }) => (
<> <>
<PopoverTrigger> <PopoverTrigger>
<TxAdditionalInfoButton isOpen={ isOpen }/> <AdditionalInfoButton isOpen={ isOpen }/>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent border="1px solid" borderColor={ infoBorderColor }> <PopoverContent border="1px solid" borderColor={ infoBorderColor }>
<PopoverBody> <PopoverBody>
......
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