@foreverrbum/ethsign
Version:
This package will allow you to electronically sign documents within your application
447 lines (419 loc) • 14.9 kB
JavaScript
import React, { useState, useEffect } from 'react';
import '../styles/main.css';
import '../styles/index.scss';
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import ReactNotification from 'react-notifications-component';
import 'react-notifications-component/dist/theme.css';
import 'animate.css/animate.compat.css';
import Landing from './Landing';
import Navbar from './Navbar';
import LogIn from './LogIn';
import Dashboard from './Dashboard';
import { getWeb3Credentials } from '../helpers/dashboard';
import {connectNetwork, resetContractAndProvider} from '../helpers/wallets/wallets';
import LoadingScreen from './LoadingScreen';
import NewRevision from './NewRevision';
import SignIn from './SignIn';
import { getBrowserLanguage, loadData } from '../helpers/language';
import { IntlProvider} from 'react-intl';
import Alert from './Alert';
import { chains, getChain } from '../helpers/chains';
import { useIntl } from 'react-intl';
import {useStateWithSessionStorage} from '../helpers/sessionStorage';
import DataVis from './DataVis';
import Footer from './Footer';
import Feedback from './feedback/Feedback';
import * as Sentry from '@sentry/react';
import { FallbackComponent } from './FallbackComponent';
import { ManageProfile } from './email-notification/manage-profile';
import icon from '../assets/contract_alert_announcement.svg';
import { getChainIndexStatus } from '../helpers/graphql.js';
export const App = (props) => {
const {handleLanguage, language} = props;
const [ethAccount, handleEthAccount] = useState(null)
const [ethAlias, handleEthAlias] = useState(null);
const [ethAvatar, handleEthAvatar] = useState(null);
const [walletName, handleWalletName] = useStateWithSessionStorage('walletName', '');
const [fortmaticPreferredNetwork, handleFortmaticPreferredNetwork] = useStateWithSessionStorage('fortmaticNetworkId', 56)
const [torusPreferredNetwork, handleTorusPreferredNetwork] = useStateWithSessionStorage('torusNetworkId', 1)
const [networkId, handleNetworkId] = useState('N/A')
const [contract, handleContract] = useState(null)
const [web3, handleWeb3] = useState(null)
const [fm, handleFm] = useState(null);
const [torus, handleTorus] = useState(null);
const [web3Case, handleWeb3Case] = useStateWithSessionStorage('web3Case', null);
const [activePage, handleActivePage] = useState('home');
const [loaded, handleLoaded] = useState(null);
const [ensEnabled, handleEnsEnabled] = useState(false);
const [networkChanged, handleNetworkChanged] = useState(false);
const [networkError, handleNetworkError] = useState(false);
const [generalError, handleGeneralError] = useState(false);
const [accountError, handleAccountError] = useState(false);
const [alertNetworkLoading, handleAlertNetworkLoading] = useState(false);
const [shouldShowAnnouncement, handleShouldShowAnnouncement] = useState(false);
const [hideAnnouncement, handleHideAnnouncement] = useState(false);
const [blocksBehind, handleBlocksBehind] = useState(null);
// email notification popup
const [open, handleOpen] = useState(false);
const { formatMessage } = useIntl();
useEffect(() => {
(async () => {
if (web3Case != null) {
await getWeb3Credentials(web3Case, web3Case === 4 ? torusPreferredNetwork : fortmaticPreferredNetwork).then((result) => {
if (!result.ethAccount) {
handleActivePage(null);
handleWeb3Case(null);
location.reload();
return;
}
handleEthAccount(result.ethAccount?.toLowerCase());
handleNetworkId(result.networkId);
handleWeb3(result.web3);
handleContract(result.contract);
handleFm(result.fm);
handleTorus(result.torus);
})
.catch(e => {
console.log(e);
handleGeneralError(true);
});
}
handleLoaded(true);
})();
}, []);
useEffect(() => {
let accListener = (accounts) => {
if((walletName !== 'Metamask' && web3Case !== 2) && (walletName !== 'Torus' && web3Case !== 4)) {
return;
}
if(!accounts[0]) {
handleEthAccount(null);
handleAccountError(true);
} else {
handleAccountError(false);
handleEthAccount(accounts[0].toLowerCase());
}
}
let netListener = async (network) => {
try {
if(web3Case == 4 && torus) {
if(network == 'loading') {
return;
}
if(!getChain(parseInt(network)).torusSupport) {
throw new Error('Unsupported Torus Network with ID: ' + network)
} else {
handleTorusPreferredNetwork(parseInt(network));
}
}
if(!getChain(parseInt(network))) {
throw new Error('Unsupported Network with ID: ' + network);
}
handleNetworkId(parseInt(network));
await resetContractAndProvider(network, handleWeb3, handleContract, web3Case == 4 ? torus : null);
handleNetworkChanged(true);
handleNetworkError(false);
} catch(err) {
// storeNotif("Network Changed", "A network switch to an unsupported network has been detected. Please switch to a supported network and log in again.", "danger");
handleNetworkId(null);
if(web3Case == null) {
handleNetworkChanged(true);
} else if(web3Case == 2 || web3Case == 4) {
if(window.location.hash === '#/signin') {
handleNetworkChanged(true);
}
handleNetworkError(true);
}
// Redirect to /login and log the user out
}
}
if(web3Case == 4 && torus) {
torus.ethereum.on('accountsChanged', accListener);
torus.ethereum.on('networkChanged', netListener);
} else if (window.ethereum !== null && window.ethereum !== undefined) {
try {
window.ethereum.on('accountsChanged', accListener);
window.ethereum.on('networkChanged', netListener);
} catch(err) {
console.log("Error adding network change listener: " + err);
Sentry.captureException(err);
}
}
return () => {
if(web3Case == 4 && torus) {
torus.ethereum.removeListener('accountsChanged', accListener);
torus.ethereum.removeListener('networkChanged', netListener);
} else if (window.ethereum !== null && window.ethereum !== undefined) {
try {
window.ethereum.removeListener('accountsChanged', accListener);
window.ethereum.removeListener('networkChanged', netListener);
} catch(err) {
console.log("Error removing network change listener: " + err);
Sentry.captureException(err);
}
}
};
}, [web3Case, torus]);
useEffect(() => {
(async () => {
if (web3Case != null && ethAccount != null && web3 != null) {
try{
let alias = await web3.lookupAddress(ethAccount);
if(alias) {
handleEthAlias(alias);
const resolver = await web3.getResolver(alias);
const avatar = await resolver.getText("avatar");
handleEthAvatar(avatar);
} else {
handleEthAvatar(null);
handleEthAlias(null);
}
handleEnsEnabled(true);
}catch(err){
handleEnsEnabled(false);
handleEthAvatar(null);
handleEthAlias(null);
}
}
})();
}, [ethAccount, web3, web3Case]);
const logout = async () => {
handleActivePage(null);
handleWeb3Case(null)
handleWeb3(null)
handleEthAccount(null)
handleContract(null);
if (fm!=null){
fm.user.logout;
handleFm(null);
}else if(torus != null){
await torus.logout();
handleTorus(null)
}
}
useEffect(() => {
(async () => {
if(!networkId || networkId == 'N/A') {
return;
}
let blocksBehind = await getChainIndexStatus(networkId);
handleBlocksBehind(blocksBehind);
if(blocksBehind > 5) {
handleShouldShowAnnouncement(true);
} else {
handleShouldShowAnnouncement(false);
}
})();
}, [networkId]);
const changeNetwork = (chain) => {
connectNetwork(chain, web3, handleWeb3, handleContract,
(chain) => {
if(torus !== null) {
handleNetworkId(chain.chainId);
}
}, walletName, handleFm, torus, handleTorus,
(finished) => {
handleNetworkError(!finished);
handleAlertNetworkLoading(false);
if(finished && fm !== null) {
handleFortmaticPreferredNetwork(chain.chainId);
}
if(finished && torus !== null) {
handleTorusPreferredNetwork(chain.chainId);
handleNetworkChanged(true);
}
}
);
}
const alertChangeNetwork = () => {
handleAlertNetworkLoading(true);
const chainId = document.getElementById("network-select").value;
for(let chain of chains) {
if(chain.chainId == chainId) {
console.log(chain);
changeNetwork(chain);
}
}
}
return (
<>
<Router>
<div className={`flex flex-col relative min-h-screen ${window.location.hash === '#/' && web3Case == null ? " bg-landing" : 'none'}`}>
<Navbar announcementShowing={shouldShowAnnouncement && !hideAnnouncement} icon={icon} numberOfBlocksBehind={blocksBehind} handleHideAnnouncement={handleHideAnnouncement} fm={fm} torus={torus} ethAvatar={ethAvatar} ethAlias={ethAlias} provider={web3} changeNetwork={(chain)=>changeNetwork(chain)} appLogout={logout} loggedIn={web3Case !== null && web3 !== null} activePage={activePage} handleActivePage={handleActivePage} handleWeb3Case={handleWeb3Case} ethAccount={ethAccount} handleLanguage={handleLanguage} language={language} handleOpen={handleOpen}/>
<Switch>
{ loaded?
<>
<Route exact path="/">
{ web3Case == null?
<Landing
handleActivePage={handleActivePage}
handleLanguage={handleLanguage}
language={language} />
:
<Redirect to="/home" />
}
</Route>
<Route exact path="/verify">
<Landing handleActivePage={handleActivePage} handleLanguage={handleLanguage} language={language} />
</Route>
<Route exact path="/signin">
{ web3Case == null?
<SignIn
handleActivePage={handleActivePage}
provider={web3}
handleProvider={handleWeb3}
handleFm={handleFm}
torus={torus}
handleTorus={handleTorus}
networkChanged={networkChanged}
handleNetworkChanged={handleNetworkChanged}
walletName={walletName}
handleWalletName={handleWalletName}
fortmaticPreferredNetwork={fortmaticPreferredNetwork}
handleFortmaticPreferredNetwork={handleFortmaticPreferredNetwork}
torusPreferredNetwork={torusPreferredNetwork}
handleTorusPreferredNetwork={handleTorusPreferredNetwork}
/>
:
<Redirect to="/home" />
}
</Route>
<Route exact path="/login">
{ web3Case == null?
<LogIn
provider={web3}
handleEthAccount={handleEthAccount}
handleNetworkId={handleNetworkId}
handleContract={handleContract}
handleWeb3={handleWeb3}
handleWeb3Case={handleWeb3Case}
handleActivePage={handleActivePage}
handleFm={handleFm}
handleTorus={handleTorus}
/>
:
<Redirect to="/home" />
}
</Route>
<Route exact path="/revision">
{ web3Case != null?
<NewRevision
ethAccount={ethAccount}
handleActivePage={handleActivePage}
web3={web3}
fm={fm}
contract={contract}
appLogout={logout}
handleOpen={handleOpen}
/>
:
<Redirect to="/home" />
}
</Route>
<Route exact path="/manage-profile">
{
web3Case != null ?
<ManageProfile
web3={web3}
fm={fm}
torus={torus}
changeNetwork={changeNetwork}
handleOpen={handleOpen}
handleActivePage={handleActivePage}
walletName={walletName}
/> :
<Redirect to="/home" />
}
</Route>
<Route path="/datavis">
<DataVis handleActivePage={handleActivePage} />
</Route>
<Dashboard language={language} fm={fm} torus={torus} ethAlias={ethAlias} ethAvatar={ethAvatar} networkId={networkId} networkChanged={networkChanged} handleNetworkChanged={handleNetworkChanged} changeNetwork={(chain)=>changeNetwork(chain)} handleActivePage={handleActivePage} web3Case={web3Case} web3={web3} contract={contract} ethAccount={ethAccount} ensEnabled={ensEnabled} appLogout={logout} open={open} handleOpen={handleOpen}/>
</>
:
<LoadingScreen/>
}
</Switch>
<Footer handleLanguage={handleLanguage} language={language} activePage={activePage}/>
{networkError &&
<Alert
loading={alertNetworkLoading}
message={formatMessage({id: 'YOU_CURRENT_NETWORK_SELECTION_IS_NOT_SUPPORT'})}
closeButtonText={formatMessage({id: 'LOG_OUT'})}
closeCallback={() => logout()}
closeOnOutsideClick={false}
okButtonText={formatMessage({id: 'OK'})}
okCallback={() => alertChangeNetwork()}
customComponent={
<select id="network-select" name="network" className="select-none bg-transparent h-8 focus:outline-none text-gray-70 rounded-md px-3 border border-gray-70">
{chains.map((chain, key)=>{
if(web3Case == 4 && !chain.torusSupport) {
return null;
}
return (
<option key={key} value={chain.chainId}>{chain.name}</option>
)
})}
</select>
}
/>
}
{generalError &&
<Alert
message={formatMessage({id: 'THERE_HAS_BEEN_A_PROBLEM_FETCHING_DATA'})}
type={1}
closeOnOutsideClick={false}
closeButtonText={formatMessage({id: 'LOG_OUT'})}
closeCallback={() => {
handleActivePage(null);
handleWeb3Case(null);
history.pushState(null, null, '/');
}}
/>
}
{accountError &&
<Alert
type={1}
message={formatMessage({id: 'NO_ACCOUNT_DETECTED'})}
closeButtonText={formatMessage({id: 'LOG_OUT'})}
closeOnOutsideClick={false}
closeCallback={() => {
handleActivePage(null);
handleWeb3Case(null);
history.pushState(null, null, '/');
}}
/>
}
</div>
<Feedback activePage={activePage}/>
</Router>
</>
);
}
export const AppContainer = (props) => {
const {Application, lang} = props;
const [language, handleLanguage] = useStateWithSessionStorage('language', ()=>getBrowserLanguage() );
const [localData, handleLocalData] = useState(null);
useEffect(()=>{
handleLocalData(loadData(language));
},[language]);
useEffect(()=>{
if(lang){
handleLanguage(lang)
}
},[lang]);
return (
<IntlProvider
locale={language}
defaultLocale="en"
messages={localData}
contract='hello'
>
<Sentry.ErrorBoundary fallback={FallbackComponent} showDialog={false}>
<ReactNotification />
<Application handleLanguage={handleLanguage} language={language}/>
</Sentry.ErrorBoundary>
</IntlProvider>
)
}
export default {AppContainer, App};