Skip to content

Commit

Permalink
feat(neuron-ui): update the view of transaction detail (#965)
Browse files Browse the repository at this point in the history
* feat(neuron-ui): update the view of transaction detail

* Update packages/neuron-ui/src/locales/en.json

Co-Authored-By: James Chen <james@ashchan.com>
  • Loading branch information
Keith-CY and ashchan committed Sep 30, 2019
1 parent 9d71ee3 commit ca38b40
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 122 deletions.
2 changes: 1 addition & 1 deletion packages/neuron-ui/src/components/Addresses/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const Addresses = ({
maxWidth: 500,
onRender: (item?: State.Address, _index?: number, column?: IColumn) => {
if (item) {
if (column && (column.calculatedWidth || 0) < 400) {
if (column && (column.calculatedWidth || 0) < 420) {
return (
<div
title={item.address}
Expand Down
310 changes: 203 additions & 107 deletions packages/neuron-ui/src/components/Transaction/index.tsx
Original file line number Diff line number Diff line change
@@ -1,115 +1,193 @@
import React, { useEffect, useState, useMemo } from 'react'
import React, { useEffect, useState, useMemo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Stack, DetailsList, Text, CheckboxVisibility, IColumn } from 'office-ui-fabric-react'
import { Stack, DetailsList, Text, CheckboxVisibility, IColumn, Icon } from 'office-ui-fabric-react'
import { currentWallet as currentWalletCache } from 'services/localCache'
import { getTransaction, showErrorMessage } from 'services/remote'
import { getTransaction, showErrorMessage, getAllNetworks, getCurrentNetworkID, openExternal } from 'services/remote'
import { ckbCore } from 'services/chain'

import { transactionState } from 'states/initStates/chain'

import { localNumberFormatter, uniformTimeFormatter, shannonToCKBFormatter } from 'utils/formatters'
import { ErrorCode } from 'utils/const'
import { explorerNavButton } from './style.module.scss'

const MIN_CELL_WIDTH = 70

const inputColumns: IColumn[] = [
{
key: 'lockHash',
name: 'Lock Hash',
minWidth: 100,
maxWidth: 200,
onRender: (item: any) => (
<span title={item.lockHash || 'none'} className="textOverflow">
{item.lockHash || 'none'}
</span>
),
},
{
key: 'outPointCell',
name: 'OutPoint Cell',
minWidth: 150,
onRender: (item: any) => {
const text = item.previousOutput ? `${item.previousOutput.txHash}[${item.previousOutput.index}]` : 'none'
return (
<span title={text} className="textOverflow">
{text}
</span>
)
},
},
{
key: 'capacity',
name: 'Capacity',
minWidth: 200,
maxWidth: 250,
},
].map(
(col): IColumn => ({
ariaLabel: col.name,
fieldName: col.key,
...col,
})
)
const outputColumns: IColumn[] = [
{
key: 'index',
name: 'Index',
minWidth: 80,
maxWidth: 150,
onRender: (item?: any | State.DetailedOutput) => {
if (item) {
return item.outPoint.index
}
return null
},
},
{
key: 'lockHash',
name: 'Lock Hash',
minWidth: 150,
},
{
key: 'capacity',
name: 'Capacity',
minWidth: 200,
maxWidth: 250,
onRender: (output?: State.DetailedOutput) => {
if (output) {
return `${shannonToCKBFormatter(output.capacity)} CKB`
}
return null
},
},
].map(col => ({
ariaLabel: col.name,
fieldName: col.key,
...col,
}))

const basicInfoColumns: IColumn[] = [
{
key: 'label',
name: 'Label',
minWidth: 100,
maxWidth: 150,
},
{
key: 'value',
name: 'value',
minWidth: 450,
},
].map(
(col): IColumn => ({
minWidth: MIN_CELL_WIDTH,
ariaLabel: col.name,
fieldName: col.key,
...col,
})
)
const Transaction = () => {
const [t] = useTranslation()
const [transaction, setTransaction] = useState(transactionState)
const [addressPrefix, setAddressPrefix] = useState(ckbCore.utils.AddressPrefix.Mainnet)
const [error, setError] = useState({ code: '', message: '' })

const inputColumns: IColumn[] = useMemo(
() =>
[
{
key: 'index',
name: t('transaction.index'),
minWidth: 60,
maxWidth: 60,
onRender: (_item?: any, index?: number) => {
if (undefined !== index) {
return index
}
return null
},
},
{
key: 'outPointCell',
name: 'OutPoint Cell',
minWidth: 150,
maxWidth: 600,
onRender: (item: any) => {
const text = item.previousOutput ? `${item.previousOutput.txHash}[${item.previousOutput.index}]` : 'none'
return (
<span title={text} className="textOverflow">
{text}
</span>
)
},
},
{
key: 'capacity',
name: t('transaction.amount'),
minWidth: 100,
maxWidth: 250,
onRender: (input?: State.DetailedOutput) => {
if (input) {
return `${shannonToCKBFormatter(input.capacity)} CKB`
}
return null
},
},
].map(
(col): IColumn => ({
ariaLabel: col.name,
fieldName: col.key,
...col,
})
),
[t]
)

const outputColumns: IColumn[] = useMemo(
() =>
[
{
key: 'index',
name: t('transaction.index'),
minWidth: 60,
maxWidth: 60,
onRender: (item?: any | State.DetailedOutput) => {
if (item) {
return item.outPoint.index
}
return null
},
},
{
key: 'address',
name: t('transaction.address'),
minWidth: 200,
maxWidth: 500,
onRender: (output?: State.DetailedOutput, _index?: number, column?: IColumn) => {
if (!output) {
return null
}
try {
const address = ckbCore.utils.bech32Address(output.lock.args[0], {
prefix: addressPrefix,
type: ckbCore.utils.AddressType.HashIdx,
codeHashIndex: '0x00',
})
if (column && (column.calculatedWidth || 0) < 450) {
return (
<div
title={address}
style={{
overflow: 'hidden',
display: 'flex',
}}
className="monospacedFont"
>
<span className="textOverflow">{address.slice(0, -6)}</span>
<span>{address.slice(-6)}</span>
</div>
)
}
return (
<span title={address} className="monospacedFont">
{address}
</span>
)
} catch {
return null
}
},
},
{
key: 'capacity',
name: t('transaction.amount'),
minWidth: 100,
maxWidth: 250,
onRender: (output?: State.DetailedOutput) => {
if (output) {
return `${shannonToCKBFormatter(output.capacity)} CKB`
}
return null
},
},
].map(col => ({
ariaLabel: col.name,
fieldName: col.key,
...col,
})),
[addressPrefix, t]
)

const basicInfoColumns: IColumn[] = useMemo(
() =>
[
{
key: 'label',
name: 'label',
minWidth: 100,
maxWidth: 120,
},
{
key: 'value',
name: 'value',
minWidth: 150,
},
].map(
(col): IColumn => ({
minWidth: MIN_CELL_WIDTH,
ariaLabel: col.name,
fieldName: col.key,
...col,
})
),
[]
)

useEffect(() => {
Promise.all([getAllNetworks(), getCurrentNetworkID()])
.then(([networksRes, idRes]) => {
if (networksRes.status === 1 && idRes.status === 1) {
const network = networksRes.result.find((n: any) => n.id === idRes.result)
if (!network) {
throw new Error('Cannot find current network in the network list')
}

setAddressPrefix(
network.chain === process.env.REACT_APP_MAINNET_TAG
? ckbCore.utils.AddressPrefix.Mainnet
: ckbCore.utils.AddressPrefix.Testnet
)
}
})
.catch(err => console.warn(err))

const currentWallet = currentWalletCache.load()
if (currentWallet) {
const hash = window.location.href.split('/').pop()
Expand Down Expand Up @@ -142,21 +220,26 @@ const Transaction = () => {
})
}, [])

// TODO: add conditional branch on mainnet and testnet
const onExplorerBtnClick = useCallback(() => {
openExternal(`https://explorer.nervos.org/transaction/${transaction.hash}`)
}, [transaction.hash])

const basicInfoItems = useMemo(
() => [
{ label: t('history.transaction-hash'), value: transaction.hash || 'none' },
{ label: t('transaction.transaction-hash'), value: transaction.hash || 'none' },
{
label: t('history.date'),
label: t('transaction.block-number'),
value: localNumberFormatter(transaction.blockNumber) || 'none',
},
{
label: t('transaction.date'),
value: +(transaction.timestamp || transaction.createdAt)
? uniformTimeFormatter(+(transaction.timestamp || transaction.createdAt))
: 'none',
},
{
label: t('history.blockNumber'),
value: localNumberFormatter(transaction.blockNumber) || 'none',
},
{
label: t('history.amount'),
label: t('transaction.income'),
value: `${shannonToCKBFormatter(transaction.value)} CKB`,
},
],
Expand Down Expand Up @@ -185,10 +268,12 @@ const Transaction = () => {
isHeaderVisible={false}
/>
</Stack>
<Stack tokens={{ childrenGap: 15 }}>
<Stack tokens={{ childrenGap: 15 }} verticalFill>
<Stack.Item>
<Text variant="xLarge" as="h1">
Inputs
{`${t('transaction.inputs')} (${transaction.inputs.length}/${localNumberFormatter(
transaction.inputsCount
)})`}
</Text>
<DetailsList
items={transaction.inputs}
Expand All @@ -200,7 +285,9 @@ const Transaction = () => {
</Stack.Item>
<Stack.Item>
<Text variant="xLarge" as="h1">
Outputs
{`${t('transaction.outputs')} (${transaction.outputs.length}/${localNumberFormatter(
transaction.outputsCount
)})`}
</Text>
<DetailsList
items={transaction.outputs}
Expand All @@ -211,6 +298,15 @@ const Transaction = () => {
/>
</Stack.Item>
</Stack>
<button
type="button"
className={explorerNavButton}
title={t('transaction.view-in-explorer-button-title')}
onClick={onExplorerBtnClick}
>
<Icon iconName="Explorer" />
<span>{t('transaction.view-in-explorer')}</span>
</button>
</Stack>
)
}
Expand Down
Loading

0 comments on commit ca38b40

Please sign in to comment.