From d9011dbdb2b2369dd901c66146a3cdff974310e7 Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Fri, 4 Apr 2025 17:11:45 +0530 Subject: [PATCH 01/12] Sign message from app --- src/components/SignRequestHandler.tsx | 75 +++++++++++++++++++++++++++ src/hooks/useGetOrCreateAccounts.ts | 40 ++++++++++++-- src/screens/HomeScreen.tsx | 33 ++++++++++-- src/types.ts | 1 + 4 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 src/components/SignRequestHandler.tsx diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx new file mode 100644 index 0000000..948a59a --- /dev/null +++ b/src/components/SignRequestHandler.tsx @@ -0,0 +1,75 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigation } from '@react-navigation/native'; +import { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useAccounts } from '../context/AccountsContext'; +import { useNetworks } from '../context/NetworksContext'; +import { StackParamsList } from '../types'; +import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; + +declare global { + interface Window { + receiveSignRequestFromAndroid?: (message: string) => void; + } +} + +export const SignRequestHandler: React.FC = () => { + const navigation = useNavigation>(); + const { accounts, currentIndex } = useAccounts(); + const { selectedNetwork } = useNetworks(); + const [pendingMessage, setPendingMessage] = useState(null); + + useGetOrCreateAccounts(); + + useEffect(() => { + if (pendingMessage && selectedNetwork && accounts && accounts.length > 0) { + navigation.reset({ + index: 0, + routes: [ + { + name: 'SignRequest', + params: { + address: accounts[currentIndex].address, + message: pendingMessage, + requestEvent: null, + path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(pendingMessage)}` + } + } + ] + }); + setPendingMessage(null); + } + }, [pendingMessage, selectedNetwork, accounts, currentIndex, navigation]); + + useEffect(() => { + window.receiveSignRequestFromAndroid = (message: string) => { + if (!selectedNetwork || !accounts || accounts.length === 0) { + // Store the message and wait for wallet creation + setPendingMessage(message); + return; + } + + navigation.reset({ + index: 0, + routes: [ + { + name: 'SignRequest', + params: { + address: accounts[currentIndex].address, + message: message, + requestEvent: null, + path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(message)}` + } + } + ] + }); + }; + + return () => { + if ('receiveSignRequestFromAndroid' in window) { + window.receiveSignRequestFromAndroid = undefined; + } + }; + }, [navigation, accounts, currentIndex, selectedNetwork]); + + return null; +}; \ No newline at end of file diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index 32a7815..4d16dd8 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -1,11 +1,20 @@ import { useEffect } from "react"; - import { createWallet } from "../utils/accounts"; import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; -const useGetOrCreateAccounts = () => { +declare global { + interface Window { + Android?: { + onSignatureComplete?: (signature: string) => void; + onSignatureError?: (error: string) => void; + onSignatureCancelled?: () => void; + }; + } +} + +const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { const { networksData } = useNetworks(); const { getAccountsData } = useAccountsData(); @@ -21,6 +30,8 @@ const useGetOrCreateAccounts = () => { // Re-fetch newly created accounts accountsData = await getAccountsData(event.data.chainId); + + onWalletCreated?.(); } sendMessage( @@ -30,12 +41,33 @@ const useGetOrCreateAccounts = () => { ); }; + const autoCreateAccounts = async () => { + const defaultChainId = networksData[0]?.chainId; + if (!defaultChainId) return; + + let accountsData = await getAccountsData(defaultChainId); + + if (accountsData.length === 0) { + console.log("Auto-creating wallet..."); + await createWallet(networksData); + accountsData = await getAccountsData(defaultChainId); + + onWalletCreated?.(); + } + }; + window.addEventListener('message', handleCreateAccounts); + const isAndroidWebView = !!(window.Android); + + if (isAndroidWebView) { + autoCreateAccounts(); + } + return () => { window.removeEventListener('message', handleCreateAccounts); }; - }, [networksData, getAccountsData]); + }, [networksData, getAccountsData, onWalletCreated]); }; -export default useGetOrCreateAccounts; +export default useGetOrCreateAccounts; \ No newline at end of file diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index ca4ce78..30f2af0 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -18,6 +18,8 @@ import { useNetworks } from "../context/NetworksContext"; import ImportWalletDialog from "../components/ImportWalletDialog"; import { MnemonicDialog } from "../components/MnemonicDialog"; import { Container } from "../components/Container"; +import { SignRequestHandler } from '../components/SignRequestHandler'; +import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; import { IS_IMPORT_WALLET_ENABLED } from "../utils/constants"; const HomeScreen = () => { @@ -64,8 +66,34 @@ const HomeScreen = () => { setPhrase(mnemonic); setSelectedNetwork(networksData[0]); } + setIsWalletCreating(false); }; + const handleWalletCreated = useCallback(() => { + setIsWalletCreated(true); + fetchAccounts(); + }, [fetchAccounts]); + + useGetOrCreateAccounts(handleWalletCreated); + + useEffect(() => { + fetchAccounts(); + }, [networksData, setAccounts, selectedNetwork, fetchAccounts]); + + useEffect(() => { + const initializeWallet = async () => { + if (!isWalletCreated && networksData.length > 0) { + const mnemonic = await createWallet(networksData); + if (mnemonic) { + await fetchAccounts(); + setSelectedNetwork(networksData[0]); + } + } + }; + + initializeWallet(); + }, [networksData, isWalletCreated, fetchAccounts, setSelectedNetwork]); + const importWalletHandler = async (recoveryPhrase: string) => { try { const mnemonic = await createWallet(networksData, recoveryPhrase); @@ -114,10 +142,6 @@ const HomeScreen = () => { setCurrentIndex(0); }; - useEffect(() => { - fetchAccounts(); - }, [networksData, setAccounts, selectedNetwork, fetchAccounts]); - return ( @@ -161,6 +185,7 @@ const HomeScreen = () => { )} )} + setImportWalletDialog(false)} diff --git a/src/types.ts b/src/types.ts index 95dcb7d..a5b8dd0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,6 +10,7 @@ export type StackParamsList = { selectedNamespace: string; selectedChainId: string; accountInfo: Account; + prefillMessage?: string; }; SignRequest: { namespace: string; -- 2.45.2 From 41acca6a42c2e94a40abef64111c63bbe285909b Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Fri, 4 Apr 2025 17:22:41 +0530 Subject: [PATCH 02/12] Update EOL --- src/components/SignRequestHandler.tsx | 2 +- src/hooks/useGetOrCreateAccounts.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx index 948a59a..89ba14f 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/components/SignRequestHandler.tsx @@ -72,4 +72,4 @@ export const SignRequestHandler: React.FC = () => { }, [navigation, accounts, currentIndex, selectedNetwork]); return null; -}; \ No newline at end of file +}; diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index 4d16dd8..38a2166 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -70,4 +70,4 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { }, [networksData, getAccountsData, onWalletCreated]); }; -export default useGetOrCreateAccounts; \ No newline at end of file +export default useGetOrCreateAccounts; -- 2.45.2 From 23151f95e5f99d66888f445080fdb5237c14e903 Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Tue, 8 Apr 2025 18:52:19 +0530 Subject: [PATCH 03/12] Embed wallet code without using sign request --- src/components/SignRequestHandler.tsx | 356 +++++++++++++++++++++++--- src/global.d.ts | 19 ++ src/hooks/useGetOrCreateAccounts.ts | 18 +- src/screens/HomeScreen.tsx | 4 +- src/screens/SignMessage.tsx | 193 ++++++++++++-- 5 files changed, 520 insertions(+), 70 deletions(-) create mode 100644 src/global.d.ts diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx index 89ba14f..bc24182 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/components/SignRequestHandler.tsx @@ -1,75 +1,355 @@ +// import React, { useState, useEffect } from 'react'; +// import { useNavigation } from '@react-navigation/native'; +// import { NativeStackNavigationProp } from '@react-navigation/native-stack'; +// import { useAccounts } from '../context/AccountsContext'; +// import { useNetworks } from '../context/NetworksContext'; +// import { StackParamsList } from '../types'; +// import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; + +// declare global { +// interface Window { +// receiveSignRequestFromAndroid?: (message: string) => void; +// } +// } + +// export const SignRequestHandler: React.FC = () => { +// const navigation = useNavigation>(); +// const { accounts, currentIndex } = useAccounts(); +// const { selectedNetwork } = useNetworks(); +// const [pendingMessage, setPendingMessage] = useState(null); + +// useGetOrCreateAccounts(); + +// useEffect(() => { +// if (pendingMessage && selectedNetwork && accounts && accounts.length > 0) { +// navigation.reset({ +// index: 0, +// routes: [ +// { +// name: 'SignRequest', +// params: { +// address: accounts[currentIndex].address, +// message: pendingMessage, +// requestEvent: null, +// path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(pendingMessage)}` +// } +// } +// ] +// }); +// setPendingMessage(null); +// } +// }, [pendingMessage, selectedNetwork, accounts, currentIndex, navigation]); + +// useEffect(() => { +// window.receiveSignRequestFromAndroid = (message: string) => { +// if (!selectedNetwork || !accounts || accounts.length === 0) { +// // Store the message and wait for wallet creation +// setPendingMessage(message); +// return; +// } + +// navigation.reset({ +// index: 0, +// routes: [ +// { +// name: 'SignRequest', +// params: { +// address: accounts[currentIndex].address, +// message: message, +// requestEvent: null, +// path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(message)}` +// } +// } +// ] +// }); +// }; + +// return () => { +// if ('receiveSignRequestFromAndroid' in window) { +// window.receiveSignRequestFromAndroid = undefined; +// } +// }; +// }, [navigation, accounts, currentIndex, selectedNetwork]); + +// return null; +// }; + + +// import React, { useState, useEffect } from 'react'; +// import { useNavigation } from '@react-navigation/native'; +// import { NativeStackNavigationProp } from '@react-navigation/native-stack'; +// import { useAccounts } from '../context/AccountsContext'; +// import { useNetworks } from '../context/NetworksContext'; +// import { StackParamsList } from '../types'; +// import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; + +// declare global { +// interface Window { +// receiveSignRequestFromAndroid?: (message: string) => void; +// reactAppReady: boolean; +// reactAppReceiveMessage: (message: string) => void; +// pendingAndroidMessage?: string; +// Android?: any; +// } +// } + +// export const SignRequestHandler: React.FC = () => { +// const navigation = useNavigation>(); +// const { accounts, currentIndex } = useAccounts(); +// const { selectedNetwork } = useNetworks(); +// const [pendingMessage, setPendingMessage] = useState(null); + +// useGetOrCreateAccounts(); + +// const handleMessage = (message: string) => { +// console.log("Handling message in React app:", message); + +// if (!selectedNetwork || !accounts || accounts.length === 0) { +// // Store the message and wait for wallet creation +// console.log("Storing message for later processing"); +// setPendingMessage(message); +// return; +// } + +// console.log("Navigating to SignRequest screen"); +// navigation.reset({ +// index: 0, +// routes: [ +// { +// name: 'SignRequest', +// params: { +// address: accounts[currentIndex].address, +// message: message, +// requestEvent: null, +// path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(message)}` +// } +// } +// ] +// }); +// }; + +// useEffect(() => { +// if (pendingMessage && selectedNetwork && accounts && accounts.length > 0) { +// handleMessage(pendingMessage); +// setPendingMessage(null); +// } +// }, [pendingMessage, selectedNetwork, accounts, currentIndex, navigation]); + +// useEffect(() => { +// window.reactAppReceiveMessage = handleMessage; +// window.reactAppReady = true; + +// if (window.pendingAndroidMessage) { +// console.log("Processing pending message:", window.pendingAndroidMessage); +// handleMessage(window.pendingAndroidMessage); +// window.pendingAndroidMessage = undefined; +// } + +// if (window.Android && typeof window.Android.bridgeReady === 'function') { +// window.Android.bridgeReady(); +// } + +// console.log("React app ready to receive messages"); + +// return () => { +// window.reactAppReady = false; +// }; +// }, [navigation, accounts, currentIndex, selectedNetwork]); + +// return null; +// }; + +// import React, { useState, useEffect } from 'react'; +// import { useNavigation } from '@react-navigation/native'; +// import { NativeStackNavigationProp } from '@react-navigation/native-stack'; +// import { useAccounts } from '../context/AccountsContext'; +// import { useNetworks } from '../context/NetworksContext'; +// import { StackParamsList } from '../types'; + +// // declare global { +// // interface Window { +// // receiveTimeFromAndroid?: (timestamp: number) => void; +// // receiveDataFromAndroid?: (data: string) => void; +// // receiveSignRequestFromAndroid?: (message: string) => void; +// // } +// // } + +// export const TimeDisplay: React.FC = () => { +// const navigation = useNavigation>(); +// const [displayContent, setDisplayContent] = useState('Waiting for time...'); +// const { accounts, currentIndex } = useAccounts(); +// const { selectedNetwork } = useNetworks(); + +// useEffect(() => { +// console.log('TimeDisplay mounted useeffect'); + +// window.receiveTimeFromAndroid = (timestamp: number) => { +// const date = new Date(timestamp); +// setDisplayContent(date.toLocaleString()); +// }; + +// window.receiveDataFromAndroid = (data: string) => { +// setDisplayContent(data); +// }; + +// window.receiveSignRequestFromAndroid = (message: string) => { +// if (!selectedNetwork || !accounts || accounts.length === 0) { +// console.error('No network or accounts available'); +// return; +// } + +// // Reset the entire navigation state and go directly to SignMessage +// navigation.reset({ +// index: 0, +// routes: [ +// { +// name: 'SignMessage', +// params: { +// selectedNamespace: selectedNetwork.namespace, +// selectedChainId: selectedNetwork.chainId, +// accountInfo: accounts[currentIndex], +// prefillMessage: message +// } +// } +// ] +// }); +// }; + +// return () => { +// if ('receiveTimeFromAndroid' in window) { +// window.receiveTimeFromAndroid = undefined; +// } +// if ('receiveDataFromAndroid' in window) { +// window.receiveDataFromAndroid = undefined; +// } +// if ('receiveSignRequestFromAndroid' in window) { +// window.receiveSignRequestFromAndroid = undefined; +// } +// }; +// }, [navigation, accounts, currentIndex, selectedNetwork]); + +// return ( +//
+//

Content from Android:

+//

{displayContent}

+//
+// ); +// }; + +// const containerStyle: React.CSSProperties = { +// padding: 20, +// backgroundColor: '#f5f5f5', +// borderRadius: 8, +// textAlign: 'center' +// }; + +// const headingStyle: React.CSSProperties = { +// fontSize: 18, +// fontWeight: 'bold', +// marginBottom: 10 +// }; + +// const timeTextStyle: React.CSSProperties = { +// fontSize: 16, +// color: '#333' +// }; + + + import React, { useState, useEffect } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useAccounts } from '../context/AccountsContext'; import { useNetworks } from '../context/NetworksContext'; import { StackParamsList } from '../types'; -import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; -declare global { - interface Window { - receiveSignRequestFromAndroid?: (message: string) => void; - } -} - -export const SignRequestHandler: React.FC = () => { +export const TimeDisplay: React.FC = () => { const navigation = useNavigation>(); + const [displayContent, setDisplayContent] = useState('Waiting for time...'); const { accounts, currentIndex } = useAccounts(); const { selectedNetwork } = useNetworks(); - const [pendingMessage, setPendingMessage] = useState(null); - - useGetOrCreateAccounts(); useEffect(() => { - if (pendingMessage && selectedNetwork && accounts && accounts.length > 0) { - navigation.reset({ - index: 0, - routes: [ - { - name: 'SignRequest', - params: { - address: accounts[currentIndex].address, - message: pendingMessage, - requestEvent: null, - path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(pendingMessage)}` - } - } - ] - }); - setPendingMessage(null); - } - }, [pendingMessage, selectedNetwork, accounts, currentIndex, navigation]); + console.log('TimeDisplay mounted useeffect'); + + // Immediately set up the function on window object + window.receiveTimeFromAndroid = (timestamp: number) => { + const date = new Date(timestamp); + setDisplayContent(date.toLocaleString()); + }; + + window.receiveDataFromAndroid = (data: string) => { + setDisplayContent(data); + }; - useEffect(() => { window.receiveSignRequestFromAndroid = (message: string) => { + console.log('Sign request received with message:', message); if (!selectedNetwork || !accounts || accounts.length === 0) { - // Store the message and wait for wallet creation - setPendingMessage(message); + console.error('No network or accounts available'); return; } + // Reset the entire navigation state and go directly to SignMessage navigation.reset({ index: 0, routes: [ { - name: 'SignRequest', + name: 'SignMessage', params: { - address: accounts[currentIndex].address, - message: message, - requestEvent: null, - path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(message)}` + selectedNamespace: selectedNetwork.namespace, + selectedChainId: selectedNetwork.chainId, + accountInfo: accounts[currentIndex], + prefillMessage: message } } ] }); }; + // Signal to Android that the JS bridge is ready + if (window.Android) { + console.log('Notifying Android that JS bridge is ready'); + setTimeout(() => { + // This timeout ensures the function is properly attached before Android tries to use it + window.Android.onJsBridgeReady?.(); + }, 100); + } + return () => { + if ('receiveTimeFromAndroid' in window) { + window.receiveTimeFromAndroid = undefined; + } + if ('receiveDataFromAndroid' in window) { + window.receiveDataFromAndroid = undefined; + } if ('receiveSignRequestFromAndroid' in window) { window.receiveSignRequestFromAndroid = undefined; } }; }, [navigation, accounts, currentIndex, selectedNetwork]); - return null; + return ( +
+

Content from Android:

+

{displayContent}

+
+ ); }; + +const containerStyle: React.CSSProperties = { + padding: 20, + backgroundColor: '#f5f5f5', + borderRadius: 8, + textAlign: 'center' +}; + +const headingStyle: React.CSSProperties = { + fontSize: 18, + fontWeight: 'bold', + marginBottom: 10 +}; + +const timeTextStyle: React.CSSProperties = { + fontSize: 16, + color: '#333' +}; \ No newline at end of file diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..5b950f6 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,19 @@ +declare global { + interface Window { + Android: { + onSignatureComplete?: (signature: string) => void; + onSignatureError?: (error: string) => void; + onSignatureCancelled?: () => void; + onJsBridgeReady?: () => void; + }; + // Android WebView bridge methods (from TimeDisplay) + receiveTimeFromAndroid?: (timestamp: number) => void; + receiveDataFromAndroid?: (data: string) => void; + receiveSignRequestFromAndroid?: (message: string) => void; + + // Android native bridge object (from useGetOrCreateAccount) + + } + } + +export {}; \ No newline at end of file diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index 38a2166..be58643 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -4,15 +4,15 @@ import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; -declare global { - interface Window { - Android?: { - onSignatureComplete?: (signature: string) => void; - onSignatureError?: (error: string) => void; - onSignatureCancelled?: () => void; - }; - } -} +// declare global { +// interface Window { +// Android?: { +// onSignatureComplete?: (signature: string) => void; +// onSignatureError?: (error: string) => void; +// onSignatureCancelled?: () => void; +// }; +// } +// } const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { const { networksData } = useNetworks(); diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 30f2af0..17ead86 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -18,7 +18,7 @@ import { useNetworks } from "../context/NetworksContext"; import ImportWalletDialog from "../components/ImportWalletDialog"; import { MnemonicDialog } from "../components/MnemonicDialog"; import { Container } from "../components/Container"; -import { SignRequestHandler } from '../components/SignRequestHandler'; +import { TimeDisplay } from '../components/SignRequestHandler'; import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; import { IS_IMPORT_WALLET_ENABLED } from "../utils/constants"; @@ -185,7 +185,7 @@ const HomeScreen = () => { )} )} - + setImportWalletDialog(false)} diff --git a/src/screens/SignMessage.tsx b/src/screens/SignMessage.tsx index 1377128..1271326 100644 --- a/src/screens/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -1,7 +1,67 @@ -import React, { useState } from "react"; -import { Text, TextInput } from "react-native-paper"; -import { Button, Divider, Stack } from "@mui/material"; +// import React, { useState } from "react"; +// import { Text, TextInput } from "react-native-paper"; +// import { Button, Divider, Stack } from "@mui/material"; +// import { NativeStackScreenProps } from "@react-navigation/native-stack"; + +// import { StackParamsList } from "../types"; +// import { signMessage } from "../utils/sign-message"; +// import AccountDetails from "../components/AccountDetails"; +// import { Layout } from "../components/Layout"; + +// type SignProps = NativeStackScreenProps; + +// const SignMessage = ({ route }: SignProps) => { +// const namespace = route.params.selectedNamespace; +// const chainId = route.params.selectedChainId; +// const account = route.params.accountInfo; + +// const [message, setMessage] = useState(""); + +// const signMessageHandler = async () => { +// const signedMessage = await signMessage({ +// message, +// namespace, +// chainId, +// accountId: account.index, +// }); +// alert(`Signature ${signedMessage}`); +// }; + +// return ( +// +// +// {account && `Account ${account.index + 1}`} +// +// + +// +// +// setMessage(text)} +// value={message} +// /> + +// +// +// +// ); +// }; + +// export default SignMessage; + + +import React, { useState, useEffect } from "react"; +import { Text, TextInput } from "react-native-paper"; +import { Button, Divider, Stack, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; import { NativeStackScreenProps } from "@react-navigation/native-stack"; import { StackParamsList } from "../types"; @@ -11,21 +71,82 @@ import { Layout } from "../components/Layout"; type SignProps = NativeStackScreenProps; -const SignMessage = ({ route }: SignProps) => { +const SignMessage = ({ route, navigation }: SignProps) => { const namespace = route.params.selectedNamespace; const chainId = route.params.selectedChainId; const account = route.params.accountInfo; + const prefillMessage = route.params.prefillMessage; - const [message, setMessage] = useState(""); + const [message, setMessage] = useState(prefillMessage || ""); + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + + // If message is prefilled from Android, show the confirmation dialog immediately + useEffect(() => { + if (prefillMessage) { + setMessage(prefillMessage); + // Show confirmation dialog automatically when prefilled from Android + setShowConfirmDialog(true); + } + }, [prefillMessage]); - const signMessageHandler = async () => { - const signedMessage = await signMessage({ - message, - namespace, - chainId, - accountId: account.index, - }); - alert(`Signature ${signedMessage}`); + const handleConfirmSignature = async () => { + try { + const signedMessage = await signMessage({ + message, + namespace, + chainId, + accountId: account.index, + }); + + // Send the result back to Android and close dialog + if (window.Android?.onSignatureComplete) { + window.Android.onSignatureComplete(signedMessage || ""); + } else { + alert(`Signature: ${signedMessage}`); + } + + // Close dialog + setShowConfirmDialog(false); + + // If this was opened from Android, we should return + if (prefillMessage && window.Android) { + // Return to previous screen if applicable + if (navigation.canGoBack()) { + navigation.goBack(); + } + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + + // Send error back to Android + if (window.Android?.onSignatureError) { + window.Android.onSignatureError(errorMessage); + } else { + alert(`Error: ${errorMessage}`); + } + setShowConfirmDialog(false); + } + }; + + const handleCancelSignature = () => { + setShowConfirmDialog(false); + + if (window.Android?.onSignatureCancelled) { + window.Android.onSignatureCancelled(); + } + + // If this was opened from Android, we should return + if (prefillMessage && window.Android) { + // Return to previous screen if applicable + if (navigation.canGoBack()) { + navigation.goBack(); + } + } + }; + + // This is triggered by the main "Sign" button + const showSignConfirmation = () => { + setShowConfirmDialog(true); }; return ( @@ -44,16 +165,46 @@ const SignMessage = ({ route }: SignProps) => { value={message} /> - + + + + + + + {/* Confirmation Dialog */} + + Confirm Signature + + + Are you sure you want to sign the following message? + + + {message} + + + + + + + ); }; -export default SignMessage; +export default SignMessage; \ No newline at end of file -- 2.45.2 From 2e84a41aaf19fe11cb29af22aa4aa443e54a4498 Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Wed, 9 Apr 2025 09:44:18 +0530 Subject: [PATCH 04/12] Embed laconic wallet in android without using sign request --- src/components/SignRequestHandler.tsx | 389 +++++--------------------- src/global.d.ts | 4 - src/hooks/useGetOrCreateAccounts.ts | 10 - src/screens/HomeScreen.tsx | 4 +- src/screens/SignMessage.tsx | 66 +---- 5 files changed, 74 insertions(+), 399 deletions(-) diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx index bc24182..78808ee 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/components/SignRequestHandler.tsx @@ -1,295 +1,32 @@ -// import React, { useState, useEffect } from 'react'; -// import { useNavigation } from '@react-navigation/native'; -// import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -// import { useAccounts } from '../context/AccountsContext'; -// import { useNetworks } from '../context/NetworksContext'; -// import { StackParamsList } from '../types'; -// import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; - -// declare global { -// interface Window { -// receiveSignRequestFromAndroid?: (message: string) => void; -// } -// } - -// export const SignRequestHandler: React.FC = () => { -// const navigation = useNavigation>(); -// const { accounts, currentIndex } = useAccounts(); -// const { selectedNetwork } = useNetworks(); -// const [pendingMessage, setPendingMessage] = useState(null); - -// useGetOrCreateAccounts(); - -// useEffect(() => { -// if (pendingMessage && selectedNetwork && accounts && accounts.length > 0) { -// navigation.reset({ -// index: 0, -// routes: [ -// { -// name: 'SignRequest', -// params: { -// address: accounts[currentIndex].address, -// message: pendingMessage, -// requestEvent: null, -// path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(pendingMessage)}` -// } -// } -// ] -// }); -// setPendingMessage(null); -// } -// }, [pendingMessage, selectedNetwork, accounts, currentIndex, navigation]); - -// useEffect(() => { -// window.receiveSignRequestFromAndroid = (message: string) => { -// if (!selectedNetwork || !accounts || accounts.length === 0) { -// // Store the message and wait for wallet creation -// setPendingMessage(message); -// return; -// } - -// navigation.reset({ -// index: 0, -// routes: [ -// { -// name: 'SignRequest', -// params: { -// address: accounts[currentIndex].address, -// message: message, -// requestEvent: null, -// path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(message)}` -// } -// } -// ] -// }); -// }; - -// return () => { -// if ('receiveSignRequestFromAndroid' in window) { -// window.receiveSignRequestFromAndroid = undefined; -// } -// }; -// }, [navigation, accounts, currentIndex, selectedNetwork]); - -// return null; -// }; - - -// import React, { useState, useEffect } from 'react'; -// import { useNavigation } from '@react-navigation/native'; -// import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -// import { useAccounts } from '../context/AccountsContext'; -// import { useNetworks } from '../context/NetworksContext'; -// import { StackParamsList } from '../types'; -// import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; - -// declare global { -// interface Window { -// receiveSignRequestFromAndroid?: (message: string) => void; -// reactAppReady: boolean; -// reactAppReceiveMessage: (message: string) => void; -// pendingAndroidMessage?: string; -// Android?: any; -// } -// } - -// export const SignRequestHandler: React.FC = () => { -// const navigation = useNavigation>(); -// const { accounts, currentIndex } = useAccounts(); -// const { selectedNetwork } = useNetworks(); -// const [pendingMessage, setPendingMessage] = useState(null); - -// useGetOrCreateAccounts(); - -// const handleMessage = (message: string) => { -// console.log("Handling message in React app:", message); - -// if (!selectedNetwork || !accounts || accounts.length === 0) { -// // Store the message and wait for wallet creation -// console.log("Storing message for later processing"); -// setPendingMessage(message); -// return; -// } - -// console.log("Navigating to SignRequest screen"); -// navigation.reset({ -// index: 0, -// routes: [ -// { -// name: 'SignRequest', -// params: { -// address: accounts[currentIndex].address, -// message: message, -// requestEvent: null, -// path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${accounts[currentIndex].address}/${encodeURIComponent(message)}` -// } -// } -// ] -// }); -// }; - -// useEffect(() => { -// if (pendingMessage && selectedNetwork && accounts && accounts.length > 0) { -// handleMessage(pendingMessage); -// setPendingMessage(null); -// } -// }, [pendingMessage, selectedNetwork, accounts, currentIndex, navigation]); - -// useEffect(() => { -// window.reactAppReceiveMessage = handleMessage; -// window.reactAppReady = true; - -// if (window.pendingAndroidMessage) { -// console.log("Processing pending message:", window.pendingAndroidMessage); -// handleMessage(window.pendingAndroidMessage); -// window.pendingAndroidMessage = undefined; -// } - -// if (window.Android && typeof window.Android.bridgeReady === 'function') { -// window.Android.bridgeReady(); -// } - -// console.log("React app ready to receive messages"); - -// return () => { -// window.reactAppReady = false; -// }; -// }, [navigation, accounts, currentIndex, selectedNetwork]); - -// return null; -// }; - -// import React, { useState, useEffect } from 'react'; -// import { useNavigation } from '@react-navigation/native'; -// import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -// import { useAccounts } from '../context/AccountsContext'; -// import { useNetworks } from '../context/NetworksContext'; -// import { StackParamsList } from '../types'; - -// // declare global { -// // interface Window { -// // receiveTimeFromAndroid?: (timestamp: number) => void; -// // receiveDataFromAndroid?: (data: string) => void; -// // receiveSignRequestFromAndroid?: (message: string) => void; -// // } -// // } - -// export const TimeDisplay: React.FC = () => { -// const navigation = useNavigation>(); -// const [displayContent, setDisplayContent] = useState('Waiting for time...'); -// const { accounts, currentIndex } = useAccounts(); -// const { selectedNetwork } = useNetworks(); - -// useEffect(() => { -// console.log('TimeDisplay mounted useeffect'); - -// window.receiveTimeFromAndroid = (timestamp: number) => { -// const date = new Date(timestamp); -// setDisplayContent(date.toLocaleString()); -// }; - -// window.receiveDataFromAndroid = (data: string) => { -// setDisplayContent(data); -// }; - -// window.receiveSignRequestFromAndroid = (message: string) => { -// if (!selectedNetwork || !accounts || accounts.length === 0) { -// console.error('No network or accounts available'); -// return; -// } - -// // Reset the entire navigation state and go directly to SignMessage -// navigation.reset({ -// index: 0, -// routes: [ -// { -// name: 'SignMessage', -// params: { -// selectedNamespace: selectedNetwork.namespace, -// selectedChainId: selectedNetwork.chainId, -// accountInfo: accounts[currentIndex], -// prefillMessage: message -// } -// } -// ] -// }); -// }; - -// return () => { -// if ('receiveTimeFromAndroid' in window) { -// window.receiveTimeFromAndroid = undefined; -// } -// if ('receiveDataFromAndroid' in window) { -// window.receiveDataFromAndroid = undefined; -// } -// if ('receiveSignRequestFromAndroid' in window) { -// window.receiveSignRequestFromAndroid = undefined; -// } -// }; -// }, [navigation, accounts, currentIndex, selectedNetwork]); - -// return ( -//
-//

Content from Android:

-//

{displayContent}

-//
-// ); -// }; - -// const containerStyle: React.CSSProperties = { -// padding: 20, -// backgroundColor: '#f5f5f5', -// borderRadius: 8, -// textAlign: 'center' -// }; - -// const headingStyle: React.CSSProperties = { -// fontSize: 18, -// fontWeight: 'bold', -// marginBottom: 10 -// }; - -// const timeTextStyle: React.CSSProperties = { -// fontSize: 16, -// color: '#333' -// }; - - - -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useAccounts } from '../context/AccountsContext'; import { useNetworks } from '../context/NetworksContext'; import { StackParamsList } from '../types'; +import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; -export const TimeDisplay: React.FC = () => { +export const SignRequestAndroid: React.FC = () => { const navigation = useNavigation>(); - const [displayContent, setDisplayContent] = useState('Waiting for time...'); const { accounts, currentIndex } = useAccounts(); const { selectedNetwork } = useNetworks(); + const pendingMessageRef = useRef(null); + + // Run on mount + useGetOrCreateAccounts(); + + // Retry navigation if message is pending and accounts are ready useEffect(() => { - console.log('TimeDisplay mounted useeffect'); - - // Immediately set up the function on window object - window.receiveTimeFromAndroid = (timestamp: number) => { - const date = new Date(timestamp); - setDisplayContent(date.toLocaleString()); - }; + if ( + pendingMessageRef.current && + selectedNetwork && + accounts && + accounts.length > 0 + ) { + const message = pendingMessageRef.current; + pendingMessageRef.current = null; - window.receiveDataFromAndroid = (data: string) => { - setDisplayContent(data); - }; - - window.receiveSignRequestFromAndroid = (message: string) => { - console.log('Sign request received with message:', message); - if (!selectedNetwork || !accounts || accounts.length === 0) { - console.error('No network or accounts available'); - return; - } - - // Reset the entire navigation state and go directly to SignMessage navigation.reset({ index: 0, routes: [ @@ -299,57 +36,73 @@ export const TimeDisplay: React.FC = () => { selectedNamespace: selectedNetwork.namespace, selectedChainId: selectedNetwork.chainId, accountInfo: accounts[currentIndex], - prefillMessage: message - } - } - ] + prefillMessage: message, + }, + }, + ], + }); + } + }, [accounts, selectedNetwork]); + + useEffect(() => { + window.receiveSignRequestFromAndroid = (message: string) => { + console.log('Sign request received with message:', message); + + if (!selectedNetwork || !accounts || accounts.length === 0) { + console.warn('Delaying sign request until data is ready...'); + pendingMessageRef.current = message; + return; + } + + navigation.reset({ + index: 0, + routes: [ + { + name: 'SignMessage', + params: { + selectedNamespace: selectedNetwork.namespace, + selectedChainId: selectedNetwork.chainId, + accountInfo: accounts[currentIndex], + prefillMessage: message, + }, + }, + ], }); }; - // Signal to Android that the JS bridge is ready if (window.Android) { console.log('Notifying Android that JS bridge is ready'); setTimeout(() => { - // This timeout ensures the function is properly attached before Android tries to use it - window.Android.onJsBridgeReady?.(); + window.Android?.onJsBridgeReady?.(); }, 100); } return () => { - if ('receiveTimeFromAndroid' in window) { - window.receiveTimeFromAndroid = undefined; - } - if ('receiveDataFromAndroid' in window) { - window.receiveDataFromAndroid = undefined; - } - if ('receiveSignRequestFromAndroid' in window) { - window.receiveSignRequestFromAndroid = undefined; - } + window.receiveSignRequestFromAndroid = undefined; }; - }, [navigation, accounts, currentIndex, selectedNetwork]); + }, []); - return ( -
-

Content from Android:

-

{displayContent}

-
- ); + return null; }; -const containerStyle: React.CSSProperties = { - padding: 20, - backgroundColor: '#f5f5f5', - borderRadius: 8, - textAlign: 'center' -}; -const headingStyle: React.CSSProperties = { - fontSize: 18, - fontWeight: 'bold', - marginBottom: 10 -}; -const timeTextStyle: React.CSSProperties = { - fontSize: 16, - color: '#333' -}; \ No newline at end of file + + + + + + + + + + + + + + + + + + + diff --git a/src/global.d.ts b/src/global.d.ts index 5b950f6..903501c 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -6,13 +6,9 @@ declare global { onSignatureCancelled?: () => void; onJsBridgeReady?: () => void; }; - // Android WebView bridge methods (from TimeDisplay) receiveTimeFromAndroid?: (timestamp: number) => void; receiveDataFromAndroid?: (data: string) => void; receiveSignRequestFromAndroid?: (message: string) => void; - - // Android native bridge object (from useGetOrCreateAccount) - } } diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index be58643..fc6bff4 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -4,16 +4,6 @@ import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; -// declare global { -// interface Window { -// Android?: { -// onSignatureComplete?: (signature: string) => void; -// onSignatureError?: (error: string) => void; -// onSignatureCancelled?: () => void; -// }; -// } -// } - const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { const { networksData } = useNetworks(); const { getAccountsData } = useAccountsData(); diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 17ead86..1ce9dc3 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -18,7 +18,7 @@ import { useNetworks } from "../context/NetworksContext"; import ImportWalletDialog from "../components/ImportWalletDialog"; import { MnemonicDialog } from "../components/MnemonicDialog"; import { Container } from "../components/Container"; -import { TimeDisplay } from '../components/SignRequestHandler'; +import { SignRequestAndroid } from '../components/SignRequestHandler'; import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; import { IS_IMPORT_WALLET_ENABLED } from "../utils/constants"; @@ -185,7 +185,7 @@ const HomeScreen = () => { )} )} - + setImportWalletDialog(false)} diff --git a/src/screens/SignMessage.tsx b/src/screens/SignMessage.tsx index 1271326..3883334 100644 --- a/src/screens/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -1,64 +1,3 @@ -// import React, { useState } from "react"; -// import { Text, TextInput } from "react-native-paper"; -// import { Button, Divider, Stack } from "@mui/material"; - -// import { NativeStackScreenProps } from "@react-navigation/native-stack"; - -// import { StackParamsList } from "../types"; -// import { signMessage } from "../utils/sign-message"; -// import AccountDetails from "../components/AccountDetails"; -// import { Layout } from "../components/Layout"; - -// type SignProps = NativeStackScreenProps; - -// const SignMessage = ({ route }: SignProps) => { -// const namespace = route.params.selectedNamespace; -// const chainId = route.params.selectedChainId; -// const account = route.params.accountInfo; - -// const [message, setMessage] = useState(""); - -// const signMessageHandler = async () => { -// const signedMessage = await signMessage({ -// message, -// namespace, -// chainId, -// accountId: account.index, -// }); -// alert(`Signature ${signedMessage}`); -// }; - -// return ( -// -// -// {account && `Account ${account.index + 1}`} -// -// - -// -// -// setMessage(text)} -// value={message} -// /> - -// -// -// -// ); -// }; - -// export default SignMessage; - - import React, { useState, useEffect } from "react"; import { Text, TextInput } from "react-native-paper"; import { Button, Divider, Stack, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; @@ -105,7 +44,6 @@ const SignMessage = ({ route, navigation }: SignProps) => { alert(`Signature: ${signedMessage}`); } - // Close dialog setShowConfirmDialog(false); // If this was opened from Android, we should return @@ -144,7 +82,6 @@ const SignMessage = ({ route, navigation }: SignProps) => { } }; - // This is triggered by the main "Sign" button const showSignConfirmation = () => { setShowConfirmDialog(true); }; @@ -184,7 +121,6 @@ const SignMessage = ({ route, navigation }: SignProps) => { - {/* Confirmation Dialog */} { Are you sure you want to sign the following message? - + {message} -- 2.45.2 From 20dee55dcad3ab789fccc03760e562c703818ab5 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Wed, 9 Apr 2025 20:16:55 +0530 Subject: [PATCH 05/12] Use SignRequest --- src/App.tsx | 26 +++- src/components/SignRequestHandler.tsx | 192 ++++++++++++++++++-------- src/global.d.ts | 26 ++-- src/hooks/useGetOrCreateAccounts.ts | 3 - src/screens/SignMessage.tsx | 6 +- src/screens/SignRequest.tsx | 14 +- src/types.ts | 3 + 7 files changed, 192 insertions(+), 78 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f297288..d6a52ec 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,6 +40,7 @@ import { WalletEmbed } from "./screens/WalletEmbed"; import { AutoSignIn } from "./screens/AutoSignIn"; import { checkSufficientFunds, getPathKey, sendMessage } from "./utils/misc"; import useAccountsData from "./hooks/useAccountsData"; +import { SignRequestAndroid } from "./components/SignRequestHandler"; const Stack = createStackNavigator(); @@ -132,16 +133,28 @@ const App = (): React.JSX.Element => { break; case EIP155_SIGNING_METHODS.PERSONAL_SIGN: + const chainId = request.params.chainId?.split(':')[1]; + const account = accounts.find(acc => acc.address === request.params[1]); + if (!account) { + throw new Error('Account not found'); + } navigation.navigate("SignRequest", { namespace: EIP155, + chainId, address: request.params[1], message: getSignParamsMessage(request.params), + accountInfo: account, requestEvent, requestSessionData, }); break; case COSMOS_METHODS.COSMOS_SIGN_DIRECT: + const cosmosChainId = request.params.chainId?.split(':')[1]; + const cosmosAccount = accounts.find(acc => acc.address === request.params.signerAddress); + if (!cosmosAccount) { + throw new Error('Account not found'); + } const message = { txbody: TxBody.toJSON( TxBody.decode( @@ -160,18 +173,27 @@ const App = (): React.JSX.Element => { }; navigation.navigate("SignRequest", { namespace: COSMOS, + chainId: cosmosChainId, address: request.params.signerAddress, message: JSON.stringify(message, undefined, 2), + accountInfo: cosmosAccount, requestEvent, requestSessionData, }); break; case COSMOS_METHODS.COSMOS_SIGN_AMINO: + const aminoChainId = request.params.chainId?.split(':')[1]; + const aminoAccount = accounts.find(acc => acc.address === request.params.signerAddress); + if (!aminoAccount) { + throw new Error('Account not found'); + } navigation.navigate("SignRequest", { namespace: COSMOS, + chainId: aminoChainId, address: request.params.signerAddress, message: request.params.signDoc.memo, + accountInfo: aminoAccount, requestEvent, requestSessionData, }); @@ -206,6 +228,7 @@ const App = (): React.JSX.Element => { setCurrentIndex, selectedNetwork, web3wallet, + accounts, ], ); @@ -254,7 +277,7 @@ const App = (): React.JSX.Element => { ).privKey; const sender = await DirectSecp256k1Wallet.fromKey( - Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), + Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), network.addressPrefix ); @@ -402,6 +425,7 @@ const App = (): React.JSX.Element => { > Session approved + ); }; diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx index 78808ee..d606ca8 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/components/SignRequestHandler.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useAccounts } from '../context/AccountsContext'; @@ -12,62 +12,158 @@ export const SignRequestAndroid: React.FC = () => { const { selectedNetwork } = useNetworks(); const pendingMessageRef = useRef(null); + const [isDataReady, setIsDataReady] = useState(false); // Run on mount useGetOrCreateAccounts(); - // Retry navigation if message is pending and accounts are ready + // Check if data is ready useEffect(() => { - if ( - pendingMessageRef.current && - selectedNetwork && - accounts && - accounts.length > 0 - ) { - const message = pendingMessageRef.current; - pendingMessageRef.current = null; + const logData = { + selectedNetwork: selectedNetwork ? { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId, + } : 'undefined', + accounts: accounts ? `Array of length ${accounts.length}` : 'undefined', + currentIndex + }; + console.log('Checking data readiness:', JSON.stringify(logData, null, 2)); - navigation.reset({ - index: 0, - routes: [ - { - name: 'SignMessage', - params: { - selectedNamespace: selectedNetwork.namespace, - selectedChainId: selectedNetwork.chainId, - accountInfo: accounts[currentIndex], - prefillMessage: message, - }, - }, - ], - }); + if (selectedNetwork && accounts && accounts.length > 0) { + setIsDataReady(true); } - }, [accounts, selectedNetwork]); + }, [selectedNetwork, accounts, currentIndex]); - useEffect(() => { - window.receiveSignRequestFromAndroid = (message: string) => { - console.log('Sign request received with message:', message); + // Handle navigation to SignRequest screen + const navigateToSignRequest = useCallback(async (message: string) => { + const logData = { + isDataReady, + selectedNetwork: selectedNetwork ? { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId, + } : 'undefined', + accounts: accounts ? `Array of length ${accounts.length}` : 'undefined', + currentIndex, + message + }; + console.log('Attempting to navigate with data:', JSON.stringify(logData, null, 2)); - if (!selectedNetwork || !accounts || accounts.length === 0) { - console.warn('Delaying sign request until data is ready...'); - pendingMessageRef.current = message; + if (!isDataReady) { + console.log('Delaying sign request until data is ready...'); + pendingMessageRef.current = message; + return; + } + + if (!selectedNetwork) { + console.error('No network selected'); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('No network selected'); + } + return; + } + + if (!accounts || accounts.length === 0) { + console.error('No accounts available'); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('No accounts available'); + } + return; + } + + const currentAccount = accounts[currentIndex]; + if (!currentAccount) { + console.error('Current account not found'); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Current account not found'); + } + return; + } + + // Verify network properties exist before using them + if (!selectedNetwork.namespace || !selectedNetwork.chainId) { + const errorData = { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId + }; + console.error('Network missing required properties:', JSON.stringify(errorData, null, 2)); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Network missing required properties'); + } + return; + } + + const navigationData = { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId, + address: currentAccount.address, + message + }; + console.log('Navigating to SignRequest with:', JSON.stringify(navigationData, null, 2)); + + try { + // Ensure the path matches the expected regex pattern: /sign/(eip155|cosmos)/(.+)/(.+)/(.+) + const path = `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`; + + // Verify the path matches the expected pattern + const pathRegex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/; + if (!pathRegex.test(path)) { + console.error('Path does not match expected pattern:', path); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Invalid path format'); + } return; } + // Parse the path to get the components + const match = path.match(pathRegex); + if (!match) { + console.error('Failed to parse path:', path); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Failed to parse path'); + } + return; + } + + const [, pathNamespace, pathChainId, pathAddress, pathMessage] = match; + navigation.reset({ index: 0, routes: [ { - name: 'SignMessage', + name: 'SignRequest', + path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`, params: { - selectedNamespace: selectedNetwork.namespace, - selectedChainId: selectedNetwork.chainId, - accountInfo: accounts[currentIndex], - prefillMessage: message, + namespace: pathNamespace, + chainId: pathChainId, + address: pathAddress, + message: decodeURIComponent(pathMessage), + accountInfo: currentAccount, }, }, ], }); + } catch (error) { + console.error('Navigation error:', error); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError(`Navigation error: ${error}`); + } + } + }, [isDataReady, selectedNetwork, accounts, currentIndex, navigation]); + + // Handle pending message when data becomes ready + useEffect(() => { + if (pendingMessageRef.current && isDataReady) { + const message = pendingMessageRef.current; + pendingMessageRef.current = null; + navigateToSignRequest(message); + } + }, [isDataReady, navigateToSignRequest]); + + // Setup Android bridge and message handler + useEffect(() => { + window.receiveSignRequestFromAndroid = (message: string) => { + console.log('Sign request received with message:', message); + navigateToSignRequest(message); }; if (window.Android) { @@ -80,29 +176,7 @@ export const SignRequestAndroid: React.FC = () => { return () => { window.receiveSignRequestFromAndroid = undefined; }; - }, []); + }, [navigateToSignRequest]); return null; }; - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/global.d.ts b/src/global.d.ts index 903501c..fc978ba 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,15 +1,19 @@ declare global { - interface Window { - Android: { - onSignatureComplete?: (signature: string) => void; - onSignatureError?: (error: string) => void; - onSignatureCancelled?: () => void; - onJsBridgeReady?: () => void; - }; - receiveTimeFromAndroid?: (timestamp: number) => void; - receiveDataFromAndroid?: (data: string) => void; - receiveSignRequestFromAndroid?: (message: string) => void; - } + interface Window { + Android: { + onSignatureComplete?: (signature: string) => void; + onSignatureError?: (error: string) => void; + onSignatureCancelled?: () => void; + onJsBridgeReady?: () => void; + }; + // Android WebView bridge methods (from TimeDisplay) + receiveTimeFromAndroid?: (timestamp: number) => void; + receiveDataFromAndroid?: (data: string) => void; + receiveSignRequestFromAndroid?: (message: string) => void; + + // Android native bridge object (from useGetOrCreateAccount) + } +} export {}; \ No newline at end of file diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index fc6bff4..edbe46c 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -20,7 +20,6 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { // Re-fetch newly created accounts accountsData = await getAccountsData(event.data.chainId); - onWalletCreated?.(); } @@ -41,7 +40,6 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { console.log("Auto-creating wallet..."); await createWallet(networksData); accountsData = await getAccountsData(defaultChainId); - onWalletCreated?.(); } }; @@ -49,7 +47,6 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { window.addEventListener('message', handleCreateAccounts); const isAndroidWebView = !!(window.Android); - if (isAndroidWebView) { autoCreateAccounts(); } diff --git a/src/screens/SignMessage.tsx b/src/screens/SignMessage.tsx index 3883334..a96304d 100644 --- a/src/screens/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -23,8 +23,10 @@ const SignMessage = ({ route, navigation }: SignProps) => { useEffect(() => { if (prefillMessage) { setMessage(prefillMessage); - // Show confirmation dialog automatically when prefilled from Android - setShowConfirmDialog(true); + // Show confirmation dialog immediately + setTimeout(() => { + setShowConfirmDialog(true); + }, 0); } }, [prefillMessage]); diff --git a/src/screens/SignRequest.tsx b/src/screens/SignRequest.tsx index 3e0839d..9259ae7 100644 --- a/src/screens/SignRequest.tsx +++ b/src/screens/SignRequest.tsx @@ -202,7 +202,13 @@ const SignRequest = ({ route }: SignRequestProps) => { chainId, accountId: account.index, }); - alert(`Signature ${signedMessage}`); + + // Send the result back to Android and close dialog + if (window.Android?.onSignatureComplete) { + window.Android.onSignatureComplete(signedMessage || ""); + } else { + alert(`Signature: ${signedMessage}`); + } } }; @@ -230,7 +236,11 @@ const SignRequest = ({ route }: SignRequestProps) => { } setIsRejecting(false); - navigation.navigate('Home'); + if (window.Android?.onSignatureCancelled) { + window.Android.onSignatureCancelled(); + } else { + navigation.navigate('Home'); + } }; useEffect(() => { diff --git a/src/types.ts b/src/types.ts index a5b8dd0..8abcb46 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,9 +13,12 @@ export type StackParamsList = { prefillMessage?: string; }; SignRequest: { + path?: string; namespace: string; + chainId?: string; address: string; message: string; + accountInfo?: Account; requestEvent?: Web3WalletTypes.SessionRequest; requestSessionData?: SessionTypes.Struct; }; -- 2.45.2 From b3b4cc12c40ec361fe4afade6a5e898a37f2591f Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 10 Apr 2025 12:37:24 +0530 Subject: [PATCH 06/12] Update handler for useGetorCreateAccouts --- src/components/SignRequestHandler.tsx | 15 +++++++++++++- src/global.d.ts | 4 +++- src/hooks/useGetOrCreateAccounts.ts | 30 +++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx index d606ca8..f0dfed1 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/components/SignRequestHandler.tsx @@ -31,6 +31,10 @@ export const SignRequestAndroid: React.FC = () => { if (selectedNetwork && accounts && accounts.length > 0) { setIsDataReady(true); + // Notify Android that accounts are ready + if (window.Android?.onAccountsReady) { + window.Android.onAccountsReady(); + } } }, [selectedNetwork, accounts, currentIndex]); @@ -166,15 +170,24 @@ export const SignRequestAndroid: React.FC = () => { navigateToSignRequest(message); }; + // Handle create/get accounts request + window.receiveCreateOrGetAccountsRequestFromAndroid = () => { + console.log('SignRequestHandler: Create/get accounts request received'); + // The useGetOrCreateAccounts hook will handle this + }; + if (window.Android) { - console.log('Notifying Android that JS bridge is ready'); setTimeout(() => { + console.log('SignRequestHandler: Calling onJsBridgeReady'); window.Android?.onJsBridgeReady?.(); }, 100); + } else { + console.log('SignRequestHandler: Android bridge is not available'); } return () => { window.receiveSignRequestFromAndroid = undefined; + window.receiveCreateOrGetAccountsRequestFromAndroid = undefined; }; }, [navigateToSignRequest]); diff --git a/src/global.d.ts b/src/global.d.ts index fc978ba..1ce5b25 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,15 +1,17 @@ declare global { interface Window { - Android: { + Android?: { onSignatureComplete?: (signature: string) => void; onSignatureError?: (error: string) => void; onSignatureCancelled?: () => void; onJsBridgeReady?: () => void; + onAccountsReady?: () => void; }; // Android WebView bridge methods (from TimeDisplay) receiveTimeFromAndroid?: (timestamp: number) => void; receiveDataFromAndroid?: (data: string) => void; receiveSignRequestFromAndroid?: (message: string) => void; + receiveCreateOrGetAccountsRequestFromAndroid?: () => void; // Android native bridge object (from useGetOrCreateAccount) diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index edbe46c..36457fb 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -10,12 +10,13 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { useEffect(() => { const handleCreateAccounts = async (event: MessageEvent) => { - if (event.data.type !== 'REQUEST_CREATE_OR_GET_ACCOUNTS') return; + 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 @@ -23,6 +24,14 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { onWalletCreated?.(); } + // Notify Android that accounts are ready + if (window.Android?.onAccountsReady) { + console.log('useGetOrCreateAccounts: Calling onAccountsReady'); + window.Android.onAccountsReady(); + } else { + console.log('useGetOrCreateAccounts: Android bridge not available'); + } + sendMessage( event.source as Window, 'WALLET_ACCOUNTS_DATA', accountsData.map(account => account.address), @@ -32,21 +41,34 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { const autoCreateAccounts = async () => { const defaultChainId = networksData[0]?.chainId; - if (!defaultChainId) return; + + if (!defaultChainId) { + console.log('useGetOrCreateAccounts: No default chainId found'); + return; + } let accountsData = await getAccountsData(defaultChainId); if (accountsData.length === 0) { - console.log("Auto-creating wallet..."); await createWallet(networksData); accountsData = await getAccountsData(defaultChainId); onWalletCreated?.(); } + + // Notify Android that accounts are ready + console.log('useGetOrCreateAccounts: Notifying Android that accounts are ready'); + if (window.Android?.onAccountsReady) { + console.log('useGetOrCreateAccounts: Calling onAccountsReady'); + window.Android.onAccountsReady(); + } else { + console.log('useGetOrCreateAccounts: Android bridge not available'); + } }; window.addEventListener('message', handleCreateAccounts); const isAndroidWebView = !!(window.Android); + if (isAndroidWebView) { autoCreateAccounts(); } -- 2.45.2 From ad636e5847380782031abc3593e4c5da5908318b Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 10 Apr 2025 14:15:17 +0530 Subject: [PATCH 07/12] Implement common hooks instead of SignRequestHandler component --- src/App.tsx | 5 ++- .../useSignRequestHandler.ts} | 40 ++++++++++++------- src/screens/HomeScreen.tsx | 5 ++- 3 files changed, 31 insertions(+), 19 deletions(-) rename src/{components/SignRequestHandler.tsx => hooks/useSignRequestHandler.ts} (90%) diff --git a/src/App.tsx b/src/App.tsx index d6a52ec..5fcfcaa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,7 +40,7 @@ import { WalletEmbed } from "./screens/WalletEmbed"; import { AutoSignIn } from "./screens/AutoSignIn"; import { checkSufficientFunds, getPathKey, sendMessage } from "./utils/misc"; import useAccountsData from "./hooks/useAccountsData"; -import { SignRequestAndroid } from "./components/SignRequestHandler"; +import { useSignRequestHandler } from "./hooks/useSignRequestHandler"; const Stack = createStackNavigator(); @@ -302,6 +302,8 @@ const App = (): React.JSX.Element => { const showWalletConnect = useMemo(() => accounts.length > 0, [accounts]); + useSignRequestHandler(); + return ( { > Session approved - ); }; diff --git a/src/components/SignRequestHandler.tsx b/src/hooks/useSignRequestHandler.ts similarity index 90% rename from src/components/SignRequestHandler.tsx rename to src/hooks/useSignRequestHandler.ts index f0dfed1..4735717 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/hooks/useSignRequestHandler.ts @@ -1,23 +1,16 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; +import { useState, useEffect, useRef, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useAccounts } from '../context/AccountsContext'; import { useNetworks } from '../context/NetworksContext'; import { StackParamsList } from '../types'; -import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; +import useGetOrCreateAccounts from './useGetOrCreateAccounts'; -export const SignRequestAndroid: React.FC = () => { - const navigation = useNavigation>(); - const { accounts, currentIndex } = useAccounts(); +export const useSignRequestData = () => { const { selectedNetwork } = useNetworks(); - - const pendingMessageRef = useRef(null); + const { accounts, currentIndex } = useAccounts(); const [isDataReady, setIsDataReady] = useState(false); - // Run on mount - useGetOrCreateAccounts(); - - // Check if data is ready useEffect(() => { const logData = { selectedNetwork: selectedNetwork ? { @@ -38,7 +31,14 @@ export const SignRequestAndroid: React.FC = () => { } }, [selectedNetwork, accounts, currentIndex]); - // Handle navigation to SignRequest screen + return { isDataReady, selectedNetwork, accounts, currentIndex }; +}; + +export const useSignRequestNavigation = () => { + const navigation = useNavigation>(); + const { isDataReady, selectedNetwork, accounts, currentIndex } = useSignRequestData(); + const pendingMessageRef = useRef(null); + const navigateToSignRequest = useCallback(async (message: string) => { const logData = { isDataReady, @@ -163,7 +163,12 @@ export const SignRequestAndroid: React.FC = () => { } }, [isDataReady, navigateToSignRequest]); - // Setup Android bridge and message handler + return { navigateToSignRequest }; +}; + +export const useAndroidBridge = () => { + const { navigateToSignRequest } = useSignRequestNavigation(); + useEffect(() => { window.receiveSignRequestFromAndroid = (message: string) => { console.log('Sign request received with message:', message); @@ -190,6 +195,11 @@ export const SignRequestAndroid: React.FC = () => { window.receiveCreateOrGetAccountsRequestFromAndroid = undefined; }; }, [navigateToSignRequest]); - - return null; +}; + +export const useSignRequestHandler = () => { + useGetOrCreateAccounts(); + useSignRequestData(); + useSignRequestNavigation(); + useAndroidBridge(); }; diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 1ce9dc3..17fa788 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -18,7 +18,7 @@ import { useNetworks } from "../context/NetworksContext"; import ImportWalletDialog from "../components/ImportWalletDialog"; import { MnemonicDialog } from "../components/MnemonicDialog"; import { Container } from "../components/Container"; -import { SignRequestAndroid } from '../components/SignRequestHandler'; +import { useSignRequestHandler } from '../hooks/useSignRequestHandler'; import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; import { IS_IMPORT_WALLET_ENABLED } from "../utils/constants"; @@ -142,6 +142,8 @@ const HomeScreen = () => { setCurrentIndex(0); }; + useSignRequestHandler(); + return ( @@ -185,7 +187,6 @@ const HomeScreen = () => { )} )} - setImportWalletDialog(false)} -- 2.45.2 From 6ad37d0fa5e7e4acea7be4a176b4c2fd04f8fb0f Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 10 Apr 2025 19:43:29 +0530 Subject: [PATCH 08/12] Fix new wallets getting created --- src/hooks/useGetOrCreateAccounts.ts | 20 ++++---------------- src/screens/HomeScreen.tsx | 14 -------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index 36457fb..d681be3 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -10,13 +10,12 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { useEffect(() => { const handleCreateAccounts = async (event: MessageEvent) => { - if (event.data.type !== 'REQUEST_CREATE_OR_GET_ACCOUNTS') { - return; - } + 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 @@ -26,10 +25,7 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { // Notify Android that accounts are ready if (window.Android?.onAccountsReady) { - console.log('useGetOrCreateAccounts: Calling onAccountsReady'); window.Android.onAccountsReady(); - } else { - console.log('useGetOrCreateAccounts: Android bridge not available'); } sendMessage( @@ -41,34 +37,26 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { const autoCreateAccounts = async () => { const defaultChainId = networksData[0]?.chainId; - - if (!defaultChainId) { - console.log('useGetOrCreateAccounts: No default chainId found'); - return; - } + if (!defaultChainId) return; let accountsData = await getAccountsData(defaultChainId); if (accountsData.length === 0) { + console.log("Auto-creating wallet..."); await createWallet(networksData); accountsData = await getAccountsData(defaultChainId); onWalletCreated?.(); } // Notify Android that accounts are ready - console.log('useGetOrCreateAccounts: Notifying Android that accounts are ready'); if (window.Android?.onAccountsReady) { - console.log('useGetOrCreateAccounts: Calling onAccountsReady'); window.Android.onAccountsReady(); - } else { - console.log('useGetOrCreateAccounts: Android bridge not available'); } }; window.addEventListener('message', handleCreateAccounts); const isAndroidWebView = !!(window.Android); - if (isAndroidWebView) { autoCreateAccounts(); } diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 17fa788..b3b3f56 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -80,20 +80,6 @@ const HomeScreen = () => { fetchAccounts(); }, [networksData, setAccounts, selectedNetwork, fetchAccounts]); - useEffect(() => { - const initializeWallet = async () => { - if (!isWalletCreated && networksData.length > 0) { - const mnemonic = await createWallet(networksData); - if (mnemonic) { - await fetchAccounts(); - setSelectedNetwork(networksData[0]); - } - } - }; - - initializeWallet(); - }, [networksData, isWalletCreated, fetchAccounts, setSelectedNetwork]); - const importWalletHandler = async (recoveryPhrase: string) => { try { const mnemonic = await createWallet(networksData, recoveryPhrase); -- 2.45.2 From 3eedadafc367af368a3c925be27a4f1c16ec03d6 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Fri, 11 Apr 2025 14:08:09 +0530 Subject: [PATCH 09/12] Remove console logs --- src/App.tsx | 24 +----- src/global.d.ts | 41 +++++---- src/hooks/useGetOrCreateAccounts.ts | 24 ------ src/hooks/useSignRequestHandler.ts | 121 ++++++++------------------ src/screens/HomeScreen.tsx | 18 +--- src/screens/SignMessage.tsx | 129 +++++----------------------- 6 files changed, 85 insertions(+), 272 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5fcfcaa..bb563f0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -133,28 +133,16 @@ const App = (): React.JSX.Element => { break; case EIP155_SIGNING_METHODS.PERSONAL_SIGN: - const chainId = request.params.chainId?.split(':')[1]; - const account = accounts.find(acc => acc.address === request.params[1]); - if (!account) { - throw new Error('Account not found'); - } navigation.navigate("SignRequest", { namespace: EIP155, - chainId, address: request.params[1], message: getSignParamsMessage(request.params), - accountInfo: account, requestEvent, requestSessionData, }); break; case COSMOS_METHODS.COSMOS_SIGN_DIRECT: - const cosmosChainId = request.params.chainId?.split(':')[1]; - const cosmosAccount = accounts.find(acc => acc.address === request.params.signerAddress); - if (!cosmosAccount) { - throw new Error('Account not found'); - } const message = { txbody: TxBody.toJSON( TxBody.decode( @@ -173,27 +161,18 @@ const App = (): React.JSX.Element => { }; navigation.navigate("SignRequest", { namespace: COSMOS, - chainId: cosmosChainId, address: request.params.signerAddress, message: JSON.stringify(message, undefined, 2), - accountInfo: cosmosAccount, requestEvent, requestSessionData, }); break; case COSMOS_METHODS.COSMOS_SIGN_AMINO: - const aminoChainId = request.params.chainId?.split(':')[1]; - const aminoAccount = accounts.find(acc => acc.address === request.params.signerAddress); - if (!aminoAccount) { - throw new Error('Account not found'); - } navigation.navigate("SignRequest", { namespace: COSMOS, - chainId: aminoChainId, address: request.params.signerAddress, message: request.params.signDoc.memo, - accountInfo: aminoAccount, requestEvent, requestSessionData, }); @@ -228,7 +207,6 @@ const App = (): React.JSX.Element => { setCurrentIndex, selectedNetwork, web3wallet, - accounts, ], ); @@ -277,7 +255,7 @@ const App = (): React.JSX.Element => { ).privKey; const sender = await DirectSecp256k1Wallet.fromKey( - Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), + Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), network.addressPrefix ); diff --git a/src/global.d.ts b/src/global.d.ts index 1ce5b25..9b9adbf 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,21 +1,30 @@ +// Extends the Window interface for Android WebView communication declare global { interface Window { - Android?: { - onSignatureComplete?: (signature: string) => void; - onSignatureError?: (error: string) => void; - onSignatureCancelled?: () => void; - onJsBridgeReady?: () => void; - onAccountsReady?: () => void; - }; - // Android WebView bridge methods (from TimeDisplay) - receiveTimeFromAndroid?: (timestamp: number) => void; - receiveDataFromAndroid?: (data: string) => void; - receiveSignRequestFromAndroid?: (message: string) => void; - receiveCreateOrGetAccountsRequestFromAndroid?: () => void; - - // Android native bridge object (from useGetOrCreateAccount) + // Android bridge callbacks for signature-related events + Android?: { + // Called when signature is successfully generated + onSignatureComplete?: (signature: string) => void; + // Called when signature generation fails + onSignatureError?: (error: string) => void; + + // Called when signature process is cancelled + onSignatureCancelled?: () => void; + + // Called when JS bridge is initialized + onJsBridgeReady?: () => void; + + // Called when accounts are ready for use + onAccountsReady?: () => void; + }; + + // Handles incoming signature requests from Android + receiveSignRequestFromAndroid?: (message: string) => void; + + // Handles request to create or retrieve accounts + receiveCreateOrGetAccountsRequestFromAndroid?: () => void; } } - -export {}; \ No newline at end of file + +export {}; diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index d681be3..ed3c8c1 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -35,32 +35,8 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { ); }; - const autoCreateAccounts = async () => { - const defaultChainId = networksData[0]?.chainId; - if (!defaultChainId) return; - - let accountsData = await getAccountsData(defaultChainId); - - if (accountsData.length === 0) { - console.log("Auto-creating wallet..."); - await createWallet(networksData); - accountsData = await getAccountsData(defaultChainId); - onWalletCreated?.(); - } - - // Notify Android that accounts are ready - if (window.Android?.onAccountsReady) { - window.Android.onAccountsReady(); - } - }; - window.addEventListener('message', handleCreateAccounts); - const isAndroidWebView = !!(window.Android); - if (isAndroidWebView) { - autoCreateAccounts(); - } - return () => { window.removeEventListener('message', handleCreateAccounts); }; diff --git a/src/hooks/useSignRequestHandler.ts b/src/hooks/useSignRequestHandler.ts index 4735717..1b5974d 100644 --- a/src/hooks/useSignRequestHandler.ts +++ b/src/hooks/useSignRequestHandler.ts @@ -1,141 +1,94 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; + import { useAccounts } from '../context/AccountsContext'; 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 = () => { const { selectedNetwork } = useNetworks(); const { accounts, currentIndex } = useAccounts(); const [isDataReady, setIsDataReady] = useState(false); useEffect(() => { - const logData = { - selectedNetwork: selectedNetwork ? { - namespace: selectedNetwork.namespace, - chainId: selectedNetwork.chainId, - } : 'undefined', - accounts: accounts ? `Array of length ${accounts.length}` : 'undefined', - currentIndex - }; - console.log('Checking data readiness:', JSON.stringify(logData, null, 2)); - if (selectedNetwork && accounts && accounts.length > 0) { setIsDataReady(true); - // Notify Android that accounts are ready - if (window.Android?.onAccountsReady) { - window.Android.onAccountsReady(); - } + // 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(); const pendingMessageRef = useRef(null); const navigateToSignRequest = useCallback(async (message: string) => { - const logData = { - isDataReady, - selectedNetwork: selectedNetwork ? { - namespace: selectedNetwork.namespace, - chainId: selectedNetwork.chainId, - } : 'undefined', - accounts: accounts ? `Array of length ${accounts.length}` : 'undefined', - currentIndex, - message - }; - console.log('Attempting to navigate with data:', JSON.stringify(logData, null, 2)); - + // Queue message if data isn't ready yet if (!isDataReady) { - console.log('Delaying sign request until data is ready...'); pendingMessageRef.current = message; return; } + // Validate required data if (!selectedNetwork) { - console.error('No network selected'); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError('No network selected'); - } + window.Android?.onSignatureError?.('No network selected'); return; } - if (!accounts || accounts.length === 0) { - console.error('No accounts available'); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError('No accounts available'); - } + if (!accounts?.length) { + window.Android?.onSignatureError?.('No accounts available'); return; } const currentAccount = accounts[currentIndex]; if (!currentAccount) { - console.error('Current account not found'); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError('Current account not found'); - } + window.Android?.onSignatureError?.('Current account not found'); return; } - // Verify network properties exist before using them + // Validate network properties if (!selectedNetwork.namespace || !selectedNetwork.chainId) { - const errorData = { - namespace: selectedNetwork.namespace, - chainId: selectedNetwork.chainId - }; - console.error('Network missing required properties:', JSON.stringify(errorData, null, 2)); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError('Network missing required properties'); - } + window.Android?.onSignatureError?.('Network missing required properties'); return; } - const navigationData = { - namespace: selectedNetwork.namespace, - chainId: selectedNetwork.chainId, - address: currentAccount.address, - message - }; - console.log('Navigating to SignRequest with:', JSON.stringify(navigationData, null, 2)); - try { - // Ensure the path matches the expected regex pattern: /sign/(eip155|cosmos)/(.+)/(.+)/(.+) + // Build and validate signing path const path = `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`; - - // Verify the path matches the expected pattern const pathRegex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/; + if (!pathRegex.test(path)) { - console.error('Path does not match expected pattern:', path); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError('Invalid path format'); - } + window.Android?.onSignatureError?.('Invalid path format'); return; } - // Parse the path to get the components const match = path.match(pathRegex); if (!match) { - console.error('Failed to parse path:', path); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError('Failed to parse path'); - } + window.Android?.onSignatureError?.('Failed to parse path'); return; } const [, pathNamespace, pathChainId, pathAddress, pathMessage] = match; + // Navigate to sign request screen navigation.reset({ index: 0, routes: [ { name: 'SignRequest', - path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`, + path, params: { namespace: pathNamespace, chainId: pathChainId, @@ -147,14 +100,11 @@ export const useSignRequestNavigation = () => { ], }); } catch (error) { - console.error('Navigation error:', error); - if (window.Android?.onSignatureError) { - window.Android.onSignatureError(`Navigation error: ${error}`); - } + window.Android?.onSignatureError?.(`Navigation error: ${error}`); } }, [isDataReady, selectedNetwork, accounts, currentIndex, navigation]); - // Handle pending message when data becomes ready + // Process any pending message when data becomes ready useEffect(() => { if (pendingMessageRef.current && isDataReady) { const message = pendingMessageRef.current; @@ -166,28 +116,26 @@ export const useSignRequestNavigation = () => { 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) => { - console.log('Sign request received with message:', message); navigateToSignRequest(message); }; - // Handle create/get accounts request + // Set up accounts request handler window.receiveCreateOrGetAccountsRequestFromAndroid = () => { - console.log('SignRequestHandler: Create/get accounts request received'); - // The useGetOrCreateAccounts hook will handle this + // Handled by useGetOrCreateAccounts hook }; + // Initialize Android bridge if (window.Android) { - setTimeout(() => { - console.log('SignRequestHandler: Calling onJsBridgeReady'); - window.Android?.onJsBridgeReady?.(); - }, 100); - } else { - console.log('SignRequestHandler: Android bridge is not available'); + setTimeout(() => window.Android?.onJsBridgeReady?.(), 100); } return () => { @@ -197,6 +145,9 @@ export const useAndroidBridge = () => { }, [navigateToSignRequest]); }; +/** + * Main hook that combines all sign request handling functionality + */ export const useSignRequestHandler = () => { useGetOrCreateAccounts(); useSignRequestData(); diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index b3b3f56..ca4ce78 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -18,8 +18,6 @@ import { useNetworks } from "../context/NetworksContext"; import ImportWalletDialog from "../components/ImportWalletDialog"; import { MnemonicDialog } from "../components/MnemonicDialog"; import { Container } from "../components/Container"; -import { useSignRequestHandler } from '../hooks/useSignRequestHandler'; -import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; import { IS_IMPORT_WALLET_ENABLED } from "../utils/constants"; const HomeScreen = () => { @@ -66,20 +64,8 @@ const HomeScreen = () => { setPhrase(mnemonic); setSelectedNetwork(networksData[0]); } - setIsWalletCreating(false); }; - const handleWalletCreated = useCallback(() => { - setIsWalletCreated(true); - fetchAccounts(); - }, [fetchAccounts]); - - useGetOrCreateAccounts(handleWalletCreated); - - useEffect(() => { - fetchAccounts(); - }, [networksData, setAccounts, selectedNetwork, fetchAccounts]); - const importWalletHandler = async (recoveryPhrase: string) => { try { const mnemonic = await createWallet(networksData, recoveryPhrase); @@ -128,7 +114,9 @@ const HomeScreen = () => { setCurrentIndex(0); }; - useSignRequestHandler(); + useEffect(() => { + fetchAccounts(); + }, [networksData, setAccounts, selectedNetwork, fetchAccounts]); return ( diff --git a/src/screens/SignMessage.tsx b/src/screens/SignMessage.tsx index a96304d..f7919b9 100644 --- a/src/screens/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -1,6 +1,7 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import { Text, TextInput } from "react-native-paper"; -import { Button, Divider, Stack, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; +import { Button, Divider, Stack } from "@mui/material"; + import { NativeStackScreenProps } from "@react-navigation/native-stack"; import { StackParamsList } from "../types"; @@ -10,82 +11,21 @@ import { Layout } from "../components/Layout"; type SignProps = NativeStackScreenProps; -const SignMessage = ({ route, navigation }: SignProps) => { +const SignMessage = ({ route }: SignProps) => { const namespace = route.params.selectedNamespace; const chainId = route.params.selectedChainId; const account = route.params.accountInfo; - const prefillMessage = route.params.prefillMessage; - const [message, setMessage] = useState(prefillMessage || ""); - const [showConfirmDialog, setShowConfirmDialog] = useState(false); - - // If message is prefilled from Android, show the confirmation dialog immediately - useEffect(() => { - if (prefillMessage) { - setMessage(prefillMessage); - // Show confirmation dialog immediately - setTimeout(() => { - setShowConfirmDialog(true); - }, 0); - } - }, [prefillMessage]); + const [message, setMessage] = useState(""); - const handleConfirmSignature = async () => { - try { - const signedMessage = await signMessage({ - message, - namespace, - chainId, - accountId: account.index, - }); - - // Send the result back to Android and close dialog - if (window.Android?.onSignatureComplete) { - window.Android.onSignatureComplete(signedMessage || ""); - } else { - alert(`Signature: ${signedMessage}`); - } - - setShowConfirmDialog(false); - - // If this was opened from Android, we should return - if (prefillMessage && window.Android) { - // Return to previous screen if applicable - if (navigation.canGoBack()) { - navigation.goBack(); - } - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - - // Send error back to Android - if (window.Android?.onSignatureError) { - window.Android.onSignatureError(errorMessage); - } else { - alert(`Error: ${errorMessage}`); - } - setShowConfirmDialog(false); - } - }; - - const handleCancelSignature = () => { - setShowConfirmDialog(false); - - if (window.Android?.onSignatureCancelled) { - window.Android.onSignatureCancelled(); - } - - // If this was opened from Android, we should return - if (prefillMessage && window.Android) { - // Return to previous screen if applicable - if (navigation.canGoBack()) { - navigation.goBack(); - } - } - }; - - const showSignConfirmation = () => { - setShowConfirmDialog(true); + const signMessageHandler = async () => { + const signedMessage = await signMessage({ + message, + namespace, + chainId, + accountId: account.index, + }); + alert(`Signature ${signedMessage}`); }; return ( @@ -104,43 +44,14 @@ const SignMessage = ({ route, navigation }: SignProps) => { value={message} /> - - - - - + - - - Confirm Signature - - - Are you sure you want to sign the following message? - - - {message} - - - - - - - ); }; -- 2.45.2 From feb86bd5f8aaa4ee41ce037de1d7d4f69fd6e252 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Fri, 11 Apr 2025 18:10:00 +0530 Subject: [PATCH 10/12] Refactor useSignRequestHandler --- src/global.d.ts | 3 - src/hooks/useGetOrCreateAccounts.ts | 62 ++++++++---- src/hooks/useSignRequestHandler.ts | 151 ++++++++++------------------ src/screens/SignMessage.tsx | 2 +- 4 files changed, 96 insertions(+), 122 deletions(-) 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; -- 2.45.2 From 1aabebf2c09bdd0bf91ca72d7b8aadd4dbb4174f Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Fri, 11 Apr 2025 19:17:49 +0530 Subject: [PATCH 11/12] Remove unnecessary code --- src/App.tsx | 4 +- src/global.d.ts | 5 +-- src/hooks/useGetOrCreateAccounts.ts | 3 +- ...RequestHandler.ts => useWebViewHandler.ts} | 38 +++---------------- src/types.ts | 2 - 5 files changed, 10 insertions(+), 42 deletions(-) rename src/hooks/{useSignRequestHandler.ts => useWebViewHandler.ts} (68%) diff --git a/src/App.tsx b/src/App.tsx index bb563f0..ac840a9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,7 +40,7 @@ import { WalletEmbed } from "./screens/WalletEmbed"; import { AutoSignIn } from "./screens/AutoSignIn"; import { checkSufficientFunds, getPathKey, sendMessage } from "./utils/misc"; import useAccountsData from "./hooks/useAccountsData"; -import { useSignRequestHandler } from "./hooks/useSignRequestHandler"; +import { useWebViewHandler } from "./hooks/useWebViewHandler"; const Stack = createStackNavigator(); @@ -280,7 +280,7 @@ const App = (): React.JSX.Element => { const showWalletConnect = useMemo(() => accounts.length > 0, [accounts]); - useSignRequestHandler(); + useWebViewHandler(); return ( diff --git a/src/global.d.ts b/src/global.d.ts index fdbb988..dfb6ad7 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,7 +1,7 @@ // Extends the Window interface for Android WebView communication declare global { interface Window { - // Android bridge callbacks for signature-related events + // Android bridge callbacks for signature and accounts related events Android?: { // Called when signature is successfully generated onSignatureComplete?: (signature: string) => void; @@ -12,9 +12,6 @@ declare global { // Called when signature process is cancelled onSignatureCancelled?: () => void; - // Called when JS bridge is initialized - onJsBridgeReady?: () => void; - // Called when accounts are ready for use onAccountsReady?: () => void; }; diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index be2d3ba..db800ec 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -57,6 +57,7 @@ const useGetOrCreateAccounts = () => { const isAndroidWebView = !!(window.Android); + // TODO: Call method to auto create accounts from android app if (isAndroidWebView) { autoCreateAccounts(); } @@ -67,4 +68,4 @@ const useGetOrCreateAccounts = () => { }, [networksData, getAccountsData, getOrCreateAccountsForChain]); }; -export default useGetOrCreateAccounts; \ No newline at end of file +export default useGetOrCreateAccounts; diff --git a/src/hooks/useSignRequestHandler.ts b/src/hooks/useWebViewHandler.ts similarity index 68% rename from src/hooks/useSignRequestHandler.ts rename to src/hooks/useWebViewHandler.ts index eb74704..a4151ea 100644 --- a/src/hooks/useSignRequestHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef, useCallback } from 'react'; +import { useEffect, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; @@ -7,7 +7,7 @@ import { useNetworks } from '../context/NetworksContext'; import { StackParamsList } from '../types'; import useGetOrCreateAccounts from './useGetOrCreateAccounts'; -export const useSignRequestHandler = () => { +export const useWebViewHandler = () => { // Navigation and context hooks const navigation = useNavigation>(); const { selectedNetwork } = useNetworks(); @@ -16,19 +16,8 @@ export const useSignRequestHandler = () => { // Initialize accounts useGetOrCreateAccounts(); - // Use a ref for pending messages instead of state to avoid re-renders - const pendingMessageRef = useRef(null); - // Core navigation handler const navigateToSignRequest = useCallback((message: string) => { - // Data readiness check - const isDataReady = !!(selectedNetwork && accounts && accounts.length > 0); - - if (!isDataReady) { - pendingMessageRef.current = message; - return; - } - try { // Validation checks if (!selectedNetwork?.namespace || !selectedNetwork?.chainId) { @@ -81,29 +70,12 @@ export const useSignRequestHandler = () => { } }, [selectedNetwork, accounts, currentIndex, navigation]); - // Setup Android bridge and handle pending messages useEffect(() => { - // Setup global function to receive sign requests from Android + // Assign the function to the window object 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); - } - - // Notify Android that JS bridge is ready - if (window.Android) { - const timeoutId = setTimeout(() => window.Android?.onJsBridgeReady?.(), 100); - return () => { - clearTimeout(timeoutId); - window.receiveSignRequestFromAndroid = undefined; - }; - } - return () => { window.receiveSignRequestFromAndroid = undefined; }; - }, [navigateToSignRequest, selectedNetwork, accounts]); -}; \ No newline at end of file + }, [navigateToSignRequest]); // Only the function reference as dependency +}; diff --git a/src/types.ts b/src/types.ts index 8abcb46..3b7341e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,10 +10,8 @@ export type StackParamsList = { selectedNamespace: string; selectedChainId: string; accountInfo: Account; - prefillMessage?: string; }; SignRequest: { - path?: string; namespace: string; chainId?: string; address: string; -- 2.45.2 From e4b9fcdb1a37351d1b116efc58c9c4d3e3dac0a7 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Mon, 14 Apr 2025 13:18:23 +0530 Subject: [PATCH 12/12] Fix error when using build files --- src/hooks/useGetOrCreateAccounts.ts | 17 ++++++++++------- src/hooks/useWebViewHandler.ts | 6 +++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index db800ec..28b51f7 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -4,10 +4,12 @@ import { createWallet } from "../utils/accounts"; import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; +import { useAccounts } from "../context/AccountsContext"; const useGetOrCreateAccounts = () => { const { networksData } = useNetworks(); const { getAccountsData } = useAccountsData(); + const { setAccounts } = useAccounts(); // Wrap the function in useCallback to prevent recreation on each render const getOrCreateAccountsForChain = useCallback(async (chainId: string) => { @@ -19,8 +21,11 @@ const useGetOrCreateAccounts = () => { accountsData = await getAccountsData(chainId); } + // Update the AccountsContext with the new accounts + setAccounts(accountsData); + return accountsData; - }, [networksData, getAccountsData]); + }, [networksData, getAccountsData, setAccounts]); useEffect(() => { const handleCreateAccounts = async (event: MessageEvent) => { @@ -42,14 +47,13 @@ const useGetOrCreateAccounts = () => { console.log('useGetOrCreateAccounts: No default chainId found'); return; } + const accounts = await getOrCreateAccountsForChain(defaultChainId); - await getOrCreateAccountsForChain(defaultChainId); - - // Notify Android that accounts are ready - if (window.Android?.onAccountsReady) { + // Only notify Android when we actually have accounts + if (accounts.length > 0 && window.Android?.onAccountsReady) { window.Android.onAccountsReady(); } else { - console.log('useGetOrCreateAccounts: Android bridge not available'); + console.log('No accounts created or Android bridge not available'); } }; @@ -57,7 +61,6 @@ const useGetOrCreateAccounts = () => { const isAndroidWebView = !!(window.Android); - // TODO: Call method to auto create accounts from android app if (isAndroidWebView) { autoCreateAccounts(); } diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index a4151ea..9085ecd 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -12,10 +12,10 @@ export const useWebViewHandler = () => { const navigation = useNavigation>(); const { selectedNetwork } = useNetworks(); const { accounts, currentIndex } = useAccounts(); - + // Initialize accounts useGetOrCreateAccounts(); - + // Core navigation handler const navigateToSignRequest = useCallback((message: string) => { try { @@ -73,7 +73,7 @@ export const useWebViewHandler = () => { useEffect(() => { // Assign the function to the window object window.receiveSignRequestFromAndroid = navigateToSignRequest; - + return () => { window.receiveSignRequestFromAndroid = undefined; }; -- 2.45.2