Commit c203ef12 authored by tom's avatar tom

rewrite pagination types

parent ae01b122
...@@ -6,35 +6,30 @@ import React, { useCallback } from 'react'; ...@@ -6,35 +6,30 @@ import React, { useCallback } from 'react';
import { animateScroll } from 'react-scroll'; import { animateScroll } from 'react-scroll';
import type { BlockFilters } from 'types/api/block'; import type { BlockFilters } from 'types/api/block';
import type { PaginationParams } from 'types/api/pagination'; import { PAGINATION_FIELDS } from 'types/api/pagination';
import type { PaginationParams, PaginatedResponse, PaginatedQueryKeys } from 'types/api/pagination';
import type { TTxsFilters } from 'types/api/txsFilters'; import type { TTxsFilters } from 'types/api/txsFilters';
import type { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
const PAGINATION_FIELDS: Array<keyof PaginationParams> = [ 'block_number', 'index', 'items_count' ]; interface Params<QueryName extends PaginatedQueryKeys> {
interface ResponseWithPagination {
next_page_params: PaginationParams | null;
}
interface Params<Response> {
apiPath: string; apiPath: string;
queryName: QueryKeys; queryName: QueryName;
queryIds?: Array<string>; queryIds?: Array<string>;
filters?: TTxsFilters | BlockFilters; filters?: TTxsFilters | BlockFilters;
options?: Omit<UseQueryOptions<unknown, unknown, Response>, 'queryKey' | 'queryFn'>; options?: Omit<UseQueryOptions<unknown, unknown, PaginatedResponse<QueryName>>, 'queryKey' | 'queryFn'>;
} }
export default function useQueryWithPages<Response extends ResponseWithPagination>({ queryName, filters, options, apiPath, queryIds }: Params<Response>) { export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>({ queryName, filters, options, apiPath, queryIds }: Params<QueryName>) {
const paginationFields = PAGINATION_FIELDS[queryName];
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter(); const router = useRouter();
const [ page, setPage ] = React.useState(1); const [ page, setPage ] = React.useState(1);
const currPageParams = pick(router.query, PAGINATION_FIELDS); const currPageParams = pick(router.query, paginationFields);
const [ pageParams, setPageParams ] = React.useState<Array<Partial<PaginationParams>>>([ {} ]); const [ pageParams, setPageParams ] = React.useState<Array<PaginationParams<QueryName>>>([ ]);
const fetch = useFetch(); const fetch = useFetch();
const queryResult = useQuery<unknown, unknown, Response>( const queryResult = useQuery<unknown, unknown, PaginatedResponse<QueryName>>(
[ queryName, ...(queryIds || []), { page, filters } ], [ queryName, ...(queryIds || []), { page, filters } ],
async() => { async() => {
const params: Array<string> = []; const params: Array<string> = [];
...@@ -58,9 +53,7 @@ export default function useQueryWithPages<Response extends ResponseWithPaginatio ...@@ -58,9 +53,7 @@ export default function useQueryWithPages<Response extends ResponseWithPaginatio
// we hide next page button if no next_page_params // we hide next page button if no next_page_params
return; return;
} }
// api adds filters into next-page-params now const nextPageParams = data.next_page_params;
// later filters will be removed from response
const nextPageParams = pick(data.next_page_params, PAGINATION_FIELDS);
if (page >= pageParams.length && data?.next_page_params) { if (page >= pageParams.length && data?.next_page_params) {
setPageParams(prev => [ ...prev, nextPageParams ]); setPageParams(prev => [ ...prev, nextPageParams ]);
} }
...@@ -71,18 +64,18 @@ export default function useQueryWithPages<Response extends ResponseWithPaginatio ...@@ -71,18 +64,18 @@ export default function useQueryWithPages<Response extends ResponseWithPaginatio
animateScroll.scrollToTop({ duration: 0 }); animateScroll.scrollToTop({ duration: 0 });
setPage(prev => prev + 1); setPage(prev => prev + 1);
}); });
}, [ data, page, pageParams, router ]); }, [ data?.next_page_params, page, pageParams.length, router ]);
const onPrevPageClick = useCallback(() => { const onPrevPageClick = useCallback(() => {
// returning to the first page // returning to the first page
// we dont have pagination params for the first page // we dont have pagination params for the first page
let nextPageQuery: typeof router.query; let nextPageQuery: typeof router.query;
if (page === 2) { if (page === 2) {
nextPageQuery = omit(router.query, PAGINATION_FIELDS); nextPageQuery = omit(router.query, paginationFields);
} else { } else {
const nextPageParams = { ...pageParams[page - 2] }; const nextPageParams = { ...pageParams[page - 2] };
nextPageQuery = { ...router.query }; nextPageQuery = { ...router.query };
Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString()); nextPageParams && Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString());
} }
router.query = nextPageQuery; router.query = nextPageQuery;
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }) router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
...@@ -91,16 +84,16 @@ export default function useQueryWithPages<Response extends ResponseWithPaginatio ...@@ -91,16 +84,16 @@ export default function useQueryWithPages<Response extends ResponseWithPaginatio
setPage(prev => prev - 1); setPage(prev => prev - 1);
page === 2 && queryClient.clear(); page === 2 && queryClient.clear();
}); });
}, [ router, page, pageParams, queryClient ]); }, [ router, page, paginationFields, pageParams, queryClient ]);
const resetPage = useCallback(() => { const resetPage = useCallback(() => {
queryClient.clear(); queryClient.clear();
router.push({ pathname: router.pathname, query: omit(router.query, PAGINATION_FIELDS) }, undefined, { shallow: true }).then(() => { router.push({ pathname: router.pathname, query: omit(router.query, paginationFields) }, undefined, { shallow: true }).then(() => {
animateScroll.scrollToTop({ duration: 0 }); animateScroll.scrollToTop({ duration: 0 });
setPage(1); setPage(1);
setPageParams([ {} ]); setPageParams([ ]);
}); });
}, [ router, queryClient ]); }, [ queryClient, router, paginationFields ]);
const hasPaginationParams = Object.keys(currPageParams).length > 0; const hasPaginationParams = Object.keys(currPageParams).length > 0;
const nextPageParams = data?.next_page_params; const nextPageParams = data?.next_page_params;
......
import type { AddressParam } from 'types/api/addressParams'; import type { AddressParam } from 'types/api/addressParams';
import type { PaginationParams } from 'types/api/pagination';
import type { Reward } from 'types/api/reward'; import type { Reward } from 'types/api/reward';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
...@@ -34,12 +33,18 @@ export interface Block { ...@@ -34,12 +33,18 @@ export interface Block {
export interface BlocksResponse { export interface BlocksResponse {
items: Array<Block>; items: Array<Block>;
next_page_params: PaginationParams | null; next_page_params: {
block_number: number;
items_count: number;
} | null;
} }
export interface BlockTransactionsResponse { export interface BlockTransactionsResponse {
items: Array<Transaction>; items: Array<Transaction>;
next_page_params: PaginationParams | null; next_page_params: {
block_number: number;
items_count: number;
} | null;
} }
export interface NewBlockSocketResponse { export interface NewBlockSocketResponse {
......
import type { AddressParam } from './addressParams'; import type { AddressParam } from './addressParams';
import type { PaginationParams } from './pagination';
export type TxInternalsType = 'call' | 'delegatecall' | 'staticcall' | 'create' | 'create2' | 'selfdestruct' | 'reward' export type TxInternalsType = 'call' | 'delegatecall' | 'staticcall' | 'create' | 'create2' | 'selfdestruct' | 'reward'
...@@ -20,7 +19,10 @@ export interface InternalTransaction { ...@@ -20,7 +19,10 @@ export interface InternalTransaction {
export interface InternalTransactionsResponse { export interface InternalTransactionsResponse {
items: Array<InternalTransaction>; items: Array<InternalTransaction>;
next_page_params: PaginationParams & { next_page_params: {
block_number: number;
index: number;
items_count: number;
transaction_hash: string; transaction_hash: string;
transaction_index: number; transaction_index: number;
}; };
......
export interface PaginationParams { import type { BlocksResponse, BlockTransactionsResponse } from 'types/api/block';
block_number: number; import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
index?: number; import type { TokenTransferResponse } from 'types/api/tokenTransfer';
items_count: number; import type { TransactionsResponseValidated, TransactionsResponsePending } from 'types/api/transaction';
import { QueryKeys } from 'types/client/queries';
import type { KeysOfObjectOrNull } from 'types/utils/KeysOfObjectOrNull';
export type PaginatedQueryKeys =
QueryKeys.blocks |
QueryKeys.blockTxs |
QueryKeys.txsValidate |
QueryKeys.txsPending |
QueryKeys.txInternals |
QueryKeys.txTokenTransfers;
export type PaginatedResponse<Q extends PaginatedQueryKeys> =
Q extends QueryKeys.blocks ? BlocksResponse :
Q extends QueryKeys.blockTxs ? BlockTransactionsResponse :
Q extends QueryKeys.txsValidate ? TransactionsResponseValidated :
Q extends QueryKeys.txsPending ? TransactionsResponsePending :
Q extends QueryKeys.txInternals ? InternalTransactionsResponse :
Q extends QueryKeys.txTokenTransfers ? TokenTransferResponse :
never
export type PaginationParams<Q extends PaginatedQueryKeys> = PaginatedResponse<Q>['next_page_params'];
type PaginationFields = {
[K in PaginatedQueryKeys]: Array<KeysOfObjectOrNull<PaginatedResponse<K>['next_page_params']>>
} }
export const PAGINATION_FIELDS: PaginationFields = {
[QueryKeys.blocks]: [ 'block_number', 'items_count' ],
[QueryKeys.blockTxs]: [ 'block_number', 'items_count' ],
[QueryKeys.txsValidate]: [ 'block_number', 'items_count', 'filter', 'index' ],
[QueryKeys.txsPending]: [ 'filter', 'hash', 'inserted_at' ],
[QueryKeys.txInternals]: [ 'block_number', 'items_count', 'transaction_hash', 'index', 'transaction_index' ],
[QueryKeys.txTokenTransfers]: [ 'block_number', 'items_count', 'transaction_hash', 'index' ],
};
import type { AddressParam } from './addressParams'; import type { AddressParam } from './addressParams';
import type { PaginationParams } from './pagination';
import type { TokenInfoGeneric } from './tokenInfo'; import type { TokenInfoGeneric } from './tokenInfo';
export type Erc20TotalPayload = { export type Erc20TotalPayload = {
...@@ -41,5 +40,10 @@ interface TokenTransferBase { ...@@ -41,5 +40,10 @@ interface TokenTransferBase {
export interface TokenTransferResponse { export interface TokenTransferResponse {
items: Array<TokenTransfer>; items: Array<TokenTransfer>;
next_page_params: PaginationParams | null; next_page_params: {
block_number: number;
index: number;
items_count: number;
transaction_hash: string;
} | null;
} }
import type { AddressParam } from './addressParams'; import type { AddressParam } from './addressParams';
import type { DecodedInput } from './decodedInput'; import type { DecodedInput } from './decodedInput';
import type { Fee } from './fee'; import type { Fee } from './fee';
import type { PaginationParams } from './pagination';
import type { TokenTransfer } from './tokenTransfer'; import type { TokenTransfer } from './tokenTransfer';
export type TransactionRevertReason = { export type TransactionRevertReason = {
...@@ -44,9 +43,25 @@ export interface Transaction { ...@@ -44,9 +43,25 @@ export interface Transaction {
tx_tag: string | null; tx_tag: string | null;
} }
export interface TransactionsResponse { export type TransactionsResponse = TransactionsResponseValidated | TransactionsResponsePending;
export interface TransactionsResponseValidated {
items: Array<Transaction>;
next_page_params: {
block_number: number;
index: number;
items_count: number;
filter: 'validated';
} | null;
}
export interface TransactionsResponsePending {
items: Array<Transaction>; items: Array<Transaction>;
next_page_params: PaginationParams | null; next_page_params: {
inserted_at: string;
hash: string;
filter: 'pending';
} | null;
} }
export type TransactionType = 'token_transfer' | 'contract_creation' | 'contract_call' | 'token_creation' | 'coin_transfer' export type TransactionType = 'token_transfer' | 'contract_creation' | 'contract_call' | 'token_creation' | 'coin_transfer'
export enum QueryKeys { export enum QueryKeys {
csrf = 'csrf', csrf = 'csrf',
profile = 'profile', profile = 'profile',
transactions = 'transactions', txsValidate = 'txs-validated',
txsPending = 'txs-pending',
tx = 'tx', tx = 'tx',
txInternals = 'tx-internals', txInternals = 'tx-internals',
txLog = 'tx-log', txLog = 'tx-log',
......
export type KeysOfObjectOrNull<T> = T extends null ? never : keyof T;
...@@ -25,7 +25,7 @@ const BlocksContent = ({ type }: Props) => { ...@@ -25,7 +25,7 @@ const BlocksContent = ({ type }: Props) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [ socketAlert, setSocketAlert ] = React.useState(''); const [ socketAlert, setSocketAlert ] = React.useState('');
const { data, isLoading, isError, pagination } = useQueryWithPages<BlocksResponse>({ const { data, isLoading, isError, pagination } = useQueryWithPages({
apiPath: '/node-api/blocks', apiPath: '/node-api/blocks',
queryName: QueryKeys.blocks, queryName: QueryKeys.blocks,
filters: { type }, filters: { type },
......
import { Hide, Show, Text, Flex, Skeleton } from '@chakra-ui/react'; import { Hide, Show, Text, Flex, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenTransferResponse } from 'types/api/tokenTransfer';
import type { QueryKeys } from 'types/client/queries'; import type { QueryKeys } from 'types/client/queries';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
...@@ -19,7 +18,7 @@ interface Props { ...@@ -19,7 +18,7 @@ interface Props {
isLoading?: boolean; isLoading?: boolean;
isDisabled?: boolean; isDisabled?: boolean;
path: string; path: string;
queryName: QueryKeys; queryName: QueryKeys.txTokenTransfers;
queryIds?: Array<string>; queryIds?: Array<string>;
baseAddress?: string; baseAddress?: string;
showTxInfo?: boolean; showTxInfo?: boolean;
...@@ -27,7 +26,7 @@ interface Props { ...@@ -27,7 +26,7 @@ interface Props {
} }
const TokenTransfer = ({ isLoading: isLoadingProp, isDisabled, queryName, queryIds, path, baseAddress, showTxInfo = true, txHash }: Props) => { const TokenTransfer = ({ isLoading: isLoadingProp, isDisabled, queryName, queryIds, path, baseAddress, showTxInfo = true, txHash }: Props) => {
const { isError, isLoading, data, pagination } = useQueryWithPages<TokenTransferResponse>({ const { isError, isLoading, data, pagination } = useQueryWithPages({
apiPath: path, apiPath: path,
queryName, queryName,
queryIds, queryIds,
......
import { Text, Box, Show, Hide } from '@chakra-ui/react'; import { Text, Box, Show, Hide } from '@chakra-ui/react';
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters'; import type { TTxsFilters } from 'types/api/txsFilters';
import type { QueryKeys } from 'types/client/queries'; import type { QueryKeys } from 'types/client/queries';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
...@@ -16,7 +15,7 @@ import TxsSkeletonMobile from './TxsSkeletonMobile'; ...@@ -16,7 +15,7 @@ import TxsSkeletonMobile from './TxsSkeletonMobile';
import TxsWithSort from './TxsWithSort'; import TxsWithSort from './TxsWithSort';
type Props = { type Props = {
queryName: QueryKeys; queryName: QueryKeys.txsPending | QueryKeys.txsValidate | QueryKeys.blockTxs;
showDescription?: boolean; showDescription?: boolean;
stateFilter?: TTxsFilters['filter']; stateFilter?: TTxsFilters['filter'];
apiPath: string; apiPath: string;
...@@ -62,7 +61,7 @@ const TxsContent = ({ ...@@ -62,7 +61,7 @@ const TxsContent = ({
isLoading, isLoading,
isError, isError,
pagination, pagination,
} = useQueryWithPages<TransactionsResponse>({ } = useQueryWithPages({
apiPath, apiPath,
queryName, queryName,
filters: stateFilter ? { filter: stateFilter } : undefined, filters: stateFilter ? { filter: stateFilter } : undefined,
......
...@@ -9,11 +9,21 @@ type Props = { ...@@ -9,11 +9,21 @@ type Props = {
} }
const TxsTab = ({ tab }: Props) => { const TxsTab = ({ tab }: Props) => {
if (tab === 'validated') {
return (
<TxsContent
queryName={ QueryKeys.txsValidate }
showDescription
stateFilter="validated"
apiPath="/api/transactions"
/>
);
}
return ( return (
<TxsContent <TxsContent
queryName={ QueryKeys.transactions } queryName={ QueryKeys.txsPending }
showDescription={ tab === 'validated' } stateFilter="pending"
stateFilter={ tab }
apiPath="/api/transactions" apiPath="/api/transactions"
/> />
); );
......
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