Skip to content

Commit 224589f

Browse files
authored
Merge pull request #1962 from oasisprotocol/mz/nodeAddr
Show address of Node ID
2 parents a457982 + 0418d4d commit 224589f

File tree

12 files changed

+160
-57
lines changed

12 files changed

+160
-57
lines changed

.changelog/1962.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Show address of Node ID
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { FC } from 'react'
2+
import Typography from '@mui/material/Typography'
3+
import { NodeDisplayType } from '../../../types/node-display-type'
4+
import { SearchScope } from '../../../types/searchScope'
5+
import { useScreenSize } from '../../hooks/useScreensize'
6+
import { useLocalSettings } from '../../hooks/useLocalSettings'
7+
import { getOasisAddressFromBase64PublicKey } from '../../utils/helpers'
8+
import { trimLongString } from '../../utils/trimLongString'
9+
import { AccountLink } from '../../components/Account/AccountLink'
10+
11+
type TableCellNodeProps = {
12+
id: string
13+
scope: SearchScope
14+
}
15+
16+
export const TableCellNode: FC<TableCellNodeProps> = ({ id, scope }) => {
17+
const { isTablet } = useScreenSize()
18+
19+
const {
20+
settings: { nodeHeaderType },
21+
} = useLocalSettings()
22+
23+
if (nodeHeaderType === NodeDisplayType.Address) {
24+
return <AccountLink alwaysTrimOnTablet scope={scope} address={getOasisAddressFromBase64PublicKey(id)} />
25+
}
26+
27+
return <Typography variant="mono">{isTablet ? trimLongString(id) : id}</Typography>
28+
}
Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
11
import { FC } from 'react'
22
import { useTranslation } from 'react-i18next'
3-
import Button from '@mui/material/Button'
4-
import Tooltip from '@mui/material/Tooltip'
5-
import Typography from '@mui/material/Typography'
6-
import { styled } from '@mui/material/styles'
73
import { TableAgeType } from '../../../types/table-age-type'
4+
import { exhaustedTypeWarning } from '../../../types/errors'
85
import { useLocalSettings } from '../../hooks/useLocalSettings'
9-
import { tooltipDelay } from '../../../styles/theme'
106
import { getTimeZone } from '../../hooks/useFormattedTimestamp'
11-
12-
const StyledButton = styled(Button)(() => ({
13-
paddingLeft: '0px',
14-
paddingRight: '0px',
15-
minWidth: 'auto',
16-
}))
7+
import { TableHeaderToggle } from '../TableHeaderToggle'
178

189
type TableHeaderAgeProps = {
1910
label?: string
@@ -31,43 +22,23 @@ export const TableHeaderAge: FC<TableHeaderAgeProps> = ({ label }) => {
3122
const timeZone = getTimeZone()
3223

3324
return (
34-
<Tooltip
35-
title={t('table.headers.dateTime.tooltipTitle')}
36-
enterDelay={tooltipDelay}
37-
leaveDelay={0}
38-
placement={'top'}
39-
>
40-
<StyledButton variant="text" onClick={() => changeSetting('ageHeaderType', TableAgeType.Distance)}>
41-
<Typography
42-
sx={{
43-
fontWeight: 700,
44-
}}
45-
>
46-
{t('table.headers.dateTime.title')} {timeZone ? `(${timeZone})` : null}
47-
</Typography>
48-
</StyledButton>
49-
</Tooltip>
25+
<TableHeaderToggle
26+
label={`${t('table.headers.dateTime.title')} ${timeZone ? `(${timeZone})` : ''}`}
27+
onClick={() => changeSetting('ageHeaderType', TableAgeType.Distance)}
28+
tooltipTitle={t('table.headers.dateTime.tooltipTitle')}
29+
/>
5030
)
5131
}
5232
case TableAgeType.Distance:
53-
default:
5433
return (
55-
<Tooltip
56-
title={t('table.headers.age.tooltipTitle')}
57-
enterDelay={tooltipDelay}
58-
leaveDelay={0}
59-
placement={'top'}
60-
>
61-
<StyledButton variant="text" onClick={() => changeSetting('ageHeaderType', TableAgeType.DateTime)}>
62-
<Typography
63-
sx={{
64-
fontWeight: 700,
65-
}}
66-
>
67-
{label || t('common.age')}
68-
</Typography>
69-
</StyledButton>
70-
</Tooltip>
34+
<TableHeaderToggle
35+
label={label || t('common.age')}
36+
onClick={() => changeSetting('ageHeaderType', TableAgeType.DateTime)}
37+
tooltipTitle={t('table.headers.age.tooltipTitle')}
38+
/>
7139
)
40+
default:
41+
exhaustedTypeWarning('Unknown age header type', ageHeaderType)
42+
return null
7243
}
7344
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import { NodeDisplayType } from '../../../types/node-display-type'
4+
import { exhaustedTypeWarning } from '../../../types/errors'
5+
import { useLocalSettings } from '../../hooks/useLocalSettings'
6+
import { TableHeaderToggle } from '../TableHeaderToggle'
7+
8+
export const TableHeaderNode: FC = () => {
9+
const { t } = useTranslation()
10+
const {
11+
settings: { nodeHeaderType },
12+
changeSetting,
13+
} = useLocalSettings()
14+
15+
switch (nodeHeaderType) {
16+
case NodeDisplayType.Address: {
17+
return (
18+
<TableHeaderToggle
19+
label={t('common.nodeAddress')}
20+
onClick={() => changeSetting('nodeHeaderType', NodeDisplayType.Id)}
21+
tooltipTitle={t('rofl.nodeIdSwitch')}
22+
/>
23+
)
24+
}
25+
case NodeDisplayType.Id:
26+
return (
27+
<TableHeaderToggle
28+
label={t('common.nodeId')}
29+
onClick={() => changeSetting('nodeHeaderType', NodeDisplayType.Address)}
30+
tooltipTitle={t('rofl.nodeAddressSwitch')}
31+
/>
32+
)
33+
default:
34+
exhaustedTypeWarning('Unknown node display type', nodeHeaderType)
35+
return null
36+
}
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { FC } from 'react'
2+
import Button from '@mui/material/Button'
3+
import Tooltip from '@mui/material/Tooltip'
4+
import Typography from '@mui/material/Typography'
5+
import { styled } from '@mui/material/styles'
6+
import { tooltipDelay } from '../../../styles/theme'
7+
8+
const StyledButton = styled(Button)(() => ({
9+
paddingLeft: '0px',
10+
paddingRight: '0px',
11+
minWidth: 'auto',
12+
}))
13+
14+
type TableHeaderToggleProps = {
15+
label: string
16+
onClick: () => void
17+
tooltipTitle: string
18+
}
19+
20+
export const TableHeaderToggle: FC<TableHeaderToggleProps> = ({ label, onClick, tooltipTitle }) => {
21+
return (
22+
<Tooltip title={tooltipTitle} enterDelay={tooltipDelay} leaveDelay={0} placement="top">
23+
<StyledButton variant="text" onClick={onClick}>
24+
<Typography
25+
sx={{
26+
fontWeight: 700,
27+
}}
28+
>
29+
{label}
30+
</Typography>
31+
</StyledButton>
32+
</Tooltip>
33+
)
34+
}

src/app/pages/RoflAppDetailsPage/InstancesList.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { FC } from 'react'
22
import { useTranslation } from 'react-i18next'
3-
import Typography from '@mui/material/Typography'
43
import { RoflInstance } from '../../../oasis-nexus/api'
54
import { SearchScope } from '../../../types/searchScope'
65
import { COLORS } from '../../../styles/theme/colors'
7-
import { useScreenSize } from '../../hooks/useScreensize'
8-
import { trimLongString } from '../../utils/trimLongString'
96
import { Table, TableCellAlign, TableColProps } from '../../components/Table'
107
import { TablePaginationProps } from '../../components/Table/TablePagination'
118
import { RoflAppInstanceStatusBadge } from '../../components/Rofl/RoflAppInstanceStatusBadge'
129
import { RoflAppInstanceLink } from '../../components/Rofl/RoflAppInstanceLink'
10+
import { TableCellNode } from '../../components/TableCellNode'
11+
import { TableHeaderNode } from '../../components/TableHeaderNode'
1312

1413
type InstancesListProps = {
1514
currentEpoch: number | undefined
@@ -31,11 +30,10 @@ export const InstancesList: FC<InstancesListProps> = ({
3130
scope,
3231
}) => {
3332
const { t } = useTranslation()
34-
const { isTablet } = useScreenSize()
3533

3634
const tableColumns: TableColProps[] = [
3735
{ key: 'rak', content: t('rofl.rakAbbreviation') },
38-
{ key: 'node', content: t('rofl.nodeId') },
36+
{ key: 'node', content: <TableHeaderNode /> },
3937
{ key: 'expirationEpoch', content: t('rofl.expirationEpoch'), align: TableCellAlign.Right },
4038
{ key: 'expirationStatus', content: t('common.status'), align: TableCellAlign.Right },
4139
]
@@ -54,11 +52,7 @@ export const InstancesList: FC<InstancesListProps> = ({
5452
},
5553
{
5654
key: 'node',
57-
content: (
58-
<Typography variant="mono">
59-
{isTablet ? trimLongString(instance.endorsing_node_id) : instance.endorsing_node_id}
60-
</Typography>
61-
),
55+
content: <TableCellNode id={instance.endorsing_node_id} scope={scope} />,
6256
},
6357
{
6458
key: 'expirationEpoch',

src/app/pages/RoflAppInstanceDetailsPage/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AppErrors } from '../../../types/errors'
88
import { RoflAppInstanceDetailsContext } from './hooks'
99
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
1010
import { useScreenSize } from '../../hooks/useScreensize'
11+
import { getOasisAddressFromBase64PublicKey } from '../../utils/helpers'
1112
import { PageLayout } from '../../components/PageLayout'
1213
import { SubPageCard } from '../../components/SubPageCard'
1314
import { TextSkeleton } from '../../components/Skeleton'
@@ -16,6 +17,7 @@ import { RouterTabs } from '../../components/RouterTabs'
1617
import { RoflAppLink } from '../../components/Rofl/RoflAppLink'
1718
import { CopyToClipboard } from '../../components/CopyToClipboard'
1819
import { useTypedSearchParam } from '../../hooks/useTypedSearchParam'
20+
import { AccountLink } from '../../components/Account/AccountLink'
1921

2022
export const RoflAppInstanceDetailsPage: FC = () => {
2123
const { t } = useTranslation()
@@ -56,6 +58,7 @@ export const RoflAppInstanceDetailsView: FC<{
5658

5759
if (isLoading) return <TextSkeleton numberOfRows={6} />
5860
if (!instance) return <></>
61+
const nodeAddress = getOasisAddressFromBase64PublicKey(instance.endorsing_node_id)
5962

6063
return (
6164
<StyledDescriptionList titleWidth={isMobile ? '100px' : '200px'}>
@@ -84,6 +87,13 @@ export const RoflAppInstanceDetailsView: FC<{
8487
{instance.endorsing_node_id} <CopyToClipboard value={instance.endorsing_node_id} />
8588
</Typography>
8689
</dd>
90+
<dt>{t('rofl.endorsingNodeAddress')}</dt>
91+
<dd>
92+
<Typography variant="mono">
93+
<AccountLink alwaysTrimOnTablet scope={scope} address={nodeAddress} />
94+
<CopyToClipboard value={nodeAddress} />
95+
</Typography>
96+
</dd>
8797
<dt>{t('rofl.extraKeys')}</dt>
8898
<dd>
8999
{instance.extra_keys.length ? (

src/app/pages/ValidatorDetailsPage/index.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from '../../../oasis-nexus/api'
1717
import { useScreenSize } from '../../hooks/useScreensize'
1818
import { useFormattedTimestampStringWithDistance } from '../../hooks/useFormattedTimestamp'
19+
import { getOasisAddressFromBase64PublicKey } from '../../utils/helpers'
1920
import { RouterTabs } from '../../components/RouterTabs'
2021
import { StyledDescriptionList } from '../../components/StyledDescriptionList'
2122
import { PageLayout } from '../../components/PageLayout'
@@ -252,8 +253,20 @@ export const ValidatorDetailsView: FC<{
252253
</dd>
253254
<dt>{t('validator.entityId')}</dt>
254255
<dd>{validator.entity_id}</dd>
255-
<dt>{t('validator.nodeId')}</dt>
256+
<dt>{t('common.nodeId')}</dt>
256257
<dd>{validator.node_id}</dd>
258+
{validator.node_id && (
259+
<>
260+
<dt>{t('common.nodeAddress')}</dt>
261+
<dd>
262+
<AccountLink
263+
alwaysTrimOnTablet
264+
scope={{ network, layer: 'consensus' }}
265+
address={getOasisAddressFromBase64PublicKey(validator.node_id)}
266+
/>
267+
</dd>
268+
</>
269+
)}
257270
</>
258271
)}
259272
{!detailsPage && (

src/app/utils/helpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export const getOasisAddressOrNull = (address: string): Address | null => {
6363
}
6464
}
6565

66+
export function getOasisAddressFromBase64PublicKey(key: string) {
67+
const keyBytes = new Uint8Array(Buffer.from(key, 'base64'))
68+
return oasis.staking.addressToBech32(oasis.staking.addressFromPublicKey(keyBytes))
69+
}
70+
6671
export const isValidTxOasisHash = (hash: string): boolean => /^[0-9a-fA-F]{64}$/.test(hash)
6772

6873
export const isValidTxEthHash = (hash: string): boolean => /^0x[0-9a-fA-F]{64}$/.test(hash)

src/locales/en/translation.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@
108108
"network": "Network",
109109
"nft": "NFT",
110110
"nfts": "NFTs",
111+
"nodeId": "Node ID",
112+
"nodeAddress": "Node Address",
111113
"nonce": "Nonce",
112114
"not_defined": "Not defined",
113115
"oasis": "Oasis",
@@ -689,6 +691,7 @@
689691
"emptyRoflAppsList": "No ROFL apps found.",
690692
"enclaveId": "Enclave ID",
691693
"endorsement": "Endorsement",
694+
"endorsingNodeAddress": "Endorsing node address",
692695
"endorsingNodeId": "Endorsing node ID",
693696
"endorsingNodePays": "Endorsing node pays",
694697
"entity": "Entity",
@@ -707,7 +710,8 @@
707710
"nameNotProvided": "Name not provided",
708711
"noData": "No data available",
709712
"node": "Node",
710-
"nodeId": "Node ID",
713+
"nodeAddressSwitch": "Show node address",
714+
"nodeIdSwitch": "Show node ID",
711715
"policy": "Policy",
712716
"rak": "Runtime Attestation Key",
713717
"rakAbbreviation": "RAK",
@@ -827,7 +831,6 @@
827831
"inactive": "Inactive",
828832
"listTitle": "Validators",
829833
"missed": "Missed",
830-
"nodeId": "Node ID",
831834
"others": "Others",
832835
"signed": "Signed",
833836
"signedBlocks": "Signed Blocks",

0 commit comments

Comments
 (0)