From 769bad8cb245c531f55af9110c5c15da2be075ce Mon Sep 17 00:00:00 2001 From: painterpuppets Date: Mon, 6 Jan 2025 20:54:04 +0900 Subject: [PATCH] chore: add overcount notice --- src/locales/en.json | 4 +- src/locales/zh.json | 4 +- src/pages/Address/AddressComp.tsx | 91 ++---------- src/pages/Address/AddressPage.tsx | 187 ++++++++++++++++++------ src/pages/Address/styles.module.scss | 12 ++ src/pages/Script/ScriptsComp.tsx | 1 + src/pages/Script/styles.module.scss | 12 ++ src/services/ExplorerService/fetcher.ts | 1 + src/services/ExplorerService/types.ts | 1 + 9 files changed, 183 insertions(+), 130 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 07fdf8438..c0a4d1361 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -612,7 +612,9 @@ "view-btc-tx": "View BTC Tx", "isomorphic-binding-with-index": "Isomorphic bound to CKB Tx {{index}}", "isomorphic-binding-with-index-commitment": "Isomorphic bound to CKB Tx {{index}}\n\nCommitment:\n{{commitment}}", - "loading": "Loading..." + "loading": "Loading...", + "range_notice": "Only the latest 5,000 related transactions will be displayed here.", + "page_range_notice": "Only the latest 200 page related transactions will be displayed here." }, "block": { "block": "Block", diff --git a/src/locales/zh.json b/src/locales/zh.json index f19701f60..be2adacf7 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -612,7 +612,9 @@ "view-btc-tx": "查看 BTC 交易", "isomorphic-binding-with-index": "与CKB Tx {{index}} 同构绑定", "isomorphic-binding-with-index-commitment": "与 CKB Tx {{index}} 同构绑定\n\nCommitment:\n{{commitment}}", - "loading": "Loading..." + "loading": "Loading...", + "range_notice": "只显示最近的 5,000 条相关交易", + "page_range_notice": "只显示最近 200 页的相关交易" }, "block": { "block": "区块", diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index 7fd329259..9b7b17033 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -1,11 +1,9 @@ import { useState, FC, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { addressToScript } from '@nervosnetwork/ckb-sdk-utils' -import { Radio } from 'antd' import { useTranslation } from 'react-i18next' import { EyeOpenIcon, EyeClosedIcon } from '@radix-ui/react-icons' import { utils } from '@ckb-lumos/base' -import { Link } from '../../components/Link' import TransactionItem from '../../components/TransactionItem/index' import NodeTransactionItem from '../../components/TransactionItem/NodeTransactionItem' import { explorerService, RawBtcRPC } from '../../services/ExplorerService' @@ -22,23 +20,13 @@ import { } from './styled' import Capacity from '../../components/Capacity' import CKBTokenIcon from './ckb_token_icon.png' -import { ReactComponent as TimeDownIcon } from '../../assets/time_down.svg' -import { ReactComponent as TimeUpIcon } from '../../assets/time_up.svg' -import { - OrderByType, - useIsMobile, - useNewAddr, - usePaginationParamsInListPage, - useSearchParams, - useUpdateSearchParams, -} from '../../hooks' +import { useNewAddr, usePaginationParamsInListPage, useSearchParams } from '../../hooks' import styles from './styles.module.scss' import LiteTransactionList from '../../components/LiteTransactionList' import Script from '../../components/Script' import AddressText from '../../components/AddressText' import { parseSimpleDateNoSecond } from '../../utils/date' import { LayoutLiteProfessional } from '../../constants/common' -import { omit } from '../../utils/object' import { CsvExport } from '../../components/CsvExport' import PaginationWithRear from '../../components/PaginationWithRear' import { Transaction } from '../../models/Transaction' @@ -323,42 +311,26 @@ export const AddressOverviewCard: FC<{ address: Address }> = ({ address }) => { export const AddressTransactions = ({ address, transactions, - timeOrderBy, - meta: { counts }, + meta, }: { address: string transactions: (Transaction & { btcTx: RawBtcRPC.BtcTx | null })[] - timeOrderBy: OrderByType - meta: { counts: Record<'committed' | 'pending', number | '-'> } + meta: { totalPages?: number } }) => { - const isMobile = useIsMobile() + const { totalPages = 0 } = meta const { t } = useTranslation() - const { currentPage, pageSize, setPage } = usePaginationParamsInListPage() + const { currentPage, setPage } = usePaginationParamsInListPage() const { Professional, Lite } = LayoutLiteProfessional - const searchParams = useSearchParams('layout', 'tx_status') const defaultLayout = Professional - const updateSearchParams = useUpdateSearchParams<'layout' | 'sort' | 'tx_type'>() + + const searchParams = useSearchParams('layout', 'tx_status') + const layout = searchParams.layout === Lite ? Lite : defaultLayout const txStatus = searchParams.tx_status const isPendingListActive = txStatus === 'pending' - const total = isPendingListActive ? counts.pending : counts.committed - const totalPages = total === '-' ? 0 : Math.ceil(total / pageSize) - - const onChangeLayout = (layoutType: LayoutLiteProfessional) => { - updateSearchParams(params => - layoutType === defaultLayout - ? Object.fromEntries(Object.entries(params).filter(entry => entry[0] !== 'layout')) - : { ...params, layout: layoutType }, - ) - } - const handleTimeSort = () => { - updateSearchParams( - params => - timeOrderBy === 'asc' ? omit(params, ['sort', 'tx_type']) : omit({ ...params, sort: 'time' }, ['tx_type']), - true, - ) - } + // const total = isPendingListActive ? counts.pending : counts.committed + // const totalPages = _totalPages ?? total === '-' ? 0 : Math.ceil(total / pageSize) const newAddr = useNewAddr(address) const isNewAddr = newAddr === address @@ -376,51 +348,8 @@ export const AddressTransactions = ({ })) : transactions - const searchOptionsAndModeSwitch = ( -
-
- {timeOrderBy === 'asc' ? : } -
- onChangeLayout(value)} - value={layout} - optionType="button" - buttonStyle="solid" - /> -
- ) - return ( <> - - - {`${t('transaction.transactions')} (${ - counts.committed === '-' ? counts.committed : localeNumberString(counts.committed) - })`} - {`${t('transaction.pending_transactions')} (${ - counts.pending === '-' ? counts.pending : localeNumberString(counts.pending) - })`} - - } - rightContent={!isMobile && searchOptionsAndModeSwitch} - /> - {isMobile && searchOptionsAndModeSwitch} - - {layout === 'lite' ? ( diff --git a/src/pages/Address/AddressPage.tsx b/src/pages/Address/AddressPage.tsx index d2e570872..46887e787 100644 --- a/src/pages/Address/AddressPage.tsx +++ b/src/pages/Address/AddressPage.tsx @@ -1,13 +1,16 @@ import { useParams } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' -import { Tooltip } from 'antd' +import { Tooltip, Radio } from 'antd' import { FC, useMemo } from 'react' import { addressToScript } from '@nervosnetwork/ckb-sdk-utils' import { Address as AddressInfo } from '../../models/Address' +import { LayoutLiteProfessional } from '../../constants/common' import Content from '../../components/Content' import { AddressContentPanel } from './styled' import { AddressTransactions, AddressOverviewCard } from './AddressComp' +import { ReactComponent as TimeDownIcon } from '../../assets/time_down.svg' +import { ReactComponent as TimeUpIcon } from '../../assets/time_up.svg' import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' import type { Transaction } from '../../models/Transaction' @@ -16,11 +19,16 @@ import { useNewAddr, usePaginationParamsInListPage, useSearchParams, + useUpdateSearchParams, + useIsMobile, useSortParam, } from '../../hooks' +import { omit } from '../../utils/object' +import { localeNumberString } from '../../utils/number' import { isAxiosError } from '../../utils/error' import RgbppBanner from '../../components/RgbppBanner' import { Card, HashCardHeader } from '../../components/Card' +import { CardHeader } from '../../components/Card/CardHeader' import { ReactComponent as ShareIcon } from './share.svg' import styles from './styles.module.scss' import { useDASAccount } from '../../hooks/useDASAccount' @@ -33,8 +41,10 @@ import Qrcode from '../../components/Qrcode' export const Address = () => { const { address } = useParams<{ address: string }>() const { t } = useTranslation() + const isMobile = useIsMobile() const { currentPage, pageSize } = usePaginationParamsInListPage() - const { tx_status: txStatus } = useSearchParams('tx_status') + const searchParams = useSearchParams('layout', 'tx_status') + const { layout: _layout, tx_status: txStatus } = searchParams // REFACTOR: avoid using useSortParam const { sortBy, orderBy, sort } = useSortParam<'time'>(s => s === 'time') @@ -44,6 +54,26 @@ export const Address = () => { const addressInfoQuery = useQuery(['address_info', address], () => explorerService.api.fetchAddressInfo(address)) const isRGBPP = isValidBTCAddress(address) + const updateSearchParams = useUpdateSearchParams<'layout' | 'sort' | 'tx_type'>() + const { Professional, Lite } = LayoutLiteProfessional + const defaultLayout = Professional + const timeOrderBy = sortBy === 'time' ? orderBy : 'desc' + const layout = _layout === Lite ? Lite : defaultLayout + + const onChangeLayout = (layoutType: LayoutLiteProfessional) => { + updateSearchParams(params => + layoutType === defaultLayout + ? Object.fromEntries(Object.entries(params).filter(entry => entry[0] !== 'layout')) + : { ...params, layout: layoutType }, + ) + } + const handleTimeSort = () => { + updateSearchParams( + params => + timeOrderBy === 'asc' ? omit(params, ['sort', 'tx_type']) : omit({ ...params, sort: 'time' }, ['tx_type']), + true, + ) + } let addressInfo: AddressInfo | undefined if (!isRGBPP) { @@ -72,42 +102,19 @@ export const Address = () => { }, defaultAddressInfo) } - const listQueryKey = [ - isPendingTxListActive ? 'address_pending_transactions' : 'address_transactions', - address, - currentPage, - pageSize, - sort, - ] - const listQueryIns = isPendingTxListActive - ? explorerService.api.fetchPendingTransactionsByAddress - : explorerService.api.fetchTransactionsByAddress - - const addressTransactionsQuery = useQuery(listQueryKey, async () => { - try { - const { transactions, total } = await listQueryIns(address, currentPage, pageSize, sort) - return { - transactions, - total, - } - } catch (err) { - const isEmptyAddress = isAxiosError(err) && err.response?.status === 404 - if (isEmptyAddress) { - return { - transactions: [], - total: 0, - } - } - throw err - } - }) - /* FIXME: the total count of tx cannot be aggregated from addresses api if its RGB++ Address because some of them are repeated and double counted */ - /* reuse the cache of address_transactions query by using the same query key */ - const transactionCountQuery = useQuery<{ transactions: Transaction[]; total: number | '-' }>( + const addressTransactionsQuery = useQuery( ['address_transactions', address, currentPage, pageSize, sort], + () => explorerService.api.fetchTransactionsByAddress(address, currentPage, pageSize, sort), + { + enabled: !isPendingTxListActive, + }, + ) + + const addressPendingTransactionsQuery = useQuery( + ['address_pending_transactions', address, currentPage, pageSize, sort], async () => { try { - const { transactions, total } = await explorerService.api.fetchTransactionsByAddress( + const { transactions, total } = await explorerService.api.fetchPendingTransactionsByAddress( address, currentPage, pageSize, @@ -117,6 +124,28 @@ export const Address = () => { transactions, total, } + } catch (err) { + const isEmptyAddress = isAxiosError(err) && err.response?.status === 404 + if (isEmptyAddress) { + return { + transactions: [], + total: 0, + } + } + throw err + } + }, + { + enabled: isPendingTxListActive, + }, + ) + /* FIXME: the total count of tx cannot be aggregated from addresses api if its RGB++ Address because some of them are repeated and double counted */ + /* reuse the cache of address_transactions query by using the same query key */ + const transactionCountQuery = useQuery<{ transactions: Transaction[]; total: number | '-'; totalPages?: number }>( + ['address_transactions', address, currentPage, pageSize, sort], + async () => { + try { + return explorerService.api.fetchTransactionsByAddress(address, currentPage, pageSize, sort) } catch (err) { return { transactions: [], total: '-' } } @@ -165,6 +194,25 @@ export const Address = () => { } }, [addressInfo?.transactionsCount, pendingTransactionCountQuery, transactionCountQuery, isRGBPP]) + const searchOptionsAndModeSwitch = ( +
+
+ {timeOrderBy === 'asc' ? : } +
+ onChangeLayout(value)} + value={layout} + optionType="button" + buttonStyle="solid" + /> +
+ ) + const newAddr = useNewAddr(address) const deprecatedAddr = useDeprecatedAddr(address) const counterpartAddr = newAddr === address ? deprecatedAddr : newAddr @@ -203,18 +251,63 @@ export const Address = () => { - - {data => ( - - )} - + + + {`${t('transaction.transactions')} (${ + transactionCounts.committed === '-' + ? transactionCounts.committed + : localeNumberString(transactionCounts.committed) + })`} + {`${t('transaction.pending_transactions')} (${ + transactionCounts.pending === '-' + ? transactionCounts.pending + : localeNumberString(transactionCounts.pending) + })`} + + } + rightContent={!isMobile && searchOptionsAndModeSwitch} + /> + {isMobile && searchOptionsAndModeSwitch} + + + {!isPendingTxListActive && (transactionCountQuery.data?.totalPages ?? 0) >= 200 && ( +
{t('transaction.page_range_notice')}
+ )} + + {isPendingTxListActive ? ( + + {data => ( + + )} + + ) : ( + + {data => ( + + )} + + )} ) diff --git a/src/pages/Address/styles.module.scss b/src/pages/Address/styles.module.scss index 866d20616..fcc930abf 100644 --- a/src/pages/Address/styles.module.scss +++ b/src/pages/Address/styles.module.scss @@ -351,3 +351,15 @@ color: rgb(0 0 0); pointer-events: auto; } + +.notice { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 8px 12px; + font-size: 14px; + color: #ffb21e; + border: 1px solid #ffdba6; + background-color: #fffcf2; +} diff --git a/src/pages/Script/ScriptsComp.tsx b/src/pages/Script/ScriptsComp.tsx index 122e9a9e2..4282b7af0 100644 --- a/src/pages/Script/ScriptsComp.tsx +++ b/src/pages/Script/ScriptsComp.tsx @@ -72,6 +72,7 @@ export const ScriptTransactions = ({ page, size }: { page: number; size: number return ( <> + {total >= 5000 &&
{t('transaction.range_notice')}
}
{ckbTransactions.length > 0 ? ( ckbTransactions.map(tr => ( diff --git a/src/pages/Script/styles.module.scss b/src/pages/Script/styles.module.scss index 0ccba18d0..349ca3899 100644 --- a/src/pages/Script/styles.module.scss +++ b/src/pages/Script/styles.module.scss @@ -192,3 +192,15 @@ } } } + +.notice { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 8px 12px; + font-size: 14px; + color: #ffb21e; + border: 1px solid #ffdba6; + background-color: #fffcf2; +} diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index bd68e347e..df925c2b3 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -210,6 +210,7 @@ export const apiFetcher = { transactions, pageSize: res.pageSize, total: res.total, + totalPages: res.totalPages, } }, diff --git a/src/services/ExplorerService/types.ts b/src/services/ExplorerService/types.ts index 07f8399b3..a66e37ae7 100644 --- a/src/services/ExplorerService/types.ts +++ b/src/services/ExplorerService/types.ts @@ -17,6 +17,7 @@ export namespace Response { export interface Meta { total: number pageSize: number + totalPages?: number } export interface Wrapper {