From cad0b6fae5ee1fd291f39f58811ccf9011e0bfb7 Mon Sep 17 00:00:00 2001 From: Adwait Gharpure <69599306+Adw8@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:39:42 +0530 Subject: [PATCH] Refactor code (#18) * Refactored accounts and sign message component * Change sign message to hyperlink * Refactor network dropdown * Add types to utils * Import react in index.js * Use components for create wallet and reset dialog * Remove inline styles from accounts component * Remove inline styles from components * Remove incorrectly placed async * Make app responsive using flex * Make review changes --------- Co-authored-by: Adw8 --- App.tsx | 2 +- components/Accounts.tsx | 89 ++++++++++++++++++-------------- components/CreateWallet.tsx | 26 ++++++++++ components/HomeScreen.tsx | 82 +++++++++++++---------------- components/NetworkDropdown.tsx | 45 ++++++++-------- components/ResetWalletDialog.tsx | 28 ++++++++++ components/Section.tsx | 29 ----------- components/SignMessage.tsx | 38 +++++++++----- constants.ts | 5 -- index.js | 1 + styles/stylesheet.js | 67 +++++++++++++++++------- types.ts | 21 +++++++- utils.ts | 42 ++++++--------- 13 files changed, 276 insertions(+), 199 deletions(-) create mode 100644 components/CreateWallet.tsx create mode 100644 components/ResetWalletDialog.tsx delete mode 100644 components/Section.tsx delete mode 100644 constants.ts diff --git a/App.tsx b/App.tsx index 4c639aa..0effacf 100644 --- a/App.tsx +++ b/App.tsx @@ -4,7 +4,7 @@ import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import SignMessage from './components/SignMessage'; -import { HomeScreen } from './components/HomeScreen'; +import HomeScreen from './components/HomeScreen'; import { StackParamsList } from './types'; diff --git a/components/Accounts.tsx b/components/Accounts.tsx index 2d726d3..d5cb2e8 100644 --- a/components/Accounts.tsx +++ b/components/Accounts.tsx @@ -1,12 +1,13 @@ -import { View } from 'react-native'; import React, { useState } from 'react'; -import { Button, List, Text } from 'react-native-paper'; +import { TouchableOpacity, View } from 'react-native'; +import { Button, List, Text, useTheme } from 'react-native-paper'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { AccountsProps, StackParamsList, Account } from '../types'; import { addAccount } from '../utils'; +import styles from '../styles/stylesheet'; const Accounts: React.FC = ({ network, @@ -20,24 +21,44 @@ const Accounts: React.FC = ({ const [expanded, setExpanded] = useState(false); const [isAccountCreating, setIsAccountCreating] = useState(false); + const handlePress = () => setExpanded(!expanded); const addAccountHandler = async () => { setIsAccountCreating(true); const newAccount = await addAccount(network); setIsAccountCreating(false); - newAccount && updateAccounts(newAccount); - updateIndex(selectedAccounts[selectedAccounts.length - 1].id + 1); + setExpanded(false); + + if (newAccount) { + updateAccounts(newAccount); + updateIndex(selectedAccounts[selectedAccounts.length - 1].id + 1); + } }; - let selectedAccounts: Account[] = []; + const selectedAccounts: Account[] = (() => { + switch (network) { + case 'eth': + return accounts.ethAccounts; + case 'cosmos': + return accounts.cosmosAccounts; + default: + return []; + } + })(); - if (network === 'eth') { - selectedAccounts = accounts.ethAccounts; - } - if (network === 'cosmos') { - selectedAccounts = accounts.cosmosAccounts; - } + const renderAccountItems = () => + selectedAccounts.map(account => ( + { + updateIndex(account.id); + setExpanded(false); + }} + /> + )); + const theme = useTheme(); return ( @@ -45,51 +66,43 @@ const Accounts: React.FC = ({ title={`Account ${currentIndex + 1}`} expanded={expanded} onPress={handlePress}> - {selectedAccounts && - selectedAccounts.map(account => ( - { - updateIndex(account.id); - setExpanded(false); - }} - /> - ))} + {renderAccountItems()} - + + - + + - Address: - {selectedAccounts && - selectedAccounts[currentIndex] && - selectedAccounts[currentIndex].address} + Address: + {selectedAccounts[currentIndex]?.address} - Public Key: - {selectedAccounts && - selectedAccounts[currentIndex] && - selectedAccounts[currentIndex].pubKey} + Public Key: + {selectedAccounts[currentIndex]?.pubKey} - - + + Sign Message + + ); diff --git a/components/CreateWallet.tsx b/components/CreateWallet.tsx new file mode 100644 index 0000000..d3814e7 --- /dev/null +++ b/components/CreateWallet.tsx @@ -0,0 +1,26 @@ +import { View } from 'react-native'; +import React from 'react'; +import { Button } from 'react-native-paper'; + +import { CreateWalletProps } from '../types'; +import styles from '../styles/stylesheet'; + +const CreateWallet = ({ + isWalletCreating, + createWalletHandler, +}: CreateWalletProps) => { + return ( + + + + + + ); +}; + +export default CreateWallet; diff --git a/components/HomeScreen.tsx b/components/HomeScreen.tsx index 03fc297..0f66164 100644 --- a/components/HomeScreen.tsx +++ b/components/HomeScreen.tsx @@ -1,12 +1,15 @@ import React, { useState } from 'react'; -import { Alert, ScrollView, View } from 'react-native'; -import { Text, Button, Dialog, Portal } from 'react-native-paper'; +import { Alert, View } from 'react-native'; +import { Button } from 'react-native-paper'; import { createWallet, resetWallet } from '../utils'; import { DialogComponent } from './Dialog'; import { NetworkDropdown } from './NetworkDropdown'; import { Account, AccountsState } from '../types'; import Accounts from './Accounts'; +import CreateWallet from './CreateWallet'; +import ResetWalletDialog from './ResetWalletDialog'; +import styles from '../styles/stylesheet'; const HomeScreen = () => { const [isWalletCreated, setIsWalletCreated] = useState(false); @@ -27,12 +30,12 @@ const HomeScreen = () => { const createWalletHandler = async () => { setIsWalletCreating(true); await new Promise(resolve => setTimeout(resolve, 2000)); - const { ethWalletInfo, cosmosWalletInfo } = await createWallet(); - ethWalletInfo && - cosmosWalletInfo && + const { ethAccounts, cosmosAccounts } = await createWallet(); + ethAccounts && + cosmosAccounts && setAccounts({ - ethAccounts: [...accounts.ethAccounts, ethWalletInfo], - cosmosAccounts: [...accounts.cosmosAccounts, cosmosWalletInfo], + ethAccounts: [...accounts.ethAccounts, ethAccounts], + cosmosAccounts: [...accounts.cosmosAccounts, cosmosAccounts], }); setWalletDialog(true); setIsWalletCreated(true); @@ -79,64 +82,53 @@ const HomeScreen = () => { } }; return ( - + - - - Reset Wallet - - Are you sure? - - - - - - - + + {isWalletCreated ? ( - + <> - - + + + + - + ) : ( - - - - - + )} - + ); }; -export { HomeScreen }; +export default HomeScreen; diff --git a/components/NetworkDropdown.tsx b/components/NetworkDropdown.tsx index 0a385ed..37ad624 100644 --- a/components/NetworkDropdown.tsx +++ b/components/NetworkDropdown.tsx @@ -3,38 +3,41 @@ import { View } from 'react-native'; import { List } from 'react-native-paper'; import { NetworkDropdownProps } from '../types'; +import styles from '../styles/stylesheet'; const NetworkDropdown: React.FC = ({ updateNetwork }) => { const [expanded, setExpanded] = useState(false); - const [title, setTitle] = useState('Ethereum'); + const [selectedNetwork, setSelectedNetwork] = useState('Ethereum'); - const expandNetworks = () => setExpanded(!expanded); + const handleNetworkPress = (network: string, displayName: string) => { + updateNetwork(network); + setSelectedNetwork(displayName); + setExpanded(false); + }; return ( - + - { - updateNetwork('eth'); - setTitle('Ethereum'); - setExpanded(false); - }} - /> - { - updateNetwork('cosmos'); - setTitle('Cosmos'); - setExpanded(false); - }} - /> + onPress={() => setExpanded(!expanded)}> + {networks.map(network => ( + + handleNetworkPress(network.value, network.displayName) + } + /> + ))} ); }; +const networks = [ + { value: 'eth', displayName: 'Ethereum' }, + { value: 'cosmos', displayName: 'Cosmos' }, +]; + export { NetworkDropdown }; diff --git a/components/ResetWalletDialog.tsx b/components/ResetWalletDialog.tsx new file mode 100644 index 0000000..7a4bb51 --- /dev/null +++ b/components/ResetWalletDialog.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Portal, Dialog, Button, Text } from 'react-native-paper'; +import { ResetDialogProps } from '../types'; + +const ResetWalletDialog = ({ + visible, + hideDialog, + onConfirm, +}: ResetDialogProps) => { + return ( + + + Reset Wallet + + Are you sure? + + + + + + + + ); +}; + +export default ResetWalletDialog; diff --git a/components/Section.tsx b/components/Section.tsx deleted file mode 100644 index f1ab8fe..0000000 --- a/components/Section.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { Text, View, useColorScheme } from 'react-native'; -import { Colors } from 'react-native/Libraries/NewAppScreen'; - -import styles from '../styles/stylesheet'; - -type SectionProps = PropsWithChildren<{ - title: string; -}>; - -const Section = ({ title }: SectionProps): React.JSX.Element => { - const isDarkMode = useColorScheme() === 'dark'; - return ( - - - {title} - - - - ); -}; - -export { Section }; diff --git a/components/SignMessage.tsx b/components/SignMessage.tsx index 91b7f19..64884f7 100644 --- a/components/SignMessage.tsx +++ b/components/SignMessage.tsx @@ -6,6 +6,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { StackParamsList } from '../types'; import { signMessage } from '../utils'; +import styles from '../styles/stylesheet'; type SignProps = NativeStackScreenProps; @@ -20,30 +21,43 @@ const SignMessage = ({ route }: SignProps) => { if (!account) { throw new Error('Account is not valid'); } - const signedMessage = await signMessage(message, network, account.id); + const signedMessage = await signMessage({ + message, + network, + accountId: account.id, + }); Alert.alert('Signature', signedMessage); } }; return ( - - - - Address: - {account && account.address} - - - Public Key: - {account && account.pubKey} - + + + + + {account && `Account ${account.id + 1}`} + + + + + Address: + {account?.address} + + + Public Key: + {account?.pubKey} + + + setMessage(text)} value={message} /> - + + diff --git a/constants.ts b/constants.ts deleted file mode 100644 index fdb44ab..0000000 --- a/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const COSMOS_SIGNATURE = - '0x56da25d5a9704e0cd685d52ecee5c14bf6637fa2f95653e8499eac4e8285f37b2d9f446c027cac56f3b7840d1b3879ea943415190d7a358cdb3ee05451cdcf7c1c'; -export const COSMOS_ADDRESS = 'cosmos1sulk9q5fmagur6m3pctmcnfeeku25gp2ectt75'; -export const COSMOS_PUBKEY = - 'cosmospub1addwnpepqt9d597c5f6zqqyxy3msrstyc7zl3vyvrl5ku02r4ueuwt5vusw4gmt70dd'; diff --git a/index.js b/index.js index bc4fafc..8a143d6 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +import React from 'react'; import 'text-encoding-polyfill'; import { AppRegistry } from 'react-native'; import { PaperProvider } from 'react-native-paper'; diff --git a/styles/stylesheet.js b/styles/stylesheet.js index e6c22ad..cab8391 100644 --- a/styles/stylesheet.js +++ b/styles/stylesheet.js @@ -1,32 +1,59 @@ import { StyleSheet } from 'react-native'; const styles = StyleSheet.create({ - headerContainer: { - padding: 15, - alignItems: 'center', - justifyContent: 'center', + createWalletContainer: { + marginTop: 20, + width: 150, + alignSelf: 'center', }, - headerText: { - color: 'black', - fontSize: 26, - fontWeight: 'bold', + signLink: { + alignItems: 'flex-end', + marginTop: 24, }, - sectionContainer: { - marginTop: 32, - paddingHorizontal: 24, - }, - sectionTitle: { - fontSize: 22, - fontWeight: '600', - }, - sectionDescription: { - marginTop: 8, - fontSize: 18, - fontWeight: '400', + hyperlink: { + fontWeight: '500', + textDecorationLine: 'underline', }, highlight: { fontWeight: '700', }, + accountContainer: { + marginTop: 24, + }, + addAccountButton: { + marginTop: 24, + alignSelf: 'center', + }, + accountComponent: { + flex: 4, + }, + appContainer: { + flexGrow: 1, + marginTop: 24, + paddingHorizontal: 24, + }, + resetContainer: { + flex: 1, + justifyContent: 'center', + }, + resetButton: { + alignSelf: 'center', + }, + signButton: { + marginTop: 20, + width: 150, + alignSelf: 'center', + }, + signPage: { + paddingHorizontal: 24, + }, + accountInfo: { + marginTop: 24, + marginBottom: 30, + }, + networkDropdown: { + marginBottom: 20, + }, }); export default styles; diff --git a/types.ts b/types.ts index a8c86ca..5a49569 100644 --- a/types.ts +++ b/types.ts @@ -10,8 +10,8 @@ export type Account = { }; export type WalletDetails = { - ethAccount: Account; - cosmosAccount: Account; + ethAccounts: Account | undefined; + cosmosAccounts: Account | undefined; }; export type AccountsProps = { @@ -34,3 +34,20 @@ export type AccountsState = { ethAccounts: Account[]; cosmosAccounts: Account[]; }; + +export type SignMessageParams = { + message: string; + network: string; + accountId: number; +}; + +export type CreateWalletProps = { + isWalletCreating: boolean; + createWalletHandler: () => Promise; +}; + +export type ResetDialogProps = { + visible: boolean; + hideDialog: () => void; + onConfirm: () => void; +}; diff --git a/utils.ts b/utils.ts index df5f365..ce928e5 100644 --- a/utils.ts +++ b/utils.ts @@ -14,10 +14,9 @@ import { import { AccountData, Secp256k1HdWallet } from '@cosmjs/amino'; import { stringToPath } from '@cosmjs/crypto'; -const createWallet = async (): Promise<{ - ethWalletInfo: { id: number; pubKey: string; address: string } | undefined; - cosmosWalletInfo: { id: number; pubKey: string; address: string } | undefined; -}> => { +import { Account, SignMessageParams, WalletDetails } from './types'; + +const createWallet = async (): Promise => { try { const mnemonic = utils.entropyToMnemonic(utils.randomBytes(32)); await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic); @@ -43,35 +42,26 @@ const createWallet = async (): Promise<{ await setInternetCredentials('eth:accountIndices', 'ethAccount', '0'); await setInternetCredentials('cosmos:accountIndices', 'cosmosAccount', '0'); - const ethWalletInfo = { + const ethAccounts = { id: 0, pubKey: ethNode.publicKey, address: ethAddress, }; - const cosmosWalletInfo = { + const cosmosAccounts = { id: 0, pubKey: cosmosNode.publicKey, address: cosmosAddress, }; - return { ethWalletInfo, cosmosWalletInfo }; + return { ethAccounts, cosmosAccounts }; } catch (error) { console.error('Error creating HD wallet:', error); - return { ethWalletInfo: undefined, cosmosWalletInfo: undefined }; + return { ethAccounts: undefined, cosmosAccounts: undefined }; } }; -const addAccount = async ( - network: string, -): Promise< - | { - pubKey: string; - address: string; - id: number; - } - | undefined -> => { +const addAccount = async (network: string): Promise => { try { const mnemonicStore = await getInternetCredentials('mnemonicServer'); if (!mnemonicStore) { @@ -138,16 +128,16 @@ const addAccount = async ( } }; -const signMessage = async ( - message: string, - walletType: string, - id: number, -): Promise => { - switch (walletType) { +const signMessage = async ({ + message, + network, + accountId, +}: SignMessageParams): Promise => { + switch (network) { case 'eth': - return await signEthMessage(message, id); + return await signEthMessage(message, accountId); case 'cosmos': - return await signCosmosMessage(message, id); + return await signCosmosMessage(message, accountId); default: throw new Error('Invalid wallet type'); }