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