Commit 327cee07 authored by tom's avatar tom

skeletons for blocks page

parent e938c242
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import Head from 'next/head'; import Head from 'next/head';
import React from 'react'; import React from 'react';
import getSeo from 'lib/next/blocks/getSeo'; import getSeo from 'lib/next/blocks/getSeo';
import Blocks from 'ui/pages/Blocks'; import Page from 'ui/shared/Page/Page';
const Blocks = dynamic(() => import('ui/pages/Blocks'), { ssr: false });
const BlockPage: NextPage = () => { const BlockPage: NextPage = () => {
const { title } = getSeo(); const { title } = getSeo();
...@@ -12,7 +15,9 @@ const BlockPage: NextPage = () => { ...@@ -12,7 +15,9 @@ const BlockPage: NextPage = () => {
<Head> <Head>
<title>{ title }</title> <title>{ title }</title>
</Head> </Head>
<Blocks/> <Page>
<Blocks/>
</Page>
</> </>
); );
}; };
......
import type { Block, BlocksResponse } from 'types/api/block';
import { ADDRESS_PARAMS } from './addressParams';
export const BLOCK_HASH = '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70'; export const BLOCK_HASH = '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70';
export const BLOCK: Block = {
base_fee_per_gas: '14',
burnt_fees: '92834504000000000',
burnt_fees_percentage: 42.2,
difficulty: '340282366920938463463374607431768211451',
extra_data: 'TODO',
gas_limit: '30000000',
gas_target_percentage: 55.79,
gas_used: '6631036',
gas_used_percentage: 22.10,
has_beacon_chain_withdrawals: null,
hash: BLOCK_HASH,
height: 8988736,
miner: ADDRESS_PARAMS,
nonce: '0x0000000000000000',
parent_hash: BLOCK_HASH,
priority_fee: '19241635454943109',
rewards: [
{
reward: '19241635454943109',
type: 'Validator Reward',
},
],
size: 46406,
state_root: 'TODO',
timestamp: '2023-05-12T19:29:12.000000Z',
total_difficulty: '10837812015930321201107455268036056402048391639',
tx_count: 142,
tx_fees: '19241635547777613',
type: 'block',
uncles_hashes: [],
};
export const BLOCKS: BlocksResponse = {
items: Array(50).fill(BLOCK),
next_page_params: {
block_number: 8988686,
items_count: 50,
},
};
...@@ -10,7 +10,7 @@ export interface Block { ...@@ -10,7 +10,7 @@ export interface Block {
tx_count: number; tx_count: number;
miner: AddressParam; miner: AddressParam;
size: number; size: number;
has_beacon_chain_withdrawals?: boolean; has_beacon_chain_withdrawals: boolean | null;
hash: string; hash: string;
parent_hash: string; parent_hash: string;
difficulty: string; difficulty: string;
......
import { Text } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import type { TypographyProps } from '@chakra-ui/react'; import type { TypographyProps } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
...@@ -7,13 +7,18 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; ...@@ -7,13 +7,18 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
interface Props { interface Props {
ts: string; ts: string;
isEnabled?: boolean; isEnabled?: boolean;
isLoading?: boolean;
fontSize?: TypographyProps['fontSize']; fontSize?: TypographyProps['fontSize'];
} }
const BlockTimestamp = ({ ts, isEnabled, fontSize }: Props) => { const BlockTimestamp = ({ ts, isEnabled, isLoading, fontSize }: Props) => {
const timeAgo = useTimeAgoIncrement(ts, isEnabled); const timeAgo = useTimeAgoIncrement(ts, isEnabled);
return <Text variant="secondary" fontWeight={ 400 } fontSize={ fontSize }>{ timeAgo }</Text>; return (
<Skeleton isLoaded={ !isLoading } color="text_secondary" fontWeight={ 400 } fontSize={ fontSize } display="inline-block">
<span>{ timeAgo }</span>
</Skeleton>
);
}; };
export default React.memo(BlockTimestamp); export default React.memo(BlockTimestamp);
...@@ -66,7 +66,7 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -66,7 +66,7 @@ const BlocksContent = ({ type, query }: Props) => {
topic: 'blocks:new_block', topic: 'blocks:new_block',
onSocketClose: handleSocketClose, onSocketClose: handleSocketClose,
onSocketError: handleSocketError, onSocketError: handleSocketError,
isDisabled: query.isLoading || query.isError || query.pagination.page !== 1, isDisabled: query.isPlaceholderData || query.isError || query.pagination.page !== 1,
}); });
useSocketMessage({ useSocketMessage({
channel, channel,
...@@ -78,10 +78,10 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -78,10 +78,10 @@ const BlocksContent = ({ type, query }: Props) => {
<> <>
{ socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> } { socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> }
<Show below="lg" key="content-mobile" ssr={ false }> <Show below="lg" key="content-mobile" ssr={ false }>
<BlocksList data={ query.data.items }/> <BlocksList data={ query.data.items } isLoading={ query.isPlaceholderData } page={ query.pagination.page }/>
</Show> </Show>
<Hide below="lg" key="content-desktop" ssr={ false }> <Hide below="lg" key="content-desktop" ssr={ false }>
<BlocksTable data={ query.data.items } top={ query.isPaginationVisible ? 80 : 0 } page={ query.pagination.page }/> <BlocksTable data={ query.data.items } top={ query.isPaginationVisible ? 80 : 0 } page={ query.pagination.page } isLoading={ query.isPlaceholderData }/>
</Hide> </Hide>
</> </>
) : null; ) : null;
...@@ -95,7 +95,7 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -95,7 +95,7 @@ const BlocksContent = ({ type, query }: Props) => {
return ( return (
<DataListDisplay <DataListDisplay
isError={ query.isError } isError={ query.isError }
isLoading={ query.isLoading } isLoading={ false }
items={ query.data?.items } items={ query.data?.items }
skeletonProps={{ skeletonDesktopColumns: [ '125px', '120px', '21%', '64px', '35%', '22%', '22%' ] }} skeletonProps={{ skeletonDesktopColumns: [ '125px', '120px', '21%', '64px', '35%', '22%', '22%' ] }}
emptyText="There are no blocks." emptyText="There are no blocks."
......
...@@ -8,14 +8,22 @@ import BlocksListItem from 'ui/blocks/BlocksListItem'; ...@@ -8,14 +8,22 @@ import BlocksListItem from 'ui/blocks/BlocksListItem';
interface Props { interface Props {
data: Array<Block>; data: Array<Block>;
isLoading: boolean;
page: number;
} }
const BlocksList = ({ data }: Props) => { const BlocksList = ({ data, isLoading, page }: Props) => {
return ( return (
<Box> <Box>
<AnimatePresence initial={ false }> <AnimatePresence initial={ false }>
{ /* TODO prop "enableTimeIncrement" should be set to false for second and later pages */ } { data.map((item, index) => (
{ data.map((item) => <BlocksListItem key={ item.height } data={ item } enableTimeIncrement/>) } <BlocksListItem
key={ item.height + (isLoading ? String(index) : '') }
data={ item }
isLoading={ isLoading }
enableTimeIncrement={ page === 1 && !isLoading }
/>
)) }
</AnimatePresence> </AnimatePresence>
</Box> </Box>
); );
......
import { Flex, Spinner, Text, Box, Icon, useColorModeValue } from '@chakra-ui/react'; import { Flex, Skeleton, Text, Box, Icon, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
...@@ -21,11 +21,11 @@ import Utilization from 'ui/shared/Utilization/Utilization'; ...@@ -21,11 +21,11 @@ import Utilization from 'ui/shared/Utilization/Utilization';
interface Props { interface Props {
data: Block; data: Block;
isPending?: boolean; isLoading?: boolean;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
} }
const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
const totalReward = getBlockTotalReward(data); const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0); const burntFees = BigNumber(data.burnt_fees || 0);
const txFees = BigNumber(data.tx_fees || 0); const txFees = BigNumber(data.tx_fees || 0);
...@@ -36,30 +36,35 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -36,30 +36,35 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<ListItemMobile rowGap={ 3 } key={ String(data.height) } isAnimated> <ListItemMobile rowGap={ 3 } key={ String(data.height) } isAnimated>
<Flex justifyContent="space-between" w="100%"> <Flex justifyContent="space-between" w="100%">
<Flex columnGap={ 2 } alignItems="center"> <Flex columnGap={ 2 } alignItems="center">
{ isPending && <Spinner size="sm"/> } <Skeleton isLoaded={ !isLoading } display="inline-block">
<LinkInternal <LinkInternal
fontWeight={ 600 } fontWeight={ 600 }
href={ route({ pathname: '/block/[height]', query: { height: data.type === 'reorg' ? String(data.hash) : String(data.height) } }) } href={ route({ pathname: '/block/[height]', query: { height: data.type === 'reorg' ? String(data.hash) : String(data.height) } }) }
> >
{ data.height } { data.height }
</LinkInternal> </LinkInternal>
</Skeleton>
</Flex> </Flex>
<BlockTimestamp ts={ data.timestamp } isEnabled={ enableTimeIncrement }/> <BlockTimestamp ts={ data.timestamp } isEnabled={ enableTimeIncrement } isLoading={ isLoading }/>
</Flex> </Flex>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Size</Text> <Text fontWeight={ 500 }>Size</Text>
<Text variant="secondary">{ data.size.toLocaleString() } bytes</Text> <Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary">
<span>{ data.size.toLocaleString() } bytes</span>
</Skeleton>
</Flex> </Flex>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>{ capitalize(getNetworkValidatorTitle()) }</Text> <Text fontWeight={ 500 }>{ capitalize(getNetworkValidatorTitle()) }</Text>
<AddressLink type="address" alias={ data.miner.name } hash={ data.miner.hash } truncation="constant"/> <AddressLink type="address" alias={ data.miner.name } hash={ data.miner.hash } truncation="constant" isLoading={ isLoading }/>
</Flex> </Flex>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Txn</Text> <Text fontWeight={ 500 }>Txn</Text>
{ data.tx_count > 0 ? ( { data.tx_count > 0 ? (
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: String(data.height), tab: 'txs' } }) }> <Skeleton isLoaded={ !isLoading } display="inline-block">
{ data.tx_count } <LinkInternal href={ route({ pathname: '/block/[height]', query: { height: String(data.height), tab: 'txs' } }) }>
</LinkInternal> { data.tx_count }
</LinkInternal>
</Skeleton>
) : ) :
<Text variant="secondary">{ data.tx_count }</Text> <Text variant="secondary">{ data.tx_count }</Text>
} }
...@@ -67,12 +72,14 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -67,12 +72,14 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Box> <Box>
<Text fontWeight={ 500 }>Gas used</Text> <Text fontWeight={ 500 }>Gas used</Text>
<Flex mt={ 2 }> <Flex mt={ 2 }>
<Text variant="secondary" mr={ 4 }>{ BigNumber(data.gas_used || 0).toFormat() }</Text> <Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary" mr={ 4 }>
<Utilization colorScheme="gray" value={ BigNumber(data.gas_used || 0).div(BigNumber(data.gas_limit)).toNumber() }/> <span>{ BigNumber(data.gas_used || 0).toFormat() }</span>
</Skeleton>
<Utilization colorScheme="gray" value={ BigNumber(data.gas_used || 0).div(BigNumber(data.gas_limit)).toNumber() } isLoading={ isLoading }/>
{ data.gas_target_percentage && ( { data.gas_target_percentage && (
<> <>
<TextSeparator color={ separatorColor } mx={ 1 }/> <TextSeparator color={ separatorColor } mx={ 1 }/>
<GasUsedToTargetRatio value={ data.gas_target_percentage }/> <GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isLoading }/>
</> </>
) } ) }
</Flex> </Flex>
...@@ -80,7 +87,9 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -80,7 +87,9 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
{ !appConfig.L2.isL2Network && ( { !appConfig.L2.isL2Network && (
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Reward { appConfig.network.currency.symbol }</Text> <Text fontWeight={ 500 }>Reward { appConfig.network.currency.symbol }</Text>
<Text variant="secondary">{ totalReward.toFixed() }</Text> <Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary">
<span>{ totalReward.toFixed() }</span>
</Skeleton>
</Flex> </Flex>
) } ) }
{ !appConfig.L2.isL2Network && ( { !appConfig.L2.isL2Network && (
...@@ -88,10 +97,14 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -88,10 +97,14 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Text fontWeight={ 500 }>Burnt fees</Text> <Text fontWeight={ 500 }>Burnt fees</Text>
<Flex columnGap={ 4 } mt={ 2 }> <Flex columnGap={ 4 } mt={ 2 }>
<Flex> <Flex>
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/> <Skeleton isLoaded={ !isLoading } boxSize={ 5 } display="inline-block">
<Text variant="secondary" ml={ 1 }>{ burntFees.div(WEI).toFixed() }</Text> <Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/>
</Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary" ml={ 1 }>
<span>{ burntFees.div(WEI).toFixed() }</span>
</Skeleton>
</Flex> </Flex>
<Utilization ml={ 4 } value={ burntFees.div(txFees).toNumber() }/> <Utilization ml={ 4 } value={ burntFees.div(txFees).toNumber() } isLoading={ isLoading }/>
</Flex> </Flex>
</Box> </Box>
) } ) }
......
...@@ -12,11 +12,12 @@ import { default as Thead } from 'ui/shared/TheadSticky'; ...@@ -12,11 +12,12 @@ import { default as Thead } from 'ui/shared/TheadSticky';
interface Props { interface Props {
data: Array<Block>; data: Array<Block>;
isLoading?: boolean;
top: number; top: number;
page: number; page: number;
} }
const BlocksTable = ({ data, top, page }: Props) => { const BlocksTable = ({ data, isLoading, top, page }: Props) => {
return ( return (
<Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }> <Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }>
...@@ -33,7 +34,14 @@ const BlocksTable = ({ data, top, page }: Props) => { ...@@ -33,7 +34,14 @@ const BlocksTable = ({ data, top, page }: Props) => {
</Thead> </Thead>
<Tbody> <Tbody>
<AnimatePresence initial={ false }> <AnimatePresence initial={ false }>
{ data.map((item) => <BlocksTableItem key={ item.height } data={ item } enableTimeIncrement={ page === 1 }/>) } { data.map((item, index) => (
<BlocksTableItem
key={ item.height + (isLoading ? String(index) : '') }
data={ item }
enableTimeIncrement={ page === 1 && !isLoading }
isLoading={ isLoading }
/>
)) }
</AnimatePresence> </AnimatePresence>
</Tbody> </Tbody>
</Table> </Table>
......
import { Tr, Td, Flex, Box, Icon, Tooltip, Spinner, useColorModeValue } from '@chakra-ui/react'; import { Tr, Td, Flex, Box, Icon, Tooltip, Skeleton, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
...@@ -19,17 +19,18 @@ import Utilization from 'ui/shared/Utilization/Utilization'; ...@@ -19,17 +19,18 @@ import Utilization from 'ui/shared/Utilization/Utilization';
interface Props { interface Props {
data: Block; data: Block;
isPending?: boolean; isLoading?: boolean;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
} }
const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
const totalReward = getBlockTotalReward(data); const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0); const burntFees = BigNumber(data.burnt_fees || 0);
const txFees = BigNumber(data.tx_fees || 0); const txFees = BigNumber(data.tx_fees || 0);
const separatorColor = useColorModeValue('gray.200', 'gray.700'); const separatorColor = useColorModeValue('gray.200', 'gray.700');
const burntFeesIconColor = useColorModeValue('gray.500', 'inherit'); const burntFeesIconColor = useColorModeValue('gray.500', 'inherit');
return ( return (
<Tr <Tr
as={ motion.tr } as={ motion.tr }
...@@ -41,57 +42,84 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -41,57 +42,84 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
> >
<Td fontSize="sm"> <Td fontSize="sm">
<Flex columnGap={ 2 } alignItems="center" mb={ 2 }> <Flex columnGap={ 2 } alignItems="center" mb={ 2 }>
{ isPending && <Spinner size="sm" flexShrink={ 0 }/> }
<Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations"> <Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations">
<LinkInternal <Skeleton isLoaded={ !isLoading } display="inline-block">
fontWeight={ 600 } <LinkInternal
href={ route({ pathname: '/block/[height]', query: { height: data.type === 'reorg' ? String(data.hash) : String(data.height) } }) } fontWeight={ 600 }
> href={ route({ pathname: '/block/[height]', query: { height: data.type === 'reorg' ? String(data.hash) : String(data.height) } }) }
{ data.height } >
</LinkInternal> { data.height }
</LinkInternal>
</Skeleton>
</Tooltip> </Tooltip>
</Flex> </Flex>
<BlockTimestamp ts={ data.timestamp } isEnabled={ enableTimeIncrement }/> <BlockTimestamp ts={ data.timestamp } isEnabled={ enableTimeIncrement } isLoading={ isLoading }/>
</Td>
<Td fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ data.size.toLocaleString() }
</Skeleton>
</Td> </Td>
<Td fontSize="sm">{ data.size.toLocaleString() }</Td>
<Td fontSize="sm"> <Td fontSize="sm">
<AddressLink type="address" alias={ data.miner.name } hash={ data.miner.hash } truncation="constant" display="inline-flex" maxW="100%"/> <AddressLink
type="address"
alias={ data.miner.name }
hash={ data.miner.hash }
truncation="constant"
display="inline-flex"
maxW="100%"
isLoading={ isLoading }
/>
</Td> </Td>
<Td isNumeric fontSize="sm"> <Td isNumeric fontSize="sm">
{ data.tx_count > 0 ? ( { data.tx_count > 0 ? (
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height: String(data.height), tab: 'txs' } }) }> <Skeleton isLoaded={ !isLoading } display="inline-block">
{ data.tx_count } <LinkInternal href={ route({ pathname: '/block/[height]', query: { height: String(data.height), tab: 'txs' } }) }>
</LinkInternal> { data.tx_count }
</LinkInternal>
</Skeleton>
) : data.tx_count } ) : data.tx_count }
</Td> </Td>
{ !appConfig.L2.isL2Network && ( { !appConfig.L2.isL2Network && (
<Td fontSize="sm"> <Td fontSize="sm">
<Box>{ BigNumber(data.gas_used || 0).toFormat() }</Box> <Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<Flex mt={ 2 }> <Flex mt={ 2 }>
<Tooltip label="Gas Used %"> <Tooltip label={ isLoading ? undefined : 'Gas Used %' }>
<Box> <Box>
<Utilization colorScheme="gray" value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() }/> <Utilization
colorScheme="gray"
value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() }
isLoading={ isLoading }
/>
</Box> </Box>
</Tooltip> </Tooltip>
{ data.gas_target_percentage && ( { data.gas_target_percentage && (
<> <>
<TextSeparator color={ separatorColor } mx={ 1 }/> <TextSeparator color={ separatorColor } mx={ 1 }/>
<GasUsedToTargetRatio value={ data.gas_target_percentage }/> <GasUsedToTargetRatio value={ data.gas_target_percentage } isLoading={ isLoading }/>
</> </>
) } ) }
</Flex> </Flex>
</Td> </Td>
) } ) }
<Td fontSize="sm">{ totalReward.toFixed(8) }</Td> <Td fontSize="sm">
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ totalReward.toFixed(8) }
</Skeleton>
</Td>
{ !appConfig.L2.isL2Network && ( { !appConfig.L2.isL2Network && (
<Td fontSize="sm"> <Td fontSize="sm">
<Flex alignItems="center" columnGap={ 1 }> <Flex alignItems="center" columnGap={ 1 }>
<Icon as={ flameIcon } boxSize={ 5 } color={ burntFeesIconColor }/> <Skeleton isLoaded={ !isLoading } boxSize={ 5 }>
{ burntFees.dividedBy(WEI).toFixed(8) } <Icon as={ flameIcon } boxSize={ 5 } color={ burntFeesIconColor }/>
</Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block">
{ burntFees.dividedBy(WEI).toFixed(8) }
</Skeleton>
</Flex> </Flex>
<Tooltip label="Burnt fees / Txn fees * 100%"> <Tooltip label={ isLoading ? undefined : 'Burnt fees / Txn fees * 100%' }>
<Box w="min-content"> <Box w="min-content">
<Utilization mt={ 2 } value={ burntFees.div(txFees).toNumber() }/> <Utilization mt={ 2 } value={ burntFees.div(txFees).toNumber() } isLoading={ isLoading }/>
</Box> </Box>
</Tooltip> </Tooltip>
</Td> </Td>
......
...@@ -6,9 +6,9 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; ...@@ -6,9 +6,9 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { BLOCKS } from 'stubs/block';
import BlocksContent from 'ui/blocks/BlocksContent'; import BlocksContent from 'ui/blocks/BlocksContent';
import BlocksTabSlot from 'ui/blocks/BlocksTabSlot'; import BlocksTabSlot from 'ui/blocks/BlocksTabSlot';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
...@@ -32,6 +32,9 @@ const BlocksPageContent = () => { ...@@ -32,6 +32,9 @@ const BlocksPageContent = () => {
const blocksQuery = useQueryWithPages({ const blocksQuery = useQueryWithPages({
resourceName: 'blocks', resourceName: 'blocks',
filters: { type }, filters: { type },
options: {
placeholderData: BLOCKS,
},
}); });
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
...@@ -41,7 +44,7 @@ const BlocksPageContent = () => { ...@@ -41,7 +44,7 @@ const BlocksPageContent = () => {
]; ];
return ( return (
<Page> <>
<PageTitle text="Blocks" withTextAd/> <PageTitle text="Blocks" withTextAd/>
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
...@@ -49,7 +52,7 @@ const BlocksPageContent = () => { ...@@ -49,7 +52,7 @@ const BlocksPageContent = () => {
rightSlot={ <BlocksTabSlot pagination={ blocksQuery.pagination } isPaginationVisible={ blocksQuery.isPaginationVisible }/> } rightSlot={ <BlocksTabSlot pagination={ blocksQuery.pagination } isPaginationVisible={ blocksQuery.isPaginationVisible }/> }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
/> />
</Page> </>
); );
}; };
......
import { Text, Tooltip } from '@chakra-ui/react'; import { Skeleton, Tooltip } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
type Props = { type Props = {
value: number; value: number;
isLoading?: boolean;
} }
const GasUsedToTargetRatio = ({ value }: Props) => { const GasUsedToTargetRatio = ({ value, isLoading }: Props) => {
return ( return (
<Tooltip label="% of Gas Target"> <Tooltip label="% of Gas Target">
<Text variant="secondary"> <Skeleton color="text_secondary" isLoaded={ !isLoading }>
{ (value > 0 ? '+' : '') + value.toLocaleString(undefined, { maximumFractionDigits: 2 }) }% <span>{ (value > 0 ? '+' : '') + value.toLocaleString(undefined, { maximumFractionDigits: 2 }) }%</span>
</Text> </Skeleton>
</Tooltip> </Tooltip>
); );
}; };
......
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