1- import React , { FC } from 'react' ;
1+ import React , { FC , useEffect , useState } from 'react' ;
22
33import classNames from 'clsx' ;
44
@@ -9,7 +9,8 @@ import { openInFullPage, useAppEnv } from 'app/env';
99import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg' ;
1010import { ReactComponent as MaximiseIcon } from 'app/icons/maximise.svg' ;
1111import ContentContainer from 'app/layouts/ContentContainer' ;
12- import { useAccount } from 'lib/miden/front' ;
12+ import { useAccount , useAllBalances , useAllTokensBaseMetadata } from 'lib/miden/front' ;
13+ import { useWalletStore } from 'lib/store' ;
1314import { Link } from 'lib/woozie' ;
1415
1516import { HeaderSelectors } from './Header.selectors' ;
@@ -33,9 +34,39 @@ const Header: FC = () => {
3334
3435export default Header ;
3536
37+ const SyncSpinner : FC < { visible : boolean } > = ( { visible } ) => (
38+ < div
39+ className = "animate-spin sync-spinner-fade"
40+ style = { {
41+ width : 16 ,
42+ height : 16 ,
43+ border : '2px solid #E5E7EB' ,
44+ borderTopColor : '#F97316' ,
45+ borderRadius : '50%' ,
46+ opacity : visible ? 1 : 0 ,
47+ transition : 'opacity 300ms ease-in-out'
48+ } }
49+ />
50+ ) ;
51+
3652const Control : FC = ( ) => {
3753 const account = useAccount ( ) ;
3854 const { popup } = useAppEnv ( ) ;
55+ const allTokensBaseMetadata = useAllTokensBaseMetadata ( ) ;
56+ const { isLoading : isLoadingBalances } = useAllBalances ( account . publicKey , allTokensBaseMetadata ) ;
57+ const hasCompletedInitialSync = useWalletStore ( s => s . hasCompletedInitialSync ) ;
58+ const isSyncing = isLoadingBalances || ! hasCompletedInitialSync ;
59+
60+ // Show spinner only if syncing takes > 1s, then fade out over 300ms
61+ const [ showSpinner , setShowSpinner ] = useState ( false ) ;
62+ useEffect ( ( ) => {
63+ if ( isSyncing ) {
64+ const showTimeout = setTimeout ( ( ) => setShowSpinner ( true ) , 1000 ) ;
65+ return ( ) => clearTimeout ( showTimeout ) ;
66+ }
67+ const hideTimeout = setTimeout ( ( ) => setShowSpinner ( false ) , 300 ) ;
68+ return ( ) => clearTimeout ( hideTimeout ) ;
69+ } , [ isSyncing ] ) ;
3970
4071 const handleMaximiseViewClick = ( ) => {
4172 openInFullPage ( ) ;
@@ -69,6 +100,7 @@ const Control: FC = () => {
69100 </ Link >
70101 </ div >
71102 < div className = "flex items-center gap-2" >
103+ { showSpinner && < SyncSpinner visible = { isSyncing } /> }
72104 < NetworkSelect className = "self-end" />
73105 { popup && (
74106 < Button
0 commit comments