main
tsx 157 lines 4.29 KB
Raw
1 import './pubsub.css'
2 import { useState } from 'react'
3 import { Button } from '../button.js'
4 import { Panel } from '../panel.js'
5 import { SmallError, SmallSuccess } from '../status.js'
6 import { TextInput } from '../text-input.js'
7 import type { MetricsRPC } from '@ipshipyard/libp2p-inspector-metrics'
8 import type { Message } from '@libp2p/gossipsub'
9 import type { ReactElement } from 'react'
10
11 export interface PubSubProps {
12 component: string
13 metrics: MetricsRPC
14 }
15
16 export function PubSub ({ component, metrics }: PubSubProps): ReactElement {
17 const [topics, setTopics] = useState<string[]>([])
18 const [topic, setTopic] = useState('')
19
20 function loadTopics (): void {
21 metrics.pubsub.getTopics(component)
22 .then(list => {
23 setTopics(list)
24 })
25 .catch(err => {
26 // eslint-disable-next-line no-console
27 console.error('could not get subs', err)
28 })
29 }
30
31 loadTopics()
32
33 return (
34 <>
35 <SubscribePanel component={component} metrics={metrics} subscribed={loadTopics} />
36 <TopicList topics={topics} setTopic={setTopic} />
37 {/* TODO: fix message display */}
38 <TopicDisplay topic={topic} component={component} metrics={metrics} messages={[]} />
39 </>
40 )
41 }
42
43 interface SubscribePanelOptions {
44 component: string
45 subscribed(): void
46 metrics: MetricsRPC
47 }
48
49 function SubscribePanel ({ component, subscribed, metrics }: SubscribePanelOptions): ReactElement {
50 const [topic, setTopic] = useState('')
51 const [result, setResult] = useState(<small>E.g. `my-topic`, etc</small>)
52
53 function subscribe (evt: { preventDefault(): void }, topic: string): boolean {
54 evt.preventDefault()
55
56 metrics.pubsub.subscribe(component, topic)
57 .then(() => {
58 setResult(<SmallSuccess message='Subscribed successfully' />)
59 subscribed()
60 }, (err) => {
61 setResult(<SmallError error={err} />)
62 })
63
64 return false
65 }
66
67 return (
68 <Panel>
69 <p>Subscribe to a PubSub topic</p>
70 <form onSubmit={(evt) => subscribe(evt, topic)}>
71 <TextInput type='text' value={topic} placeholder='Topic' onChange={(e) => { setTopic(e.target.value) }} />
72 <Button onClick={(evt) => subscribe(evt, topic)} primary>Subscribe</Button>
73 </form>
74 {result}
75 </Panel>
76 )
77 }
78
79 interface TopicListOptions {
80 topics: string[]
81 setTopic(topic: string): void
82 }
83
84 function TopicList ({ topics, setTopic }: TopicListOptions): ReactElement {
85 return (
86 <div>
87 <ul>
88 {topics.map((topic, index) => (
89 <li key={`sub-${index}`} onClick={() => { setTopic(topic) }}>
90 {topic}
91 </li>
92 ))}
93 </ul>
94 </div>
95 )
96 }
97
98 interface TopicDisplayOptions {
99 component: string
100 topic: string
101 metrics: MetricsRPC
102 messages: Message[]
103 }
104
105 function TopicDisplay ({ component, topic, metrics, messages }: TopicDisplayOptions): ReactElement {
106 if (topic === '') {
107 return <></>
108 }
109
110 const [message, setMessage] = useState('')
111 const [result, setResult] = useState(<small>E.g. `hello world`, etc</small>)
112
113 function publish (evt: { preventDefault(): void }, topic: string): boolean {
114 evt.preventDefault()
115
116 metrics.pubsub.publish(component, topic, new TextEncoder().encode(message))
117 .then(() => {
118 setResult(<SmallSuccess message='Published successfully' />)
119 }, (err) => {
120 setResult(<SmallError error={err} />)
121 })
122
123 return false
124 }
125
126 return (
127 <>
128 <Panel>
129 <p>Publish a message</p>
130 <form onSubmit={(evt) => publish(evt, topic)}>
131 <TextInput type='text' value={message} placeholder='Message' onChange={(e) => { setMessage(e.target.value) }} />
132 <Button onClick={(evt) => publish(evt, topic)} primary>Publish</Button>
133 </form>
134 {result}
135 </Panel>
136 <Panel>
137 {messages.map((message, index) => {
138 if (message.type === 'signed') {
139 return (
140 <div key={`message-${index}`}>
141 <p>From: {message.from.toString()}</p>
142 <p>Seq: {message.sequenceNumber.toString()}</p>
143 <p>Data: {new TextDecoder().decode(message.data)}</p>
144 </div>
145 )
146 }
147
148 return (
149 <div key={`message-${index}`}>
150 <p>Data: {new TextDecoder().decode(message.data)}</p>
151 </div>
152 )
153 })}
154 </Panel>
155 </>
156 )
157 }