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 {
blocks = 'blocks',
chartsTxs = 'charts-txs',
chartsMarket = 'charts-market',
indexBlocks='indexBlocks',
}
import { Text } from '@chakra-ui/react';
import type { TypographyProps } from '@chakra-ui/react';
import React from 'react';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
......@@ -6,12 +7,13 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
interface Props {
ts: string;
isEnabled?: boolean;
fontSize?: TypographyProps['fontSize'];
}
const BlockTimestamp = ({ ts, isEnabled }: Props) => {
const BlockTimestamp = ({ ts, isEnabled, fontSize }: Props) => {
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);
......@@ -7,7 +7,8 @@ import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config';
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 getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp';
......@@ -23,9 +24,7 @@ interface Props {
}
const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
const totalReward = data.rewards
?.map(({ reward }) => BigNumber(reward))
.reduce((result, item) => result.plus(item), ZERO) || ZERO;
const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0);
const txFees = BigNumber(data.tx_fees || 0);
......@@ -65,7 +64,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
</Box>
<Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Reward { appConfig.network.currency.symbol }</Text>
<Text variant="secondary">{ totalReward.div(WEI).toFixed() }</Text>
<Text variant="secondary">{ totalReward }</Text>
</Flex>
<Box>
<Text fontWeight={ 500 }>Burnt fees</Text>
......
......@@ -6,7 +6,8 @@ import React from 'react';
import type { Block } from 'types/api/block';
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 BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -20,9 +21,7 @@ interface Props {
}
const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
const totalReward = data.rewards
?.map(({ reward }) => BigNumber(reward))
.reduce((result, item) => result.plus(item), ZERO) || ZERO;
const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0);
const txFees = BigNumber(data.tx_fees || 0);
......@@ -61,7 +60,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<GasUsedToTargetRatio ml={ 2 } value={ data.gas_target_percentage || undefined }/>
</Flex>
</Td>
<Td fontSize="sm">{ totalReward.dividedBy(WEI).toFixed() }</Td>
<Td fontSize="sm">{ totalReward }</Td>
<Td fontSize="sm">
<Flex alignItems="center" columnGap={ 1 }>
<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 = () => {
const { data, isLoading, isError } = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ],
async() => await fetch(`/api/stats`),
async() => await fetch(`/api/index/stats`),
);
if (isError) {
......
import { Box, Heading } from '@chakra-ui/react';
import { Box, Heading, Flex } from '@chakra-ui/react';
import React from 'react';
import LatestBlocks from 'ui/home/LatestBlocks';
import Stats from 'ui/home/Stats';
import Page from 'ui/shared/Page/Page';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
......@@ -10,7 +11,6 @@ const Home = () => {
<Page hasSearch={ false }>
<Box
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%)"
borderRadius="24px"
padding={{ base: '24px 40px', lg: '48px' }}
......@@ -27,6 +27,10 @@ const Home = () => {
<SearchBar backgroundColor="white" isHomepage/>
</Box>
<Stats/>
<Flex mt={ 12 }>
<Box mr={ 12 }><LatestBlocks/></Box>
<Box><Heading as="h4" fontSize="18px" mb={ 8 }>Latest transactions</Heading></Box>
</Flex>
</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