Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add pending transaction list on address page #175

Merged
merged 9 commits into from
Dec 21, 2023
3 changes: 2 additions & 1 deletion src/components/TransactionItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ const TransactionItem = ({
>
{transaction.transactionHash}
</AddressText>
{!isBlock && (
{/* transactio.blockNumber is empty string when the transaction is pending or rejected */}
{!isBlock && transaction.blockNumber !== '' && (
<div className="transactionItemBlock">
{`(${t('block.block')} ${localeNumberString(transaction.blockNumber)}) ${parsedBlockCreateAt}`}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@
"transaction": {
"transaction": "Transaction",
"transactions": "Transactions",
"pending_transactions": "Pending Transactions",
"transaction_fee": "Transaction Fee",
"fee_rate": "Fee Rate",
"cell_deps": "CellDeps",
Expand Down
1 change: 1 addition & 0 deletions src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
"home": {
"height": "高度",
"transactions": "交易",
"pending_transactions": "待处理交易",
"block_reward": "奖励 (CKB)",
"time": "时间戳",
"more": "更多",
Expand Down
4 changes: 2 additions & 2 deletions src/models/Transaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export interface CellDep {

export interface Transaction {
transactionHash: string
blockNumber: number
blockTimestamp: number
blockNumber: number | string
blockTimestamp: number | string
WhiteMinds marked this conversation as resolved.
Show resolved Hide resolved
transactionFee: string
income: string
isCellbase: boolean
Expand Down
22 changes: 18 additions & 4 deletions src/pages/Address/AddressComp.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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()
Expand All @@ -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 =>
Expand Down Expand Up @@ -370,7 +375,16 @@ export const AddressTransactions = ({
<Card className={styles.transactionListOptionsCard} rounded="top">
<CardHeader
className={styles.cardHeader}
leftContent={`${t('transaction.transactions')} (${localeNumberString(total)})`}
leftContent={
<div className={styles.txHeaderLabels}>
<Link to={`/address/${address}`} data-is-active={!isPendingListActive}>{`${t(
'transaction.transactions',
)} (${counts.committed === '-' ? counts.committed : localeNumberString(counts.committed)})`}</Link>
<Link to={`/address/${address}?tx_status=pending`} data-is-active={isPendingListActive}>{`${t(
'transaction.pending_transactions',
)} (${counts.pending === '-' ? counts.pending : localeNumberString(counts.pending)})`}</Link>
</div>
}
rightContent={!isMobile && searchOptionsAndModeSwitch}
/>
{isMobile && searchOptionsAndModeSwitch}
Expand Down
81 changes: 59 additions & 22 deletions src/pages/Address/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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)
WhiteMinds marked this conversation as resolved.
Show resolved Hide resolved
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) ?? '-'
WhiteMinds marked this conversation as resolved.
Show resolved Hide resolved
: '-',
pending: pendingTransactionCountQuery.isFetched ? pendingTransactionCountQuery.data ?? '-' : '-',
Keith-CY marked this conversation as resolved.
Show resolved Hide resolved
}

const newAddr = useNewAddr(address)
const deprecatedAddr = useDeprecatedAddr(address)
const counterpartAddr = newAddr === address ? deprecatedAddr : newAddr
Expand Down Expand Up @@ -95,8 +130,10 @@ export const Address = () => {
<AddressTransactions
address={address}
transactions={data?.transactions ?? []}
transactionsTotal={data?.total ?? 0}
timeOrderBy={sortBy === 'time' ? orderBy : 'desc'}
meta={{
counts: transactionCounts,
}}
/>
)}
</QueryResult>
Expand Down
58 changes: 56 additions & 2 deletions src/pages/Address/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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.']
) {
Expand All @@ -46,9 +96,10 @@

.cardHeader {
padding: 20px 0;
overflow: scroll;
WhiteMinds marked this conversation as resolved.
Show resolved Hide resolved

@media (width <= 750px) {
padding: 16px 0;
padding: 10px 0;
}
}
}
Expand All @@ -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;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/pages/Transaction/TransactionComp/TransactionOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: <TransactionBlockHeight blockNumber={blockNumber} txStatus={txStatus} />,
content: <TransactionBlockHeight blockNumber={Number(blockNumber)} txStatus={txStatus} />,
className: styles.firstCardCell,
}
const timestampData: CardCellInfo = {
Expand Down
16 changes: 16 additions & 0 deletions src/services/ExplorerService/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ export const apiFetcher = {
},
}),

fetchPendingTransactionsByAddress: (
address: string,
page: number,
size: number,
sort?: string,
txTypeFilter?: string,
) =>
v1GetUnwrappedPagedList<Transaction>(`address_pending_transactions/${address}`, {
params: {
page,
page_size: size,
sort,
tx_type: txTypeFilter,
},
}),

fetchTransactionRaw: (hash: string) => requesterV2.get<unknown>(`transactions/${hash}/raw`).then(res => res.data),

fetchTransactionByHash: (hash: string) => v1GetUnwrapped<Transaction>(`transactions/${hash}`),
Expand Down