Commit 80271437 authored by lynn's avatar lynn Committed by GitHub

fix: display warning / reload prompt modal when metamask erroneously disconnects (#5722)

* init

* hook up modal to error detection

* zzmp comments

* zzmp fixes
parent 352daf95
import { useWeb3React, Web3ReactHooks, Web3ReactProvider } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import { Connection } from 'connection'
import { ConnectionType, setMetMaskErrorHandler } from 'connection'
import { getConnectionName } from 'connection/utils'
import { isSupportedChain } from 'constants/chains'
import { RPC_PROVIDERS } from 'constants/providers'
......@@ -8,8 +9,19 @@ import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/tra
import useEagerlyConnect from 'hooks/useEagerlyConnect'
import useOrderedConnections from 'hooks/useOrderedConnections'
import { ReactNode, useEffect, useMemo } from 'react'
import { updateConnectionError } from 'state/connection/reducer'
import { useAppDispatch } from 'state/hooks'
export default function Web3Provider({ children }: { children: ReactNode }) {
const dispatch = useAppDispatch()
// Set metamask error handler for metamask disconnection warning modal.
useEffect(() => {
setMetMaskErrorHandler((error: Error) =>
dispatch(updateConnectionError({ connectionType: ConnectionType.INJECTED, error: error.message }))
)
}, [dispatch])
useEagerlyConnect()
const connections = useOrderedConnections()
const connectors: [Connector, Web3ReactHooks][] = connections.map(({ hooks, connector }) => [connector, hooks])
......
import { Trans } from '@lingui/macro'
import { ButtonPrimary } from 'components/Button'
import { AutoColumn } from 'components/Column'
import Modal from 'components/Modal'
import { RowBetween } from 'components/Row'
import { AlertTriangle } from 'react-feather'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { CloseIcon, ThemedText } from 'theme'
import { useModalIsOpen, useToggleMetamaskConnectionErrorModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
const Wrapper = styled.div`
width: 100%;
position: relative;
display: flex;
flex-flow: column;
align-items: center;
`
const Container = styled.div`
width: 100%;
padding: 32px 32px;
display: flex;
flex-flow: column;
align-items: center;
`
const LogoContainer = styled.div`
display: flex;
gap: 16px;
`
const ShortColumn = styled(AutoColumn)`
margin-top: 10px;
`
const InfoText = styled(Text)`
padding: 0 12px 0 12px;
font-size: 14px;
line-height: 20px;
text-align: center;
`
const StyledButton = styled(ButtonPrimary)`
margin-top: 24px;
width: 100%;
font-weight: 600;
`
const WarningIcon = styled(AlertTriangle)`
width: 76px;
height: 76px;
margin-top: 4px;
margin-bottom: 28px;
stroke-width: 1px;
margin-right: 4px;
color: ${({ theme }) => theme.accentCritical};
`
const onReconnect = () => window.location.reload()
const header = 'Wallet disconnected'
const description = 'A Metamask error caused your wallet to disconnect. Reload the page to reconnect.'
export default function MetamaskConnectionError() {
const modalOpen = useModalIsOpen(ApplicationModal.METAMASK_CONNECTION_ERROR)
const toggleModal = useToggleMetamaskConnectionErrorModal()
return (
<Modal isOpen={modalOpen} onDismiss={toggleModal} minHeight={false} maxHeight={90}>
<Wrapper>
<RowBetween style={{ padding: '1rem' }}>
<div />
<CloseIcon onClick={toggleModal} />
</RowBetween>
<Container>
<AutoColumn>
<LogoContainer>
<WarningIcon />
</LogoContainer>
</AutoColumn>
<ShortColumn>
<InfoText>
<ThemedText.HeadlineSmall marginBottom="8px">{header}</ThemedText.HeadlineSmall>
<ThemedText.BodySmall>{description}</ThemedText.BodySmall>
</InfoText>
</ShortColumn>
<StyledButton onClick={onReconnect}>
<Trans>Reload</Trans>
</StyledButton>
</Container>
</Wrapper>
</Modal>
)
}
......@@ -5,12 +5,12 @@ import { useWeb3React } from '@web3-react/core'
import { FiatOnrampAnnouncement } from 'components/FiatOnrampAnnouncement'
import { IconWrapper } from 'components/Identicon/StatusIcon'
import WalletDropdown from 'components/WalletDropdown'
import { getConnection } from 'connection/utils'
import { getConnection, getIsMetaMask } from 'connection/utils'
import { Portal } from 'nft/components/common/Portal'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { getIsValidSwapQuote } from 'pages/Swap'
import { darken } from 'polished'
import { useCallback, useMemo, useRef } from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { AlertTriangle, ChevronDown, ChevronUp } from 'react-feather'
import { useAppSelector } from 'state/hooks'
import { useDerivedSwapInfo } from 'state/swap/hooks'
......@@ -22,6 +22,7 @@ import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import {
useCloseModal,
useModalIsOpen,
useToggleMetamaskConnectionErrorModal,
useToggleWalletDropdown,
useToggleWalletModal,
} from '../../state/application/hooks'
......@@ -34,6 +35,7 @@ import StatusIcon from '../Identicon/StatusIcon'
import Loader from '../Loader'
import { RowBetween } from '../Row'
import WalletModal from '../WalletModal'
import MetamaskConnectionError from './MetamaskConnectionError'
// https://stackoverflow.com/a/31617326
const FULL_BORDER_RADIUS = 9999
......@@ -211,10 +213,16 @@ function Web3StatusInner() {
toggleWalletDropdown()
}, [toggleWalletDropdown])
const toggleWalletModal = useToggleWalletModal()
const toggleMetamaskConnectionErrorModal = useToggleMetamaskConnectionErrorModal()
const walletIsOpen = useModalIsOpen(ApplicationModal.WALLET_DROPDOWN)
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
const error = useAppSelector((state) => state.connection.errorByConnectionType[getConnection(connector).type])
useEffect(() => {
if (getIsMetaMask() && error) {
toggleMetamaskConnectionErrorModal()
}
}, [error, toggleMetamaskConnectionErrorModal])
const allTransactions = useAllTransactions()
......@@ -318,6 +326,7 @@ export default function Web3Status() {
<Web3StatusInner />
<FiatOnrampAnnouncement />
<WalletModal ENSName={ENSName ?? undefined} pendingTransactions={pending} confirmedTransactions={confirmed} />
<MetamaskConnectionError />
<Portal>
<span ref={walletRef}>
<WalletDropdown />
......
......@@ -25,10 +25,21 @@ export interface Connection {
type: ConnectionType
}
let metaMaskErrorHandler: (error: Error) => void | undefined
export function setMetMaskErrorHandler(errorHandler: (error: Error) => void) {
metaMaskErrorHandler = errorHandler
}
function onError(error: Error) {
console.debug(`web3-react error: ${error}`)
}
function onMetamaskError(error: Error) {
onError(error)
metaMaskErrorHandler?.(error)
}
const [web3Network, web3NetworkHooks] = initializeConnector<Network>(
(actions) => new Network({ actions, urlMap: RPC_PROVIDERS, defaultChainId: 1 })
)
......@@ -38,7 +49,9 @@ export const networkConnection: Connection = {
type: ConnectionType.NETWORK,
}
const [web3Injected, web3InjectedHooks] = initializeConnector<MetaMask>((actions) => new MetaMask({ actions, onError }))
const [web3Injected, web3InjectedHooks] = initializeConnector<MetaMask>(
(actions) => new MetaMask({ actions, onError: onMetamaskError })
)
export const injectedConnection: Connection = {
connector: web3Injected,
hooks: web3InjectedHooks,
......
......@@ -92,6 +92,10 @@ export function useCloseModal(): () => void {
return useCallback(() => dispatch(setOpenModal(null)), [dispatch])
}
export function useToggleMetamaskConnectionErrorModal(): () => void {
return useToggleModal(ApplicationModal.METAMASK_CONNECTION_ERROR)
}
export function useOpenModal(modal: ApplicationModal): () => void {
const dispatch = useAppDispatch()
return useCallback(() => dispatch(setOpenModal(modal)), [dispatch, modal])
......
......@@ -22,6 +22,7 @@ export enum ApplicationModal {
FEATURE_FLAGS,
FIAT_ONRAMP,
MENU,
METAMASK_CONNECTION_ERROR,
NETWORK_FILTER,
NETWORK_SELECTOR,
POOL_OVERVIEW_OPTIONS,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment