Skip to content

Commit

Permalink
feat: add graph node list
Browse files Browse the repository at this point in the history
  • Loading branch information
Keith-CY committed Oct 14, 2024
1 parent 1fca1b4 commit 1c4abbf
Show file tree
Hide file tree
Showing 6 changed files with 431 additions and 2 deletions.
10 changes: 10 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,16 @@
"tlc_balance": "TLC Balance",
"offered": "Offered",
"received": "Received"
},
"graph": {
"node": {
"name": "Name",
"auto_accept_min_ckb_funding_amount": "Auto-accepting Threshold",
"first_seen": "First Seen",
"node_id": "Node ID",
"chain_hash": "Chain Hash",
"addresses": "Addresses"
}
}
}
}
Expand Down
156 changes: 156 additions & 0 deletions src/pages/Fiber/GraphNodeList/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
@import '../../../styles/variables.module';
@import '../../../styles/table.module';

.container {
text-wrap: nowrap;
display: flex;
flex-direction: column;
margin: 24px 120px;
font-size: 1rem;

a {
color: var(--primary-color);
}

table {
@extend %base-table;

tr[data-role='pagination']:hover {
background: #fff;
}
}

svg {
pointer-events: none;
}

button {
display: flex;
align-items: center;
appearance: none;
padding: 0;
border: none;
background: none;
cursor: pointer;

&:hover {
color: var(--primary-color);
}
}

.name {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
}

.nodeId,
.chainHash {
display: flex;
gap: 4px;
}

.address {
display: flex;
align-items: center;
justify-content: flex-end;
flex-wrap: nowrap;
gap: 4px;

& > span:first-child {
display: block;
max-width: 240px;
overflow: hidden;
text-overflow: ellipsis;
}

button,
a,
.more {
display: flex;
align-items: center;
}
}

.header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.5rem;
margin-bottom: 20px;

button {
font-size: 0.875rem;
color: var(--primary-color);
padding-left: 8px;
}
}

.amount {
display: flex;
flex-direction: column;
}

@media screen and (width < $extraLargeBreakPoint) {
margin: 24px 20px;
}

@media screen and (width < 1330px) {
font-size: 14px;

table {
th,
td {
&:nth-child(6) {
display: none;
}
}
}
}

@media screen and (width < 810px) {
table {
tr:not([data-role='pagination']) {
th,
td {
&:last-child {
display: none;
}
}
}
}
}

@media screen and (width < 900px) {
table {
th,
td {
&:nth-child(5) {
display: none;
}
}
}
}

@media screen and (width < 700px) {
table {
th,
td {
&:nth-child(3) {
display: none;
}
}
}
}

@media screen and (width < 520px) {
table {
th,
td {
&:first-child {
display: none;
}
}
}
}
}
188 changes: 188 additions & 0 deletions src/pages/Fiber/GraphNodeList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
import { Tooltip } from 'antd'
import { CopyIcon, InfoCircledIcon } from '@radix-ui/react-icons'
import dayjs from 'dayjs'
import Content from '../../../components/Content'
import { useSetToast } from '../../../components/Toast'
import { explorerService } from '../../../services/ExplorerService'
import type { Fiber } from '../../../services/ExplorerService/fetcher'
import { shannonToCkb } from '../../../utils/util'
import { localeNumberString } from '../../../utils/number'
import { parseNumericAbbr } from '../../../utils/chart'
import styles from './index.module.scss'
import Pagination from '../Pagination'
import { PAGE_SIZE } from '../../../constants/common'

const TIME_TEMPLATE = 'YYYY/MM/DD hh:mm:ss'

const fields = [
{
key: 'alias',
label: 'name',
transformer: (v: unknown, i: Fiber.Graph.Node) => {
if (typeof v !== 'string') return v
return (
<Tooltip title={v}>
<div className={styles.name}>
<Link to={`/fiber/graph/node/${i.nodeId}`}>{v || <span style={{ color: '#999' }}>Untitled</span>}</Link>
</div>
</Tooltip>
)
},
},
{
key: 'autoAcceptMinCkbFundingAmount',
label: 'auto_accept_min_ckb_funding_amount',
transformer: (v: unknown) => {
if (typeof v !== 'string' || Number.isNaN(+v)) return v
const ckb = shannonToCkb(v)
const amount = parseNumericAbbr(ckb)
return (
<div className={styles.balance}>
<Tooltip title={`${localeNumberString(ckb)} CKB`}>
<span>{`${amount} CKB`}</span>
</Tooltip>
</div>
)
},
},
{
key: 'timestamp',
label: 'first_seen',
transformer: (v: unknown) => {
if (typeof v !== 'string') return v
return dayjs(+v).format(TIME_TEMPLATE)
},
},
{
key: 'nodeId',
label: 'node_id',
transformer: (v: unknown) => {
if (typeof v !== 'string') return v
return (
<span className={styles.nodeId}>
<Tooltip title={v}>
<Link to={`/fiber/graph/node/${v}`} className="monospace">
{v.length > 16 ? `${v.slice(0, 8)}...${v.slice(-8)}` : v}
</Link>
</Tooltip>
<button type="button" data-copy-text={v}>
<CopyIcon />
</button>
</span>
)
},
},
{
key: 'chainHash',
label: 'chain_hash',
transformer: (v: unknown) => {
if (typeof v !== 'string') return v
return (
<span className={styles.chainHash}>
<Tooltip title={v}>
<span className="monospace">{v.length > 16 ? `${v.slice(0, 8)}...${v.slice(-8)}` : v}</span>
</Tooltip>
<button type="button" data-copy-text={v}>
<CopyIcon />
</button>
</span>
)
},
},
{
key: 'addresses',
label: 'addresses',
transformer: (v: unknown) => {
if (!Array.isArray(v)) return v
const addr = v[0]
if (!addr || typeof addr !== 'string') return v
return (
<span className={styles.address}>
<Tooltip title={addr}>
<span>{addr}</span>
</Tooltip>
<button type="button" data-copy-text={v}>
<CopyIcon />
</button>
{/* <a href={rpcAddr} title={rpcAddr} target="_blank" rel="noopener noreferrer"> */}
{/* <OpenInNewWindowIcon /> */}
{/* </a> */}
{v.length > 1 ? (
<Tooltip title={`${v.length - 1} more address(es)`}>
<span className={styles.more}>
<InfoCircledIcon />
</span>
</Tooltip>
) : null}
</span>
)
},
},
]

const GraphNodeList = () => {
const [t] = useTranslation()
const setToast = useSetToast()

const { data } = useQuery({
queryKey: ['fiber', 'graph', 'nodes'],
queryFn: () => explorerService.api.getGraphNodes(),
})

const list = data?.data.fiberGraphNodes ?? []
const pageInfo = data?.data.meta ?? { total: 1, pageSize: PAGE_SIZE }
const totalPages = Math.ceil(pageInfo.total / pageInfo.pageSize)

const handleCopy = (e: React.SyntheticEvent) => {
const elm = e.target
if (!(elm instanceof HTMLElement)) return
const { copyText } = elm.dataset
if (!copyText) return
e.stopPropagation()
e.preventDefault()
navigator?.clipboard.writeText(copyText).then(() => setToast({ message: t('common.copied') }))
}

return (
<Content>
<div className={styles.container} onClick={handleCopy}>
<h1 className={styles.header}>
<span>CKB Fiber Graph Nodes</span>
</h1>
<table>
<thead>
<tr>
{fields.map(f => {
return <th key={f.key}>{t(`fiber.graph.node.${f.label}`)}</th>
})}
</tr>
</thead>
<div className={styles.tableSeparator} />
<tbody>
{list.map(i => {
return (
<tr>
{fields.map(f => {
const v = i[f.key as keyof typeof i]
return <td key={f.key}>{f.transformer?.(v, i) ?? v}</td>
})}
</tr>
)
})}
<div className={styles.tableSeparator} />
<tr data-role="pagination">
<td colSpan={fields.length}>
<Pagination totalPages={totalPages} />
</td>
</tr>
</tbody>
</table>
</div>
</Content>
)
}

export default GraphNodeList
2 changes: 1 addition & 1 deletion src/pages/Fiber/PeerList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const fields = [
<OpenInNewWindowIcon />
</a>
{v.length > 1 ? (
<Tooltip title={`${v.length - 1} more rpc addresses`}>
<Tooltip title={`${v.length - 1} more rpc address(es)`}>
<span className={styles.more}>
<InfoCircledIcon />
</span>
Expand Down
5 changes: 5 additions & 0 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const MoleculeParser = lazy(() => import('../pages/Tools/MoleculeParser'))
const FiberPeerList = lazy(() => import('../pages/Fiber/PeerList'))
const FiberPeer = lazy(() => import('../pages/Fiber/Peer'))
const FiberChannel = lazy(() => import('../pages/Fiber/Channel'))
const FiberGraphNodeList = lazy(() => import('../pages/Fiber/GraphNodeList'))
// ======

const routes: RouteProps[] = [
Expand Down Expand Up @@ -358,6 +359,10 @@ const routes: RouteProps[] = [
path: '/fiber/channels/:id',
component: FiberChannel,
},
{
path: '/fiber/graph/nodes',
component: FiberGraphNodeList,
},
]

type PageErrorBoundaryState = {
Expand Down
Loading

0 comments on commit 1c4abbf

Please sign in to comment.