laconic-wallet/src/components/PairingModal.tsx
shreerang6921 15da99a827 Send back namespaces object based on chains requested by dApp (#94)
* Refactor pairing modal code

* Refactor update session code

* Reset state after session proposal is approved

* Refactor approve and update session

* Remove unused helper methods

* Remove completed todos
2024-04-25 17:08:27 +05:30

247 lines
7.4 KiB
TypeScript

import React, { useEffect, useMemo, useState } from 'react';
import { Image, View, Modal, ScrollView } from 'react-native';
import { Button, Text } from 'react-native-paper';
import { SvgUri } from 'react-native-svg';
import mergeWith from 'lodash/mergeWith';
import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils';
import { PairingModalProps } from '../types';
import styles from '../styles/stylesheet';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { useAccounts } from '../context/AccountsContext';
import { useWalletConnect } from '../context/WalletConnectContext';
import { useNetworks } from '../context/NetworksContext';
import { getNamespaces } from '../utils/wallet-connect/helpers';
const PairingModal = ({
visible,
currentProposal,
setCurrentProposal,
setModalVisible,
setToastVisible,
}: PairingModalProps) => {
const { accounts, currentIndex } = useAccounts();
const { selectedNetwork, networksData } = useNetworks();
const [isLoading, setIsLoading] = useState(false);
const dappName = currentProposal?.params?.proposer?.metadata.name;
const url = currentProposal?.params?.proposer?.metadata.url;
const icon = currentProposal?.params.proposer?.metadata.icons[0];
const [walletConnectData, setWalletConnectData] = useState<{
walletConnectMethods: string[];
walletConnectEvents: string[];
walletConnectChains: string[];
}>({
walletConnectMethods: [],
walletConnectEvents: [],
walletConnectChains: [],
});
const [supportedNamespaces, setSupportedNamespaces] = useState<
Record<
string,
{
chains: string[];
methods: string[];
events: string[];
accounts: string[];
}
>
>();
useEffect(() => {
if (!currentProposal) {
return;
}
const { params } = currentProposal;
const { requiredNamespaces, optionalNamespaces } = params;
setWalletConnectData({
walletConnectMethods: [],
walletConnectEvents: [],
walletConnectChains: [],
});
const combinedNamespaces = mergeWith(
requiredNamespaces,
optionalNamespaces,
(obj, src) =>
Array.isArray(obj) && Array.isArray(src) ? [...src, ...obj] : undefined,
);
Object.keys(combinedNamespaces).forEach(key => {
const { methods, events, chains } = combinedNamespaces[key];
setWalletConnectData(prevData => {
return {
walletConnectMethods: [...prevData.walletConnectMethods, ...methods],
walletConnectEvents: [...prevData.walletConnectEvents, ...events],
walletConnectChains: chains
? [...prevData.walletConnectChains, ...chains]
: [...prevData.walletConnectChains],
};
});
});
}, [currentProposal]);
const { setActiveSessions } = useWalletConnect();
useEffect(() => {
const getSupportedNamespaces = async () => {
if (!currentProposal) {
return;
}
const { optionalNamespaces, requiredNamespaces } = currentProposal.params;
const nameSpaces = await getNamespaces(
optionalNamespaces,
requiredNamespaces,
networksData,
selectedNetwork,
accounts,
currentIndex,
);
setSupportedNamespaces(nameSpaces);
};
getSupportedNamespaces();
}, [currentProposal, networksData, selectedNetwork, accounts, currentIndex]);
const namespaces = useMemo(() => {
return (
currentProposal &&
supportedNamespaces &&
buildApprovedNamespaces({
proposal: currentProposal.params,
supportedNamespaces,
})
);
}, [currentProposal, supportedNamespaces]);
const handleAccept = async () => {
try {
if (currentProposal && namespaces) {
setIsLoading(true);
const { id } = currentProposal;
await web3wallet!.approveSession({
id,
namespaces,
});
const sessions = web3wallet!.getActiveSessions();
setIsLoading(false);
setActiveSessions(sessions);
setModalVisible(false);
setToastVisible(true);
setCurrentProposal(undefined);
setSupportedNamespaces(undefined);
setWalletConnectData({
walletConnectMethods: [],
walletConnectEvents: [],
walletConnectChains: [],
});
}
} catch (error) {
console.error('Error in approve session:', error);
throw error;
}
};
const handleReject = async () => {
if (currentProposal) {
const { id } = currentProposal;
await web3wallet!.rejectSession({
id,
reason: getSdkError('USER_REJECTED_METHODS'),
});
setModalVisible(false);
setCurrentProposal(undefined);
setWalletConnectData({
walletConnectMethods: [],
walletConnectEvents: [],
walletConnectChains: [],
});
}
};
return (
<Modal visible={visible} animationType="slide" transparent>
<View style={{ flex: 1 }}>
<View style={styles.modalContentContainer}>
<ScrollView showsVerticalScrollIndicator={true}>
<View style={styles.container}>
{icon && (
<>
{icon.endsWith('.svg') ? (
<View style={styles.dappLogo}>
<SvgUri height="50" width="50" uri={icon} />
</View>
) : (
<Image style={styles.dappLogo} source={{ uri: icon }} />
)}
</>
)}
<Text variant="titleMedium">{dappName}</Text>
<Text variant="bodyMedium">{url}</Text>
<View style={styles.marginVertical8} />
<Text variant="titleMedium">Connect to this site?</Text>
{walletConnectData.walletConnectMethods.length > 0 && (
<View>
<Text variant="titleMedium">Chains:</Text>
{walletConnectData.walletConnectChains.map(chain => (
<Text style={styles.centerText} key={chain}>
{chain}
</Text>
))}
</View>
)}
{walletConnectData.walletConnectMethods.length > 0 && (
<View style={styles.marginVertical8}>
<Text variant="titleMedium">Methods Requested:</Text>
{walletConnectData.walletConnectMethods.map(method => (
<Text style={styles.centerText} key={method}>
{method}
</Text>
))}
</View>
)}
{walletConnectData.walletConnectEvents.length > 0 && (
<View style={styles.marginVertical8}>
<Text variant="titleMedium">Events Requested:</Text>
{walletConnectData.walletConnectEvents.map(event => (
<Text style={styles.centerText} key={event}>
{event}
</Text>
))}
</View>
)}
</View>
</ScrollView>
<View style={styles.flexRow}>
<Button mode="contained" onPress={handleAccept} loading={isLoading}>
{isLoading ? 'Connecting' : 'Yes'}
</Button>
<View style={styles.space} />
<Button mode="outlined" onPress={handleReject}>
No
</Button>
</View>
</View>
</View>
</Modal>
);
};
export default PairingModal;