Commit 05964619 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Add link to advanced filter page filtered by current address and token transfer types (#2665)

* Add link to advanced filter page filtered by current address and token transfer types

* fix ts

* fix tests
parent 479fe68c
import { chakra } from '@chakra-ui/react';
import React from 'react';
import type { AddressFromToFilter } from 'types/api/address';
import { ADVANCED_FILTER_TYPES } from 'types/api/advancedFilter';
import type { TokenType } from 'types/api/token';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import { Link } from 'toolkit/chakra/link';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
isLoading?: boolean;
address: string;
typeFilter: Array<TokenType>;
directionFilter: AddressFromToFilter;
}
const AddressAdvancedFilterLink = ({ isLoading, address, typeFilter, directionFilter }: Props) => {
const isInitialLoading = useIsInitialLoading(isLoading);
if (!config.features.advancedFilter.isEnabled) {
return null;
}
const queryParams = {
to_address_hashes_to_include: !directionFilter || directionFilter === 'to' ? [ address ] : undefined,
from_address_hashes_to_include: !directionFilter || directionFilter === 'from' ? [ address ] : undefined,
transaction_types: typeFilter.length > 0 ? typeFilter : ADVANCED_FILTER_TYPES.filter((type) => type !== 'coin_transfer'),
};
return (
<Link
whiteSpace="nowrap"
href={ route({ pathname: '/advanced-filter', query: queryParams }) }
flexShrink={ 0 }
loading={ isInitialLoading }
minW={ 8 }
justifyContent="center"
>
<IconSvg name="filter" boxSize={ 6 }/>
<chakra.span ml={ 1 } hideBelow="lg">Advanced filter</chakra.span>
</Link>
);
};
export default React.memo(AddressAdvancedFilterLink);
import { chakra, Flex } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { CsvExportParams } from 'types/client/address'; import type { CsvExportParams } from 'types/client/address';
...@@ -9,7 +9,6 @@ import config from 'configs/app'; ...@@ -9,7 +9,6 @@ import config from 'configs/app';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { Link } from 'toolkit/chakra/link'; import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip'; import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -28,15 +27,6 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props) ...@@ -28,15 +27,6 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
return null; return null;
} }
if (isInitialLoading) {
return (
<Flex className={ className } flexShrink={ 0 } alignItems="center">
<Skeleton loading boxSize={{ base: 8, lg: 6 }}/>
<Skeleton loading hideBelow="lg" w="112px" h={ 6 } ml={ 1 }/>
</Flex>
);
}
return ( return (
<Tooltip disabled={ !isMobile } content="Download CSV"> <Tooltip disabled={ !isMobile } content="Download CSV">
<Link <Link
...@@ -44,8 +34,11 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props) ...@@ -44,8 +34,11 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
whiteSpace="nowrap" whiteSpace="nowrap"
href={ route({ pathname: '/csv-export', query: { ...params, address } }) } href={ route({ pathname: '/csv-export', query: { ...params, address } }) }
flexShrink={ 0 } flexShrink={ 0 }
loading={ isInitialLoading }
minW={ 8 }
justifyContent="center"
> >
<IconSvg name="files/csv" boxSize={{ base: '30px', lg: 6 }}/> <IconSvg name="files/csv" boxSize={ 6 }/>
<chakra.span ml={ 1 } hideBelow="lg">Download CSV</chakra.span> <chakra.span ml={ 1 } hideBelow="lg">Download CSV</chakra.span>
</Link> </Link>
</Tooltip> </Tooltip>
......
...@@ -8,10 +8,9 @@ import { test, expect, devices } from 'playwright/lib'; ...@@ -8,10 +8,9 @@ import { test, expect, devices } from 'playwright/lib';
import AddressTokenTransfers from './AddressTokenTransfers'; import AddressTokenTransfers from './AddressTokenTransfers';
const CURRENT_ADDRESS = '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859'; const CURRENT_ADDRESS = '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859';
const TOKEN_HASH = '0x1189a607CEac2f0E14867de4EB15b15C9FFB5859';
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash: CURRENT_ADDRESS, token: TOKEN_HASH }, query: { hash: CURRENT_ADDRESS },
}, },
}; };
...@@ -28,10 +27,10 @@ const tokenTransfersWoPagination = { ...@@ -28,10 +27,10 @@ const tokenTransfersWoPagination = {
next_page_params: null, next_page_params: null,
}; };
test('with token filter and pagination', async({ render, mockApiResponse }) => { test('with pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { token: TOKEN_HASH }, queryParams: { type: [] },
}); });
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
...@@ -42,10 +41,10 @@ test('with token filter and pagination', async({ render, mockApiResponse }) => { ...@@ -42,10 +41,10 @@ test('with token filter and pagination', async({ render, mockApiResponse }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('with token filter and no pagination', async({ render, mockApiResponse }) => { test('without pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWoPagination, { await mockApiResponse('address_token_transfers', tokenTransfersWoPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { token: TOKEN_HASH }, queryParams: { type: [] },
}); });
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
...@@ -59,10 +58,10 @@ test('with token filter and no pagination', async({ render, mockApiResponse }) = ...@@ -59,10 +58,10 @@ test('with token filter and no pagination', async({ render, mockApiResponse }) =
test.describe('mobile', () => { test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('with token filter and pagination', async({ render, mockApiResponse }) => { test('with pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { token: TOKEN_HASH }, queryParams: { type: [] },
}); });
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
...@@ -73,10 +72,10 @@ test.describe('mobile', () => { ...@@ -73,10 +72,10 @@ test.describe('mobile', () => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('with token filter and no pagination', async({ render, mockApiResponse }) => { test('without pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWoPagination, { await mockApiResponse('address_token_transfers', tokenTransfersWoPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { token: TOKEN_HASH }, queryParams: { type: [] },
}); });
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
......
import { Box, Flex, Text } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -12,7 +12,6 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; ...@@ -12,7 +12,6 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useIsMobile from 'lib/hooks/useIsMobile';
import useIsMounted from 'lib/hooks/useIsMounted'; import useIsMounted from 'lib/hooks/useIsMounted';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
...@@ -22,16 +21,14 @@ import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes'; ...@@ -22,16 +21,14 @@ import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token'; import { getTokenTransfersStub } from 'stubs/token';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import ResetIconButton from 'ui/shared/ResetIconButton';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable'; import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable';
import AddressAdvancedFilterLink from './AddressAdvancedFilterLink';
import AddressCsvExportLink from './AddressCsvExportLink'; import AddressCsvExportLink from './AddressCsvExportLink';
type Filters = { type Filters = {
...@@ -72,7 +69,6 @@ type Props = { ...@@ -72,7 +69,6 @@ type Props = {
const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => { const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = true, isQueryEnabled = true }: Props) => {
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const isMobile = useIsMobile();
const isMounted = useIsMounted(); const isMounted = useIsMounted();
const currentAddress = getQueryParamString(router.query.hash); const currentAddress = getQueryParamString(router.query.hash);
...@@ -80,8 +76,6 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -80,8 +76,6 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
const [ socketAlert, setSocketAlert ] = React.useState(''); const [ socketAlert, setSocketAlert ] = React.useState('');
const [ newItemsCount, setNewItemsCount ] = React.useState(0); const [ newItemsCount, setNewItemsCount ] = React.useState(0);
const tokenFilter = getQueryParamString(router.query.token) || undefined;
const [ filters, setFilters ] = React.useState<Filters>( const [ filters, setFilters ] = React.useState<Filters>(
{ {
type: getTokenFilterValue(router.query.type) || [], type: getTokenFilterValue(router.query.type) || [],
...@@ -92,7 +86,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -92,7 +86,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'address_token_transfers', resourceName: 'address_token_transfers',
pathParams: { hash: currentAddress }, pathParams: { hash: currentAddress },
filters: tokenFilter ? { token: tokenFilter } : filters, filters,
options: { options: {
enabled: isQueryEnabled, enabled: isQueryEnabled,
placeholderData: getTokenTransfersStub(undefined, { placeholderData: getTokenTransfersStub(undefined, {
...@@ -114,10 +108,6 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -114,10 +108,6 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
setFilters((prevState) => ({ ...prevState, filter: filterVal })); setFilters((prevState) => ({ ...prevState, filter: filterVal }));
}, [ filters, onFilterChange ]); }, [ filters, onFilterChange ]);
const resetTokenFilter = React.useCallback(() => {
onFilterChange({});
}, [ onFilterChange ]);
const handleNewSocketMessage: SocketMessage.AddressTokenTransfer['handler'] = (payload) => { const handleNewSocketMessage: SocketMessage.AddressTokenTransfer['handler'] = (payload) => {
setSocketAlert(''); setSocketAlert('');
...@@ -173,7 +163,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -173,7 +163,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
topic: `addresses:${ currentAddress.toLowerCase() }`, topic: `addresses:${ currentAddress.toLowerCase() }`,
onSocketClose: handleSocketClose, onSocketClose: handleSocketClose,
onSocketError: handleSocketError, onSocketError: handleSocketError,
isDisabled: pagination.page !== 1 || Boolean(tokenFilter), isDisabled: pagination.page !== 1,
}); });
useSocketMessage({ useSocketMessage({
...@@ -182,20 +172,12 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -182,20 +172,12 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
handler: handleNewSocketMessage, handler: handleNewSocketMessage,
}); });
const tokenData = React.useMemo(() => ({
address: tokenFilter || '',
name: '',
icon_url: '',
symbol: '',
type: 'ERC-20' as const,
}), [ tokenFilter ]);
if (!isMounted || !shouldRender) { if (!isMounted || !shouldRender) {
return null; return null;
} }
const numActiveFilters = (filters.type?.length || 0) + (filters.filter ? 1 : 0); const numActiveFilters = (filters.type?.length || 0) + (filters.filter ? 1 : 0);
const isActionBarHidden = !tokenFilter && !numActiveFilters && !data?.items.length && !currentAddress; const isActionBarHidden = !numActiveFilters && !data?.items.length && !currentAddress;
const content = data?.items ? ( const content = data?.items ? (
<> <>
...@@ -206,14 +188,14 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -206,14 +188,14 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
showTxInfo showTxInfo
top={ isActionBarHidden ? 0 : ACTION_BAR_HEIGHT_DESKTOP } top={ isActionBarHidden ? 0 : ACTION_BAR_HEIGHT_DESKTOP }
enableTimeIncrement enableTimeIncrement
showSocketInfo={ pagination.page === 1 && !tokenFilter } showSocketInfo={ pagination.page === 1 }
socketInfoAlert={ socketAlert } socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount } socketInfoNum={ newItemsCount }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
</Box> </Box>
<Box hideFrom="lg"> <Box hideFrom="lg">
{ pagination.page === 1 && !tokenFilter && ( { pagination.page === 1 && (
<SocketNewItemsNotice.Mobile <SocketNewItemsNotice.Mobile
url={ window.location.href } url={ window.location.href }
num={ newItemsCount } num={ newItemsCount }
...@@ -233,47 +215,33 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -233,47 +215,33 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
</> </>
) : null; ) : null;
const tokenFilterComponent = tokenFilter && ( const actionBar = !isActionBarHidden ? (
<Flex alignItems="center" flexWrap="wrap" mb={{ base: isActionBarHidden ? 3 : 6, lg: 0 }} mr={ 4 }> <ActionBar mt={ -6 }>
<Text whiteSpace="nowrap" mr={ 2 } py={ 1 }>Filtered by token</Text> <TokenTransferFilter
<Flex alignItems="center" py={ 1 }> defaultTypeFilters={ filters.type }
<TokenEntity.Icon token={ tokenData } isLoading={ isPlaceholderData }/> onTypeFilterChange={ handleTypeFilterChange }
{ isMobile ? <HashStringShorten hash={ tokenFilter }/> : tokenFilter } appliedFiltersNum={ numActiveFilters }
<ResetIconButton onClick={ resetTokenFilter }/> withAddressFilter
onAddressFilterChange={ handleAddressFilterChange }
defaultAddressFilter={ filters.filter }
isLoading={ isPlaceholderData }
/>
<Flex columnGap={{ base: 2, lg: 6 }} ml={{ base: 2, lg: 'auto' }} _empty={{ display: 'none' }}>
<AddressAdvancedFilterLink
isLoading={ isPlaceholderData }
address={ currentAddress }
typeFilter={ filters.type }
directionFilter={ filters.filter }
/>
<AddressCsvExportLink
address={ currentAddress }
params={{ type: 'token-transfers', filterType: 'address', filterValue: filters.filter }}
isLoading={ isPlaceholderData }
/>
</Flex> </Flex>
</Flex> <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/>
); </ActionBar>
) : null;
const actionBar = (
<>
{ isMobile && tokenFilterComponent }
{ !isActionBarHidden && (
<ActionBar mt={ -6 }>
{ !isMobile && tokenFilterComponent }
{ !tokenFilter && (
<TokenTransferFilter
defaultTypeFilters={ filters.type }
onTypeFilterChange={ handleTypeFilterChange }
appliedFiltersNum={ numActiveFilters }
withAddressFilter
onAddressFilterChange={ handleAddressFilterChange }
defaultAddressFilter={ filters.filter }
isLoading={ isPlaceholderData }
/>
) }
{ currentAddress && (
<AddressCsvExportLink
address={ currentAddress }
params={{ type: 'token-transfers', filterType: 'address', filterValue: filters.filter }}
ml={{ base: 2, lg: 'auto' }}
isLoading={ isPlaceholderData }
/>
) }
<Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/>
</ActionBar>
) }
</>
);
return ( return (
<DataListDisplay <DataListDisplay
......
...@@ -91,6 +91,7 @@ const NameDomainsActionBar = ({ ...@@ -91,6 +91,7 @@ const NameDomainsActionBar = ({
variant="link" variant="link"
onClick={ handleProtocolReset } onClick={ handleProtocolReset }
disabled={ protocolsFilterValue.length === 0 } disabled={ protocolsFilterValue.length === 0 }
textStyle="sm"
> >
Reset Reset
</Button> </Button>
......
...@@ -99,11 +99,12 @@ test.describe('mobile', () => { ...@@ -99,11 +99,12 @@ test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', async({ render, page, createSocket }) => { test('base view', async({ render, page, createSocket }) => {
test.slow();
const component = await render(<Token/>, { hooksConfig }, { withSocket: true }); const component = await render(<Token/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `tokens:${ hash }`); const channel = await socketServer.joinChannel(socket, `tokens:${ hash }`);
socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 }); socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 });
await component.getByText('100 ARIA').waitFor({ state: 'visible' }); await component.getByText('100 ARIA').waitFor({ state: 'visible', timeout: 10_000 });
await expect(component).toHaveScreenshot({ await expect(component).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ], mask: [ page.locator(pwConfig.adsBannerSelector) ],
...@@ -112,6 +113,7 @@ test.describe('mobile', () => { ...@@ -112,6 +113,7 @@ test.describe('mobile', () => {
}); });
test('with verified info', async({ render, page, createSocket, mockApiResponse, mockAssetResponse }) => { test('with verified info', async({ render, page, createSocket, mockApiResponse, mockAssetResponse }) => {
test.slow();
await mockApiResponse('token_verified_info', verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED, { pathParams: { chainId, hash } }); await mockApiResponse('token_verified_info', verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED, { pathParams: { chainId, hash } });
await mockAssetResponse(tokenInfo.icon_url as string, './playwright/mocks/image_s.jpg'); await mockAssetResponse(tokenInfo.icon_url as string, './playwright/mocks/image_s.jpg');
...@@ -119,7 +121,7 @@ test.describe('mobile', () => { ...@@ -119,7 +121,7 @@ test.describe('mobile', () => {
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `tokens:${ hash }`); const channel = await socketServer.joinChannel(socket, `tokens:${ hash }`);
socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 }); socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 });
await component.getByText('100 ARIA').waitFor({ state: 'visible' }); await component.getByText('100 ARIA').waitFor({ state: 'visible', timeout: 10_000 });
await expect(component).toHaveScreenshot({ await expect(component).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ], mask: [ page.locator(pwConfig.adsBannerSelector) ],
......
...@@ -21,11 +21,12 @@ interface Props { ...@@ -21,11 +21,12 @@ interface Props {
className?: string; className?: string;
isLoading?: boolean; isLoading?: boolean;
tokenHash?: string; tokenHash?: string;
tokenSymbol?: string;
truncation?: EntityProps['truncation']; truncation?: EntityProps['truncation'];
noIcon?: boolean; noIcon?: boolean;
} }
const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading, tokenHash = '', noIcon }: Props) => { const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading, tokenHash = '', tokenSymbol = '', noIcon }: Props) => {
const mode = useBreakpointValue( const mode = useBreakpointValue(
{ {
base: (typeof modeProp === 'object' && 'base' in modeProp ? modeProp.base : modeProp), base: (typeof modeProp === 'object' && 'base' in modeProp ? modeProp.base : modeProp),
...@@ -34,7 +35,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading ...@@ -34,7 +35,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading
}, },
) ?? 'long'; ) ?? 'long';
const Entity = tokenHash ? AddressEntityWithTokenFilter : AddressEntity; const Entity = tokenHash && tokenSymbol ? AddressEntityWithTokenFilter : AddressEntity;
if (mode === 'compact') { if (mode === 'compact') {
return ( return (
...@@ -52,6 +53,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading ...@@ -52,6 +53,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading
noCopy={ current === from.hash } noCopy={ current === from.hash }
noIcon={ noIcon } noIcon={ noIcon }
tokenHash={ tokenHash } tokenHash={ tokenHash }
tokenSymbol={ tokenSymbol }
truncation="constant" truncation="constant"
maxW="calc(100% - 28px)" maxW="calc(100% - 28px)"
w="min-content" w="min-content"
...@@ -65,6 +67,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading ...@@ -65,6 +67,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading
noCopy={ current === to.hash } noCopy={ current === to.hash }
noIcon={ noIcon } noIcon={ noIcon }
tokenHash={ tokenHash } tokenHash={ tokenHash }
tokenSymbol={ tokenSymbol }
truncation="constant" truncation="constant"
maxW="calc(100% - 28px)" maxW="calc(100% - 28px)"
w="min-content" w="min-content"
...@@ -87,6 +90,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading ...@@ -87,6 +90,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading
noCopy={ isOutgoing } noCopy={ isOutgoing }
noIcon={ noIcon } noIcon={ noIcon }
tokenHash={ tokenHash } tokenHash={ tokenHash }
tokenSymbol={ tokenSymbol }
truncation="constant" truncation="constant"
mr={ isOutgoing ? 4 : 2 } mr={ isOutgoing ? 4 : 2 }
/> />
...@@ -102,6 +106,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading ...@@ -102,6 +106,7 @@ const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading
noCopy={ current === to.hash } noCopy={ current === to.hash }
noIcon={ noIcon } noIcon={ noIcon }
tokenHash={ tokenHash } tokenHash={ tokenHash }
tokenSymbol={ tokenSymbol }
truncation="constant" truncation="constant"
ml={ 3 } ml={ 3 }
/> />
......
...@@ -3,21 +3,29 @@ import React from 'react'; ...@@ -3,21 +3,29 @@ import React from 'react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app';
import * as AddressEntity from './AddressEntity'; import * as AddressEntity from './AddressEntity';
interface Props extends AddressEntity.EntityProps { interface Props extends AddressEntity.EntityProps {
tokenHash: string; tokenHash: string;
tokenSymbol: string;
} }
const AddressEntityWithTokenFilter = (props: Props) => { const AddressEntityWithTokenFilter = (props: Props) => {
if (!config.features.advancedFilter.isEnabled) {
return <AddressEntity.default { ...props }/>;
}
const defaultHref = route({ const defaultHref = route({
pathname: '/address/[hash]', pathname: '/advanced-filter',
query: { query: {
...props.query, ...props.query,
hash: props.address.hash, to_address_hashes_to_include: [ props.address.hash ],
tab: 'token_transfers', from_address_hashes_to_include: [ props.address.hash ],
token: props.tokenHash, token_contract_address_hashes_to_include: [ props.tokenHash ],
scroll_to_tabs: 'true', token_contract_symbols_to_include: [ props.tokenSymbol ],
}, },
}); });
......
...@@ -3,6 +3,8 @@ import React from 'react'; ...@@ -3,6 +3,8 @@ import React from 'react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app';
import * as TokenEntity from './TokenEntity'; import * as TokenEntity from './TokenEntity';
interface Props extends TokenEntity.EntityProps { interface Props extends TokenEntity.EntityProps {
...@@ -10,14 +12,19 @@ interface Props extends TokenEntity.EntityProps { ...@@ -10,14 +12,19 @@ interface Props extends TokenEntity.EntityProps {
} }
const TokenEntityWithAddressFilter = (props: Props) => { const TokenEntityWithAddressFilter = (props: Props) => {
if (!config.features.advancedFilter.isEnabled) {
return <TokenEntity.default { ...props }/>;
}
const defaultHref = route({ const defaultHref = route({
pathname: '/address/[hash]', pathname: '/advanced-filter',
query: { query: {
...props.query, ...props.query,
hash: props.addressHash, to_address_hashes_to_include: [ props.addressHash ],
tab: 'token_transfers', from_address_hashes_to_include: [ props.addressHash ],
token: props.token.address, token_contract_address_hashes_to_include: [ props.token.address ],
scroll_to_tabs: 'true', token_contract_symbols_to_include: [ props.token.symbol ?? '' ],
}, },
}); });
......
...@@ -30,12 +30,13 @@ const TokenTypeFilter = <T extends TokenType | NFTTokenType>({ nftOnly, onChange ...@@ -30,12 +30,13 @@ const TokenTypeFilter = <T extends TokenType | NFTTokenType>({ nftOnly, onChange
return ( return (
<> <>
<Flex justifyContent="space-between" fontSize="sm"> <Flex justifyContent="space-between" textStyle="sm">
<Text fontWeight={ 600 } color="text.secondary">Type</Text> <Text fontWeight={ 600 } color="text.secondary">Type</Text>
<Button <Button
variant="link" variant="link"
onClick={ handleReset } onClick={ handleReset }
disabled={ value.length === 0 } disabled={ value.length === 0 }
textStyle="sm"
> >
Reset Reset
</Button> </Button>
......
...@@ -62,6 +62,7 @@ const TokenTransferListItem = ({ ...@@ -62,6 +62,7 @@ const TokenTransferListItem = ({
to={ to } to={ to }
isLoading={ isLoading } isLoading={ isLoading }
tokenHash={ token?.address } tokenHash={ token?.address }
tokenSymbol={ token?.symbol ?? undefined }
w="100%" w="100%"
fontWeight="500" fontWeight="500"
/> />
......
...@@ -73,6 +73,7 @@ const TokenTransferTableItem = ({ ...@@ -73,6 +73,7 @@ const TokenTransferTableItem = ({
mt="5px" mt="5px"
mode={{ lg: 'compact', xl: 'long' }} mode={{ lg: 'compact', xl: 'long' }}
tokenHash={ token?.address } tokenHash={ token?.address }
tokenSymbol={ token?.symbol ?? undefined }
/> />
</TableCell> </TableCell>
{ (token && NFT_TOKEN_TYPE_IDS.includes(token.type)) && ( { (token && NFT_TOKEN_TYPE_IDS.includes(token.type)) && (
......
...@@ -34,12 +34,13 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => { ...@@ -34,12 +34,13 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => {
return ( return (
<> <>
<Flex justifyContent="space-between" fontSize="sm"> <Flex justifyContent="space-between" textStyle="sm">
<Text fontWeight={ 600 } color="text.secondary">Show bridged tokens from</Text> <Text fontWeight={ 600 } color="text.secondary">Show bridged tokens from</Text>
<Button <Button
variant="link" variant="link"
onClick={ handleReset } onClick={ handleReset }
disabled={ value.length === 0 } disabled={ value.length === 0 }
textStyle="sm"
> >
Reset Reset
</Button> </Button>
......
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