@@ -18,6 +18,7 @@ import {
18
18
Typography ,
19
19
} from '@mui/material' ;
20
20
import {
21
+ CHAIN_INFO_MAP ,
21
22
MISS_THRESHOLD_IN_MINS ,
22
23
MISS_THRESHOLD_LABEL ,
23
24
explorerBlock ,
@@ -32,6 +33,7 @@ import { CloudGovernorInfo } from '../hooks/useCloudGovernorInfo';
32
33
import useMonitorInfo , { MissesByChain , ObservedMessage } from '../hooks/useMonitorInfo' ;
33
34
import { DataWrapper , getEmptyDataWrapper , receiveDataWrapper } from '../utils/DataWrapper' ;
34
35
import CollapsibleSection from './CollapsibleSection' ;
36
+ import { CHAIN_ICON_MAP } from '../utils/consts' ;
35
37
36
38
const inlineIconButtonSx : SxProps < Theme > = {
37
39
fontSize : '1em' ,
@@ -373,11 +375,87 @@ function Misses({
373
375
}
374
376
375
377
function Monitor ( { governorInfo } : { governorInfo ?: CloudGovernorInfo | null } ) {
378
+ const network = useCurrentEnvironment ( ) ;
379
+ const {
380
+ settings : { showAllMisses } ,
381
+ } = useSettingsContext ( ) ;
376
382
const { lastBlockByChainWrapper, messageCountsWrapper, missesWrapper } = useMonitorInfo ( ) ;
377
383
const lastBlockByChain = lastBlockByChainWrapper . data ;
378
384
const messageCounts = messageCountsWrapper . data ;
385
+ const misses = missesWrapper . data ;
386
+ const missesByChain = useMemo ( ( ) => {
387
+ const now = new Date ( ) ;
388
+ now . setMinutes ( now . getMinutes ( ) - MISS_THRESHOLD_IN_MINS ) ;
389
+ const missThreshold = now . toISOString ( ) ;
390
+ return misses
391
+ ? Object . entries ( misses ) . reduce < { [ chainId : number ] : number } > ( ( counts , [ chain , info ] ) => {
392
+ const filteredMisses = showAllMisses
393
+ ? info . messages
394
+ : info . messages
395
+ . filter ( ( message ) => message . timestamp < missThreshold )
396
+ . filter (
397
+ ( message ) =>
398
+ ! governorInfo ?. enqueuedVAAs . some (
399
+ ( enqueuedVAA ) =>
400
+ enqueuedVAA . emitterChain === message . chain &&
401
+ enqueuedVAA . emitterAddress === message . emitter &&
402
+ enqueuedVAA . sequence === message . seq
403
+ )
404
+ ) ;
405
+ return filteredMisses . length === 0
406
+ ? counts
407
+ : { ...counts , [ Number ( chain ) as ChainId ] : filteredMisses . length } ;
408
+ } , { } )
409
+ : { } ;
410
+ } , [ governorInfo ?. enqueuedVAAs , misses , showAllMisses ] ) ;
379
411
return (
380
- < CollapsibleSection header = "Monitor" >
412
+ < CollapsibleSection
413
+ defaultExpanded = { false }
414
+ header = {
415
+ < Box
416
+ sx = { {
417
+ display : 'flex' ,
418
+ alignItems : 'center' ,
419
+ paddingRight : 1 ,
420
+ } }
421
+ >
422
+ < Box > Monitor</ Box >
423
+ < Box flexGrow = { 1 } />
424
+ < Box sx = { { display : 'flex' , alignItems : 'center' , flexWrap : 'wrap' } } >
425
+ { Object . keys ( missesByChain )
426
+ . sort ( )
427
+ . map ( ( chainId ) => (
428
+ < Box key = { chainId } display = "flex" alignItems = "center" >
429
+ < Box
430
+ ml = { 2 }
431
+ display = "flex"
432
+ alignItems = "center"
433
+ borderRadius = "50%"
434
+ sx = { { p : 0.5 , backgroundColor : 'rgba(0,0,0,0.5)' } }
435
+ >
436
+ { CHAIN_ICON_MAP [ chainId ] ? (
437
+ < img
438
+ src = { CHAIN_ICON_MAP [ chainId ] }
439
+ alt = {
440
+ CHAIN_INFO_MAP [ network ] [ chainId ] ?. name ||
441
+ coalesceChainName ( Number ( chainId ) as ChainId )
442
+ }
443
+ width = { 24 }
444
+ height = { 24 }
445
+ />
446
+ ) : (
447
+ < Typography variant = "body2" > { chainId } </ Typography >
448
+ ) }
449
+ </ Box >
450
+ < Typography variant = "h6" component = "strong" sx = { { ml : 0.5 } } >
451
+ { missesByChain [ Number ( chainId ) ] }
452
+ </ Typography >
453
+ </ Box >
454
+ ) ) }
455
+ </ Box >
456
+ </ Box >
457
+ }
458
+ >
381
459
< Box mt = { 2 } >
382
460
< Card >
383
461
< Misses governorInfo = { governorInfo } missesWrapper = { missesWrapper } />
0 commit comments