Skip to content

Commit

Permalink
feat: address page support node-connect mode (#391)
Browse files Browse the repository at this point in the history
  • Loading branch information
PainterPuppets authored Jul 11, 2024
1 parent efd7b32 commit 65b5020
Show file tree
Hide file tree
Showing 17 changed files with 718 additions and 323 deletions.
9 changes: 7 additions & 2 deletions src/components/Transaction/TransactionCellArrow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,21 @@ export const CellInputIcon = ({ cell }: { cell: Partial<Pick<Cell, 'generatedTxH
</Link>
) : null

export const CellOutputIcon = ({ cell }: { cell: Pick<Cell, 'status' | 'consumedTxHash'> }) => {
export const CellOutputIcon = ({ cell }: { cell: Partial<Pick<Cell, 'status' | 'consumedTxHash'>> }) => {
const { t } = useTranslation()

if (cell.status === 'dead') {
if (cell.status === 'dead' && cell.consumedTxHash) {
return (
<Link to={`/transaction/${cell.consumedTxHash}`}>
<RightArrow status="dead" />
</Link>
)
}

if (cell.status === 'dead') {
return <RightArrow status="dead" />
}

return (
<Tooltip placement="topRight" title={t('transaction.unspent_output')} arrowPointAtCenter>
<RightArrow status="live" />
Expand Down
52 changes: 46 additions & 6 deletions src/components/TransactionItem/NodeTransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,30 @@ import { useTranslation } from 'react-i18next'
import { useQuery } from '@tanstack/react-query'
import { Transaction } from '@ckb-lumos/base'
import styles from './styles.module.scss'
import { useParsedDate } from '../../hooks'
import { ReactComponent as DirectionIcon } from '../../assets/direction.svg'
import NodeTransactionItemCell from './TransactionItemCell/NodeTransactionItemCell'
import { FullPanel, TransactionHashBlockPanel, TransactionCellPanel, TransactionPanel } from './styled'
import TransactionCellListPanel from './TransactionItemCellList/styled'
import { localeNumberString, isBlockNumber } from '../../utils/number'
import { getTransactionOutputCells, checkIsCellBase } from '../../utils/transaction'
import Loading from '../Loading'
import AddressText from '../AddressText'
import { useCKBNode } from '../../hooks/useCKBNode'
import Cellbase from '../Transaction/Cellbase'
import { IOType } from '../../constants/common'

const NodeTransactionItem = ({ transaction, blockNumber }: { transaction: Transaction; blockNumber?: number }) => {
const NodeTransactionItem = ({
transaction,
blockHashOrNumber,
highlightAddress,
showBlock = true,
}: {
transaction: Transaction
blockHashOrNumber?: string
highlightAddress?: string
showBlock?: boolean
}) => {
const { t } = useTranslation()
const ref = useRef<HTMLDivElement>(null)
const { nodeService } = useCKBNode()
Expand All @@ -23,6 +36,22 @@ const NodeTransactionItem = ({ transaction, blockNumber }: { transaction: Transa
() => nodeService.getInputCells(transaction.inputs),
)

const { data: blockHeader } = useQuery(
['node', 'header', blockHashOrNumber],
() => {
if (!blockHashOrNumber) return null

return isBlockNumber(blockHashOrNumber)
? nodeService.rpc.getHeaderByNumber(`0x${parseInt(blockHashOrNumber, 10).toString(16)}`)
: nodeService.rpc.getHeader(blockHashOrNumber)
},
{
enabled: !!blockHashOrNumber,
},
)

const localTime = useParsedDate(blockHeader?.timestamp ?? 0)

const outputCells = getTransactionOutputCells(transaction)

return (
Expand All @@ -41,18 +70,29 @@ const NodeTransactionItem = ({ transaction, blockNumber }: { transaction: Transa
{transaction.hash!}
</AddressText>
</div>
{blockHashOrNumber && blockHeader && showBlock && (
<div className={styles.right}>
<time dateTime={localTime} className="transactionItemBlock">
{`(${t('block.block')} ${localeNumberString(parseInt(blockHeader.number, 16))}) ${localTime}`}
</time>
</div>
)}
</div>
</TransactionHashBlockPanel>
<TransactionCellPanel>
<div className="transactionItemInput">
<TransactionCellListPanel>
{checkIsCellBase(transaction) ? (
<Cellbase cell={blockNumber ? { targetBlockNumber: blockNumber - 11 } : {}} />
<Cellbase cell={blockHeader ? { targetBlockNumber: parseInt(blockHeader.number, 16) - 11 } : {}} />
) : null}
<Loading show={isInputsLoading} />
{inputCells.map((cell, index) => (
// eslint-disable-next-line react/no-array-index-key
<NodeTransactionItemCell cell={cell} key={index} />
{inputCells.map(cell => (
<NodeTransactionItemCell
cell={cell}
key={`${cell.outPoint?.txHash!}-${cell.outPoint?.index}`}
ioType={IOType.Input}
highlightAddress={highlightAddress}
/>
))}
</TransactionCellListPanel>
</div>
Expand All @@ -63,7 +103,7 @@ const NodeTransactionItem = ({ transaction, blockNumber }: { transaction: Transa
{outputCells.map((cell, index) => (
// eslint-disable-next-line react/no-array-index-key
<FullPanel key={index}>
<NodeTransactionItemCell cell={cell} />
<NodeTransactionItemCell cell={cell} ioType={IOType.Output} highlightAddress={highlightAddress} />
</FullPanel>
))}
</TransactionCellListPanel>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Cell } from '@ckb-lumos/base'
import { useTranslation } from 'react-i18next'
import { useQuery } from '@tanstack/react-query'
import { parseUDTAmount } from '../../../utils/number'
import { shannonToCkb } from '../../../utils/util'
import { explorerService } from '../../../services/ExplorerService'
import { UDT_CELL_TYPES, getCellType, calculateScriptHash, getUDTAmountByData } from '../../../utils/cell'
import Capacity from '../../Capacity'

export const NodeCellCapacityAmount = ({ cell }: { cell: Cell }) => {
const { t } = useTranslation()
const cellType = getCellType(cell)
const isUDTCell = UDT_CELL_TYPES.findIndex(type => type === cellType) !== -1
const udtTypeHash = isUDTCell ? calculateScriptHash(cell.cellOutput.type!) : undefined
const udtInfo = useQuery(
['udt', udtTypeHash],
() => {
if (!udtTypeHash) return undefined
return explorerService.api.fetchSimpleUDT(udtTypeHash)
},
{
enabled: isUDTCell,
staleTime: Infinity,
},
)

if (isUDTCell && udtTypeHash && udtInfo.data) {
const amount = getUDTAmountByData(cell.data)
if (cellType === 'udt' && udtInfo.data.published) {
return <span>{`${parseUDTAmount(amount, udtInfo.data.decimal)} ${udtInfo.data.symbol}`}</span>
}

if (cellType === 'xudt' && udtInfo.data.decimal && udtInfo.data.symbol) {
return <span>{`${parseUDTAmount(amount, udtInfo.data.decimal)} ${udtInfo.data.symbol}`}</span>
}

return <span>{`${t('udt.unknown_token')} #${udtTypeHash.substring(udtTypeHash.length - 4)}`}</span>
}

if (isUDTCell && udtTypeHash) {
return <span>{`${t('udt.unknown_token')} #${udtTypeHash.substring(udtTypeHash.length - 4)}`}</span>
}

return <Capacity capacity={shannonToCkb(cell.cellOutput.capacity)} layout="responsive" />
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import { FC } from 'react'
import { Cell } from '@ckb-lumos/base'
import { Tooltip } from 'antd'
import classNames from 'classnames'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { Link } from '../../Link'
import { shannonToCkb } from '../../../utils/util'
import { TransactionCellPanel, TransactionCellCapacityPanel } from './styled'
import Capacity from '../../Capacity'
import { encodeNewAddress } from '../../../utils/address'
import { NodeCellCapacityAmount } from './NodeCellCapacityAmount'
import CurrentAddressIcon from '../../../assets/current_address.svg'
import { encodeNewAddress, compareAddress } from '../../../utils/address'
import styles from './index.module.scss'
import { useBoolean } from '../../../hooks'
import CopyTooltipText from '../../Text/CopyTooltipText'
import EllipsisMiddle from '../../EllipsisMiddle'
import { IOType } from '../../../constants/common'
import { CellInputIcon, CellOutputIcon } from '../../Transaction/TransactionCellArrow'
import { useCKBNode } from '../../../hooks/useCKBNode'

const Address: FC<{
address: string
Expand Down Expand Up @@ -39,17 +44,49 @@ const Address: FC<{
)
}

const NodeTransactionItemCell = ({ cell }: { cell: Cell }) => {
const NodeTransactionItemCell = ({
cell,
ioType,
highlightAddress,
}: {
cell: Cell
ioType?: IOType
highlightAddress?: string
}) => {
const address = encodeNewAddress(cell.cellOutput.lock)
const { t } = useTranslation()
const { nodeService } = useCKBNode()

const cellStatus = useQuery(
['cellStatus', cell.outPoint],
async () => {
if (!cell.outPoint) return 'dead'
const liveCell = await nodeService.rpc.getLiveCell(cell.outPoint, false)
if (liveCell.status === 'live') return 'live'
return 'dead'
},
{ enabled: cell.outPoint && ioType && ioType === IOType.Output },
)

const highLight = !highlightAddress || !compareAddress(highlightAddress, address)

return (
<TransactionCellPanel highLight>
<TransactionCellPanel highLight={highLight}>
{ioType && ioType === IOType.Input && (
<CellInputIcon cell={{ generatedTxHash: cell.outPoint?.txHash, cellIndex: cell.outPoint?.index }} />
)}
<div className="transactionCellAddress">
<Address address={address} to={`/address/${address}`} />
{ioType === IOType.Output && <CellOutputIcon cell={{ status: cellStatus.data ?? 'dead' }} />}
{!highLight && (
<Tooltip placement="top" title={`${t('address.current-address')} `}>
<img className={styles.currentAddressIcon} src={CurrentAddressIcon} alt="current Address" />
</Tooltip>
)}
</div>
<TransactionCellCapacityPanel>
<div className="transactionCellWithoutIcon">
<Capacity capacity={shannonToCkb(cell.cellOutput.capacity)} />
<NodeCellCapacityAmount cell={cell} />
</div>
</TransactionCellCapacityPanel>
</TransactionCellPanel>
Expand Down
55 changes: 55 additions & 0 deletions src/hooks/transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { RPC } from '@ckb-lumos/rpc'
import { Hash, Transaction } from '@ckb-lumos/base'
import { useInfiniteQuery } from '@tanstack/react-query'
import { useCKBNode } from './useCKBNode'

export const useTransactions = ({
searchKey,
pageSize = 100,
order = 'desc',
}: {
searchKey: Parameters<RPC['getTransactions']>['0']
pageSize?: number
order?: 'desc' | 'asc'
}) => {
const { nodeService } = useCKBNode()

return useInfiniteQuery({
queryKey: ['node', 'transactions', searchKey, pageSize, order],
queryFn: async ({ pageParam = undefined }) => {
const { lastCursor, objects } = await nodeService.rpc.getTransactions(
{ ...searchKey, groupByTransaction: true },
order,
`0x${pageSize.toString(16)}`,
pageParam,
)

if (objects.length === 0) {
return {
lastCursor: undefined,
txs: [],
}
}

const txHashes = objects.map(tx => tx.txHash)

const txs = await nodeService.rpc
.createBatchRequest<'getTransaction', [Hash], CKBComponents.TransactionWithStatus[]>(
txHashes.map(txHash => ['getTransaction', txHash]),
)
.exec()

return {
lastCursor,
txs: txs.map(tx => ({
transaction: tx.transaction as Transaction,
txStatus: tx.txStatus,
})),
}
},
getNextPageParam: options => {
if (options.txs.length < pageSize) return undefined
return options.lastCursor
},
})
}
4 changes: 3 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,9 @@
"current_page": "Page",
"of_page": "of",
"only_first_pages_visible": "Showing first {{pages}} pages",
"show_rows": "Show Rows"
"show_rows": "Show Rows",
"load_more": "Load More",
"no_more_data": "No more data"
},
"udt": {
"sudt": "sUDT",
Expand Down
4 changes: 3 additions & 1 deletion src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,9 @@
"current_page": "",
"of_page": "页,共",
"only_first_pages_visible": "显示最近 {{pages}} 页",
"show_rows": "每页显示"
"show_rows": "每页显示",
"load_more": "加载更多",
"no_more_data": "没有更多数据了"
},
"udt": {
"sudt": "sUDT",
Expand Down
Loading

0 comments on commit 65b5020

Please sign in to comment.