From 3ed331134ea965fdfcb4e21b4de7674ef576bba1 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 12 Dec 2023 19:54:06 +0900 Subject: [PATCH 1/8] feat: add pending transaction list on address page --- src/components/TransactionItem/index.tsx | 3 +- src/hooks/index.ts | 2 +- src/locales/en.json | 1 + src/locales/zh.json | 1 + src/models/Transaction/index.ts | 4 +- src/pages/Address/AddressComp.tsx | 22 ++++- src/pages/Address/index.tsx | 81 ++++++++++++++----- src/pages/Address/styles.module.scss | 58 ++++++++++++- .../TransactionComp/TransactionOverview.tsx | 4 +- src/services/ExplorerService/fetcher.ts | 16 ++++ 10 files changed, 158 insertions(+), 34 deletions(-) diff --git a/src/components/TransactionItem/index.tsx b/src/components/TransactionItem/index.tsx index 2112f0201..90b7c737c 100644 --- a/src/components/TransactionItem/index.tsx +++ b/src/components/TransactionItem/index.tsx @@ -67,7 +67,8 @@ const TransactionItem = ({ > {transaction.transactionHash} - {!isBlock && ( + {/* transactio.blockNumber is empty string when the transaction is pending or rejected */} + {!isBlock && transaction.blockNumber !== '' && (
{`(${t('block.block')} ${localeNumberString(transaction.blockNumber)}) ${parsedBlockCreateAt}`}
diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 5b7d5d82c..c85ab6d24 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -208,7 +208,7 @@ export function useTimestamp(): number { return timestamp } -export function useParsedDate(timestamp: number): string { +export function useParsedDate(timestamp: number | string): string { const parseDate = useParseDate() const now = useTimestamp() return parseDate(timestamp, now) diff --git a/src/locales/en.json b/src/locales/en.json index 49473e5d2..c530640f9 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -375,6 +375,7 @@ "transaction": { "transaction": "Transaction", "transactions": "Transactions", + "pending_transactions": "Pending Transactions", "transaction_fee": "Transaction Fee", "fee_rate": "Fee Rate", "cell_deps": "CellDeps", diff --git a/src/locales/zh.json b/src/locales/zh.json index c48092f68..385ea8291 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -298,6 +298,7 @@ "home": { "height": "高度", "transactions": "交易", + "pending_transactions": "待处理交易", "block_reward": "奖励 (CKB)", "time": "时间戳", "more": "更多", diff --git a/src/models/Transaction/index.ts b/src/models/Transaction/index.ts index 33cb47d57..080cc9cfa 100644 --- a/src/models/Transaction/index.ts +++ b/src/models/Transaction/index.ts @@ -10,8 +10,8 @@ export interface CellDep { export interface Transaction { transactionHash: string - blockNumber: number - blockTimestamp: number + blockNumber: number | string + blockTimestamp: number | string transactionFee: string income: string isCellbase: boolean diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index 9d871cfb0..d7c59b2ee 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -1,5 +1,6 @@ import axios, { AxiosResponse } from 'axios' import { useState, useEffect, FC } from 'react' +import { Link } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { Radio } from 'antd' import { Base64 } from 'js-base64' @@ -297,13 +298,13 @@ export const AddressOverviewCard: FC<{ address: Address }> = ({ address }) => { export const AddressTransactions = ({ address, transactions, - transactionsTotal: total, timeOrderBy, + meta: { counts }, }: { address: string transactions: Transaction[] - transactionsTotal: number timeOrderBy: OrderByType + meta: { counts: Record<'committed' | 'pending', number | '-'> } }) => { const isMobile = useIsMobile() const { t } = useTranslation() @@ -313,7 +314,11 @@ export const AddressTransactions = ({ const defaultLayout = Professional const updateSearchParams = useUpdateSearchParams<'layout' | 'sort' | 'tx_type'>() const layout = searchParams.layout === Lite ? Lite : defaultLayout - const totalPages = Math.ceil(total / pageSize) + + const { tx_status: txStatus } = useSearchParams('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 => @@ -370,7 +375,16 @@ export const AddressTransactions = ({ + {`${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} diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index 3d6a60f24..f68c0b38c 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -8,7 +8,13 @@ import { AddressContentPanel } from './styled' import { AddressTransactions, AddressOverviewCard } from './AddressComp' import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' -import { useDeprecatedAddr, useNewAddr, usePaginationParamsInListPage, useSortParam } from '../../hooks' +import { + useDeprecatedAddr, + useNewAddr, + usePaginationParamsInListPage, + useSearchParams, + useSortParam, +} from '../../hooks' import { isAxiosError } from '../../utils/error' import { Card, HashCardHeader } from '../../components/Card' import { ReactComponent as ShareIcon } from './share.svg' @@ -19,39 +25,68 @@ export const Address = () => { const { address } = useParams<{ address: string }>() const { t } = useTranslation() const { currentPage, pageSize } = usePaginationParamsInListPage() + const { tx_status: txStatus } = useSearchParams('tx_status') // REFACTOR: avoid using useSortParam const { sortBy, orderBy, sort } = useSortParam<'time'>(s => s === 'time') + const isPendingTxListActive = txStatus === 'pending' + const addressInfoQuery = useQuery(['address_info', address], () => explorerService.api.fetchAddressInfo(address)) - const addressTransactionsQuery = useQuery( - ['address_transactions', address, currentPage, pageSize, sort], - async () => { - try { - const { data: transactions, total } = await explorerService.api.fetchTransactionsByAddress( - address, - currentPage, - pageSize, - sort, - ) + 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 { data: 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, + transactions: [], + total: 0, } + } + throw err + } + }) + + const pendingTransactionCountQuery = useQuery( + ['address_pending_transactions', address], + async () => { + try { + const { total } = await explorerService.api.fetchPendingTransactionsByAddress(address, 1, 10) + return total } catch (err) { - const isEmptyAddress = isAxiosError(err) && err.response?.status === 404 - if (isEmptyAddress) { - return { - transactions: [], - total: 0, - } - } - throw err + return '-' } }, + { + initialData: '-', + }, ) + const transactionCounts: Record<'committed' | 'pending', number | '-'> = { + committed: + addressInfoQuery.isFetched && addressInfoQuery.data + ? Number(addressInfoQuery.data.transactionsCount) ?? '-' + : '-', + pending: pendingTransactionCountQuery.isFetched ? pendingTransactionCountQuery.data ?? '-' : '-', + } + const newAddr = useNewAddr(address) const deprecatedAddr = useDeprecatedAddr(address) const counterpartAddr = newAddr === address ? deprecatedAddr : newAddr @@ -95,8 +130,10 @@ export const Address = () => { )} diff --git a/src/pages/Address/styles.module.scss b/src/pages/Address/styles.module.scss index ac9caf97f..b6f649400 100644 --- a/src/pages/Address/styles.module.scss +++ b/src/pages/Address/styles.module.scss @@ -35,6 +35,56 @@ } } +.txHeaderLabels { + display: flex; + align-items: stretch; + + @media (width <= 750px) { + margin-bottom: 1rem; + } + + a { + position: relative; + height: 100%; + font-weight: 400; + white-space: nowrap; + margin-right: 1rem; + + &[data-is-active='true'] { + font-weight: 600; // 500 is set in figma, but not obvious enough in fact + &::after { + content: ''; + display: block; + width: 72px; + height: 4px; + background: var(--primary-color); + position: absolute; + bottom: -24px; + left: 50%; + transform: translateX(-50%); + + @media (width <= 750px) { + width: 104px; + height: 2px; + bottom: -10px; + } + } + } + + &:first-child { + margin-right: 4rem; + + @media (width <= 1200px) { + margin-right: 1rem; + } + } + + &:hover { + color: var(--primary-color); + } + } +} + .transactionListOptionsCard:not( [_='This `:not` selector is used to increase the specificity of the selector and serves no other purpose.'] ) { @@ -46,9 +96,10 @@ .cardHeader { padding: 20px 0; + overflow: scroll; @media (width <= 750px) { - padding: 16px 0; + padding: 10px 0; } } } @@ -57,10 +108,13 @@ display: flex; gap: 40px; + @media (width <= 1200px) { + gap: 1rem; + } + @media (width <= 750px) { flex-direction: column-reverse; align-items: flex-end; - gap: 16px; } } diff --git a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx index 821bb090c..b00ec6aae 100644 --- a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx @@ -144,13 +144,13 @@ export const TransactionOverviewCard: FC<{ const isProfessional = layout === LayoutLiteProfessional.Professional if (tipBlockNumber && blockNumber) { - confirmation = tipBlockNumber - blockNumber + confirmation = Number(tipBlockNumber) - Number(blockNumber) } const blockHeightData: CardCellInfo = { title: t('block.block_height'), tooltip: t('glossary.block_height'), - content: , + content: , className: styles.firstCardCell, } const timestampData: CardCellInfo = { diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 58c80f425..f244ebed3 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -79,6 +79,22 @@ export const apiFetcher = { }, }), + fetchPendingTransactionsByAddress: ( + address: string, + page: number, + size: number, + sort?: string, + txTypeFilter?: string, + ) => + v1GetUnwrappedPagedList(`address_pending_transactions/${address}`, { + params: { + page, + page_size: size, + sort, + tx_type: txTypeFilter, + }, + }), + fetchTransactionRaw: (hash: string) => requesterV2.get(`transactions/${hash}/raw`).then(res => res.data), fetchTransactionByHash: (hash: string) => v1GetUnwrapped(`transactions/${hash}`), From 7faab8fd340c8bea38950532ea51d95af69d5d2c Mon Sep 17 00:00:00 2001 From: Keith Date: Wed, 13 Dec 2023 19:31:22 +0900 Subject: [PATCH 2/8] feat: set scrollbar of table header invisible --- src/pages/Address/styles.module.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/Address/styles.module.scss b/src/pages/Address/styles.module.scss index b6f649400..2eb552036 100644 --- a/src/pages/Address/styles.module.scss +++ b/src/pages/Address/styles.module.scss @@ -98,6 +98,10 @@ padding: 20px 0; overflow: scroll; + &::-webkit-scrollbar { + display: none; + } + @media (width <= 750px) { padding: 10px 0; } From abcdef8c02b7d2f98b0ef72da01dedeacf0e11f1 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 13 Dec 2023 19:32:14 +0900 Subject: [PATCH 3/8] Update src/pages/Address/index.tsx Co-authored-by: WhiteMind Signed-off-by: Chen Yu --- src/pages/Address/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index f68c0b38c..9b9bc1d53 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -84,7 +84,7 @@ export const Address = () => { addressInfoQuery.isFetched && addressInfoQuery.data ? Number(addressInfoQuery.data.transactionsCount) ?? '-' : '-', - pending: pendingTransactionCountQuery.isFetched ? pendingTransactionCountQuery.data ?? '-' : '-', + pending: pendingTransactionCountQuery.data ?? '-', } const newAddr = useNewAddr(address) From d56acfb29a1d877a45d95d39dea53bc1a7a4e91f Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 14 Dec 2023 17:32:59 +0900 Subject: [PATCH 4/8] docs: add comments about the type declaration to fix --- src/models/Transaction/index.ts | 1 + src/pages/Address/index.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/models/Transaction/index.ts b/src/models/Transaction/index.ts index 080cc9cfa..0d5ddf443 100644 --- a/src/models/Transaction/index.ts +++ b/src/models/Transaction/index.ts @@ -10,6 +10,7 @@ export interface CellDep { export interface Transaction { transactionHash: string + // FIXME: this type declaration should be fixed by adding a transformation between internal state and response of API blockNumber: number | string blockTimestamp: number | string transactionFee: string diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index f68c0b38c..41db406d6 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -82,7 +82,8 @@ export const Address = () => { const transactionCounts: Record<'committed' | 'pending', number | '-'> = { committed: addressInfoQuery.isFetched && addressInfoQuery.data - ? Number(addressInfoQuery.data.transactionsCount) ?? '-' + ? // FIXME: this type convertion could be removed once the type declaration of Transaction is fixed + Number(addressInfoQuery.data.transactionsCount) ?? '-' : '-', pending: pendingTransactionCountQuery.isFetched ? pendingTransactionCountQuery.data ?? '-' : '-', } From dfd8f5d5ae37cb117a2f0cff8f1900466888a4f0 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 18 Dec 2023 19:17:54 +0900 Subject: [PATCH 5/8] fix: use the same query to fetch total count and list of pending transactions --- src/pages/Address/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index 8b591e28c..6a519efb8 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -65,10 +65,15 @@ export const Address = () => { }) const pendingTransactionCountQuery = useQuery( - ['address_pending_transactions', address], + ['address_pending_transactions', address, currentPage, pageSize, sort], async () => { try { - const { total } = await explorerService.api.fetchPendingTransactionsByAddress(address, 1, 10) + const { total } = await explorerService.api.fetchPendingTransactionsByAddress( + address, + currentPage, + pageSize, + sort, + ) return total } catch (err) { return '-' From 3ae33e6d58194e62b41158cd139aaf953448ea74 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 19 Dec 2023 13:18:15 +0900 Subject: [PATCH 6/8] fix: fix cache of pending transactions query --- src/pages/Address/index.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index 6a519efb8..a3a287f32 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -8,6 +8,7 @@ import { AddressContentPanel } from './styled' import { AddressTransactions, AddressOverviewCard } from './AddressComp' import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' +import type { Transaction } from '../../models/Transaction' import { useDeprecatedAddr, useNewAddr, @@ -64,23 +65,27 @@ export const Address = () => { } }) - const pendingTransactionCountQuery = useQuery( + /* reuse the cache of address_pending_transactions query by using the same query key */ + const pendingTransactionCountQuery = useQuery<{ transactions: Transaction[]; total: number | '-' }>( ['address_pending_transactions', address, currentPage, pageSize, sort], async () => { try { - const { total } = await explorerService.api.fetchPendingTransactionsByAddress( + const { data: transactions, total } = await explorerService.api.fetchPendingTransactionsByAddress( address, currentPage, pageSize, sort, ) - return total + return { + transactions, + total, + } } catch (err) { - return '-' + return { transactions: [], total: '-' } } }, { - initialData: '-', + initialData: { transactions: [], total: '-' }, }, ) @@ -90,7 +95,7 @@ export const Address = () => { ? // FIXME: this type conversion could be removed once the type declaration of Transaction is fixed Number(addressInfoQuery.data.transactionsCount) ?? '-' : '-', - pending: pendingTransactionCountQuery.data ?? '-', + pending: pendingTransactionCountQuery.data.total ?? '-', } const newAddr = useNewAddr(address) From 9c115bf37896a61fc863cb6fd63b4f3b9a347044 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 21 Dec 2023 00:27:38 +0900 Subject: [PATCH 7/8] feat: update lite transaction list on address page for pending list --- .../LiteTransactionList.module.scss | 148 ++++++++++++++++++ src/components/LiteTransactionList/index.tsx | 125 +++++++++++++++ .../TransactionLiteIncome/index.module.scss | 26 --- .../TransactionLiteIncome/index.tsx | 23 --- .../TransactionLiteItem/index.module.scss | 119 -------------- .../TransactionLiteItem/index.tsx | 55 ------- src/components/TransactionItem/index.tsx | 46 +++++- src/locales/en.json | 3 +- src/locales/zh.json | 3 +- src/pages/Address/AddressComp.tsx | 59 ++++--- src/pages/Address/index.tsx | 2 +- src/pages/Address/styles.module.scss | 9 ++ 12 files changed, 353 insertions(+), 265 deletions(-) create mode 100644 src/components/LiteTransactionList/LiteTransactionList.module.scss create mode 100644 src/components/LiteTransactionList/index.tsx delete mode 100644 src/components/TransactionItem/TransactionLiteIncome/index.module.scss delete mode 100644 src/components/TransactionItem/TransactionLiteIncome/index.tsx delete mode 100644 src/components/TransactionItem/TransactionLiteItem/index.module.scss delete mode 100644 src/components/TransactionItem/TransactionLiteItem/index.tsx diff --git a/src/components/LiteTransactionList/LiteTransactionList.module.scss b/src/components/LiteTransactionList/LiteTransactionList.module.scss new file mode 100644 index 000000000..05b6e6ad4 --- /dev/null +++ b/src/components/LiteTransactionList/LiteTransactionList.module.scss @@ -0,0 +1,148 @@ +.container { + padding: 0 2rem; + background: #fff; + + table { + width: 100%; + + th, + td { + height: 4rem; + padding: 0 8px; + white-space: nowrap; + font-size: 1rem; + font-weight: 500; + text-align: left; + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + text-align: right; + } + + &.noRecords:last-child { + text-align: center; + } + } + + thead { + th { + font-weight: 600; + } + } + + tbody { + tr { + border-top: 1px solid #e5e5e5; + } + } + } + + a { + color: var(--primary-color); + } + + .hash { + font-size: 0.875rem; + max-width: 25vw; + } + + .height { + font-family: Roboto, inherit, sans-serif; + } + + .cells { + white-space: pre; + } + + .income > span { + display: inline-flex; + + * { + color: inherit; + font-size: 1rem; + + // REFACTOR: it's hard to maintain, the component of decimal should be refactored + :global { + div.addition, + div.subtraction { + font-size: 0.875rem; + } + } + } + + &[data-is-positive='true'][data-is-zero='false'] { + color: var(--primary-color); + + &::before { + content: '+'; + } + } + + &[data-is-zero='true'] { + color: #999; + } + + &[data-is-positive='false'] { + color: var(--accent-color); + } + } + + // TODO: use https://github.com/Magickbase/ckb-explorer-frontend/pull/178/files#diff-0f21b8f9c1d917feff4b138db3e62f202f2e3c1b691a2cd309397400edfca591R3 + @media screen and (width <= 1200px) { + padding: 0; + + table { + thead { + display: none; + } + + tbody { + tr { + display: flex; + flex-direction: column; + align-items: stretch; + border-top: 4px solid #ededed; + } + + td { + position: relative; + display: flex; + flex-direction: column; + height: auto; + padding: 0.625rem 1rem; + + &:first-child { + padding-left: 1rem; + max-width: calc(100vw - 2.25rem); + } + + &:last-child { + text-align: left; + } + + &:not(:last-child)::after { + position: absolute; + bottom: 0; + left: 1rem; + right: 1rem; + content: ''; + display: block; + height: 1px; + background: #f5f5f5; + } + + &::before { + content: attr(title); + font-size: 1rem; + color: #666; + margin-bottom: 0.5rem; + } + } + } + } + } +} diff --git a/src/components/LiteTransactionList/index.tsx b/src/components/LiteTransactionList/index.tsx new file mode 100644 index 000000000..2c25feaea --- /dev/null +++ b/src/components/LiteTransactionList/index.tsx @@ -0,0 +1,125 @@ +import { useEffect, useState } from 'react' +import { Link } from 'react-router-dom' +import { useTranslation } from 'react-i18next' +import BigNumber from 'bignumber.js' +import type { Transaction } from '../../models/Transaction' +import { useSearchParams } from '../../hooks' +import styles from './LiteTransactionList.module.scss' +import AddressText from '../AddressText' +import { localeNumberString } from '../../utils/number' +import { useParseDate } from '../../utils/date' +import DecimalCapacity from '../DecimalCapacity' +import { shannonToCkb } from '../../utils/util' + +const LiteTransactionList: React.FC<{ + address?: string + list: Transaction[] +}> = ({ address, list }) => { + const [t] = useTranslation() + const [timeBase, setTimeBase] = useState(Date.now()) + const dateParser = useParseDate() + + useEffect(() => { + const timer = setInterval(() => { + setTimeBase(Date.now()) + }, 1000) + return () => { + clearInterval(timer) + } + }, [setTimeBase]) + + const { tx_status: txStatus } = useSearchParams('tx_status') + + const isPendingListActive = txStatus === 'pending' + + const headers = [ + t('transaction.transaction_hash'), + isPendingListActive ? null : t('transaction.height'), + t('transaction.time'), + `${t('transaction.input')} & ${t('transaction.output')}`, + address ? t('transaction.capacity_change') : null, + ].filter(v => v) + + return ( +
+ + + + {headers.map(header => ( + + ))} + + + + {list.length ? ( + list.map(item => { + let timestamp = 0 + if (item.blockTimestamp) { + timestamp = +item.blockTimestamp + } else if (item.createTimestamp) { + // FIXME: round the timestamp because sometimes a decimal is returned from the API, could be removed when the API is fixed + timestamp = Math.floor(Number(item.createTimestamp)) + } + + const dateTime = new Date(timestamp).toISOString() + const localTime = dateParser(timestamp, timeBase) + + let bigIncome = new BigNumber(item.income) + if (bigIncome.isNaN()) { + bigIncome = new BigNumber(0) + } + const isIncome = bigIncome.isGreaterThanOrEqualTo(0) + return ( + + + {isPendingListActive ? null : ( + + )} + + + {address ? ( + + ) : null} + + ) + }) + ) : ( + + + + )} + +
{header}
+ + {item.transactionHash} + + + + {localeNumberString(item.blockNumber)} + + + + + {`${t('transaction.input')}: ${item.displayInputs.length} ${t('transaction.output')}: ${ + item.displayOutputs.length + }`} + + + + +
+ {t('transaction.no_records')} +
+
+ ) +} + +export default LiteTransactionList diff --git a/src/components/TransactionItem/TransactionLiteIncome/index.module.scss b/src/components/TransactionItem/TransactionLiteIncome/index.module.scss deleted file mode 100644 index 3b4fabb5a..000000000 --- a/src/components/TransactionItem/TransactionLiteIncome/index.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -.transactionLitePanel { - > div { - font-weight: 500; - font-size: 16px; - line-height: 19px; - - > div:not(:last-child) { - font-size: 14px; - line-height: 16px; - } - - @media (width >= 751px) and (width <= 960px) { - > div:not(:last-child) { - display: none; - } - } - } -} - -.increased { - color: var(--primary-color); -} - -.decreased { - color: var(--accent-color); -} diff --git a/src/components/TransactionItem/TransactionLiteIncome/index.tsx b/src/components/TransactionItem/TransactionLiteIncome/index.tsx deleted file mode 100644 index 8f389f742..000000000 --- a/src/components/TransactionItem/TransactionLiteIncome/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import BigNumber from 'bignumber.js' -import classNames from 'classnames' -import { shannonToCkb } from '../../../utils/util' -import { localeNumberString } from '../../../utils/number' -import DecimalCapacity from '../../DecimalCapacity' -import styles from './index.module.scss' - -export default ({ income }: { income: string }) => { - let bigIncome = new BigNumber(income) - if (bigIncome.isNaN()) { - bigIncome = new BigNumber(0) - } - const isIncome = bigIncome.isGreaterThanOrEqualTo(0) - return ( -
- -
- ) -} diff --git a/src/components/TransactionItem/TransactionLiteItem/index.module.scss b/src/components/TransactionItem/TransactionLiteItem/index.module.scss deleted file mode 100644 index dd1a1051c..000000000 --- a/src/components/TransactionItem/TransactionLiteItem/index.module.scss +++ /dev/null @@ -1,119 +0,0 @@ -.transactionLitePanel { - width: 100%; - background-color: #fff; - padding: 0 40px; - display: flex; - - .transactionLiteRow { - width: 100%; - border-top: 1px solid #e5e5e5; - padding: 23px 0; - display: flex; - flex-direction: row; - font-weight: 500; - font-size: 16px; - line-height: 19px; - - .transactionLink { - font-weight: 500; - font-size: 14px; - line-height: 18px; - color: var(--primary-color); - } - - .blockLink { - font-weight: 400; - font-size: 16px; - line-height: 19px; - color: var(--primary-color); - } - - > div { - padding: 0 6px 0 0; - font-weight: 400; - font-size: 16px; - line-height: 19px; - color: #333; - - &:first-child { - width: 28%; - padding-right: 20px; - - @media (width >= 1200px) { - padding-right: 50px; - } - } - - &:nth-child(2) { - width: 13%; - } - - &:nth-child(3) { - width: 20%; - } - - &:nth-child(4) { - width: 27%; - - > span:last-child { - margin-left: 16px; - } - } - - &:last-child { - width: 12%; - text-align: right; - padding: 0; - } - } - } - - @media (width <= 750px) { - padding: 0 16px; - margin-bottom: 4px; - - .transactionLiteRow { - flex-direction: column; - border-top: none; - padding: 0; - - > div { - // This selector is just to increase the specificity. - &:nth-child(n) { - width: 100%; - padding: 16px 0; - } - - > div:first-child { - color: #666; - margin-bottom: 8px; - } - - &:not(:first-child) { - border-top: 1px solid #f0f0f0; - } - } - - > div:first-child { - > a > div { - padding-right: 38px; - } - } - - > div:nth-child(4) { - > span:last-child { - margin-left: 24px; - } - } - - > div:last-child { - text-align: left; - width: 100%; - - > div:last-child > div { - justify-content: flex-start; - } - } - } - } -} diff --git a/src/components/TransactionItem/TransactionLiteItem/index.tsx b/src/components/TransactionItem/TransactionLiteItem/index.tsx deleted file mode 100644 index 2169bce25..000000000 --- a/src/components/TransactionItem/TransactionLiteItem/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Link } from 'react-router-dom' -import { useTranslation } from 'react-i18next' -import { localeNumberString } from '../../../utils/number' -import AddressText from '../../AddressText' -import styles from './index.module.scss' -import TransactionLiteIncome from '../TransactionLiteIncome' -import { useIsMobile, useParsedDate } from '../../../hooks' -import { Transaction } from '../../../models/Transaction' - -const TransactionLiteItem = ({ transaction, address }: { transaction: Transaction; address?: string }) => { - const isMobile = useIsMobile() - const { t } = useTranslation() - const parsedBlockCreateAt = useParsedDate(transaction.blockTimestamp) - return ( -
-
-
- {isMobile &&
{t('transaction.transaction_hash')}
} - - {transaction.transactionHash} - -
-
- {isMobile &&
{t('transaction.height')}
} - - {localeNumberString(transaction.blockNumber)} - -
-
- {isMobile &&
{t('transaction.time')}
} - {parsedBlockCreateAt} -
-
- {isMobile &&
{`${t('transaction.input')} & ${t('transaction.output')}`}
} - {transaction.displayInputs && `${t('transaction.input')}: ${transaction.displayInputs.length}`} - - {transaction.displayOutputs && `${t('transaction.output')}: ${transaction.displayOutputs.length}`} - -
-
- {isMobile &&
{t('transaction.capacity_change')}
} - {address && } -
-
-
- ) -} - -export default TransactionLiteItem diff --git a/src/components/TransactionItem/index.tsx b/src/components/TransactionItem/index.tsx index ea3989d7f..dedd18f08 100644 --- a/src/components/TransactionItem/index.tsx +++ b/src/components/TransactionItem/index.tsx @@ -17,6 +17,43 @@ export interface CircleCorner { bottom?: boolean } +const Time: React.FC<{ tx?: Transaction }> = ({ tx }) => { + const { t } = useTranslation() + let timestamp = 0 + if (tx) { + if (tx.blockTimestamp) { + timestamp = +tx.blockTimestamp + } else if (tx.createTimestamp) { + // FIXME: round the timestamp because sometimes a decimal is returned from the API, could be removed when the API is fixed + timestamp = Math.floor(Number(tx.createTimestamp)) + } + } + + const dateTime = new Date(timestamp).toISOString() + const localTime = useParsedDate(timestamp) + + if (!tx) { + return null + } + + if (tx.blockTimestamp) { + return ( + + ) + } + + if (tx.createTimestamp) { + return ( + + ) + } + return null +} + const TransactionItem = ({ transaction, address, @@ -51,8 +88,6 @@ const TransactionItem = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - const parsedBlockCreateAt = useParsedDate(transaction.blockTimestamp) - return ( {titleCard} @@ -67,12 +102,7 @@ const TransactionItem = ({ > {transaction.transactionHash} - {/* FIXME: transactio.blockNumber is empty string when the transaction is pending or rejected, this should be updated once the type declaration is fixed to number */} - {!isBlock && transaction.blockNumber !== '' && ( -
- {`(${t('block.block')} ${localeNumberString(transaction.blockNumber)}) ${parsedBlockCreateAt}`} -
- )} +