Commit d034f644 authored by isstuev's avatar isstuev

tokens sorting

parent 057f4d1f
...@@ -50,7 +50,7 @@ import type { ...@@ -50,7 +50,7 @@ import type {
TokenInstanceTransfersCount, TokenInstanceTransfersCount,
TokenVerifiedInfo, TokenVerifiedInfo,
} from 'types/api/token'; } from 'types/api/token';
import type { TokensResponse, TokensFilters, TokenInstanceTransferResponse } from 'types/api/tokens'; import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer'; import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction, TransactionsResponseWatchlist } from 'types/api/transaction'; import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction, TransactionsResponseWatchlist } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters'; import type { TTxsFilters } from 'types/api/txsFilters';
...@@ -69,6 +69,8 @@ export interface ApiResource { ...@@ -69,6 +69,8 @@ export interface ApiResource {
needAuth?: boolean; // for external APIs which require authentication needAuth?: boolean; // for external APIs which require authentication
} }
export const SORTING_FIELDS = [ 'sort', 'order' ];
export const RESOURCES = { export const RESOURCES = {
// ACCOUNT // ACCOUNT
csrf: { csrf: {
...@@ -633,3 +635,9 @@ Q extends 'tokens' ? TokensFilters : ...@@ -633,3 +635,9 @@ Q extends 'tokens' ? TokensFilters :
Q extends 'verified_contracts' ? VerifiedContractsFilters : Q extends 'verified_contracts' ? VerifiedContractsFilters :
never; never;
/* eslint-enable @typescript-eslint/indent */ /* eslint-enable @typescript-eslint/indent */
/* eslint-disable @typescript-eslint/indent */
export type PaginationSorting<Q extends PaginatedResources> =
Q extends 'tokens' ? TokensSorting :
never;
/* eslint-enable @typescript-eslint/indent */
...@@ -24,3 +24,8 @@ export interface TokenInstanceTransferPagination { ...@@ -24,3 +24,8 @@ export interface TokenInstanceTransferPagination {
items_count: number; items_count: number;
token_id: string; token_id: string;
} }
export interface TokensSorting {
sort: 'fiat_value' | 'holder_count' | 'circulating_market_cap';
order: 'asc' | 'desc';
}
...@@ -45,6 +45,10 @@ const responses = { ...@@ -45,6 +45,10 @@ const responses = {
items_count: 43, items_count: 43,
}, },
}, },
page_sorted: {
items: [ { hash: '61' }, { hash: '62' } ],
next_page_params: null,
},
}; };
beforeEach(() => { beforeEach(() => {
...@@ -423,13 +427,16 @@ describe('if there is page query param in URL', () => { ...@@ -423,13 +427,16 @@ describe('if there is page query param in URL', () => {
}); });
describe('queries with filters', () => { describe('queries with filters', () => {
it('reset page when filter is changed', async() => { it('reset page, keep sorting when filter is changed', async() => {
const routerPush = jest.fn(() => Promise.resolve()); const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { foo: 'bar' } }); useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { foo: 'bar', sort: 'val-desc' } });
const params: Params<'address_txs'> = { const params: Params<'address_txs'> = {
resourceName: 'address_txs', resourceName: 'address_txs',
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
sorting: { sort: 'val-desc' },
}; };
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2));
...@@ -462,7 +469,7 @@ describe('queries with filters', () => { ...@@ -462,7 +469,7 @@ describe('queries with filters', () => {
expect(routerPush).toHaveBeenLastCalledWith( expect(routerPush).toHaveBeenLastCalledWith(
{ {
pathname: '/current-route', pathname: '/current-route',
query: { filter: 'from', foo: 'bar' }, query: { filter: 'from', foo: 'bar', sort: 'val-desc' },
}, },
undefined, undefined,
{ shallow: true }, { shallow: true },
...@@ -508,6 +515,98 @@ describe('queries with filters', () => { ...@@ -508,6 +515,98 @@ describe('queries with filters', () => {
}); });
}); });
describe('queries with sorting', () => {
it('reset page, save filter when sorting is changed', async() => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { foo: 'bar', filter: 'from' } });
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
filters: { filter: 'from' },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_sorted));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
await act(async() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
result.current.onSortingChange({ sort: 'val-desc' });
});
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_sorted);
expect(result.current.pagination).toMatchObject({
page: 1,
canGoBackwards: true,
hasNextPage: false,
isLoading: false,
isVisible: false,
hasPages: false,
});
expect(routerPush).toHaveBeenCalledTimes(2);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: { filter: 'from', foo: 'bar', sort: 'val-desc' },
},
undefined,
{ shallow: true },
);
expect(animateScroll.scrollToTop).toHaveBeenCalledTimes(2);
expect(animateScroll.scrollToTop).toHaveBeenLastCalledWith({ duration: 0 });
});
it('saves sorting params in query when navigating between pages', async() => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush, query: { foo: 'bar', sort: 'val-desc' } });
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
sorting: { sort: 'val-desc' },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
await act(async() => {
result.current.pagination.onNextPageClick();
});
await waitForApiResponse();
expect(routerPush).toHaveBeenCalledTimes(1);
expect(routerPush).toHaveBeenLastCalledWith(
{
pathname: '/current-route',
query: {
sort: 'val-desc',
foo: 'bar',
next_page_params: encodeURIComponent(JSON.stringify(responses.page_1.next_page_params)),
page: '2',
},
},
undefined,
{ shallow: true },
);
});
});
async function waitForApiResponse() { async function waitForApiResponse() {
await flushPromises(); await flushPromises();
await act(flushPromises); await act(flushPromises);
......
...@@ -7,8 +7,8 @@ import { animateScroll } from 'react-scroll'; ...@@ -7,8 +7,8 @@ import { animateScroll } from 'react-scroll';
import type { PaginationParams } from './types'; import type { PaginationParams } from './types';
import type { PaginatedResources, PaginationFilters, ResourceError, ResourcePayload } from 'lib/api/resources'; import type { PaginatedResources, PaginationFilters, PaginationSorting, ResourceError, ResourcePayload } from 'lib/api/resources';
import { RESOURCES } from 'lib/api/resources'; import { RESOURCES, SORTING_FIELDS } from 'lib/api/resources';
import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery'; import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
...@@ -18,6 +18,7 @@ export interface Params<Resource extends PaginatedResources> { ...@@ -18,6 +18,7 @@ export interface Params<Resource extends PaginatedResources> {
options?: UseApiQueryParams<Resource>['queryOptions']; options?: UseApiQueryParams<Resource>['queryOptions'];
pathParams?: UseApiQueryParams<Resource>['pathParams']; pathParams?: UseApiQueryParams<Resource>['pathParams'];
filters?: PaginationFilters<Resource>; filters?: PaginationFilters<Resource>;
sorting?: PaginationSorting<Resource>;
scrollRef?: React.RefObject<HTMLDivElement>; scrollRef?: React.RefObject<HTMLDivElement>;
} }
...@@ -37,12 +38,14 @@ export type QueryWithPagesResult<Resource extends PaginatedResources> = ...@@ -37,12 +38,14 @@ export type QueryWithPagesResult<Resource extends PaginatedResources> =
UseQueryResult<ResourcePayload<Resource>, ResourceError<unknown>> & UseQueryResult<ResourcePayload<Resource>, ResourceError<unknown>> &
{ {
onFilterChange: (filters: PaginationFilters<Resource>) => void; onFilterChange: (filters: PaginationFilters<Resource>) => void;
onSortingChange: (sorting?: PaginationSorting<Resource>) => void;
pagination: PaginationParams; pagination: PaginationParams;
} }
export default function useQueryWithPages<Resource extends PaginatedResources>({ export default function useQueryWithPages<Resource extends PaginatedResources>({
resourceName, resourceName,
filters, filters,
sorting,
options, options,
pathParams, pathParams,
scrollRef, scrollRef,
...@@ -59,7 +62,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -59,7 +62,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
const isMounted = React.useRef(false); const isMounted = React.useRef(false);
const canGoBackwards = React.useRef(!router.query.page); const canGoBackwards = React.useRef(!router.query.page);
const queryParams = { ...pageParams[page], ...filters }; const queryParams = { ...pageParams[page], ...filters, ...sorting };
const scrollToTop = useCallback(() => { const scrollToTop = useCallback(() => {
scrollRef?.current ? scrollRef.current.scrollIntoView(true) : animateScroll.scrollToTop({ duration: 0 }); scrollRef?.current ? scrollRef.current.scrollIntoView(true) : animateScroll.scrollToTop({ duration: 0 });
...@@ -160,6 +163,26 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -160,6 +163,26 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
}); });
}, [ router, resource.filterFields, scrollToTop ]); }, [ router, resource.filterFields, scrollToTop ]);
const onSortingChange = useCallback((newSorting: PaginationSorting<Resource> | undefined) => {
const newQuery = {
...omit<typeof router.query>(router.query, 'next_page_params', 'page', SORTING_FIELDS),
...newSorting,
};
scrollToTop();
router.push(
{
pathname: router.pathname,
query: newQuery,
},
undefined,
{ shallow: true },
).then(() => {
setHasPages(false);
setPage(1);
setPageParams({});
});
}, [ router, scrollToTop ]);
const nextPageParams = data?.next_page_params; const nextPageParams = data?.next_page_params;
const hasNextPage = nextPageParams ? Object.keys(nextPageParams).length > 0 : false; const hasNextPage = nextPageParams ? Object.keys(nextPageParams).length > 0 : false;
...@@ -190,5 +213,5 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -190,5 +213,5 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
}, 0); }, 0);
}, []); }, []);
return { ...queryResult, pagination, onFilterChange }; return { ...queryResult, pagination, onFilterChange, onSortingChange };
} }
export default function getNextSortValue<SortField extends string, Sort extends string>(
sortSequence: Record<SortField, Array<Sort| undefined>>, field: SortField,
) {
return (prevValue: Sort | undefined) => {
const sequence = sortSequence[field];
const curIndex = sequence.findIndex((sort) => sort === prevValue);
const nextIndex = curIndex + 1 > sequence.length - 1 ? 0 : curIndex + 1;
return sequence[nextIndex];
};
}
import { Hide, HStack, Show } from '@chakra-ui/react'; import { Hide, HStack, Show } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type { Query } from 'nextjs-routes';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import type { TokensSorting } from 'types/api/tokens';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
...@@ -18,6 +20,8 @@ import PopoverFilter from 'ui/shared/filters/PopoverFilter'; ...@@ -18,6 +20,8 @@ import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter'; import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import type { Option } from 'ui/shared/sort/Sort';
import Sort from 'ui/shared/sort/Sort';
import TokensListItem from './TokensListItem'; import TokensListItem from './TokensListItem';
import TokensTable from './TokensTable'; import TokensTable from './TokensTable';
...@@ -25,16 +29,50 @@ import TokensTable from './TokensTable'; ...@@ -25,16 +29,50 @@ import TokensTable from './TokensTable';
const TOKEN_TYPES = TOKEN_TYPE.map(i => i.id); const TOKEN_TYPES = TOKEN_TYPE.map(i => i.id);
const getTokenFilterValue = (getFilterValuesFromQuery<TokenType>).bind(null, TOKEN_TYPES); const getTokenFilterValue = (getFilterValuesFromQuery<TokenType>).bind(null, TOKEN_TYPES);
export type TokensSortingField = TokensSorting['sort'];
export type TokensSortingValue = `${ TokensSortingField }-${ TokensSorting['order'] }`;
const SORT_OPTIONS: Array<Option<TokensSortingValue>> = [
{ title: 'Default', id: undefined },
{ title: 'Price ascending', id: 'fiat_value-asc' },
{ title: 'Price descending', id: 'fiat_value-desc' },
{ title: 'Holders ascending', id: 'holder_count-asc' },
{ title: 'Holders descending', id: 'holder_count-desc' },
{ title: 'On-chain market cap ascending', id: 'circulating_market_cap-asc' },
{ title: 'On-chain market cap descending', id: 'circulating_market_cap-desc' },
];
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;
}
};
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] };
};
const Tokens = () => { const Tokens = () => {
const router = useRouter(); const router = useRouter();
const [ filter, setFilter ] = React.useState<string>(router.query.q?.toString() || ''); const [ filter, setFilter ] = React.useState<string>(router.query.q?.toString() || '');
const [ sorting, setSorting ] = React.useState<TokensSortingValue | undefined>(getSortValueFromQuery(router.query));
const [ type, setType ] = React.useState<Array<TokenType> | undefined>(getTokenFilterValue(router.query.type)); const [ type, setType ] = React.useState<Array<TokenType> | undefined>(getTokenFilterValue(router.query.type));
const debouncedFilter = useDebounce(filter, 300); const debouncedFilter = useDebounce(filter, 300);
const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange, onSortingChange } = useQueryWithPages({
resourceName: 'tokens', resourceName: 'tokens',
filters: { q: debouncedFilter, type }, filters: { q: debouncedFilter, type },
sorting: getSortParamsFromValue(sorting),
options: { options: {
placeholderData: generateListStub<'tokens'>( placeholderData: generateListStub<'tokens'>(
TOKEN_INFO_ERC_20, TOKEN_INFO_ERC_20,
...@@ -61,6 +99,11 @@ const Tokens = () => { ...@@ -61,6 +99,11 @@ const Tokens = () => {
setType(value); setType(value);
}, [ debouncedFilter, onFilterChange ]); }, [ debouncedFilter, onFilterChange ]);
const onSort = useCallback((value?: TokensSortingValue) => {
setSorting(value);
onSortingChange(getSortParamsFromValue(value));
}, [ setSorting, onSortingChange ]);
if (isError) { if (isError) {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
...@@ -85,6 +128,11 @@ const Tokens = () => { ...@@ -85,6 +128,11 @@ const Tokens = () => {
<> <>
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}> <HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ typeFilter } { typeFilter }
<Sort
options={ SORT_OPTIONS }
setSort={ onSort }
sort={ sorting }
/>
{ filterInput } { filterInput }
</HStack> </HStack>
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
...@@ -110,7 +158,16 @@ const Tokens = () => { ...@@ -110,7 +158,16 @@ const Tokens = () => {
/> />
)) } )) }
</Show> </Show>
<Hide below="lg" ssr={ false }><TokensTable items={ data.items } page={ pagination.page } isLoading={ isPlaceholderData }/></Hide></> <Hide below="lg" ssr={ false }>
<TokensTable
items={ data.items }
page={ pagination.page }
isLoading={ isPlaceholderData }
setSorting={ onSort }
sorting={ sorting }
/>
</Hide>
</>
) : null; ) : null;
return ( return (
......
import { Table, Tbody, Th, Tr } from '@chakra-ui/react'; import { Icon, Link, Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import rightArrowIcon from 'icons/arrows/east.svg';
import { default as getNextSortValueShared } from 'ui/shared/sort/getNextSortValue';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import type { TokensSortingValue, TokensSortingField } from './Tokens';
import TokensTableItem from './TokensTableItem'; import TokensTableItem from './TokensTableItem';
const SORT_SEQUENCE: Record<TokensSortingField, Array<TokensSortingValue | undefined>> = {
fiat_value: [ 'fiat_value-desc', 'fiat_value-asc', undefined ],
holder_count: [ 'holder_count-desc', 'holder_count-asc', undefined ],
circulating_market_cap: [ 'circulating_market_cap-desc', 'circulating_market_cap-asc', undefined ],
};
const getNextSortValue = (getNextSortValueShared<TokensSortingField, TokensSortingValue>).bind(undefined, SORT_SEQUENCE);
type Props = { type Props = {
items: Array<TokenInfo>; items: Array<TokenInfo>;
page: number; page: number;
sorting?: TokensSortingValue;
setSorting: (val?: TokensSortingValue) => void;
isLoading?: boolean; isLoading?: boolean;
} }
const TokensTable = ({ items, page, isLoading }: Props) => { const TokensTable = ({ items, page, isLoading, sorting, setSorting }: Props) => {
const sortIconTransform = sorting?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
const sort = React.useCallback((field: TokensSortingField) => () => {
const value = getNextSortValue(field)(sorting);
setSorting(value);
}, [ sorting, setSorting ]);
return ( return (
<Table> <Table>
<Thead top={ 80 }> <Thead top={ 80 }>
<Tr> <Tr>
<Th w="50%">Token</Th> <Th w="50%">Token</Th>
<Th isNumeric w="15%">Price</Th> <Th isNumeric w="15%">
<Th isNumeric w="20%">On-chain market cap</Th> <Link onClick={ sort('fiat_value') } display="flex" justifyContent="end">
<Th isNumeric w="15%">Holders</Th> { sorting?.includes('fiat_value') && <Icon as={ rightArrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Price
</Link>
</Th>
<Th isNumeric w="20%">
<Link onClick={ sort('circulating_market_cap') } display="flex" justifyContent="end">
{ sorting?.includes('circulating_market_cap') && <Icon as={ rightArrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
On-chain market cap
</Link>
</Th>
<Th isNumeric w="15%">
<Link onClick={ sort('holder_count') } display="flex" justifyContent="end">
{ sorting?.includes('holder_count') && <Icon as={ rightArrowIcon } boxSize={ 4 } transform={ sortIconTransform }/> }
Holders
</Link>
</Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
......
...@@ -13,6 +13,7 @@ import DataListDisplay from 'ui/shared/DataListDisplay'; ...@@ -13,6 +13,7 @@ import DataListDisplay from 'ui/shared/DataListDisplay';
// import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter'; // import TxInternalsFilter from 'ui/tx/internals/TxInternalsFilter';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import { default as getNextSortValueShared } from 'ui/shared/sort/getNextSortValue';
import TxInternalsList from 'ui/tx/internals/TxInternalsList'; import TxInternalsList from 'ui/tx/internals/TxInternalsList';
import TxInternalsTable from 'ui/tx/internals/TxInternalsTable'; import TxInternalsTable from 'ui/tx/internals/TxInternalsTable';
import type { Sort, SortField } from 'ui/tx/internals/utils'; import type { Sort, SortField } from 'ui/tx/internals/utils';
...@@ -25,12 +26,7 @@ const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = { ...@@ -25,12 +26,7 @@ const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = {
'gas-limit': [ 'gas-limit-desc', 'gas-limit-asc', undefined ], 'gas-limit': [ 'gas-limit-desc', 'gas-limit-asc', undefined ],
}; };
const getNextSortValue = (field: SortField) => (prevValue: Sort | undefined) => { const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE);
const sequence = SORT_SEQUENCE[field];
const curIndex = sequence.findIndex((sort) => sort === prevValue);
const nextIndex = curIndex + 1 > sequence.length - 1 ? 0 : curIndex + 1;
return sequence[nextIndex];
};
const sortFn = (sort: Sort | undefined) => (a: InternalTransaction, b: InternalTransaction) => { const sortFn = (sort: Sort | undefined) => (a: InternalTransaction, b: InternalTransaction) => {
switch (sort) { switch (sort) {
......
import type { VerifiedContract } from 'types/api/contracts'; import type { VerifiedContract } from 'types/api/contracts';
import compareBns from 'lib/bigint/compareBns'; import compareBns from 'lib/bigint/compareBns';
import { default as getNextSortValueShared } from 'ui/shared/sort/getNextSortValue';
import type { Option } from 'ui/shared/sort/Sort'; import type { Option } from 'ui/shared/sort/Sort';
export type SortField = 'balance' | 'txs'; export type SortField = 'balance' | 'txs';
...@@ -19,12 +20,7 @@ const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = { ...@@ -19,12 +20,7 @@ const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = {
txs: [ 'txs-desc', 'txs-asc', undefined ], txs: [ 'txs-desc', 'txs-asc', undefined ],
}; };
export const getNextSortValue = (field: SortField) => (prevValue: Sort | undefined) => { export const getNextSortValue = (getNextSortValueShared<SortField, Sort>).bind(undefined, SORT_SEQUENCE);
const sequence = SORT_SEQUENCE[field];
const curIndex = sequence.findIndex((sort) => sort === prevValue);
const nextIndex = curIndex + 1 > sequence.length - 1 ? 0 : curIndex + 1;
return sequence[nextIndex];
};
export const sortFn = (sort: Sort | undefined) => (a: VerifiedContract, b: VerifiedContract) => { export const sortFn = (sort: Sort | undefined) => (a: VerifiedContract, b: VerifiedContract) => {
switch (sort) { switch (sort) {
......
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