diff --git a/src/components/Pagination/index.module.scss b/src/components/Pagination/index.module.scss new file mode 100644 index 000000000..3458a5f54 --- /dev/null +++ b/src/components/Pagination/index.module.scss @@ -0,0 +1,11 @@ +.pageSize { + margin: 0 10px; +} + +.pageSizeInput { + border-radius: 4px; + border: none; + width: 40px; + background: #f5f5f5; + padding: 4px 10px; +} diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx index 5274a559f..f136ab618 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -8,25 +8,31 @@ import RightGrey from './pagination_grey_right.png' import { useIsMobile } from '../../hooks' import SimpleButton from '../SimpleButton' import { HelpTip } from '../HelpTip' +import styles from './index.module.scss' const Pagination = ({ currentPage, totalPages, gotoPage = currentPage === totalPages ? totalPages : currentPage + 1, - onChange, + onPageNumberChange: onChange, + onPageSizeChange, + pageSize = 10, className, annotation, }: { currentPage: number + pageSize?: number totalPages: number gotoPage?: number - onChange: (page: number) => void + onPageNumberChange: (pageNumber: number) => void + onPageSizeChange?: (pageSize: number) => void className?: string annotation?: string }) => { const isMobile = useIsMobile() const { t } = useTranslation() const [inputPage, setInputPage] = useState(gotoPage) + const [inputPageSize, setInputPageSize] = useState(pageSize) const total = Math.max(totalPages, 1) const current = Math.min(Math.max(currentPage, 1), totalPages) @@ -45,6 +51,13 @@ const Pagination = ({ } } + const changePageSize = (pageSize: number) => { + if (onPageSizeChange) { + onPageSizeChange(pageSize) + } + setInputPageSize(pageSize) + } + return ( @@ -74,6 +87,28 @@ const Pagination = ({ changePage(total)}> {t('pagination.last')} + {!isMobile && ( + <> + {t('pagination.page_size')} + { + const value = parseInt(event.target.value, 10) + if (!Number.isNaN(value)) { + setInputPageSize(value) + } + }} + onKeyUp={event => { + if (event.keyCode === 13) { + changePageSize(inputPageSize) + } + }} + /> + + )} {t('pagination.page')} diff --git a/src/components/PaginationWithRear/index.tsx b/src/components/PaginationWithRear/index.tsx index 9fe523835..a7f42377c 100644 --- a/src/components/PaginationWithRear/index.tsx +++ b/src/components/PaginationWithRear/index.tsx @@ -8,11 +8,15 @@ const PaginationWithRear = ({ totalPages, onChange, paginationClassName, + onPageSizeChange, + pageSize, rear, }: { currentPage: number totalPages: number + pageSize?: number onChange: (page: number) => void + onPageSizeChange?: (pageSize: number) => void paginationClassName?: string rear: ReactNode }) => { @@ -22,7 +26,9 @@ const PaginationWithRear = ({ )} diff --git a/src/locales/en.json b/src/locales/en.json index 9bf9e472a..bcf9811d1 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -552,7 +552,8 @@ "end_page": "", "current_page": "Page", "of_page": "of", - "only_first_pages_visible": "Showing first {{pages}} pages" + "only_first_pages_visible": "Showing first {{pages}} pages", + "page_size": "Size" }, "udt": { "sudt": "sUDT", diff --git a/src/locales/zh.json b/src/locales/zh.json index ac7d53456..03d465d49 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -553,7 +553,8 @@ "end_page": "页", "current_page": "第", "of_page": "页,共", - "only_first_pages_visible": "显示最近 {{pages}} 页" + "only_first_pages_visible": "显示最近 {{pages}} 页", + "page_size": "每页" }, "udt": { "sudt": "sUDT", diff --git a/src/pages/BlockDetail/BlockComp.tsx b/src/pages/BlockDetail/BlockComp.tsx index 45e5aecfa..cade8fb91 100644 --- a/src/pages/BlockDetail/BlockComp.tsx +++ b/src/pages/BlockDetail/BlockComp.tsx @@ -354,7 +354,7 @@ export const BlockComp = ({ )} {totalPages > 1 && ( - + )} diff --git a/src/pages/NervosDao/DaoTransactions/index.tsx b/src/pages/NervosDao/DaoTransactions/index.tsx index b615aae07..2808d993f 100644 --- a/src/pages/NervosDao/DaoTransactions/index.tsx +++ b/src/pages/NervosDao/DaoTransactions/index.tsx @@ -59,7 +59,7 @@ export default ({ )} {totalPages > 1 && ( - + )} diff --git a/src/pages/NftCollectionInfo/index.tsx b/src/pages/NftCollectionInfo/index.tsx index c37d0740b..299d90d4b 100644 --- a/src/pages/NftCollectionInfo/index.tsx +++ b/src/pages/NftCollectionInfo/index.tsx @@ -206,14 +206,14 @@ const NftCollectionInfo = () => { ) : null} {tab === tabs[2] ? ( <> - + ) : null} diff --git a/src/pages/NftCollections/index.tsx b/src/pages/NftCollections/index.tsx index 27f42c02a..df7315243 100644 --- a/src/pages/NftCollections/index.tsx +++ b/src/pages/NftCollections/index.tsx @@ -62,7 +62,7 @@ const NftCollections = () => { diff --git a/src/pages/NftInfo/index.tsx b/src/pages/NftInfo/index.tsx index c300299bc..77f809d71 100644 --- a/src/pages/NftInfo/index.tsx +++ b/src/pages/NftInfo/index.tsx @@ -140,7 +140,7 @@ const NftInfo = () => { diff --git a/src/pages/Script/ScriptsComp.tsx b/src/pages/Script/ScriptsComp.tsx index c537437a4..6ddc17656 100644 --- a/src/pages/Script/ScriptsComp.tsx +++ b/src/pages/Script/ScriptsComp.tsx @@ -81,7 +81,7 @@ export const ScriptTransactions = ({ page, size }: { page: number; size: number {totalPages > 1 && (
- +
)} @@ -217,7 +217,7 @@ export const ScriptCells = ({ {totalPages > 1 && (
- +
)} diff --git a/src/pages/Tokens/index.tsx b/src/pages/Tokens/index.tsx index 3ba0d6997..3eee071b1 100644 --- a/src/pages/Tokens/index.tsx +++ b/src/pages/Tokens/index.tsx @@ -161,7 +161,7 @@ export default () => { )} - + ) diff --git a/src/pages/Transaction/TransactionCellList/index.tsx b/src/pages/Transaction/TransactionCellList/index.tsx index 71f9b174f..13ececfef 100644 --- a/src/pages/Transaction/TransactionCellList/index.tsx +++ b/src/pages/Transaction/TransactionCellList/index.tsx @@ -20,11 +20,15 @@ export default ({ outputs, txHash, showReward, + total, + indiceOffset, }: { inputs?: Cell[] outputs?: Cell[] txHash?: string showReward?: boolean + total?: number + indiceOffset?: number }) => { const { t } = useTranslation() const [offset, setOffset] = useState(PAGE_CELL_COUNT) @@ -54,6 +58,9 @@ export default ({ const toggleDeprecatedAddressesDisplayed = () => setIsDeprecatedAddressesDisplayed(value => !value) const cellsCount = () => { + if (total) { + return total + } if (inputs) { return inputs.length } @@ -99,7 +106,7 @@ export default ({ key={cell.id} cell={cell} cellType={inputs ? CellType.Input : CellType.Output} - index={index} + index={index + (indiceOffset || 0)} txHash={txHash} showReward={showReward} isAddrNew={!isDeprecatedAddressesDisplayed} diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx index be98cacb8..62e73a453 100644 --- a/src/pages/Transaction/TransactionComp/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -1,6 +1,12 @@ +import { useQuery } from '@tanstack/react-query' +import { useHistory, useLocation } from 'react-router-dom' import TransactionCellList from '../TransactionCellList' import { Cell } from '../../../models/Cell' import { Transaction } from '../../../models/Transaction' +import { explorerService } from '../../../services/ExplorerService' +import Pagination from '../../../components/Pagination' +import Loading from '../../../components/Loading' +import { useSearchParams } from '../../../hooks' const handleCellbaseInputs = (inputs: Cell[], outputs: Cell[]) => { if (inputs[0] && inputs[0].fromCellbase && outputs[0] && outputs[0].baseReward) { @@ -18,18 +24,108 @@ const handleCellbaseInputs = (inputs: Cell[], outputs: Cell[]) => { } export const TransactionComp = ({ transaction }: { transaction: Transaction }) => { - const { transactionHash, displayInputs, displayOutputs, blockNumber, isCellbase } = transaction + const DEFAULT_PAGE_SIZE = 10 + const txHash = transaction.transactionHash + const location = useLocation() + const queryParams = new URLSearchParams(location.search) + const history = useHistory() - const inputs = handleCellbaseInputs(displayInputs, displayOutputs) + const { + inputCellsPageNumber = 1, + inputCellsPageSize = DEFAULT_PAGE_SIZE, + outputCellsPageNumber = 1, + outputCellsPageSize = DEFAULT_PAGE_SIZE, + } = useSearchParams('inputCellsPageNumber', 'inputCellsPageSize', 'outputCellsPageNumber', 'outputCellsPageSize') + const handleInputCellsPageChange = (page: number) => { + queryParams.set('inputCellsPageNumber', page.toString()) + history.replace(`${location.pathname}?${queryParams.toString()}`) + } + const handleInputCellsPageSizeChange = (pageSize: number) => { + queryParams.set('inputCellsPageSize', pageSize.toString()) + history.replace(`${location.pathname}?${queryParams.toString()}`) + } + + const txInputsQuery = useQuery(['transactionInputs', txHash, inputCellsPageNumber, inputCellsPageSize], async () => { + const result = await explorerService.api.fetchTransactionInputsByHash( + txHash, + Number(inputCellsPageNumber), + Number(inputCellsPageSize), + ) + return result + }) + const handleOutputCellsPageChange = (page: number) => { + queryParams.set('outputCellsPageNumber', page.toString()) + history.replace(`${location.pathname}?${queryParams.toString()}`) + } + const handleOutputCellsPageSizeChange = (pageSize: number) => { + queryParams.set('outputCellsPageSize', pageSize.toString()) + history.replace(`${location.pathname}?${queryParams.toString()}`) + } + + const txOutputsQuery = useQuery( + ['transactionOutputs', txHash, outputCellsPageNumber, outputCellsPageSize], + async () => { + const result = await explorerService.api.fetchTransactionOutputsByHash( + txHash, + Number(outputCellsPageNumber), + Number(outputCellsPageSize), + ) + // TODO: When will displayOutputs be empty? Its type description indicates that it will not be empty. + if (result.data.length > 0) { + result.data[0].isGenesisOutput = transaction.blockNumber === 0 + } + return result + }, + ) + + const { transactionHash, blockNumber, isCellbase } = transaction + + const inputs = handleCellbaseInputs( + txInputsQuery.isSuccess ? txInputsQuery.data.data : [], + txOutputsQuery.isSuccess ? txOutputsQuery.data.data : [], + ) + + const totalInputsCount = txInputsQuery.data?.meta?.total ?? 0 + const totalOutputsCount = txOutputsQuery.data?.meta?.total ?? 0 /// [0, 11] block doesn't show block reward and only cellbase show block reward return ( <>
- {inputs && 0 && isCellbase} />} + {txInputsQuery.isFetching && } + {inputs && ( + 0 && isCellbase} + indiceOffset={(Number(inputCellsPageNumber) - 1) * Number(inputCellsPageSize)} + /> + )} +
- {displayOutputs && } + {txOutputsQuery.isFetching && } + {txOutputsQuery.isSuccess && ( + + )} +
) diff --git a/src/pages/Transaction/index.tsx b/src/pages/Transaction/index.tsx index b538f71bc..19a0b8b16 100644 --- a/src/pages/Transaction/index.tsx +++ b/src/pages/Transaction/index.tsx @@ -15,14 +15,7 @@ export default () => { const { Professional, Lite } = LayoutLiteProfessional const { hash: txHash } = useParams<{ hash: string }>() - const query = useQuery(['transaction', txHash], async () => { - const transaction = await explorerService.api.fetchTransactionByHash(txHash) - // TODO: When will displayOutputs be empty? Its type description indicates that it will not be empty. - if (transaction.displayOutputs && transaction.displayOutputs.length > 0) { - transaction.displayOutputs[0].isGenesisOutput = transaction.blockNumber === 0 - } - return transaction - }) + const query = useQuery(['transaction', txHash], async () => explorerService.api.fetchTransactionByHash(txHash)) const transaction = query.data ?? defaultTransactionInfo const searchParams = useSearchParams('layout') diff --git a/src/pages/TransactionList/index.tsx b/src/pages/TransactionList/index.tsx index 8d6d92df6..a08385e54 100644 --- a/src/pages/TransactionList/index.tsx +++ b/src/pages/TransactionList/index.tsx @@ -300,7 +300,7 @@ const TransactionsPanel: FC<{ type: TxStatus }> = ({ type }) => { className={styles.pagination} currentPage={currentPage} totalPages={totalPages} - onChange={setPage} + onPageNumberChange={setPage} annotation={ totalPages === MAX_PAGE_NUMBER ? t('pagination.only_first_pages_visible', { pages: MAX_PAGE_NUMBER }) diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 240b10a69..472f67a45 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -98,7 +98,23 @@ export const apiFetcher = { fetchTransactionRaw: (hash: string) => requesterV2.get(`transactions/${hash}/raw`).then(res => res.data), - fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}`), + fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}?display_cells=false`), + fetchTransactionInputsByHash: (hash: string, page: number, size: number) => + requesterV2 + .get(`ckb_transactions/${hash}/display_inputs`, { + params: { + page, + page_size: size, + }, + }) + .then(res => toCamelcase>(res.data)), + fetchTransactionOutputsByHash: (hash: string, page: number, size: number) => + requesterV2(`ckb_transactions/${hash}/display_outputs`, { + params: { + page, + page_size: size, + }, + }).then(res => toCamelcase>(res.data)), fetchTransactionLiteDetailsByHash: (hash: string) => requesterV2