Commit 5f0e0c0e authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #302 from blockscout/blocks-pagination

blocks pagination
parents 935a7414 c65ef13f
......@@ -4,23 +4,32 @@ import { useRouter } from 'next/router';
import React, { useCallback } from 'react';
import { animateScroll } from 'react-scroll';
import type { TransactionsResponse } from 'types/api/transaction';
import type { BlockFilters } from 'types/api/block';
import type { PaginationParams } from 'types/api/pagination';
import type { TTxsFilters } from 'types/api/txsFilters';
import type { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch';
const PAGINATION_FIELDS = [ 'block_number', 'index', 'items_count' ];
const PAGINATION_FIELDS: Array<keyof PaginationParams> = [ 'block_number', 'index', 'items_count' ];
export default function useQueryWithPages(apiPath: string, queryName: QueryKeys, filters?: TTxsFilters) {
interface ResponseWithPagination {
next_page_params: PaginationParams | null;
}
export default function useQueryWithPages<Response extends ResponseWithPagination>(
apiPath: string,
queryName: QueryKeys,
filters?: TTxsFilters | BlockFilters,
) {
const queryClient = useQueryClient();
const router = useRouter();
const [ page, setPage ] = React.useState(1);
const currPageParams = pick(router.query, PAGINATION_FIELDS);
const [ pageParams, setPageParams ] = React.useState<Array<Partial<TransactionsResponse['next_page_params']>>>([ {} ]);
const [ pageParams, setPageParams ] = React.useState<Array<Partial<PaginationParams>>>([ {} ]);
const fetch = useFetch();
const { data, isLoading, isError } = useQuery<unknown, unknown, TransactionsResponse>(
const queryResult = useQuery<unknown, unknown, Response>(
[ queryName, { page, filters } ],
async() => {
const params: Array<string> = [];
......@@ -37,6 +46,7 @@ export default function useQueryWithPages(apiPath: string, queryName: QueryKeys,
},
{ staleTime: Infinity },
);
const { data } = queryResult;
const onNextPageClick = useCallback(() => {
if (!data?.next_page_params) {
......@@ -63,7 +73,6 @@ export default function useQueryWithPages(apiPath: string, queryName: QueryKeys,
// we dont have pagination params for the first page
let nextPageQuery: typeof router.query;
if (page === 2) {
queryClient.clear();
nextPageQuery = omit(router.query, PAGINATION_FIELDS);
} else {
const nextPageParams = { ...pageParams[page - 2] };
......@@ -75,6 +84,7 @@ export default function useQueryWithPages(apiPath: string, queryName: QueryKeys,
.then(() => {
animateScroll.scrollToTop({ duration: 0 });
setPage(prev => prev - 1);
page === 2 && queryClient.clear();
});
}, [ router, page, pageParams, queryClient ]);
......@@ -88,6 +98,7 @@ export default function useQueryWithPages(apiPath: string, queryName: QueryKeys,
}, [ router, queryClient ]);
const hasPaginationParams = Object.keys(currPageParams).length > 0;
const nextPageParams = data?.next_page_params;
const pagination = {
page,
......@@ -95,7 +106,8 @@ export default function useQueryWithPages(apiPath: string, queryName: QueryKeys,
onPrevPageClick,
hasPaginationParams,
resetPage,
hasNextPage: nextPageParams ? Object.keys(nextPageParams).length > 0 : false,
};
return { data, isError, isLoading, pagination };
return { ...queryResult, pagination };
}
import type { NextApiRequest } from 'next';
import getSearchParams from 'lib/api/getSearchParams';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
return `/v2/blocks${ req.query.type ? `?type=${ req.query.type }` : '' }`;
const searchParamsStr = getSearchParams(req);
return `/v2/blocks${ searchParamsStr ? '?' + searchParamsStr : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ]);
......
import type { AddressParam } from 'types/api/addressParams';
import type { PaginationParams } from 'types/api/pagination';
import type { Reward } from 'types/api/reward';
import type { Transaction } from 'types/api/transaction';
......@@ -33,22 +34,19 @@ export interface Block {
export interface BlocksResponse {
items: Array<Block>;
next_page_params: {
block_number: number;
items_count: number;
} | null;
next_page_params: PaginationParams | null;
}
export interface BlockTransactionsResponse {
items: Array<Transaction>;
next_page_params: {
block_number: number;
index: number;
items_count: number;
} | null;
next_page_params: PaginationParams | null;
}
export interface NewBlockSocketResponse {
average_block_time: string;
block: Block;
}
export interface BlockFilters {
type?: BlockType;
}
import type { AddressParam } from './addressParams';
import type { PaginationParams } from './pagination';
export type TxInternalsType = 'call' | 'delegatecall' | 'staticcall' | 'create' | 'create2' | 'selfdestruct' | 'reward'
......@@ -19,10 +20,7 @@ export interface InternalTransaction {
export interface InternalTransactionsResponse {
items: Array<InternalTransaction>;
next_page_params: {
block_number: number;
index: number;
items_count: number;
next_page_params: PaginationParams & {
transaction_hash: string;
transaction_index: number;
};
......
export interface PaginationParams {
block_number: number;
index?: number;
items_count: number;
}
import type { AddressParam } from './addressParams';
import type { DecodedInput } from './decodedInput';
import type { Fee } from './fee';
import type { PaginationParams } from './pagination';
import type { TokenTransfer } from './tokenTransfer';
export type TransactionRevertReason = {
......@@ -45,11 +46,7 @@ export interface Transaction {
export interface TransactionsResponse {
items: Array<Transaction>;
next_page_params: {
block_number: number;
index: number;
items_count: number;
} | null;
next_page_params: PaginationParams | null;
}
export type TransactionType = 'token_transfer' | 'contract_creation' | 'contract_call' | 'token_creation' | 'coin_transfer'
export enum QueryKeys {
addressTags = 'address-tags',
apiKeys = 'api-keys',
block = 'block',
blocks = 'blocks',
customAbis = 'custom-abis',
profile = 'profile',
publicTags = 'public-tags',
......
......@@ -7,4 +7,6 @@ export enum QueryKeys {
txLog = 'tx-log',
txRawTrace = 'tx-raw-trace',
blockTxs = 'block-transactions',
block = 'block',
blocks = 'blocks',
}
......@@ -7,7 +7,7 @@ import React from 'react';
import { scroller, Element } from 'react-scroll';
import type { Block } from 'types/api/block';
import { QueryKeys } from 'types/client/accountQueries';
import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config';
import clockIcon from 'icons/clock.svg';
......
import { Box, Text, Show, Hide, Skeleton, Alert } from '@chakra-ui/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Text, Show, Hide, Skeleton, Alert } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { BlockType, BlocksResponse } from 'types/api/block';
import { QueryKeys } from 'types/client/accountQueries';
import { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import BlocksList from 'ui/blocks/BlocksList';
import BlocksSkeletonMobile from 'ui/blocks/BlocksSkeletonMobile';
import BlocksTable from 'ui/blocks/BlocksTable';
import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Pagination from 'ui/shared/Pagination';
import SkeletonTable from 'ui/shared/SkeletonTable';
......@@ -21,17 +22,13 @@ interface Props {
}
const BlocksContent = ({ type }: Props) => {
const fetch = useFetch();
const queryClient = useQueryClient();
const [ socketAlert, setSocketAlert ] = React.useState('');
const { data, isLoading, isError } = useQuery<unknown, unknown, BlocksResponse>(
[ QueryKeys.blocks, type ],
async() => await fetch(`/node-api/blocks${ type ? `?type=${ type }` : '' }`),
);
const { data, isLoading, isError, pagination } = useQueryWithPages<BlocksResponse>('/node-api/blocks', QueryKeys.blocks, { type });
const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => {
queryClient.setQueryData([ QueryKeys.blocks, type ], (prevData: BlocksResponse | undefined) => {
queryClient.setQueryData([ QueryKeys.blocks, { page: pagination.page, filters: { type } } ], (prevData: BlocksResponse | undefined) => {
const shouldAddToList = !type || type === payload.block.type;
if (!prevData) {
......@@ -42,7 +39,7 @@ const BlocksContent = ({ type }: Props) => {
}
return shouldAddToList ? { ...prevData, items: [ payload.block, ...prevData.items ] } : prevData;
});
}, [ queryClient, type ]);
}, [ pagination.page, queryClient, type ]);
const handleSocketClose = React.useCallback(() => {
setSocketAlert('Connection is lost. Please click here to load new blocks.');
......@@ -56,7 +53,7 @@ const BlocksContent = ({ type }: Props) => {
topic: 'blocks:new_block',
onSocketClose: handleSocketClose,
onSocketError: handleSocketError,
isDisabled: isLoading || isError,
isDisabled: isLoading || isError || pagination.page !== 1,
});
useSocketMessage({
channel,
......@@ -64,38 +61,48 @@ const BlocksContent = ({ type }: Props) => {
handler: handleNewBlockMessage,
});
if (isLoading) {
const content = (() => {
if (isLoading) {
return (
<>
<Show below="lg" key="skeleton-mobile">
<BlocksSkeletonMobile/>
</Show>
<Hide below="lg" key="skeleton-desktop">
<SkeletonTable columns={ [ '125px', '120px', '21%', '64px', '35%', '22%', '22%' ] }/>
</Hide>
</>
);
}
if (isError) {
return <DataFetchAlert/>;
}
if (data.items.length === 0) {
return <Text as="span">There are no blocks.</Text>;
}
return (
<>
<Show below="lg" key="skeleton-mobile">
<BlocksSkeletonMobile/>
</Show>
<Hide below="lg" key="skeleton-desktop">
<Skeleton h={ 6 } mb={ 8 } w="150px"/>
<SkeletonTable columns={ [ '125px', '120px', '21%', '64px', '35%', '22%', '22%' ] }/>
</Hide>
{ socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> }
<Show below="lg" key="content-mobile"><BlocksList data={ data.items }/></Show>
<Hide below="lg" key="content-desktop"><BlocksTable data={ data.items }/></Hide>
</>
);
}
if (isError) {
return <DataFetchAlert/>;
}
if (data.items.length === 0) {
return <Text as="span">There are no blocks.</Text>;
}
})();
return (
<>
<Text as="span">Total of { data.items[0].height.toLocaleString() } blocks</Text>
{ socketAlert && <Alert status="warning" mt={ 8 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> }
<Show below="lg" key="content-mobile"><BlocksList data={ data.items }/></Show>
<Hide below="lg" key="content-desktop"><BlocksTable data={ data.items }/></Hide>
<Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}>
{ /* eslint-disable-next-line react/jsx-no-bind */ }
<Pagination page={ 1 } onNextPageClick={ () => {} } onPrevPageClick={ () => {} } resetPage={ () => {} } hasNextPage/>
</Box>
{ data ?
<Text mb={{ base: 0, lg: 6 }}>Total of { data.items[0].height.toLocaleString() } blocks</Text> :
<Skeleton h="24px" w="200px" mb={{ base: 0, lg: 6 }}/>
}
<ActionBar>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
{ content }
</>
);
};
......
......@@ -12,7 +12,7 @@ interface Props {
const BlocksList = ({ data }: Props) => {
return (
<Box mt={ 8 }>
<Box>
<AnimatePresence initial={ false }>
{ /* TODO prop "enableTimeIncrement" should be set to false for second and later pages */ }
{ data.map((item) => <BlocksListItem key={ item.height } data={ item } enableTimeIncrement/>) }
......
......@@ -17,8 +17,8 @@ interface Props {
const BlocksTable = ({ data }: Props) => {
return (
<Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 } mt={ 8 }>
<Thead top={ 0 }>
<Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }>
<Thead top={ 80 }>
<Tr>
<Th width="125px">Block</Th>
<Th width="120px">Size</Th>
......
......@@ -24,9 +24,7 @@ const BlockPageContent = () => {
return (
<Page>
<PageTitle text={ `Block #${ router.query.id }` }/>
<RoutedTabs
tabs={ TABS }
/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 12 }}/>
</Page>
);
};
......
......@@ -17,9 +17,7 @@ const BlocksPageContent = () => {
return (
<Page>
<PageTitle text="Blocks"/>
<RoutedTabs
tabs={ TABS }
/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/>
</Page>
);
};
......
......@@ -17,7 +17,7 @@ const PrivateTags = () => {
return (
<Page>
<PageTitle text="Private tags"/>
<RoutedTabs tabs={ TABS }/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/>
</Page>
);
};
......
......@@ -80,9 +80,7 @@ const TransactionPageContent = () => {
</Flex>
) }
</Flex>
<RoutedTabs
tabs={ TABS }
/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 12 }}/>
</Page>
);
};
......
......@@ -22,7 +22,7 @@ const Transactions = () => {
<Page hideMobileHeaderOnScrollDown>
<Box h="100%">
<PageTitle text="Transactions"/>
<RoutedTabs tabs={ TABS }/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/>
</Box>
</Page>
);
......
import { Flex, useColorModeValue, chakra } from '@chakra-ui/react';
import throttle from 'lodash/throttle';
import React from 'react';
import ScrollDirectionContext from 'ui/ScrollDirectionContext';
type Props = {
children: React.ReactNode;
className?: string;
}
const TOP_UP = 106;
const TOP_DOWN = 0;
const ActionBar = ({ children, className }: Props) => {
const [ isSticky, setIsSticky ] = React.useState(false);
const ref = React.useRef<HTMLDivElement>(null);
const handleScroll = React.useCallback(() => {
if (
Number(ref.current?.getBoundingClientRect().y) < TOP_UP + 5
) {
setIsSticky(true);
} else {
setIsSticky(false);
}
}, [ ]);
React.useEffect(() => {
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ ]);
const bgColor = useColorModeValue('white', 'black');
return (
<ScrollDirectionContext.Consumer>
{ (scrollDirection) => (
<Flex
className={ className }
backgroundColor={ bgColor }
py={ 6 }
mx={{ base: -4, lg: 0 }}
px={{ base: 4, lg: 0 }}
justifyContent="space-between"
width={{ base: '100vw', lg: 'unset' }}
position="sticky"
top={{ base: scrollDirection === 'down' ? `${ TOP_DOWN }px` : `${ TOP_UP }px`, lg: 0 }}
transitionProperty="top,box-shadow,background-color,color"
transitionDuration="slow"
zIndex={{ base: 'sticky2', lg: 'docked' }}
boxShadow={{ base: isSticky ? 'md' : 'none', lg: 'none' }}
ref={ ref }
>
{ children }
</Flex>
) }
</ScrollDirectionContext.Consumer>
);
};
export default chakra(ActionBar);
import { Button, Flex, Icon, IconButton } from '@chakra-ui/react';
import { Button, Flex, Icon, IconButton, chakra } from '@chakra-ui/react';
import React from 'react';
import arrowIcon from 'icons/arrows/east-mini.svg';
......@@ -10,12 +10,14 @@ export type Props = {
resetPage: () => void;
hasNextPage: boolean;
hasPaginationParams?: boolean;
className?: string;
}
const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNextPage, hasPaginationParams }: Props) => {
const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNextPage, hasPaginationParams, className }: Props) => {
return (
<Flex
className={ className }
fontSize="sm"
alignItems="center"
>
......@@ -69,4 +71,4 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
);
};
export default Pagination;
export default chakra(Pagination);
import type { ChakraProps } from '@chakra-ui/react';
import {
Tab,
Tabs,
......@@ -25,9 +26,10 @@ const hiddenItemStyles: StyleProps = {
interface Props {
tabs: Array<RoutedTab>;
tabListMarginBottom?: ChakraProps['marginBottom'];
}
const RoutedTabs = ({ tabs }: Props) => {
const RoutedTabs = ({ tabs, tabListMarginBottom }: Props) => {
const router = useRouter();
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
useEffect(() => {
......@@ -59,7 +61,7 @@ const RoutedTabs = ({ tabs }: Props) => {
return (
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ handleTabChange } index={ activeTabIndex }>
<TabList
marginBottom={{ base: 6, lg: 12 }}
marginBottom={ tabListMarginBottom }
flexWrap="nowrap"
whiteSpace="nowrap"
ref={ listRef }
......
import { Text, Box, Show, Hide } from '@chakra-ui/react';
import React, { useState, useCallback } from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters';
import type { QueryKeys } from 'types/client/queries';
import type { Sort } from 'types/client/txs-sort';
import * as cookies from 'lib/cookies';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxsHeader from './TxsHeader';
import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile';
import TxsWithSort from './TxsWithSort';
import useQueryWithPages from './useQueryWithPages';
type Props = {
queryName: QueryKeys;
......@@ -61,39 +62,36 @@ const TxsContent = ({
isLoading,
isError,
pagination,
} = useQueryWithPages(apiPath, queryName, stateFilter && { filter: stateFilter });
} = useQueryWithPages<TransactionsResponse>(apiPath, queryName, stateFilter && { filter: stateFilter });
// } = useQueryWithPages({ ...filters, filter: stateFilter, apiPath });
if (isError) {
return <DataFetchAlert/>;
}
const content = (() => {
if (isError) {
return <DataFetchAlert/>;
}
const txs = data?.items;
const txs = data?.items;
if (!isLoading && !txs) {
return <Text as="span">There are no transactions.</Text>;
}
if (!isLoading && !txs?.length) {
return <Text as="span">There are no transactions.</Text>;
}
let content = (
<>
<Show below="lg" ssr={ false }><TxsSkeletonMobile/></Show>
<Hide below="lg" ssr={ false }><TxsSkeletonDesktop/></Hide>
</>
);
if (!isLoading && txs) {
content = <TxsWithSort txs={ txs } sorting={ sorting } sort={ sort }/>;
}
if (!isLoading && txs) {
return <TxsWithSort txs={ txs } sorting={ sorting } sort={ sort }/>;
}
const paginationProps = {
...pagination,
hasNextPage: data?.next_page_params !== undefined && data?.next_page_params !== null && Object.keys(data?.next_page_params).length > 0,
};
return (
<>
<Show below="lg" ssr={ false }><TxsSkeletonMobile/></Show>
<Hide below="lg" ssr={ false }><TxsSkeletonDesktop/></Hide>
</>
);
})();
return (
<>
{ showDescription && <Box mb={ 12 }>Only the first 10,000 elements are displayed</Box> }
<TxsHeader sorting={ sorting } setSorting={ setSorting } paginationProps={ paginationProps }/>
{ showDescription && <Box mb={{ base: 6, lg: 12 }}>Only the first 10,000 elements are displayed</Box> }
<TxsHeader mt={ -6 } sorting={ sorting } setSorting={ setSorting } paginationProps={ pagination }/>
{ content }
</>
);
......
import { HStack, Flex, useColorModeValue } from '@chakra-ui/react';
import throttle from 'lodash/throttle';
import React, { useCallback } from 'react';
import { HStack, chakra } from '@chakra-ui/react';
import React from 'react';
import type { Sort } from 'types/client/txs-sort';
import useIsMobile from 'lib/hooks/useIsMobile';
// import FilterInput from 'ui/shared/FilterInput';
import ScrollDirectionContext from 'ui/ScrollDirectionContext';
import ActionBar from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import TxsSorting from 'ui/txs/TxsSorting';
......@@ -17,89 +16,40 @@ type Props = {
sorting: Sort;
setSorting: (val: Sort | ((val: Sort) => Sort)) => void;
paginationProps: PaginationProps;
className?: string;
}
const TOP_UP = 106;
const TOP_DOWN = 0;
const TxsHeader = ({ sorting, setSorting, paginationProps }: Props) => {
const [ isSticky, setIsSticky ] = React.useState(false);
const ref = React.useRef<HTMLDivElement>(null);
const TxsHeader = ({ sorting, setSorting, paginationProps, className }: Props) => {
const isMobile = useIsMobile(false);
const handleScroll = useCallback(() => {
if (
Number(ref.current?.getBoundingClientRect().y) < TOP_UP + 5
) {
setIsSticky(true);
} else {
setIsSticky(false);
}
}, [ ]);
React.useEffect(() => {
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ ]);
const bgColor = useColorModeValue('white', 'black');
return (
<ScrollDirectionContext.Consumer>
{ (scrollDirection) => (
<Flex
backgroundColor={ bgColor }
mt={ -6 }
pt={ 6 }
pb={ 6 }
mx={{ base: -4, lg: 0 }}
px={{ base: 4, lg: 0 }}
justifyContent="space-between"
width={{ base: '100vw', lg: 'unset' }}
position="sticky"
top={{ base: scrollDirection === 'down' ? `${ TOP_DOWN }px` : `${ TOP_UP }px`, lg: 0 }}
transitionProperty="top,box-shadow"
transitionDuration="slow"
zIndex={{ base: 'sticky2', lg: 'docked' }}
boxShadow={{ base: isSticky ? 'md' : 'none', lg: 'none' }}
ref={ ref }
>
<HStack>
{ /* api is not implemented */ }
{ /* <TxsFilters
<ActionBar className={ className }>
<HStack>
{ /* api is not implemented */ }
{ /* <TxsFilters
filters={ filters }
onFiltersChange={ setFilters }
appliedFiltersNum={ 0 }
/> */ }
{ isMobile && (
<TxsSorting
isActive={ Boolean(sorting) }
setSorting={ setSorting }
sorting={ sorting }
/>
) }
{ /* api is not implemented */ }
{ /* <FilterInput
{ isMobile && (
<TxsSorting
isActive={ Boolean(sorting) }
setSorting={ setSorting }
sorting={ sorting }
/>
) }
{ /* api is not implemented */ }
{ /* <FilterInput
// eslint-disable-next-line react/jsx-no-bind
onChange={ () => {} }
maxW="360px"
size="xs"
placeholder="Search by addresses, hash, method..."
/> */ }
</HStack>
<Pagination { ...paginationProps }/>
</Flex>
) }
</ScrollDirectionContext.Consumer>
</HStack>
<Pagination { ...paginationProps }/>
</ActionBar>
);
};
export default TxsHeader;
export default chakra(TxsHeader);
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