Skip to content

Instantly share code, notes, and snippets.

@PaulRBerg
Created November 9, 2019 00:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PaulRBerg/7e9901caa11a34f8ece59c368f18d8bb to your computer and use it in GitHub Desktop.
Save PaulRBerg/7e9901caa11a34f8ece59c368f18d8bb to your computer and use it in GitHub Desktop.
import React, { useCallback, useEffect, useRef, useState } from "react";
import Jazzicon from "jazzicon";
import styled, { useTheme } from "styled-components";
import typy from "typy";
import { Activity, User } from "react-feather";
import { darken } from "polished";
import { ethers } from "ethers";
import { shortenAddress } from "@sablier/utils";
import { useTranslation } from "react-i18next";
import { Connectors, useWeb3Context } from "web3-react";
import Spinner from "../Spinner";
import WalletModal from "../../modals/WalletModal";
import { useENSName } from "../../hooks";
import { useModalManager } from "../../contexts/Application";
const { Connector } = Connectors;
const Web3StatusWrapper = styled.div`
${props => props.theme.flexRowNoWrap};
align-items: center;
border-bottom-right-radius: 1.25rem;
border-top-right-radius: 1.25rem;
flex-grow: 1;
justify-content: center;
padding: 0.5rem;
user-select: none;
`;
const Label = styled.span`
margin-left: 0.25rem;
margin-right: 0.25rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const Web3StatusErrorWrapper = styled(Web3StatusWrapper)`
background-color: ${props => props.theme.pastelRed};
border: 1px solid ${props => props.theme.pastelRed};
color: ${props => props.theme.white};
font-weight: 500;
&:hover,
&:focus {
background-color: ${props => darken(0.1, props.theme.pastelRed)};
}
`;
const NetworkIcon = styled(Activity)`
height: 1rem;
margin-left: 0.25rem;
margin-right: 0.5rem;
width: 1rem;
`;
const Web3StatusConnect = styled(Web3StatusWrapper)`
font-weight: 500;
`;
const UserIcon = styled(User)`
height: 0.875rem;
width: 0.875rem;
`;
const Web3StatusConnected = styled(Web3StatusWrapper)`
background-color: ${props => props.theme.transparent};
color: ${props => props.theme.darkGunmetalBlack};
font-weight: 400;
`;
const IdenticonWrapper = styled.div`
background-color: ${props => props.theme.silverSandGray};
border-radius: 1.125rem;
height: 1rem;
width: 1rem;
`;
export default function Web3Status() {
const theme = useTheme();
const { account, active, connectorName, setConnector } = useWeb3Context();
const { toggleWalletModal } = useModalManager();
const { t } = useTranslation();
const ENSName = useENSName(account);
const identiconRef = useRef();
const [walletError, setWalletError] = useState(undefined);
async function onClick() {
if (walletError) {
toggleWalletModal();
} else if (connectorName === "Network" && (window.ethereum || window.web3)) {
try {
await setConnector("Injected", { suppressAndThrowErrors: true });
} catch (error) {
if (error.code === Connector.errorCodes.UNSUPPORTED_NETWORK) {
setWalletError(error);
}
}
} else {
toggleWalletModal();
}
}
const tryToActivateInjected = useCallback(async () => {
const library = new ethers.providers.Web3Provider(window.ethereum);
// If calling enable won't pop an approve modal, then try to activate injected...
try {
const accounts = await library.listAccounts();
if (accounts.length >= 1) {
await setConnector("Injected", { suppressAndThrowErrors: true });
setWalletError(undefined);
}
} catch (error) {
// ...and if the error is that they're on the wrong network, display it, otherwise eat it
if (error.code === Connector.errorCodes.UNSUPPORTED_NETWORK) {
setWalletError(error);
}
}
}, [setConnector]);
// Janky logic to detect log{ins,outs}...
useEffect(() => {
const { ethereum } = window;
if (typy(ethereum).isFalsy) {
return undefined;
}
// Poll to check the accounts array, and if it's ever 0 i.e. the user logged out, update the connector
if (connectorName === "Injected") {
const accountPoll = setInterval(async () => {
const library = new ethers.providers.Web3Provider(ethereum);
try {
const accounts = await library.listAccounts();
if (accounts.length === 0) {
setConnector("Network");
}
// eslint-disable-next-line no-empty
} catch {}
}, 750);
return () => {
clearInterval(accountPoll);
};
} else if (
/* If the network or the account changes, check whether this has been caused by a new injected connector */
connectorName === "Network" &&
typy(ethereum, "on").isTruthy &&
typy(ethereum, "removeListener").isTruthy
) {
ethereum.on("networkChanged", tryToActivateInjected);
ethereum.on("accountsChanged", tryToActivateInjected);
return () => {
if (typy(ethereum, "removeListener").isTruthy) {
ethereum.removeListener("networkChanged", tryToActivateInjected);
ethereum.removeListener("accountsChanged", tryToActivateInjected);
}
};
}
return undefined;
}, [connectorName, setConnector, tryToActivateInjected]);
/* Generate the Jazzicon only when the account changes */
useEffect(() => {
if (identiconRef.current) {
identiconRef.current.innerHTML = "";
if (account) {
identiconRef.current.appendChild(Jazzicon(16, parseInt(account.slice(2, 10), 16)));
}
}
}, [account, walletError]);
function renderWebStatus() {
if (typy(walletError).isTruthy) {
/* This is ok because we're guaranteed that the error is a wrong network error */
return (
<Web3StatusErrorWrapper onClick={onClick}>
<NetworkIcon color={theme.white} />
<Label>
{t("wrong")}&nbsp;{t("network")}
</Label>
</Web3StatusErrorWrapper>
);
} else if (typy(account).isFalsy) {
return (
<Web3StatusConnect onClick={onClick}>
<UserIcon color={theme.darkGunmetalBlack} />
<Label>{t("signIn")}</Label>
</Web3StatusConnect>
);
} else {
return (
<Web3StatusConnected onClick={onClick}>
<IdenticonWrapper ref={identiconRef} />
<Label>{ENSName || shortenAddress(account)}</Label>
</Web3StatusConnected>
);
}
}
if (active) {
return (
<>
{renderWebStatus()}
<WalletModal error={walletError} />
</>
);
} else {
return (
<Web3StatusWrapper>
<Spinner color={theme.darkGunmetalBlack} delay={400} size={16} />
</Web3StatusWrapper>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment