Commit 73e75e0e authored by isstuev's avatar isstuev

index blocks

parent 7f9837b0
import BigNumber from 'bignumber.js';
import type { Block } from 'types/api/block';
import { WEI, ZERO } from 'lib/consts';
export default function getBlockTotalReward(block: Block) {
const totalReward = block.rewards
?.map(({ reward }) => BigNumber(reward))
.reduce((result, item) => result.plus(item), ZERO) || ZERO;
return totalReward.div(WEI).toFixed();
}
import handler from 'lib/api/handler';
const getUrl = () => '/v2/main-page/blocks';
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
...@@ -14,4 +14,5 @@ export enum QueryKeys { ...@@ -14,4 +14,5 @@ export enum QueryKeys {
blocks = 'blocks', blocks = 'blocks',
chartsTxs = 'charts-txs', chartsTxs = 'charts-txs',
chartsMarket = 'charts-market', chartsMarket = 'charts-market',
indexBlocks='indexBlocks',
} }
import { Text } from '@chakra-ui/react'; import { Text } from '@chakra-ui/react';
import type { TypographyProps } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
...@@ -6,12 +7,13 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; ...@@ -6,12 +7,13 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
interface Props { interface Props {
ts: string; ts: string;
isEnabled?: boolean; isEnabled?: boolean;
fontSize?: TypographyProps['fontSize'];
} }
const BlockTimestamp = ({ ts, isEnabled }: Props) => { const BlockTimestamp = ({ ts, isEnabled, fontSize }: Props) => {
const timeAgo = useTimeAgoIncrement(ts, isEnabled); const timeAgo = useTimeAgoIncrement(ts, isEnabled);
return <Text variant="secondary" fontWeight={ 400 }>{ timeAgo }</Text>; return <Text variant="secondary" fontWeight={ 400 } fontSize={ fontSize }>{ timeAgo }</Text>;
}; };
export default React.memo(BlockTimestamp); export default React.memo(BlockTimestamp);
...@@ -7,7 +7,8 @@ import type { Block } from 'types/api/block'; ...@@ -7,7 +7,8 @@ import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import { WEI, ZERO } from 'lib/consts'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import link from 'lib/link/link'; import link from 'lib/link/link';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
...@@ -23,9 +24,7 @@ interface Props { ...@@ -23,9 +24,7 @@ interface Props {
} }
const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
const totalReward = data.rewards const totalReward = getBlockTotalReward(data);
?.map(({ reward }) => BigNumber(reward))
.reduce((result, item) => result.plus(item), ZERO) || ZERO;
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);
...@@ -65,7 +64,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -65,7 +64,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
</Box> </Box>
<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.div(WEI).toFixed() }</Text> <Text variant="secondary">{ totalReward }</Text>
</Flex> </Flex>
<Box> <Box>
<Text fontWeight={ 500 }>Burnt fees</Text> <Text fontWeight={ 500 }>Burnt fees</Text>
......
...@@ -6,7 +6,8 @@ import React from 'react'; ...@@ -6,7 +6,8 @@ import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import { WEI, ZERO } from 'lib/consts'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts';
import link from 'lib/link/link'; import link from 'lib/link/link';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -20,9 +21,7 @@ interface Props { ...@@ -20,9 +21,7 @@ interface Props {
} }
const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
const totalReward = data.rewards const totalReward = getBlockTotalReward(data);
?.map(({ reward }) => BigNumber(reward))
.reduce((result, item) => result.plus(item), ZERO) || ZERO;
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);
...@@ -61,7 +60,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -61,7 +60,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<GasUsedToTargetRatio ml={ 2 } value={ data.gas_target_percentage || undefined }/> <GasUsedToTargetRatio ml={ 2 } value={ data.gas_target_percentage || undefined }/>
</Flex> </Flex>
</Td> </Td>
<Td fontSize="sm">{ totalReward.dividedBy(WEI).toFixed() }</Td> <Td fontSize="sm">{ totalReward }</Td>
<Td fontSize="sm"> <Td fontSize="sm">
<Flex alignItems="center" columnGap={ 1 }> <Flex alignItems="center" columnGap={ 1 }>
<Icon as={ flameIcon } boxSize={ 5 } color={ useColorModeValue('gray.500', 'inherit') }/> <Icon as={ flameIcon } boxSize={ 5 } color={ useColorModeValue('gray.500', 'inherit') }/>
......
import { Box, Heading, Link, Text, VStack, Skeleton, useColorModeValue } from '@chakra-ui/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { AnimatePresence } from 'framer-motion';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Block } from 'types/api/block';
import { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch';
import { nbsp } from 'lib/html-entities';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import LatestBlocksItem from './LatestBlocksItem';
const LatestBlocks = () => {
const fetch = useFetch();
const { data, isLoading, isError } = useQuery<unknown, unknown, Array<Block>>(
[ QueryKeys.indexBlocks ],
async() => await fetch(`/api/index/blocks`),
);
const queryClient = useQueryClient();
const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => {
queryClient.setQueryData([ QueryKeys.indexBlocks ], (prevData: Array<Block> | undefined) => {
const newData = prevData ? [ ...prevData ] : [];
return [ payload.block, ...newData ].slice(0, 4);
});
}, [ queryClient ]);
const channel = useSocketChannel({
topic: 'blocks:new_block',
isDisabled: isLoading || isError,
});
useSocketMessage({
channel,
event: 'new_block',
handler: handleNewBlockMessage,
});
let content;
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
if (isLoading) {
content = (
<>
<Skeleton w="100%" h={ 6 } mb={ 9 }/>
{ Array.from(Array(4)).map((item, index) => {
return (
<Box
key={ index }
width="100%"
borderRadius="12px"
border="1px solid"
borderColor={ borderColor }
p={ 6 }
mb={ 6 }
>
<Skeleton w="100%" h="30px" mb={ 3 }/>
<Skeleton w="100%" h="21px" mb={ 2 }/>
<Skeleton w="100%" h="21px" mb={ 2 }/>
<Skeleton w="100%" h="21px"/>
</Box>
);
}) }
</>
);
}
if (isError) {
// ???
content = null;
}
if (data) {
content = (
<>
<Box mb={ 9 }>
<Text as="span" fontSize="sm">
Network utilization:{ nbsp }
</Text>
{ /* Not implemented in API yet */ }
<Text as="span" fontSize="sm" color="blue.400" fontWeight={ 700 }>
43.8%
</Text>
</Box>
<VStack spacing={ 6 } mb={ 6 }>
<AnimatePresence initial={ false }>
{ data.map((block => <LatestBlocksItem key={ block.height } block={ block }/>)) }
</AnimatePresence>
</VStack>
</>
);
}
return (
<Box width="280px">
<Heading as="h4" fontSize="18px" mb={ 8 }>Latest Blocks</Heading>
{ content }
<Link fontSize="sm">View all blocks</Link>
</Box>
);
};
export default LatestBlocks;
import {
Box,
Flex,
Grid,
GridItem,
HStack,
Icon,
Link,
Text,
useColorModeValue,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import React from 'react';
import type { Block } from 'types/api/block';
import blockIcon from 'icons/block.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import link from 'lib/link/link';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
type Props = {
block: Block;
}
const LatestBlocksItem = ({ block }: Props) => {
const totalReward = getBlockTotalReward(block);
return (
<Box
as={ motion.div }
initial={{ opacity: 0, scale: 0.97 }}
animate={{ opacity: 1, scale: 1 }}
transitionDuration="normal"
transitionTimingFunction="linear"
width="100%"
borderRadius="12px"
border="1px solid"
borderColor={ useColorModeValue('gray.200', 'whiteAlpha.200') }
p={ 6 }
>
<Flex justifyContent="space-between" alignItems="center" mb={ 3 }>
<HStack spacing={ 2 }>
<Icon as={ blockIcon } boxSize="30px" color={ useColorModeValue('blue.600', 'blue.300') }/>
<Link
href={ link('block', { id: String(block.height) }) }
fontSize="xl"
fontWeight="500"
>
{ block.height }
</Link>
</HStack>
{ /* <Text fontSize="sm" variant="secondary">{ block.timestamp }</Text> */ }
<BlockTimestamp ts={ block.timestamp } isEnabled fontSize="sm"/>
</Flex>
<Grid gridGap={ 2 } templateColumns="auto minmax(0, 1fr)" fontSize="sm">
<GridItem>Txn</GridItem>
<GridItem><Text variant="secondary">{ block.tx_count }</Text></GridItem>
{ totalReward !== '0' && (
<>
<GridItem>Reward</GridItem>
<GridItem><Text variant="secondary">{ totalReward }</Text></GridItem>
</>
) }
<GridItem>Miner</GridItem>
<GridItem><AddressLink alias={ block.miner.name } hash={ block.miner.hash } truncation="constant" maxW="100%"/></GridItem>
</Grid>
</Box>
);
};
export default LatestBlocksItem;
...@@ -18,7 +18,7 @@ const Stats = () => { ...@@ -18,7 +18,7 @@ const Stats = () => {
const { data, isLoading, isError } = useQuery<unknown, unknown, Stats>( const { data, isLoading, isError } = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ], [ QueryKeys.stats ],
async() => await fetch(`/api/stats`), async() => await fetch(`/api/index/stats`),
); );
if (isError) { if (isError) {
......
import { Box, Heading } from '@chakra-ui/react'; import { Box, Heading, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import LatestBlocks from 'ui/home/LatestBlocks';
import Stats from 'ui/home/Stats'; import Stats from 'ui/home/Stats';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import SearchBar from 'ui/snippets/searchBar/SearchBar'; import SearchBar from 'ui/snippets/searchBar/SearchBar';
...@@ -10,7 +11,6 @@ const Home = () => { ...@@ -10,7 +11,6 @@ const Home = () => {
<Page hasSearch={ false }> <Page hasSearch={ false }>
<Box <Box
w="100%" w="100%"
// h="236px"
backgroundImage="radial-gradient(farthest-corner at 0 0, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%)" backgroundImage="radial-gradient(farthest-corner at 0 0, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%)"
borderRadius="24px" borderRadius="24px"
padding={{ base: '24px 40px', lg: '48px' }} padding={{ base: '24px 40px', lg: '48px' }}
...@@ -27,6 +27,10 @@ const Home = () => { ...@@ -27,6 +27,10 @@ const Home = () => {
<SearchBar backgroundColor="white" isHomepage/> <SearchBar backgroundColor="white" isHomepage/>
</Box> </Box>
<Stats/> <Stats/>
<Flex mt={ 12 }>
<Box mr={ 12 }><LatestBlocks/></Box>
<Box><Heading as="h4" fontSize="18px" mb={ 8 }>Latest transactions</Heading></Box>
</Flex>
</Page> </Page>
); );
}; };
......
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