- {show ? `${!sudtAmount.includes('-') ? '+' : ''}${sudtAmount}` : HIDE_BALANCE}
+
+
+ {show ? `${isReceive ? '+' : ''}${sudtAmount}` : HIDE_BALANCE}
+
) : (
-
{amount}
+
+
+ {amount}
+
+ {symbol}
+
)
}
@@ -36,6 +43,7 @@ export const FormattedTokenAmount = ({ item, show, symbolClassName }: FormattedT
let sudtAmount = ''
let copyText = amount
let isReceive = false
+ let symbol = ''
if (item.blockNumber !== undefined) {
if (item.nftInfo) {
@@ -43,20 +51,46 @@ export const FormattedTokenAmount = ({ item, show, symbolClassName }: FormattedT
const { type, data } = item.nftInfo
amount = show ? `${type === 'receive' ? '+' : '-'}${nftFormatter(data)}` : `${HIDE_BALANCE}mNFT`
copyText = amount
+ symbol = amount.includes('mNFT') ? 'mNFT' : ''
+ amount = amount.replace('mNFT', '')
isReceive = type === 'receive'
} else if (item.sudtInfo?.sUDT) {
if (item.sudtInfo.sUDT.decimal) {
sudtAmount = sUDTAmountFormatter(sudtValueToAmount(item.sudtInfo.amount, item.sudtInfo.sUDT.decimal))
copyText = `${sudtValueToAmount(item.sudtInfo.amount, item.sudtInfo.sUDT.decimal)} ${item.sudtInfo.sUDT.symbol}`
+ isReceive = !sudtAmount.includes('-')
}
} else {
- amount = show ? `${shannonToCKBFormatter(item.value, true)} CKB` : `${HIDE_BALANCE} CKB`
+ amount = show ? `${shannonToCKBFormatter(item.value, true)}` : `${HIDE_BALANCE}`
isReceive = !amount.includes('-')
- copyText = amount
+ copyText = `${amount} CKB`
+ symbol = 'CKB'
}
}
- const props = { sudtAmount, show, item, isReceive, amount, symbolClassName }
+ const props = { sudtAmount, show, item, isReceive, amount, symbolClassName, symbol }
+
+ return show ? (
+
+
+
+ ) : (
+
+ )
+}
+
+export const FormattedCKBBalanceChange = ({ item, show, symbolClassName }: FormattedTokenAmountProps) => {
+ let amount = '--'
+ let copyText = amount
+ let isReceive = false
+
+ if (item.blockNumber !== undefined) {
+ amount = show ? `${shannonToCKBFormatter(item.value, true)}` : `${HIDE_BALANCE}`
+ isReceive = !amount.includes('-')
+ copyText = amount
+ }
+
+ const props = { show, item, isReceive, amount, symbolClassName }
return show ? (
diff --git a/packages/neuron-ui/src/components/History/history.module.scss b/packages/neuron-ui/src/components/History/history.module.scss
index c389de4ac8..c749dc8d9e 100644
--- a/packages/neuron-ui/src/components/History/history.module.scss
+++ b/packages/neuron-ui/src/components/History/history.module.scss
@@ -88,11 +88,16 @@ body {
.infoBox {
display: flex;
+ .infoBlock {
+ margin-bottom: 0;
+ }
}
.infoBlock {
+ display: flex;
+ margin-bottom: 20px;
&Title {
- margin-bottom: 8px;
+ margin-right: 12px;
color: $history-info-title-color;
}
@@ -104,7 +109,7 @@ body {
}
.descText {
- padding-bottom: 16px;
+ padding-bottom: 20px;
word-break: break-all;
cursor: pointer;
&:hover {
@@ -130,7 +135,6 @@ body {
.infoOperationBox {
display: flex;
- margin-top: 20px;
gap: 20px;
.explorerNavButton,
diff --git a/packages/neuron-ui/src/components/History/hooks.ts b/packages/neuron-ui/src/components/History/hooks.ts
index 94ba80e0e7..19a40d836f 100644
--- a/packages/neuron-ui/src/components/History/hooks.ts
+++ b/packages/neuron-ui/src/components/History/hooks.ts
@@ -4,6 +4,7 @@ import { listParams, backToTop } from 'utils'
export const useSearch = (search: string, walletID: string, dispatch: React.Dispatch) => {
const [keywords, setKeywords] = useState('')
+ const [sortInfo, setSortInfo] = useState({ sort: '', direction: '' })
const onKeywordsChange = (_e?: React.FormEvent, newValue?: string) => {
if (undefined !== newValue) {
@@ -15,9 +16,10 @@ export const useSearch = (search: string, walletID: string, dispatch: React.Disp
backToTop()
const params = listParams(search)
setKeywords(params.keywords)
+ setSortInfo({ sort: params.sort, direction: params.direction })
updateTransactionList({ ...params, keywords: params.keywords, walletID })(dispatch)
}, [search, walletID, dispatch])
- return { keywords, onKeywordsChange, setKeywords }
+ return { keywords, onKeywordsChange, setKeywords, sortInfo }
}
export default {
diff --git a/packages/neuron-ui/src/components/History/index.tsx b/packages/neuron-ui/src/components/History/index.tsx
index 38c96c5429..73a9bdbb55 100644
--- a/packages/neuron-ui/src/components/History/index.tsx
+++ b/packages/neuron-ui/src/components/History/index.tsx
@@ -10,7 +10,7 @@ import { Download, Search, ArrowNext, Clean } from 'widgets/Icons/icon'
import PageContainer from 'components/PageContainer'
import TransactionStatusWrap from 'components/TransactionStatusWrap'
-import FormattedTokenAmount from 'components/FormattedTokenAmount'
+import FormattedTokenAmount, { FormattedCKBBalanceChange } from 'components/FormattedTokenAmount'
import { useState as useGlobalState, useDispatch } from 'states'
import { exportTransactions } from 'services/remote'
@@ -45,8 +45,11 @@ const History = () => {
const [isExporting, setIsExporting] = useState(false)
const isMainnet = isMainnetUtil(networks, networkID)
- const { keywords, onKeywordsChange } = useSearch(search, id, dispatch)
- const onSearch = useCallback(() => navigate(`${RoutePath.History}?keywords=${keywords}`), [navigate, keywords])
+ const { keywords, onKeywordsChange, sortInfo } = useSearch(search, id, dispatch)
+ const onSearch = useCallback(
+ () => navigate(`${RoutePath.History}?keywords=${keywords}&sort=${sortInfo.sort}&direction=${sortInfo.direction}`),
+ [navigate, keywords]
+ )
const onClean = useCallback(() => onKeywordsChange(undefined, ''), [onKeywordsChange])
const onExport = useCallback(() => {
setIsExporting(true)
@@ -109,7 +112,7 @@ const History = () => {
title: t('history.table.type'),
dataIndex: 'type',
align: 'left',
- minWidth: '120px',
+ minWidth: '100px',
render: (_, __, item) => {
return (
{
/>
)
},
+ sortable: true,
},
{
title: t('history.table.amount'),
dataIndex: 'amount',
align: 'left',
isBalance: true,
- minWidth: '200px',
+ minWidth: '140px',
render(_, __, item, show) {
return
},
},
+ {
+ title: t('history.table.balance'),
+ dataIndex: 'value',
+ align: 'left',
+ isBalance: true,
+ minWidth: '140px',
+ render(_, __, item, show) {
+ return
+ },
+ sortable: true,
+ },
{
title: t('history.table.timestamp'),
dataIndex: 'timestamp',
align: 'left',
minWidth: '150px',
render: (_, __, item) => uniformTimeFormatter(item.timestamp),
+ sortable: true,
},
{
title: t('history.table.status'),
@@ -216,6 +232,9 @@ const History = () => {
)}
expandedRow={expandedRow}
onRowClick={(_, __, idx) => handleExpandClick(idx)}
+ onSorted={(key, type) => {
+ navigate(`${RoutePath.History}?pageNo=${pageNo}&keywords=${keywords}&sort=${key}&direction=${type}`)
+ }}
/>
@@ -225,7 +244,9 @@ const History = () => {
pageSize={pageSize}
pageNo={pageNo}
onChange={(no: number) => {
- navigate(`${RoutePath.History}?pageNo=${no}&keywords=${keywords}`)
+ navigate(
+ `${RoutePath.History}?pageNo=${no}&keywords=${keywords}&sort=${sortInfo.sort}&direction=${sortInfo.direction}`
+ )
}}
/>
diff --git a/packages/neuron-ui/src/components/HistoryDetailPage/historyDetailPage.module.scss b/packages/neuron-ui/src/components/HistoryDetailPage/historyDetailPage.module.scss
index c3242948b3..066467f75b 100644
--- a/packages/neuron-ui/src/components/HistoryDetailPage/historyDetailPage.module.scss
+++ b/packages/neuron-ui/src/components/HistoryDetailPage/historyDetailPage.module.scss
@@ -1,26 +1,36 @@
@import '../../styles/mixin.scss';
-.basicInfoWrap {
+.tx {
+ @include card;
margin-bottom: 16px;
+}
- .basicInfoTitle {
- font-weight: 500;
- font-size: 16px;
+.basicInfoWrap {
+ .basicInfoItemBox {
+ display: flex;
+ font-size: 14px;
padding: 16px;
- }
- .basicInfoItemWrap {
- .basicInfoItemBox {
- display: flex;
- font-size: 14px;
- padding: 16px;
-
- .infoItemLabel {
- min-width: 125px;
- }
+ .infoItemLabel {
+ min-width: 125px;
}
- .basicInfoMiddleWrap {
- display: flex;
+ }
+ .twoColumns {
+ display: flex;
+ }
+ .txHash {
+ flex: 1;
+ border: 1px solid var(--table-head-border-color);
+ border-left: none;
+ border-right: none;
+ }
+ .flexItem {
+ display: flex;
+ align-items: center;
+
+ & > svg {
+ margin-left: 8px;
+ cursor: pointer;
}
}
}
@@ -81,22 +91,6 @@
}
}
-.income {
- display: flex;
- align-items: center;
-
- .incomeCopy {
- height: 20px;
- line-height: 20px;
- cursor: pointer;
- }
-
- & > svg {
- margin-left: 8px;
- cursor: pointer;
- }
-}
-
.amount {
height: 32px;
line-height: 32px;
@@ -115,3 +109,20 @@
.scriptTag:hover {
color: var(--primary-color);
}
+
+.cellInfo {
+ display: flex;
+ align-items: center;
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ &:hover {
+ & > span {
+ color: var(--primary-color);
+ }
+ }
+ & > span {
+ color: var(--main-text-color);
+ margin-right: 4px;
+ }
+}
diff --git a/packages/neuron-ui/src/components/HistoryDetailPage/hooks.ts b/packages/neuron-ui/src/components/HistoryDetailPage/hooks.ts
new file mode 100644
index 0000000000..f8c8d65832
--- /dev/null
+++ b/packages/neuron-ui/src/components/HistoryDetailPage/hooks.ts
@@ -0,0 +1,32 @@
+import { TFunction } from 'i18next'
+import { useCallback, useState } from 'react'
+
+export const TabId = {
+ Basic: 'Basic',
+ Topology: 'Topology',
+}
+
+export const useTxTabs = ({ t }: { t: TFunction }) => {
+ const tabs = [
+ { id: TabId.Basic, label: t('send-tx-detail.basic-info') },
+ { id: TabId.Topology, label: t('send-tx-detail.topology') },
+ ]
+ const [currentTab, setCurrentTab] = useState(tabs[0])
+ return {
+ currentTab,
+ setCurrentTab,
+ tabs,
+ }
+}
+
+export const useCellInfoDialog = () => {
+ const [outputCell, setOutputCell] = useState()
+ const onCancel = useCallback(() => {
+ setOutputCell(undefined)
+ }, [])
+ return {
+ outputCell,
+ setOutputCell,
+ onCancel,
+ }
+}
diff --git a/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx b/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx
index 5dbc8515e4..7a6b141652 100644
--- a/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx
+++ b/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx
@@ -2,16 +2,16 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { scriptToAddress } from '@nervosnetwork/ckb-sdk-utils'
-import { getTransaction } from 'services/remote'
+import { calculateUnlockDaoMaximumWithdraw, getTransaction } from 'services/remote'
import { showPageNotice, transactionState, useDispatch, useState as useGlobalState } from 'states'
import PageContainer from 'components/PageContainer'
import LockInfoDialog from 'components/LockInfoDialog'
import ScriptTag from 'components/ScriptTag'
import AlertDialog from 'widgets/AlertDialog'
import Tabs from 'widgets/Tabs'
-import Table from 'widgets/Table'
+import Table, { TableProps } from 'widgets/Table'
import CopyZone from 'widgets/CopyZone'
-import { BalanceHide, BalanceShow, Copy } from 'widgets/Icons/icon'
+import { ArrowNext, BalanceHide, BalanceShow, Copy } from 'widgets/Icons/icon'
import Tooltip from 'widgets/Tooltip'
import Breadcrum from 'widgets/Breadcrum'
@@ -22,9 +22,16 @@ import {
shannonToCKBFormatter,
isSuccessResponse,
isMainnet as isMainnetUtil,
+ calculateFee,
} from 'utils'
-import { HIDE_BALANCE } from 'utils/const'
+import { CONFIRMATION_THRESHOLD, HIDE_BALANCE } from 'utils/const'
+import TxTopology from 'components/SendTxDetail/TxTopology'
+import FormattedTokenAmount, { FormattedCKBBalanceChange } from 'components/FormattedTokenAmount'
+import TransactionStatusWrap from 'components/TransactionStatusWrap'
+import CellInfoDialog from 'components/CellInfoDialog'
+import TransactionType from 'components/TransactionType'
+import { TabId, useCellInfoDialog, useTxTabs } from './hooks'
import styles from './historyDetailPage.module.scss'
type InputOrOutputType = (State.DetailedInput | State.DetailedOutput) & { idx: number }
@@ -36,18 +43,147 @@ const InfoItem = ({ label, value, className }: { label: string; value: React.Rea
)
+const BasicInfo = ({
+ transaction,
+ cacheTipBlockNumber,
+ bestKnownBlockNumber,
+ txFee,
+}: {
+ transaction: State.DetailedTransaction
+ cacheTipBlockNumber: number
+ bestKnownBlockNumber: number
+ txFee: string
+}) => {
+ const [t] = useTranslation()
+ const dispatch = useDispatch()
+ const onCopy = useCallback(() => {
+ window.navigator.clipboard.writeText(transaction.hash)
+ showPageNotice('common.copied')(dispatch)
+ }, [transaction.hash, dispatch])
+ const [isAmountShow, setIsAmountShow] = useState(true)
+ const onChangeAmountShow = useCallback(() => {
+ setIsAmountShow(v => !v)
+ }, [])
+ const [isBalanceShow, setIsBalanceShow] = useState(true)
+ const onChangeBalanceShow = useCallback(() => {
+ setIsBalanceShow(v => !v)
+ }, [])
+ const bestBlockNumber = Math.max(cacheTipBlockNumber, bestKnownBlockNumber)
+ const confirmationCount = 1 + bestBlockNumber - +transaction.blockNumber
+ const status =
+ transaction.status === 'success' && confirmationCount < CONFIRMATION_THRESHOLD ? 'confirming' : transaction.status
+
+ const infos = {
+ hash: {
+ label: t('transaction.transaction-hash'),
+ value: (
+