dappros/ethora

View on GitHub
client-reactnative/src/Screens/Account/Authentication.tsx

Summary

Maintainability
D
2 days
Test Coverage
import Clipboard from '@react-native-clipboard/clipboard';
import { useWalletConnect } from '@walletconnect/react-native-dapp';
import React, { useEffect, useState } from 'react';
import { Linking, StyleSheet, Text, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { commonColors, textStyles } from '../../../docs/config';
import { Button } from '../../components/Button';
import { DeleteDialog } from '../../components/Modals/DeleteDialog';
import { ScanQrModal } from '../../components/Modals/ScanQrModal';
import SecondaryHeader from '../../components/SecondaryHeader/SecondaryHeader';
import { showError, showSuccess } from '../../components/Toast/toast';
import { httpGet, httpDelete, httpPost } from '../../config/apiService';
import { isAddress } from '../../helpers/isAddress';
import { useStores } from '../../stores/context';

//interfaces and types
export interface IAuthentication { }
//interfaces and types


//handle to get mail domain eg: Gmail etc
const getMail = (email: string) => {
  if (!email) return '';
  const splittedEmail = email.split('@');
  if (splittedEmail.length) {
    return splittedEmail[1].split('.')[0];
  }
  return '';
};
const walletRoute = '/wallets/ext-wallet/';

export const AuthenticationScreen: React.FC<IAuthentication> = ({ }) => {

  //mobx stores
  const { loginStore } = useStores();
  //mobx stores

  //local states
  const [showQrScan, setShowQrScan] = useState(false);
  const [showRemoveAccount, setShowRemoveAccount] = useState(false);
  const [loading, setLoading] = useState(false);
  const [account, setAccount] = useState('');
  const [accountVerified, setAccountVerified] = useState(false);
  //local states

  //local variables
  const connector = useWalletConnect();
  //local variables

  //hooks
  useEffect(() => {
    getAddress();
    return () => {
      connector.killSession();
    };
  }, []);
  
  useEffect(() => {
    if (connector.accounts.length) {
      updateAddress(connector.accounts[0]);
    }
  }, [connector.accounts]);
  //hooks

  //handle to connect to metamask
  const onMetamaskPress = () => {
    connector.connect({ chainId: 1 });
  };

  //handle to show qr
  const onQRPress = () => {
    setShowQrScan(true);
  };


  const onRemovePress = () => {
    setShowRemoveAccount(true);
  };

  //handle to get mainnet address
  const getAddress = async () => {
    setLoading(true);
    try {
      const res = await httpGet(
        walletRoute + loginStore.initialData.walletAddress,
        loginStore.userToken,
      );
      if (res.data.result) {
        setAccount(res.data.result.address);
        setAccountVerified(res.data.result?.verified);
      }
    } catch (error: any) {
      console.log(error.response);
    }
    setLoading(false);
  };

  //handle to open qr scanner
  const onQRScan = async (e: any) => {
    const data = e.data?.split(':')[1]?.split('@')[0];
    if (!isAddress(data)) {
      showError('Error', 'This doesnt look like a valid Ethereum address');
      setShowQrScan(false);
      return;
    }
    setAccount(data);
    await updateAddress(data);
  };


  //handle to remove mainnet address
  const removeAddress = async () => {
    setLoading(true);
    try {
      const res = await httpDelete(walletRoute + account, loginStore.userToken);
      console.log(res.data);

      showSuccess('Success', 'Your Mainnet address was successfully removed.');
      setAccountVerified(false);
      setAccount('');
    } catch (error) {
      console.log(error);
      showError('Error', 'Something went wrong, please try again');
    }
    setLoading(false);
    setShowRemoveAccount(false);
  };

  //update or add new address
  const updateAddress = async (address?: string) => {
    try {
      const res = await httpPost(
        walletRoute,
        {
          address: address || account,
        },
        loginStore.userToken,
      );
      console.log(res.data);
      setAccount(res.data.data.address);

      showSuccess('Success', 'Your address was successfully added.');
    } catch (error) {
      console.log(error);
      showError('Error', 'Something went wrong, please try again');
    }
  };

  //ui to for displaying the main net address section
  const renderConnected = () => {
    if (account) {
      return (
        <>
          <View>
            <Text style={styles.title}>Mainnet address</Text>
            <Text style={styles.description}>
              You have confirmed the following address on Ethereum Mainnet:
            </Text>
            <TouchableOpacity
              onPress={() => {
                Clipboard.setString(account);
              }}>
              <Text style={styles.boldFont}>{account} 📋</Text>
            </TouchableOpacity>
            <TouchableOpacity
              onPress={() =>
                Linking.openURL('https://etherscan.io/address/' + account)
              }>
              <Text
                style={{
                  color: commonColors.primaryColor,
                  textDecorationLine: 'underline',
                  textAlign: 'center',
                }}>
                (View on Etherscan)
              </Text>
            </TouchableOpacity>
          </View>
          {!accountVerified && (
            <>
              <View style={{ marginTop: 20 }}>
                <Text style={styles.description}>
                  Use button below if you need to remove your Mainnet address
                  association from your Ethora account.
                </Text>
              </View>
              <View style={styles.buttonBlock}>
                <View style={{ width: '50%' }}>
                  <Button
                    onPress={onRemovePress}
                    title="Remove"
                    style={{ backgroundColor: 'red', marginBottom: 10 }}
                  />
                </View>
              </View>
            </>
          )}
        </>
      );
    }
    return (
      <>
        <View>
          <Text style={styles.title}>Mainnet address</Text>
          <Text style={styles.description}>
            (Optional) confirm your L1 (Ethereum Mainnet) wallet address here if
            you need to export your assets to Mainnet or carry out other L1
            related transactions.
          </Text>
        </View>
        <View style={styles.buttonBlock}>
          <View style={{ width: '50%' }}>
            <Button
              loading={loading}
              onPress={onMetamaskPress}
              title="Read from Metamask"
              style={{ backgroundColor: '#cc6228', marginBottom: 10 }}
            />
            <Button
              loading={loading}
              onPress={onQRPress}
              title="QR Scan"
              style={{ backgroundColor: 'lightgrey' }}
              textStyle={{ color: 'black' }}
            />
          </View>
        </View>
      </>
    );
  };
  return (
    <View>
      <SecondaryHeader title="Authentication" />
      <View style={{ paddingHorizontal: 10 }}>
        {renderConnected()}
        <View>
          <Text style={styles.title}>L2 Address</Text>
          <Text style={styles.description}>
            Your local address within Ethora side chain is:
          </Text>
          <TouchableOpacity
            onPress={() =>
              Clipboard.setString(loginStore.initialData.walletAddress)
            }>
            <Text style={styles.boldFont}>
              {loginStore.initialData.walletAddress} 📋
            </Text>
          </TouchableOpacity>
        </View>
        <View>
          <Text style={styles.title}>Ethora Sign On method</Text>
          <Text style={styles.description}>
            Your current sign on method is:
          </Text>
          <Text style={{ textAlign: 'center' }}>
            <Text style={[styles.boldFont, { textTransform: 'capitalize' }]}>
              {getMail(loginStore.initialData.email)}
            </Text>{' '}
            (
            {loginStore.initialData.email ??
              loginStore.initialData.username ??
              loginStore.initialData.walletAddress}
            )
          </Text>
          <View style={{ marginTop: 20 }}>
            <Text style={styles.description}>
              Note: different sign on methods will generate different identities
              in our L2 chain. Please make sure to use the same sign on method
              to operate same profile and assets.
            </Text>
          </View>
        </View>
      </View>
      <ScanQrModal
        closeModal={() => setShowQrScan(false)}
        open={showQrScan}
        onSuccess={onQRScan}
      />
      <DeleteDialog
        title="Would you like to remove your Mainnet address association?"
        description={
          'Note: you can always add an association again using this screen later.'
        }
        cancelButtonTitle={'No, Cancel'}
        deleteButtonTitle={'Yes, Remove'}
        loading={loading}
        onDeletePress={removeAddress}
        onClose={() => setShowRemoveAccount(false)}
        open={showRemoveAccount}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  title: {
    textAlign: 'center',
    fontFamily: textStyles.boldFont,
    marginTop: 20,
    color: 'black',
    fontSize: 16,
  },
  description: {
    fontFamily: textStyles.mediumFont,
    color: 'black',
  },
  boldFont: {
    fontWeight: 'bold',
    color: 'black',
  },
  buttonBlock: {
    justifyContent: 'center',
    alignItems: 'center',
    marginTop: 10,
  },
});