UNPKG

@foreverrbum/ethsign

Version:

This package will allow you to electronically sign documents within your application

372 lines (335 loc) 13.8 kB
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);