@@ -8,13 +8,12 @@ import {
88 DropdownMenuSeparator ,
99 DropdownMenuTrigger ,
1010} from "@/components/ui/dropdown-menu" ;
11- import { useWallet , useWalletList } from "@meshsdk/react" ;
11+ import { useWallet , useWalletList , useNetwork , useAssets } from "@meshsdk/react" ;
1212import { useSiteStore } from "@/lib/zustand/site" ;
13- import { useEffect , useRef , useState } from "react" ;
13+ import { useEffect } from "react" ;
1414import useUser from "@/hooks/useUser" ;
1515import { useUserStore } from "@/lib/zustand/user" ;
1616import { getProvider } from "@/utils/get-provider" ;
17- import { Asset } from "@meshsdk/core" ;
1817
1918export default function ConnectWallet ( ) {
2019 const setNetwork = useSiteStore ( ( state ) => state . setNetwork ) ;
@@ -28,277 +27,69 @@ export default function ConnectWallet() {
2827
2928 const wallets = useWalletList ( ) ;
3029 const { connect, connected, wallet, name } = useWallet ( ) ;
30+ const networkId = useNetwork ( ) ;
31+ const assets = useAssets ( ) ;
3132 const network = useSiteStore ( ( state ) => state . network ) ;
32- const connectingRef = useRef ( false ) ;
33- const fetchingAssetsRef = useRef ( false ) ;
34- const lastWalletIdRef = useRef < string | null > ( null ) ;
35- const fetchingNetworkRef = useRef ( false ) ;
36- const lastNetworkWalletRef = useRef < string | null > ( null ) ;
37- const userAssets = useUserStore ( ( state ) => state . userAssets ) ;
38- const [ walletsLoading , setWalletsLoading ] = useState ( true ) ;
39- const [ isMounted , setIsMounted ] = useState ( false ) ;
40- const walletsRetryTimeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
41-
42- // Ensure component only runs on client side (important for SSR/production)
43- useEffect ( ( ) => {
44- setIsMounted ( true ) ;
45- } , [ ] ) ;
4633
4734 async function connectWallet ( walletId : string ) {
4835 setPastWallet ( walletId ) ;
4936 await connect ( walletId ) ;
5037 }
5138
52- // Monitor wallet list loading state and retry if empty
39+ // Auto-connect if user had connected before
5340 useEffect ( ( ) => {
54- // Only run on client side
55- if ( ! isMounted ) return ;
56-
57- if ( wallets . length > 0 ) {
58- setWalletsLoading ( false ) ;
59- // Clear any pending retry timeout
60- if ( walletsRetryTimeoutRef . current ) {
61- clearTimeout ( walletsRetryTimeoutRef . current ) ;
62- walletsRetryTimeoutRef . current = null ;
63- }
64- } else if ( ! connected ) {
65- // Only show loading state if not connected (wallets might load after connection)
66- // If wallets are empty, wait a bit and check again
67- // This handles cases where wallet extensions load asynchronously
68- if ( walletsRetryTimeoutRef . current === null ) {
69- setWalletsLoading ( true ) ;
70- let retryCount = 0 ;
71- const maxRetries = 10 ; // Try for up to 10 seconds in production (longer timeout)
72-
73- const checkWallets = ( ) => {
74- retryCount ++ ;
75- // Re-check wallets array length (it might have updated)
76- if ( wallets . length > 0 ) {
77- setWalletsLoading ( false ) ;
78- walletsRetryTimeoutRef . current = null ;
79- return ;
80- }
81-
82- if ( retryCount < maxRetries ) {
83- walletsRetryTimeoutRef . current = setTimeout ( checkWallets , 1000 ) ; // Check every second
84- } else {
85- console . warn ( "Wallet list still empty after retries, wallets may not be available" ) ;
86- setWalletsLoading ( false ) ; // Stop showing loading state
87- walletsRetryTimeoutRef . current = null ;
88- }
89- } ;
90-
91- walletsRetryTimeoutRef . current = setTimeout ( checkWallets , 1000 ) ;
92- }
93- } else {
94- // If connected but no wallets, they're probably not needed
95- setWalletsLoading ( false ) ;
96- }
97-
98- return ( ) => {
99- if ( walletsRetryTimeoutRef . current ) {
100- clearTimeout ( walletsRetryTimeoutRef . current ) ;
101- walletsRetryTimeoutRef . current = null ;
102- }
103- } ;
104- } , [ wallets , connected , isMounted ] ) ;
105-
106- /**
107- * Try to connect the wallet when the user loads the application, if user had connected before,
108- * but only if:
109- * 1. Component is mounted (client-side only)
110- * 2. The wallet list has been loaded (wallets.length > 0)
111- * 3. The pastWallet exists in the available wallets
112- * 4. We're not already connected
113- * 5. We're not already attempting to connect
114- */
115- useEffect ( ( ) => {
116- // Only run on client side
117- if ( ! isMounted ) return ;
118-
119- async function handleAutoWalletConnect ( ) {
120- // Don't attempt if already connected or already connecting
121- if ( connected || connectingRef . current ) {
122- return ;
123- }
124-
125- // Don't attempt if no pastWallet is stored
126- if ( ! pastWallet ) {
127- return ;
128- }
129-
130- // Wait for wallet list to be available
131- // If wallets array is empty, wallets might still be loading
132- // The effect will re-run when wallets become available
133- if ( wallets . length === 0 ) {
134- console . log ( "Waiting for wallets to load..." ) ;
135- return ;
136- }
137-
138- // Check if the pastWallet exists in the available wallets
41+ if ( pastWallet && ! connected && wallets . length > 0 ) {
13942 const walletExists = wallets . some ( ( w ) => w . id === pastWallet ) ;
140- if ( ! walletExists ) {
141- console . warn (
142- `Stored wallet "${ pastWallet } " not found in available wallets. Clearing stored wallet.` ,
143- ) ;
43+ if ( walletExists ) {
44+ connect ( pastWallet ) . catch ( ( ) => {
45+ setPastWallet ( undefined ) ;
46+ } ) ;
47+ } else {
14448 setPastWallet ( undefined ) ;
145- return ;
146- }
147-
148- // Attempt to connect
149- connectingRef . current = true ;
150- try {
151- console . log ( `Attempting to auto-connect wallet: ${ pastWallet } ` ) ;
152- await connect ( pastWallet ) ;
153- console . log ( `Successfully auto-connected wallet: ${ pastWallet } ` ) ;
154- } catch ( e ) {
155- console . error (
156- `Failed to auto-connect wallet "${ pastWallet } ":` ,
157- e instanceof Error ? e . message : e ,
158- ) ;
159- setPastWallet ( undefined ) ;
160- } finally {
161- connectingRef . current = false ;
16249 }
16350 }
51+ } , [ pastWallet , connected , wallets , connect , setPastWallet ] ) ;
16452
165- handleAutoWalletConnect ( ) ;
166- } , [ pastWallet , connected , wallets , connect , setPastWallet , isMounted ] ) ;
167-
53+ // Sync network from hook to store
16854 useEffect ( ( ) => {
169- async function lookupWalletAssets ( ) {
170- if ( ! wallet ) return ;
171-
172- // Prevent multiple simultaneous calls
173- if ( fetchingAssetsRef . current ) {
174- console . log ( "Assets fetch already in progress, skipping..." ) ;
175- return ;
176- }
177-
178- // Use wallet name as identifier (doesn't require API call)
179- const walletId = name || "unknown" ;
180-
181- // Skip if we've already fetched for this wallet and have assets
182- if ( lastWalletIdRef . current === walletId && userAssets . length > 0 ) {
183- console . log ( "Assets already loaded for this wallet, skipping fetch" ) ;
184- return ;
185- }
55+ if ( networkId !== undefined && networkId !== null ) {
56+ setNetwork ( networkId ) ;
57+ }
58+ } , [ networkId , setNetwork ] ) ;
18659
187- fetchingAssetsRef . current = true ;
188- lastWalletIdRef . current = walletId ;
60+ // Process assets and fetch metadata
61+ useEffect ( ( ) => {
62+ if ( ! connected || ! assets || assets . length === 0 || networkId === undefined || networkId === null ) return ;
18963
64+ async function processAssets ( ) {
65+ if ( ! assets || networkId === undefined || networkId === null ) return ;
66+
19067 try {
191- console . log ( "Fetching wallet balance..." ) ;
192- const assets = await wallet . getBalance ( ) ;
193- // Use network from store if available, otherwise fetch it
194- let networkId = network ;
195- if ( ! networkId ) {
196- try {
197- networkId = await wallet . getNetworkId ( ) ;
198- setNetwork ( networkId ) ;
199- } catch ( networkError ) {
200- console . error ( "Error getting network ID for provider:" , networkError ) ;
201- // Use default network if we can't get it
202- networkId = 0 ; // Mainnet default
203- }
204- }
20568 const provider = getProvider ( networkId ) ;
206- const fetchedAssets : Asset [ ] = [ ] ;
207- if ( assets ) {
208- for ( const asset of assets ) {
209- fetchedAssets . push ( {
210- unit : asset . unit ,
211- quantity : asset . quantity ,
212- } ) ;
213- if ( asset . unit === "lovelace" ) continue ;
214- try {
215- const assetInfo = await provider . get ( `/assets/${ asset . unit } ` ) ;
216- setUserAssetMetadata (
69+ setUserAssets ( assets ) ;
70+
71+ for ( const asset of assets ) {
72+ if ( asset . unit === "lovelace" ) continue ;
73+ try {
74+ const assetInfo = await provider . get ( `/assets/${ asset . unit } ` ) ;
75+ setUserAssetMetadata (
76+ asset . unit ,
77+ assetInfo ?. metadata ?. name ||
78+ assetInfo ?. onchain_metadata ?. name ||
21779 asset . unit ,
218- assetInfo ?. metadata ?. name ||
219- assetInfo ?. onchain_metadata ?. name ||
220- asset . unit ,
221- assetInfo ?. metadata ?. decimals || 0 ,
222- ) ;
223- } catch ( assetError ) {
224- // If asset metadata fetch fails, continue with other assets
225- console . warn ( `Failed to fetch metadata for asset ${ asset . unit } :` , assetError ) ;
226- }
80+ assetInfo ?. metadata ?. decimals || 0 ,
81+ ) ;
82+ } catch ( error ) {
83+ // Continue if asset metadata fetch fails
22784 }
228- setUserAssets ( fetchedAssets ) ;
229- console . log ( "Successfully fetched wallet assets" ) ;
230- }
231- // Reset the fetching flag after successful fetch
232- fetchingAssetsRef . current = false ;
233- } catch ( error ) {
234- console . error ( "Error looking up wallet assets:" , error ) ;
235- // If it's a rate limit error, don't clear the ref immediately
236- // to prevent rapid retries - wait 5 seconds before allowing retry
237- if ( error instanceof Error && error . message . includes ( "too many requests" ) ) {
238- console . warn ( "Rate limit hit, will retry after delay" ) ;
239- setTimeout ( ( ) => {
240- fetchingAssetsRef . current = false ;
241- } , 5000 ) ;
242- return ;
24385 }
244- // For other errors, reset immediately so we can retry
245- fetchingAssetsRef . current = false ;
246- }
247- }
248-
249- async function handleNetworkChange ( ) {
250- if ( ! connected || ! wallet ) return ;
251-
252- // Prevent multiple simultaneous network ID fetches
253- if ( fetchingNetworkRef . current ) {
254- console . log ( "Network ID fetch already in progress, skipping..." ) ;
255- return ;
256- }
257-
258- // Use wallet name as identifier (doesn't require API call)
259- const walletId = name || "unknown" ;
260-
261- // Skip if we've already fetched network for this wallet
262- if ( lastNetworkWalletRef . current === walletId && network !== undefined ) {
263- console . log ( "Network ID already fetched for this wallet, skipping" ) ;
264- return ;
265- }
266-
267- fetchingNetworkRef . current = true ;
268- lastNetworkWalletRef . current = walletId ;
269-
270- try {
271- console . log ( "Fetching network ID..." ) ;
272- const networkId = await wallet . getNetworkId ( ) ;
273- setNetwork ( networkId ) ;
274- console . log ( "Successfully fetched network ID:" , networkId ) ;
275- fetchingNetworkRef . current = false ;
27686 } catch ( error ) {
277- console . error ( "Error getting network ID:" , error ) ;
278- // If rate limited, wait before retry
279- if ( error instanceof Error && error . message . includes ( "too many requests" ) ) {
280- console . warn ( "Rate limit hit for network ID, will retry after delay" ) ;
281- setTimeout ( ( ) => {
282- fetchingNetworkRef . current = false ;
283- } , 5000 ) ;
284- return ;
285- }
286- fetchingNetworkRef . current = false ;
87+ console . error ( "Error processing assets:" , error ) ;
28788 }
28889 }
28990
290- async function getWalletAssets ( ) {
291- if ( wallet && connected ) {
292- await lookupWalletAssets ( ) ;
293- }
294- }
295-
296- // Only run if wallet and connected state are available, and component is mounted
297- if ( isMounted && wallet && connected ) {
298- handleNetworkChange ( ) ;
299- getWalletAssets ( ) ;
300- }
301- } , [ connected , wallet , name , setNetwork , setUserAssets , setUserAssetMetadata , isMounted ] ) ;
91+ processAssets ( ) ;
92+ } , [ assets , connected , networkId , setUserAssets , setUserAssetMetadata ] ) ;
30293
30394 return (
30495 < DropdownMenu >
@@ -313,11 +104,7 @@ export default function ConnectWallet() {
313104 < DropdownMenuContent align = "end" >
314105 < DropdownMenuLabel > Select Wallet</ DropdownMenuLabel >
315106 < DropdownMenuSeparator />
316- { walletsLoading && wallets . length === 0 ? (
317- < DropdownMenuItem disabled >
318- < span className = "text-muted-foreground" > Loading wallets...</ span >
319- </ DropdownMenuItem >
320- ) : wallets . length === 0 ? (
107+ { wallets . length === 0 ? (
321108 < DropdownMenuItem disabled >
322109 < span className = "text-muted-foreground" >
323110 No wallets available. Please install a Cardano wallet extension.
0 commit comments