Commit 1c78a12c authored by tom's avatar tom

highlight similar addresses in the table views

parent e1cf276c
import React from 'react';
interface AddressHighlightProviderProps {
children: React.ReactNode;
}
interface TAddressHighlightContext {
highlightedAddress: string | null;
onMouseEnter: (event: React.MouseEvent) => void;
onMouseLeave: (event: React.MouseEvent) => void;
}
export const AddressHighlightContext = React.createContext<TAddressHighlightContext | null>(null);
export function AddressHighlightProvider({ children }: AddressHighlightProviderProps) {
const [ highlightedAddress, setHighlightedAddress ] = React.useState<string | null>(null);
const onMouseEnter = React.useCallback((event: React.MouseEvent) => {
// TODO @tom2drum add throttling
const hash = event.currentTarget.getAttribute('data-hash');
hash && setHighlightedAddress(hash);
}, []);
const onMouseLeave = React.useCallback(() => {
setHighlightedAddress(null);
}, []);
const value = React.useMemo(() => {
return {
highlightedAddress,
onMouseEnter,
onMouseLeave,
};
}, [ highlightedAddress, onMouseEnter, onMouseLeave ]);
return (
<AddressHighlightContext.Provider value={ value }>
{ children }
</AddressHighlightContext.Provider>
);
}
export function useAddressHighlightContext() {
const context = React.useContext(AddressHighlightContext);
if (context === undefined) {
return null;
}
return context;
}
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import AddressIntTxsTableItem from './AddressIntTxsTableItem'; import AddressIntTxsTableItem from './AddressIntTxsTableItem';
...@@ -16,31 +17,34 @@ interface Props { ...@@ -16,31 +17,34 @@ interface Props {
const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => { const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
return ( return (
<Table variant="simple" size="sm"> <AddressHighlightProvider>
<Thead top={ 80 }> <Table variant="simple" size="sm">
<Tr> <Thead top={ 80 }>
<Th width="15%">Parent txn hash</Th> <Tr>
<Th width="15%">Type</Th> <Th width="15%">Parent txn hash</Th>
<Th width="10%">Block</Th> <Th width="15%">Type</Th>
<Th width="20%">From</Th> <Th width="10%">Block</Th>
<Th width="48px" px={ 0 }/> <Th width="20%">From</Th>
<Th width="20%">To</Th> <Th width="48px" px={ 0 }/>
<Th width="20%" isNumeric> <Th width="20%">To</Th>
<Th width="20%" isNumeric>
Value { config.chain.currency.symbol } Value { config.chain.currency.symbol }
</Th> </Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{ data.map((item, index) => ( { data.map((item, index) => (
<AddressIntTxsTableItem <AddressIntTxsTableItem
key={ item.transaction_hash + '_' + index } key={ item.transaction_hash + '_' + index }
{ ...item } { ...item }
currentAddress={ currentAddress } currentAddress={ currentAddress }
isLoading={ isLoading } isLoading={ isLoading }
/> />
)) } )) }
</Tbody> </Tbody>
</Table> </Table>
</AddressHighlightProvider>
); );
}; };
......
...@@ -83,6 +83,7 @@ const AddressIntTxsTableItem = ({ ...@@ -83,6 +83,7 @@ const AddressIntTxsTableItem = ({
isLoading={ isLoading } isLoading={ isLoading }
noLink={ isOut } noLink={ isOut }
noCopy={ isOut } noCopy={ isOut }
w="min-content"
/> />
</Td> </Td>
<Td px={ 0 } verticalAlign="middle"> <Td px={ 0 } verticalAlign="middle">
...@@ -98,6 +99,7 @@ const AddressIntTxsTableItem = ({ ...@@ -98,6 +99,7 @@ const AddressIntTxsTableItem = ({
isLoading={ isLoading } isLoading={ isLoading }
noLink={ isIn } noLink={ isIn }
noCopy={ isIn } noCopy={ isIn }
w="min-content"
/> />
) } ) }
</Td> </Td>
......
...@@ -6,6 +6,7 @@ import React from 'react'; ...@@ -6,6 +6,7 @@ import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import config from 'configs/app'; import config from 'configs/app';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlocksTableItem from 'ui/blocks/BlocksTableItem'; import BlocksTableItem from 'ui/blocks/BlocksTableItem';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
...@@ -37,43 +38,45 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum ...@@ -37,43 +38,45 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum
(!isRollup && !config.UI.views.block.hiddenFields?.burnt_fees ? FEES_COL_WEIGHT : 0); (!isRollup && !config.UI.views.block.hiddenFields?.burnt_fees ? FEES_COL_WEIGHT : 0);
return ( return (
<Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }> <AddressHighlightProvider>
<Thead top={ top }> <Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }>
<Tr> <Thead top={ top }>
<Th width="125px">Block</Th> <Tr>
<Th width="120px">Size, bytes</Th> <Th width="125px">Block</Th>
{ !config.UI.views.block.hiddenFields?.miner && <Th width="120px">Size, bytes</Th>
{ !config.UI.views.block.hiddenFields?.miner &&
<Th width={ `${ VALIDATOR_COL_WEIGHT / widthBase * 100 }%` } minW="160px">{ capitalize(getNetworkValidatorTitle()) }</Th> } <Th width={ `${ VALIDATOR_COL_WEIGHT / widthBase * 100 }%` } minW="160px">{ capitalize(getNetworkValidatorTitle()) }</Th> }
<Th width="64px" isNumeric>Txn</Th> <Th width="64px" isNumeric>Txn</Th>
<Th width={ `${ GAS_COL_WEIGHT / widthBase * 100 }%` }>Gas used</Th> <Th width={ `${ GAS_COL_WEIGHT / widthBase * 100 }%` }>Gas used</Th>
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward && { !isRollup && !config.UI.views.block.hiddenFields?.total_reward &&
<Th width={ `${ REWARD_COL_WEIGHT / widthBase * 100 }%` }>Reward { config.chain.currency.symbol }</Th> } <Th width={ `${ REWARD_COL_WEIGHT / widthBase * 100 }%` }>Reward { config.chain.currency.symbol }</Th> }
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && { !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees &&
<Th width={ `${ FEES_COL_WEIGHT / widthBase * 100 }%` }>Burnt fees { config.chain.currency.symbol }</Th> } <Th width={ `${ FEES_COL_WEIGHT / widthBase * 100 }%` }>Burnt fees { config.chain.currency.symbol }</Th> }
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{ showSocketInfo && ( { showSocketInfo && (
<SocketNewItemsNotice.Desktop <SocketNewItemsNotice.Desktop
url={ window.location.href } url={ window.location.href }
alert={ socketInfoAlert } alert={ socketInfoAlert }
num={ socketInfoNum } num={ socketInfoNum }
type="block" type="block"
isLoading={ isLoading }
/>
) }
<AnimatePresence initial={ false }>
{ data.map((item, index) => (
<BlocksTableItem
key={ item.height + (isLoading ? `${ index }_${ page }` : '') }
data={ item }
enableTimeIncrement={ page === 1 && !isLoading }
isLoading={ isLoading } isLoading={ isLoading }
/> />
)) } ) }
</AnimatePresence> <AnimatePresence initial={ false }>
</Tbody> { data.map((item, index) => (
</Table> <BlocksTableItem
key={ item.height + (isLoading ? `${ index }_${ page }` : '') }
data={ item }
enableTimeIncrement={ page === 1 && !isLoading }
isLoading={ isLoading }
/>
)) }
</AnimatePresence>
</Tbody>
</Table>
</AddressHighlightProvider>
); );
}; };
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket'; import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
import { TX } from 'stubs/tx'; import { TX } from 'stubs/tx';
...@@ -42,15 +43,17 @@ const LatestTransactions = () => { ...@@ -42,15 +43,17 @@ const LatestTransactions = () => {
/> />
))) } ))) }
</Box> </Box>
<Box mb={ 4 } display={{ base: 'none', lg: 'block' }}> <AddressHighlightProvider>
{ data.slice(0, txsCount).map(((tx, index) => ( <Box mb={ 4 } display={{ base: 'none', lg: 'block' }}>
<LatestTxsItem { data.slice(0, txsCount).map(((tx, index) => (
key={ tx.hash + (isPlaceholderData ? index : '') } <LatestTxsItem
tx={ tx } key={ tx.hash + (isPlaceholderData ? index : '') }
isLoading={ isPlaceholderData } tx={ tx }
/> isLoading={ isPlaceholderData }
))) } />
</Box> ))) }
</Box>
</AddressHighlightProvider>
<Flex justifyContent="center"> <Flex justifyContent="center">
<LinkInternal fontSize="sm" href={ txsUrl }>View all transactions</LinkInternal> <LinkInternal fontSize="sm" href={ txsUrl }>View all transactions</LinkInternal>
</Flex> </Flex>
......
...@@ -35,7 +35,10 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -35,7 +35,10 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
return ( return (
<Grid <Grid
gridTemplateColumns={ columnNum === 2 ? '3fr 2fr' : '3fr 2fr 150px' } gridTemplateColumns={{
lg: columnNum === 2 ? '3fr minmax(auto, 160px)' : '3fr minmax(auto, 160px) 150px',
xl: columnNum === 2 ? '3fr minmax(auto, 250px)' : '3fr minmax(auto, 250px) 150px',
}}
gridGap={ 8 } gridGap={ 8 }
width="100%" width="100%"
minW="700px" minW="700px"
...@@ -77,15 +80,16 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -77,15 +80,16 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
</Flex> </Flex>
</Box> </Box>
</Flex> </Flex>
<Grid alignItems="center" alignSelf="flex-start" templateColumns="24px auto"> <Flex alignItems="center" alignSelf="flex-start">
<Icon <Icon
as={ rightArrowIcon } as={ rightArrowIcon }
boxSize={ 6 } boxSize={ 6 }
color="gray.500" color="gray.500"
transform="rotate(90deg)" transform="rotate(90deg)"
isLoading={ isLoading } isLoading={ isLoading }
flexShrink={ 0 }
/> />
<Box overflow="hidden" ml={ 1 }> <Box ml={ 1 } maxW="calc(100% - 24px)">
<AddressEntity <AddressEntity
isLoading={ isLoading } isLoading={ isLoading }
address={ tx.from } address={ tx.from }
...@@ -104,7 +108,7 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -104,7 +108,7 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
/> />
) } ) }
</Box> </Box>
</Grid> </Flex>
<Box> <Box>
{ !config.UI.views.tx.hiddenFields?.value && ( { !config.UI.views.tx.hiddenFields?.value && (
<Skeleton isLoaded={ !isLoading } mb={ 2 }> <Skeleton isLoaded={ !isLoading } mb={ 2 }>
......
...@@ -3,6 +3,7 @@ import React from 'react'; ...@@ -3,6 +3,7 @@ import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import * as 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';
...@@ -32,41 +33,43 @@ const TokenTransferTable = ({ ...@@ -32,41 +33,43 @@ const TokenTransferTable = ({
}: Props) => { }: Props) => {
return ( return (
<Table variant="simple" size="sm" minW="950px"> <AddressHighlightProvider>
<Thead top={ top }> <Table variant="simple" size="sm" minW="950px">
<Tr> <Thead top={ top }>
{ showTxInfo && <Th width="44px"></Th> } <Tr>
<Th width="185px">Token</Th> { showTxInfo && <Th width="44px"></Th> }
<Th width="160px">Token ID</Th> <Th width="185px">Token</Th>
{ showTxInfo && <Th width="25%">Txn hash</Th> } <Th width="160px">Token ID</Th>
<Th width="25%">From</Th> { showTxInfo && <Th width="25%">Txn hash</Th> }
{ baseAddress && <Th width="50px" px={ 0 }/> } <Th width="25%">From</Th>
<Th width="25%">To</Th> { baseAddress && <Th width="50px" px={ 0 }/> }
<Th width="25%" isNumeric>Value</Th> <Th width="25%">To</Th>
</Tr> <Th width="25%" isNumeric>Value</Th>
</Thead> </Tr>
<Tbody> </Thead>
{ showSocketInfo && ( <Tbody>
<SocketNewItemsNotice.Desktop { showSocketInfo && (
url={ window.location.href } <SocketNewItemsNotice.Desktop
alert={ socketInfoAlert } url={ window.location.href }
num={ socketInfoNum } alert={ socketInfoAlert }
type="token_transfer" num={ socketInfoNum }
isLoading={ isLoading } type="token_transfer"
/> isLoading={ isLoading }
) } />
{ data.map((item, index) => ( ) }
<TokenTransferTableItem { data.map((item, index) => (
key={ item.tx_hash + item.block_hash + item.log_index + (isLoading ? index : '') } <TokenTransferTableItem
{ ...item } key={ item.tx_hash + item.block_hash + item.log_index + (isLoading ? index : '') }
baseAddress={ baseAddress } { ...item }
showTxInfo={ showTxInfo } baseAddress={ baseAddress }
enableTimeIncrement={ enableTimeIncrement } showTxInfo={ showTxInfo }
isLoading={ isLoading } enableTimeIncrement={ enableTimeIncrement }
/> isLoading={ isLoading }
)) } />
</Tbody> )) }
</Table> </Tbody>
</Table>
</AddressHighlightProvider>
); );
}; };
......
import type { As } from '@chakra-ui/react'; import type { As } from '@chakra-ui/react';
import { Box, Flex, Skeleton, Tooltip, chakra, VStack } from '@chakra-ui/react'; import { Box, Flex, Skeleton, Tooltip, chakra, VStack, useColorModeValue } from '@chakra-ui/react';
import _omit from 'lodash/omit'; import _omit from 'lodash/omit';
import React from 'react'; import React from 'react';
...@@ -10,6 +10,7 @@ import { route } from 'nextjs-routes'; ...@@ -10,6 +10,7 @@ import { route } from 'nextjs-routes';
import iconSafe from 'icons/brands/safe.svg'; import iconSafe from 'icons/brands/safe.svg';
import iconContractVerified from 'icons/contract_verified.svg'; import iconContractVerified from 'icons/contract_verified.svg';
import iconContract from 'icons/contract.svg'; import iconContract from 'icons/contract.svg';
import { useAddressHighlightContext } from 'lib/contexts/addressHighlight';
import * as EntityBase from 'ui/shared/entities/base/components'; import * as EntityBase from 'ui/shared/entities/base/components';
import { getIconProps } from '../base/utils'; import { getIconProps } from '../base/utils';
...@@ -148,8 +149,32 @@ const AddressEntry = (props: EntityProps) => { ...@@ -148,8 +149,32 @@ const AddressEntry = (props: EntityProps) => {
const linkProps = _omit(props, [ 'className' ]); const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]); const partsProps = _omit(props, [ 'className', 'onClick' ]);
const context = useAddressHighlightContext();
const highlightedBgColor = useColorModeValue('blue.50', 'blue.900');
const highlightedBorderColor = useColorModeValue('blue.200', 'blue.600');
return ( return (
<Container className={ props.className }> <Container
className={ props.className }
data-hash={ props.address.hash }
onMouseEnter={ context?.onMouseEnter }
onMouseLeave={ context?.onMouseLeave }
position="relative"
_before={ !props.isLoading && context?.highlightedAddress === props.address.hash ? {
content: `" "`,
position: 'absolute',
top: '-7px',
left: '-5px',
width: `calc(100% + ${ props.noCopy ? 10 : 5 }px)`,
height: 'calc(100% + 12px)',
borderRadius: 'base',
borderColor: highlightedBorderColor,
borderWidth: '1px',
borderStyle: 'dashed',
bgColor: highlightedBgColor,
zIndex: -1,
} : undefined }
>
<Icon { ...partsProps }/> <Icon { ...partsProps }/>
<Link { ...linkProps }> <Link { ...linkProps }>
<Content { ...partsProps }/> <Content { ...partsProps }/>
......
...@@ -32,14 +32,17 @@ export interface EntityBaseProps { ...@@ -32,14 +32,17 @@ export interface EntityBaseProps {
export interface ContainerBaseProps extends Pick<EntityBaseProps, 'className'> { export interface ContainerBaseProps extends Pick<EntityBaseProps, 'className'> {
children: React.ReactNode; children: React.ReactNode;
onMouseEnter?: (event: React.MouseEvent) => void;
onMouseLeave?: (event: React.MouseEvent) => void;
} }
const Container = chakra(({ className, children }: ContainerBaseProps) => { const Container = chakra(({ className, children, ...props }: ContainerBaseProps) => {
return ( return (
<Flex <Flex
className={ className } className={ className }
alignItems="center" alignItems="center"
minWidth={ 0 } // for content truncation - https://css-tricks.com/flexbox-truncated-text/ minWidth={ 0 } // for content truncation - https://css-tricks.com/flexbox-truncated-text/
{ ...props }
> >
{ children } { children }
</Flex> </Flex>
......
...@@ -5,6 +5,7 @@ import React from 'react'; ...@@ -5,6 +5,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
...@@ -58,21 +59,23 @@ const TokenInventory = ({ inventoryQuery, tokenQuery, ownerFilter }: Props) => { ...@@ -58,21 +59,23 @@ const TokenInventory = ({ inventoryQuery, tokenQuery, ownerFilter }: Props) => {
const token = tokenQuery.data; const token = tokenQuery.data;
const content = items && token ? ( const content = items && token ? (
<Grid <AddressHighlightProvider>
w="100%" <Grid
columnGap={{ base: 3, lg: 6 }} w="100%"
rowGap={{ base: 3, lg: 6 }} columnGap={{ base: 3, lg: 6 }}
gridTemplateColumns={{ base: 'repeat(2, calc((100% - 12px)/2))', lg: 'repeat(auto-fill, minmax(210px, 1fr))' }} rowGap={{ base: 3, lg: 6 }}
> gridTemplateColumns={{ base: 'repeat(2, calc((100% - 12px)/2))', lg: 'repeat(auto-fill, minmax(210px, 1fr))' }}
{ items.map((item, index) => ( >
<TokenInventoryItem { items.map((item, index) => (
key={ token.address + '_' + item.id + (inventoryQuery.isPlaceholderData ? '_' + index : '') } <TokenInventoryItem
item={ item } key={ token.address + '_' + item.id + (inventoryQuery.isPlaceholderData ? '_' + index : '') }
isLoading={ inventoryQuery.isPlaceholderData || tokenQuery.isPlaceholderData } item={ item }
token={ token } isLoading={ inventoryQuery.isPlaceholderData || tokenQuery.isPlaceholderData }
/> token={ token }
)) } />
</Grid> )) }
</Grid>
</AddressHighlightProvider>
) : null; ) : null;
return ( return (
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import * as 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 TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
...@@ -24,42 +25,44 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket ...@@ -24,42 +25,44 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket
const tokenType = data[0].token.type; const tokenType = data[0].token.type;
return ( return (
<Table variant="simple" size="sm" minW="950px"> <AddressHighlightProvider>
<Thead top={ top }> <Table variant="simple" size="sm" minW="950px">
<Tr> <Thead top={ top }>
<Th width={ tokenType === 'ERC-1155' ? '60%' : '80%' }>Txn hash</Th> <Tr>
<Th width="164px">Method</Th> <Th width={ tokenType === 'ERC-1155' ? '60%' : '80%' }>Txn hash</Th>
<Th width="160px">From</Th> <Th width="164px">Method</Th>
<Th width="36px" px={ 0 }/> <Th width="160px">From</Th>
<Th width="218px" >To</Th> <Th width="36px" px={ 0 }/>
{ (tokenType === 'ERC-721' || tokenType === 'ERC-1155') && <Th width="20%" isNumeric={ tokenType === 'ERC-721' }>Token ID</Th> } <Th width="218px" >To</Th>
{ (tokenType === 'ERC-20' || tokenType === 'ERC-1155') && ( { (tokenType === 'ERC-721' || tokenType === 'ERC-1155') && <Th width="20%" isNumeric={ tokenType === 'ERC-721' }>Token ID</Th> }
<Th width="20%" isNumeric> { (tokenType === 'ERC-20' || tokenType === 'ERC-1155') && (
<TruncatedValue value={ `Value ${ token?.symbol || '' }` } w="100%" verticalAlign="middle"/> <Th width="20%" isNumeric>
</Th> <TruncatedValue value={ `Value ${ token?.symbol || '' }` } w="100%" verticalAlign="middle"/>
</Th>
) }
</Tr>
</Thead>
<Tbody>
{ showSocketInfo && (
<SocketNewItemsNotice.Desktop
url={ window.location.href }
alert={ socketInfoAlert }
num={ socketInfoNum }
type="token_transfer"
isLoading={ isLoading }
/>
) } ) }
</Tr> { data.map((item, index) => (
</Thead> <TokenTransferTableItem
<Tbody> key={ item.tx_hash + item.block_hash + item.log_index + '_' + index }
{ showSocketInfo && ( { ...item }
<SocketNewItemsNotice.Desktop tokenId={ tokenId }
url={ window.location.href } isLoading={ isLoading }
alert={ socketInfoAlert } />
num={ socketInfoNum } )) }
type="token_transfer" </Tbody>
isLoading={ isLoading } </Table>
/> </AddressHighlightProvider>
) }
{ data.map((item, index) => (
<TokenTransferTableItem
key={ item.tx_hash + item.block_hash + item.log_index + '_' + index }
{ ...item }
tokenId={ tokenId }
isLoading={ isLoading }
/>
)) }
</Tbody>
</Table>
); );
}; };
......
...@@ -67,6 +67,7 @@ const TokenTransferTableItem = ({ ...@@ -67,6 +67,7 @@ const TokenTransferTableItem = ({
truncation="constant" truncation="constant"
tokenHash={ token.address } tokenHash={ token.address }
my="5px" my="5px"
w="min-content"
/> />
</Td> </Td>
<Td px={ 0 }> <Td px={ 0 }>
...@@ -81,6 +82,7 @@ const TokenTransferTableItem = ({ ...@@ -81,6 +82,7 @@ const TokenTransferTableItem = ({
truncation="constant" truncation="constant"
tokenHash={ token.address } tokenHash={ token.address }
my="5px" my="5px"
w="min-content"
/> />
</Td> </Td>
{ (token.type === 'ERC-721' || token.type === 'ERC-1155') && ( { (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
......
...@@ -5,6 +5,7 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; ...@@ -5,6 +5,7 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import arrowIcon from 'icons/arrows/east.svg'; import arrowIcon from 'icons/arrows/east.svg';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem'; import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
import type { Sort, SortField } from 'ui/tx/internals/utils'; import type { Sort, SortField } from 'ui/tx/internals/utils';
...@@ -21,33 +22,35 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) = ...@@ -21,33 +22,35 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) =
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)'; const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return ( return (
<Table variant="simple" size="sm"> <AddressHighlightProvider>
<Thead top={ top }> <Table variant="simple" size="sm">
<Tr> <Thead top={ top }>
<Th width="28%">Type</Th> <Tr>
<Th width="20%">From</Th> <Th width="28%">Type</Th>
<Th width="24px" px={ 0 }/> <Th width="20%">From</Th>
<Th width="20%">To</Th> <Th width="24px" px={ 0 }/>
<Th width="16%" isNumeric> <Th width="20%">To</Th>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }> <Th width="16%" isNumeric>
{ sort?.includes('value') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> } <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }>
{ sort?.includes('value') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Value { config.chain.currency.symbol } Value { config.chain.currency.symbol }
</Link> </Link>
</Th> </Th>
<Th width="16%" isNumeric> <Th width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }> <Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }>
{ sort?.includes('gas-limit') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> } { sort?.includes('gas-limit') && <Icon as={ arrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Gas limit { config.chain.currency.symbol } Gas limit { config.chain.currency.symbol }
</Link> </Link>
</Th> </Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{ data.map((item, index) => ( { data.map((item, index) => (
<TxInternalsTableItem key={ item.transaction_hash + (isLoading ? index : '') } { ...item } isLoading={ isLoading }/> <TxInternalsTableItem key={ item.transaction_hash + (isLoading ? index : '') } { ...item } isLoading={ isLoading }/>
)) } )) }
</Tbody> </Tbody>
</Table> </Table>
</AddressHighlightProvider>
); );
}; };
......
...@@ -8,6 +8,7 @@ import React from 'react'; ...@@ -8,6 +8,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges'; import type { TxStateChange } from 'types/api/txStateChanges';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import TxStateTableItem from 'ui/tx/state/TxStateTableItem'; import TxStateTableItem from 'ui/tx/state/TxStateTableItem';
...@@ -19,21 +20,23 @@ interface Props { ...@@ -19,21 +20,23 @@ interface Props {
const TxStateTable = ({ data, isLoading, top }: Props) => { const TxStateTable = ({ data, isLoading, top }: Props) => {
return ( return (
<Table variant="simple" minWidth="1000px" size="sm" w="100%"> <AddressHighlightProvider>
<Thead top={ top }> <Table variant="simple" minWidth="1000px" size="sm" w="100%">
<Tr> <Thead top={ top }>
<Th width="140px">Type</Th> <Tr>
<Th width="160px">Address</Th> <Th width="140px">Type</Th>
<Th width="33%" isNumeric>Before</Th> <Th width="160px">Address</Th>
<Th width="33%" isNumeric>After</Th> <Th width="33%" isNumeric>Before</Th>
<Th width="33%" isNumeric>Change</Th> <Th width="33%" isNumeric>After</Th>
<Th width="150px" minW="80px" maxW="150px">Token ID</Th> <Th width="33%" isNumeric>Change</Th>
</Tr> <Th width="150px" minW="80px" maxW="150px">Token ID</Th>
</Thead> </Tr>
<Tbody> </Thead>
{ data.map((item, index) => <TxStateTableItem data={ item } key={ index } isLoading={ isLoading }/>) } <Tbody>
</Tbody> { data.map((item, index) => <TxStateTableItem data={ item } key={ index } isLoading={ isLoading }/>) }
</Table> </Tbody>
</Table>
</AddressHighlightProvider>
); );
}; };
......
...@@ -27,7 +27,8 @@ const TxStateTableItem = ({ data, isLoading }: Props) => { ...@@ -27,7 +27,8 @@ const TxStateTableItem = ({ data, isLoading }: Props) => {
address={ data.address } address={ data.address }
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant" truncation="constant"
py="7px" my="7px"
w="min-content"
/> />
</Td> </Td>
<Td isNumeric><Box py="7px">{ before }</Box></Td> <Td isNumeric><Box py="7px">{ before }</Box></Td>
......
...@@ -6,6 +6,7 @@ import type { Transaction, TransactionsSortingField, TransactionsSortingValue } ...@@ -6,6 +6,7 @@ import type { Transaction, TransactionsSortingField, TransactionsSortingValue }
import config from 'configs/app'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TheadSticky from 'ui/shared/TheadSticky'; import TheadSticky from 'ui/shared/TheadSticky';
...@@ -39,65 +40,67 @@ const TxsTable = ({ ...@@ -39,65 +40,67 @@ const TxsTable = ({
isLoading, isLoading,
}: Props) => { }: Props) => {
return ( return (
<Table variant="simple" minWidth="950px" size="xs"> <AddressHighlightProvider>
<TheadSticky top={ top }> <Table variant="simple" minWidth="950px" size="xs">
<Tr> <TheadSticky top={ top }>
<Th width="54px"></Th> <Tr>
<Th width="22%">Txn hash</Th> <Th width="54px"></Th>
<Th width="160px">Type</Th> <Th width="22%">Txn hash</Th>
<Th width="20%">Method</Th> <Th width="160px">Type</Th>
{ showBlockInfo && <Th width="18%">Block</Th> } <Th width="20%">Method</Th>
<Th width={{ xl: '152px', base: '86px' }}> { showBlockInfo && <Th width="18%">Block</Th> }
<Show above="xl" ssr={ false }>From</Show> <Th width={{ xl: '152px', base: '86px' }}>
<Hide above="xl" ssr={ false }>From / To</Hide> <Show above="xl" ssr={ false }>From</Show>
</Th> <Hide above="xl" ssr={ false }>From / To</Hide>
<Th width={{ xl: currentAddress ? '48px' : '36px', base: currentAddress ? '52px' : '28px' }}></Th>
<Th width={{ xl: '152px', base: '86px' }}>
<Show above="xl" ssr={ false }>To</Show>
</Th>
{ !config.UI.views.tx.hiddenFields?.value && (
<Th width="20%" isNumeric>
<Link onClick={ sort('value') } display="flex" justifyContent="end">
{ sorting === 'value-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ sorting === 'value-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
{ `Value ${ config.chain.currency.symbol }` }
</Link>
</Th> </Th>
) } <Th width={{ xl: currentAddress ? '48px' : '36px', base: currentAddress ? '52px' : '28px' }}></Th>
{ !config.UI.views.tx.hiddenFields?.tx_fee && ( <Th width={{ xl: '152px', base: '86px' }}>
<Th width="20%" isNumeric pr={ 5 }> <Show above="xl" ssr={ false }>To</Show>
<Link onClick={ sort('fee') } display="flex" justifyContent="end">
{ sorting === 'fee-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ sorting === 'fee-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
{ `Fee${ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ config.chain.currency.symbol }` }` }
</Link>
</Th> </Th>
) } { !config.UI.views.tx.hiddenFields?.value && (
</Tr> <Th width="20%" isNumeric>
</TheadSticky> <Link onClick={ sort('value') } display="flex" justifyContent="end">
<Tbody> { sorting === 'value-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ showSocketInfo && ( { sorting === 'value-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
<SocketNewItemsNotice.Desktop { `Value ${ config.chain.currency.symbol }` }
url={ window.location.href } </Link>
alert={ socketInfoAlert } </Th>
num={ socketInfoNum } ) }
isLoading={ isLoading } { !config.UI.views.tx.hiddenFields?.tx_fee && (
/> <Th width="20%" isNumeric pr={ 5 }>
) } <Link onClick={ sort('fee') } display="flex" justifyContent="end">
<AnimatePresence initial={ false }> { sorting === 'fee-asc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(-90deg)"/> }
{ txs.map((item, index) => ( { sorting === 'fee-desc' && <Icon boxSize={ 5 } as={ rightArrowIcon } transform="rotate(90deg)"/> }
<TxsTableItem { `Fee${ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ config.chain.currency.symbol }` }` }
key={ item.hash + (isLoading ? index : '') } </Link>
tx={ item } </Th>
showBlockInfo={ showBlockInfo } ) }
currentAddress={ currentAddress } </Tr>
enableTimeIncrement={ enableTimeIncrement } </TheadSticky>
<Tbody>
{ showSocketInfo && (
<SocketNewItemsNotice.Desktop
url={ window.location.href }
alert={ socketInfoAlert }
num={ socketInfoNum }
isLoading={ isLoading } isLoading={ isLoading }
/> />
)) } ) }
</AnimatePresence> <AnimatePresence initial={ false }>
</Tbody> { txs.map((item, index) => (
</Table> <TxsTableItem
key={ item.hash + (isLoading ? index : '') }
tx={ item }
showBlockInfo={ showBlockInfo }
currentAddress={ currentAddress }
enableTimeIncrement={ enableTimeIncrement }
isLoading={ isLoading }
/>
)) }
</AnimatePresence>
</Tbody>
</Table>
</AddressHighlightProvider>
); );
}; };
......
...@@ -52,7 +52,8 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -52,7 +52,8 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
noCopy={ isOut } noCopy={ isOut }
noLink={ isOut } noLink={ isOut }
truncation="constant" truncation="constant"
w="100%" w="min-content"
maxW="100%"
py="2px" py="2px"
/> />
); );
...@@ -64,7 +65,8 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -64,7 +65,8 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
truncation="constant" truncation="constant"
noCopy={ isIn } noCopy={ isIn }
noLink={ isIn } noLink={ isIn }
w="100%" w="min-content"
maxW="100%"
py="2px" py="2px"
/> />
) : '-'; ) : '-';
...@@ -150,7 +152,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -150,7 +152,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
isLoading={ isLoading } isLoading={ isLoading }
/> />
) } ) }
<VStack alignItems="start" overflow="hidden" ml={ 1 }> <VStack alignItems="start" ml={ 1 } w="calc(100% - 48px)">
{ addressFrom } { addressFrom }
{ addressTo } { addressTo }
</VStack> </VStack>
......
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