Commit 0a115fab authored by cartcrom's avatar cartcrom Committed by GitHub

fix: unsupported chain displays message instead of crash (#4054)

* made initial changes for pools page displaying w/ unsupported chains
* condensed styling
* added chain validation to CTACards and wrote tests for both CTAcards and Pools page
* linted changes
* switched from snapshot to text matching tests
* switched test to use check for text instead of testid
parent 882147b5
import * as useV3Positions from 'hooks/useV3Positions'
import { BrowserRouter as Router } from 'react-router-dom'
import { render, screen } from 'test-utils'
import * as switchChain from 'utils/switchChain'
import CTACards from './CTACards'
jest.mock('@web3-react/core', () => {
const web3React = jest.requireActual('@web3-react/core')
return {
...web3React,
useWeb3React: () => ({
chainId: 10,
}),
}
})
describe('CTAcard links', () => {
it('renders mainnet link when chain is not supported', () => {
jest.spyOn(switchChain, 'isChainAllowed').mockReturnValue(false)
jest.spyOn(useV3Positions, 'useV3Positions').mockImplementation(() => {
return { loading: false, positions: undefined }
})
render(
<Router>
<CTACards />
</Router>
)
expect(screen.getByTestId('cta-infolink')).toHaveAttribute('href', 'https://info.uniswap.org/#/pools')
})
it('renders specific link when chain is supported', () => {
jest.spyOn(switchChain, 'isChainAllowed').mockReturnValue(true)
jest.spyOn(useV3Positions, 'useV3Positions').mockImplementation(() => {
return { loading: false, positions: undefined }
})
render(
<Router>
<CTACards />
</Router>
)
expect(screen.getByTestId('cta-infolink')).toHaveAttribute('href', 'https://info.uniswap.org/#/optimism/pools')
})
})
...@@ -5,6 +5,7 @@ import { CHAIN_INFO } from 'constants/chainInfo' ...@@ -5,6 +5,7 @@ import { CHAIN_INFO } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains' import { SupportedChainId } from 'constants/chains'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { isChainAllowed } from 'utils/switchChain'
import { ExternalLink } from '../../theme' import { ExternalLink } from '../../theme'
...@@ -93,8 +94,9 @@ const ResponsiveColumn = styled(AutoColumn)` ...@@ -93,8 +94,9 @@ const ResponsiveColumn = styled(AutoColumn)`
` `
export default function CTACards() { export default function CTACards() {
const { chainId } = useWeb3React() const { chainId, connector } = useWeb3React()
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET] const { infoLink } = CHAIN_INFO[chainId && isChainAllowed(connector, chainId) ? chainId : SupportedChainId.MAINNET]
return ( return (
<CTASection> <CTASection>
<CTA1 href={'https://help.uniswap.org/en/articles/5391541-providing-liquidity-on-uniswap-v3'}> <CTA1 href={'https://help.uniswap.org/en/articles/5391541-providing-liquidity-on-uniswap-v3'}>
...@@ -107,7 +109,7 @@ export default function CTACards() { ...@@ -107,7 +109,7 @@ export default function CTACards() {
</ThemedText.Body> </ThemedText.Body>
</ResponsiveColumn> </ResponsiveColumn>
</CTA1> </CTA1>
<CTA2 href={infoLink + 'pools'}> <CTA2 data-testid="cta-infolink" href={infoLink + 'pools'}>
<ResponsiveColumn> <ResponsiveColumn>
<HeaderText style={{ alignSelf: 'flex-start' }}> <HeaderText style={{ alignSelf: 'flex-start' }}>
<Trans>Top pools</Trans> <Trans>Top pools</Trans>
......
import * as useV3Positions from 'hooks/useV3Positions'
import { BrowserRouter as Router } from 'react-router-dom'
import { render, screen } from 'test-utils'
import * as switchChain from 'utils/switchChain'
import Pool from '.'
jest.mock('@web3-react/core', () => {
const web3React = jest.requireActual('@web3-react/core')
return {
...web3React,
useWeb3React: () => ({
chainId: 1,
}),
}
})
describe('networks', () => {
it('renders error card when unsupported chain is selected', () => {
jest.spyOn(switchChain, 'isChainAllowed').mockReturnValue(false)
jest.spyOn(useV3Positions, 'useV3Positions').mockImplementation(() => {
return { loading: false, positions: undefined }
})
render(
<Router>
<Pool />
</Router>
)
expect(
screen.getByText('Your connected network is unsupported. Request support', { exact: false })
).toBeInTheDocument()
})
it('renders empty positions card when on supported chain with no positions', () => {
jest.spyOn(switchChain, 'isChainAllowed').mockReturnValue(true)
jest.spyOn(useV3Positions, 'useV3Positions').mockImplementation(() => {
return { loading: false, positions: undefined }
})
render(
<Router>
<Pool />
</Router>
)
expect(screen.getByText('Your active V3 liquidity positions will appear here.')).toBeInTheDocument()
})
})
...@@ -9,13 +9,14 @@ import { RowBetween, RowFixed } from 'components/Row' ...@@ -9,13 +9,14 @@ import { RowBetween, RowFixed } from 'components/Row'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink' import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import { useV3Positions } from 'hooks/useV3Positions' import { useV3Positions } from 'hooks/useV3Positions'
import { useContext } from 'react' import { useContext } from 'react'
import { BookOpen, ChevronDown, ChevronsRight, Inbox, Layers, PlusCircle } from 'react-feather' import { Activity, BookOpen, ChevronDown, ChevronsRight, Inbox, Layers, PlusCircle } from 'react-feather'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { useToggleWalletModal } from 'state/application/hooks' import { useToggleWalletModal } from 'state/application/hooks'
import { useUserHideClosedPositions } from 'state/user/hooks' import { useUserHideClosedPositions } from 'state/user/hooks'
import styled, { ThemeContext } from 'styled-components/macro' import styled, { css, ThemeContext } from 'styled-components/macro'
import { HideSmall, ThemedText } from 'theme' import { ExternalLink, HideSmall, ThemedText } from 'theme'
import { PositionDetails } from 'types/position' import { PositionDetails } from 'types/position'
import { isChainAllowed } from 'utils/switchChain'
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses' import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
import CTACards from './CTACards' import CTACards from './CTACards'
...@@ -80,7 +81,13 @@ const MoreOptionsButton = styled(ButtonGray)` ...@@ -80,7 +81,13 @@ const MoreOptionsButton = styled(ButtonGray)`
background-color: ${({ theme }) => theme.bg0}; background-color: ${({ theme }) => theme.bg0};
margin-right: 8px; margin-right: 8px;
` `
const NoLiquidity = styled.div`
const MoreOptionsText = styled(ThemedText.Body)`
align-items: center;
display: flex;
`
const ErrorContainer = styled.div`
align-items: center; align-items: center;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -89,6 +96,21 @@ const NoLiquidity = styled.div` ...@@ -89,6 +96,21 @@ const NoLiquidity = styled.div`
max-width: 300px; max-width: 300px;
min-height: 25vh; min-height: 25vh;
` `
const IconStyle = css`
width: 48px;
height: 48px;
margin-bottom: 0.5rem;
`
const NetworkIcon = styled(Activity)`
${IconStyle}
`
const InboxIcon = styled(Inbox)`
${IconStyle}
`
const ResponsiveButtonPrimary = styled(ButtonPrimary)` const ResponsiveButtonPrimary = styled(ButtonPrimary)`
border-radius: 12px; border-radius: 12px;
padding: 6px 8px; padding: 6px 8px;
...@@ -126,8 +148,43 @@ function PositionsLoadingPlaceholder() { ...@@ -126,8 +148,43 @@ function PositionsLoadingPlaceholder() {
) )
} }
function WrongNetworkCard() {
const theme = useContext(ThemeContext)
return (
<>
<PageWrapper>
<SwapPoolTabs active="pool" />
<AutoColumn gap="lg" justify="center">
<AutoColumn gap="lg" style={{ width: '100%' }}>
<TitleRow style={{ marginTop: '1rem' }} padding={'0'}>
<ThemedText.Body fontSize={'20px'}>
<Trans>Pools Overview</Trans>
</ThemedText.Body>
</TitleRow>
<MainContentWrapper>
<ErrorContainer>
<ThemedText.Body color={theme.text3} textAlign="center">
<NetworkIcon strokeWidth={1.2} />
<div data-testid="pools-unsupported-err">
<Trans>
Your connected network is unsupported. Request support{' '}
<ExternalLink href="https://uniswap.canny.io/feature-requests">here</ExternalLink>.
</Trans>
</div>
</ThemedText.Body>
</ErrorContainer>
</MainContentWrapper>
</AutoColumn>
</AutoColumn>
</PageWrapper>
<SwitchLocaleLink />
</>
)
}
export default function Pool() { export default function Pool() {
const { account, chainId } = useWeb3React() const { account, chainId, connector } = useWeb3React()
const toggleWalletModal = useToggleWalletModal() const toggleWalletModal = useToggleWalletModal()
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
...@@ -135,6 +192,10 @@ export default function Pool() { ...@@ -135,6 +192,10 @@ export default function Pool() {
const { positions, loading: positionsLoading } = useV3Positions(account) const { positions, loading: positionsLoading } = useV3Positions(account)
if (chainId && !isChainAllowed(connector, chainId)) {
return <WrongNetworkCard />
}
const [openPositions, closedPositions] = positions?.reduce<[PositionDetails[], PositionDetails[]]>( const [openPositions, closedPositions] = positions?.reduce<[PositionDetails[], PositionDetails[]]>(
(acc, p) => { (acc, p) => {
acc[p.liquidity?.isZero() ? 1 : 0].push(p) acc[p.liquidity?.isZero() ? 1 : 0].push(p)
...@@ -193,7 +254,7 @@ export default function Pool() { ...@@ -193,7 +254,7 @@ export default function Pool() {
return ( return (
<> <>
<PageWrapper> <PageWrapper>
<SwapPoolTabs active={'pool'} /> <SwapPoolTabs active="pool" />
<AutoColumn gap="lg" justify="center"> <AutoColumn gap="lg" justify="center">
<AutoColumn gap="lg" style={{ width: '100%' }}> <AutoColumn gap="lg" style={{ width: '100%' }}>
<TitleRow style={{ marginTop: '1rem' }} padding={'0'}> <TitleRow style={{ marginTop: '1rem' }} padding={'0'}>
...@@ -207,10 +268,10 @@ export default function Pool() { ...@@ -207,10 +268,10 @@ export default function Pool() {
flyoutAlignment={FlyoutAlignment.LEFT} flyoutAlignment={FlyoutAlignment.LEFT}
ToggleUI={(props: any) => ( ToggleUI={(props: any) => (
<MoreOptionsButton {...props}> <MoreOptionsButton {...props}>
<ThemedText.Body style={{ alignItems: 'center', display: 'flex' }}> <MoreOptionsText>
<Trans>More</Trans> <Trans>More</Trans>
<ChevronDown size={15} /> <ChevronDown size={15} />
</ThemedText.Body> </MoreOptionsText>
</MoreOptionsButton> </MoreOptionsButton>
)} )}
/> />
...@@ -231,9 +292,9 @@ export default function Pool() { ...@@ -231,9 +292,9 @@ export default function Pool() {
userHideClosedPositions={userHideClosedPositions} userHideClosedPositions={userHideClosedPositions}
/> />
) : ( ) : (
<NoLiquidity> <ErrorContainer>
<ThemedText.Body color={theme.text3} textAlign="center"> <ThemedText.Body color={theme.text3} textAlign="center">
<Inbox size={48} strokeWidth={1} style={{ marginBottom: '.5rem' }} /> <InboxIcon strokeWidth={1} />
<div> <div>
<Trans>Your active V3 liquidity positions will appear here.</Trans> <Trans>Your active V3 liquidity positions will appear here.</Trans>
</div> </div>
...@@ -251,7 +312,7 @@ export default function Pool() { ...@@ -251,7 +312,7 @@ export default function Pool() {
<Trans>Connect a wallet</Trans> <Trans>Connect a wallet</Trans>
</ButtonPrimary> </ButtonPrimary>
)} )}
</NoLiquidity> </ErrorContainer>
)} )}
</MainContentWrapper> </MainContentWrapper>
<HideSmall> <HideSmall>
......
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