@@ -12,7 +12,7 @@ import { ExplorerLink } from '../components/ExplorerLink';
1212import { ExplorerSearch } from '../components/ExplorerSearch' ;
1313import type { BlockRow , StatsRow , TxRow } from '../library/api-types' ;
1414import { vibenetApi } from '../library/client' ;
15- import { timeAgoFromSeconds } from '../library/explorer' ;
15+ import { timeAgoFromMilliseconds , timeAgoFromSeconds } from '../library/explorer' ;
1616
1717const NEW_ROW_HIGHLIGHT = 'bg-bds-blue-0' ;
1818const TH =
@@ -62,12 +62,24 @@ export default function ExplorerPage() {
6262
6363 useEffect ( ( ) => {
6464 let cancelled = false ;
65+ let inFlight = false ;
66+ let pollTimer : number | undefined ;
67+ let rowHighlightTimer : number | undefined ;
68+ let statHighlightTimer : number | undefined ;
69+ const controller = new AbortController ( ) ;
70+
71+ function schedule ( ) {
72+ if ( cancelled || document . hidden ) return ;
73+ pollTimer = window . setTimeout ( ( ) => void load ( ) , 1_000 ) ;
74+ }
6575
6676 async function load ( ) {
77+ if ( cancelled || document . hidden || inFlight ) return ;
78+ inFlight = true ;
6779 try {
6880 const [ blocksRes , statsRes ] = await Promise . all ( [
69- vibenetApi . explorer . blocks ( ) ,
70- vibenetApi . explorer . stats ( ) ,
81+ vibenetApi . explorer . blocks ( controller . signal ) ,
82+ vibenetApi . explorer . stats ( controller . signal ) ,
7183 ] ) ;
7284 if ( cancelled ) return ;
7385
@@ -84,7 +96,8 @@ export default function ExplorerPage() {
8496 } ) ;
8597 if ( fresh . size > 0 ) {
8698 setNewKeys ( fresh ) ;
87- window . setTimeout ( ( ) => setNewKeys ( new Set ( ) ) , 1300 ) ;
99+ window . clearTimeout ( rowHighlightTimer ) ;
100+ rowHighlightTimer = window . setTimeout ( ( ) => setNewKeys ( new Set ( ) ) , 450 ) ;
88101 }
89102 const changed = new Set < string > ( ) ;
90103 const prev = lastStats . current ;
@@ -95,7 +108,8 @@ export default function ExplorerPage() {
95108 }
96109 if ( changed . size > 0 ) {
97110 setStatHighlight ( changed ) ;
98- window . setTimeout ( ( ) => setStatHighlight ( new Set ( ) ) , 1300 ) ;
111+ window . clearTimeout ( statHighlightTimer ) ;
112+ statHighlightTimer = window . setTimeout ( ( ) => setStatHighlight ( new Set ( ) ) , 450 ) ;
99113 }
100114 }
101115
@@ -113,14 +127,30 @@ export default function ExplorerPage() {
113127 setFetchError ( 'Could not reach the explorer API. Retrying…' ) ;
114128 setLoading ( false ) ;
115129 }
130+ } finally {
131+ inFlight = false ;
132+ schedule ( ) ;
133+ }
134+ }
135+
136+ function handleVisibilityChange ( ) {
137+ if ( document . hidden ) {
138+ window . clearTimeout ( pollTimer ) ;
139+ } else if ( ! inFlight ) {
140+ window . clearTimeout ( pollTimer ) ;
141+ void load ( ) ;
116142 }
117143 }
118144
145+ document . addEventListener ( 'visibilitychange' , handleVisibilityChange ) ;
119146 void load ( ) ;
120- const timer = window . setInterval ( ( ) => void load ( ) , 5_000 ) ;
121147 return ( ) => {
122148 cancelled = true ;
123- window . clearInterval ( timer ) ;
149+ controller . abort ( ) ;
150+ document . removeEventListener ( 'visibilitychange' , handleVisibilityChange ) ;
151+ window . clearTimeout ( pollTimer ) ;
152+ window . clearTimeout ( rowHighlightTimer ) ;
153+ window . clearTimeout ( statHighlightTimer ) ;
124154 } ;
125155 } , [ ] ) ;
126156
@@ -144,7 +174,7 @@ export default function ExplorerPage() {
144174 < Card
145175 key = { stat . key }
146176 className = { cn (
147- 'bg-background p-4 transition-colors duration-700 dark:bg-white/5' ,
177+ 'bg-background p-4 transition-colors duration-300 motion-reduce:transition-none dark:bg-white/5' ,
148178 statHighlight . has ( stat . key ) && NEW_ROW_HIGHLIGHT ,
149179 ) }
150180 >
@@ -178,7 +208,7 @@ export default function ExplorerPage() {
178208 key = { b . hash }
179209 aria-label = { `Block ${ b . number } ` }
180210 className = { cn (
181- 'border-b border-bds-gray-10 transition-colors duration-1000 last:border-0 hover:bg-bds-gray-5/50 dark:border-white/10' ,
211+ 'border-b border-bds-gray-10 transition-colors duration-300 motion-reduce:transition-none last:border-0 hover:bg-bds-gray-5/50 dark:border-white/10' ,
182212 newKeys . has ( `block-${ b . hash } ` ) && NEW_ROW_HIGHLIGHT ,
183213 ) }
184214 >
@@ -192,7 +222,9 @@ export default function ExplorerPage() {
192222 'whitespace-nowrap text-right text-bds-gray-60 dark:text-bds-gray-40' ,
193223 ) }
194224 >
195- { timeAgoFromSeconds ( b . timestamp ) }
225+ { b . timestamp_ms != null
226+ ? timeAgoFromMilliseconds ( b . timestamp_ms )
227+ : timeAgoFromSeconds ( b . timestamp ) }
196228 </ td >
197229 </ tr >
198230 ) ) }
@@ -223,7 +255,7 @@ export default function ExplorerPage() {
223255 key = { tx . hash }
224256 aria-label = { `Transaction ${ tx . hash } ` }
225257 className = { cn (
226- 'border-b border-bds-gray-10 transition-colors duration-1000 last:border-0 hover:bg-bds-gray-5/50 dark:border-white/10' ,
258+ 'border-b border-bds-gray-10 transition-colors duration-300 motion-reduce:transition-none last:border-0 hover:bg-bds-gray-5/50 dark:border-white/10' ,
227259 newKeys . has ( `tx-${ tx . hash } ` ) && NEW_ROW_HIGHLIGHT ,
228260 ) }
229261 >
@@ -260,7 +292,7 @@ export default function ExplorerPage() {
260292 < div
261293 key = { tx . hash }
262294 className = { cn (
263- 'flex flex-col gap-1.5 border-b border-bds-gray-10 px-1 py-3 transition-colors duration-1000 last:border-0 dark:border-white/10' ,
295+ 'flex flex-col gap-1.5 border-b border-bds-gray-10 px-1 py-3 transition-colors duration-300 motion-reduce:transition-none last:border-0 dark:border-white/10' ,
264296 newKeys . has ( `tx-${ tx . hash } ` ) && NEW_ROW_HIGHLIGHT ,
265297 ) }
266298 >
0 commit comments