Skip to content

Commit

Permalink
Merge branch 'develop' into feature/rgb-list-others
Browse files Browse the repository at this point in the history
  • Loading branch information
Keith-CY authored Jul 23, 2024
2 parents 9c9354e + dc9f2c9 commit 7302614
Show file tree
Hide file tree
Showing 47 changed files with 2,148 additions and 200 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@sentry/tracing": "7.114.0",
"@spore-sdk/core": "0.2.0-beta.8",
"@tanstack/react-query": "4.0.5",
"@types/cytoscape": "^3.21.2",
"antd": "4.24.5",
"axios": "1.7.2",
"bech32": "2.0.0",
Expand All @@ -32,6 +33,7 @@
"camelcase": "7.0.1",
"camelcase-keys": "7.0.2",
"classnames": "2.5.1",
"cytoscape": "^3.29.2",
"dayjs": "1.11.11",
"default-passive-events": "2.0.0",
"echarts": "4.9.0",
Expand Down
10 changes: 10 additions & 0 deletions src/assets/more.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/open-source.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
132 changes: 85 additions & 47 deletions src/components/Cell/CellInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
import { useState, ReactNode, useRef } from 'react'
import { useState, ReactNode, useRef, useCallback } from 'react'
import BigNumber from 'bignumber.js'
import { useTranslation } from 'react-i18next'
import { useQuery } from '@tanstack/react-query'
import { scriptToHash } from '@nervosnetwork/ckb-sdk-utils'
import classNames from 'classnames'
import { Tooltip } from 'antd'
import type { ContractHashTag } from '../../../constants/scripts'
import { explorerService } from '../../../services/ExplorerService'
import { hexToUtf8 } from '../../../utils/string'
import { TransactionCellDetailTab, TransactionCellDetailPane, TransactionCellDetailTitle } from './styled'
import SmallLoading from '../../Loading/SmallLoading'
import CloseIcon from './modal_close.png'
import config from '../../../config'
import { getBtcTimeLockInfo, getBtcUtxo, getContractHashTag } from '../../../utils/util'
import { localeNumberString } from '../../../utils/number'
import HashTag from '../../HashTag'
Expand All @@ -25,24 +25,25 @@ import { ReactComponent as OuterLinkIcon } from './outer_link_icon.svg'
import { ReactComponent as ScriptHashIcon } from './script_hash_icon.svg'
import { HelpTip } from '../../HelpTip'
import { useSetToast } from '../../Toast'
import { isTypeIdScript, TYPE_ID_TAG } from '../../../utils/typeid'
import { CellBasicInfo } from '../../../utils/transformer'
import { isAxiosError } from '../../../utils/error'
import { Script } from '../../../models/Script'
import { ReactComponent as CompassIcon } from './compass.svg'
import styles from './styles.module.scss'
import EllipsisMiddle from '../../EllipsisMiddle'
import { useIsMobile } from '../../../hooks'
import { Link } from '../../Link'
import { BTCExplorerLink } from '../../Link'
import { CellInfoProps } from './types'
import { getBtcChainIdentify } from '../../../services/BTCIdentifier'
import { IS_MAINNET } from '../../../constants/common'
import UTXOGraph from '../../UTXOGraph'

enum CellInfo {
LOCK = 'lock',
TYPE = 'type',
DATA = 'data',
CAPACITY = 'capacity',
RGBPP = 'rgbpp',
UTXO = 'uxto',
}

type CapacityUsage = Record<'declared' | 'occupied', string | null>
Expand All @@ -55,7 +56,11 @@ interface RGBPP {
btcTx: string
}

type CellInfoValue = Script | CellData | CapacityUsage | RGBPP | null | undefined
type UtxoGraphInfo = CellBasicInfo & {
lock: Script | null
}

type CellInfoValue = Script | CellData | CapacityUsage | RGBPP | null | undefined | UtxoGraphInfo

function isScript(content: CellInfoValue): content is Script {
return content != null && 'codeHash' in content
Expand All @@ -69,6 +74,10 @@ function isCellData(content: CellInfoValue): content is CellData {
return content != null && 'data' in content
}

function isUTXOData(content: CellInfoValue): content is UtxoGraphInfo {
return content != null && 'generatedTxHash' in content
}

function isRGBPP(content: CellInfoValue): content is RGBPP {
return content != null && 'btcTx' in content
}
Expand Down Expand Up @@ -147,6 +156,12 @@ const fetchCellInfo = async (cell: CellBasicInfo, state: CellInfo): Promise<Cell
}
}

case CellInfo.UTXO:
return {
lock: await fetchLock(),
...cell,
}

case CellInfo.RGBPP: {
return {
btcTx: cell.rgbInfo?.txid ?? '',
Expand All @@ -165,22 +180,20 @@ const JSONKeyValueView = ({ title = '', value = '' }: { title?: string; value?:
</div>
)

///
const ScriptRender = ({ content: script }: { content: Script }) => {
const { t } = useTranslation()
const hashTag = getContractHashTag(script)
let hashTag: Pick<ContractHashTag, 'tag' | 'category'> | undefined
if (isTypeIdScript(script)) {
hashTag = { tag: TYPE_ID_TAG }
} else {
hashTag = getContractHashTag(script)
}
const btcUtxo = getBtcUtxo(script)
const btcTimeLockInfo = !btcUtxo ? getBtcTimeLockInfo(script) : null

const txid = btcUtxo?.txid ?? btcTimeLockInfo?.txid

const { data: identity } = useQuery({
queryKey: ['btc-testnet-identity', txid],
queryFn: () => (txid ? getBtcChainIdentify(txid) : null),
enabled: !IS_MAINNET && !!txid,
})

if (!IS_MAINNET && txid && !identity) return null

return (
<>
<JSONKeyValueView title={`"${t('transaction.script_code_hash')}": `} value={script.codeHash} />
Expand All @@ -198,34 +211,25 @@ const ScriptRender = ({ content: script }: { content: Script }) => {
{btcUtxo ? (
<JSONKeyValueView
value={
<a
href={`${config.BITCOIN_EXPLORER}${IS_MAINNET ? '' : `/${identity}`}/tx/${btcUtxo.txid}#vout=${parseInt(
btcUtxo.index!,
16,
)}`}
target="_blank"
rel="noopener noreferrer"
<BTCExplorerLink
className={styles.btcUtxo}
btcTxId={txid ?? ''}
path={`/tx/${btcUtxo.txid}#vout=${parseInt(btcUtxo.index!, 16)}`}
>
BTC UTXO
<CompassIcon />
</a>
</BTCExplorerLink>
}
/>
) : null}

{btcTimeLockInfo ? (
<JSONKeyValueView
value={
<a
href={`${config.BITCOIN_EXPLORER}${IS_MAINNET ? '' : `/${identity}`}/tx/${btcTimeLockInfo.txid}`}
target="_blank"
rel="noopener noreferrer"
className={styles.btcUtxo}
>
<BTCExplorerLink className={styles.btcUtxo} btcTxId={txid ?? ''} path={`/tx/${btcTimeLockInfo.txid}`}>
{`${btcTimeLockInfo.after} confirmations after BTC Tx`}
<CompassIcon />
</a>
</BTCExplorerLink>
}
/>
) : null}
Expand Down Expand Up @@ -267,9 +271,9 @@ const CellInfoValueRender = ({ content }: { content: CellInfoValue }) => {
value={
<>
<EllipsisMiddle useTextWidthForPlaceholderWidth>{content.btcTx}</EllipsisMiddle>
<Link to={`${config.BITCOIN_EXPLORER}/tx/${content.btcTx}`}>
<BTCExplorerLink btcTxId={content.btcTx} path="/tx">
<ViewIcon />
</Link>
</BTCExplorerLink>
</>
}
/>
Expand All @@ -279,13 +283,28 @@ const CellInfoValueRender = ({ content }: { content: CellInfoValue }) => {
return <JSONKeyValueView title="null" />
}

const CellInfoValueView = ({ content, state }: { content: CellInfoValue; state: CellInfo }) => {
const CellInfoValueView = ({
content,
state,
modalRef,
onViewCell,
}: {
content: CellInfoValue
state: CellInfo
modalRef?: HTMLDivElement | null
onViewCell: (cell: CellBasicInfo) => void
}) => {
switch (state) {
case CellInfo.LOCK:
case CellInfo.TYPE:
case CellInfo.DATA:
case CellInfo.CAPACITY:
return <CellInfoValueJSONView content={content} state={state} />
case CellInfo.UTXO:
if (isUTXOData(content)) {
return <UTXOGraph {...content} modalRef={modalRef} onViewCell={onViewCell} />
}
return null
case CellInfo.RGBPP:
return <CellInfoNormalValueView content={content} state={state} />
default:
Expand All @@ -307,7 +326,7 @@ const CellInfoValueJSONView = ({ content, state }: { content: CellInfoValue; sta
</div>
)

export default ({ cell, onClose }: CellInfoProps) => {
export default ({ cell: entryCell, onClose }: CellInfoProps) => {
const setToast = useSetToast()
const { t } = useTranslation()
const [selectedInfo, setSelectedInfo] = useState<CellInfo>(CellInfo.LOCK)
Expand All @@ -318,6 +337,13 @@ export default ({ cell, onClose }: CellInfoProps) => {
setSelectedInfo(selectedInfo !== newState ? newState : selectedInfo)
}

const [viewCell, setViewCell] = useState<CellBasicInfo | undefined>()
const onViewCell = useCallback((newViewCell: CellBasicInfo) => {
setViewCell(newViewCell)
setSelectedInfo(CellInfo.LOCK)
}, [])
const cell = viewCell ?? entryCell

const { data: content, isFetched } = useQuery(
['cell-info', cell, selectedInfo],
() =>
Expand All @@ -342,6 +368,9 @@ export default ({ cell, onClose }: CellInfoProps) => {
},
)

const isContentAScript = isScript(content)
const isContentATypeIdScript = isContentAScript && isTypeIdScript(content)

const onCopy = (e: React.SyntheticEvent<HTMLButtonElement>) => {
const { role } = e.currentTarget.dataset

Expand Down Expand Up @@ -468,6 +497,7 @@ export default ({ cell, onClose }: CellInfoProps) => {
changeType(state)
}
}}
activeKey={selectedInfo}
>
<TransactionCellDetailPane
tab={
Expand Down Expand Up @@ -502,52 +532,60 @@ export default ({ cell, onClose }: CellInfoProps) => {
/>
{cell.rgbInfo && (
<TransactionCellDetailPane
tab={
<>
<TransactionCellDetailTitle>{t('transaction.rgbpp')}</TransactionCellDetailTitle>
<HelpTip title={t('glossary.capacity_usage')} placement="bottom" containerRef={ref} />
</>
}
tab={<TransactionCellDetailTitle>{t('transaction.rgbpp')}</TransactionCellDetailTitle>}
key={CellInfo.RGBPP}
/>
)}
<TransactionCellDetailPane
tab={<TransactionCellDetailTitle>{t('transaction.utxo_graph')}</TransactionCellDetailTitle>}
key={CellInfo.UTXO}
/>
</TransactionCellDetailTab>
</div>

<div className={styles.transactionDetailPanel}>
{isFetched ? (
<div className={styles.transactionDetailContent}>
<CellInfoValueView content={content} state={selectedInfo} />
<div
className={classNames(
styles.transactionDetailContent,
isUTXOData(content) ? styles.utxoContent : undefined,
)}
>
<CellInfoValueView content={content} state={selectedInfo} modalRef={ref.current} onViewCell={onViewCell} />
</div>
) : (
<div className={styles.transactionDetailLoading}>{!isFetched ? <SmallLoading /> : null}</div>
)}

{!isFetched || !content ? null : (
<div className={styles.scriptActions}>
{!isRGBPP(content) && (
{!isRGBPP(content) && !isUTXOData(content) && (
<button data-role="copy-script" className={styles.button} type="button" onClick={onCopy}>
<div>{t('common.copy')}</div>
<CopyIcon />
</button>
)}

{isScript(content) ? (
{isContentAScript ? (
<button data-role="copy-script-hash" className={styles.button} type="button" onClick={onCopy}>
<div>Script Hash</div>
<ScriptHashIcon />
</button>
) : null}

{isScript(content) ? (
{isContentAScript ? (
<a
data-role="script-info"
className={styles.button}
href={`/script/${content.codeHash}/${content.hashType}`}
href={
isContentATypeIdScript
? `/script/${scriptToHash(content as CKBComponents.Script)}/type`
: `/script/${content.codeHash}/${content.hashType}`
}
target="_blank"
rel="noopener noreferrer"
>
<div>{`${t('scripts.script')} Info`}</div>
<div>{isContentATypeIdScript ? t('scripts.deployed_script') : `${t('scripts.script')} Info`}</div>
<OuterLinkIcon />
</a>
) : null}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Cell/CellInfo/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;

&.utxoContent {
max-height: 440px;
}

@media (max-width: $mobileBreakPoint) {
font-size: 10px;
padding: 10px;
Expand Down
Loading

0 comments on commit 7302614

Please sign in to comment.