From ec4b4b5f93b4c38ec928bdbb21bd97ee6ab457f8 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 19 Jan 2024 17:06:14 +0900 Subject: [PATCH 1/5] feat: add pagination for cell list on transaction page --- src/components/Pagination/index.module.scss | 66 +++++++++++ src/components/Pagination/index.tsx | 27 ++++- src/components/Pagination/styled.tsx | 1 + src/constants/common.ts | 1 + src/locales/en.json | 3 +- src/locales/zh.json | 3 +- .../Transaction/TransactionCellList/index.tsx | 4 +- .../TransactionCellList/styled.tsx | 2 +- .../TransactionComp/TransactionComp.tsx | 108 +++++++++++++++++- src/services/ExplorerService/fetcher.ts | 13 ++- src/styles/index.css | 1 + 11 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 src/components/Pagination/index.module.scss diff --git a/src/components/Pagination/index.module.scss b/src/components/Pagination/index.module.scss new file mode 100644 index 000000000..b7ae09ed9 --- /dev/null +++ b/src/components/Pagination/index.module.scss @@ -0,0 +1,66 @@ +.pageSizeSelector { + white-space: nowrap; + position: relative; + display: flex; + align-items: center; + align-content: stretch; + height: 100%; + margin-left: 2rem; + font-size: 0.875rem; + line-height: 1em; + color: #333; + + .pageSize { + text-align: left; + width: 3.5rem; + margin-left: 0.5rem; + border-radius: 4px; + padding: 8px; + height: 2rem; + background: var(--input-bg-color); + border: none; + + &:focus { + & + div[role='menu'] { + display: flex; + } + } + } + + div[role='menu'] { + position: absolute; + z-index: 1; + width: 3.5rem; + top: 100%; + right: 0; + display: none; + flex-direction: column; + align-items: flex-start; + border-radius: 4px; + padding: 2px 0; + background: #fff; + overflow: hidden; + box-shadow: 0 2px 10px 0 #eee; + } + + div[role='menuitem'] { + width: 100%; + padding: 0.375rem 0.5rem; + cursor: pointer; + text-align: left; + + &:hover { + background: var(--input-bg-color); + } + + &[data-is-selected='true'] { + color: var(--primary-color); + } + } + + &:hover { + div[role='menu'] { + display: flex; + } + } +} diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx index 5274a559f..3de641c75 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -8,6 +8,7 @@ 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, @@ -16,13 +17,17 @@ const Pagination = ({ onChange, className, annotation, + pageSize, + presetPageSizes, }: { currentPage: number totalPages: number gotoPage?: number - onChange: (page: number) => void + onChange: (page: number, size?: number) => void className?: string annotation?: string + pageSize?: number + presetPageSizes?: number[] }) => { const isMobile = useIsMobile() const { t } = useTranslation() @@ -74,6 +79,26 @@ const Pagination = ({ changePage(total)}> {t('pagination.last')} + {presetPageSizes ? ( +
+ {`${t('pagination.show_rows')}:`} +
{pageSize}
+
+ {presetPageSizes.map(size => ( +
onChange(current, size)} + onKeyDown={() => onChange(current, size)} + > + {size} +
+ ))} +
+
+ ) : null} {t('pagination.page')} diff --git a/src/components/Pagination/styled.tsx b/src/components/Pagination/styled.tsx index e9a307984..5c12d9e65 100644 --- a/src/components/Pagination/styled.tsx +++ b/src/components/Pagination/styled.tsx @@ -82,6 +82,7 @@ export const PaginationLeftItem = styled.div` font-size: 12px; padding: 0 12px; margin-left: 20px; + white-space: nowrap; @media (max-width: ${variables.mobileBreakPoint}) { background: white; diff --git a/src/constants/common.ts b/src/constants/common.ts index d3ddc0908..cd2830d86 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -18,6 +18,7 @@ export const ONE_HOUR_MILLISECOND = ONE_HOUR_SECOND * 1000 export const ONE_MINUTE_SECOND = 60 export const EPOCHS_PER_HALVING = 8760 export const THEORETICAL_EPOCH_TIME = 1000 * 60 * 60 * 4 // 4 hours +export const PAGE_SIZE = 10 export function getPrimaryColor() { return isMainnet() ? '#00CC9B' : '#9A2CEC' diff --git a/src/locales/en.json b/src/locales/en.json index 278e40285..6e4eb60c6 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -555,7 +555,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", + "show_rows": "Show Rows" }, "udt": { "sudt": "sUDT", diff --git a/src/locales/zh.json b/src/locales/zh.json index b2c12326f..b9f6e5f08 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -556,7 +556,8 @@ "end_page": "页", "current_page": "第", "of_page": "页,共", - "only_first_pages_visible": "显示最近 {{pages}} 页" + "only_first_pages_visible": "显示最近 {{pages}} 页", + "show_rows": "每页显示" }, "udt": { "sudt": "sUDT", diff --git a/src/pages/Transaction/TransactionCellList/index.tsx b/src/pages/Transaction/TransactionCellList/index.tsx index 71f9b174f..970678b75 100644 --- a/src/pages/Transaction/TransactionCellList/index.tsx +++ b/src/pages/Transaction/TransactionCellList/index.tsx @@ -20,11 +20,13 @@ export default ({ outputs, txHash, showReward, + startIndex, }: { inputs?: Cell[] outputs?: Cell[] txHash?: string showReward?: boolean + startIndex: number }) => { const { t } = useTranslation() const [offset, setOffset] = useState(PAGE_CELL_COUNT) @@ -99,7 +101,7 @@ export default ({ key={cell.id} cell={cell} cellType={inputs ? CellType.Input : CellType.Output} - index={index} + index={index + startIndex} txHash={txHash} showReward={showReward} isAddrNew={!isDeprecatedAddressesDisplayed} diff --git a/src/pages/Transaction/TransactionCellList/styled.tsx b/src/pages/Transaction/TransactionCellList/styled.tsx index a419585b6..524f97bb6 100644 --- a/src/pages/Transaction/TransactionCellList/styled.tsx +++ b/src/pages/Transaction/TransactionCellList/styled.tsx @@ -54,7 +54,7 @@ export const TransactionCellListTitlePanel = styled.div` export const TransactionCellListPanel = styled.div` width: 100%; - border-radius: 6px; + border-radius: 6px 6px 0 0; box-shadow: 2px 2px 6px 0 #dfdfdf; background-color: #fff; padding: 12px 20px 12px 40px; diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx index be98cacb8..dd8660704 100644 --- a/src/pages/Transaction/TransactionComp/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -1,6 +1,14 @@ +import { useQuery } from '@tanstack/react-query' +import { useHistory, useLocation } from 'react-router-dom' +import type { Response } from '../../../services/ExplorerService/types' import TransactionCellList from '../TransactionCellList' import { Cell } from '../../../models/Cell' import { Transaction } from '../../../models/Transaction' +import { explorerService } from '../../../services/ExplorerService' +import Loading from '../../../components/Loading' +import Pagination from '../../../components/Pagination' +import { PAGE_SIZE } from '../../../constants/common' +import { useSearchParams } from '../../../hooks' const handleCellbaseInputs = (inputs: Cell[], outputs: Cell[]) => { if (inputs[0] && inputs[0].fromCellbase && outputs[0] && outputs[0].baseReward) { @@ -17,19 +25,109 @@ const handleCellbaseInputs = (inputs: Cell[], outputs: Cell[]) => { return inputs } -export const TransactionComp = ({ transaction }: { transaction: Transaction }) => { - const { transactionHash, displayInputs, displayOutputs, blockNumber, isCellbase } = transaction +const emptyList: Response.Response = { + data: [], + meta: { + total: 0, + pageSize: PAGE_SIZE, + }, +} + +const PRESET_PAGE_SIZES = [5, 10, 15] + +export const TransactionComp = ({ + transaction: { transactionHash: txHash, blockNumber, isCellbase }, +}: { + transaction: Transaction +}) => { + const { + page_of_inputs = '1', + page_of_outputs = '1', + page_size = `${PAGE_SIZE}`, + } = useSearchParams('page_of_inputs', 'page_of_outputs', 'page_size') + const { pathname, search } = useLocation() + + const history = useHistory() + + const inputsPage = page_of_inputs && Number.isNaN(+page_of_inputs) ? 1 : +page_of_inputs + const outputsPage = page_of_outputs && Number.isNaN(+page_of_outputs) ? 1 : +page_of_outputs + const pageSize = Number.isNaN(+page_size) ? PAGE_SIZE : +page_size + + const { data: displayInputs, isFetching: isInputsLoading } = useQuery( + ['transaction_inputs', txHash, inputsPage, pageSize], + async () => { + try { + const res = await explorerService.api.fetchCellsByTxHash(txHash, 'inputs', { no: inputsPage, size: pageSize }) + return res + } catch (e) { + return emptyList + } + }, + { + initialData: emptyList, + }, + ) + + const { data: displayOutputs, isFetching: isOutputsLoading } = useQuery( + ['transaction_outputs', txHash, outputsPage, pageSize], + async () => { + try { + const res = await explorerService.api.fetchCellsByTxHash(txHash, 'outputs', { no: outputsPage, size: pageSize }) + return res + } catch (e) { + return emptyList + } + }, + { + initialData: emptyList, + }, + ) - const inputs = handleCellbaseInputs(displayInputs, displayOutputs) + const inputs = handleCellbaseInputs(displayInputs.data, displayOutputs.data) + const inputsPageCount = displayInputs.meta ? Math.ceil(displayInputs.meta.total / displayInputs.meta.pageSize) : 1 + const outputsPageCount = displayOutputs.meta ? Math.ceil(displayOutputs.meta.total / displayOutputs.meta.pageSize) : 1 + + const handlePageChange = (type: 'inputs' | 'outputs') => (page: number, size?: number) => { + const pageField = `page_of_${type}` + const searchParams = new URLSearchParams(search) + searchParams.set(pageField, `${page}`) + if (size) { + searchParams.set('page_size', `${size}`) + } + const url = `${pathname}?${searchParams.toString()}` + history.push(url) + } /// [0, 11] block doesn't show block reward and only cellbase show block reward return ( <>
- {inputs && 0 && isCellbase} />} + + 0 && isCellbase} + /> +
+
- {displayOutputs && } + + +
+
) diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 75081b631..4479c6372 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -98,7 +98,18 @@ export const apiFetcher = { fetchTransactionRaw: (hash: string) => requesterV2.get(`transactions/${hash}/raw`).then(res => res.data), - fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}`), + fetchTransactionByHash: (hash: string, displayCells: boolean = false) => + v1GetUnwrapped(`transactions/${hash}?display_cells=${displayCells}`), + + fetchCellsByTxHash: (hash: string, type: 'inputs' | 'outputs', page: Record<'no' | 'size', number>) => + requesterV2 + .get(`ckb_transactions/${hash}/display_${type}`, { + params: { + page: page.no, + page_size: page.size, + }, + }) + .then(res => toCamelcase>(res.data)), fetchTransactionLiteDetailsByHash: (hash: string) => requesterV2 diff --git a/src/styles/index.css b/src/styles/index.css index 38af44b31..d82c90f27 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -9,6 +9,7 @@ body { --navbar-height: 64px; --table-separator-color: #f5f5f5; --decimal-color: #999; + --input-bg-color: #f5f5f5; margin: 0; padding: 0; From 4fd924dc1cea4107897a51778a9cc1f6ea3fb9c2 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 22 Jan 2024 16:58:49 +0900 Subject: [PATCH 2/5] feat: hide page size selector in some cases for further design The page size selector will be hidden when the width in scope of (750px, 900px) and (0, 600px) because its layout will be reconsidered in terms of design --- src/components/Pagination/index.module.scss | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Pagination/index.module.scss b/src/components/Pagination/index.module.scss index b7ae09ed9..4ff55063d 100644 --- a/src/components/Pagination/index.module.scss +++ b/src/components/Pagination/index.module.scss @@ -5,7 +5,7 @@ align-items: center; align-content: stretch; height: 100%; - margin-left: 2rem; + margin: 0 2rem; font-size: 0.875rem; line-height: 1em; color: #333; @@ -63,4 +63,13 @@ display: flex; } } + + // FIXME: the layout will be optimized later with a new design + @media (750px <=width) and (width <= 900px) { + display: none; + } + + @media (width <= 600px) { + display: none; + } } From e57e75dbb0b58fe11408e0b02bcf74293b6ce773 Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 24 Jan 2024 18:02:52 +0900 Subject: [PATCH 3/5] feat: go to the last page when page size overflows --- src/pages/Transaction/TransactionComp/TransactionComp.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx index dd8660704..5a52bc98b 100644 --- a/src/pages/Transaction/TransactionComp/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -93,6 +93,11 @@ export const TransactionComp = ({ searchParams.set(pageField, `${page}`) if (size) { searchParams.set('page_size', `${size}`) + const total = (type === 'inputs' ? displayInputs.meta?.total : displayOutputs.meta?.total) ?? 0 + const maxPage = Math.ceil(total / size) + if (page > maxPage) { + searchParams.set(pageField, `${maxPage}`) + } } const url = `${pathname}?${searchParams.toString()}` history.push(url) From d3e85023ec7c84c5ba3628c4a8a97108b9f159b5 Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 24 Jan 2024 18:56:14 +0900 Subject: [PATCH 4/5] fix: fix total of cell list on transaction page --- src/pages/Transaction/TransactionCellList/index.tsx | 11 +++-------- .../Transaction/TransactionComp/TransactionComp.tsx | 8 +++++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pages/Transaction/TransactionCellList/index.tsx b/src/pages/Transaction/TransactionCellList/index.tsx index 970678b75..5a462147b 100644 --- a/src/pages/Transaction/TransactionCellList/index.tsx +++ b/src/pages/Transaction/TransactionCellList/index.tsx @@ -16,12 +16,14 @@ const SCROLL_BOTTOM_OFFSET = 5 const SCROLL_LOADING_TIME = 400 export default ({ + total, inputs, outputs, txHash, showReward, startIndex, }: { + total?: number inputs?: Cell[] outputs?: Cell[] txHash?: string @@ -55,18 +57,11 @@ export default ({ const [isDeprecatedAddressesDisplayed, setIsDeprecatedAddressesDisplayed] = useIsDeprecatedAddressesDisplayed() const toggleDeprecatedAddressesDisplayed = () => setIsDeprecatedAddressesDisplayed(value => !value) - const cellsCount = () => { - if (inputs) { - return inputs.length - } - return outputs ? outputs.length : 0 - } - const cellTitle = () => { const title = inputs ? t('transaction.input') : t('transaction.output') return (
- {`${title} (${cellsCount()})`} + {`${title} (${total ?? '-'})`}
{!isDeprecatedAddressesDisplayed ? : } diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx index 5a52bc98b..62e9b82b0 100644 --- a/src/pages/Transaction/TransactionComp/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -109,6 +109,7 @@ export const TransactionComp = ({
0 && isCellbase} @@ -124,7 +125,12 @@ export const TransactionComp = ({
- +
Date: Fri, 26 Jan 2024 17:32:02 +0900 Subject: [PATCH 5/5] fix: visit page 1 when page size is updated --- .../Transaction/TransactionComp/TransactionComp.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx index 62e9b82b0..8f4e2202c 100644 --- a/src/pages/Transaction/TransactionComp/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -90,14 +90,12 @@ export const TransactionComp = ({ const handlePageChange = (type: 'inputs' | 'outputs') => (page: number, size?: number) => { const pageField = `page_of_${type}` const searchParams = new URLSearchParams(search) - searchParams.set(pageField, `${page}`) if (size) { searchParams.set('page_size', `${size}`) - const total = (type === 'inputs' ? displayInputs.meta?.total : displayOutputs.meta?.total) ?? 0 - const maxPage = Math.ceil(total / size) - if (page > maxPage) { - searchParams.set(pageField, `${maxPage}`) - } + searchParams.delete('page_of_inputs') + searchParams.delete('page_of_outputs') + } else { + searchParams.set(pageField, `${page}`) } const url = `${pathname}?${searchParams.toString()}` history.push(url)