diff --git a/src/components/CreateWallet.tsx b/src/components/CreateWallet.tsx index 5ba0708..584377c 100644 --- a/src/components/CreateWallet.tsx +++ b/src/components/CreateWallet.tsx @@ -1,5 +1,5 @@ -import { View } from 'react-native'; import React from 'react'; +import { View } from 'react-native'; import { Button } from 'react-native-paper'; import { CreateWalletProps } from '../types'; diff --git a/src/components/ImportWalletDialog.tsx b/src/components/ImportWalletDialog.tsx new file mode 100644 index 0000000..c05b428 --- /dev/null +++ b/src/components/ImportWalletDialog.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useState } from 'react'; + +import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Grid, Button, Typography } from "@mui/material"; + +const ImportWalletDialog = ({ + visible, + hideDialog, + importWalletHandler +}: { + visible: boolean; + hideDialog: () => void; + importWalletHandler: (recoveryPhrase: string) => Promise; +}) => { + const [words, setWords] = useState(Array(12).fill('')); + + const handleWordChange = (index: number, value: string) => { + const newWords = [...words]; + newWords[index] = value; + setWords(newWords); + }; + + const handlePaste = (event: React.ClipboardEvent) => { + const pastedText = event.clipboardData.getData('Text'); + const splitWords = pastedText.trim().split(/\s+/); + + if (splitWords.length === 12) { + setWords(splitWords); + event.preventDefault(); + } + }; + + useEffect(() => { + setWords(Array(12).fill('')); + },[visible]); + + return ( + + Import your wallet from your mnemonic + + + (You can paste your entire mnemonic into the first textbox) + + + {words.map((word, index) => ( + + handleWordChange(index, e.target.value)} + onPaste={index === 0 ? handlePaste : undefined} + placeholder={`Word ${index + 1}`} + fullWidth + margin="normal" + /> + + ))} + + + + + + + + ); +} + +export default ImportWalletDialog; diff --git a/src/components/Dialog.tsx b/src/components/MnemonicDialog.tsx similarity index 86% rename from src/components/Dialog.tsx rename to src/components/MnemonicDialog.tsx index d907e27..1e10297 100644 --- a/src/components/Dialog.tsx +++ b/src/components/MnemonicDialog.tsx @@ -5,7 +5,7 @@ import styles from '../styles/stylesheet'; import GridView from './Grid'; import { CustomDialogProps } from '../types'; -const DialogComponent = ({ visible, hideDialog, contentText }: CustomDialogProps) => { +const MnemonicDialog = ({ visible, hideDialog, contentText }: CustomDialogProps) => { const words = contentText.split(' '); const copyMnemonic = () => { @@ -26,10 +26,10 @@ const DialogComponent = ({ visible, hideDialog, contentText }: CustomDialogProps - + ); }; -export { DialogComponent }; +export { MnemonicDialog }; diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index db7ee75..83996a6 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -5,9 +5,11 @@ import { Button, Text } from 'react-native-paper'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useNavigation } from '@react-navigation/native'; import { getSdkError } from '@walletconnect/utils'; +import { Portal, Snackbar } from '@mui/material'; import { createWallet, resetWallet, retrieveAccounts } from '../utils/accounts'; -import { DialogComponent } from '../components/Dialog'; +import { MnemonicDialog } from '../components/MnemonicDialog'; +import ImportWalletDialog from '../components/ImportWalletDialog'; import { NetworkDropdown } from '../components/NetworkDropdown'; import Accounts from '../components/Accounts'; import CreateWallet from '../components/CreateWallet'; @@ -55,12 +57,15 @@ const HomeScreen = () => { const [isWalletCreated, setIsWalletCreated] = useState(false); const [isWalletCreating, setIsWalletCreating] = useState(false); - const [walletDialog, setWalletDialog] = useState(false); + const [importWalletDialog, setImportWalletDialog] = useState(false); + const [mnemonicDialog, setMnemonicDialog] = useState(false); const [resetWalletDialog, setResetWalletDialog] = useState(false); + const [toastVisible, setToastVisible] = useState(false); + const [invalidMnemonicError, setInvalidMnemonicError] = useState(''); const [isAccountsFetched, setIsAccountsFetched] = useState(true); const [phrase, setPhrase] = useState(''); - const hideWalletDialog = () => setWalletDialog(false); + const hideMnemonicDialog = () => setMnemonicDialog(false); const hideResetDialog = () => setResetWalletDialog(false); const fetchAccounts = useCallback(async () => { @@ -83,12 +88,27 @@ const HomeScreen = () => { const mnemonic = await createWallet(networksData); if (mnemonic) { fetchAccounts(); - setWalletDialog(true); + setMnemonicDialog(true); setPhrase(mnemonic); setSelectedNetwork(networksData[0]); } }; + const importWalletHandler = async (recoveryPhrase: string) => { + try{ + const mnemonic = await createWallet(networksData, recoveryPhrase); + if (mnemonic) { + fetchAccounts(); + setPhrase(mnemonic); + setSelectedNetwork(networksData[0]); + setImportWalletDialog(false); + } + } catch (error: any) { + setInvalidMnemonicError((error.message as string).toUpperCase()) + setToastVisible(true); + } + }; + const confirmResetWallet = useCallback(async () => { setIsWalletCreated(false); setIsWalletCreating(false); @@ -152,14 +172,28 @@ const HomeScreen = () => { ) : ( - + <> + + + + + )} - setImportWalletDialog(false)} + importWalletHandler={importWalletHandler} + /> + { hideDialog={hideResetDialog} onConfirm={confirmResetWallet} /> + + setToastVisible(false)} + anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }} + ContentProps={{ style: { backgroundColor: 'red', color: 'white'} }} + /> + ); }; diff --git a/src/styles/stylesheet.js b/src/styles/stylesheet.js index 017d59e..ad9b1b8 100644 --- a/src/styles/stylesheet.js +++ b/src/styles/stylesheet.js @@ -5,7 +5,7 @@ const styles = StyleSheet.create({ marginTop: 20, width: 150, alignSelf: 'center', - marginBottom: 40 + marginBottom: 30, }, signLink: { alignItems: 'flex-end', diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts index b3f6f9e..d7921a6 100644 --- a/src/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -27,12 +27,23 @@ import { COSMOS, EIP155 } from './constants'; const createWallet = async ( networksData: NetworksDataState[], + recoveryPhrase?: string, ): Promise => { - const mnemonic = utils.entropyToMnemonic(utils.randomBytes(16)); - await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic); + const mnemonic = recoveryPhrase ? recoveryPhrase : utils.entropyToMnemonic(utils.randomBytes(16)); const hdNode = HDNode.fromMnemonic(mnemonic); + await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic); + await createWalletFromMnemonic(networksData, hdNode, mnemonic); + + return mnemonic; +}; + +const createWalletFromMnemonic = async ( + networksData: NetworksDataState[], + hdNode: HDNode, + mnemonic: string +): Promise => { for (const network of networksData) { const hdPath = `m/44'/${network.coinType}'/0'/0/0`; const node = hdNode.derivePath(hdPath); @@ -73,8 +84,6 @@ const createWallet = async ( ), ]); } - - return mnemonic; }; const addAccount = async (