@@ -6,7 +6,7 @@ import { gql, useMutation, useSubscription } from '@apollo/client';
6
6
import { b64DecodeUnicode } from "./ResponseDisplay" ;
7
7
import { SearchBar } from './ResponseDisplay' ;
8
8
import Pagination from '@mui/material/Pagination' ;
9
- import { Typography , CircularProgress , Select , IconButton } from '@mui/material' ;
9
+ import { Typography , CircularProgress , Select , IconButton , Backdrop } from '@mui/material' ;
10
10
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline' ;
11
11
import Input from '@mui/material/Input' ;
12
12
import MenuItem from '@mui/material/MenuItem' ;
@@ -78,54 +78,62 @@ const getClassnames = (entry) => {
78
78
//console.log(entry);
79
79
return classnames . join ( " " ) ;
80
80
}
81
- const GetOutputFormat = ( { data, myTask, taskID, useASNIColor, messagesEndRef, showTaskStatus, wrapText} ) => {
81
+ export const GetOutputFormatAll = ( { data, myTask, taskID, useASNIColor, messagesEndRef, showTaskStatus, wrapText, search } ) => {
82
82
const [ dataElement , setDataElement ] = React . useState ( null ) ;
83
83
React . useEffect ( ( ) => {
84
-
85
- if ( data . response ) {
86
- // we're looking at response output
87
- if ( data . is_error ) {
88
- setDataElement ( < pre style = { { display : "inline" , backgroundColor : "#311717" , margin : "0 0 0 0" ,
89
- wordBreak : wrapText ? "break-all" : "" ,
90
- whiteSpace : wrapText ? "pre-wrap" : "" } } key = { data . timestamp + data . id } >
91
- { data . response }
84
+ const elements = data . map ( d => {
85
+ if ( d . response ) {
86
+ // we're looking at response output
87
+ if ( d . is_error ) {
88
+ return ( < pre id = { "response" + d . timestamp + d . id } style = { { display : "inline" , backgroundColor : "#311717" , color : "white ", margin : "0 0 0 0" ,
89
+ wordBreak : wrapText ? "break-all" : "" ,
90
+ whiteSpace : wrapText ? "pre-wrap" : "" } } key = { d . timestamp + d . id } >
91
+ { d . response }
92
92
</ pre > )
93
- } else {
94
- if ( useASNIColor ) {
95
- let ansiJSON = Anser . ansiToJson ( data . response , { use_classes : true } ) ;
96
- //console.log(ansiJSON)
97
- setDataElement (
98
- ansiJSON . map ( ( a , i ) => (
99
- < pre style = { { display : "inline" , margin : "0 0 0 0" ,
100
- wordBreak : wrapText ? "break-all" : "" ,
101
- whiteSpace : wrapText ? "pre-wrap" : "" ,
102
- } } className = { getClassnames ( a ) } key = { data . id + data . timestamp + i } > { a . content } </ pre >
103
- ) )
104
- )
105
93
} else {
106
- setDataElement ( < pre style = { { display : "inline" , margin : "0 0 0 0" ,
107
- wordBreak : wrapText ? "break-all" : "" ,
108
- whiteSpace : wrapText ? "pre-wrap" : "" ,
109
- } } key = { data . timestamp + data . id } > { data . response } </ pre > )
110
- }
94
+ if ( useASNIColor ) {
95
+ let ansiJSON = Anser . ansiToJson ( d . response , { use_classes : true } ) ;
96
+ //console.log(ansiJSON)
97
+ return (
98
+ ansiJSON . map ( ( a , i ) => (
99
+ < pre id = { "response" + d . timestamp + d . id } style = { { display : "inline" , margin : "0 0 0 0" ,
100
+ wordBreak : wrapText ? "break-all" : "" ,
101
+ whiteSpace : wrapText ? "pre-wrap" : "" ,
102
+ } } className = { getClassnames ( a ) } key = { d . id + d . timestamp + i } > { a . content } </ pre >
103
+ ) )
104
+ )
105
+ } else {
106
+ return ( < pre id = { "response" + d . timestamp + d . id } style = { { display : "inline" , margin : "0 0 0 0" ,
107
+ wordBreak : wrapText ? "break-all" : "" ,
108
+ whiteSpace : wrapText ? "pre-wrap" : "" ,
109
+ } } key = { d . timestamp + d . id } > { d . response } </ pre > )
110
+ }
111
111
112
- }
113
- } else {
114
- // we're looking at tasking
115
- setDataElement (
116
- < pre key = { data . timestamp + data . id } style = { { display : "inline" , margin : "0 0 0 0" ,
117
- wordBreak : wrapText ? "break-all" : "" , whiteSpace : "pre-wrap" } } >
118
- { showTaskStatus && getTaskingStatus ( data ) }
119
- { data . original_params }
112
+ }
113
+ } else {
114
+ // we're looking at tasking
115
+ return (
116
+ < pre id = { "task" + d . timestamp + d . id } key = { d . timestamp + d . id } style = { { display : "inline" , margin : "0 0 0 0" ,
117
+ wordBreak : wrapText ? "break-all" : "" , whiteSpace : "pre-wrap" } } >
118
+ { showTaskStatus && getTaskingStatus ( d ) }
119
+ { d . original_params }
120
120
</ pre >
121
- )
122
- }
123
- } , [ data . timestamp , useASNIColor , showTaskStatus , wrapText ] ) ;
121
+ )
122
+ }
123
+ } )
124
+ setDataElement ( elements ) ;
125
+
126
+ } , [ data , useASNIColor , showTaskStatus , wrapText ] ) ;
124
127
React . useLayoutEffect ( ( ) => {
125
128
if ( myTask ) {
126
129
let el = document . getElementById ( `ptytask${ taskID } ` ) ;
127
130
if ( el && el . scrollHeight - el . scrollTop - el . clientHeight < 500 ) {
128
- messagesEndRef . current ?. scrollIntoView ( { behavior : "auto" } ) ;
131
+ if ( ! search ) {
132
+ messagesEndRef ?. current ?. scrollIntoView ( { behavior : "auto" , block : "nearest" } ) ;
133
+ //console.log("scrolling");
134
+ }
135
+ } else {
136
+ // console.log("not scrolled down enough")
129
137
}
130
138
}
131
139
} , [ dataElement ] ) ;
@@ -135,6 +143,7 @@ const GetOutputFormat = ({data, myTask, taskID, useASNIColor, messagesEndRef, s
135
143
136
144
}
137
145
146
+
138
147
const InteractiveMessageTypes = [
139
148
{ "name" : "None" , "value" : - 1 , "text" : "None" } ,
140
149
{ "name" : "Tab" , "value" : 13 , "text" : "^I" } ,
@@ -168,6 +177,7 @@ const EnterOptions = [
168
177
] ;
169
178
export const ResponseDisplayInteractive = ( props ) => {
170
179
const me = useReactiveVar ( meState ) ;
180
+ const [ backdropOpen , setBackdropOpen ] = React . useState ( false ) ;
171
181
const [ scrollToBottom , setScrollToBottom ] = React . useState ( false ) ;
172
182
const pageSize = React . useRef ( 100 ) ;
173
183
const highestFetched = React . useRef ( 0 ) ;
@@ -183,9 +193,8 @@ export const ResponseDisplayInteractive = (props) =>{
183
193
const [ useASNIColor , setUseANSIColor ] = React . useState ( true ) ;
184
194
const [ showTaskStatus , setShowTaskStatus ] = React . useState ( true ) ;
185
195
const [ wrapText , setWrapText ] = React . useState ( true ) ;
186
- useSubscription ( getInteractiveTaskingQuery , {
196
+ const { loading : loadingTasks } = useSubscription ( getInteractiveTaskingQuery , {
187
197
variables : { parent_task_id : props . task . id } ,
188
- shouldResubscribe : true ,
189
198
onError : data => {
190
199
console . error ( data )
191
200
} ,
@@ -206,8 +215,12 @@ export const ResponseDisplayInteractive = (props) =>{
206
215
} , [ ...taskData ] ) ;
207
216
setTaskData ( newTaskData ) ;
208
217
}
218
+ if ( backdropOpen ) {
219
+ setBackdropOpen ( false ) ;
220
+ }
221
+
209
222
}
210
- } )
223
+ } )
211
224
const subscriptionDataCallback = React . useCallback ( ( { data} ) => {
212
225
// we still have some room to view more, but only room for fetchLimit - totalFetched.current
213
226
if ( props . task . id !== taskIDRef . current ) {
@@ -224,20 +237,25 @@ export const ResponseDisplayInteractive = (props) =>{
224
237
highestFetched . current = highestFetchedId ;
225
238
taskIDRef . current = props . task . id ;
226
239
} else {
227
- const newResponses = data . data . response_stream . filter ( r => r . id > highestFetched . current ) ;
228
- const newerResponses = newResponses . map ( ( r ) => { return { ...r , response : b64DecodeUnicode ( r . response ) } } ) ;
229
- newerResponses . sort ( ( a , b ) => a . id > b . id ? 1 : - 1 ) ;
240
+ const newResponses = data . data . response_stream . filter ( r => r . id > highestFetched . current ) ;
241
+ const newerResponses = newResponses . map ( ( r ) => {
242
+ return { ...r , response : b64DecodeUnicode ( r . response ) }
243
+ } ) ;
244
+ newerResponses . sort ( ( a , b ) => a . id > b . id ? 1 : - 1 ) ;
230
245
let rawResponseArray = [ ...rawResponses ] ;
231
246
let highestFetchedId = highestFetched . current ;
232
- for ( let i = 0 ; i < newerResponses . length ; i ++ ) {
247
+ for ( let i = 0 ; i < newerResponses . length ; i ++ ) {
233
248
rawResponseArray . push ( newerResponses [ i ] ) ;
234
249
highestFetchedId = newerResponses [ i ] [ "id" ] ;
235
250
}
236
251
setRawResponses ( rawResponseArray ) ;
237
252
highestFetched . current = highestFetchedId ;
238
253
}
254
+ if ( backdropOpen ) {
255
+ setBackdropOpen ( false ) ;
256
+ }
239
257
240
- } , [ highestFetched . current , rawResponses , props . task . id ] ) ;
258
+ } , [ highestFetched . current , rawResponses , props . task . id , backdropOpen , taskIDRef . current ] ) ;
241
259
useSubscription ( subResponsesQuery , {
242
260
variables : { task_id : props . task . id } ,
243
261
fetchPolicy : "no-cache" ,
@@ -325,31 +343,51 @@ export const ResponseDisplayInteractive = (props) =>{
325
343
messagesEndRef . current . scrollIntoView ( ) ;
326
344
}
327
345
} , [ scrollToBottom ] ) ;
328
- React . useLayoutEffect ( ( ) => {
329
- if ( ! scrollToBottom && alloutput . length > 0 ) { setScrollToBottom ( true ) }
330
- } , [ alloutput ] ) ;
346
+ React . useEffect ( ( ) => {
347
+ if ( loadingTasks ) {
348
+ setTaskData ( [ ] ) ;
349
+ setBackdropOpen ( true ) ;
350
+ } else {
351
+ setBackdropOpen ( false ) ;
352
+ }
353
+ } , [ loadingTasks ] ) ;
331
354
return (
332
355
333
356
< div style = { {
334
357
display : "flex" , overflowY : "auto" ,
335
358
position : "relative" , height : props . expand ? "100%" : undefined , maxHeight : props . expand ? "100%" : "500px" ,
336
359
flexDirection : "column"
337
360
} } >
361
+ < Backdrop open = { backdropOpen } style = { { zIndex : 2 , position : "absolute" , } } invisible = { false } >
362
+ < div style = { {
363
+ borderRadius : "4px" ,
364
+ border : "1px solid black" ,
365
+ padding : "5px" ,
366
+ backgroundColor : "rgba(37,37,37,0.92)" , color : "white" ,
367
+ alignItems : "center" ,
368
+ display : "flex" , flexDirection : "column" } } >
369
+ < CircularProgress color = "inherit" />
370
+ < Typography variant = { "h5" } >
371
+ Fetching Interactive Task Data....
372
+ </ Typography >
373
+ </ div >
374
+ </ Backdrop >
338
375
{ props . searchOutput &&
339
376
< SearchBar onSubmitSearch = { onSubmitSearch } />
340
377
}
341
378
< div style = { { overflowY : "auto" , width : "100%" , marginBottom : "5px" ,
342
379
flexGrow : 1 , paddingLeft : "10px" } } ref = { props . responseRef }
343
380
id = { `ptytask${ props . task . id } ` } >
344
- { alloutput . map ( ( e , index ) => (
345
- < GetOutputFormat key = { "getoutput" + index } data = { e }
381
+
382
+ < GetOutputFormatAll data = { alloutput }
346
383
myTask = { props . task . operator . username === ( me ?. user ?. username || "" ) }
347
384
taskID = { props . task . id }
348
385
useASNIColor = { useASNIColor }
349
386
messagesEndRef = { messagesEndRef }
350
387
showTaskStatus = { showTaskStatus }
388
+ search = { props . searchOutput ? search : undefined }
351
389
wrapText = { wrapText } />
352
- ) ) }
390
+
353
391
< div ref = { messagesEndRef } />
354
392
</ div >
355
393
{ ! props . task ?. is_interactive_task &&
0 commit comments