main
tsx 127 lines 3.39 KB
Raw
1 import './list.css'
2 import { useState } from 'react'
3 import disclosureTriangleClosed from '../../../public/img/icon-disclosure-triangle-closed.svg'
4 import disclosureTriangleOpen from '../../../public/img/icon-disclosure-triangle-open.svg'
5 import { getAgent } from '../../utils/get-agent.js'
6 import { DeleteIcon } from '../icons/icon-delete.js'
7 import { SpinnerIcon } from '../icons/icon-spinner.js'
8 import { MultiaddrList } from '../multiaddr-list.js'
9 import { Panel } from '../panel.js'
10 import type { Peer as PeerType, MetricsRPC } from '@ipshipyard/libp2p-inspector-metrics'
11 import type { ReactElement } from 'react'
12
13 export interface PeersProps {
14 peers: PeerType[]
15 metrics: MetricsRPC
16 }
17
18 export function PeerList ({ peers, metrics }: PeersProps): ReactElement {
19 if (peers.length === 0) {
20 return (
21 <Panel>
22 <p>This node is not connected to any peers.</p>
23 </Panel>
24 )
25 }
26
27 return (
28 <>
29 {
30 peers.map(peer => <Peer key={peer.id.toString()} peer={peer} metrics={metrics} />)
31 }
32 </>
33 )
34 }
35
36 interface PeerProps {
37 peer: PeerType
38 metrics: MetricsRPC
39 key?: string
40 }
41
42 function Peer ({ peer, metrics }: PeerProps): ReactElement {
43 const [expanded, setExpanded] = useState(false)
44 const [disconnecting, setDisconnecting] = useState(false)
45
46 function closeConnection (evt: any): void {
47 evt.preventDefault()
48
49 setDisconnecting(true)
50 metrics.closeConnection(peer.id)
51 .catch(err => {
52 // eslint-disable-next-line
53 console.error('could not close connection', err)
54 })
55 .finally(() => {
56 setDisconnecting(false)
57 })
58 }
59
60 let disconnectIcon = <DeleteIcon onClick={closeConnection} />
61
62 if (disconnecting) {
63 disconnectIcon = <SpinnerIcon />
64 }
65
66 if (expanded) {
67 return (
68 <Panel>
69 <h2><img src={disclosureTriangleOpen} height={12} width={12} className='DisclosureTriangle' onClick={() => { setExpanded(false) }} /> {disconnectIcon} <PeerAgent peer={peer} /> <span className='PeerId'>{peer.id.toString()}</span></h2>
70 <p>
71 <PeerTags peer={peer} />
72 </p>
73 <h3>Multiaddrs</h3>
74 <MultiaddrList addresses={peer.addresses} includeCertification />
75 <PeerProtocols peer={peer} />
76 </Panel>
77 )
78 }
79
80 return (
81 <Panel>
82 <h2><img src={disclosureTriangleClosed} height={12} width={12} className='DisclosureTriangle' onClick={() => { setExpanded(true) }} /> {disconnectIcon} <PeerAgent peer={peer} /> <span className='PeerId'>{peer.id.toString()}</span></h2>
83 <p>
84 <PeerTags peer={peer} />
85 </p>
86 </Panel>
87 )
88 }
89
90 function PeerAgent ({ peer }: Pick<PeerProps, 'peer'>): ReactElement | undefined {
91 if (peer.metadata.AgentVersion == null) {
92 return undefined
93 }
94
95 return (
96 <span className='PeerAgent'>{getAgent(peer.metadata)}</span>
97 )
98 }
99
100 function PeerTags ({ peer }: Pick<PeerProps, 'peer'>): ReactElement | undefined {
101 const entries = Object.entries(peer.tags)
102
103 if (entries.length === 0) {
104 return undefined
105 }
106
107 return (
108 <>
109 {entries.map(([key, value], index) => <span key={index} className='PeerTag'>{key}</span>)}
110 </>
111 )
112 }
113
114 function PeerProtocols ({ peer }: Pick<PeerProps, 'peer'>): ReactElement | undefined {
115 if (peer.protocols.length === 0) {
116 return undefined
117 }
118
119 return (
120 <>
121 <h3>Protocols</h3>
122 <ul>
123 {peer.protocols.map((proto, index) => <li key={`proto-${index}`}>{proto}</li>)}
124 </ul>
125 </>
126 )
127 }