Skip to content

Commit

Permalink
Merge pull request #1378 from blockscout/fe-1210
Browse files Browse the repository at this point in the history
Sorting in tables v0.5
  • Loading branch information
isstuev authored Dec 14, 2023
2 parents 2d7ce28 + 9f9707c commit fbba31f
Show file tree
Hide file tree
Showing 24 changed files with 321 additions and 192 deletions.
11 changes: 10 additions & 1 deletion lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,16 @@ import type {
} from 'types/api/token';
import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse, TokensBridgedFilters } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction, TransactionsResponseWatchlist } from 'types/api/transaction';
import type {
TransactionsResponseValidated,
TransactionsResponsePending,
Transaction,
TransactionsResponseWatchlist,
TransactionsSorting,
} from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters';
import type { TxStateChanges } from 'types/api/txStateChanges';
import type { VerifiedContractsSorting } from 'types/api/verifiedContracts';
import type { VisualizedContract } from 'types/api/visualization';
import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals';
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2TxnBatchesResponse, ZkEvmL2TxnBatchTxs } from 'types/api/zkEvmL2TxnBatches';
Expand Down Expand Up @@ -725,5 +732,7 @@ never;
export type PaginationSorting<Q extends PaginatedResources> =
Q extends 'tokens' ? TokensSorting :
Q extends 'tokens_bridged' ? TokensSorting :
Q extends 'verified_contracts' ? VerifiedContractsSorting :
Q extends 'address_txs' ? TransactionsSorting :
never;
/* eslint-enable @typescript-eslint/indent */
21 changes: 0 additions & 21 deletions lib/tx/sortTxs.ts

This file was deleted.

9 changes: 9 additions & 0 deletions types/api/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,12 @@ export type TransactionType = 'rootstock_remasc' |
'coin_transfer'

export type TxsResponse = TransactionsResponseValidated | TransactionsResponsePending | BlockTransactionsResponse;

export interface TransactionsSorting {
sort: 'value' | 'fee';
order: 'asc' | 'desc';
}

export type TransactionsSortingField = TransactionsSorting['sort'];

export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }`;
8 changes: 8 additions & 0 deletions types/api/verifiedContracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface VerifiedContractsSorting {
sort: 'balance' | 'txs_count';
order: 'asc' | 'desc';
}

export type VerifiedContractsSortingField = VerifiedContractsSorting['sort'];

export type VerifiedContractsSortingValue = `${ VerifiedContractsSortingField }-${ VerifiedContractsSorting['order'] }`;
1 change: 0 additions & 1 deletion types/client/txs-sort.ts

This file was deleted.

13 changes: 10 additions & 3 deletions ui/address/AddressTxs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { AddressFromToFilter, AddressTransactionsResponse } from 'types/api/address';
import { AddressFromToFilterValues } from 'types/api/address';
import type { Transaction } from 'types/api/transaction';
import type { Transaction, TransactionsSortingField, TransactionsSortingValue, TransactionsSorting } from 'types/api/transaction';

import { getResourceKey } from 'lib/api/useApiQuery';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
Expand All @@ -18,7 +18,10 @@ import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxsContent from 'ui/txs/TxsContent';
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
import TxsWithAPISorting from 'ui/txs/TxsWithAPISorting';
import { SORT_OPTIONS } from 'ui/txs/useTxsSort';

import AddressCsvExportLink from './AddressCsvExportLink';
import AddressTxsFilter from './AddressTxsFilter';
Expand Down Expand Up @@ -53,6 +56,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {

const [ socketAlert, setSocketAlert ] = React.useState('');
const [ newItemsCount, setNewItemsCount ] = React.useState(0);
const [ sort, setSort ] = React.useState<TransactionsSortingValue | undefined>(getSortValueFromQuery<TransactionsSortingValue>(router.query, SORT_OPTIONS));

const isMobile = useIsMobile();
const currentAddress = getQueryParamString(router.query.hash);
Expand All @@ -63,6 +67,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
resourceName: 'address_txs',
pathParams: { hash: currentAddress },
filters: { filter: filterValue },
sorting: getSortParamsFromValue<TransactionsSortingValue, TransactionsSortingField, TransactionsSorting['order']>(sort),
scrollRef,
options: {
placeholderData: generateListStub<'address_txs'>(TX, 50, { next_page_params: {
Expand Down Expand Up @@ -177,7 +182,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
<Pagination { ...addressTxsQuery.pagination } ml={ 8 }/>
</ActionBar>
) }
<TxsContent
<TxsWithAPISorting
filter={ filter }
filterValue={ filterValue }
query={ addressTxsQuery }
Expand All @@ -187,6 +192,8 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount }
top={ 80 }
sorting={ sort }
setSort={ setSort }
/>
</>
);
Expand Down
4 changes: 2 additions & 2 deletions ui/pages/Block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TxsContent from 'ui/txs/TxsContent';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';

const TAB_LIST_PROPS = {
marginBottom: 0,
Expand Down Expand Up @@ -82,7 +82,7 @@ const BlockPageContent = () => {

const tabs: Array<RoutedTab> = React.useMemo(() => ([
{ id: 'index', title: 'Details', component: <BlockDetails query={ blockQuery }/> },
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
{ id: 'txs', title: 'Transactions', component: <TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
config.features.beaconChain.isEnabled && Boolean(blockQuery.data?.withdrawals_count) ?
{ id: 'withdrawals', title: 'Withdrawals', component: <BlockWithdrawals blockWithdrawalsQuery={ blockWithdrawalsQuery }/> } :
null,
Expand Down
4 changes: 2 additions & 2 deletions ui/pages/KettleTxs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { generateListStub } from 'stubs/utils';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import PageTitle from 'ui/shared/Page/PageTitle';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import TxsContent from 'ui/txs/TxsContent';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';

const KettleTxs = () => {
const router = useRouter();
Expand All @@ -31,7 +31,7 @@ const KettleTxs = () => {
<>
<PageTitle title="Computor transactions" withTextAd/>
<AddressEntity address={{ hash }} mb={ 6 }/>
<TxsContent
<TxsWithFrontendSorting
query={ query }
showSocketInfo={ false }
/>
Expand Down
10 changes: 6 additions & 4 deletions ui/pages/Tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import React from 'react';

import type { TokenType } from 'types/api/token';
import type { TokensSortingValue } from 'types/api/tokens';
import type { TokensSortingValue, TokensSortingField, TokensSorting } from 'types/api/tokens';
import type { RoutedTab } from 'ui/shared/Tabs/types';

import config from 'configs/app';
Expand All @@ -16,11 +16,13 @@ import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
import PageTitle from 'ui/shared/Page/PageTitle';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TokensList from 'ui/tokens/Tokens';
import TokensActionBar from 'ui/tokens/TokensActionBar';
import TokensBridgedChainsFilter from 'ui/tokens/TokensBridgedChainsFilter';
import { getSortParamsFromValue, getSortValueFromQuery, getTokenFilterValue, getBridgedChainsFilterValue } from 'ui/tokens/utils';
import { SORT_OPTIONS, getTokenFilterValue, getBridgedChainsFilterValue } from 'ui/tokens/utils';

const TAB_LIST_PROPS = {
marginBottom: 0,
Expand All @@ -44,7 +46,7 @@ const Tokens = () => {
const q = getQueryParamString(router.query.q);

const [ searchTerm, setSearchTerm ] = React.useState<string>(q ?? '');
const [ sort, setSort ] = React.useState<TokensSortingValue | undefined>(getSortValueFromQuery(router.query));
const [ sort, setSort ] = React.useState<TokensSortingValue | undefined>(getSortValueFromQuery<TokensSortingValue>(router.query, SORT_OPTIONS));
const [ tokenTypes, setTokenTypes ] = React.useState<Array<TokenType> | undefined>(getTokenFilterValue(router.query.type));
const [ bridgeChains, setBridgeChains ] = React.useState<Array<string> | undefined>(getBridgedChainsFilterValue(router.query.chain_ids));

Expand All @@ -53,7 +55,7 @@ const Tokens = () => {
const tokensQuery = useQueryWithPages({
resourceName: tab === 'bridged' ? 'tokens_bridged' : 'tokens',
filters: tab === 'bridged' ? { q: debouncedSearchTerm, chain_ids: bridgeChains } : { q: debouncedSearchTerm, type: tokenTypes },
sorting: getSortParamsFromValue(sort),
sorting: getSortParamsFromValue<TokensSortingValue, TokensSortingField, TokensSorting['order']>(sort),
options: {
placeholderData: generateListStub<'tokens'>(
TOKEN_INFO_ERC_20,
Expand Down
7 changes: 4 additions & 3 deletions ui/pages/Transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TxsContent from 'ui/txs/TxsContent';
import TxsWatchlist from 'ui/txs/TxsWatchlist';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';

const TAB_LIST_PROPS = {
marginBottom: 0,
Expand Down Expand Up @@ -60,12 +60,13 @@ const Transactions = () => {
{
id: 'validated',
title: verifiedTitle,
component: <TxsContent query={ txsQuery } showSocketInfo={ txsQuery.pagination.page === 1 } socketInfoNum={ num } socketInfoAlert={ socketAlert }/> },
component:
<TxsWithFrontendSorting query={ txsQuery } showSocketInfo={ txsQuery.pagination.page === 1 } socketInfoNum={ num } socketInfoAlert={ socketAlert }/> },
{
id: 'pending',
title: 'Pending',
component: (
<TxsContent
<TxsWithFrontendSorting
query={ txsQuery }
showBlockInfo={ false }
showSocketInfo={ txsQuery.pagination.page === 1 }
Expand Down
31 changes: 16 additions & 15 deletions ui/pages/VerifiedContracts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useRouter } from 'next/router';
import React from 'react';

import type { VerifiedContractsFilters } from 'types/api/contracts';
import type { VerifiedContractsSorting, VerifiedContractsSortingField, VerifiedContractsSortingValue } from 'types/api/verifiedContracts';

import useDebounce from 'lib/hooks/useDebounce';
import useIsMobile from 'lib/hooks/useIsMobile';
Expand All @@ -16,9 +17,10 @@ import FilterInput from 'ui/shared/filters/FilterInput';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
import Sort from 'ui/shared/sort/Sort';
import type { SortField, Sort as TSort } from 'ui/verifiedContracts/utils';
import { SORT_OPTIONS, sortFn, getNextSortValue } from 'ui/verifiedContracts/utils';
import { SORT_OPTIONS } from 'ui/verifiedContracts/utils';
import VerifiedContractsCounters from 'ui/verifiedContracts/VerifiedContractsCounters';
import VerifiedContractsFilter from 'ui/verifiedContracts/VerifiedContractsFilter';
import VerifiedContractsList from 'ui/verifiedContracts/VerifiedContractsList';
Expand All @@ -28,15 +30,17 @@ const VerifiedContracts = () => {
const router = useRouter();
const [ searchTerm, setSearchTerm ] = React.useState(getQueryParamString(router.query.q) || undefined);
const [ type, setType ] = React.useState(getQueryParamString(router.query.filter) as VerifiedContractsFilters['filter'] || undefined);
const [ sort, setSort ] = React.useState<TSort>();
const [ sort, setSort ] =
React.useState<VerifiedContractsSortingValue | undefined>(getSortValueFromQuery<VerifiedContractsSortingValue>(router.query, SORT_OPTIONS));

const debouncedSearchTerm = useDebounce(searchTerm || '', 300);

const isMobile = useIsMobile();

const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
const { isError, isPlaceholderData, data, pagination, onFilterChange, onSortingChange } = useQueryWithPages({
resourceName: 'verified_contracts',
filters: { q: debouncedSearchTerm, filter: type },
sorting: getSortParamsFromValue<VerifiedContractsSortingValue, VerifiedContractsSortingField, VerifiedContractsSorting['order']>(sort),
options: {
placeholderData: generateListStub<'verified_contracts'>(
VERIFIED_CONTRACT_INFO,
Expand Down Expand Up @@ -67,11 +71,10 @@ const VerifiedContracts = () => {
setType(filter);
}, [ debouncedSearchTerm, onFilterChange ]);

const handleSortToggle = React.useCallback((field: SortField) => {
return () => {
setSort(getNextSortValue(field));
};
}, []);
const handleSortChange = React.useCallback((value?: VerifiedContractsSortingValue) => {
setSort(value);
onSortingChange(getSortParamsFromValue(value));
}, [ onSortingChange ]);

const typeFilter = <VerifiedContractsFilter onChange={ handleTypeChange } defaultValue={ type } isActive={ Boolean(type) }/>;

Expand All @@ -89,7 +92,7 @@ const VerifiedContracts = () => {
<Sort
options={ SORT_OPTIONS }
sort={ sort }
setSort={ setSort }
setSort={ handleSortChange }
/>
);

Expand All @@ -112,15 +115,13 @@ const VerifiedContracts = () => {
</>
);

const sortedData = data?.items.slice().sort(sortFn(sort));

const content = sortedData ? (
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
<VerifiedContractsList data={ sortedData } isLoading={ isPlaceholderData }/>
<VerifiedContractsList data={ data.items } isLoading={ isPlaceholderData }/>
</Show>
<Hide below="lg" ssr={ false }>
<VerifiedContractsTable data={ sortedData } sort={ sort } onSortToggle={ handleSortToggle } isLoading={ isPlaceholderData }/>
<VerifiedContractsTable data={ data.items } sort={ sort } setSorting={ handleSortChange } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null;
Expand Down
4 changes: 2 additions & 2 deletions ui/pages/ZkEvmL2TxnBatch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TxsContent from 'ui/txs/TxsContent';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
import ZkEvmL2TxnBatchDetails from 'ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails';

const ZkEvmL2TxnBatch = () => {
Expand Down Expand Up @@ -51,7 +51,7 @@ const ZkEvmL2TxnBatch = () => {

const tabs: Array<RoutedTab> = React.useMemo(() => ([
{ id: 'index', title: 'Details', component: <ZkEvmL2TxnBatchDetails query={ batchQuery }/> },
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ batchTxsQuery } showSocketInfo={ false }/> },
{ id: 'txs', title: 'Transactions', component: <TxsWithFrontendSorting query={ batchTxsQuery } showSocketInfo={ false }/> },
].filter(Boolean)), [ batchQuery, batchTxsQuery ]);

const backLink = React.useMemo(() => {
Expand Down
8 changes: 8 additions & 0 deletions ui/shared/sort/getSortParamsFromValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function getSortParamsFromValue<SortValue extends string, SortField extends string, SortOrder extends string>(val?: SortValue) {
if (!val) {
return undefined;
}

const sortingChunks = val.split('-') as [ SortField, SortOrder ];
return { sort: sortingChunks[0], order: sortingChunks[1] };
}
14 changes: 14 additions & 0 deletions ui/shared/sort/getSortValueFromQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Query } from 'nextjs-routes';

import type { Option } from 'ui/shared/sort/Sort';

export default function getSortValueFromQuery<SortValue extends string>(query: Query, sortOptions: Array<Option<SortValue>>) {
if (!query.sort || !query.order) {
return undefined;
}

const str = query.sort + '-' + query.order;
if (sortOptions.map(option => option.id).includes(str as SortValue)) {
return str as SortValue;
}
}
23 changes: 1 addition & 22 deletions ui/tokens/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { TokenType } from 'types/api/token';
import type { TokensSortingField, TokensSortingValue, TokensSorting } from 'types/api/tokens';

import type { Query } from 'nextjs-routes';
import type { TokensSortingValue } from 'types/api/tokens';

import config from 'configs/app';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
Expand Down Expand Up @@ -29,22 +27,3 @@ const bridgedTokensChainIds = (() => {
return feature.chains.map(chain => chain.id);
})();
export const getBridgedChainsFilterValue = (getFilterValuesFromQuery<string>).bind(null, bridgedTokensChainIds);

export const getSortValueFromQuery = (query: Query): TokensSortingValue | undefined => {
if (!query.sort || !query.order) {
return undefined;
}

const str = query.sort + '-' + query.order;
if (SORT_OPTIONS.map(option => option.id).includes(str)) {
return str as TokensSortingValue;
}
};

export const getSortParamsFromValue = (val?: TokensSortingValue): TokensSorting | undefined => {
if (!val) {
return undefined;
}
const sortingChunks = val.split('-') as [ TokensSortingField, TokensSorting['order'] ];
return { sort: sortingChunks[0], order: sortingChunks[1] };
};
Loading

0 comments on commit fbba31f

Please sign in to comment.