Show tags for watch list txs (#1289)
watchlist - show private tag Fixes #1209
@@ -68,8 +68,25 @@ export const base: Transaction = {
|
||||
has_error_in_internal_txs: false,
|
||||
};
|
||||
|
||||
export const withWatchListNames: Transaction = {
|
||||
...base,
|
||||
hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3194',
|
||||
from: {
|
||||
...base.from,
|
||||
watchlist_names: [
|
||||
{ label: 'from #1', display_name: 'from utka' },
|
||||
{ label: 'kitty', display_name: 'kitty kitty kitty cat where are you' },
|
||||
],
|
||||
},
|
||||
to: {
|
||||
...base.to,
|
||||
watchlist_names: [ { label: 'to #1', display_name: 'to utka' } ],
|
||||
} as Transaction['to'],
|
||||
};
|
||||
|
||||
export const withContractCreation: Transaction = {
|
||||
...base,
|
||||
hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3195',
|
||||
to: null,
|
||||
created_contract: {
|
||||
hash: '0xdda21946FF3FAa027104b15BE6970CA756439F5a',
|
||||
@@ -88,6 +105,7 @@ export const withContractCreation: Transaction = {
|
||||
|
||||
export const withTokenTransfer: Transaction = {
|
||||
...base,
|
||||
hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3196',
|
||||
to: {
|
||||
hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859',
|
||||
implementation_name: null,
|
||||
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 53 KiB |
@@ -21,6 +21,7 @@ test.describe('mobile', () => {
|
||||
txMock.base,
|
||||
txMock.withContractCreation,
|
||||
txMock.withTokenTransfer,
|
||||
txMock.withWatchListNames,
|
||||
]),
|
||||
}));
|
||||
|
||||
@@ -41,6 +42,7 @@ test('default view +@dark-mode', async({ mount, page }) => {
|
||||
txMock.base,
|
||||
txMock.withContractCreation,
|
||||
txMock.withTokenTransfer,
|
||||
txMock.withWatchListNames,
|
||||
]),
|
||||
}));
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
|
||||
import Icon from 'ui/shared/chakra/Icon';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxStatus from 'ui/shared/TxStatus';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
import TxType from 'ui/txs/TxType';
|
||||
@@ -32,89 +33,92 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
|
||||
const columnNum = config.UI.views.tx.hiddenFields?.value && config.UI.views.tx.hiddenFields?.tx_fee ? 2 : 3;
|
||||
|
||||
return (
|
||||
<Box
|
||||
<Grid
|
||||
gridTemplateColumns={ columnNum === 2 ? '3fr 2fr' : '3fr 2fr 150px' }
|
||||
gridGap={ 8 }
|
||||
width="100%"
|
||||
minW="700px"
|
||||
borderTop="1px solid"
|
||||
borderColor="divider"
|
||||
p={ 4 }
|
||||
_last={{ borderBottom: '1px solid', borderColor: 'divider' }}
|
||||
display={{ base: 'none', lg: 'block' }}
|
||||
display={{ base: 'none', lg: 'grid' }}
|
||||
>
|
||||
<Grid width="100%" gridTemplateColumns={ columnNum === 2 ? '3fr 2fr' : '3fr 2fr 150px' } gridGap={ 8 }>
|
||||
<Flex overflow="hidden" w="100%">
|
||||
<TxAdditionalInfo tx={ tx } isLoading={ isLoading }/>
|
||||
<Box ml={ 3 } w="calc(100% - 40px)">
|
||||
<HStack>
|
||||
<TxType types={ tx.tx_types } isLoading={ isLoading }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/>
|
||||
</HStack>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
>
|
||||
<TxEntity
|
||||
isLoading={ isLoading }
|
||||
hash={ tx.hash }
|
||||
fontWeight="700"
|
||||
/>
|
||||
{ tx.timestamp && (
|
||||
<Skeleton
|
||||
isLoaded={ !isLoading }
|
||||
color="text_secondary"
|
||||
fontWeight="400"
|
||||
fontSize="sm"
|
||||
flexShrink={ 0 }
|
||||
ml={ 2 }
|
||||
>
|
||||
<span>{ timeAgo }</span>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Grid alignItems="center" templateColumns="24px auto">
|
||||
<Icon
|
||||
as={ rightArrowIcon }
|
||||
boxSize={ 6 }
|
||||
color="gray.500"
|
||||
transform="rotate(90deg)"
|
||||
<Flex overflow="hidden" w="100%">
|
||||
<TxAdditionalInfo tx={ tx } isLoading={ isLoading }/>
|
||||
<Box ml={ 3 } w="calc(100% - 40px)">
|
||||
<HStack flexWrap="wrap">
|
||||
<TxType types={ tx.tx_types } isLoading={ isLoading }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/>
|
||||
<TxWatchListTags tx={ tx } isLoading={ isLoading }/>
|
||||
</HStack>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
>
|
||||
<TxEntity
|
||||
isLoading={ isLoading }
|
||||
hash={ tx.hash }
|
||||
fontWeight="700"
|
||||
/>
|
||||
{ tx.timestamp && (
|
||||
<Skeleton
|
||||
isLoaded={ !isLoading }
|
||||
color="text_secondary"
|
||||
fontWeight="400"
|
||||
fontSize="sm"
|
||||
flexShrink={ 0 }
|
||||
ml={ 2 }
|
||||
>
|
||||
<span>{ timeAgo }</span>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Grid alignItems="center" alignSelf="flex-start" templateColumns="24px auto">
|
||||
<Icon
|
||||
as={ rightArrowIcon }
|
||||
boxSize={ 6 }
|
||||
color="gray.500"
|
||||
transform="rotate(90deg)"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
<Box overflow="hidden" ml={ 1 }>
|
||||
<AddressEntity
|
||||
isLoading={ isLoading }
|
||||
address={ tx.from }
|
||||
fontSize="sm"
|
||||
lineHeight={ 6 }
|
||||
fontWeight="500"
|
||||
mb={ 2 }
|
||||
/>
|
||||
<Box overflow="hidden" ml={ 1 }>
|
||||
{ dataTo && (
|
||||
<AddressEntity
|
||||
isLoading={ isLoading }
|
||||
address={ tx.from }
|
||||
address={ dataTo }
|
||||
fontSize="sm"
|
||||
lineHeight={ 6 }
|
||||
fontWeight="500"
|
||||
mb={ 2 }
|
||||
/>
|
||||
{ dataTo && (
|
||||
<AddressEntity
|
||||
isLoading={ isLoading }
|
||||
address={ dataTo }
|
||||
fontSize="sm"
|
||||
fontWeight="500"
|
||||
/>
|
||||
) }
|
||||
</Box>
|
||||
</Grid>
|
||||
<Box>
|
||||
{ !config.UI.views.tx.hiddenFields?.value && (
|
||||
<Skeleton isLoaded={ !isLoading } mb={ 2 }>
|
||||
<Text as="span" whiteSpace="pre">{ config.chain.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span">Fee </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Box>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Box>
|
||||
{ !config.UI.views.tx.hiddenFields?.value && (
|
||||
<Skeleton isLoaded={ !isLoading } mb={ 2 }>
|
||||
<Text as="span" whiteSpace="pre">{ config.chain.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Text as="span">Fee </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
|
||||
import Icon from 'ui/shared/chakra/Icon';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxStatus from 'ui/shared/TxStatus';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
import TxType from 'ui/txs/TxType';
|
||||
@@ -39,9 +40,10 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
|
||||
display={{ base: 'block', lg: 'none' }}
|
||||
>
|
||||
<Flex justifyContent="space-between">
|
||||
<HStack>
|
||||
<HStack flexWrap="wrap">
|
||||
<TxType types={ tx.tx_types } isLoading={ isLoading }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/>
|
||||
<TxWatchListTags tx={ tx } isLoading={ isLoading }/>
|
||||
</HStack>
|
||||
<TxAdditionalInfo tx={ tx } isMobile isLoading={ isLoading }/>
|
||||
</Flex>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { TX } from 'stubs/tx';
|
||||
import LinkInternal from 'ui/shared/LinkInternal';
|
||||
|
||||
import LatestTxsItem from './LatestTxsItem';
|
||||
import LatestTxsItemMobile from './LatestTxsItemMobile';
|
||||
|
||||
const LatestWatchlistTxs = () => {
|
||||
useRedirectForInvalidAuthToken();
|
||||
@@ -33,7 +34,16 @@ const LatestWatchlistTxs = () => {
|
||||
const txsUrl = route({ pathname: '/txs', query: { tab: 'watchlist' } });
|
||||
return (
|
||||
<>
|
||||
<Box mb={{ base: 3, lg: 4 }}>
|
||||
<Box mb={ 3 } display={{ base: 'block', lg: 'none' }}>
|
||||
{ data.slice(0, txsCount).map(((tx, index) => (
|
||||
<LatestTxsItemMobile
|
||||
key={ tx.hash + (isPlaceholderData ? index : '') }
|
||||
tx={ tx }
|
||||
isLoading={ isPlaceholderData }
|
||||
/>
|
||||
))) }
|
||||
</Box>
|
||||
<Box mb={ 4 } display={{ base: 'none', lg: 'block' }}>
|
||||
{ data.slice(0, txsCount).map(((tx, index) => (
|
||||
<LatestTxsItem
|
||||
key={ tx.hash + (isPlaceholderData ? index : '') }
|
||||
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 148 KiB |
@@ -0,0 +1,41 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
|
||||
import Tag from 'ui/shared/chakra/Tag';
|
||||
|
||||
interface Props {
|
||||
tx: Transaction;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const TxWatchListTags = ({ tx, isLoading }: Props) => {
|
||||
const tags = [
|
||||
...(tx.from?.watchlist_names || []),
|
||||
...(tx.to?.watchlist_names || []),
|
||||
].filter(Boolean);
|
||||
|
||||
if (tags.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex columnGap={ 2 } rowGap={ 2 } flexWrap="wrap" overflow="hidden" maxW="100%">
|
||||
{ tags.map((tag) => (
|
||||
<Tag
|
||||
key={ tag.label }
|
||||
isLoading={ isLoading }
|
||||
isTruncated
|
||||
// maxW={{ base: '115px', lg: 'initial' }}
|
||||
colorScheme="gray"
|
||||
variant="subtle"
|
||||
>
|
||||
{ tag.display_name }
|
||||
</Tag>
|
||||
)) }
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TxWatchListTags);
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
@@ -11,7 +11,7 @@ test.use({ viewport: devices['iPhone 13 Pro'].viewport });
|
||||
test('base view +@dark-mode', async({ mount }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<TxsListItem tx={ txMock.base } showBlockInfo/>
|
||||
<TxsListItem tx={ txMock.withWatchListNames } showBlockInfo/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
@@ -21,7 +21,7 @@ test('base view +@dark-mode', async({ mount }) => {
|
||||
test('with base address', async({ mount }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<TxsListItem tx={ txMock.base } showBlockInfo currentAddress={ txMock.base.from.hash }/>
|
||||
<TxsListItem tx={ txMock.withWatchListNames } showBlockInfo currentAddress={ txMock.base.from.hash }/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import InOutTag from 'ui/shared/InOutTag';
|
||||
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxStatus from 'ui/shared/TxStatus';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
import TxType from 'ui/txs/TxType';
|
||||
@@ -44,9 +45,10 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
|
||||
return (
|
||||
<ListItemMobile display="block" width="100%" isAnimated key={ tx.hash }>
|
||||
<Flex justifyContent="space-between" mt={ 4 }>
|
||||
<HStack>
|
||||
<HStack flexWrap="wrap">
|
||||
<TxType types={ tx.tx_types } isLoading={ isLoading }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/>
|
||||
<TxWatchListTags tx={ tx } isLoading={ isLoading }/>
|
||||
</HStack>
|
||||
<TxAdditionalInfo tx={ tx } isMobile isLoading={ isLoading }/>
|
||||
</Flex>
|
||||
|
||||
@@ -13,7 +13,7 @@ test.describe('base view', () => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
{ /* eslint-disable-next-line react/jsx-no-bind */ }
|
||||
<TxsTable txs={ [ txMock.base, txMock.base ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
|
||||
<TxsTable txs={ [ txMock.base, txMock.withWatchListNames ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ test.describe('base view', () => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
{ /* eslint-disable-next-line react/jsx-no-bind */ }
|
||||
<TxsTable txs={ [ txMock.base, txMock.base ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
|
||||
<TxsTable txs={ [ txMock.base, txMock.withWatchListNames ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import InOutTag from 'ui/shared/InOutTag';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxStatus from 'ui/shared/TxStatus';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
|
||||
@@ -95,6 +96,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
|
||||
<VStack alignItems="start">
|
||||
<TxType types={ tx.tx_types } isLoading={ isLoading }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined } isLoading={ isLoading }/>
|
||||
<TxWatchListTags tx={ tx } isLoading={ isLoading }/>
|
||||
</VStack>
|
||||
</Td>
|
||||
<Td whiteSpace="nowrap">
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 45 KiB |