Files
explore/ui/home/LatestBlocks.tsx
tom goriunovandGitHub 24eb4fc014 API resource refactoring (#2699)
* add prefix to API resources

* fix pw tests

* refactor config

* remove unnecessary prefixes in resource names

* fixes

* update screenshots
2025-05-01 15:22:38 +02:00

131 lines
4.2 KiB
TypeScript

import { chakra, Box, Flex, Text, VStack } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useInitialList from 'lib/hooks/useInitialList';
import useIsMobile from 'lib/hooks/useIsMobile';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import { BLOCK } from 'stubs/block';
import { HOMEPAGE_STATS } from 'stubs/stats';
import { Heading } from 'toolkit/chakra/heading';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { nbsp } from 'toolkit/utils/htmlEntities';
import LatestBlocksItem from './LatestBlocksItem';
const LatestBlocks = () => {
const isMobile = useIsMobile();
// const blocksMaxCount = isMobile ? 2 : 3;
let blocksMaxCount: number;
if (config.features.rollup.isEnabled || config.UI.views.block.hiddenFields?.total_reward) {
blocksMaxCount = isMobile ? 4 : 5;
} else {
blocksMaxCount = isMobile ? 2 : 3;
}
const { data, isPlaceholderData, isError } = useApiQuery('general:homepage_blocks', {
queryOptions: {
placeholderData: Array(blocksMaxCount).fill(BLOCK),
},
});
const initialList = useInitialList({
data: data ?? [],
idFn: (block) => block.height,
enabled: !isPlaceholderData,
});
const queryClient = useQueryClient();
const statsQueryResult = useApiQuery('general:stats', {
queryOptions: {
refetchOnMount: false,
placeholderData: HOMEPAGE_STATS,
},
});
const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => {
queryClient.setQueryData(getResourceKey('general:homepage_blocks'), (prevData: Array<Block> | undefined) => {
const newData = prevData ? [ ...prevData ] : [];
if (newData.some((block => block.height === payload.block.height))) {
return newData;
}
return [ payload.block, ...newData ].sort((b1, b2) => b2.height - b1.height).slice(0, blocksMaxCount);
});
}, [ queryClient, blocksMaxCount ]);
const channel = useSocketChannel({
topic: 'blocks:new_block',
isDisabled: isPlaceholderData || isError,
});
useSocketMessage({
channel,
event: 'new_block',
handler: handleNewBlockMessage,
});
let content;
if (isError) {
content = <Text>No data. Please reload the page.</Text>;
}
if (data) {
const dataToShow = data.slice(0, blocksMaxCount);
content = (
<>
<VStack gap={ 2 } mb={ 3 } overflow="hidden" alignItems="stretch">
{ dataToShow.map(((block, index) => (
<LatestBlocksItem
key={ block.height + (isPlaceholderData ? String(index) : '') }
block={ block }
isLoading={ isPlaceholderData }
animation={ initialList.getAnimationProp(block) }
/>
))) }
</VStack>
<Flex justifyContent="center">
<Link textStyle="sm" href={ route({ pathname: '/blocks' }) }>View all blocks</Link>
</Flex>
</>
);
}
return (
<Box width={{ base: '100%', lg: '280px' }} flexShrink={ 0 }>
<Heading level="3">Latest blocks</Heading>
{ statsQueryResult.data?.network_utilization_percentage !== undefined && (
<Skeleton loading={ statsQueryResult.isPlaceholderData } mt={ 2 } display="inline-block" textStyle="sm">
<Text as="span">
Network utilization:{ nbsp }
</Text>
<Text as="span" color="blue.400" fontWeight={ 700 }>
{ statsQueryResult.data?.network_utilization_percentage.toFixed(2) }%
</Text>
</Skeleton>
) }
{ statsQueryResult.data?.celo && (
<Box whiteSpace="pre-wrap" textStyle="sm" mt={ 2 }>
<span>Current epoch: </span>
<chakra.span fontWeight={ 700 }>#{ statsQueryResult.data.celo.epoch_number }</chakra.span>
</Box>
) }
<Box mt={ 3 }>
{ content }
</Box>
</Box>
);
};
export default LatestBlocks;