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

trim block height if it is too long (#1033)

Fixes #1023
parent 940af88e
import { Skeleton } from '@chakra-ui/react';
import type { TypographyProps } from '@chakra-ui/react';
import { Skeleton, chakra } from '@chakra-ui/react';
import React from 'react';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
......@@ -8,17 +7,17 @@ interface Props {
ts: string;
isEnabled?: boolean;
isLoading?: boolean;
fontSize?: TypographyProps['fontSize'];
className?: string;
}
const BlockTimestamp = ({ ts, isEnabled, isLoading, fontSize }: Props) => {
const BlockTimestamp = ({ ts, isEnabled, isLoading, className }: Props) => {
const timeAgo = useTimeAgoIncrement(ts, isEnabled);
return (
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight={ 400 } fontSize={ fontSize } display="inline-block">
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight={ 400 } className={ className } display="inline-block">
<span>{ timeAgo }</span>
</Skeleton>
);
};
export default React.memo(BlockTimestamp);
export default React.memo(chakra(BlockTimestamp));
......@@ -38,6 +38,30 @@ test('default view +@mobile +@dark-mode', async({ mount, page }) => {
await expect(component).toHaveScreenshot();
});
test('with long block height', async({ mount, page }) => {
await page.route(STATS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await page.route(BLOCKS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify([
{
...blockMock.base,
height: 123456789012345,
},
]),
}));
const component = await mount(
<TestApp>
<LatestBlocks/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test.describe('socket', () => {
test.describe.configure({ mode: 'serial' });
......
......@@ -2,11 +2,9 @@ import {
Box,
Flex,
Grid,
HStack,
Skeleton,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react';
import type { Block } from 'types/api/block';
......@@ -17,7 +15,6 @@ import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon';
import LinkInternal from 'ui/shared/LinkInternal';
type Props = {
block: Block;
......@@ -40,22 +37,29 @@ const LatestBlocksItem = ({ block, h, isLoading }: Props) => {
p={ 6 }
h={ `${ h }px` }
minWidth={{ base: '100%', lg: '280px' }}
w="100%"
>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
<HStack spacing={ 2 }>
<Icon as={ blockIcon } boxSize="30px" color="link" isLoading={ isLoading } borderRadius="base"/>
<LinkInternal
href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(block.height) } }) }
fontSize="xl"
fontWeight="500"
isLoading={ isLoading }
>
<Skeleton isLoaded={ !isLoading }>
{ block.height }
</Skeleton>
</LinkInternal>
</HStack>
<BlockTimestamp ts={ block.timestamp } isEnabled={ !isLoading } isLoading={ isLoading } fontSize="sm"/>
<Flex alignItems="center" overflow="hidden" w="100%" mb={ 3 }>
<Icon as={ blockIcon } boxSize="30px" color="link" isLoading={ isLoading } borderRadius="base"/>
<AddressLink
isLoading={ isLoading }
type="block"
hash={ String(block.height) }
blockHeight={ String(block.height) }
fontSize="xl"
fontWeight="500"
ml={ 2 }
mr="auto"
tailLength={ 2 }
/>
<BlockTimestamp
ts={ block.timestamp }
isEnabled={ !isLoading }
isLoading={ isLoading }
fontSize="sm"
flexShrink={ 0 }
ml={ 2 }
/>
</Flex>
<Grid gridGap={ 2 } templateColumns="auto minmax(0, 1fr)" fontSize="sm">
<Skeleton isLoaded={ !isLoading }>Txn</Skeleton>
......
......@@ -23,9 +23,10 @@ interface Props {
hash: string;
fontWeight?: string | number;
isTooltipDisabled?: boolean;
tailLength?: number;
}
const HashStringShortenDynamic = ({ hash, fontWeight = '400', isTooltipDisabled }: Props) => {
const HashStringShortenDynamic = ({ hash, fontWeight = '400', isTooltipDisabled, tailLength = TAIL_LENGTH }: Props) => {
const elementRef = useRef<HTMLSpanElement>(null);
const [ displayedString, setDisplayedString ] = React.useState(hash);
......@@ -48,9 +49,9 @@ const HashStringShortenDynamic = ({ hash, fontWeight = '400', isTooltipDisabled
const parentWidth = getWidth(parent);
if (getWidth(shadowEl) > parentWidth) {
const tail = hash.slice(-TAIL_LENGTH);
const tail = hash.slice(-tailLength);
let leftI = HEAD_MIN_LENGTH;
let rightI = hash.length - TAIL_LENGTH;
let rightI = hash.length - tailLength;
while (rightI - leftI > 1) {
const medI = ((rightI - leftI) % 2) ? leftI + (rightI - leftI + 1) / 2 : leftI + (rightI - leftI) / 2;
......@@ -68,7 +69,7 @@ const HashStringShortenDynamic = ({ hash, fontWeight = '400', isTooltipDisabled
}
parent.removeChild(shadowEl);
}, [ hash ]);
}, [ hash, tailLength ]);
// we want to do recalculation when isFontFaceLoaded flag is changed
// but we don't want to create more resize event listeners
......
......@@ -20,6 +20,7 @@ type CommonProps = {
isLoading?: boolean;
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
query?: Record<string, string>;
tailLength?: number;
}
type AddressTokenTxProps = {
......@@ -78,7 +79,7 @@ const AddressLink = (props: Props) => {
case 'constant':
return <HashStringShorten hash={ hash } isTooltipDisabled={ isMobile }/>;
case 'dynamic':
return <HashStringShortenDynamic hash={ hash } fontWeight={ fontWeight } isTooltipDisabled={ isMobile }/>;
return <HashStringShortenDynamic hash={ hash } fontWeight={ fontWeight } isTooltipDisabled={ isMobile } tailLength={ props.tailLength }/>;
case 'none':
return <span>{ hash }</span>;
}
......
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