1
1
import React , { useState , useEffect , ChangeEvent , Dispatch , useRef } from "react" ;
2
2
import SplitPane from "react-split-pane" ;
3
+ import _ from "lodash" ;
3
4
import JSONRPCRequestEditor from "./JSONRPCRequestEditor" ;
4
5
import PlayCircle from "@material-ui/icons/PlayCircleFilled" ;
5
- import { IconButton , AppBar , Toolbar , Typography , Button , InputBase } from "@material-ui/core" ;
6
+ import CloseIcon from "@material-ui/icons/Close" ;
7
+ import PlusIcon from "@material-ui/icons/Add" ;
8
+ import { IconButton , AppBar , Toolbar , Typography , Button , InputBase , Tab , Tabs } from "@material-ui/core" ;
6
9
import { Client , RequestManager , HTTPTransport , WebSocketTransport } from "@open-rpc/client-js" ;
7
10
import Brightness3Icon from "@material-ui/icons/Brightness3" ;
8
11
import WbSunnyIcon from "@material-ui/icons/WbSunny" ;
9
12
import { JSONRPCError } from "@open-rpc/client-js/build/Error" ;
10
13
import { MethodObject } from "@open-rpc/meta-schema" ;
11
14
import MonacoEditor from "@etclabscore/react-monaco-editor" ;
15
+ import useTabs from "../hooks/useTabs" ;
16
+ import { useDebounce } from "use-debounce" ;
12
17
13
18
const errorToJSON = ( error : JSONRPCError | undefined ) : any => {
14
19
if ( ! error ) {
@@ -48,7 +53,6 @@ const useClient = (url: string): [Client, JSONRPCError | undefined, Dispatch<JSO
48
53
setClient ( c ) ;
49
54
c . onError ( ( e ) => {
50
55
console . log ( "onError" , e ) ; //tslint:disable-line
51
- setError ( e ) ;
52
56
} ) ;
53
57
} catch ( e ) {
54
58
setError ( e ) ;
@@ -67,8 +71,29 @@ function useCounter(defaultValue: number): [number, () => void] {
67
71
return [ counter , incrementCounter ] ;
68
72
}
69
73
74
+ const emptyJSONRPC = {
75
+ jsonrpc : "2.0" ,
76
+ method : "" ,
77
+ params : [ ] ,
78
+ id : "0" ,
79
+ } ;
80
+
70
81
const Inspector : React . FC < IProps > = ( props ) => {
82
+ const {
83
+ setTabContent,
84
+ setTabEditing,
85
+ setTabIndex,
86
+ tabs,
87
+ setTabs,
88
+ handleClose,
89
+ tabIndex,
90
+ setTabOpenRPCDocument,
91
+ setTabUrl,
92
+ handleLabelChange,
93
+ setTabResults,
94
+ } = useTabs ( ) ;
71
95
const [ id , incrementId ] = useCounter ( 0 ) ;
96
+ const [ openrpcDocument , setOpenRpcDocument ] = useState ( ) ;
72
97
const [ json , setJson ] = useState ( props . request || {
73
98
jsonrpc : "2.0" ,
74
99
method : "" ,
@@ -78,6 +103,7 @@ const Inspector: React.FC<IProps> = (props) => {
78
103
const editorRef = useRef ( ) ;
79
104
const [ results , setResults ] = useState ( ) ;
80
105
const [ url , setUrl ] = useState ( props . url || "" ) ;
106
+ const [ debouncedUrl ] = useDebounce ( url , 1000 ) ;
81
107
const [ client , error , setError ] = useClient ( url ) ;
82
108
useEffect ( ( ) => {
83
109
if ( props . openrpcMethodObject ) {
@@ -92,18 +118,26 @@ const Inspector: React.FC<IProps> = (props) => {
92
118
} , [ ] ) ;
93
119
useEffect ( ( ) => {
94
120
if ( json ) {
95
- setJson ( {
121
+ const jsonResult = {
96
122
...json ,
97
123
jsonrpc : "2.0" ,
98
124
id : id . toString ( ) ,
99
- } ) ;
125
+ } ;
126
+ setJson ( jsonResult ) ;
100
127
}
101
128
// eslint-disable-next-line react-hooks/exhaustive-deps
102
129
} , [ id ] ) ;
103
130
131
+ useEffect ( ( ) => {
132
+ if ( json ) {
133
+ setTabContent ( tabIndex , json ) ;
134
+ }
135
+ } , [ json ] ) ;
136
+
104
137
useEffect ( ( ) => {
105
138
if ( props . url ) {
106
139
setUrl ( props . url ) ;
140
+ setTabUrl ( tabIndex , props . url ) ;
107
141
}
108
142
} , [ props . url ] ) ;
109
143
@@ -113,19 +147,22 @@ const Inspector: React.FC<IProps> = (props) => {
113
147
incrementId ( ) ;
114
148
try {
115
149
const result = await client . request ( json . method , json . params ) ;
116
- setResults ( { jsonrpc : "2.0" , result, id } ) ;
150
+ const r = { jsonrpc : "2.0" , result, id } ;
151
+ setResults ( r ) ;
152
+ setTabResults ( tabIndex , r ) ;
117
153
} catch ( e ) {
118
- setError ( e ) ;
154
+ setResults ( e ) ;
155
+ setTabResults ( tabIndex , e ) ;
119
156
}
120
157
}
121
158
} ;
122
- function handleResponseEditorDidMount ( _ : any , editor : any ) {
159
+ function handleResponseEditorDidMount ( __ : any , editor : any ) {
123
160
editorRef . current = editor ;
124
161
}
125
162
126
163
const clear = ( ) => {
127
164
setResults ( undefined ) ;
128
- setError ( undefined ) ;
165
+ setTabResults ( tabIndex , undefined ) ;
129
166
} ;
130
167
131
168
const handleClearButton = ( ) => {
@@ -137,9 +174,83 @@ const Inspector: React.FC<IProps> = (props) => {
137
174
props . onToggleDarkMode ( ) ;
138
175
}
139
176
} ;
177
+ const refreshOpenRpcDocument = async ( ) => {
178
+ if ( url ) {
179
+ try {
180
+ const d = await client . request ( "rpc.discover" , [ ] ) ;
181
+ setOpenRpcDocument ( d ) ;
182
+ setTabOpenRPCDocument ( tabIndex , d ) ;
183
+ } catch ( e ) {
184
+ setOpenRpcDocument ( undefined ) ;
185
+ setTabOpenRPCDocument ( tabIndex , undefined ) ;
186
+ }
187
+ }
188
+ } ;
189
+
190
+ useEffect ( ( ) => {
191
+ refreshOpenRpcDocument ( ) ;
192
+ } , [ debouncedUrl ] ) ;
193
+
194
+ useEffect ( ( ) => {
195
+ if ( tabs [ tabIndex ] ) {
196
+ setJson ( tabs [ tabIndex ] . content ) ;
197
+ setUrl ( tabs [ tabIndex ] . url || "" ) ;
198
+ setOpenRpcDocument ( tabs [ tabIndex ] . openrpcDocument ) ;
199
+ setResults ( tabs [ tabIndex ] . results ) ;
200
+ }
201
+ } , [ tabIndex ] ) ;
202
+
203
+ const handleTabIndexChange = ( event : React . ChangeEvent < { } > , newValue : number ) => {
204
+ setTabIndex ( newValue ) ;
205
+ } ;
140
206
141
207
return (
142
208
< >
209
+ < div style = { { position : "relative" } } >
210
+ < Tabs
211
+ style = { { background : "transparent" } }
212
+ value = { tabIndex }
213
+ variant = "scrollable"
214
+ indicatorColor = "primary"
215
+ onChange = { handleTabIndexChange }
216
+ >
217
+ { tabs . map ( ( tab , index ) => (
218
+ < Tab disableRipple style = { {
219
+ border : "none" ,
220
+ outline : "none" ,
221
+ userSelect : "none" ,
222
+ } } onDoubleClick = { ( ) => setTabEditing ( tab , true ) } label = {
223
+ < div style = { { userSelect : "none" } } >
224
+ { tab . editing
225
+ ? < InputBase
226
+ value = { tab . name }
227
+ onChange = { ( ev ) => handleLabelChange ( ev , tab ) }
228
+ onBlur = { ( ) => setTabEditing ( tab , false ) }
229
+ autoFocus
230
+ style = { { maxWidth : "80px" , marginRight : "25px" } }
231
+ />
232
+ : < Typography style = { { display : "inline" , textTransform : "none" , marginRight : "25px" } } variant = "body1" > { tab . name } </ Typography >
233
+ }
234
+ { tabIndex === index
235
+ ?
236
+ < IconButton onClick = {
237
+ ( ev ) => handleClose ( ev , index )
238
+ } style = { { position : "absolute" , right : "10px" , top : "25%" } } size = "small" >
239
+ < CloseIcon />
240
+ </ IconButton >
241
+ : null
242
+ }
243
+ </ div >
244
+ } > </ Tab >
245
+ ) ) }
246
+ < Tab disableRipple style = { { minWidth : "50px" } } label = {
247
+ < IconButton onClick = { ( ) => setTabs ( [ ...tabs , { name : "New Tab" , content : { ...emptyJSONRPC } , url : "" } ] ) } >
248
+ < PlusIcon scale = { 0.5 } />
249
+ </ IconButton >
250
+ } >
251
+ </ Tab >
252
+ </ Tabs >
253
+ </ div >
143
254
< AppBar elevation = { 0 } position = "static" >
144
255
< Toolbar >
145
256
< img
@@ -156,7 +267,10 @@ const Inspector: React.FC<IProps> = (props) => {
156
267
value = { url }
157
268
placeholder = "Enter a JSON-RPC server URL"
158
269
onChange = {
159
- ( event : ChangeEvent < HTMLInputElement > ) => setUrl ( event . target . value )
270
+ ( event : ChangeEvent < HTMLInputElement > ) => {
271
+ setUrl ( event . target . value ) ;
272
+ setTabUrl ( tabIndex , event . target . value ) ;
273
+ }
160
274
}
161
275
fullWidth
162
276
style = { { background : "rgba(0,0,0,0.1)" , borderRadius : "4px" , padding : "0px 10px" , marginRight : "5px" } }
@@ -183,8 +297,18 @@ const Inspector: React.FC<IProps> = (props) => {
183
297
} } >
184
298
< JSONRPCRequestEditor
185
299
onChange = { ( val ) => {
186
- setJson ( JSON . parse ( val ) ) ;
300
+ let jsonResult ;
301
+ try {
302
+ jsonResult = JSON . parse ( val ) ;
303
+ } catch ( e ) {
304
+ console . error ( e ) ;
305
+ }
306
+ if ( jsonResult ) {
307
+ setJson ( jsonResult ) ;
308
+ setTabContent ( tabIndex , jsonResult ) ;
309
+ }
187
310
} }
311
+ openrpcDocument = { openrpcDocument }
188
312
openrpcMethodObject = { props . openrpcMethodObject }
189
313
value = { JSON . stringify ( json , null , 4 ) }
190
314
/>
0 commit comments