Commit 08f56b55 authored by isstuev's avatar isstuev

indexing alert

parent d402eecf
...@@ -5,6 +5,7 @@ import type { NewBlockSocketResponse } from 'types/api/block'; ...@@ -5,6 +5,7 @@ import type { NewBlockSocketResponse } from 'types/api/block';
export type SocketMessageParams = SocketMessage.NewBlock | export type SocketMessageParams = SocketMessage.NewBlock |
SocketMessage.BlocksIndexStatus | SocketMessage.BlocksIndexStatus |
SocketMessage.InternalTxsIndexStatus |
SocketMessage.TxStatusUpdate | SocketMessage.TxStatusUpdate |
SocketMessage.NewTx | SocketMessage.NewTx |
SocketMessage.NewPendingTx | SocketMessage.NewPendingTx |
...@@ -23,7 +24,8 @@ interface SocketMessageParamsGeneric<Event extends string | undefined, Payload e ...@@ -23,7 +24,8 @@ interface SocketMessageParamsGeneric<Event extends string | undefined, Payload e
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace SocketMessage { export namespace SocketMessage {
export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>; export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>;
export type BlocksIndexStatus = SocketMessageParamsGeneric<'index_status', {finished: boolean; ratio: string}>; export type BlocksIndexStatus = SocketMessageParamsGeneric<'block_index_status', {finished: boolean; ratio: string}>;
export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'internal_txs_index_status', {finished: boolean; ratio: string}>;
export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>; export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>;
export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>; export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>;
export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>; export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>;
......
import handler from 'lib/api/handler';
const getUrl = () => '/v2/main-page/indexing-status';
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
export type IndexingStatus = {
finished_indexing: boolean;
finished_indexing_blocks: boolean;
indexed_blocks_ratio: string;
indexed_inernal_transactions_ratio: string;
}
...@@ -4,6 +4,7 @@ export enum QueryKeys { ...@@ -4,6 +4,7 @@ export enum QueryKeys {
txsValidate = 'txs-validated', txsValidate = 'txs-validated',
txsPending = 'txs-pending', txsPending = 'txs-pending',
homeStats='homeStats', homeStats='homeStats',
indexingStatus='indexingStatus',
stats='stats', stats='stats',
charts='stats', charts='stats',
tx = 'tx', tx = 'tx',
......
import { Alert, AlertIcon, AlertTitle, chakra } from '@chakra-ui/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { IndexingStatus } from 'types/api/indexingStatus';
import { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp, ndash } from 'lib/html-entities';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
const IndexingAlert = ({ className }: { className?: string }) => {
const fetch = useFetch();
const isMobile = useIsMobile();
const { data } = useQuery<unknown, unknown, IndexingStatus>(
[ QueryKeys.indexingStatus ],
async() => await fetch(`/node-api/index/indexing-status`),
);
const queryClient = useQueryClient();
const handleBlocksIndexStatus: SocketMessage.BlocksIndexStatus['handler'] = React.useCallback((payload) => {
queryClient.setQueryData([ QueryKeys.indexingStatus ], (prevData: IndexingStatus | undefined) => {
const newData = prevData ? { ...prevData } : {} as IndexingStatus;
newData.finished_indexing_blocks = payload.finished;
newData.indexed_blocks_ratio = payload.ratio;
return newData;
});
}, [ queryClient ]);
const blockIndexingChannel = useSocketChannel({
topic: 'blocks:indexing',
isDisabled: !data || data.finished_indexing_blocks,
});
useSocketMessage({
channel: blockIndexingChannel,
event: 'block_index_status',
handler: handleBlocksIndexStatus,
});
const handleIntermalTxsIndexStatus: SocketMessage.InternalTxsIndexStatus['handler'] = React.useCallback((payload) => {
queryClient.setQueryData([ QueryKeys.indexingStatus ], (prevData: IndexingStatus | undefined) => {
const newData = prevData ? { ...prevData } : {} as IndexingStatus;
newData.finished_indexing = payload.finished;
newData.indexed_inernal_transactions_ratio = payload.ratio;
return newData;
});
}, [ queryClient ]);
const internalTxsIndexingChannel = useSocketChannel({
topic: 'blocks:indexing_internal_transactions',
isDisabled: !data || data.finished_indexing,
});
useSocketMessage({
channel: internalTxsIndexingChannel,
event: 'internal_txs_index_status',
handler: handleIntermalTxsIndexStatus,
});
if (!data) {
return null;
}
let content;
if (data.finished_indexing_blocks === false) {
content = `${ data.indexed_blocks_ratio && `${ (Number(data.indexed_blocks_ratio) * 100).toFixed() }% Blocks Indexed${ nbsp }${ ndash } ` }
We're indexing this chain right now. Some of the counts may be inaccurate.` ;
} else if (data.finished_indexing === false) {
content = `${ data.indexed_inernal_transactions_ratio &&
`${ (Number(data.indexed_inernal_transactions_ratio) * 100).toFixed() }% Blocks With Internal Transactions Indexed${ nbsp }${ ndash } ` }
We're indexing this chain right now. Some of the counts may be inaccurate.`;
}
if (!content) {
return null;
}
return (
<Alert status="warning" variant="solid" py={ 3 } borderRadius="12px" mb={ 6 } className={ className }>
{ !isMobile && <AlertIcon/> }
<AlertTitle>
{ content }
</AlertTitle>
</Alert>
);
};
export default chakra(IndexingAlert);
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import Page from './Page';
const API_URL = '/node-api/index/indexing-status';
test('without indexing alert +@mobile', async({ mount }) => {
const component = await mount(
<TestApp>
<Page>Page Content</Page>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('with indexing alert +@mobile', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ finished_indexing_blocks: false, indexed_blocks_ratio: 0.1 }),
}));
const component = await mount(
<TestApp>
<Page>Page Content</Page>
</TestApp>,
);
await page.waitForResponse(API_URL),
await expect(component).toHaveScreenshot();
});
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import IndexingAlert from 'ui/home/IndexingAlert';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
isHomePage?: boolean; isHomePage?: boolean;
...@@ -13,8 +15,9 @@ const PageContent = ({ children, isHomePage }: Props) => { ...@@ -13,8 +15,9 @@ const PageContent = ({ children, isHomePage }: Props) => {
w="100%" w="100%"
paddingX={{ base: 4, lg: 12 }} paddingX={{ base: 4, lg: 12 }}
paddingBottom={ 10 } paddingBottom={ 10 }
paddingTop={{ base: isHomePage ? '88px' : '138px', lg: isHomePage ? 9 : 0 }} paddingTop={{ base: isHomePage ? '88px' : '138px', lg: 0 }}
> >
<IndexingAlert display={{ base: 'block', lg: 'none' }}/>
{ children } { children }
</Box> </Box>
); );
......
...@@ -2,6 +2,7 @@ import { HStack, Box, Flex, useColorModeValue } from '@chakra-ui/react'; ...@@ -2,6 +2,7 @@ import { HStack, Box, Flex, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import IndexingAlert from 'ui/home/IndexingAlert';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo'; import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop'; import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile'; import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile';
...@@ -44,6 +45,12 @@ const Header = ({ hideOnScrollDown, isHomePage }: Props) => { ...@@ -44,6 +45,12 @@ const Header = ({ hideOnScrollDown, isHomePage }: Props) => {
</Flex> </Flex>
{ !isHomePage && <SearchBar withShadow={ !hideOnScrollDown }/> } { !isHomePage && <SearchBar withShadow={ !hideOnScrollDown }/> }
</Box> </Box>
<Box
paddingX={ 12 }
paddingTop={ 9 }
display={{ base: 'none', lg: 'block' }}
>
<IndexingAlert/>
{ !isHomePage && ( { !isHomePage && (
<HStack <HStack
as="header" as="header"
...@@ -51,9 +58,6 @@ const Header = ({ hideOnScrollDown, isHomePage }: Props) => { ...@@ -51,9 +58,6 @@ const Header = ({ hideOnScrollDown, isHomePage }: Props) => {
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
gap={ 12 } gap={ 12 }
display={{ base: 'none', lg: 'flex' }}
paddingX={ 12 }
paddingTop={ 9 }
paddingBottom="52px" paddingBottom="52px"
> >
<Box width="100%"> <Box width="100%">
...@@ -63,6 +67,7 @@ const Header = ({ hideOnScrollDown, isHomePage }: Props) => { ...@@ -63,6 +67,7 @@ const Header = ({ hideOnScrollDown, isHomePage }: Props) => {
<ProfileMenuDesktop/> <ProfileMenuDesktop/>
</HStack> </HStack>
) } ) }
</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