import { useContext, useCallback, useState, useEffect } from 'react' import { ConnectionContext } from '../providers/ConnectionProvider' import type { NetworkId, UseConnectionReturn, WalletError } from '@meshconnect/uwc-types' /** * Hook for managing wallet connections with both injected and WalletConnect modes * @param walletId - The ID of the wallet to connect to (e.g., 'metamask', 'trust') * @returns Connection methods and state for both injected and WalletConnect modes * @throws Error if used outside of ConnectionProvider * @example * ```tsx * const { walletConnect, injected } = useConnection('metamask') * * // Connect with browser extension * await injected.connect('eip155:1') * * // Connect with WalletConnect QR code * await walletConnect.connect('eip155:1') * if (walletConnect.connectionURI) { * // Display QR code with walletConnect.connectionURI * } * ``` */ export function useConnection(walletId: string): UseConnectionReturn { const context = useContext(ConnectionContext) if (!context) { throw new Error('useConnection must be used within a ConnectionProvider') } const { connector, isReady } = context const [walletConnectLoading, setWalletConnectLoading] = useState(false) const [injectedLoading, setInjectedLoading] = useState(false) const [walletConnectError, setWalletConnectError] = useState< WalletError | undefined >() const [injectedError, setInjectedError] = useState() const [walletConnectURI, setWalletConnectURI] = useState() const [tonConnectURI, setTonConnectURI] = useState() const [tonConnectLoading, setTonConnectLoading] = useState(false) const [tonConnectError, setTonConnectError] = useState< WalletError | undefined >() const [walletConnectAvailable, setWalletConnectAvailable] = useState(false) const [injectedAvailable, setInjectedAvailable] = useState(false) const [tonConnectAvailable, setTonConnectAvailable] = useState(false) // Update connection URI and availability when they change useEffect(() => { const checkState = () => { const uri = connector.getConnectionURI() if (walletConnectLoading) { setWalletConnectURI(uri) } if (tonConnectLoading) { setTonConnectURI(uri) } // Check availability for both connection modes const wcAvailable = connector.isConnectionModeAvailable( 'walletConnect', walletId ) const injAvailable = connector.isConnectionModeAvailable( 'injected', walletId ) const tonAvailable = connector.isConnectionModeAvailable( 'tonConnect', walletId ) setWalletConnectAvailable(wcAvailable) setInjectedAvailable(injAvailable) setTonConnectAvailable(tonAvailable) } // Check initially and on state changes checkState() const unsubscribe = connector.subscribe(checkState) return unsubscribe }, [connector, walletId, walletConnectLoading, tonConnectLoading]) const connectWalletConnect = useCallback( async (networkId?: NetworkId) => { setWalletConnectLoading(true) setWalletConnectError(undefined) setWalletConnectURI(undefined) // Keep retrying if the proposal expires let retrying = true while (retrying) { try { await connector.connect('walletConnect', walletId, networkId) retrying = false } catch (error) { const walletError = error as WalletError // If the proposal expired, automatically retry with a new connection if (walletError.type === 'expired') { // Continue the loop to retry continue } // For non-expired errors, stop retrying and propagate the error setWalletConnectError(walletError) setWalletConnectLoading(false) throw error } } setWalletConnectLoading(false) setWalletConnectURI(undefined) }, [connector, walletId] ) const connectInjected = useCallback( async (networkId?: NetworkId) => { setInjectedLoading(true) setInjectedError(undefined) try { await connector.connect('injected', walletId, networkId) } catch (error) { const walletError = error as WalletError setInjectedError(walletError) throw error } finally { setInjectedLoading(false) } }, [connector, walletId] ) const connectTonConnect = useCallback( async (networkId?: NetworkId) => { setTonConnectLoading(true) setTonConnectError(undefined) setTonConnectURI(undefined) try { await connector.connect('tonConnect', walletId, networkId) setTonConnectURI(undefined) } catch (error) { const walletError = error as WalletError setTonConnectError(walletError) throw error } finally { setTonConnectLoading(false) } }, [connector, walletId] ) return { walletConnect: { connect: connectWalletConnect, connectionURI: walletConnectURI, isLoading: walletConnectLoading, error: walletConnectError, available: walletConnectAvailable }, injected: { connect: connectInjected, isLoading: injectedLoading, error: injectedError, available: injectedAvailable }, tonConnect: { connect: connectTonConnect, connectionURI: tonConnectURI, isLoading: tonConnectLoading, error: tonConnectError, available: tonConnectAvailable }, isReady } }