Skip to content

Commit

Permalink
Merge branch 'main' of github.com:blockscout/frontend into address-en…
Browse files Browse the repository at this point in the history
…tity
  • Loading branch information
tom2drum committed Sep 6, 2023
2 parents 2b2aac2 + 1bd65cf commit 835c9a5
Show file tree
Hide file tree
Showing 17 changed files with 222 additions and 38 deletions.
6 changes: 6 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
import type {
Address,
AddressCounters,
AddressTabsCounters,
AddressTransactionsResponse,
AddressTokenTransferResponse,
AddressCoinBalanceHistoryResponse,
Expand Down Expand Up @@ -248,6 +249,10 @@ export const RESOURCES = {
path: '/api/v2/addresses/:hash/counters',
pathParams: [ 'hash' as const ],
},
address_tabs_counters: {
path: '/api/v2/addresses/:hash/tabs-counters',
pathParams: [ 'hash' as const ],
},
// this resource doesn't have pagination, so causing huge problems on some addresses page
// address_token_balances: {
// path: '/api/v2/addresses/:hash/token-balances',
Expand Down Expand Up @@ -581,6 +586,7 @@ Q extends 'tx_state_changes' ? TxStateChanges :
Q extends 'addresses' ? AddressesResponse :
Q extends 'address' ? Address :
Q extends 'address_counters' ? AddressCounters :
Q extends 'address_tabs_counters' ? AddressTabsCounters :
Q extends 'address_txs' ? AddressTransactionsResponse :
Q extends 'address_internal_txs' ? AddressInternalTxsResponse :
Q extends 'address_token_transfers' ? AddressTokenTransferResponse :
Expand Down
4 changes: 3 additions & 1 deletion lib/highlightText.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import xss from 'xss';

import escapeRegExp from 'lib/escapeRegExp';

export default function highlightText(text: string, query: string) {
const regex = new RegExp('(' + escapeRegExp(query) + ')', 'gi');
return text.replace(regex, '<mark>$1</mark>');
return xss(text.replace(regex, '<mark>$1</mark>'));
}
31 changes: 14 additions & 17 deletions lib/web3/useAddOrSwitchChain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,23 @@ export default function useAddOrSwitchChain() {

// This error code indicates that the chain has not been added to Wallet.
if (code === 4902) {
const params = {
method: 'wallet_addEthereumChain',
params: [ {
chainId: hexadecimalChainId,
chainName: config.chain.name,
nativeCurrency: {
name: config.chain.currency.name,
symbol: config.chain.currency.symbol,
decimals: config.chain.currency.decimals,
},
rpcUrls: [ config.chain.rpcUrl ],
blockExplorerUrls: [ config.app.baseUrl ],
} ],
// in wagmi types for wallet_addEthereumChain method is not provided
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
const params = [ {
chainId: hexadecimalChainId,
chainName: config.chain.name,
nativeCurrency: {
name: config.chain.currency.name,
symbol: config.chain.currency.symbol,
decimals: config.chain.currency.decimals,
},
rpcUrls: [ config.chain.rpcUrl ],
blockExplorerUrls: [ config.app.baseUrl ],
} ] as never;
// in wagmi types for wallet_addEthereumChain method is not provided
// eslint-disable-next-line @typescript-eslint/no-explicit-any

return await provider.request({
method: 'wallet_addEthereumChain',
params,
params: params,
});
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
"swagger-ui-react": "^5.1.0",
"use-font-face-observer": "^1.2.1",
"viem": "^1.1.8",
"wagmi": "^1.3.3"
"wagmi": "^1.3.3",
"xss": "^1.0.14"
},
"devDependencies": {
"@playwright/experimental-ct-react": "1.35.1",
Expand Down
13 changes: 12 additions & 1 deletion stubs/address.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Address, AddressCoinBalanceHistoryItem, AddressCounters, AddressTokenBalance } from 'types/api/address';
import type { Address, AddressCoinBalanceHistoryItem, AddressCounters, AddressTabsCounters, AddressTokenBalance } from 'types/api/address';
import type { AddressesItem } from 'types/api/addresses';

import { ADDRESS_HASH } from './addressParams';
Expand Down Expand Up @@ -42,6 +42,17 @@ export const ADDRESS_COUNTERS: AddressCounters = {
validations_count: '0',
};

export const ADDRESS_TABS_COUNTERS: AddressTabsCounters = {
coin_balances_count: 10,
internal_txs_count: 10,
logs_count: 10,
token_balances_count: 10,
token_transfers_count: 10,
transactions_count: 10,
validations_count: 10,
withdrawals_count: 10,
};

export const TOP_ADDRESS: AddressesItem = {
coin_balance: '11886682377162664596540805',
tx_count: '1835',
Expand Down
12 changes: 12 additions & 0 deletions types/api/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface Address extends UserTags {
creator_address_hash: string | null;
creation_tx_hash: string | null;
exchange_rate: string | null;
// TODO: if we are happy with tabs-counters method, should we delete has_something fields?
has_beacon_chain_withdrawals?: boolean;
has_custom_methods_read: boolean;
has_custom_methods_write: boolean;
Expand Down Expand Up @@ -149,3 +150,14 @@ export type AddressWithdrawalsItem = {
timestamp: string;
validator_index: number;
}

export type AddressTabsCounters = {
coin_balances_count: number;
internal_txs_count: number;
logs_count: number;
token_balances_count: number;
token_transfers_count: number;
transactions_count: number;
validations_count: number;
withdrawals_count: number;
}
77 changes: 62 additions & 15 deletions ui/pages/Address.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs';
import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString';
import { ADDRESS_INFO } from 'stubs/address';
import { ADDRESS_INFO, ADDRESS_TABS_COUNTERS } from 'stubs/address';
import AddressBlocksValidated from 'ui/address/AddressBlocksValidated';
import AddressCoinBalance from 'ui/address/AddressCoinBalance';
import AddressContract from 'ui/address/AddressContract';
Expand Down Expand Up @@ -54,24 +54,71 @@ const AddressPageContent = () => {
},
});

const addressTabsCountersQuery = useApiQuery('address_tabs_counters', {
pathParams: { hash },
queryOptions: {
enabled: Boolean(hash),
placeholderData: ADDRESS_TABS_COUNTERS,
},
});

const contractTabs = useContractTabs(addressQuery.data);

const tabs: Array<RoutedTab> = React.useMemo(() => {
return [
{ id: 'txs', title: 'Transactions', component: <AddressTxs scrollRef={ tabsScrollRef }/> },
config.features.beaconChain.isEnabled && addressQuery.data?.has_beacon_chain_withdrawals ?
{ id: 'withdrawals', title: 'Withdrawals', component: <AddressWithdrawals scrollRef={ tabsScrollRef }/> } :
undefined,
addressQuery.data?.has_token_transfers ?
{ id: 'token_transfers', title: 'Token transfers', component: <AddressTokenTransfers scrollRef={ tabsScrollRef }/> } :
{
id: 'txs',
title: 'Transactions',
count: addressTabsCountersQuery.data?.transactions_count,
component: <AddressTxs scrollRef={ tabsScrollRef }/>,
},
config.features.beaconChain.isEnabled ?
{
id: 'withdrawals',
title: 'Withdrawals',
count: addressTabsCountersQuery.data?.withdrawals_count,
component: <AddressWithdrawals scrollRef={ tabsScrollRef }/>,
} :
undefined,
addressQuery.data?.has_tokens ? { id: 'tokens', title: 'Tokens', component: <AddressTokens/>, subTabs: TOKEN_TABS } : undefined,
{ id: 'internal_txns', title: 'Internal txns', component: <AddressInternalTxs scrollRef={ tabsScrollRef }/> },
{ id: 'coin_balance_history', title: 'Coin balance history', component: <AddressCoinBalance/> },
addressQuery.data?.has_validated_blocks ?
{ id: 'blocks_validated', title: 'Blocks validated', component: <AddressBlocksValidated scrollRef={ tabsScrollRef }/> } :
{
id: 'token_transfers',
title: 'Token transfers',
count: addressTabsCountersQuery.data?.token_transfers_count,
component: <AddressTokenTransfers scrollRef={ tabsScrollRef }/>,
},
{
id: 'tokens',
title: 'Tokens',
count: addressTabsCountersQuery.data?.token_balances_count,
component: <AddressTokens/>,
subTabs: TOKEN_TABS,
},
{
id: 'internal_txns',
title: 'Internal txns',
count: addressTabsCountersQuery.data?.internal_txs_count,
component: <AddressInternalTxs scrollRef={ tabsScrollRef }/>,
},
{
id: 'coin_balance_history',
title: 'Coin balance history',
count: addressTabsCountersQuery.data?.coin_balances_count,
component: <AddressCoinBalance/>,
},
config.chain.verificationType === 'validation' ?
{
id: 'blocks_validated',
title: 'Blocks validated',
count: addressTabsCountersQuery.data?.validations_count,
component: <AddressBlocksValidated scrollRef={ tabsScrollRef }/>,
} :
undefined,
addressQuery.data?.has_logs ? { id: 'logs', title: 'Logs', component: <AddressLogs scrollRef={ tabsScrollRef }/> } : undefined,
{
id: 'logs',
title: 'Logs',
count: addressTabsCountersQuery.data?.logs_count,
component: <AddressLogs scrollRef={ tabsScrollRef }/>,
},
addressQuery.data?.is_contract ? {
id: 'contract',
title: () => {
Expand All @@ -90,7 +137,7 @@ const AddressPageContent = () => {
subTabs: contractTabs.map(tab => tab.id),
} : undefined,
].filter(Boolean);
}, [ addressQuery.data, contractTabs ]);
}, [ addressQuery.data, contractTabs, addressTabsCountersQuery.data ]);

const tags = (
<EntityTags
Expand Down Expand Up @@ -134,7 +181,7 @@ const AddressPageContent = () => {
<AddressDetails addressQuery={ addressQuery } scrollRef={ tabsScrollRef }/>
{ /* should stay before tabs to scroll up with pagination */ }
<Box ref={ tabsScrollRef }></Box>
{ addressQuery.isPlaceholderData ? <TabsSkeleton tabs={ tabs }/> : content }
{ (addressQuery.isPlaceholderData || addressTabsCountersQuery.isPlaceholderData) ? <TabsSkeleton tabs={ tabs }/> : content }
</>
);
};
Expand Down
3 changes: 2 additions & 1 deletion ui/searchResults/SearchResultListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Flex, Grid, Icon, Image, Box, Text, Skeleton, useColorMode } from '@chakra-ui/react';
import React from 'react';
import xss from 'xss';

import type { SearchResultItem } from 'types/api/search';

Expand Down Expand Up @@ -265,7 +266,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
case 'contract':
case 'address': {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
return data.name ? <span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? data.name : highlightText(data.name, searchTerm) }}/> : null;
return data.name ? <span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/> : null;
}

default:
Expand Down
3 changes: 2 additions & 1 deletion ui/searchResults/SearchResultTableItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tr, Td, Text, Flex, Icon, Image, Box, Skeleton, useColorMode } from '@chakra-ui/react';
import React from 'react';
import xss from 'xss';

import type { SearchResultItem } from 'types/api/search';

Expand Down Expand Up @@ -123,7 +124,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
</AddressEntity.Container>
</Td>
<Td colSpan={ 2 } fontSize="sm" verticalAlign="middle">
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? data.name : highlightText(data.name, searchTerm) }}/>
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/>
</Td>
</>
);
Expand Down
5 changes: 4 additions & 1 deletion ui/shared/DetailsSponsoredItem.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { GridItem } from '@chakra-ui/react';
import React from 'react';

import config from 'configs/app';
import * as cookies from 'lib/cookies';
import useIsMobile from 'lib/hooks/useIsMobile';
import AdBanner from 'ui/shared/ad/AdBanner';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';

const feature = config.features.adsBanner;

interface Props {
isLoading?: boolean;
}
Expand All @@ -14,7 +17,7 @@ const DetailsSponsoredItem = ({ isLoading }: Props) => {
const isMobile = useIsMobile();
const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED);

if (hasAdblockCookie) {
if (!feature.isEnabled || hasAdblockCookie) {
return null;
}

Expand Down
38 changes: 38 additions & 0 deletions ui/shared/Tabs/TabCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { SystemStyleObject } from '@chakra-ui/react';
import { Text, useColorModeValue } from '@chakra-ui/react';
import React from 'react';

const COUNTER_OVERLOAD = 50;

type Props = {
count?: number;
parentClassName: string;
}

const TasCounter = ({ count, parentClassName }: Props) => {

const zeroCountColor = useColorModeValue('blackAlpha.400', 'whiteAlpha.400');

if (count === undefined) {
return null;
}

const sx: SystemStyleObject = {
[`.${ parentClassName }:hover &`]: { color: 'inherit' },
};

return (
<Text
color={ count > 0 ? 'text_secondary' : zeroCountColor }
ml={ 1 }
sx={ sx }
transitionProperty="color"
transitionDuration="normal"
transitionTimingFunction="ease"
>
{ count > COUNTER_OVERLOAD ? `${ COUNTER_OVERLOAD }+` : count }
</Text>
);
};

export default TasCounter;
5 changes: 5 additions & 0 deletions ui/shared/Tabs/TabsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import React from 'react';

import type { MenuButton, TabItem } from './types';

import TabCounter from './TabCounter';
import { menuButton } from './utils';

const BUTTON_CLASSNAME = 'button-item';

interface Props {
tabs: Array<TabItem | MenuButton>;
activeTab?: TabItem;
Expand Down Expand Up @@ -59,8 +62,10 @@ const TabsMenu = ({ tabs, tabsCut, isActive, styles, onItemClick, buttonRef, act
isActive={ activeTab ? activeTab.id === tab.id : false }
justifyContent="left"
data-index={ index }
className={ BUTTON_CLASSNAME }
>
{ typeof tab.title === 'function' ? tab.title() : tab.title }
<TabCounter count={ tab.count } parentClassName={ BUTTON_CLASSNAME }/>
</Button>
)) }
</PopoverBody>
Expand Down
Loading

0 comments on commit 835c9a5

Please sign in to comment.