Skip to content

Commit fbba31f

Browse files
authored
Merge pull request blockscout#1378 from blockscout/fe-1210
Sorting in tables v0.5
2 parents 2d7ce28 + 9f9707c commit fbba31f

24 files changed

+321
-192
lines changed

lib/api/resources.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,16 @@ import type {
5858
} from 'types/api/token';
5959
import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse, TokensBridgedFilters } from 'types/api/tokens';
6060
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
61-
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction, TransactionsResponseWatchlist } from 'types/api/transaction';
61+
import type {
62+
TransactionsResponseValidated,
63+
TransactionsResponsePending,
64+
Transaction,
65+
TransactionsResponseWatchlist,
66+
TransactionsSorting,
67+
} from 'types/api/transaction';
6268
import type { TTxsFilters } from 'types/api/txsFilters';
6369
import type { TxStateChanges } from 'types/api/txStateChanges';
70+
import type { VerifiedContractsSorting } from 'types/api/verifiedContracts';
6471
import type { VisualizedContract } from 'types/api/visualization';
6572
import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals';
6673
import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2TxnBatchesResponse, ZkEvmL2TxnBatchTxs } from 'types/api/zkEvmL2TxnBatches';
@@ -725,5 +732,7 @@ never;
725732
export type PaginationSorting<Q extends PaginatedResources> =
726733
Q extends 'tokens' ? TokensSorting :
727734
Q extends 'tokens_bridged' ? TokensSorting :
735+
Q extends 'verified_contracts' ? VerifiedContractsSorting :
736+
Q extends 'address_txs' ? TransactionsSorting :
728737
never;
729738
/* eslint-enable @typescript-eslint/indent */

lib/tx/sortTxs.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

types/api/transaction.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,12 @@ export type TransactionType = 'rootstock_remasc' |
117117
'coin_transfer'
118118

119119
export type TxsResponse = TransactionsResponseValidated | TransactionsResponsePending | BlockTransactionsResponse;
120+
121+
export interface TransactionsSorting {
122+
sort: 'value' | 'fee';
123+
order: 'asc' | 'desc';
124+
}
125+
126+
export type TransactionsSortingField = TransactionsSorting['sort'];
127+
128+
export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }`;

types/api/verifiedContracts.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface VerifiedContractsSorting {
2+
sort: 'balance' | 'txs_count';
3+
order: 'asc' | 'desc';
4+
}
5+
6+
export type VerifiedContractsSortingField = VerifiedContractsSorting['sort'];
7+
8+
export type VerifiedContractsSortingValue = `${ VerifiedContractsSortingField }-${ VerifiedContractsSorting['order'] }`;

types/client/txs-sort.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

ui/address/AddressTxs.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import React from 'react';
55
import type { SocketMessage } from 'lib/socket/types';
66
import type { AddressFromToFilter, AddressTransactionsResponse } from 'types/api/address';
77
import { AddressFromToFilterValues } from 'types/api/address';
8-
import type { Transaction } from 'types/api/transaction';
8+
import type { Transaction, TransactionsSortingField, TransactionsSortingValue, TransactionsSorting } from 'types/api/transaction';
99

1010
import { getResourceKey } from 'lib/api/useApiQuery';
1111
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
@@ -18,7 +18,10 @@ import { generateListStub } from 'stubs/utils';
1818
import ActionBar from 'ui/shared/ActionBar';
1919
import Pagination from 'ui/shared/pagination/Pagination';
2020
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
21-
import TxsContent from 'ui/txs/TxsContent';
21+
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
22+
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
23+
import TxsWithAPISorting from 'ui/txs/TxsWithAPISorting';
24+
import { SORT_OPTIONS } from 'ui/txs/useTxsSort';
2225

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

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

5761
const isMobile = useIsMobile();
5862
const currentAddress = getQueryParamString(router.query.hash);
@@ -63,6 +67,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
6367
resourceName: 'address_txs',
6468
pathParams: { hash: currentAddress },
6569
filters: { filter: filterValue },
70+
sorting: getSortParamsFromValue<TransactionsSortingValue, TransactionsSortingField, TransactionsSorting['order']>(sort),
6671
scrollRef,
6772
options: {
6873
placeholderData: generateListStub<'address_txs'>(TX, 50, { next_page_params: {
@@ -177,7 +182,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
177182
<Pagination { ...addressTxsQuery.pagination } ml={ 8 }/>
178183
</ActionBar>
179184
) }
180-
<TxsContent
185+
<TxsWithAPISorting
181186
filter={ filter }
182187
filterValue={ filterValue }
183188
query={ addressTxsQuery }
@@ -187,6 +192,8 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
187192
socketInfoAlert={ socketAlert }
188193
socketInfoNum={ newItemsCount }
189194
top={ 80 }
195+
sorting={ sort }
196+
setSort={ setSort }
190197
/>
191198
</>
192199
);

ui/pages/Block.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import Pagination from 'ui/shared/pagination/Pagination';
2424
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
2525
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
2626
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
27-
import TxsContent from 'ui/txs/TxsContent';
27+
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
2828

2929
const TAB_LIST_PROPS = {
3030
marginBottom: 0,
@@ -82,7 +82,7 @@ const BlockPageContent = () => {
8282

8383
const tabs: Array<RoutedTab> = React.useMemo(() => ([
8484
{ id: 'index', title: 'Details', component: <BlockDetails query={ blockQuery }/> },
85-
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
85+
{ id: 'txs', title: 'Transactions', component: <TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
8686
config.features.beaconChain.isEnabled && Boolean(blockQuery.data?.withdrawals_count) ?
8787
{ id: 'withdrawals', title: 'Withdrawals', component: <BlockWithdrawals blockWithdrawalsQuery={ blockWithdrawalsQuery }/> } :
8888
null,

ui/pages/KettleTxs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { generateListStub } from 'stubs/utils';
77
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
88
import PageTitle from 'ui/shared/Page/PageTitle';
99
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
10-
import TxsContent from 'ui/txs/TxsContent';
10+
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
1111

1212
const KettleTxs = () => {
1313
const router = useRouter();
@@ -31,7 +31,7 @@ const KettleTxs = () => {
3131
<>
3232
<PageTitle title="Computor transactions" withTextAd/>
3333
<AddressEntity address={{ hash }} mb={ 6 }/>
34-
<TxsContent
34+
<TxsWithFrontendSorting
3535
query={ query }
3636
showSocketInfo={ false }
3737
/>

ui/pages/Tokens.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
33
import React from 'react';
44

55
import type { TokenType } from 'types/api/token';
6-
import type { TokensSortingValue } from 'types/api/tokens';
6+
import type { TokensSortingValue, TokensSortingField, TokensSorting } from 'types/api/tokens';
77
import type { RoutedTab } from 'ui/shared/Tabs/types';
88

99
import config from 'configs/app';
@@ -16,11 +16,13 @@ import PopoverFilter from 'ui/shared/filters/PopoverFilter';
1616
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
1717
import PageTitle from 'ui/shared/Page/PageTitle';
1818
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
19+
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
20+
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
1921
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
2022
import TokensList from 'ui/tokens/Tokens';
2123
import TokensActionBar from 'ui/tokens/TokensActionBar';
2224
import TokensBridgedChainsFilter from 'ui/tokens/TokensBridgedChainsFilter';
23-
import { getSortParamsFromValue, getSortValueFromQuery, getTokenFilterValue, getBridgedChainsFilterValue } from 'ui/tokens/utils';
25+
import { SORT_OPTIONS, getTokenFilterValue, getBridgedChainsFilterValue } from 'ui/tokens/utils';
2426

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

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

@@ -53,7 +55,7 @@ const Tokens = () => {
5355
const tokensQuery = useQueryWithPages({
5456
resourceName: tab === 'bridged' ? 'tokens_bridged' : 'tokens',
5557
filters: tab === 'bridged' ? { q: debouncedSearchTerm, chain_ids: bridgeChains } : { q: debouncedSearchTerm, type: tokenTypes },
56-
sorting: getSortParamsFromValue(sort),
58+
sorting: getSortParamsFromValue<TokensSortingValue, TokensSortingField, TokensSorting['order']>(sort),
5759
options: {
5860
placeholderData: generateListStub<'tokens'>(
5961
TOKEN_INFO_ERC_20,

ui/pages/Transactions.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import PageTitle from 'ui/shared/Page/PageTitle';
1313
import Pagination from 'ui/shared/pagination/Pagination';
1414
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
1515
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
16-
import TxsContent from 'ui/txs/TxsContent';
1716
import TxsWatchlist from 'ui/txs/TxsWatchlist';
17+
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
1818

1919
const TAB_LIST_PROPS = {
2020
marginBottom: 0,
@@ -60,12 +60,13 @@ const Transactions = () => {
6060
{
6161
id: 'validated',
6262
title: verifiedTitle,
63-
component: <TxsContent query={ txsQuery } showSocketInfo={ txsQuery.pagination.page === 1 } socketInfoNum={ num } socketInfoAlert={ socketAlert }/> },
63+
component:
64+
<TxsWithFrontendSorting query={ txsQuery } showSocketInfo={ txsQuery.pagination.page === 1 } socketInfoNum={ num } socketInfoAlert={ socketAlert }/> },
6465
{
6566
id: 'pending',
6667
title: 'Pending',
6768
component: (
68-
<TxsContent
69+
<TxsWithFrontendSorting
6970
query={ txsQuery }
7071
showBlockInfo={ false }
7172
showSocketInfo={ txsQuery.pagination.page === 1 }

ui/pages/VerifiedContracts.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useRouter } from 'next/router';
33
import React from 'react';
44

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

78
import useDebounce from 'lib/hooks/useDebounce';
89
import useIsMobile from 'lib/hooks/useIsMobile';
@@ -16,9 +17,10 @@ import FilterInput from 'ui/shared/filters/FilterInput';
1617
import PageTitle from 'ui/shared/Page/PageTitle';
1718
import Pagination from 'ui/shared/pagination/Pagination';
1819
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
20+
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
21+
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
1922
import Sort from 'ui/shared/sort/Sort';
20-
import type { SortField, Sort as TSort } from 'ui/verifiedContracts/utils';
21-
import { SORT_OPTIONS, sortFn, getNextSortValue } from 'ui/verifiedContracts/utils';
23+
import { SORT_OPTIONS } from 'ui/verifiedContracts/utils';
2224
import VerifiedContractsCounters from 'ui/verifiedContracts/VerifiedContractsCounters';
2325
import VerifiedContractsFilter from 'ui/verifiedContracts/VerifiedContractsFilter';
2426
import VerifiedContractsList from 'ui/verifiedContracts/VerifiedContractsList';
@@ -28,15 +30,17 @@ const VerifiedContracts = () => {
2830
const router = useRouter();
2931
const [ searchTerm, setSearchTerm ] = React.useState(getQueryParamString(router.query.q) || undefined);
3032
const [ type, setType ] = React.useState(getQueryParamString(router.query.filter) as VerifiedContractsFilters['filter'] || undefined);
31-
const [ sort, setSort ] = React.useState<TSort>();
33+
const [ sort, setSort ] =
34+
React.useState<VerifiedContractsSortingValue | undefined>(getSortValueFromQuery<VerifiedContractsSortingValue>(router.query, SORT_OPTIONS));
3235

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

3538
const isMobile = useIsMobile();
3639

37-
const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
40+
const { isError, isPlaceholderData, data, pagination, onFilterChange, onSortingChange } = useQueryWithPages({
3841
resourceName: 'verified_contracts',
3942
filters: { q: debouncedSearchTerm, filter: type },
43+
sorting: getSortParamsFromValue<VerifiedContractsSortingValue, VerifiedContractsSortingField, VerifiedContractsSorting['order']>(sort),
4044
options: {
4145
placeholderData: generateListStub<'verified_contracts'>(
4246
VERIFIED_CONTRACT_INFO,
@@ -67,11 +71,10 @@ const VerifiedContracts = () => {
6771
setType(filter);
6872
}, [ debouncedSearchTerm, onFilterChange ]);
6973

70-
const handleSortToggle = React.useCallback((field: SortField) => {
71-
return () => {
72-
setSort(getNextSortValue(field));
73-
};
74-
}, []);
74+
const handleSortChange = React.useCallback((value?: VerifiedContractsSortingValue) => {
75+
setSort(value);
76+
onSortingChange(getSortParamsFromValue(value));
77+
}, [ onSortingChange ]);
7578

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

@@ -89,7 +92,7 @@ const VerifiedContracts = () => {
8992
<Sort
9093
options={ SORT_OPTIONS }
9194
sort={ sort }
92-
setSort={ setSort }
95+
setSort={ handleSortChange }
9396
/>
9497
);
9598

@@ -112,15 +115,13 @@ const VerifiedContracts = () => {
112115
</>
113116
);
114117

115-
const sortedData = data?.items.slice().sort(sortFn(sort));
116-
117-
const content = sortedData ? (
118+
const content = data?.items ? (
118119
<>
119120
<Show below="lg" ssr={ false }>
120-
<VerifiedContractsList data={ sortedData } isLoading={ isPlaceholderData }/>
121+
<VerifiedContractsList data={ data.items } isLoading={ isPlaceholderData }/>
121122
</Show>
122123
<Hide below="lg" ssr={ false }>
123-
<VerifiedContractsTable data={ sortedData } sort={ sort } onSortToggle={ handleSortToggle } isLoading={ isPlaceholderData }/>
124+
<VerifiedContractsTable data={ data.items } sort={ sort } setSorting={ handleSortChange } isLoading={ isPlaceholderData }/>
124125
</Hide>
125126
</>
126127
) : null;

ui/pages/ZkEvmL2TxnBatch.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
1414
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
1515
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
1616
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
17-
import TxsContent from 'ui/txs/TxsContent';
17+
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
1818
import ZkEvmL2TxnBatchDetails from 'ui/zkEvmL2TxnBatches/ZkEvmL2TxnBatchDetails';
1919

2020
const ZkEvmL2TxnBatch = () => {
@@ -51,7 +51,7 @@ const ZkEvmL2TxnBatch = () => {
5151

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

5757
const backLink = React.useMemo(() => {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function getSortParamsFromValue<SortValue extends string, SortField extends string, SortOrder extends string>(val?: SortValue) {
2+
if (!val) {
3+
return undefined;
4+
}
5+
6+
const sortingChunks = val.split('-') as [ SortField, SortOrder ];
7+
return { sort: sortingChunks[0], order: sortingChunks[1] };
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Query } from 'nextjs-routes';
2+
3+
import type { Option } from 'ui/shared/sort/Sort';
4+
5+
export default function getSortValueFromQuery<SortValue extends string>(query: Query, sortOptions: Array<Option<SortValue>>) {
6+
if (!query.sort || !query.order) {
7+
return undefined;
8+
}
9+
10+
const str = query.sort + '-' + query.order;
11+
if (sortOptions.map(option => option.id).includes(str as SortValue)) {
12+
return str as SortValue;
13+
}
14+
}

ui/tokens/utils.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { TokenType } from 'types/api/token';
2-
import type { TokensSortingField, TokensSortingValue, TokensSorting } from 'types/api/tokens';
3-
4-
import type { Query } from 'nextjs-routes';
2+
import type { TokensSortingValue } from 'types/api/tokens';
53

64
import config from 'configs/app';
75
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
@@ -29,22 +27,3 @@ const bridgedTokensChainIds = (() => {
2927
return feature.chains.map(chain => chain.id);
3028
})();
3129
export const getBridgedChainsFilterValue = (getFilterValuesFromQuery<string>).bind(null, bridgedTokensChainIds);
32-
33-
export const getSortValueFromQuery = (query: Query): TokensSortingValue | undefined => {
34-
if (!query.sort || !query.order) {
35-
return undefined;
36-
}
37-
38-
const str = query.sort + '-' + query.order;
39-
if (SORT_OPTIONS.map(option => option.id).includes(str)) {
40-
return str as TokensSortingValue;
41-
}
42-
};
43-
44-
export const getSortParamsFromValue = (val?: TokensSortingValue): TokensSorting | undefined => {
45-
if (!val) {
46-
return undefined;
47-
}
48-
const sortingChunks = val.split('-') as [ TokensSortingField, TokensSorting['order'] ];
49-
return { sort: sortingChunks[0], order: sortingChunks[1] };
50-
};

0 commit comments

Comments
 (0)