Commit f11218a1 authored by tom's avatar tom

token transfer table

parent 8e46f14d
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -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