@foreverrbum/ethsign
Version:
This package will allow you to electronically sign documents within your application
372 lines (335 loc) • 13.8 kB
JavaScript
import React, { useEffect, useState } from 'react';
import { withRouter } from 'react-router-dom';
import logo from '../assets/logo-white-orange.svg';
import ethsignBG from '../assets/ethsign-bg-uncropped.jpg';
import CheckIcon from '../assets/check.svg';
import { useStateWithSessionStorage } from "../helpers/sessionStorage";
import {Terms} from './Terms';
import { getChain, getMainNetChain, getTestNetChain } from '../helpers/chains';
import { initializeProvider, connectNetwork, updateNetwork } from '../helpers/wallets/wallets';
import '../styles/sign_in.scss';
import { getSignInWalletLogo, storeNotif } from '../helpers/dashboard';
import { FormattedMessage, useIntl } from 'react-intl';
import { isMobile } from '../helpers/window';
const SignIn = (props) => {
const { handleActivePage, provider, handleProvider, handleFm, torus, handleTorus, walletName, handleWalletName, fortmaticPreferredNetwork, handleFortmaticPreferredNetwork, torusPreferredNetwork, handleTorusPreferredNetwork, networkChanged, handleNetworkChanged } = props;
const [agree, handleAgree] = useStateWithSessionStorage("agreeTerms", false);
const [network, handleNetwork] = useState(null);
const [showNetworks, handleShowNetworks] = useState(false);
const [showWallets, handleShowWallets] = useState(false);
const [updateUI, handleUpdateUI] = useState(false);
const { formatMessage } = useIntl();
const mobile = isMobile();
const wallets = ["Metamask", "imToken", "Torus", "Fortmatic"];
useEffect(() => {
(async () => {
let isSubscribed = true
handleActivePage('login')
await initializeProvider(handleProvider, handleNetwork, handleWalletName);
return () => isSubscribed = false;
})();
}, []);
// This changes the network if the network is changed externally.
// This only considers Metamask wallet because at the time of writing this case is not possible for:
// imToken (page refreshes on network change)
// Torus (at this point there is still no instance of torus wallet)
// Fortmatic (at this point there is still no instance of torus wallet)
useEffect(() => {
let isSubscribed = true;
(async () => {
if(networkChanged && walletName === 'Metamask') {
await updateNetwork(provider, handleNetwork);
handleNetworkChanged(false);
}
})();
return () => isSubscribed = false;
}, [networkChanged])
useEffect(() => {
handleUpdateUI(!updateUI);
}, [network ? network.chainId : null]);
// useEffect(() => {
// let isSubscribed = true;
// if(showWallets == true){
// handleShowNetworks(false)
// }
// return () => isSubscribed = false;
// }, [showWallets])
// useEffect(() => {
// let isSubscribed = true;
// if(showNetworks == true){
// handleShowWallets(false)
// }
// return () => isSubscribed = false;
// }, [showNetworks])
useEffect(() => {
let networkDropdownListener = hideNetworkDropdown.bind(this);
document.addEventListener('click', networkDropdownListener);
return () => document.removeEventListener('click', networkDropdownListener);
}, [showNetworks]);
useEffect(() => {
let walletDropdownListener = hideWalletDropdown.bind(this);
document.addEventListener('click', walletDropdownListener);
return () => document.removeEventListener('click', walletDropdownListener);
}, [showWallets]);
const handleWalletSelect = async(walletName) => {
if( ( (walletName == "Metamask" || walletName == "imToken")&& !window.ethereum) || (walletName == "Metamask" && !window.ethereum?.isMetaMask) || (walletName == "imToken" && !window.ethereum?.isImToken)){
storeNotif(
formatMessage({id: "WALLET_NOT_FOUND"}),
<div>
<FormattedMessage id="WALLET_NOT_FOUND_MSG" values={{ wallet: walletName, mobile: !mobile? formatMessage({id:"WALLET_BROWSER"}):'' }} /> <a className="underline" target="_blank" href={walletName=="Metamask"? "https://metamask.io/download":"https://token.im/download"}><FormattedMessage id="HOW_TO_INSTALL"/></a>
</div>,
"warning"
);
return;
}
if(walletName == 'Fortmatic' || walletName == 'Torus') {
if(network) {
if(walletName == 'Fortmatic' && getChain(network.chainId).fortmaticSupport) {
handleFortmaticPreferredNetwork(network.chainId);
} else if (walletName == 'Torus' && getChain(network.chainId).torusSupport) {
handleTorusPreferredNetwork(network.chainId);
}else{
handleNetwork(null);
}
}
} else {
await initializeProvider(handleProvider, handleNetwork, handleWalletName);
}
handleWalletName(walletName)
handleShowWallets(false)
}
const handleNetworkChange = async (chain) => {
if(walletName === 'Fortmatic' || walletName == 'Torus') {
handleFortmaticPreferredNetwork(chain.chainId);
handleTorusPreferredNetwork(chain.chainId);
handleNetwork(chain);
}else{
if(!provider) {
// Notify the user to enter their password into MetaMask to connect their account to our website.
} else {
await connectNetwork(chain, provider, handleProvider, null, handleNetwork, walletName, handleFm, torus, handleTorus, null);
}
}
handleShowNetworks(false)
}
const verifyTerms = () => {
if(!agree) {
storeNotif("", formatMessage({id: 'MUST_ACCEPT_TERMS'}), 'warning');
}
return agree;
}
const login = () => {
if(verifyTerms()) {
props.history.push('/login');
}
}
const handleAuth = async () => {
// We should not be processing a login attempt if the button is in a disabled state.
if(!(agree && walletName && network)) {
return;
}
if(walletName === "Fortmatic") {
handleFortmaticAuth();
} else if(walletName === "Torus") {
handleTorusAuth();
} else {
handleMetaMaskAuth();
}
}
const handleMetaMaskAuth = async () => {
login();
}
const handleFortmaticAuth = async (e) => {
if(verifyTerms()) {
props.history.push({
pathname: '/login',
search: '',
state: { fortmatic: true, networkId: fortmaticPreferredNetwork },
})
}
}
const handleTorusAuth = async (e) => {
if(verifyTerms()) {
props.history.push({
pathname: '/login',
search: '',
state: { torus: true, networkId: torusPreferredNetwork },
})
}
}
const hideNetworkDropdown = (event) => {
const networkDropdown = document.getElementById('network-dropdown');
if( showNetworks && (!networkDropdown || (event && !networkDropdown.contains(event.target) ) ) ) {
handleShowNetworks(false);
}
}
const hideWalletDropdown = (event) => {
const walletDropdown = document.getElementById('wallet-dropdown');
if(showWallets && (!walletDropdown || (event && !walletDropdown.contains(event.target)))) {
handleShowWallets(false);
}
}
return (
<div className="select-none max-w-7xl mx-auto w-full flex-grow flex flex-col justify-center mt-16 ">
<div className="flex flex-grow flex-col md:flex-row">
<div className="flex-grow relative flex flex-col justify-center">
<img className="bg-ethsign absolute md:-left-72" src={ethsignBG}></img>
<img className="z-10 px-24" src={logo}></img>
</div>
<div className="flex-shrink-0 mb-10 md:mb-0 z-10 mx-3 md:w-112 flex flex-col justify-center mx-3 xs:mx-5 sm:mx-10 md:mx-3 lg:mx-5">
<div className="text-12 font-semibold text-gray-60 my-3">
<FormattedMessage id='SELECT_WALLET'/>
</div>
<div className="flex text-15 my-3 flex-wrap xs:flex-nowrap">
<div className="relative inline-block text-center flex-grow mb-2 xs:mb-0 mr-0 xs:mr-3 ">
<div
onClick={()=>{
handleShowWallets(!showWallets)
}}
className="select-none flex justify-center"
>
{getSignInWalletLogo(walletName) !== null && <img className="h-6 w-6 my-auto mr-2" src={getSignInWalletLogo(walletName)} />}
<div className={`${!walletName && 'invisible'} py-3`}>
{walletName? walletName: 'No Wallet'}
</div>
</div>
{/* <!--
Dropdown menu, show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
--> */}
{showWallets &&
<div id="wallet-dropdown" className={`z-20 origin-top-right absolute right-0 mt-2 w-full rounded-md shadow-lg bg-white ring-orange-500 ring-1 ring-opacity-50 focus:outline-none`} role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabIndex="-1">
<div className="py-1" role="none">
<div>
{
wallets.map((wallet, key) => {
const disabled = wallet=="imToken" && !mobile
return (
<div key={key}
onClick={()=>{
if(!disabled){
handleWalletSelect(wallet)
}
}}
className={`${ disabled && 'text-gray-130 cursor-not-allowed'} hover:bg-gray-25 cursor-pointer block px-4 py-2 text-sm`} role="menuitem" tabIndex="-1" >{wallet}</div>
)
})
}
</div>
</div>
</div>
}
</div>
<div id="wallet-select" className="w-full xs:w-40 flex-shrink-0 font-bold cursor-pointer rounded-lg px-2 text-white bg-orange-500 hover:bg-orange-600 justify-center py-3 flex" onClick={()=>{handleShowWallets(!showWallets)}} >
{walletName? formatMessage({id: 'SWITCH'}) :formatMessage({id: 'SELECT'})} <FormattedMessage id='WALLET' />
</div>
</div>
<div className="text-12 font-semibold text-gray-60 my-3">
<FormattedMessage id='SELECT_NETWORK'/>
</div>
<div className="flex text-15 my-3 flex-wrap xs:flex-nowrap">
<div className="relative inline-block text-center flex-grow mb-2 xs:mb-0 mr-0 xs:mr-3 ">
<div
onClick={()=>{
if(provider){
handleShowNetworks(!showNetworks)
}
}}
>
<button type="button" className={`${walletName? 'cursor-pointer':'cursor-not-allowed'} w-full bg-gray-40 rounded-lg text-gray-300 inline-flex justify-center w-full rounded-md border shadow-sm px-2 py-3 focus:outline-none`} id="menu-button" aria-expanded="true" aria-haspopup="true">
{network ? network.name: <span className="invisible"><FormattedMessage id='NO_NETWORK'/></span>}
</button>
</div>
{/* <!--
Dropdown menu, show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
--> */}
{walletName && showNetworks &&
<div id="network-dropdown" className={`origin-top-right absolute right-0 mt-2 w-full rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none`} role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabIndex="-1">
<div className="py-1" role="none">
<div>
{
getMainNetChain()?.map((chain, key) => {
if(walletName==='Fortmatic') {
if(!chain.fortmaticSupport) {
return null;
}
} else if(walletName === 'Torus') {
if(!chain.torusSupport) {
return null;
}
}
return (
<div key={key}
onClick={()=>{
handleNetworkChange(chain)
}}
className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-sm" role="menuitem" tabIndex="-1" >{chain.name}</div>
)
})
}
</div>
<div className="border-t mx-4"/>
<div >
{
getTestNetChain()?.map((chain, key) => {
if(walletName==='Fortmatic') {
if(!chain.fortmaticSupport) {
return null;
}
} else if(walletName === 'Torus') {
if(!chain.torusSupport) {
return null;
}
}
return (
<div key={key}
onClick={()=>{
handleNetworkChange(chain)
}}
className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-sm" role="menuitem" tabIndex="-1" >{chain.name}</div>
)
})
}
</div>
</div>
</div>
}
</div>
<div className={`${walletName? 'bg-blue-50 cursor-pointer hover:bg-blue-60':'bg-blue-70 cursor-not-allowed '} w-full xs:w-40 flex-shrink-0 font-bold cursor-pointer rounded-lg px-2 text-white justify-center py-3 flex`}
onClick={()=>{
handleShowNetworks(!showNetworks)
}}
>
{network? formatMessage({id: 'SWITCH'}) :formatMessage({id: 'SELECT'})} <FormattedMessage id='NETWORK' />
</div>
</div>
<div className="text-12 mt-3 w-full style-5 flex select-none">
<div className="w-4 h-4 flex-shrink-0 mr-2 border border-gray-300 rounded-sm flex justify-center cursor-pointer"
onClick={() => {
handleAgree(!agree);
}}
>
{agree && <img src={CheckIcon} />}
</div>
<Terms/>
</div>
<div onClick={() => { handleAuth() }} className={`text-15 text-white rounded-lg my-3 py-3 flex justify-center ${agree && walletName && network? 'bg-blue-50 cursor-pointer hover:bg-blue-60':'bg-blue-70 cursor-not-allowed'} `}>
<FormattedMessage id='SIGN_IN' />
</div>
</div>
</div>
</div>
);
}
export default withRouter(SignIn);