forked from cerc-io/laconic-wallet
UI to add multiple accounts (#16)
* Change button position * Keep reset button at the bottom * Use dropdown for accounts in separate component * Display data of selected account * Add method to add multiple accounts * Change reset button position * Clear account state on reset * Display correct account info after creating * Added account info to sign page * Change variable names * Use consistent variable names * Use account id in ui * Make review changes * Fix imports --------- Co-authored-by: Adw8 <adwait@deepstacksoft.com>
This commit is contained in:
parent
a158abac0b
commit
31c6999e9f
93
components/Accounts.tsx
Normal file
93
components/Accounts.tsx
Normal file
@ -0,0 +1,93 @@
|
||||
import { View } from 'react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, List, Text } 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';
|
||||
|
||||
const Accounts: React.FC<AccountsProps> = ({
|
||||
network,
|
||||
accounts,
|
||||
updateAccounts,
|
||||
currentIndex,
|
||||
updateIndex,
|
||||
}) => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const handlePress = () => setExpanded(!expanded);
|
||||
|
||||
const addAccountHandler = async () => {
|
||||
const newAccount = await addAccount(network);
|
||||
newAccount && updateAccounts(newAccount);
|
||||
updateIndex(selectedAccounts[selectedAccounts.length -1].id);
|
||||
};
|
||||
|
||||
let selectedAccounts: Account[] = [];
|
||||
|
||||
if (network === 'eth') {
|
||||
selectedAccounts = accounts.ethAccounts;
|
||||
}
|
||||
if (network === 'cosmos') {
|
||||
selectedAccounts = accounts.cosmosAccounts;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<List.Accordion
|
||||
title={`Account ${currentIndex + 1}`}
|
||||
expanded={expanded}
|
||||
onPress={handlePress}>
|
||||
{selectedAccounts &&
|
||||
selectedAccounts.map((account) => (
|
||||
<List.Item
|
||||
key={account.id}
|
||||
title={`Account ${account.id + 1}`}
|
||||
onPress={() => {
|
||||
updateIndex(account.id);
|
||||
setExpanded(false);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</List.Accordion>
|
||||
<View style={{ alignItems: 'center', marginTop: 24 }}>
|
||||
<Button mode="contained" onPress={addAccountHandler}>
|
||||
Add Account
|
||||
</Button>
|
||||
</View>
|
||||
<View style={{ marginTop: 24 }}>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={{ fontWeight: '700' }}>Address: </Text>
|
||||
{selectedAccounts &&
|
||||
selectedAccounts[currentIndex] &&
|
||||
selectedAccounts[currentIndex].address}
|
||||
</Text>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={{ fontWeight: '700' }}>Public Key: </Text>
|
||||
{selectedAccounts &&
|
||||
selectedAccounts[currentIndex] &&
|
||||
selectedAccounts[currentIndex].pubKey}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ alignItems: 'center', marginTop: 24 }}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => {
|
||||
navigation.navigate('SignMessage', {
|
||||
selectedNetwork: network,
|
||||
accountInfo: selectedAccounts[currentIndex],
|
||||
});
|
||||
}}>
|
||||
Sign Message
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Accounts;
|
||||
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { Button, Dialog, Portal, Text } from "react-native-paper";
|
||||
import React from 'react';
|
||||
import { Button, Dialog, Portal, Text } from 'react-native-paper';
|
||||
|
||||
type CustomDialogProps = {
|
||||
visible: boolean;
|
||||
@ -8,7 +8,11 @@ type CustomDialogProps = {
|
||||
titleText?: string;
|
||||
};
|
||||
|
||||
const DialogComponent: React.FC<CustomDialogProps> = ({ visible, hideDialog, titleText, contentText }) => {
|
||||
const DialogComponent: React.FC<CustomDialogProps> = ({
|
||||
visible,
|
||||
hideDialog,
|
||||
contentText,
|
||||
}) => {
|
||||
return (
|
||||
<Portal>
|
||||
<Dialog visible={visible} onDismiss={hideDialog}>
|
||||
|
||||
@ -1,27 +1,25 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Alert, ScrollView, View } from 'react-native';
|
||||
import { Text, Button, Dialog, Portal } from 'react-native-paper';
|
||||
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { createWallet, resetWallet } from '../utils';
|
||||
import { DialogComponent } from './Dialog';
|
||||
import { NetworkDropdown } from './NetworkDropdown';
|
||||
import { StackParamsList, Account } from '../types';
|
||||
import { Account, AccountsState } from '../types';
|
||||
import Accounts from './Accounts';
|
||||
|
||||
const HomeScreen = () => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
const [isWalletCreated, setIsWalletCreated] = useState<boolean>(false);
|
||||
const [isWalletCreating, setIsWalletCreating] = useState<boolean>(false);
|
||||
const [walletDialog, setWalletDialog] = useState<boolean>(false);
|
||||
const [resetWalletDialog, setResetWalletDialog] = useState<boolean>(false);
|
||||
const [network, setNetwork] = useState<string>('eth');
|
||||
const [currentAccount, setCurrentAccount] = useState<Account>();
|
||||
const [ethAccount, setEthAccount] = useState<Account>();
|
||||
const [cosmosAccount, setCosmosAccount] = useState<Account>();
|
||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||
|
||||
const [accounts, setAccounts] = useState<AccountsState>({
|
||||
ethAccounts: [],
|
||||
cosmosAccounts: [],
|
||||
});
|
||||
|
||||
const hideWalletDialog = () => setWalletDialog(false);
|
||||
const hideResetDialog = () => setResetWalletDialog(false);
|
||||
@ -30,10 +28,12 @@ const HomeScreen = () => {
|
||||
setIsWalletCreating(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
const { ethWalletInfo, cosmosWalletInfo } = await createWallet();
|
||||
|
||||
setEthAccount(ethWalletInfo);
|
||||
setCosmosAccount(cosmosWalletInfo);
|
||||
setCurrentAccount(ethWalletInfo);
|
||||
ethWalletInfo &&
|
||||
cosmosWalletInfo &&
|
||||
setAccounts({
|
||||
ethAccounts: [...accounts.ethAccounts, ethWalletInfo],
|
||||
cosmosAccounts: [...accounts.cosmosAccounts, cosmosWalletInfo],
|
||||
});
|
||||
setWalletDialog(true);
|
||||
setIsWalletCreated(true);
|
||||
};
|
||||
@ -42,25 +42,44 @@ const HomeScreen = () => {
|
||||
await resetWallet();
|
||||
setIsWalletCreated(false);
|
||||
setIsWalletCreating(false);
|
||||
setAccounts({
|
||||
ethAccounts: [],
|
||||
cosmosAccounts: [],
|
||||
});
|
||||
setCurrentIndex(0);
|
||||
hideResetDialog();
|
||||
setNetwork('eth');
|
||||
};
|
||||
|
||||
const updateNetwork = (newNetwork: string) => {
|
||||
setNetwork(newNetwork);
|
||||
switch (newNetwork) {
|
||||
case 'eth':
|
||||
setCurrentAccount(ethAccount);
|
||||
break;
|
||||
case 'cosmos':
|
||||
setCurrentAccount(cosmosAccount);
|
||||
break;
|
||||
default:
|
||||
console.error('Error updating network');
|
||||
}
|
||||
setCurrentIndex(0);
|
||||
};
|
||||
|
||||
const updateIndex = (index: number) => {
|
||||
setCurrentIndex(index);
|
||||
};
|
||||
|
||||
const updateAccounts = (account: Account) => {
|
||||
switch (network) {
|
||||
case 'eth':
|
||||
setAccounts({
|
||||
...accounts,
|
||||
ethAccounts: [...accounts.ethAccounts, account],
|
||||
});
|
||||
break;
|
||||
case 'cosmos':
|
||||
setAccounts({
|
||||
...accounts,
|
||||
cosmosAccounts: [...accounts.cosmosAccounts, account],
|
||||
});
|
||||
break;
|
||||
default:
|
||||
Alert.alert('Select a valid network!');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<View style={{ marginTop: 24, paddingHorizontal: 24 }}>
|
||||
<ScrollView style={{ marginTop: 24, paddingHorizontal: 24 }}>
|
||||
<DialogComponent
|
||||
visible={walletDialog}
|
||||
hideDialog={hideWalletDialog}
|
||||
@ -86,30 +105,14 @@ const HomeScreen = () => {
|
||||
selectedNetwork={network}
|
||||
updateNetwork={updateNetwork}
|
||||
/>
|
||||
<Text variant="headlineSmall">Account 1</Text>
|
||||
<View style={{ marginTop: 15, marginBottom: 15 }}>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={{ fontWeight: '700' }}>Address: </Text>
|
||||
{currentAccount && currentAccount.address}
|
||||
</Text>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={{ fontWeight: '700' }}>Public Key: </Text>
|
||||
{currentAccount && currentAccount.pubKey}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ alignItems: 'center', marginTop: 30 }}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => {
|
||||
navigation.navigate('SignMessage', {
|
||||
selectedNetwork: network,
|
||||
});
|
||||
}}>
|
||||
Sign Message
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<View style={{ marginTop: 400, alignSelf: 'center' }}>
|
||||
<Accounts
|
||||
network={network}
|
||||
accounts={accounts}
|
||||
currentIndex={currentIndex}
|
||||
updateIndex={updateIndex}
|
||||
updateAccounts={updateAccounts}
|
||||
/>
|
||||
<View style={{ marginTop: 300, alignSelf: 'center' }}>
|
||||
<Button
|
||||
mode="contained"
|
||||
buttonColor="#B82B0D"
|
||||
@ -122,18 +125,17 @@ const HomeScreen = () => {
|
||||
</View>
|
||||
) : (
|
||||
<View>
|
||||
<Text variant="headlineSmall">Create Wallet</Text>
|
||||
<View style={{ marginTop: 20, width: 150, alignSelf: 'center' }}>
|
||||
<Button
|
||||
mode="contained"
|
||||
loading={isWalletCreating}
|
||||
onPress={createWalletHandler}>
|
||||
{isWalletCreating ? 'Creating' : 'Create'}{' '}
|
||||
{isWalletCreating ? 'Creating' : 'Create Wallet'}{' '}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -2,10 +2,7 @@ import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { List } from 'react-native-paper';
|
||||
|
||||
type NetworkDropdownProps = {
|
||||
selectedNetwork: string;
|
||||
updateNetwork: (network: string) => void;
|
||||
};
|
||||
import { NetworkDropdownProps } from '../types';
|
||||
|
||||
const NetworkDropdown: React.FC<NetworkDropdownProps> = ({
|
||||
updateNetwork,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { View } from 'react-native';
|
||||
import { Button, TextInput } from 'react-native-paper';
|
||||
import React, { useState } from 'react';
|
||||
import { Alert } from 'react-native';
|
||||
import { ScrollView, View, Alert } from 'react-native';
|
||||
import { Button, Text, TextInput } from 'react-native-paper';
|
||||
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
|
||||
@ -12,18 +11,32 @@ type SignProps = NativeStackScreenProps<StackParamsList, 'SignMessage'>;
|
||||
|
||||
const SignMessage = ({ route }: SignProps) => {
|
||||
const network = route.params?.selectedNetwork;
|
||||
const account = route.params?.accountInfo;
|
||||
|
||||
const [message, setMessage] = useState<string>('');
|
||||
|
||||
const signMessageHandler = async () => {
|
||||
if (network) {
|
||||
const signedMessage = await signMessage(message, network, 0);
|
||||
if (!account){
|
||||
throw new Error("Account is not valid");
|
||||
}
|
||||
const signedMessage = await signMessage(message, network, account.id);
|
||||
Alert.alert('Signature', signedMessage);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ marginTop: 30, paddingHorizontal: 48 }}>
|
||||
<ScrollView style={{ marginTop: 24, paddingHorizontal: 24 }}>
|
||||
<View style={{ marginTop: 24, marginBottom: 30 }}>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={{ fontWeight: '700' }}>Address: </Text>
|
||||
{account && account.address}
|
||||
</Text>
|
||||
<Text variant="bodyLarge">
|
||||
<Text style={{ fontWeight: '700' }}>Public Key: </Text>
|
||||
{account && account.pubKey}
|
||||
</Text>
|
||||
</View>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
placeholder="Enter your message"
|
||||
@ -35,7 +48,7 @@ const SignMessage = ({ route }: SignProps) => {
|
||||
Sign
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
24
types.ts
24
types.ts
@ -1,9 +1,10 @@
|
||||
export type StackParamsList = {
|
||||
Laconic: undefined;
|
||||
SignMessage: { selectedNetwork: string } | undefined;
|
||||
SignMessage: { selectedNetwork: string; accountInfo: Account } | undefined;
|
||||
};
|
||||
|
||||
export type Account = {
|
||||
id: number,
|
||||
pubKey: string;
|
||||
address: string;
|
||||
};
|
||||
@ -12,3 +13,24 @@ export type WalletDetails = {
|
||||
ethAccount: Account;
|
||||
cosmosAccount: Account;
|
||||
};
|
||||
|
||||
export type AccountsProps = {
|
||||
network: string;
|
||||
accounts: {
|
||||
ethAccounts: Account[];
|
||||
cosmosAccounts: Account[];
|
||||
};
|
||||
currentIndex: number;
|
||||
updateIndex: (index: number) => void;
|
||||
updateAccounts: (account: Account) => void;
|
||||
};
|
||||
|
||||
export type NetworkDropdownProps = {
|
||||
selectedNetwork: string;
|
||||
updateNetwork: (network: string) => void;
|
||||
};
|
||||
|
||||
export type AccountsState = {
|
||||
ethAccounts: Account[];
|
||||
cosmosAccounts: Account[];
|
||||
};
|
||||
|
||||
24
utils.ts
24
utils.ts
@ -15,8 +15,8 @@ import { AccountData, Secp256k1HdWallet } from '@cosmjs/amino';
|
||||
import { stringToPath } from '@cosmjs/crypto';
|
||||
|
||||
const createWallet = async (): Promise<{
|
||||
ethWalletInfo: { pubKey: string; address: string } | undefined;
|
||||
cosmosWalletInfo: { pubKey: string; address: string } | undefined;
|
||||
ethWalletInfo: { id: number; pubKey: string; address: string } | undefined;
|
||||
cosmosWalletInfo: { id: number; pubKey: string; address: string } | undefined;
|
||||
}> => {
|
||||
try {
|
||||
const mnemonic = utils.entropyToMnemonic(utils.randomBytes(32));
|
||||
@ -41,8 +41,13 @@ const createWallet = async (): Promise<{
|
||||
cosmosNode.privateKey,
|
||||
);
|
||||
|
||||
const ethWalletInfo = { pubKey: ethNode.publicKey, address: ethAddress };
|
||||
const ethWalletInfo = {
|
||||
id: 0,
|
||||
pubKey: ethNode.publicKey,
|
||||
address: ethAddress,
|
||||
};
|
||||
const cosmosWalletInfo = {
|
||||
id: 0,
|
||||
pubKey: cosmosNode.publicKey,
|
||||
address: cosmosAddress,
|
||||
};
|
||||
@ -75,7 +80,6 @@ const signMessage = async (
|
||||
index: number,
|
||||
): Promise<string | undefined> => {
|
||||
try {
|
||||
console.log(walletType);
|
||||
switch (walletType) {
|
||||
case 'eth':
|
||||
return await signEthMessage(message, index);
|
||||
@ -153,6 +157,16 @@ const signCosmosMessage = async (
|
||||
};
|
||||
|
||||
// const createAccount
|
||||
const addAccount = async (network: string) => {
|
||||
// // const index = 5;
|
||||
// switch (network) {
|
||||
// case 'eth':
|
||||
// return dummyEthAccounts[3];
|
||||
// case 'cosmos':
|
||||
// return dummyCosmosAccounts[3];
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
const resetWallet = async () => {
|
||||
// TODO: Add method to reset all the accounts
|
||||
@ -161,4 +175,4 @@ const resetWallet = async () => {
|
||||
await resetInternetCredentials('cosmos:keyServer:0');
|
||||
};
|
||||
|
||||
export { createWallet, signMessage, resetWallet };
|
||||
export { createWallet, signMessage, resetWallet, addAccount };
|
||||
|
||||
Loading…
Reference in New Issue
Block a user