diff --git a/src/global.d.ts b/src/global.d.ts index 9b9adbf..fdbb988 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -21,9 +21,6 @@ declare global { // Handles incoming signature requests from Android receiveSignRequestFromAndroid?: (message: string) => void; - - // Handles request to create or retrieve accounts - receiveCreateOrGetAccountsRequestFromAndroid?: () => void; } } diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index ed3c8c1..be2d3ba 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -1,32 +1,32 @@ -import { useEffect } from "react"; +import { useEffect, useCallback } from "react"; + import { createWallet } from "../utils/accounts"; import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; -const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { +const useGetOrCreateAccounts = () => { const { networksData } = useNetworks(); const { getAccountsData } = useAccountsData(); + // Wrap the function in useCallback to prevent recreation on each render + const getOrCreateAccountsForChain = useCallback(async (chainId: string) => { + let accountsData = await getAccountsData(chainId); + + if (accountsData.length === 0) { + console.log("Accounts not found, creating wallet..."); + await createWallet(networksData); + accountsData = await getAccountsData(chainId); + } + + return accountsData; + }, [networksData, getAccountsData]); + useEffect(() => { const handleCreateAccounts = async (event: MessageEvent) => { if (event.data.type !== 'REQUEST_CREATE_OR_GET_ACCOUNTS') return; - let accountsData = await getAccountsData(event.data.chainId); - - if (accountsData.length === 0) { - console.log("Accounts not found, creating wallet..."); - await createWallet(networksData); - - // Re-fetch newly created accounts - accountsData = await getAccountsData(event.data.chainId); - onWalletCreated?.(); - } - - // Notify Android that accounts are ready - if (window.Android?.onAccountsReady) { - window.Android.onAccountsReady(); - } + const accountsData = await getOrCreateAccountsForChain(event.data.chainId); sendMessage( event.source as Window, 'WALLET_ACCOUNTS_DATA', @@ -35,12 +35,36 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { ); }; + const autoCreateAccounts = async () => { + const defaultChainId = networksData[0]?.chainId; + + if (!defaultChainId) { + console.log('useGetOrCreateAccounts: No default chainId found'); + return; + } + + await getOrCreateAccountsForChain(defaultChainId); + + // Notify Android that accounts are ready + if (window.Android?.onAccountsReady) { + window.Android.onAccountsReady(); + } else { + console.log('useGetOrCreateAccounts: Android bridge not available'); + } + }; + window.addEventListener('message', handleCreateAccounts); + const isAndroidWebView = !!(window.Android); + + if (isAndroidWebView) { + autoCreateAccounts(); + } + return () => { window.removeEventListener('message', handleCreateAccounts); }; - }, [networksData, getAccountsData, onWalletCreated]); + }, [networksData, getAccountsData, getOrCreateAccountsForChain]); }; -export default useGetOrCreateAccounts; +export default useGetOrCreateAccounts; \ No newline at end of file diff --git a/src/hooks/useSignRequestHandler.ts b/src/hooks/useSignRequestHandler.ts index 1b5974d..eb74704 100644 --- a/src/hooks/useSignRequestHandler.ts +++ b/src/hooks/useSignRequestHandler.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useCallback } from 'react'; +import { useEffect, useRef, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; @@ -7,82 +7,59 @@ import { useNetworks } from '../context/NetworksContext'; import { StackParamsList } from '../types'; import useGetOrCreateAccounts from './useGetOrCreateAccounts'; -/** - * Hook to manage sign request data state and Android bridge notifications - */ -export const useSignRequestData = () => { +export const useSignRequestHandler = () => { + // Navigation and context hooks + const navigation = useNavigation>(); const { selectedNetwork } = useNetworks(); const { accounts, currentIndex } = useAccounts(); - const [isDataReady, setIsDataReady] = useState(false); - - useEffect(() => { - if (selectedNetwork && accounts && accounts.length > 0) { - setIsDataReady(true); - // Notify Android when accounts are ready for signing - window.Android?.onAccountsReady?.(); - } - }, [selectedNetwork, accounts, currentIndex]); - - return { isDataReady, selectedNetwork, accounts, currentIndex }; -}; - -/** - * Hook to handle navigation for sign requests with validation - */ -export const useSignRequestNavigation = () => { - const navigation = useNavigation>(); - const { isDataReady, selectedNetwork, accounts, currentIndex } = useSignRequestData(); + + // Initialize accounts + useGetOrCreateAccounts(); + + // Use a ref for pending messages instead of state to avoid re-renders const pendingMessageRef = useRef(null); - - const navigateToSignRequest = useCallback(async (message: string) => { - // Queue message if data isn't ready yet + + // Core navigation handler + const navigateToSignRequest = useCallback((message: string) => { + // Data readiness check + const isDataReady = !!(selectedNetwork && accounts && accounts.length > 0); + if (!isDataReady) { pendingMessageRef.current = message; return; } - // Validate required data - if (!selectedNetwork) { - window.Android?.onSignatureError?.('No network selected'); - return; - } - - if (!accounts?.length) { - window.Android?.onSignatureError?.('No accounts available'); - return; - } - - const currentAccount = accounts[currentIndex]; - if (!currentAccount) { - window.Android?.onSignatureError?.('Current account not found'); - return; - } - - // Validate network properties - if (!selectedNetwork.namespace || !selectedNetwork.chainId) { - window.Android?.onSignatureError?.('Network missing required properties'); - return; - } - try { - // Build and validate signing path - const path = `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`; - const pathRegex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/; - - if (!pathRegex.test(path)) { - window.Android?.onSignatureError?.('Invalid path format'); + // Validation checks + if (!selectedNetwork?.namespace || !selectedNetwork?.chainId) { + window.Android?.onSignatureError?.('Invalid network configuration'); return; } + if (!accounts?.length) { + window.Android?.onSignatureError?.('No accounts available'); + return; + } + + const currentAccount = accounts[currentIndex]; + if (!currentAccount) { + window.Android?.onSignatureError?.('Current account not found'); + return; + } + + // Create the path and validate with regex + const path = `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`; + const pathRegex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/; const match = path.match(pathRegex); + if (!match) { - window.Android?.onSignatureError?.('Failed to parse path'); + window.Android?.onSignatureError?.('Invalid signing path'); return; } const [, pathNamespace, pathChainId, pathAddress, pathMessage] = match; - // Navigate to sign request screen + // Reset navigation stack and navigate to sign request navigation.reset({ index: 0, routes: [ @@ -102,55 +79,31 @@ export const useSignRequestNavigation = () => { } catch (error) { window.Android?.onSignatureError?.(`Navigation error: ${error}`); } - }, [isDataReady, selectedNetwork, accounts, currentIndex, navigation]); + }, [selectedNetwork, accounts, currentIndex, navigation]); - // Process any pending message when data becomes ready + // Setup Android bridge and handle pending messages useEffect(() => { - if (pendingMessageRef.current && isDataReady) { + // Setup global function to receive sign requests from Android + window.receiveSignRequestFromAndroid = navigateToSignRequest; + + // Process any pending messages whenever dependencies change + if (pendingMessageRef.current && selectedNetwork && accounts?.length > 0) { const message = pendingMessageRef.current; pendingMessageRef.current = null; navigateToSignRequest(message); } - }, [isDataReady, navigateToSignRequest]); - - return { navigateToSignRequest }; -}; - -/** - * Hook to set up Android bridge communication - */ -export const useAndroidBridge = () => { - const { navigateToSignRequest } = useSignRequestNavigation(); - - useEffect(() => { - // Handle sign requests from Android - window.receiveSignRequestFromAndroid = (message: string) => { - navigateToSignRequest(message); - }; - - // Set up accounts request handler - window.receiveCreateOrGetAccountsRequestFromAndroid = () => { - // Handled by useGetOrCreateAccounts hook - }; - - // Initialize Android bridge + + // Notify Android that JS bridge is ready if (window.Android) { - setTimeout(() => window.Android?.onJsBridgeReady?.(), 100); + const timeoutId = setTimeout(() => window.Android?.onJsBridgeReady?.(), 100); + return () => { + clearTimeout(timeoutId); + window.receiveSignRequestFromAndroid = undefined; + }; } - + return () => { window.receiveSignRequestFromAndroid = undefined; - window.receiveCreateOrGetAccountsRequestFromAndroid = undefined; }; - }, [navigateToSignRequest]); -}; - -/** - * Main hook that combines all sign request handling functionality - */ -export const useSignRequestHandler = () => { - useGetOrCreateAccounts(); - useSignRequestData(); - useSignRequestNavigation(); - useAndroidBridge(); -}; + }, [navigateToSignRequest, selectedNetwork, accounts]); +}; \ No newline at end of file diff --git a/src/screens/SignMessage.tsx b/src/screens/SignMessage.tsx index f7919b9..1377128 100644 --- a/src/screens/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -56,4 +56,4 @@ const SignMessage = ({ route }: SignProps) => { ); }; -export default SignMessage; \ No newline at end of file +export default SignMessage;