laconic-wallet/src/components/Accounts.tsx
IshaVenikar da4bed9c4b Send networks data to dapp (#87)
* Send networks data to dapp

* Reflect switching tabs in dapp dropdown

* Fix current network name not showing in Dapp

* Modify variable names

* Modify method and variable names

* Modify networks type

* Use selectedNetwork state and networkId

* Fix networks context states

* Add separate file for network methods
2024-04-25 17:08:27 +05:30

225 lines
7.1 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { ScrollView, TouchableOpacity, View } from 'react-native';
import { Button, List, Text, useTheme } from 'react-native-paper';
import mergeWith from 'lodash/mergeWith';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { AccountsProps, StackParamsList, Account } from '../types';
import { addAccount } from '../utils/accounts';
import styles from '../styles/stylesheet';
import HDPathDialog from './HDPathDialog';
import AccountDetails from './AccountDetails';
import { useAccounts } from '../context/AccountsContext';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
import { useNetworks } from '../context/NetworksContext';
import { COSMOS, EIP155 } from '../utils/constants';
import { NETWORK_METHODS } from '../utils/wallet-connect/common-data';
const Accounts = ({ currentIndex, updateIndex }: AccountsProps) => {
const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>();
const { accounts, setAccounts } = useAccounts();
const { selectedNetwork } = useNetworks();
const [expanded, setExpanded] = useState(false);
const [isAccountCreating, setIsAccountCreating] = useState(false);
const [hdDialog, setHdDialog] = useState(false);
const [pathCode, setPathCode] = useState('');
const theme = useTheme();
const handlePress = () => setExpanded(!expanded);
const updateAccounts = (account: Account) => {
setAccounts([...accounts, account]);
};
useEffect(() => {
const updateSessions = async () => {
const sessions = (web3wallet && web3wallet.getActiveSessions()) || {};
// Iterate through each session
for (const topic in sessions) {
const session = sessions[topic];
const combinedNamespaces = mergeWith(
session.requiredNamespaces,
session.optionalNamespaces,
(obj, src) =>
Array.isArray(obj) && Array.isArray(src)
? [...src, ...obj]
: undefined,
);
const namespaceChainId = `${selectedNetwork?.namespace}:${selectedNetwork?.chainId}`;
let updatedNamespaces;
switch (selectedNetwork?.namespace) {
case EIP155:
updatedNamespaces = {
eip155: {
chains: [namespaceChainId],
// TODO: Debug optional namespace methods and events being required for approval
methods: [
...Object.values(EIP155_SIGNING_METHODS),
...Object.values(NETWORK_METHODS),
...(combinedNamespaces.eip155?.methods ?? []),
],
events: [...(combinedNamespaces.eip155?.events ?? [])],
accounts: accounts.map(ethAccount => {
return `${namespaceChainId}:${ethAccount.address}`;
}),
},
cosmos: {
chains: [],
methods: [],
events: [],
accounts: [],
},
};
break;
case COSMOS:
updatedNamespaces = {
cosmos: {
chains: [namespaceChainId],
methods: [
...Object.values(COSMOS_METHODS),
...Object.values(NETWORK_METHODS),
...(combinedNamespaces.cosmos?.methods ?? []),
],
events: [...(combinedNamespaces.cosmos?.events ?? [])],
accounts: accounts.map(cosmosAccount => {
return `${namespaceChainId}:${cosmosAccount.address}`;
}),
},
eip155: {
chains: [],
methods: [],
events: [],
accounts: [],
},
};
break;
default:
break;
}
if (!updatedNamespaces) {
return;
}
await web3wallet!.updateSession({
topic,
namespaces: updatedNamespaces,
});
}
};
// Call the updateSessions function when the 'accounts' dependency changes
updateSessions();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [accounts]);
const addAccountHandler = async () => {
setIsAccountCreating(true);
const newAccount = await addAccount(selectedNetwork!);
setIsAccountCreating(false);
if (newAccount) {
updateAccounts(newAccount);
updateIndex(newAccount.index);
}
};
const renderAccountItems = () =>
accounts.map(account => (
<List.Item
key={account.index}
title={`Account ${account.index + 1}`}
onPress={() => {
updateIndex(account.index);
setExpanded(false);
}}
/>
));
return (
<ScrollView>
<View>
<HDPathDialog
visible={hdDialog}
hideDialog={() => setHdDialog(false)}
updateAccounts={updateAccounts}
updateIndex={updateIndex}
pathCode={pathCode}
/>
<List.Accordion
title={`Account ${currentIndex + 1}`}
expanded={expanded}
onPress={handlePress}>
{renderAccountItems()}
</List.Accordion>
<View style={styles.addAccountButton}>
<Button
mode="contained"
onPress={addAccountHandler}
loading={isAccountCreating}>
{isAccountCreating ? 'Adding' : 'Add Account'}
</Button>
</View>
<View style={styles.addAccountButton}>
<Button
mode="contained"
onPress={() => {
setHdDialog(true);
// TODO: Use coin type while adding from HD path
setPathCode(
selectedNetwork!.namespace === EIP155
? "m/44'/60'/"
: "m/44'/118'/",
);
}}>
Add Account from HD path
</Button>
</View>
<AccountDetails account={accounts[currentIndex]} />
<View style={styles.signLink}>
<TouchableOpacity
onPress={() => {
navigation.navigate('SignMessage', {
selectedNamespace: selectedNetwork!.namespace,
selectedChainId: selectedNetwork!.chainId,
accountInfo: accounts[currentIndex],
});
}}>
<Text
variant="titleSmall"
style={[styles.hyperlink, { color: theme.colors.primary }]}>
Sign Message
</Text>
</TouchableOpacity>
</View>
<View style={styles.signLink}>
<TouchableOpacity
onPress={() => {
navigation.navigate('AddNetwork');
}}>
<Text
variant="titleSmall"
style={[styles.hyperlink, { color: theme.colors.primary }]}>
Add Network
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
};
export default Accounts;