Commit def2832f authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #267 from blockscout/block-txs-api

api for block txs
parents 1cec1447 410f2f0d
import type { NextApiRequest } from 'next';
export default function getSearchParams(req: NextApiRequest) {
const searchParams: Record<string, string> = {};
Object.entries(req.query).forEach(([ key, value ]) => {
searchParams[key] = Array.isArray(value) ? value.join(',') : (value || '');
});
return new URLSearchParams(searchParams).toString();
}
import type { NextApiRequest } from 'next';
import getSearchParams from 'lib/api/getSearchParams';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const searchParamsStr = getSearchParams(req);
return `/v2/blocks/${ req.query.id }/transactions${ searchParamsStr ? '?' + searchParamsStr : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
import type { NextApiRequest } from 'next'; import type { NextApiRequest } from 'next';
import getSearchParams from 'lib/api/getSearchParams';
import handler from 'lib/api/handler'; import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => { const getUrl = (req: NextApiRequest) => {
const searchParams: Record<string, string> = {}; const searchParamsStr = getSearchParams(req);
Object.entries(req.query).forEach(([ key, value ]) => {
searchParams[key] = Array.isArray(value) ? value.join(',') : (value || '');
});
const searchParamsStr = new URLSearchParams(searchParams).toString();
return `/v2/transactions/${ searchParamsStr ? '?' + searchParamsStr : '' }`; return `/v2/transactions/${ searchParamsStr ? '?' + searchParamsStr : '' }`;
}; };
......
import type { AddressParam } from 'types/api/addressParams'; import type { AddressParam } from 'types/api/addressParams';
import type { Reward } from 'types/api/reward'; import type { Reward } from 'types/api/reward';
import type { Transaction } from 'types/api/transaction';
export type BlockType = 'block' | 'reorg' | 'uncle'; export type BlockType = 'block' | 'reorg' | 'uncle';
...@@ -37,3 +38,12 @@ export interface BlocksResponse { ...@@ -37,3 +38,12 @@ export interface BlocksResponse {
items_count: number; items_count: number;
}; };
} }
export interface BlockTransactionsResponse {
items: Array<Transaction>;
next_page_params: {
block_number: number;
index: number;
items_count: number;
} | null;
}
...@@ -47,5 +47,5 @@ export interface TransactionsResponse { ...@@ -47,5 +47,5 @@ export interface TransactionsResponse {
block_number: number; block_number: number;
index: number; index: number;
items_count: number; items_count: number;
}; } | null;
} }
...@@ -6,4 +6,5 @@ export enum QueryKeys { ...@@ -6,4 +6,5 @@ export enum QueryKeys {
txInternals = 'tx-internals', txInternals = 'tx-internals',
txLog = 'tx-log', txLog = 'tx-log',
txRawTrace = 'tx-raw-trace', txRawTrace = 'tx-raw-trace',
blockTxs = 'block-transactions',
} }
...@@ -109,7 +109,7 @@ const BlockDetails = () => { ...@@ -109,7 +109,7 @@ const BlockDetails = () => {
title="Transactions" title="Transactions"
hint="The number of transactions in the block." hint="The number of transactions in the block."
> >
<Link href={ link('block', { id: router.query.id }, { tab: 'transactions' }) }> <Link href={ link('block', { id: router.query.id }, { tab: 'txs' }) }>
{ data.tx_count } transactions { data.tx_count } transactions
</Link> </Link>
</DetailsInfoItem> </DetailsInfoItem>
......
import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import TxsWithSort from 'ui/txs/TxsWithSort'; import { QueryKeys } from 'types/client/queries';
import TxsContent from 'ui/txs/TxsContent';
const BlockTxs = () => { const BlockTxs = () => {
const router = useRouter();
return ( return (
// <TxsContent <TxsContent
// showDescription={ false } queryName={ QueryKeys.blockTxs }
// showSortButton={ false } apiPath={ `/api/blocks/${ router.query.id }/transactions` }
// txs={ [] } />
// page={ 1 }
// // eslint-disable-next-line react/jsx-no-bind
// onNextPageClick={ () => {} }
// // eslint-disable-next-line react/jsx-no-bind
// onPrevPageClick={ () => {} }
// />
// eslint-disable-next-line react/jsx-no-bind
<TxsWithSort txs={ [] } sort={ () => () => {} }/>
); );
}; };
......
...@@ -2,6 +2,7 @@ import { Alert, Box, HStack, Show, Button } from '@chakra-ui/react'; ...@@ -2,6 +2,7 @@ import { Alert, Box, HStack, Show, Button } from '@chakra-ui/react';
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import type { TTxsFilters } from 'types/api/txsFilters'; import type { TTxsFilters } from 'types/api/txsFilters';
import type { QueryKeys } from 'types/client/queries';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -17,13 +18,17 @@ import TxsWithSort from './TxsWithSort'; ...@@ -17,13 +18,17 @@ import TxsWithSort from './TxsWithSort';
import useQueryWithPages from './useQueryWithPages'; import useQueryWithPages from './useQueryWithPages';
type Props = { type Props = {
queryName: QueryKeys;
showDescription?: boolean; showDescription?: boolean;
stateFilter: TTxsFilters['filter']; stateFilter?: TTxsFilters['filter'];
apiPath: string;
} }
const TxsContent = ({ const TxsContent = ({
queryName,
showDescription, showDescription,
stateFilter, stateFilter,
apiPath,
}: Props) => { }: Props) => {
const [ sorting, setSorting ] = useState<Sort>(); const [ sorting, setSorting ] = useState<Sort>();
// const [ filters, setFilters ] = useState<Partial<TTxsFilters>>({ type: [], method: [] }); // const [ filters, setFilters ] = useState<Partial<TTxsFilters>>({ type: [], method: [] });
...@@ -62,8 +67,8 @@ const TxsContent = ({ ...@@ -62,8 +67,8 @@ const TxsContent = ({
onNextPageClick, onNextPageClick,
hasPagination, hasPagination,
resetPage, resetPage,
} = useQueryWithPages({ filter: stateFilter }); } = useQueryWithPages(apiPath, queryName, stateFilter && { filter: stateFilter });
// } = useQueryWithPages({ ...filters, filter: stateFilter }); // } = useQueryWithPages({ ...filters, filter: stateFilter, apiPath });
const isMobile = useIsMobile(false); const isMobile = useIsMobile(false);
...@@ -120,7 +125,7 @@ const TxsContent = ({ ...@@ -120,7 +125,7 @@ const TxsContent = ({
{ hasPagination ? ( { hasPagination ? (
<Pagination <Pagination
currentPage={ page } currentPage={ page }
hasNextPage={ data?.next_page_params !== undefined && Object.keys(data?.next_page_params).length > 0 } hasNextPage={ data?.next_page_params !== undefined && Object.keys(data?.next_page_params || {}).length > 0 }
onNextPageClick={ onNextPageClick } onNextPageClick={ onNextPageClick }
onPrevPageClick={ onPrevPageClick } onPrevPageClick={ onPrevPageClick }
/> />
......
import React from 'react'; import React from 'react';
import { QueryKeys } from 'types/client/queries';
import TxsContent from './TxsContent'; import TxsContent from './TxsContent';
type Props = { type Props = {
...@@ -9,8 +11,10 @@ type Props = { ...@@ -9,8 +11,10 @@ type Props = {
const TxsTab = ({ tab }: Props) => { const TxsTab = ({ tab }: Props) => {
return ( return (
<TxsContent <TxsContent
queryName={ QueryKeys.transactions }
showDescription={ tab === 'validated' } showDescription={ tab === 'validated' }
stateFilter={ tab } stateFilter={ tab }
apiPath="/api/transactions"
/> />
); );
}; };
......
...@@ -6,13 +6,13 @@ import { animateScroll } from 'react-scroll'; ...@@ -6,13 +6,13 @@ import { animateScroll } from 'react-scroll';
import type { TransactionsResponse } from 'types/api/transaction'; import type { TransactionsResponse } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters'; import type { TTxsFilters } from 'types/api/txsFilters';
import { QueryKeys } from 'types/client/queries'; import type { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
const PAGINATION_FIELDS = [ 'block_number', 'index', 'items_count' ]; const PAGINATION_FIELDS = [ 'block_number', 'index', 'items_count' ];
export default function useQueryWithPages(filters: TTxsFilters) { export default function useQueryWithPages(apiPath: string, queryName: QueryKeys, filters?: TTxsFilters) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter(); const router = useRouter();
const [ page, setPage ] = React.useState(1); const [ page, setPage ] = React.useState(1);
...@@ -21,7 +21,7 @@ export default function useQueryWithPages(filters: TTxsFilters) { ...@@ -21,7 +21,7 @@ export default function useQueryWithPages(filters: TTxsFilters) {
const fetch = useFetch(); const fetch = useFetch();
const { data, isLoading, isError } = useQuery<unknown, unknown, TransactionsResponse>( const { data, isLoading, isError } = useQuery<unknown, unknown, TransactionsResponse>(
[ QueryKeys.transactions, { page, filters } ], [ queryName, { page, filters } ],
async() => { async() => {
const params: Array<string> = []; const params: Array<string> = [];
...@@ -33,7 +33,7 @@ export default function useQueryWithPages(filters: TTxsFilters) { ...@@ -33,7 +33,7 @@ export default function useQueryWithPages(filters: TTxsFilters) {
} }
}); });
return fetch(`/api/transactions${ params.length ? '?' + params.join('&') : '' }`); return fetch(`${ apiPath }${ params.length ? '?' + params.join('&') : '' }`);
}, },
{ staleTime: Infinity }, { staleTime: Infinity },
); );
......
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