Skip to content

Commit ca38b40

Browse files
Keith-CYashchan
andauthored
feat(neuron-ui): update the view of transaction detail (#965)
* 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>
1 parent 9d71ee3 commit ca38b40

12 files changed

Lines changed: 314 additions & 122 deletions

File tree

packages/neuron-ui/src/components/Addresses/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const Addresses = ({
8383
maxWidth: 500,
8484
onRender: (item?: State.Address, _index?: number, column?: IColumn) => {
8585
if (item) {
86-
if (column && (column.calculatedWidth || 0) < 400) {
86+
if (column && (column.calculatedWidth || 0) < 420) {
8787
return (
8888
<div
8989
title={item.address}

packages/neuron-ui/src/components/Transaction/index.tsx

Lines changed: 203 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,193 @@
1-
import React, { useEffect, useState, useMemo } from 'react'
1+
import React, { useEffect, useState, useMemo, useCallback } from 'react'
22
import { useTranslation } from 'react-i18next'
3-
import { Stack, DetailsList, Text, CheckboxVisibility, IColumn } from 'office-ui-fabric-react'
3+
import { Stack, DetailsList, Text, CheckboxVisibility, IColumn, Icon } from 'office-ui-fabric-react'
44
import { currentWallet as currentWalletCache } from 'services/localCache'
5-
import { getTransaction, showErrorMessage } from 'services/remote'
5+
import { getTransaction, showErrorMessage, getAllNetworks, getCurrentNetworkID, openExternal } from 'services/remote'
6+
import { ckbCore } from 'services/chain'
67

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

910
import { localNumberFormatter, uniformTimeFormatter, shannonToCKBFormatter } from 'utils/formatters'
1011
import { ErrorCode } from 'utils/const'
12+
import { explorerNavButton } from './style.module.scss'
1113

1214
const MIN_CELL_WIDTH = 70
1315

14-
const inputColumns: IColumn[] = [
15-
{
16-
key: 'lockHash',
17-
name: 'Lock Hash',
18-
minWidth: 100,
19-
maxWidth: 200,
20-
onRender: (item: any) => (
21-
<span title={item.lockHash || 'none'} className="textOverflow">
22-
{item.lockHash || 'none'}
23-
</span>
24-
),
25-
},
26-
{
27-
key: 'outPointCell',
28-
name: 'OutPoint Cell',
29-
minWidth: 150,
30-
onRender: (item: any) => {
31-
const text = item.previousOutput ? `${item.previousOutput.txHash}[${item.previousOutput.index}]` : 'none'
32-
return (
33-
<span title={text} className="textOverflow">
34-
{text}
35-
</span>
36-
)
37-
},
38-
},
39-
{
40-
key: 'capacity',
41-
name: 'Capacity',
42-
minWidth: 200,
43-
maxWidth: 250,
44-
},
45-
].map(
46-
(col): IColumn => ({
47-
ariaLabel: col.name,
48-
fieldName: col.key,
49-
...col,
50-
})
51-
)
52-
const outputColumns: IColumn[] = [
53-
{
54-
key: 'index',
55-
name: 'Index',
56-
minWidth: 80,
57-
maxWidth: 150,
58-
onRender: (item?: any | State.DetailedOutput) => {
59-
if (item) {
60-
return item.outPoint.index
61-
}
62-
return null
63-
},
64-
},
65-
{
66-
key: 'lockHash',
67-
name: 'Lock Hash',
68-
minWidth: 150,
69-
},
70-
{
71-
key: 'capacity',
72-
name: 'Capacity',
73-
minWidth: 200,
74-
maxWidth: 250,
75-
onRender: (output?: State.DetailedOutput) => {
76-
if (output) {
77-
return `${shannonToCKBFormatter(output.capacity)} CKB`
78-
}
79-
return null
80-
},
81-
},
82-
].map(col => ({
83-
ariaLabel: col.name,
84-
fieldName: col.key,
85-
...col,
86-
}))
87-
88-
const basicInfoColumns: IColumn[] = [
89-
{
90-
key: 'label',
91-
name: 'Label',
92-
minWidth: 100,
93-
maxWidth: 150,
94-
},
95-
{
96-
key: 'value',
97-
name: 'value',
98-
minWidth: 450,
99-
},
100-
].map(
101-
(col): IColumn => ({
102-
minWidth: MIN_CELL_WIDTH,
103-
ariaLabel: col.name,
104-
fieldName: col.key,
105-
...col,
106-
})
107-
)
10816
const Transaction = () => {
10917
const [t] = useTranslation()
11018
const [transaction, setTransaction] = useState(transactionState)
19+
const [addressPrefix, setAddressPrefix] = useState(ckbCore.utils.AddressPrefix.Mainnet)
11120
const [error, setError] = useState({ code: '', message: '' })
21+
22+
const inputColumns: IColumn[] = useMemo(
23+
() =>
24+
[
25+
{
26+
key: 'index',
27+
name: t('transaction.index'),
28+
minWidth: 60,
29+
maxWidth: 60,
30+
onRender: (_item?: any, index?: number) => {
31+
if (undefined !== index) {
32+
return index
33+
}
34+
return null
35+
},
36+
},
37+
{
38+
key: 'outPointCell',
39+
name: 'OutPoint Cell',
40+
minWidth: 150,
41+
maxWidth: 600,
42+
onRender: (item: any) => {
43+
const text = item.previousOutput ? `${item.previousOutput.txHash}[${item.previousOutput.index}]` : 'none'
44+
return (
45+
<span title={text} className="textOverflow">
46+
{text}
47+
</span>
48+
)
49+
},
50+
},
51+
{
52+
key: 'capacity',
53+
name: t('transaction.amount'),
54+
minWidth: 100,
55+
maxWidth: 250,
56+
onRender: (input?: State.DetailedOutput) => {
57+
if (input) {
58+
return `${shannonToCKBFormatter(input.capacity)} CKB`
59+
}
60+
return null
61+
},
62+
},
63+
].map(
64+
(col): IColumn => ({
65+
ariaLabel: col.name,
66+
fieldName: col.key,
67+
...col,
68+
})
69+
),
70+
[t]
71+
)
72+
73+
const outputColumns: IColumn[] = useMemo(
74+
() =>
75+
[
76+
{
77+
key: 'index',
78+
name: t('transaction.index'),
79+
minWidth: 60,
80+
maxWidth: 60,
81+
onRender: (item?: any | State.DetailedOutput) => {
82+
if (item) {
83+
return item.outPoint.index
84+
}
85+
return null
86+
},
87+
},
88+
{
89+
key: 'address',
90+
name: t('transaction.address'),
91+
minWidth: 200,
92+
maxWidth: 500,
93+
onRender: (output?: State.DetailedOutput, _index?: number, column?: IColumn) => {
94+
if (!output) {
95+
return null
96+
}
97+
try {
98+
const address = ckbCore.utils.bech32Address(output.lock.args[0], {
99+
prefix: addressPrefix,
100+
type: ckbCore.utils.AddressType.HashIdx,
101+
codeHashIndex: '0x00',
102+
})
103+
if (column && (column.calculatedWidth || 0) < 450) {
104+
return (
105+
<div
106+
title={address}
107+
style={{
108+
overflow: 'hidden',
109+
display: 'flex',
110+
}}
111+
className="monospacedFont"
112+
>
113+
<span className="textOverflow">{address.slice(0, -6)}</span>
114+
<span>{address.slice(-6)}</span>
115+
</div>
116+
)
117+
}
118+
return (
119+
<span title={address} className="monospacedFont">
120+
{address}
121+
</span>
122+
)
123+
} catch {
124+
return null
125+
}
126+
},
127+
},
128+
{
129+
key: 'capacity',
130+
name: t('transaction.amount'),
131+
minWidth: 100,
132+
maxWidth: 250,
133+
onRender: (output?: State.DetailedOutput) => {
134+
if (output) {
135+
return `${shannonToCKBFormatter(output.capacity)} CKB`
136+
}
137+
return null
138+
},
139+
},
140+
].map(col => ({
141+
ariaLabel: col.name,
142+
fieldName: col.key,
143+
...col,
144+
})),
145+
[addressPrefix, t]
146+
)
147+
148+
const basicInfoColumns: IColumn[] = useMemo(
149+
() =>
150+
[
151+
{
152+
key: 'label',
153+
name: 'label',
154+
minWidth: 100,
155+
maxWidth: 120,
156+
},
157+
{
158+
key: 'value',
159+
name: 'value',
160+
minWidth: 150,
161+
},
162+
].map(
163+
(col): IColumn => ({
164+
minWidth: MIN_CELL_WIDTH,
165+
ariaLabel: col.name,
166+
fieldName: col.key,
167+
...col,
168+
})
169+
),
170+
[]
171+
)
172+
112173
useEffect(() => {
174+
Promise.all([getAllNetworks(), getCurrentNetworkID()])
175+
.then(([networksRes, idRes]) => {
176+
if (networksRes.status === 1 && idRes.status === 1) {
177+
const network = networksRes.result.find((n: any) => n.id === idRes.result)
178+
if (!network) {
179+
throw new Error('Cannot find current network in the network list')
180+
}
181+
182+
setAddressPrefix(
183+
network.chain === process.env.REACT_APP_MAINNET_TAG
184+
? ckbCore.utils.AddressPrefix.Mainnet
185+
: ckbCore.utils.AddressPrefix.Testnet
186+
)
187+
}
188+
})
189+
.catch(err => console.warn(err))
190+
113191
const currentWallet = currentWalletCache.load()
114192
if (currentWallet) {
115193
const hash = window.location.href.split('/').pop()
@@ -142,21 +220,26 @@ const Transaction = () => {
142220
})
143221
}, [])
144222

223+
// TODO: add conditional branch on mainnet and testnet
224+
const onExplorerBtnClick = useCallback(() => {
225+
openExternal(`https://explorer.nervos.org/transaction/${transaction.hash}`)
226+
}, [transaction.hash])
227+
145228
const basicInfoItems = useMemo(
146229
() => [
147-
{ label: t('history.transaction-hash'), value: transaction.hash || 'none' },
230+
{ label: t('transaction.transaction-hash'), value: transaction.hash || 'none' },
148231
{
149-
label: t('history.date'),
232+
label: t('transaction.block-number'),
233+
value: localNumberFormatter(transaction.blockNumber) || 'none',
234+
},
235+
{
236+
label: t('transaction.date'),
150237
value: +(transaction.timestamp || transaction.createdAt)
151238
? uniformTimeFormatter(+(transaction.timestamp || transaction.createdAt))
152239
: 'none',
153240
},
154241
{
155-
label: t('history.blockNumber'),
156-
value: localNumberFormatter(transaction.blockNumber) || 'none',
157-
},
158-
{
159-
label: t('history.amount'),
242+
label: t('transaction.income'),
160243
value: `${shannonToCKBFormatter(transaction.value)} CKB`,
161244
},
162245
],
@@ -185,10 +268,12 @@ const Transaction = () => {
185268
isHeaderVisible={false}
186269
/>
187270
</Stack>
188-
<Stack tokens={{ childrenGap: 15 }}>
271+
<Stack tokens={{ childrenGap: 15 }} verticalFill>
189272
<Stack.Item>
190273
<Text variant="xLarge" as="h1">
191-
Inputs
274+
{`${t('transaction.inputs')} (${transaction.inputs.length}/${localNumberFormatter(
275+
transaction.inputsCount
276+
)})`}
192277
</Text>
193278
<DetailsList
194279
items={transaction.inputs}
@@ -200,7 +285,9 @@ const Transaction = () => {
200285
</Stack.Item>
201286
<Stack.Item>
202287
<Text variant="xLarge" as="h1">
203-
Outputs
288+
{`${t('transaction.outputs')} (${transaction.outputs.length}/${localNumberFormatter(
289+
transaction.outputsCount
290+
)})`}
204291
</Text>
205292
<DetailsList
206293
items={transaction.outputs}
@@ -211,6 +298,15 @@ const Transaction = () => {
211298
/>
212299
</Stack.Item>
213300
</Stack>
301+
<button
302+
type="button"
303+
className={explorerNavButton}
304+
title={t('transaction.view-in-explorer-button-title')}
305+
onClick={onExplorerBtnClick}
306+
>
307+
<Icon iconName="Explorer" />
308+
<span>{t('transaction.view-in-explorer')}</span>
309+
</button>
214310
</Stack>
215311
)
216312
}

0 commit comments

Comments
 (0)