diff --git a/src/components/HashTag/styled.tsx b/src/components/HashTag/styled.tsx index b2776b439..98b27b72c 100644 --- a/src/components/HashTag/styled.tsx +++ b/src/components/HashTag/styled.tsx @@ -2,7 +2,9 @@ import styled from 'styled-components' import variables from '../../styles/variables.module.scss' export const TagPanel = styled.div` + width: max-content; height: 20px; + white-space: nowrap; border-radius: 4px; border: solid 0.5px ${({ isLock }: { isLock?: boolean }) => (isLock ? '#b1caff' : '#caacef')}; background-color: ${({ isLock }: { isLock?: boolean }) => (isLock ? '#d8e4ff' : '#f0e0fb')}; diff --git a/src/components/RawTransactionView/index.tsx b/src/components/RawTransactionView/index.tsx index a604c954d..057cdf089 100644 --- a/src/components/RawTransactionView/index.tsx +++ b/src/components/RawTransactionView/index.tsx @@ -7,7 +7,7 @@ import { getTx } from '../../services/NodeService' import styles from './styles.module.scss' const RawTransactionView: FC<{ hash: string }> = ({ hash }) => { - const { data, isLoading } = useQuery<{ result: { transaction: any } }>(['tx', hash], () => getTx(hash)) + const { data, isLoading } = useQuery(['tx', hash], () => getTx(hash)) if (isLoading) { return (
@@ -16,6 +16,7 @@ const RawTransactionView: FC<{ hash: string }> = ({ hash }) => { ) } if (!data?.result?.transaction) return
{`Transaction ${hash} not loaded`}
+ return ( = ({ hash }) => { case 'code_hash': { const [, index, lockType] = select.namespace if (!index || !lockType) return - const script = data.result.transaction.outputs[index][lockType] - window.open(`/script/${script.code_hash}/${script.hash_type}`, '_blank') + const script = data.result.transaction?.outputs[index as any][lockType as 'lock' | 'type'] + if (script) { + window.open(`/script/${script.code_hash}/${script.hash_type}`, '_blank') + } break } default: { diff --git a/src/components/TransactionParameters/index.module.scss b/src/components/TransactionParameters/index.module.scss new file mode 100644 index 000000000..a044feea5 --- /dev/null +++ b/src/components/TransactionParameters/index.module.scss @@ -0,0 +1,118 @@ +@import '../../styles/variables.module'; + +.container { + background: rgb(245 245 245); + margin-top: 8px; + padding: 24px 40px; + border-radius: 4px; + color: rgb(51 51 51); + + .fieldSet { + margin: 16px 0; + + &:first-child { + margin-top: 0; + + /* TODO: This is a highly hardcoded implementation. The entire Transaction Parameters section needs to be refactored into a more maintainable layout structure. */ + & > :nth-child(1) { + margin-top: 0; + } + } + + &:last-child { + margin-bottom: 0; + + & > :nth-last-child(1) { + margin-bottom: 0; + } + } + } + + @media screen and (width <= $mobileBreakPoint) { + padding: 4px; + } +} + +.loading { + display: flex; + justify-content: center; +} + +.section { + &:not(:first-child) { + margin-top: 24px; + } + + .sectionTitle { + display: flex; + align-items: center; + } + + .sectionValue { + margin-top: 16px; + padding: 12px; + max-height: 250px; + font-size: 16px; + overflow-y: auto; + background: #eee; + border-radius: 4px; + + @media (width <= $mobileBreakPoint) { + margin-top: 8px; + padding: 12px 8px; + } + } + + @media (width <= $mobileBreakPoint) { + margin-top: 12px; + } +} + +.field { + display: flex; + align-items: flex-start; + font-size: 14px; + word-break: break-all; + line-height: 1.6em; + + a { + color: var(--primary-color); + } + + .title { + flex-basis: 160px; + display: flex; + flex-shrink: 0; + align-items: center; + white-space: nowrap; + + @media (width <= $mobileBreakPoint) { + width: 130px; + } + } + + .value { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 12px 16px; + } + + @media (width <= $mobileBreakPoint) { + font-size: 12px; + flex-flow: column wrap; + + .title { + flex-basis: auto; + width: min-content; + margin-bottom: 4px; + padding-right: 4px; + } + } + + @media (width <= 1150px) { + .value { + display: block; + } + } +} diff --git a/src/components/TransactionParameters/index.tsx b/src/components/TransactionParameters/index.tsx new file mode 100644 index 000000000..9c9f96fb4 --- /dev/null +++ b/src/components/TransactionParameters/index.tsx @@ -0,0 +1,173 @@ +import { useQuery } from '@tanstack/react-query' +import { FC, ReactNode } from 'react' +import { Trans, useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' +import classNames from 'classnames' +import { getTx } from '../../services/NodeService' +import { matchTxHash } from '../../utils/util' +import Loading from '../AwesomeLoadings/Spinner' +import HashTag from '../HashTag' +import { HelpTip } from '../HelpTip' +import styles from './index.module.scss' + +const Field = ({ + title, + tooltip, + value, + valueTooltip, + linkUrl, + tag, +}: Partial> & { value: ReactNode }) => ( +
+
+ {title ? ( + <> + {title} + {tooltip && } + : + + ) : ( + '' + )} +
+
+
+ {linkUrl ? ( + + {value} + + ) : ( + value + )} + {valueTooltip && } +
+ {tag &&
{tag}
} +
+
+) + +const TransactionParameters: FC<{ hash: string }> = ({ hash }) => { + const [t] = useTranslation() + + const { data, isLoading } = useQuery(['tx', hash], () => getTx(hash)) + if (isLoading) { + return ( +
+ +
+ ) + } + + if (!data?.result?.transaction) return
{`Transaction ${hash} not loaded`}
+ + const { header_deps: headerDeps, cell_deps: cellDeps, witnesses } = data.result.transaction + + const parameters = [ + { + title: t('transaction.cell_deps'), + tooltip: ( + + ), + }} + /> + ), + content: cellDeps.length ? ( + cellDeps.map(cellDep => { + const { + out_point: { tx_hash: txHash, index }, + dep_type: depType, + } = cellDep + const hashTag = matchTxHash(txHash, Number(index)) + return ( +
+ } + /> + + +
+ ) + }) + ) : ( +
+ +
+ ), + }, + { + title: t('transaction.header_deps'), + tooltip: t('glossary.header_deps'), + content: headerDeps.length ? ( + headerDeps.map(headerDep => ( +
+ +
+ )) + ) : ( +
+ +
+ ), + }, + { + title: t('transaction.witnesses'), + tooltip: t('glossary.witnesses'), + content: witnesses.length ? ( + witnesses.map((witness, index) => { + const key = `${witness}-${index}` + return ( +
+ {witness}
} + /> +
+ ) + }) + ) : ( +
+ +
+ ), + }, + ] + + return ( +
+ {parameters.map(item => ( +
+
+ {item.title} + {item.tooltip && } +
+
{item.content}
+
+ ))} +
+ ) +} + +export default TransactionParameters diff --git a/src/models/Transaction/index.ts b/src/models/Transaction/index.ts index 0d5ddf443..35b0af911 100644 --- a/src/models/Transaction/index.ts +++ b/src/models/Transaction/index.ts @@ -20,9 +20,6 @@ export interface Transaction { version: number displayInputs: Cell[] displayOutputs: Cell[] - cellDeps: CellDep[] - headerDeps: string[] - witnesses: string[] liveCellChanges: string capacityInvolved: string txStatus: string diff --git a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx index 83160721d..01e2e47e6 100644 --- a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx +++ b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx @@ -1,28 +1,17 @@ /* eslint-disable react/no-array-index-key */ -import { useState, ReactNode, FC } from 'react' +import { useState, FC } from 'react' import BigNumber from 'bignumber.js' -import { Trans, useTranslation } from 'react-i18next' +import { useTranslation } from 'react-i18next' import { Radio, Tooltip } from 'antd' -import classNames from 'classnames' import { Link } from '../../../components/Link' import Capacity from '../../../components/Capacity' -import HashTag from '../../../components/HashTag' -import { HelpTip } from '../../../components/HelpTip' import SimpleButton from '../../../components/SimpleButton' import ComparedToMaxTooltip from '../../../components/Tooltip/ComparedToMaxTooltip' import { LayoutLiteProfessional } from '../../../constants/common' import { parseSimpleDate } from '../../../utils/date' import { localeNumberString } from '../../../utils/number' -import { shannonToCkb, useFormatConfirmation, matchTxHash } from '../../../utils/util' -import { - TransactionBlockHeightPanel, - TransactionInfoContentItem, - TransactionInfoContentPanel, - TransactionOverviewPanel, - TransactionInfoItemPanel, - TransactionInfoContentContainer, - TransactionInfoContentTitle, -} from './styled' +import { shannonToCkb, useFormatConfirmation } from '../../../utils/util' +import { TransactionBlockHeightPanel, TransactionOverviewPanel } from './styled' import { explorerService, useLatestBlockNumber } from '../../../services/ExplorerService' import { Transaction } from '../../../models/Transaction' import { Card, CardCellInfo, CardCellsLayout, HashCardHeader } from '../../../components/Card' @@ -31,6 +20,7 @@ import { ReactComponent as DownloadIcon } from './download.svg' import { useSetToast } from '../../../components/Toast' import { useIsMobile, useUpdateSearchParams } from '../../../hooks' import styles from './TransactionOverview.module.scss' +import TransactionParameters from '../../../components/TransactionParameters' const showTxStatus = (txStatus: string) => txStatus?.replace(/^\S/, s => s.toUpperCase()) ?? '-' const TransactionBlockHeight = ({ blockNumber, txStatus }: { blockNumber: number; txStatus: string }) => ( @@ -43,65 +33,6 @@ const TransactionBlockHeight = ({ blockNumber, txStatus }: { blockNumber: number ) -const TransactionInfoItem = ({ - title, - tooltip, - value, - valueTooltip, - linkUrl, - tag, -}: { - title?: string - tooltip?: string - value: string | ReactNode - valueTooltip?: string - linkUrl?: string - tag?: ReactNode -}) => ( - - - {title ? ( - <> - {title} - {tooltip && } - : - - ) : ( - '' - )} - - -
- {linkUrl ? ( - - {value} - - ) : ( - value - )} - {valueTooltip && } -
- {tag &&
{tag}
} -
-
-) - -const TransactionInfoItemWrapper = ({ - title, - tooltip, - value, - linkUrl, -}: { - title?: string - tooltip?: string - value: string | ReactNode - linkUrl?: string -}) => ( - - - -) - export const TransactionOverviewCard: FC<{ txHash: string transaction: Transaction @@ -111,9 +42,6 @@ export const TransactionOverviewCard: FC<{ const tipBlockNumber = useLatestBlockNumber() const { blockNumber, - cellDeps, - headerDeps, - witnesses, blockTimestamp, transactionFee, txStatus, @@ -269,94 +197,6 @@ export const TransactionOverviewCard: FC<{ if (isProfessional) { overviewItems.push(liteTxSizeData, liteTxCyclesData) } - const TransactionParams = [ - { - title: t('transaction.cell_deps'), - tooltip: ( - - ), - }} - /> - ), - content: - cellDeps && cellDeps.length > 0 ? ( - cellDeps.map(cellDep => { - const { - outPoint: { txHash, index }, - depType, - } = cellDep - const hashTag = matchTxHash(txHash, index) - return ( - - } - /> - - - - ) - }) - ) : ( - - ), - }, - { - title: t('transaction.header_deps'), - tooltip: t('glossary.header_deps'), - content: - headerDeps && headerDeps.length > 0 ? ( - headerDeps.map(headerDep => ( - - )) - ) : ( - - ), - }, - { - title: t('transaction.witnesses'), - tooltip: t('glossary.witnesses'), - content: - witnesses && witnesses.length > 0 ? ( - witnesses.map((witness, index) => ( - {witness}} - /> - )) - ) : ( - - ), - }, - ] const setToast = useSetToast() @@ -439,19 +279,7 @@ export const TransactionOverviewCard: FC<{
- {detailTab === 'params' ? ( -
- {TransactionParams.map(item => ( - -
- {item.title} - {item.tooltip && } -
-
{item.content}
-
- ))} -
- ) : null} + {detailTab === 'params' ? : null} {detailTab === 'raw' ? : null} )} diff --git a/src/pages/Transaction/TransactionComp/styled.tsx b/src/pages/Transaction/TransactionComp/styled.tsx index a4cc493d8..d3505f0e8 100644 --- a/src/pages/Transaction/TransactionComp/styled.tsx +++ b/src/pages/Transaction/TransactionComp/styled.tsx @@ -74,144 +74,3 @@ export const TransactionBlockHeightPanel = styled.div` color: #000; } ` - -export const TransactionInfoItemPanel = styled.div` - flex: 1; - - .transactionInfoTitle { - display: flex; - align-items: center; - margin-top: 24px; - - @media (max-width: ${variables.mobileBreakPoint}) { - margin-top: 12px; - } - } - - &:first-child { - .transactionInfoTitle { - margin-top: 0; - } - } - - .transactionInfoValue { - margin-top: 16px; - padding: 12px; - max-height: 250px; - font-size: 16px; - overflow-y: auto; - background: #eee; - border-radius: 4px; - - @media (max-width: ${variables.mobileBreakPoint}) { - margin-top: 8px; - padding: 12px 8px; - } - } -` - -export const TransactionInfoContentPanel = styled.div` - margin: 16px 0; - - &:first-child { - margin-top: 0; - - /* TODO: This is a highly hardcoded implementation. The entire Transaction Parameters section needs to be refactored into a more maintainable layout structure. */ - & > :nth-child(1) { - margin-top: 0; - } - } - - &:last-child { - margin-bottom: 0; - - & > :nth-last-child(1) { - margin-bottom: 0; - } - } -` - -export const TransactionInfoContentTitle = styled.div` - flex: 0 0 auto; - width: 160px; - font-size: 14px; - - @media (max-width: ${variables.mobileBreakPoint}) { - width: 130px; - font-size: 12px; - } -` - -export const TransactionInfoContentContainer = styled.div` - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 12px 16px; - font-size: 14px; - - @media (max-width: ${variables.mobileBreakPoint}) { - font-size: 12px; - } -` - -export const TransactionInfoContentItem = styled.div` - display: flex; - margin: 12px 0; - - @media (max-width: ${variables.mobileBreakPoint}) { - margin: 8px 0; - } - - a { - color: ${props => props.theme.primary}; - word-wrap: break-word; - word-break: break-all; - } - - a:hover { - color: ${props => props.theme.primary}; - } - - .transactionInfoContentTitle { - display: flex; - align-items: center; - width: 160px; - color: #333; - font-size: 14px; - - @media (max-width: ${variables.mobileBreakPoint}) { - font-size: 12px; - } - } - - .transactionInfoContentContainer { - color: #333; - font-size: 14px; - width: 100%; - word-wrap: break-word; - word-break: break-all; - display: flex; - justify-content: flex-start; - flex-wrap: wrap; - gap: 0 12px; - - @media (max-width: ${variables.mobileBreakPoint}) { - font-size: 12px; - } - - .transactionInfoContentValue { - display: flex; - align-items: center; - flex-shrink: 0; - } - - .transactionInfoContentTag { - width: 400px; - max-width: 100%; - } - } -` - -export const TransactionCellDepTagPanel = styled.div` - margin-left: 160px; -` diff --git a/src/pages/Transaction/state.ts b/src/pages/Transaction/state.ts index 9e8857770..f5b90cd36 100644 --- a/src/pages/Transaction/state.ts +++ b/src/pages/Transaction/state.ts @@ -12,9 +12,6 @@ export const defaultTransactionInfo: Transaction = { version: 0, displayInputs: [], displayOutputs: [], - cellDeps: [], - headerDeps: [], - witnesses: [], liveCellChanges: '', capacityInvolved: '', txStatus: '', diff --git a/src/services/NodeService/index.ts b/src/services/NodeService/index.ts index 900893bfb..8c6be9597 100644 --- a/src/services/NodeService/index.ts +++ b/src/services/NodeService/index.ts @@ -9,7 +9,7 @@ if (!node) { throw new Error('NodeService not implemented') } -export const getTx = async (hash: string) => { +export const getTx = async (hash: string): Promise<{ result: { transaction: NodeRpc.RawTransaction | null } }> => { const body = { id: 1, jsonrpc: '2.0', @@ -22,3 +22,42 @@ export const getTx = async (hash: string) => { .then(res => res.data) .catch(() => null) } + +namespace NodeRpc { + interface Script { + code_hash: string + args: string + hash_type: 'data' | 'type' | 'data1' | 'data2' + } + + interface CellDep { + out_point: { + tx_hash: string + index: string + } + dep_type: string + } + + interface CellInput { + previous_output: { + tx_hash: string + index: string + } + since: string + } + + interface CellOutput { + capacity: string + lock: Script + type: Script | null + } + export interface RawTransaction { + version: string + cell_deps: CellDep[] + header_deps: string[] + inputs: CellInput[] + outputs: CellOutput[] + witnesses: string[] + outputs_data: string[] + } +} diff --git a/src/utils/transformer.ts b/src/utils/transformer.ts index 4031b97aa..1391ab50b 100644 --- a/src/utils/transformer.ts +++ b/src/utils/transformer.ts @@ -18,9 +18,6 @@ export const transformToTransaction = (tx: CKBTransactionInScript): Transaction income: '', targetBlockNumber: 0, version: 0, - cellDeps: [], - headerDeps: [], - witnesses: [], liveCellChanges: '', capacityInvolved: '', detailedMessage: '',