Commit 9d6a512c authored by tom goriunov's avatar tom goriunov Committed by GitHub

collapse long token name (#994)

* fix tokens list, token transfers and token select items

* fix tx details view

* delete trimTokenSymbol utility

* fix max token supply

* truncate page title

* change verified token badge

* change api host (for preview purpose)

* fix tests

* update screenshots
parent cec71aa0
...@@ -77,7 +77,7 @@ frontend: ...@@ -77,7 +77,7 @@ frontend:
NEXT_PUBLIC_FEATURED_NETWORKS: NEXT_PUBLIC_FEATURED_NETWORKS:
_default: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json _default: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_API_HOST: NEXT_PUBLIC_API_HOST:
_default: blockscout-main.k8s-dev.blockscout.com _default: eth-goerli.blockscout.com
NEXT_PUBLIC_STATS_API_HOST: NEXT_PUBLIC_STATS_API_HOST:
_default: https://stats-test.k8s-dev.blockscout.com/ _default: https://stats-test.k8s-dev.blockscout.com/
NEXT_PUBLIC_VISUALIZE_API_HOST: NEXT_PUBLIC_VISUALIZE_API_HOST:
......
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.522 2.624a2 2 0 0 1 2.957 0l1.02 1.12a2 2 0 0 0 1.572.651l1.514-.07a2 2 0 0 1 2.091 2.09l-.07 1.514a2 2 0 0 0 .65 1.572l1.12 1.02a2 2 0 0 1 0 2.958l-1.12 1.02a2 2 0 0 0-.65 1.572l.07 1.514a2 2 0 0 1-2.091 2.091l-1.514-.07a2 2 0 0 0-1.572.65l-1.02 1.12a2 2 0 0 1-2.957 0l-1.02-1.12a2 2 0 0 0-1.573-.65l-1.513.07a2 2 0 0 1-2.091-2.091l.07-1.514a2 2 0 0 0-.65-1.572l-1.121-1.02a2 2 0 0 1 0-2.957l1.12-1.02a2 2 0 0 0 .651-1.573l-.07-1.513a2 2 0 0 1 2.09-2.091l1.514.07a2 2 0 0 0 1.572-.65l1.02-1.121Z" stroke="currentColor" stroke-width="1.5"/>
<path d="m9 12 2 2 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
import appConfig from 'configs/app/config'; // import appConfig from 'configs/app/config';
// FIXME // FIXME
// I was not able to figure out how to send CORS with credentials from localhost // I was not able to figure out how to send CORS with credentials from localhost
// unsuccessfully tried different ways, even custom local dev domain // unsuccessfully tried different ways, even custom local dev domain
// so for local development we have to use next.js api as proxy server // so for local development we have to use next.js api as proxy server
export default function isNeedProxy() { export default function isNeedProxy() {
return appConfig.host === 'localhost' && appConfig.host !== appConfig.api.host; // eslint-disable-next-line no-restricted-properties
if (process.env.NEXT_PUBLIC_APP_INSTANCE === 'pw') {
return false;
}
return true;
} }
// some tokens could have symbols like "ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY"
// so in some cases we trim it to max 10 symbols
export default function trimTokenSymbol(symbol: string | null) {
if (!symbol) {
return '';
}
if (symbol.length <= 7) {
return symbol;
}
return symbol.slice(0, 7) + '...';
}
...@@ -184,7 +184,7 @@ export const withActionsUniswap: Transaction = { ...@@ -184,7 +184,7 @@ export const withActionsUniswap: Transaction = {
address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6',
amount0: '7143.488560357232097378', amount0: '7143.488560357232097378',
amount1: '10', amount1: '10',
symbol0: 'XFT', symbol0: 'Ring ding ding daa baa Baa aramba baa bom baa barooumba Wh-wha-what&#39;s going on-on? Ding, ding This is the Crazy Frog Ding, ding Bem',
symbol1: 'Ether', symbol1: 'Ether',
}, },
protocol: 'uniswap_v3', protocol: 'uniswap_v3',
......
import { Skeleton } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Address } from 'types/api/address'; import type { Address } from 'types/api/address';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LinkInternal from 'ui/shared/LinkInternal'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
interface Props { interface Props {
data: Pick<Address, 'name' | 'token' | 'is_contract'>; data: Pick<Address, 'name' | 'token' | 'is_contract'>;
...@@ -15,18 +13,13 @@ interface Props { ...@@ -15,18 +13,13 @@ interface Props {
const AddressNameInfo = ({ data, isLoading }: Props) => { const AddressNameInfo = ({ data, isLoading }: Props) => {
if (data.token) { if (data.token) {
const symbol = data.token.symbol ? ` (${ trimTokenSymbol(data.token.symbol) })` : '';
return ( return (
<DetailsInfoItem <DetailsInfoItem
title="Token name" title="Token name"
hint="Token name and symbol" hint="Token name and symbol"
isLoading={ isLoading } isLoading={ isLoading }
> >
<Skeleton isLoaded={ !isLoading }> <TokenSnippet data={ data.token } isLoading={ isLoading } hideIcon/>
<LinkInternal href={ route({ pathname: '/token/[hash]', query: { hash: data.token.address } }) }>
{ data.token.name || 'Unnamed token' }{ symbol }
</LinkInternal>
</Skeleton>
</DetailsInfoItem> </DetailsInfoItem>
); );
} }
......
...@@ -3,9 +3,8 @@ import BigNumber from 'bignumber.js'; ...@@ -3,9 +3,8 @@ import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import trimTokenSymbol from 'lib/token/trimTokenSymbol'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import HashStringShorten from 'ui/shared/HashStringShorten'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TokenLogo from 'ui/shared/TokenLogo';
import type { TokenEnhancedData } from '../utils/tokenUtils'; import type { TokenEnhancedData } from '../utils/tokenUtils';
...@@ -19,15 +18,28 @@ const TokenSelectItem = ({ data }: Props) => { ...@@ -19,15 +18,28 @@ const TokenSelectItem = ({ data }: Props) => {
switch (data.token.type) { switch (data.token.type) {
case 'ERC-20': { case 'ERC-20': {
const tokenDecimals = Number(data.token.decimals) || 18; const tokenDecimals = Number(data.token.decimals) || 18;
const text = `${ BigNumber(data.value).dividedBy(10 ** tokenDecimals).toFormat(2) } ${ data.token.symbol }`;
return ( return (
<> <>
<span >{ BigNumber(data.value).dividedBy(10 ** tokenDecimals).toFormat(2) } { trimTokenSymbol(data.token.symbol) }</span> <TruncatedTextTooltip label={ text }>
{ data.token.exchange_rate && <span >@{ Number(data.token.exchange_rate).toLocaleString() }</span> } <chakra.span textOverflow="ellipsis" overflow="hidden">
{ text }
</chakra.span>
</TruncatedTextTooltip>
{ data.token.exchange_rate && <chakra.span ml={ 2 }>@{ Number(data.token.exchange_rate).toLocaleString() }</chakra.span> }
</> </>
); );
} }
case 'ERC-721': { case 'ERC-721': {
return <chakra.span textOverflow="ellipsis" overflow="hidden">{ BigNumber(data.value).toFormat() } { data.token.symbol }</chakra.span>; const text = `${ BigNumber(data.value).toFormat() } ${ data.token.symbol }`;
return (
<TruncatedTextTooltip label={ text }>
<chakra.span textOverflow="ellipsis" overflow="hidden">
{ text }
</chakra.span>
</TruncatedTextTooltip>
);
} }
case 'ERC-1155': { case 'ERC-1155': {
return ( return (
...@@ -64,9 +76,8 @@ const TokenSelectItem = ({ data }: Props) => { ...@@ -64,9 +76,8 @@ const TokenSelectItem = ({ data }: Props) => {
as="a" as="a"
href={ url } href={ url }
> >
<Flex alignItems="center" w="100%"> <Flex alignItems="center" w="100%" overflow="hidden">
<TokenLogo data={ data.token } boxSize={ 6 }/> <TokenSnippet data={ data.token } hideSymbol fontWeight={ 700 } isDisabled/>
<Text fontWeight={ 700 } ml={ 2 }>{ data.token.name || <HashStringShorten hash={ data.token.address }/> }</Text>
{ data.usd && <Text fontWeight={ 700 } ml="auto">${ data.usd.toFormat(2) }</Text> } { data.usd && <Text fontWeight={ 700 } ml="auto">${ data.usd.toFormat(2) }</Text> }
</Flex> </Flex>
<Flex alignItems="center" justifyContent="space-between" w="100%" whiteSpace="nowrap"> <Flex alignItems="center" justifyContent="space-between" w="100%" whiteSpace="nowrap">
......
import { Box, Icon } from '@chakra-ui/react'; import { Box, Icon, Tooltip } 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, { useEffect } from 'react'; import React, { useEffect } from 'react';
...@@ -10,6 +10,7 @@ import type { RoutedTab } from 'ui/shared/Tabs/types'; ...@@ -10,6 +10,7 @@ import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import iconVerifiedToken from 'icons/verified_token.svg';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs'; import useContractTabs from 'lib/hooks/useContractTabs';
...@@ -17,7 +18,6 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -17,7 +18,6 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import * as addressStubs from 'stubs/address'; import * as addressStubs from 'stubs/address';
import * as tokenStubs from 'stubs/token'; import * as tokenStubs from 'stubs/token';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
...@@ -197,7 +197,7 @@ const TokenPageContent = () => { ...@@ -197,7 +197,7 @@ const TokenPageContent = () => {
pagination = inventoryQuery.pagination; pagination = inventoryQuery.pagination;
} }
const tokenSymbolText = tokenQuery.data?.symbol ? ` (${ trimTokenSymbol(tokenQuery.data.symbol) })` : ''; const tokenSymbolText = tokenQuery.data?.symbol ? ` (${ tokenQuery.data.symbol })` : '';
const tabListProps = React.useCallback(({ isSticky, activeTabIndex }: { isSticky: boolean; activeTabIndex: number }) => { const tabListProps = React.useCallback(({ isSticky, activeTabIndex }: { isSticky: boolean; activeTabIndex: number }) => {
if (isMobile) { if (isMobile) {
...@@ -225,30 +225,39 @@ const TokenPageContent = () => { ...@@ -225,30 +225,39 @@ const TokenPageContent = () => {
}; };
}, [ appProps.referrer ]); }, [ appProps.referrer ]);
const tags = ( const titleContentAfter = (
<EntityTags <>
data={ contractQuery.data } { verifiedInfoQuery.data?.tokenAddress && (
isLoading={ tokenQuery.isPlaceholderData || contractQuery.isPlaceholderData } <Tooltip label={ `Information on this token has been verified by ${ appConfig.network.name }` }>
tagsBefore={ [ <Box boxSize={ 6 }>
tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined, <Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/>
] } </Box>
tagsAfter={ </Tooltip>
verifiedInfoQuery.data?.projectSector ? ) }
[ { label: verifiedInfoQuery.data.projectSector, display_name: verifiedInfoQuery.data.projectSector } ] : <EntityTags
undefined data={ contractQuery.data }
} isLoading={ tokenQuery.isPlaceholderData || contractQuery.isPlaceholderData }
contentAfter={ tagsBefore={ [
<NetworkExplorers type="token" pathParam={ hashString } ml="auto" hideText={ isMobile }/> tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined,
} ] }
flexGrow={ 1 } tagsAfter={
/> verifiedInfoQuery.data?.projectSector ?
[ { label: verifiedInfoQuery.data.projectSector, display_name: verifiedInfoQuery.data.projectSector } ] :
undefined
}
contentAfter={
<NetworkExplorers type="token" pathParam={ hashString } ml="auto" hideText={ isMobile }/>
}
flexGrow={ 1 }
/>
</>
); );
return ( return (
<> <>
<TextAd mb={ 6 }/> <TextAd mb={ 6 }/>
<PageTitle <PageTitle
title={ `${ tokenQuery.data?.name || 'Unnamed' }${ tokenSymbolText } token` } title={ `${ tokenQuery.data?.name || 'Unnamed token' }${ tokenSymbolText }` }
isLoading={ tokenQuery.isPlaceholderData } isLoading={ tokenQuery.isPlaceholderData }
backLink={ backLink } backLink={ backLink }
beforeTitle={ ( beforeTitle={ (
...@@ -256,18 +265,10 @@ const TokenPageContent = () => { ...@@ -256,18 +265,10 @@ const TokenPageContent = () => {
data={ tokenQuery.data } data={ tokenQuery.data }
boxSize={ 6 } boxSize={ 6 }
isLoading={ tokenQuery.isPlaceholderData } isLoading={ tokenQuery.isPlaceholderData }
display="inline-block"
mr={ 2 } mr={ 2 }
my={{ base: 'auto', lg: tokenQuery.isPlaceholderData ? 2 : 'auto' }}
verticalAlign={{ base: undefined, lg: tokenQuery.isPlaceholderData ? 'text-bottom' : undefined }}
/> />
) } ) }
afterTitle={ contentAfter={ titleContentAfter }
verifiedInfoQuery.data?.tokenAddress ?
<Icon as={ iconSuccess } color="green.500" boxSize={ 4 } verticalAlign="top"/> :
<Box boxSize={ 4 } display="inline-block"/>
}
contentAfter={ tags }
/> />
<TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/> <TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/>
<TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/> <TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/>
......
...@@ -10,7 +10,6 @@ import txIcon from 'icons/transactions.svg'; ...@@ -10,7 +10,6 @@ import txIcon from 'icons/transactions.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -39,10 +38,10 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -39,10 +38,10 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
const firstRow = (() => { const firstRow = (() => {
switch (data.type) { switch (data.type) {
case 'token': { case 'token': {
const name = data.name + (data.symbol ? ` (${ trimTokenSymbol(data.symbol) })` : ''); const name = data.name + (data.symbol ? ` (${ data.symbol })` : '');
return ( return (
<Flex alignItems="flex-start"> <Flex alignItems="flex-start" overflow="hidden">
<TokenLogo boxSize={ 6 } data={ data } flexShrink={ 0 } isLoading={ isLoading }/> <TokenLogo boxSize={ 6 } data={ data } flexShrink={ 0 } isLoading={ isLoading }/>
<LinkInternal <LinkInternal
ml={ 2 } ml={ 2 }
...@@ -51,8 +50,15 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -51,8 +50,15 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
wordBreak="break-all" wordBreak="break-all"
isLoading={ isLoading } isLoading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
overflow="hidden"
> >
<Skeleton isLoaded={ !isLoading } dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}/> <Skeleton
isLoaded={ !isLoading }
dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
/>
</LinkInternal> </LinkInternal>
</Flex> </Flex>
); );
......
...@@ -10,7 +10,6 @@ import txIcon from 'icons/transactions.svg'; ...@@ -10,7 +10,6 @@ import txIcon from 'icons/transactions.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -38,7 +37,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -38,7 +37,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
const content = (() => { const content = (() => {
switch (data.type) { switch (data.type) {
case 'token': { case 'token': {
const name = data.name + (data.symbol ? ` (${ trimTokenSymbol(data.symbol) })` : ''); const name = data.name + (data.symbol ? ` (${ data.symbol })` : '');
return ( return (
<> <>
...@@ -50,10 +49,17 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -50,10 +49,17 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
overflow="hidden"
isLoading={ isLoading } isLoading={ isLoading }
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
<Skeleton isLoaded={ !isLoading } dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}/> <Skeleton
isLoaded={ !isLoading }
overflow="hidden"
textOverflow="ellipsis"
whiteSpace="nowrap"
dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}
/>
</LinkInternal> </LinkInternal>
</Flex> </Flex>
</Td> </Td>
......
...@@ -37,6 +37,7 @@ const AdditionalInfoButton = ({ isOpen, onClick, className, isLoading }: Props, ...@@ -37,6 +37,7 @@ const AdditionalInfoButton = ({ isOpen, onClick, className, isLoading }: Props,
h="24px" h="24px"
onClick={ onClick } onClick={ onClick }
cursor="pointer" cursor="pointer"
flexShrink={ 0 }
> >
<Icon <Icon
as={ infoIcon } as={ infoIcon }
......
import { Heading, Flex, Tooltip, Icon, Link, chakra, Box, Skeleton } from '@chakra-ui/react'; import { Heading, Flex, Tooltip, Icon, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react';
import _debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -18,6 +20,8 @@ type Props = { ...@@ -18,6 +20,8 @@ type Props = {
withTextAd?: boolean; withTextAd?: boolean;
} }
const TEXT_MAX_LINES = 1;
const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => { const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
if (!props) { if (!props) {
return null; return null;
...@@ -49,6 +53,42 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => { ...@@ -49,6 +53,42 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
}; };
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading, afterTitle, beforeTitle }: Props) => { const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading, afterTitle, beforeTitle }: Props) => {
const tooltip = useDisclosure();
const isMobile = useIsMobile();
const [ isTextTruncated, setIsTextTruncated ] = React.useState(false);
const headingRef = React.useRef<HTMLHeadingElement>(null);
const textRef = React.useRef<HTMLSpanElement>(null);
const updatedTruncateState = React.useCallback(() => {
if (!headingRef.current || !textRef.current) {
return;
}
const headingRect = headingRef.current.getBoundingClientRect();
const textRect = textRef.current.getBoundingClientRect();
if ((TEXT_MAX_LINES + 1) * headingRect.height <= textRect.height) {
setIsTextTruncated(true);
} else {
setIsTextTruncated(false);
}
}, []);
React.useEffect(() => {
if (!isLoading) {
updatedTruncateState();
}
}, [ isLoading, updatedTruncateState ]);
React.useEffect(() => {
const handleResize = _debounce(updatedTruncateState, 1000);
window.addEventListener('resize', handleResize);
return function cleanup() {
window.removeEventListener('resize', handleResize);
};
}, [ updatedTruncateState ]);
return ( return (
<Flex <Flex
className={ className } className={ className }
...@@ -59,26 +99,46 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa ...@@ -59,26 +99,46 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
columnGap={ 3 } columnGap={ 3 }
alignItems="center" alignItems="center"
> >
<Box h={{ base: 'auto', lg: isLoading ? 10 : 'auto' }}> <Flex h={{ base: 'auto', lg: isLoading ? 10 : 'auto' }} maxW="100%" alignItems="center">
{ backLink && <BackLink { ...backLink } isLoading={ isLoading }/> } { backLink && <BackLink { ...backLink } isLoading={ isLoading }/> }
{ beforeTitle } { beforeTitle }
<Skeleton <Skeleton
isLoaded={ !isLoading } isLoaded={ !isLoading }
display={{ base: 'inline', lg: isLoading ? 'inline-block' : 'inline' }} overflow="hidden"
verticalAlign={ isLoading ? 'super' : undefined }
> >
<Heading <Tooltip
as="h1" label={ title }
size="lg" isOpen={ tooltip.isOpen }
display="inline" onClose={ tooltip.onClose }
wordBreak="break-word" maxW={{ base: 'calc(100vw - 32px)', lg: '500px' }}
w="100%" closeOnScroll={ isMobile ? true : false }
isDisabled={ !isTextTruncated }
> >
{ title } <Heading
</Heading> ref={ headingRef }
as="h1"
size="lg"
whiteSpace="normal"
wordBreak="break-all"
style={{
WebkitLineClamp: TEXT_MAX_LINES,
WebkitBoxOrient: 'vertical',
display: '-webkit-box',
}}
overflow="hidden"
textOverflow="ellipsis"
onMouseEnter={ tooltip.onOpen }
onMouseLeave={ tooltip.onClose }
onClick={ isMobile ? tooltip.onToggle : undefined }
>
<span ref={ textRef }>
{ title }
</span>
</Heading>
</Tooltip>
</Skeleton> </Skeleton>
{ afterTitle } { afterTitle }
</Box> </Flex>
{ contentAfter } { contentAfter }
{ withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto" w={{ base: '100%', lg: 'auto' }}/> } { withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto" w={{ base: '100%', lg: 'auto' }}/> }
</Flex> </Flex>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import iconSuccess from 'icons/status/success.svg'; import iconVerifiedToken from 'icons/verified_token.svg';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
...@@ -33,22 +33,24 @@ const DefaultView = () => { ...@@ -33,22 +33,24 @@ const DefaultView = () => {
}; };
const contentAfter = ( const contentAfter = (
<EntityTags <>
tagsBefore={ [ <Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/>
{ label: 'example', display_name: 'Example label' }, <EntityTags
] } tagsBefore={ [
contentAfter={ <NetworkExplorers type="token" pathParam="token-hash" ml="auto" hideText={ isMobile }/> } { label: 'example', display_name: 'Example label' },
flexGrow={ 1 } ] }
/> contentAfter={ <NetworkExplorers type="token" pathParam="token-hash" ml="auto" hideText={ isMobile }/> }
flexGrow={ 1 }
/>
</>
); );
return ( return (
<PageTitle <PageTitle
title="Shavukha Token (SHVKH) token" title="Shavukha Token (SHVKH) token"
beforeTitle={ ( beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } display="inline-block" mr={ 2 }/> <TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/>
) } ) }
afterTitle={ <Icon as={ iconSuccess } color="green.500" boxSize={ 4 } verticalAlign="top"/> }
backLink={ backLink } backLink={ backLink }
contentAfter={ contentAfter } contentAfter={ contentAfter }
/> />
......
...@@ -4,9 +4,8 @@ import React from 'react'; ...@@ -4,9 +4,8 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import iconSuccess from 'icons/status/success.svg'; import iconVerifiedToken from 'icons/verified_token.svg';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import { publicTag, privateTag, watchlistName } from 'mocks/address/tag'; import { publicTag, privateTag, watchlistName } from 'mocks/address/tag';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
...@@ -31,33 +30,35 @@ const LongNameAndManyTags = () => { ...@@ -31,33 +30,35 @@ const LongNameAndManyTags = () => {
}; };
const contentAfter = ( const contentAfter = (
<EntityTags <>
data={{ <Icon as={ iconVerifiedToken } color="green.500" boxSize={ 6 } cursor="pointer"/>
private_tags: [ privateTag ], <EntityTags
public_tags: [ publicTag ], data={{
watchlist_names: [ watchlistName ], private_tags: [ privateTag ],
}} public_tags: [ publicTag ],
tagsBefore={ [ watchlist_names: [ watchlistName ],
{ label: 'example', display_name: 'Example with long name' }, }}
] } tagsBefore={ [
tagsAfter={ [ { label: 'example', display_name: 'Example with long name' },
{ label: 'after_1', display_name: 'Another tag' }, ] }
{ label: 'after_2', display_name: 'And yet more' }, tagsAfter={ [
] } { label: 'after_1', display_name: 'Another tag' },
contentAfter={ <NetworkExplorers type="token" pathParam="token-hash" ml="auto" hideText={ isMobile }/> } { label: 'after_2', display_name: 'And yet more' },
flexGrow={ 1 } ] }
/> contentAfter={ <NetworkExplorers type="token" pathParam="token-hash" ml="auto" hideText={ isMobile }/> }
flexGrow={ 1 }
/>
</>
); );
const tokenSymbolText = ` (${ trimTokenSymbol(tokenData.symbol) })`; const tokenSymbolText = ` (${ tokenData.symbol })`;
return ( return (
<PageTitle <PageTitle
title={ `${ tokenData?.name }${ tokenSymbolText } token` } title={ `${ tokenData?.name }${ tokenSymbolText } token` }
beforeTitle={ ( beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } display="inline-block" mr={ 2 }/> <TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/>
) } ) }
afterTitle={ <Icon as={ iconSuccess } color="green.500" boxSize={ 4 } verticalAlign="top"/> }
contentAfter={ contentAfter } contentAfter={ contentAfter }
/> />
); );
......
import { Flex, chakra, Skeleton } from '@chakra-ui/react'; import type { StyleProps } from '@chakra-ui/react';
import { Flex, chakra, Skeleton, Box, shouldForwardProp } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
interface Props { interface Props {
data?: Pick<TokenInfo, 'address' | 'icon_url' | 'name' | 'symbol'>; data?: Pick<TokenInfo, 'address' | 'icon_url' | 'name' | 'symbol'>;
...@@ -14,20 +15,53 @@ interface Props { ...@@ -14,20 +15,53 @@ interface Props {
isDisabled?: boolean; isDisabled?: boolean;
isLoading?: boolean; isLoading?: boolean;
hideSymbol?: boolean; hideSymbol?: boolean;
hideIcon?: boolean;
maxW: StyleProps['maxW'];
} }
const TokenSnippet = ({ data, className, logoSize = 6, isDisabled, hideSymbol, isLoading }: Props) => { const TokenSnippet = ({ data, className, logoSize = 6, isDisabled, hideSymbol, hideIcon, isLoading, maxW }: Props) => {
const withSymbol = data && data.symbol && !hideSymbol;
const columnGap = 2;
return ( return (
<Flex className={ className } alignItems="center" columnGap={ 2 } w="100%"> <Flex className={ className } alignItems="center" columnGap={ columnGap } w="100%" overflow="hidden">
<TokenLogo boxSize={ logoSize } data={ data } isLoading={ isLoading }/> { !hideIcon && <TokenLogo boxSize={ logoSize } data={ data } isLoading={ isLoading }/> }
<AddressLink hash={ data?.address || '' } alias={ data?.name || 'Unnamed token' } type="token" isDisabled={ isDisabled } isLoading={ isLoading }/> <AddressLink
{ data?.symbol && !hideSymbol && ( flexShrink={ 0 }
<Skeleton isLoaded={ !isLoading } color="text_secondary"> hash={ data?.address || '' }
<span>({ trimTokenSymbol(data.symbol) })</span> alias={ data?.name || 'Unnamed token' }
type="token"
isDisabled={ isDisabled }
isLoading={ isLoading }
maxW={ withSymbol ?
`calc(80% - ${ (logoSize + columnGap * 2) * 4 }px)` :
`calc(${ maxW || '100%' } - ${ (logoSize + columnGap) * 4 }px)`
}
overflow="hidden"
textOverflow="ellipsis"
/>
{ withSymbol && (
<Skeleton isLoaded={ !isLoading } color="text_secondary" maxW="20%" display="flex">
<div>(</div>
<TruncatedTextTooltip label={ data.symbol || '' }>
<Box overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" wordBreak="break-all">{ data.symbol }</Box>
</TruncatedTextTooltip>
<div>)</div>
</Skeleton> </Skeleton>
) } ) }
</Flex> </Flex>
); );
}; };
export default chakra(TokenSnippet); export default chakra(TokenSnippet, {
shouldForwardProp: (prop) => {
const isChakraProp = !shouldForwardProp(prop);
if (isChakraProp && prop !== 'maxW') {
return false;
}
return true;
},
});
...@@ -54,8 +54,8 @@ const TokenTransferListItem = ({ ...@@ -54,8 +54,8 @@ const TokenTransferListItem = ({
return ( return (
<ListItemMobile rowGap={ 3 } isAnimated> <ListItemMobile rowGap={ 3 } isAnimated>
<Flex w="100%" justifyContent="space-between"> <Flex w="100%" justifyContent="space-between">
<Flex flexWrap="wrap" rowGap={ 1 } mr={ showTxInfo && txHash ? 2 : 0 } columnGap={ 2 }> <Flex flexWrap="wrap" rowGap={ 1 } mr={ showTxInfo && txHash ? 2 : 0 } columnGap={ 2 } overflow="hidden">
<TokenSnippet data={ token } w="auto" maxW="calc(100% - 140px)" hideSymbol isLoading={ isLoading }/> <TokenSnippet data={ token } w="auto" hideSymbol isLoading={ isLoading }/>
<Tag flexShrink={ 0 } isLoading={ isLoading }>{ token.type }</Tag> <Tag flexShrink={ 0 } isLoading={ isLoading }>{ token.type }</Tag>
<Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag> <Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex> </Flex>
......
...@@ -60,7 +60,7 @@ const TruncatedTextTooltip = ({ children, label }: Props) => { ...@@ -60,7 +60,7 @@ const TruncatedTextTooltip = ({ children, label }: Props) => {
); );
if (isTruncated) { if (isTruncated) {
return <Tooltip label={ label }>{ modifiedChildren }</Tooltip>; return <Tooltip label={ label } maxW={{ base: '100vw', lg: '400px' }}>{ modifiedChildren }</Tooltip>;
} }
return modifiedChildren; return modifiedChildren;
......
...@@ -61,7 +61,7 @@ const AddressLink = (props: Props) => { ...@@ -61,7 +61,7 @@ const AddressLink = (props: Props) => {
const content = (() => { const content = (() => {
if (alias) { if (alias) {
const text = <Box overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">{ alias }</Box>; const text = <Box overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">{ alias }</Box>;
if (type === 'token') { if (type === 'token' || type === 'address_token') {
return ( return (
<TruncatedTextTooltip label={ alias }> <TruncatedTextTooltip label={ alias }>
{ text } { text }
......
import { Box, Flex, Grid, Link, Skeleton } from '@chakra-ui/react'; import { Box, Grid, Link, Skeleton } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -13,8 +13,7 @@ import { TOKEN_COUNTERS } from 'stubs/token'; ...@@ -13,8 +13,7 @@ import { TOKEN_COUNTERS } from 'stubs/token';
import type { TokenTabs } from 'ui/pages/Token'; import type { TokenTabs } from 'ui/pages/Token';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem'; import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
interface Props { interface Props {
tokenQuery: UseQueryResult<TokenInfo>; tokenQuery: UseQueryResult<TokenInfo>;
} }
...@@ -120,13 +119,16 @@ const TokenDetails = ({ tokenQuery }: Props) => { ...@@ -120,13 +119,16 @@ const TokenDetails = ({ tokenQuery }: Props) => {
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
isLoading={ tokenQuery.isPlaceholderData } isLoading={ tokenQuery.isPlaceholderData }
> >
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData }> <Skeleton isLoaded={ !tokenQuery.isPlaceholderData } w="100%" display="flex">
<Flex w="100%"> <TruncatedTextTooltip label={ totalSupplyValue || '0' }>
<Box whiteSpace="nowrap" overflow="hidden"> <Box overflow="hidden" textOverflow="ellipsis" flexShrink={ 0 } maxW="80%" whiteSpace="nowrap">
<HashStringShortenDynamic hash={ totalSupplyValue || '0' }/> { totalSupplyValue || '0' }
</Box> </Box>
<Box flexShrink={ 0 }> { symbol || '' }</Box> </TruncatedTextTooltip>
</Flex> <Box flexShrink={ 0 }> </Box>
<TruncatedTextTooltip label={ symbol || '' }>
<Box overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">{ symbol || '' }</Box>
</TruncatedTextTooltip>
</Skeleton> </Skeleton>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
......
...@@ -7,7 +7,6 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; ...@@ -7,7 +7,6 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -16,6 +15,7 @@ import Tag from 'ui/shared/chakra/Tag'; ...@@ -16,6 +15,7 @@ import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
type Props = TokenTransfer & { tokenId?: string; isLoading?: boolean }; type Props = TokenTransfer & { tokenId?: string; isLoading?: boolean };
...@@ -92,7 +92,13 @@ const TokenTransferListItem = ({ ...@@ -92,7 +92,13 @@ const TokenTransferListItem = ({
<Skeleton isLoaded={ !isLoading } color="text_secondary"> <Skeleton isLoaded={ !isLoading } color="text_secondary">
<span>{ value }</span> <span>{ value }</span>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading }>{ trimTokenSymbol(token.symbol) }</Skeleton> { token.symbol && (
<TruncatedTextTooltip label={ token.symbol }>
<Skeleton isLoaded={ !isLoading } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">
{ token.symbol }
</Skeleton>
</TruncatedTextTooltip>
) }
</Flex> </Flex>
) } ) }
{ 'token_id' in total && (token.type === 'ERC-721' || token.type === 'ERC-1155') && ( { 'token_id' in total && (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
......
import { Table, Tbody, Tr, Th } from '@chakra-ui/react'; import { Table, Tbody, Tr, Th, Box } from '@chakra-ui/react';
import React from 'react'; 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 trimTokenSymbol from 'lib/token/trimTokenSymbol';
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 TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TokenTransferTableItem from 'ui/token/TokenTransfer/TokenTransferTableItem'; import TokenTransferTableItem from 'ui/token/TokenTransfer/TokenTransferTableItem';
interface Props { interface Props {
...@@ -33,8 +33,13 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket ...@@ -33,8 +33,13 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket
<Th width="36px" px={ 0 }/> <Th width="36px" px={ 0 }/>
<Th width="218px" >To</Th> <Th width="218px" >To</Th>
{ (tokenType === 'ERC-721' || tokenType === 'ERC-1155') && <Th width="20%" isNumeric={ tokenType === 'ERC-721' }>Token ID</Th> } { (tokenType === 'ERC-721' || tokenType === 'ERC-1155') && <Th width="20%" isNumeric={ tokenType === 'ERC-721' }>Token ID</Th> }
{ (tokenType === 'ERC-20' || tokenType === 'ERC-1155') && { (tokenType === 'ERC-20' || tokenType === 'ERC-1155') && (
<Th width="20%" isNumeric whiteSpace="nowrap">Value { trimTokenSymbol(token?.symbol || '') }</Th> } <Th width="20%" isNumeric whiteSpace="nowrap">
<TruncatedTextTooltip label={ `Value ${ token?.symbol }` }>
<Box overflow="hidden" textOverflow="ellipsis">Value { token?.symbol }</Box>
</TruncatedTextTooltip>
</Th>
) }
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -15,13 +15,13 @@ type Props = { ...@@ -15,13 +15,13 @@ type Props = {
const TokensTable = ({ items, page, isLoading }: Props) => { const TokensTable = ({ items, page, isLoading }: Props) => {
return ( return (
<Table style={{ tableLayout: 'auto' }}> <Table>
<Thead top={ 80 }> <Thead top={ 80 }>
<Tr> <Tr>
<Th>Token</Th> <Th w="50%">Token</Th>
<Th isNumeric>Price</Th> <Th isNumeric w="15%">Price</Th>
<Th isNumeric>On-chain market cap</Th> <Th isNumeric w="20%">On-chain market cap</Th>
<Th isNumeric>Holders</Th> <Th isNumeric w="15%">Holders</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -53,7 +53,7 @@ const TokensTableItem = ({ ...@@ -53,7 +53,7 @@ const TokensTableItem = ({
> >
{ (page - 1) * PAGE_SIZE + index + 1 } { (page - 1) * PAGE_SIZE + index + 1 }
</Skeleton> </Skeleton>
<Box> <Box overflow="hidden">
<Flex alignItems="center"> <Flex alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> <TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading }/> <AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading }/>
......
...@@ -20,7 +20,7 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => { ...@@ -20,7 +20,7 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => {
const url = route({ pathname: '/token/[hash]/instance/[id]', query: { hash: token.address, id: tokenId } }); const url = route({ pathname: '/token/[hash]/instance/[id]', query: { hash: token.address, id: tokenId } });
return ( return (
<Flex alignItems="center" columnGap={ 3 } rowGap={ 2 } flexWrap="wrap"> <Flex alignItems="center" columnGap={ 3 } rowGap={ 2 } flexWrap={{ base: 'wrap', lg: 'nowrap' }}>
<Text fontWeight={ 500 } as="span">For { num } token ID:</Text> <Text fontWeight={ 500 } as="span">For { num } token ID:</Text>
<Box display="inline-flex" alignItems="center"> <Box display="inline-flex" alignItems="center">
<Icon as={ nftIcon } boxSize={ 6 } mr={ 1 }/> <Icon as={ nftIcon } boxSize={ 6 } mr={ 1 }/>
...@@ -29,7 +29,7 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => { ...@@ -29,7 +29,7 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => {
</Link> </Link>
</Box> </Box>
{ token.name ? ( { token.name ? (
<TokenSnippet data={ token } w="auto" logoSize={ 5 } columnGap={ 1 }/> <TokenSnippet data={ token } logoSize={ 5 } columnGap={ 1 }/>
) : ( ) : (
<AddressLink hash={ token.address } truncation="constant" type="token"/> <AddressLink hash={ token.address } truncation="constant" type="token"/>
) } ) }
......
...@@ -42,6 +42,7 @@ import LinkInternal from 'ui/shared/LinkInternal'; ...@@ -42,6 +42,7 @@ import LinkInternal from 'ui/shared/LinkInternal';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import RawInputData from 'ui/shared/RawInputData'; import RawInputData from 'ui/shared/RawInputData';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
import TxDetailsActions from 'ui/tx/details/TxDetailsActions'; import TxDetailsActions from 'ui/tx/details/TxDetailsActions';
...@@ -240,7 +241,7 @@ const TxDetails = () => { ...@@ -240,7 +241,7 @@ const TxDetails = () => {
{ toAddress ? ( { toAddress ? (
<> <>
{ data.to && data.to.hash ? ( { data.to && data.to.hash ? (
<Address alignItems="center"> <Address alignItems="center" flexShrink={ 0 } w={{ base: '100%', lg: 'auto' }}>
<AddressIcon address={ toAddress } isLoading={ isPlaceholderData }/> <AddressIcon address={ toAddress } isLoading={ isPlaceholderData }/>
<AddressLink type="address" ml={ 2 } hash={ toAddress.hash } isLoading={ isPlaceholderData }/> <AddressLink type="address" ml={ 2 } hash={ toAddress.hash } isLoading={ isPlaceholderData }/>
{ executionSuccessBadge } { executionSuccessBadge }
...@@ -248,7 +249,7 @@ const TxDetails = () => { ...@@ -248,7 +249,7 @@ const TxDetails = () => {
<CopyToClipboard text={ toAddress.hash } isLoading={ isPlaceholderData }/> <CopyToClipboard text={ toAddress.hash } isLoading={ isPlaceholderData }/>
</Address> </Address>
) : ( ) : (
<Flex width={{ base: '100%', lg: 'auto' }} whiteSpace="pre" alignItems="center"> <Flex width={{ base: '100%', lg: 'auto' }} whiteSpace="pre" alignItems="center" flexShrink={ 0 }>
<span>[Contract </span> <span>[Contract </span>
<AddressLink type="address" hash={ toAddress.hash }/> <AddressLink type="address" hash={ toAddress.hash }/>
<span> created]</span> <span> created]</span>
...@@ -257,7 +258,11 @@ const TxDetails = () => { ...@@ -257,7 +258,11 @@ const TxDetails = () => {
<CopyToClipboard text={ toAddress.hash }/> <CopyToClipboard text={ toAddress.hash }/>
</Flex> </Flex>
) } ) }
{ toAddress.name && <Text>{ toAddress.name }</Text> } { toAddress.name && (
<TruncatedTextTooltip label={ toAddress.name }>
<chakra.span overflow="hidden" textOverflow="ellipsis">{ toAddress.name }</chakra.span>
</TruncatedTextTooltip>
) }
{ addressToTags.length > 0 && ( { addressToTags.length > 0 && (
<Flex columnGap={ 3 }> <Flex columnGap={ 3 }>
{ addressToTags } { addressToTags }
......
...@@ -64,6 +64,9 @@ const TxDetailsAction = ({ action }: Props) => { ...@@ -64,6 +64,9 @@ const TxDetailsAction = ({ action }: Props) => {
columnGap={ 1 } columnGap={ 1 }
logoSize={ 5 } logoSize={ 5 }
isDisabled={ data.symbol0 === 'Ether' } isDisabled={ data.symbol0 === 'Ether' }
hideSymbol
maxW="200px"
flexShrink={ 0 }
/> />
<chakra.span color="text_secondary">{ type === 'swap' ? 'For' : 'And' }: </chakra.span> <chakra.span color="text_secondary">{ type === 'swap' ? 'For' : 'And' }: </chakra.span>
...@@ -75,6 +78,9 @@ const TxDetailsAction = ({ action }: Props) => { ...@@ -75,6 +78,9 @@ const TxDetailsAction = ({ action }: Props) => {
columnGap={ 1 } columnGap={ 1 }
logoSize={ 5 } logoSize={ 5 }
isDisabled={ data.symbol1 === 'Ether' } isDisabled={ data.symbol1 === 'Ether' }
hideSymbol
maxW="200px"
flexShrink={ 0 }
/> />
<chakra.span color="text_secondary">{ text1 } </chakra.span> <chakra.span color="text_secondary">{ text1 } </chakra.span>
...@@ -105,7 +111,6 @@ const TxDetailsAction = ({ action }: Props) => { ...@@ -105,7 +111,6 @@ const TxDetailsAction = ({ action }: Props) => {
columnGap={ 1 } columnGap={ 1 }
logoSize={ 5 } logoSize={ 5 }
rowGap={ 2 } rowGap={ 2 }
flexWrap="wrap"
/> />
<chakra.span> to </chakra.span> <chakra.span> to </chakra.span>
<AddressLink hash={ data.to } type="address" truncation="constant"/> <AddressLink hash={ data.to } type="address" truncation="constant"/>
......
...@@ -34,7 +34,7 @@ const TxDetailsActions = ({ actions }: Props) => { ...@@ -34,7 +34,7 @@ const TxDetailsActions = ({ actions }: Props) => {
> >
<Flex <Flex
flexDirection="column" flexDirection="column"
alignItems="flex-start" alignItems="stretch"
rowGap={ 5 } rowGap={ 5 }
w="100%" w="100%"
maxH="200px" maxH="200px"
......
...@@ -64,17 +64,18 @@ const TxDetailsTokenTransfer = ({ data }: Props) => { ...@@ -64,17 +64,18 @@ const TxDetailsTokenTransfer = ({ data }: Props) => {
return ( return (
<Flex <Flex
alignItems="center" alignItems="center"
flexWrap="wrap" flexWrap={{ base: 'wrap', lg: 'nowrap' }}
columnGap={ 3 } columnGap={ 3 }
rowGap={ 3 } rowGap={ 3 }
flexDir="row" flexDir="row"
w="100%"
> >
<Flex alignItems="center"> <Flex alignItems="center">
<AddressLink type="address" fontWeight="500" hash={ data.from.hash } truncation="constant"/> <AddressLink type="address" fontWeight="500" hash={ data.from.hash } truncation="constant"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/> <Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/>
<AddressLink type="address" fontWeight="500" hash={ data.to.hash } truncation="constant"/> <AddressLink type="address" fontWeight="500" hash={ data.to.hash } truncation="constant"/>
</Flex> </Flex>
<Flex flexDir="column" rowGap={ 5 }> <Flex flexDir="column" rowGap={ 5 } w="100%" overflow="hidden">
{ content } { content }
</Flex> </Flex>
</Flex> </Flex>
......
...@@ -51,6 +51,7 @@ const TxDetailsTokenTransfers = ({ data, txHash }: Props) => { ...@@ -51,6 +51,7 @@ const TxDetailsTokenTransfers = ({ data, txHash }: Props) => {
alignItems="flex-start" alignItems="flex-start"
rowGap={ 5 } rowGap={ 5 }
w="100%" w="100%"
overflow="hidden"
> >
{ items.slice(0, VISIBLE_ITEMS_NUM).map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) } { items.slice(0, VISIBLE_ITEMS_NUM).map((item, index) => <TxDetailsTokenTransfer key={ index } data={ item }/>) }
</Flex> </Flex>
......
...@@ -19,7 +19,7 @@ interface Props { ...@@ -19,7 +19,7 @@ interface Props {
const TxStateTable = ({ data, isLoading, top }: Props) => { const TxStateTable = ({ data, isLoading, top }: Props) => {
return ( return (
<Table variant="simple" minWidth="1000px" size="sm" w="auto"> <Table variant="simple" minWidth="1000px" size="sm" w="100%">
<Thead top={ top }> <Thead top={ top }>
<Tr> <Tr>
<Th width="140px">Type</Th> <Th width="140px">Type</Th>
......
...@@ -8,7 +8,6 @@ import appConfig from 'configs/app/config'; ...@@ -8,7 +8,6 @@ import appConfig from 'configs/app/config';
import { ZERO_ADDRESS } from 'lib/consts'; import { ZERO_ADDRESS } from 'lib/consts';
import { nbsp, space } from 'lib/html-entities'; import { nbsp, space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
...@@ -58,8 +57,16 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { ...@@ -58,8 +57,16 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
const changeSign = beforeBn.lte(afterBn) ? '+' : '-'; const changeSign = beforeBn.lte(afterBn) ? '+' : '-';
return { return {
before: <Skeleton isLoaded={ !isLoading } display="inline-block">{ beforeBn.toFormat() } { appConfig.network.currency.symbol }</Skeleton>, before: (
after: <Skeleton isLoaded={ !isLoading } display="inline-block">{ afterBn.toFormat() } { appConfig.network.currency.symbol }</Skeleton>, <Skeleton isLoaded={ !isLoading } wordBreak="break-all" display="inline-block">
{ beforeBn.toFormat() } { appConfig.network.currency.symbol }
</Skeleton>
),
after: (
<Skeleton isLoaded={ !isLoading } wordBreak="break-all" display="inline-block">
{ afterBn.toFormat() } { appConfig.network.currency.symbol }
</Skeleton>
),
change: ( change: (
<Skeleton isLoaded={ !isLoading } display="inline-block" color={ changeColor }> <Skeleton isLoaded={ !isLoading } display="inline-block" color={ changeColor }>
<span>{ changeSign }{ nbsp }{ differenceBn.abs().toFormat() }</span> <span>{ changeSign }{ nbsp }{ differenceBn.abs().toFormat() }</span>
...@@ -73,7 +80,7 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { ...@@ -73,7 +80,7 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
<AddressLink <AddressLink
type="token" type="token"
hash={ data.token.address } hash={ data.token.address }
alias={ trimTokenSymbol(data.token?.symbol || data.token.address) } alias={ data.token?.symbol || data.token.address }
isLoading={ isLoading } isLoading={ isLoading }
/> />
); );
......
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