@@ -2,11 +2,13 @@ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef,
2
2
import { useDispatch , useSelector } from 'react-redux'
3
3
import cx from 'classnames'
4
4
import { useParams } from 'react-router-dom'
5
+ import { debounce , isUndefined , reject } from 'lodash'
5
6
6
7
import {
7
8
EuiText ,
8
9
EuiToolTip ,
9
10
EuiTextColor ,
11
+ EuiLoadingContent ,
10
12
} from '@elastic/eui'
11
13
import {
12
14
formatBytes ,
@@ -25,6 +27,7 @@ import {
25
27
ScanNoResultsFoundText ,
26
28
} from 'uiSrc/constants/texts'
27
29
import {
30
+ fetchKeysMetadata ,
28
31
keysDataSelector ,
29
32
keysSelector ,
30
33
selectedKeySelector ,
@@ -40,7 +43,7 @@ import { SCAN_COUNT_DEFAULT } from 'uiSrc/constants/api'
40
43
import { KeysStoreData , KeyViewType } from 'uiSrc/slices/interfaces/keys'
41
44
import VirtualTable from 'uiSrc/components/virtual-table/VirtualTable'
42
45
import { ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
43
- import { OVER_RENDER_BUFFER_COUNT , Pages , TableCellAlignment , TableCellTextAlignment } from 'uiSrc/constants'
46
+ import { Pages , TableCellAlignment , TableCellTextAlignment } from 'uiSrc/constants'
44
47
import { IKeyPropTypes } from 'uiSrc/constants/prop-types/keys'
45
48
import { getBasedOnViewTypeEvent , sendEventTelemetry , TelemetryEvent } from 'uiSrc/telemetry'
46
49
@@ -70,9 +73,10 @@ const KeyList = forwardRef((props: Props, ref) => {
70
73
const { isSearched, isFiltered, viewType } = useSelector ( keysSelector )
71
74
const { keyList : { scrollTopPosition } } = useSelector ( appContextBrowser )
72
75
73
- const [ items , setItems ] = useState ( keysState . keys )
76
+ const [ , rerender ] = useState ( { } )
74
77
75
- const formattedLastIndexRef = useRef ( OVER_RENDER_BUFFER_COUNT )
78
+ const itemsRef = useRef ( keysState . keys )
79
+ const renderedRowsIndexesRef = useRef ( { startIndex : 0 , lastIndex : 0 } )
76
80
77
81
const dispatch = useDispatch ( )
78
82
@@ -87,20 +91,20 @@ const KeyList = forwardRef((props: Props, ref) => {
87
91
if ( viewType === KeyViewType . Tree ) {
88
92
return
89
93
}
90
- setItems ( ( prevItems ) => {
91
- dispatch ( setLastBatchKeys ( prevItems . slice ( - SCAN_COUNT_DEFAULT ) ) )
92
- return [ ]
94
+ rerender ( ( ) => {
95
+ dispatch ( setLastBatchKeys ( itemsRef . current ?. slice ( - SCAN_COUNT_DEFAULT ) ) )
93
96
} )
94
97
} , [ ] )
95
98
96
99
useEffect ( ( ) => {
97
- const newKeys = bufferFormatRangeItems ( keysState . keys , 0 , OVER_RENDER_BUFFER_COUNT , formatItem )
98
-
99
- if ( keysState . keys . length < items . length ) {
100
- formattedLastIndexRef . current = 0
100
+ itemsRef . current = [ ...keysState . keys ]
101
+ if ( itemsRef . current . length === 0 ) {
102
+ return
101
103
}
102
104
103
- setItems ( newKeys )
105
+ const { lastIndex, startIndex } = renderedRowsIndexesRef . current
106
+ onRowsRendered ( startIndex , lastIndex )
107
+ rerender ( { } )
104
108
} , [ keysState . keys ] )
105
109
106
110
const onNoKeysLinkClick = ( ) => {
@@ -130,8 +134,7 @@ const KeyList = forwardRef((props: Props, ref) => {
130
134
}
131
135
132
136
const onLoadMoreItems = ( props : { startIndex : number , stopIndex : number } ) => {
133
- const formattedAllKeys = bufferFormatRangeItems ( items , formattedLastIndexRef . current , items . length , formatItem )
134
- loadMoreItems ?.( formattedAllKeys , props )
137
+ loadMoreItems ?.( itemsRef . current , props )
135
138
}
136
139
137
140
const onWheelSearched = ( event : React . WheelEvent ) => {
@@ -159,34 +162,96 @@ const KeyList = forwardRef((props: Props, ref) => {
159
162
nameString : bufferToString ( item . name )
160
163
} ) , [ ] )
161
164
162
- const bufferFormatRows = ( lastIndex : number ) => {
163
- const newItems = bufferFormatRangeItems ( items , formattedLastIndexRef . current , lastIndex , formatItem )
165
+ const onRowsRendered = debounce ( async ( startIndex : number , lastIndex : number ) => {
166
+ renderedRowsIndexesRef . current = { lastIndex, startIndex }
164
167
165
- setItems ( newItems )
168
+ const newItems = bufferFormatRows ( startIndex , lastIndex )
166
169
167
- if ( lastIndex > formattedLastIndexRef . current ) {
168
- formattedLastIndexRef . current = lastIndex
169
- }
170
+ getMetadata ( startIndex , lastIndex , newItems )
171
+ } , 100 )
172
+
173
+ const bufferFormatRows = ( startIndex : number , lastIndex : number ) : GetKeyInfoResponse [ ] => {
174
+ const newItems = bufferFormatRangeItems (
175
+ itemsRef . current , startIndex , lastIndex , formatItem
176
+ )
177
+ itemsRef . current . splice ( startIndex , newItems . length , ...newItems )
170
178
171
179
return newItems
172
180
}
173
181
182
+ const getMetadata = (
183
+ startIndex : number ,
184
+ lastIndex : number ,
185
+ itemsInit : GetKeyInfoResponse [ ] = [ ]
186
+ ) : void => {
187
+ const isSomeNotUndefined = ( { type, size, length } : GetKeyInfoResponse ) =>
188
+ ! isUndefined ( type ) || ! isUndefined ( size ) || ! isUndefined ( length )
189
+
190
+ const emptyItems = reject ( itemsInit , isSomeNotUndefined )
191
+
192
+ if ( ! emptyItems . length ) return
193
+
194
+ dispatch ( fetchKeysMetadata (
195
+ emptyItems . map ( ( { name } ) => name ) ,
196
+ ( loadedItems ) =>
197
+ onSuccessFetchedMetadata ( {
198
+ startIndex,
199
+ lastIndex,
200
+ loadedItems,
201
+ isFirstEmpty : ! isSomeNotUndefined ( itemsInit [ 0 ] ) ,
202
+ } )
203
+ ) )
204
+ }
205
+
206
+ const onSuccessFetchedMetadata = ( data : {
207
+ startIndex : number ,
208
+ lastIndex : number ,
209
+ isFirstEmpty : boolean
210
+ loadedItems : GetKeyInfoResponse [ ] ,
211
+ } ) => {
212
+ const {
213
+ startIndex,
214
+ lastIndex,
215
+ isFirstEmpty,
216
+ loadedItems,
217
+ } = data
218
+ const items = loadedItems . map ( formatItem )
219
+ const startIndexDel = isFirstEmpty ? startIndex : lastIndex - items . length + 1
220
+
221
+ itemsRef . current . splice ( startIndexDel , items . length , ...items )
222
+
223
+ rerender ( { } )
224
+ }
225
+
174
226
const columns : ITableColumn [ ] = [
175
227
{
176
228
id : 'type' ,
177
229
label : 'Type' ,
178
230
absoluteWidth : 'auto' ,
179
231
minWidth : 126 ,
180
- render : ( cellData : any , { nameString : name } : any ) => < GroupBadge type = { cellData } name = { name } /> ,
232
+ render : ( cellData : any , { nameString : name } : any ) => (
233
+ isUndefined ( cellData )
234
+ ? < EuiLoadingContent lines = { 1 } className = { styles . keyInfoLoading } data-testid = "type-loading" />
235
+ : < GroupBadge type = { cellData } name = { name } />
236
+ )
181
237
} ,
182
238
{
183
239
id : 'nameString' ,
184
240
label : 'Key' ,
185
241
minWidth : 100 ,
186
242
truncateText : true ,
187
- render : ( cellData : string = '' ) => {
243
+ render : ( cellData : string ) => {
244
+ if ( isUndefined ( cellData ) ) {
245
+ return (
246
+ < EuiLoadingContent
247
+ lines = { 1 }
248
+ className = { cx ( styles . keyInfoLoading , styles . keyNameLoading ) }
249
+ data-testid = "name-loading"
250
+ />
251
+ )
252
+ }
188
253
// Better to cut the long string, because it could affect virtual scroll performance
189
- const name = cellData
254
+ const name = cellData || ''
190
255
const cellContent = replaceSpaces ( name ?. substring ( 0 , 200 ) )
191
256
const tooltipContent = formatLongName ( name )
192
257
return (
@@ -214,6 +279,9 @@ const KeyList = forwardRef((props: Props, ref) => {
214
279
truncateText : true ,
215
280
alignment : TableCellAlignment . Right ,
216
281
render : ( cellData : number , { nameString : name } : GetKeyInfoResponse ) => {
282
+ if ( isUndefined ( cellData ) ) {
283
+ return < EuiLoadingContent lines = { 1 } className = { styles . keyInfoLoading } data-testid = "ttl-loading" />
284
+ }
217
285
if ( cellData === - 1 ) {
218
286
return (
219
287
< EuiTextColor color = "subdued" data-testid = { `ttl-${ name } ` } >
@@ -252,6 +320,10 @@ const KeyList = forwardRef((props: Props, ref) => {
252
320
alignment : TableCellAlignment . Right ,
253
321
textAlignment : TableCellTextAlignment . Right ,
254
322
render : ( cellData : number , { nameString : name } : GetKeyInfoResponse ) => {
323
+ if ( isUndefined ( cellData ) ) {
324
+ return < EuiLoadingContent lines = { 1 } className = { styles . keyInfoLoading } data-testid = "size-loading" />
325
+ }
326
+
255
327
if ( ! cellData ) {
256
328
return (
257
329
< EuiText color = "subdued" size = "s" style = { { maxWidth : '100%' } } data-testid = { `size-${ name } ` } >
@@ -297,15 +369,16 @@ const KeyList = forwardRef((props: Props, ref) => {
297
369
loadMoreItems = { onLoadMoreItems }
298
370
onWheel = { onWheelSearched }
299
371
loading = { loading }
300
- items = { items }
372
+ items = { itemsRef . current }
301
373
totalItemsCount = { keysState . total ? keysState . total : Infinity }
302
374
scanned = { isSearched || isFiltered ? keysState . scanned : 0 }
303
375
noItemsMessage = { getNoItemsMessage ( ) }
304
376
selectedKey = { selectedKey }
305
377
scrollTopProp = { scrollTopPosition }
306
378
setScrollTopPosition = { setScrollTopPosition }
307
379
hideFooter = { hideFooter }
308
- onRowsRendered = { ( { overscanStopIndex } ) => bufferFormatRows ( overscanStopIndex ) }
380
+ onRowsRendered = { ( { overscanStartIndex, overscanStopIndex } ) =>
381
+ onRowsRendered ( overscanStartIndex , overscanStopIndex ) }
309
382
/>
310
383
</ div >
311
384
</ div >
@@ -314,4 +387,4 @@ const KeyList = forwardRef((props: Props, ref) => {
314
387
)
315
388
} )
316
389
317
- export default KeyList
390
+ export default React . memo ( KeyList )
0 commit comments