Commit 2349289e authored by tom's avatar tom

pagination for logs and internal txs

parent e74c29e9
import type { BlocksResponse, BlockTransactionsResponse } from 'types/api/block'; import type { BlocksResponse, BlockTransactionsResponse } from 'types/api/block';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction'; import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { LogsResponse } from 'types/api/log';
import type { TokenTransferResponse } from 'types/api/tokenTransfer'; import type { TokenTransferResponse } from 'types/api/tokenTransfer';
import type { TransactionsResponseValidated, TransactionsResponsePending } from 'types/api/transaction'; import type { TransactionsResponseValidated, TransactionsResponsePending } from 'types/api/transaction';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
...@@ -11,6 +12,7 @@ export type PaginatedQueryKeys = ...@@ -11,6 +12,7 @@ export type PaginatedQueryKeys =
QueryKeys.txsValidate | QueryKeys.txsValidate |
QueryKeys.txsPending | QueryKeys.txsPending |
QueryKeys.txInternals | QueryKeys.txInternals |
QueryKeys.txLogs |
QueryKeys.txTokenTransfers; QueryKeys.txTokenTransfers;
export type PaginatedResponse<Q extends PaginatedQueryKeys> = export type PaginatedResponse<Q extends PaginatedQueryKeys> =
...@@ -19,8 +21,9 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> = ...@@ -19,8 +21,9 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> =
Q extends QueryKeys.txsValidate ? TransactionsResponseValidated : Q extends QueryKeys.txsValidate ? TransactionsResponseValidated :
Q extends QueryKeys.txsPending ? TransactionsResponsePending : Q extends QueryKeys.txsPending ? TransactionsResponsePending :
Q extends QueryKeys.txInternals ? InternalTransactionsResponse : Q extends QueryKeys.txInternals ? InternalTransactionsResponse :
Q extends QueryKeys.txTokenTransfers ? TokenTransferResponse : Q extends QueryKeys.txLogs ? LogsResponse :
never Q extends QueryKeys.txTokenTransfers ? TokenTransferResponse :
never
export type PaginationParams<Q extends PaginatedQueryKeys> = PaginatedResponse<Q>['next_page_params']; export type PaginationParams<Q extends PaginatedQueryKeys> = PaginatedResponse<Q>['next_page_params'];
...@@ -35,4 +38,5 @@ export const PAGINATION_FIELDS: PaginationFields = { ...@@ -35,4 +38,5 @@ export const PAGINATION_FIELDS: PaginationFields = {
[QueryKeys.txsPending]: [ 'filter', 'hash', 'inserted_at' ], [QueryKeys.txsPending]: [ 'filter', 'hash', 'inserted_at' ],
[QueryKeys.txInternals]: [ 'block_number', 'items_count', 'transaction_hash', 'index', 'transaction_index' ], [QueryKeys.txInternals]: [ 'block_number', 'items_count', 'transaction_hash', 'index', 'transaction_index' ],
[QueryKeys.txTokenTransfers]: [ 'block_number', 'items_count', 'transaction_hash', 'index' ], [QueryKeys.txTokenTransfers]: [ 'block_number', 'items_count', 'transaction_hash', 'index' ],
[QueryKeys.txLogs]: [ 'items_count', 'transaction_hash', 'index' ],
}; };
...@@ -5,7 +5,7 @@ export enum QueryKeys { ...@@ -5,7 +5,7 @@ export enum QueryKeys {
txsPending = 'txs-pending', txsPending = 'txs-pending',
tx = 'tx', tx = 'tx',
txInternals = 'tx-internals', txInternals = 'tx-internals',
txLog = 'tx-log', txLogs = 'tx-logs',
txRawTrace = 'tx-raw-trace', txRawTrace = 'tx-raw-trace',
txTokenTransfers = 'tx-token-transfers', txTokenTransfers = 'tx-token-transfers',
blockTxs = 'block-transactions', blockTxs = 'block-transactions',
......
import { Box, Text, Show, Hide } from '@chakra-ui/react'; import { Box, Text, Show, Hide } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { InternalTransactionsResponse, InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import EmptySearchResult from 'ui/apps/EmptySearchResult'; import EmptySearchResult from 'ui/apps/EmptySearchResult';
import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
// import FilterInput from 'ui/shared/FilterInput'; // import FilterInput from 'ui/shared/FilterInput';
// import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter'; // import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter';
import Pagination from 'ui/shared/Pagination';
import TxInternalsList from 'ui/tx/internals/TxInternalsList'; import TxInternalsList from 'ui/tx/internals/TxInternalsList';
import TxInternalsSkeletonDesktop from 'ui/tx/internals/TxInternalsSkeletonDesktop'; import TxInternalsSkeletonDesktop from 'ui/tx/internals/TxInternalsSkeletonDesktop';
import TxInternalsSkeletonMobile from 'ui/tx/internals/TxInternalsSkeletonMobile'; import TxInternalsSkeletonMobile from 'ui/tx/internals/TxInternalsSkeletonMobile';
...@@ -70,21 +70,20 @@ const sortFn = (sort: Sort | undefined) => (a: InternalTransaction, b: InternalT ...@@ -70,21 +70,20 @@ const sortFn = (sort: Sort | undefined) => (a: InternalTransaction, b: InternalT
// }; // };
const TxInternals = () => { const TxInternals = () => {
const router = useRouter();
const fetch = useFetch();
// filters are not implemented yet in api // filters are not implemented yet in api
// const [ filters, setFilters ] = React.useState<Array<TxInternalsType>>([]); // const [ filters, setFilters ] = React.useState<Array<TxInternalsType>>([]);
// const [ searchTerm, setSearchTerm ] = React.useState<string>(''); // const [ searchTerm, setSearchTerm ] = React.useState<string>('');
const [ sort, setSort ] = React.useState<Sort>(); const [ sort, setSort ] = React.useState<Sort>();
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND }); const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isLoading, isError } = useQuery<unknown, unknown, InternalTransactionsResponse>( const { data, isLoading, isError, pagination } = useQueryWithPages({
[ QueryKeys.txInternals, router.query.id ], apiPath: `/node-api/transactions/${ txInfo.data?.hash }/internal-transactions`,
async() => await fetch(`/node-api/transactions/${ router.query.id }/internal-transactions`), queryName: QueryKeys.txInternals,
{ queryIds: txInfo.data?.hash ? [ txInfo.data.hash ] : undefined,
enabled: Boolean(router.query.id) && Boolean(txInfo.data?.status), options: {
enabled: Boolean(txInfo.data?.hash) && Boolean(txInfo.data?.status),
}, },
); });
const isPaginatorHidden = !isLoading && !isError && pagination.page === 1 && !pagination.hasNextPage;
const isMobile = useIsMobile(); const isMobile = useIsMobile();
...@@ -132,11 +131,16 @@ const TxInternals = () => { ...@@ -132,11 +131,16 @@ const TxInternals = () => {
return isMobile ? return isMobile ?
<TxInternalsList data={ filteredData }/> : <TxInternalsList data={ filteredData }/> :
<TxInternalsTable data={ filteredData } sort={ sort } onSortToggle={ handleSortToggle }/>; <TxInternalsTable data={ filteredData } sort={ sort } onSortToggle={ handleSortToggle } top={ isPaginatorHidden ? 0 : 80 }/>;
})(); })();
return ( return (
<Box> <Box>
{ !isPaginatorHidden && (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
) }
{ /* <Flex mb={ 6 }> { /* <Flex mb={ 6 }>
<TxInternalsFilter onFilterChange={ handleFilterChange } defaultFilters={ filters } appliedFiltersNum={ filters.length }/> <TxInternalsFilter onFilterChange={ handleFilterChange } defaultFilters={ filters } appliedFiltersNum={ filters.length }/>
<FilterInput onChange={ setSearchTerm } maxW="360px" ml={ 3 } size="xs" placeholder="Search by addresses, hash, method..."/> <FilterInput onChange={ setSearchTerm } maxW="360px" ml={ 3 } size="xs" placeholder="Search by addresses, hash, method..."/>
......
import { Box, Text } from '@chakra-ui/react'; import { Box, Text } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { LogsResponse } from 'types/api/log';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import useFetch from 'lib/hooks/useFetch'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Pagination from 'ui/shared/Pagination';
import TxLogItem from 'ui/tx/logs/TxLogItem'; import TxLogItem from 'ui/tx/logs/TxLogItem';
import TxLogSkeleton from 'ui/tx/logs/TxLogSkeleton'; import TxLogSkeleton from 'ui/tx/logs/TxLogSkeleton';
import TxPendingAlert from 'ui/tx/TxPendingAlert'; import TxPendingAlert from 'ui/tx/TxPendingAlert';
...@@ -16,17 +15,16 @@ import TxSocketAlert from 'ui/tx/TxSocketAlert'; ...@@ -16,17 +15,16 @@ import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo'; import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TxLogs = () => { const TxLogs = () => {
const router = useRouter();
const fetch = useFetch();
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND }); const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isLoading, isError } = useQuery<unknown, unknown, LogsResponse>( const { data, isLoading, isError, pagination } = useQueryWithPages({
[ QueryKeys.txLog, router.query.id ], apiPath: `/node-api/transactions/${ txInfo.data?.hash }/logs`,
async() => await fetch(`/node-api/transactions/${ router.query.id }/logs`), queryName: QueryKeys.txLogs,
{ queryIds: txInfo.data?.hash ? [ txInfo.data.hash ] : undefined,
enabled: Boolean(router.query.id) && Boolean(txInfo.data?.status), options: {
enabled: Boolean(txInfo.data?.hash) && Boolean(txInfo.data?.status),
}, },
); });
const isPaginatorHidden = !isLoading && !isError && pagination.page === 1 && !pagination.hasNextPage;
if (!txInfo.isLoading && !txInfo.isError && !txInfo.data.status) { if (!txInfo.isLoading && !txInfo.isError && !txInfo.data.status) {
return txInfo.socketStatus ? <TxSocketAlert status={ txInfo.socketStatus }/> : <TxPendingAlert/>; return txInfo.socketStatus ? <TxSocketAlert status={ txInfo.socketStatus }/> : <TxPendingAlert/>;
...@@ -51,6 +49,11 @@ const TxLogs = () => { ...@@ -51,6 +49,11 @@ const TxLogs = () => {
return ( return (
<Box> <Box>
{ !isPaginatorHidden && (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
) }
{ data.items.map((item, index) => <TxLogItem key={ index } { ...item }/>) } { data.items.map((item, index) => <TxLogItem key={ index } { ...item }/>) }
</Box> </Box>
); );
......
...@@ -7,7 +7,7 @@ import TxInternalsListItem from 'ui/tx/internals/TxInternalsListItem'; ...@@ -7,7 +7,7 @@ import TxInternalsListItem from 'ui/tx/internals/TxInternalsListItem';
const TxInternalsList = ({ data }: { data: Array<InternalTransaction>}) => { const TxInternalsList = ({ data }: { data: Array<InternalTransaction>}) => {
return ( return (
<Box mt={ 6 }> <Box>
{ data.map((item) => <TxInternalsListItem key={ item.transaction_hash } { ...item }/>) } { data.map((item) => <TxInternalsListItem key={ item.transaction_hash } { ...item }/>) }
</Box> </Box>
); );
......
import { Skeleton, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import SkeletonTable from 'ui/shared/SkeletonTable'; import SkeletonTable from 'ui/shared/SkeletonTable';
const TxInternalsSkeletonDesktop = () => { const TxInternalsSkeletonDesktop = () => {
return ( return (
<> <SkeletonTable columns={ [ '28%', '20%', '24px', '20%', '16%', '16%' ] }/>
<Flex columnGap={ 3 } h={ 8 } mb={ 6 }>
<Skeleton w="78px"/>
<Skeleton w="360px"/>
</Flex>
<SkeletonTable columns={ [ '28%', '20%', '24px', '20%', '16%', '16%' ] }/>
</>
); );
}; };
......
...@@ -5,47 +5,41 @@ const TxInternalsSkeletonMobile = () => { ...@@ -5,47 +5,41 @@ const TxInternalsSkeletonMobile = () => {
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200'); const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
return ( return (
<> <Box>
<Flex columnGap={ 3 } h={ 8 } mb={ 6 }> { Array.from(Array(2)).map((item, index) => (
<Skeleton w="36px" flexShrink={ 0 }/> <Flex
<Skeleton w="100%"/> key={ index }
</Flex> rowGap={ 3 }
<Box> flexDirection="column"
{ Array.from(Array(2)).map((item, index) => ( paddingY={ 6 }
<Flex borderTopWidth="1px"
key={ index } borderColor={ borderColor }
rowGap={ 3 } _last={{
flexDirection="column" borderBottomWidth: '1px',
paddingY={ 6 } }}
borderTopWidth="1px" >
borderColor={ borderColor } <Flex h={ 6 }>
_last={{ <Skeleton w="100px" mr={ 2 }/>
borderBottomWidth: '1px', <Skeleton w="90px"/>
}}
>
<Flex h={ 6 }>
<Skeleton w="100px" mr={ 2 }/>
<Skeleton w="90px"/>
</Flex>
<Flex h={ 6 }>
<SkeletonCircle size="6" mr={ 2 } flexShrink={ 0 }/>
<Skeleton flexGrow={ 1 } mr={ 3 }/>
<Skeleton w={ 6 } mr={ 3 }/>
<SkeletonCircle size="6" mr={ 2 } flexShrink={ 0 }/>
<Skeleton flexGrow={ 1 } mr={ 3 }/>
</Flex>
<Flex h={ 6 }>
<Skeleton w="70px" mr={ 2 }/>
<Skeleton w="30px"/>
</Flex>
<Flex h={ 6 }>
<Skeleton w="70px" mr={ 2 }/>
<Skeleton w="60px"/>
</Flex>
</Flex> </Flex>
)) } <Flex h={ 6 }>
</Box> <SkeletonCircle size="6" mr={ 2 } flexShrink={ 0 }/>
</> <Skeleton flexGrow={ 1 } mr={ 3 }/>
<Skeleton w={ 6 } mr={ 3 }/>
<SkeletonCircle size="6" mr={ 2 } flexShrink={ 0 }/>
<Skeleton flexGrow={ 1 } mr={ 3 }/>
</Flex>
<Flex h={ 6 }>
<Skeleton w="70px" mr={ 2 }/>
<Skeleton w="30px"/>
</Flex>
<Flex h={ 6 }>
<Skeleton w="70px" mr={ 2 }/>
<Skeleton w="60px"/>
</Flex>
</Flex>
)) }
</Box>
); );
}; };
......
...@@ -13,14 +13,15 @@ interface Props { ...@@ -13,14 +13,15 @@ interface Props {
data: Array<InternalTransaction>; data: Array<InternalTransaction>;
sort: Sort | undefined; sort: Sort | undefined;
onSortToggle: (field: SortField) => () => void; onSortToggle: (field: SortField) => () => void;
top: number;
} }
const TxInternalsTable = ({ data, sort, onSortToggle }: Props) => { const TxInternalsTable = ({ data, sort, onSortToggle, top }: Props) => {
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)'; const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return ( return (
<Table variant="simple" size="sm" mt={ 6 }> <Table variant="simple" size="sm">
<Thead top={ 0 }> <Thead top={ top }>
<Tr> <Tr>
<Th width="28%">Type</Th> <Th width="28%">Type</Th>
<Th width="20%">From</Th> <Th width="20%">From</Th>
......
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