Commit ad71c2b3 authored by tom's avatar tom

prev next block

parent ac627348
...@@ -5,7 +5,7 @@ import type { Block } from 'types/api/block'; ...@@ -5,7 +5,7 @@ import type { Block } from 'types/api/block';
export default function getBlockReward(block: Block) { export default function getBlockReward(block: Block) {
const txFees = utils.parseUnits(block.tx_fees || '0', 'wei'); const txFees = utils.parseUnits(block.tx_fees || '0', 'wei');
const burntFees = utils.parseUnits(String(block.burnt_fees || '0'), 'wei'); const burntFees = utils.parseUnits(String(block.burnt_fees || '0'), 'wei');
const totalReward = utils.parseUnits(String(block.rewards?.find(({ type }) => type === 'Miner Reward' || type === 'Validator Reward')?.reward) || '0', 'wei'); const totalReward = utils.parseUnits(String(block.rewards?.find(({ type }) => type === 'Miner Reward' || type === 'Validator Reward')?.reward || '0'), 'wei');
const staticReward = totalReward.sub(txFees).add(burntFees); const staticReward = totalReward.sub(txFees).add(burntFees);
return { return {
......
...@@ -136,6 +136,10 @@ const variantSubtle = defineStyle((props) => { ...@@ -136,6 +136,10 @@ const variantSubtle = defineStyle((props) => {
color: mode('blackAlpha.800', 'whiteAlpha.800')(props), color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
_hover: { _hover: {
color: 'blue.400', color: 'blue.400',
_disabled: {
color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
bg: mode('blackAlpha.200', 'whiteAlpha.200')(props),
},
}, },
}; };
} }
......
import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react'; import { Grid, GridItem, Text, Icon, Link, Box, Tooltip, Alert } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { utils } from 'ethers'; import { utils } from 'ethers';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -11,6 +11,7 @@ import clockIcon from 'icons/clock.svg'; ...@@ -11,6 +11,7 @@ import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockReward from 'lib/block/getBlockReward'; import getBlockReward from 'lib/block/getBlockReward';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import type { ErrorType } from 'lib/hooks/useFetch';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useNetwork from 'lib/hooks/useNetwork'; import useNetwork from 'lib/hooks/useNetwork';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
...@@ -33,7 +34,7 @@ const BlockDetails = () => { ...@@ -33,7 +34,7 @@ const BlockDetails = () => {
const network = useNetwork(); const network = useNetwork();
const fetch = useFetch(); const fetch = useFetch();
const { data, isLoading, isError } = useQuery<unknown, unknown, Block>( const { data, isLoading, isError, error } = useQuery<unknown, ErrorType<{ status: number }>, Block>(
[ 'block', router.query.id ], [ 'block', router.query.id ],
async() => await fetch(`/api/blocks/${ router.query.id }`), async() => await fetch(`/api/blocks/${ router.query.id }`),
{ {
...@@ -49,12 +50,19 @@ const BlockDetails = () => { ...@@ -49,12 +50,19 @@ const BlockDetails = () => {
}); });
}, []); }, []);
const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => {
const increment = direction === 'next' ? +1 : -1;
const url = link('block_index', { id: String(Number(router.query.id) + increment) });
router.push(url, undefined, { shallow: true });
}, [ link, router ]);
if (isLoading) { if (isLoading) {
return <BlockDetailsSkeleton/>; return <BlockDetailsSkeleton/>;
} }
if (isError) { if (isError) {
return <DataFetchAlert/>; const is404 = error?.error?.status === 404;
return is404 ? <Alert>This block has not been processed yet.</Alert> : <DataFetchAlert/>;
} }
const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>; const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>;
...@@ -67,7 +75,13 @@ const BlockDetails = () => { ...@@ -67,7 +75,13 @@ const BlockDetails = () => {
hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain." hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain."
> >
{ data.height } { data.height }
<PrevNext ml={ 6 }/> <PrevNext
ml={ 6 }
onClick={ handlePrevNextClick }
prevLabel="View previous block"
nextLabel="View next block"
isPrevDisabled={ router.query.id === '0' }
/>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Size" title="Size"
...@@ -235,14 +249,16 @@ const BlockDetails = () => { ...@@ -235,14 +249,16 @@ const BlockDetails = () => {
</Box> </Box>
<CopyToClipboard text={ data.hash }/> <CopyToClipboard text={ data.hash }/>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem { data.height > 0 && (
title="Parent hash" <DetailsInfoItem
hint="The hash of the block from which this block was generated." title="Parent hash"
flexWrap="nowrap" hint="The hash of the block from which this block was generated."
> flexWrap="nowrap"
<AddressLink hash={ data.parent_hash } type="block" id={ String(data.height - 1) }/> >
<CopyToClipboard text={ data.parent_hash }/> <AddressLink hash={ data.parent_hash } type="block" id={ String(data.height - 1) }/>
</DetailsInfoItem> <CopyToClipboard text={ data.parent_hash }/>
</DetailsInfoItem>
) }
{ /* api doesn't support state root yet */ } { /* api doesn't support state root yet */ }
{ /* <DetailsInfoItem { /* <DetailsInfoItem
title="State root" title="State root"
......
import { Box, Icon, IconButton, chakra } from '@chakra-ui/react'; import { Box, Icon, IconButton, chakra, Tooltip } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import eastArrow from 'icons/arrows/east-mini.svg'; import eastArrow from 'icons/arrows/east-mini.svg';
interface Props { interface Props {
className?: string; className?: string;
onClick: (direction: 'prev' | 'next') => void;
prevLabel?: string;
nextLabel?: string;
isPrevDisabled?: boolean;
isNextDisabled?: boolean;
} }
const PrevNext = ({ className }: Props) => { const PrevNext = ({ className, onClick, prevLabel, nextLabel, isPrevDisabled, isNextDisabled }: Props) => {
const handelPrevClick = React.useCallback(() => {
onClick('prev');
}, [ onClick ]);
const handelNextClick = React.useCallback(() => {
onClick('next');
}, [ onClick ]);
return ( return (
<Box className={ className }> <Box className={ className }>
<IconButton <Tooltip label={ prevLabel }>
aria-label="prev" <IconButton
icon={ <Icon as={ eastArrow } boxSize={ 6 }/> } aria-label="prev"
h={ 6 } icon={ <Icon as={ eastArrow } boxSize={ 6 }/> }
borderRadius="sm" h={ 6 }
variant="subtle" borderRadius="sm"
colorScheme="gray" variant="subtle"
/> colorScheme="gray"
<IconButton onClick={ handelPrevClick }
aria-label="next" disabled={ isPrevDisabled }
icon={ <Icon as={ eastArrow }boxSize={ 6 } transform="rotate(180deg)"/> } />
h={ 6 } </Tooltip>
borderRadius="sm" <Tooltip label={ nextLabel }>
variant="subtle" <IconButton
colorScheme="gray" aria-label="next"
ml="10px" icon={ <Icon as={ eastArrow }boxSize={ 6 } transform="rotate(180deg)"/> }
/> h={ 6 }
borderRadius="sm"
variant="subtle"
colorScheme="gray"
ml="10px"
onClick={ handelNextClick }
disabled={ isNextDisabled }
/>
</Tooltip>
</Box> </Box>
); );
}; };
......
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