feat: use router depending on the origin (#6982)

* feat: initial commit

* chore: whitelist vercel

* chore: fix e2e test

* chore: update all occurences of #

* chore: tweaks

* chore: remove todo

* chore: revert changes

* chore: bring back old tweet logic and fix nested URLs

* chore: fix merge

* improve check

* fix: cypress

* chore: fix tests

* chore: update snapshot

* chore: update readme

* address review

* satisfy eslint

* chore: fix ui issue

* fix: tests

* fix: e2e test
This commit is contained in:
Mike Grabowski 2023-08-07 15:01:45 -07:00 committed by GitHub
parent a7135c9ab1
commit c9b4016b78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 113 additions and 91 deletions

@ -69,10 +69,10 @@ Other things to note:
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2. The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
- Swap on Uniswap V2: <https://app.uniswap.org/#/swap?use=v2> - Swap on Uniswap V2: <https://app.uniswap.org/swap?use=v2>
- View V2 liquidity: <https://app.uniswap.org/#/pools/v2> - View V2 liquidity: <https://app.uniswap.org/pools/v2>
- Add V2 liquidity: <https://app.uniswap.org/#/add/v2> - Add V2 liquidity: <https://app.uniswap.org/add/v2>
- Migrate V2 liquidity to V3: <https://app.uniswap.org/#/migrate/v2> - Migrate V2 liquidity to V3: <https://app.uniswap.org/migrate/v2>
## Accessing Uniswap V1 ## Accessing Uniswap V1

@ -12,7 +12,7 @@ describe('Testing nfts', () => {
}) })
it('should load pudgy penguin collection page', () => { it('should load pudgy penguin collection page', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`) cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-collection-asset')).should('exist') cy.get(getTestSelector('nft-collection-asset')).should('exist')
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist') cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
cy.get(getTestSelector('nft-filter')).first().click() cy.get(getTestSelector('nft-filter')).first().click()
@ -20,13 +20,13 @@ describe('Testing nfts', () => {
}) })
it('should be able to navigate to activity', () => { it('should be able to navigate to activity', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`) cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-activity')).first().click() cy.get(getTestSelector('nft-activity')).first().click()
cy.get(getTestSelector('nft-activity-row')).should('exist') cy.get(getTestSelector('nft-activity-row')).should('exist')
}) })
it('should go to the details page', () => { it('should go to the details page', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`) cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-filter')).first().click() cy.get(getTestSelector('nft-filter')).first().click()
cy.get(getTestSelector('nft-collection-filter-buy-now')).click() cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
cy.get(getTestSelector('nft-collection-asset')).first().click() cy.get(getTestSelector('nft-collection-asset')).first().click()
@ -37,7 +37,7 @@ describe('Testing nfts', () => {
}) })
it('should toggle buy now on details page', () => { it('should toggle buy now on details page', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`) cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-filter')).first().click() cy.get(getTestSelector('nft-filter')).first().click()
cy.get(getTestSelector('nft-collection-filter-buy-now')).click() cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
cy.get(getTestSelector('nft-collection-asset')).first().click() cy.get(getTestSelector('nft-collection-asset')).first().click()

@ -69,8 +69,5 @@ describe('Token explore', () => {
cy.get(getTestSelector('tokens-network-filter-selected')).click() cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click() cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism') cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.reload()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
}) })
}) })

@ -25,7 +25,7 @@ describe('Universal search bar', () => {
.and('contain.text', '$') .and('contain.text', '$')
.and('contain.text', '%') .and('contain.text', '%')
.click() .click()
cy.location('hash').should('equal', '#/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984') cy.location('pathname').should('equal', '/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
openSearch() openSearch()
cy.get(getTestSelector('searchbar-dropdown')) cy.get(getTestSelector('searchbar-dropdown'))

@ -26,11 +26,11 @@ describe('Wallet Dropdown', () => {
cy.contains('Uniswap available in: English').should('not.exist') cy.contains('Uniswap available in: English').should('not.exist')
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true }) cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
cy.location('hash').should('match', /\?lng=af-ZA$/) cy.location('search').should('match', /\?lng=af-ZA$/)
cy.contains('Uniswap available in: English') cy.contains('Uniswap available in: English')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true }) cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
cy.location('hash').should('match', /\?lng=en-US$/) cy.location('search').should('match', /\?lng=en-US$/)
cy.contains('Uniswap available in: English').should('not.exist') cy.contains('Uniswap available in: English').should('not.exist')
}) })
} }

@ -12,7 +12,7 @@ describe('translations', () => {
cy.get(getTestSelector('web3-status-connected')).click() cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click() cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true }) cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true })
cy.location('hash').should('match', /\?lng=fr-FR$/) cy.location('search').should('match', /\?lng=fr-FR$/)
cy.contains('Échanger') cy.contains('Échanger')
cy.contains('Uniswap disponible en : English') cy.contains('Uniswap disponible en : English')
}) })

@ -2,7 +2,6 @@
"name": "@uniswap/interface", "name": "@uniswap/interface",
"version": "1.1.0", "version": "1.1.0",
"description": "Uniswap Interface", "description": "Uniswap Interface",
"homepage": ".",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"scripts": { "scripts": {
"ajv": "node scripts/compile-ajv-validators.js", "ajv": "node scripts/compile-ajv-validators.js",
@ -94,7 +93,6 @@
"@types/react": "^18.0.15", "@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/react-redux": "^7.1.24", "@types/react-redux": "^7.1.24",
"@types/react-router-dom": "^5.3.3",
"@types/react-table": "^7.7.12", "@types/react-table": "^7.7.12",
"@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.2", "@types/react-window": "^1.8.2",

@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useHref } from 'react-router-dom'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks' import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
@ -79,6 +80,8 @@ export default function FiatOnrampModal() {
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const swapUrl = useHref('/swap')
const fetchSignedIframeUrl = useCallback(async () => { const fetchSignedIframeUrl = useCallback(async () => {
if (!account) { if (!account) {
setError('Please connect an account before making a purchase.') setError('Please connect an account before making a purchase.')
@ -98,7 +101,7 @@ export default function FiatOnrampModal() {
theme: isDarkMode ? 'dark' : 'light', theme: isDarkMode ? 'dark' : 'light',
colorCode: theme.accentAction, colorCode: theme.accentAction,
defaultCurrencyCode: 'eth', defaultCurrencyCode: 'eth',
redirectUrl: 'https://app.uniswap.org/#/swap', redirectUrl: swapUrl,
walletAddresses: JSON.stringify( walletAddresses: JSON.stringify(
MOONPAY_SUPPORTED_CURRENCY_CODES.reduce( MOONPAY_SUPPORTED_CURRENCY_CODES.reduce(
(acc, currencyCode) => ({ (acc, currencyCode) => ({
@ -118,7 +121,7 @@ export default function FiatOnrampModal() {
} finally { } finally {
setLoading(false) setLoading(false)
} }
}, [account, isDarkMode, theme.accentAction]) }, [account, isDarkMode, swapUrl, theme.accentAction])
useEffect(() => { useEffect(() => {
fetchSignedIframeUrl() fetchSignedIframeUrl()

@ -343,7 +343,7 @@ exports[`LoadedRow.tsx renders a row 1`] = `
> >
<a <a
class="c0" class="c0"
href="#/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" href="/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
> >
<div <div
class="c1" class="c1"

@ -12,8 +12,9 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from 'react-query' import { QueryClient, QueryClientProvider } from 'react-query'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { HashRouter } from 'react-router-dom' import { BrowserRouter, HashRouter } from 'react-router-dom'
import { SystemThemeUpdater } from 'theme/components/ThemeToggle' import { SystemThemeUpdater } from 'theme/components/ThemeToggle'
import { isBrowserRouterEnabled } from 'utils/env'
import Web3Provider from './components/Web3Provider' import Web3Provider from './components/Web3Provider'
import { LanguageProvider } from './i18n' import { LanguageProvider } from './i18n'
@ -51,12 +52,14 @@ const queryClient = new QueryClient()
const container = document.getElementById('root') as HTMLElement const container = document.getElementById('root') as HTMLElement
const Router = isBrowserRouterEnabled() ? BrowserRouter : HashRouter
createRoot(container).render( createRoot(container).render(
<StrictMode> <StrictMode>
<Provider store={store}> <Provider store={store}>
<FeatureFlagsProvider> <FeatureFlagsProvider>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<HashRouter> <Router>
<LanguageProvider> <LanguageProvider>
<Web3Provider> <Web3Provider>
<ApolloProvider client={apolloClient}> <ApolloProvider client={apolloClient}>
@ -70,7 +73,7 @@ createRoot(container).render(
</ApolloProvider> </ApolloProvider>
</Web3Provider> </Web3Provider>
</LanguageProvider> </LanguageProvider>
</HashRouter> </Router>
</QueryClientProvider> </QueryClientProvider>
</FeatureFlagsProvider> </FeatureFlagsProvider>
</Provider> </Provider>

@ -95,7 +95,7 @@ const ActionButton = ({
<StyledActionButton <StyledActionButton
selected={isSelected} selected={isSelected}
isDisabled={isDisabled} isDisabled={isDisabled}
onClick={(e: React.MouseEvent) => (isDisabled ? undefined : clickActionButton(e))} onClick={(e) => (isDisabled ? undefined : clickActionButton(e))}
> >
{children} {children}
</StyledActionButton> </StyledActionButton>

@ -38,6 +38,7 @@ export const eventRow = style([
borderBottomColor: 'backgroundOutline', borderBottomColor: 'backgroundOutline',
}), }),
{ {
textDecoration: 'none',
height: '84px', height: '84px',
':hover': { ':hover': {
background: themeVars.colors.backgroundSurface, background: themeVars.colors.backgroundSurface,

@ -5,10 +5,11 @@ import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex' import { Column, Row } from 'nft/components/Flex'
import { themeVars, vars } from 'nft/css/sprinkles.css' import { themeVars, vars } from 'nft/css/sprinkles.css'
import { useBag, useIsMobile } from 'nft/hooks' import { useBag, useIsMobile } from 'nft/hooks'
import { ActivityEvent, ActivityEventType } from 'nft/types' import { ActivityEventType } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice' import { fetchPrice } from 'nft/utils/fetchPrice'
import { useCallback, useEffect, useReducer, useState } from 'react' import { useCallback, useEffect, useReducer, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component' import InfiniteScroll from 'react-infinite-scroll-component'
import { Link } from 'react-router-dom'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { useIsDarkMode } from 'theme/components/ThemeToggle' import { useIsDarkMode } from 'theme/components/ThemeToggle'
@ -60,8 +61,6 @@ export const reduceFilters = (state: typeof initialFilterState, action: { eventT
return { ...state, [action.eventType]: !state[action.eventType] } return { ...state, [action.eventType]: !state[action.eventType] }
} }
const baseHref = (event: ActivityEvent) => `/#/nfts/asset/${event.collectionAddress}/${event.tokenId}?origin=activity`
export const Activity = ({ contractAddress, rarityVerified, collectionName, chainId }: ActivityProps) => { export const Activity = ({ contractAddress, rarityVerified, collectionName, chainId }: ActivityProps) => {
const [activeFilters, filtersDispatch] = useReducer(reduceFilters, initialFilterState) const [activeFilters, filtersDispatch] = useReducer(reduceFilters, initialFilterState)
@ -138,9 +137,11 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName, chai
(event, i) => (event, i) =>
event.eventType && ( event.eventType && (
<Box <Box
as="a" as={Link}
data-testid="nft-activity-row" data-testid="nft-activity-row"
href={baseHref(event)} // @ts-ignore Box component is not typed properly to typecheck
// custom components' props and will incorrectly report `to` as invalid
to={`/nfts/asset/${event.collectionAddress}/${event.tokenId}?origin=activity`}
className={styles.eventRow} className={styles.eventRow}
key={i} key={i}
> >

@ -309,7 +309,7 @@ exports[`NftCard renders correctly 1`] = `
> >
<a <a
class="c1" class="c1"
href="#/nfts/asset/0xed5af388653567af2f388e6224dc7c4b3241c544/3318?origin=collection" href="/nfts/asset/0xed5af388653567af2f388e6224dc7c4b3241c544/3318?origin=collection"
> >
<div <div
class="c2" class="c2"

@ -106,6 +106,14 @@ const LazyLoadSpinner = () => (
</SpinnerSVG> </SpinnerSVG>
) )
const RedirectHashToPath = ({ children }: { children: JSX.Element }) => {
const { hash } = useLocation()
if (hash) {
return <Navigate to={hash.replace('#', '')} replace />
}
return children
}
export default function App() { export default function App() {
const isLoaded = useFeatureFlagsIsLoaded() const isLoaded = useFeatureFlagsIsLoaded()
const [shouldDisableNFTRoutes, setShouldDisableNFTRoutes] = useAtom(shouldDisableNFTRoutesAtom) const [shouldDisableNFTRoutes, setShouldDisableNFTRoutes] = useAtom(shouldDisableNFTRoutesAtom)
@ -209,7 +217,16 @@ export default function App() {
<Suspense fallback={<Loader />}> <Suspense fallback={<Loader />}>
{isLoaded ? ( {isLoaded ? (
<Routes> <Routes>
<Route path="/" element={<Landing />} /> <Route
path="/"
element={
// if react-router-dom matches "/" with window.location.hash defined, it means that we're
// using BrowserRouter and can safely redirect to a path route
<RedirectHashToPath>
<Landing />
</RedirectHashToPath>
}
/>
<Route path="tokens" element={<Tokens />}> <Route path="tokens" element={<Tokens />}>
<Route path=":chainName" /> <Route path=":chainName" />

@ -1741,7 +1741,7 @@ exports[`disable nft on landing page does not render nft information and card 1`
> >
<a <a
class="c2" class="c2"
href="#/swap" href="/swap"
> >
<div <div
class="c3" class="c3"
@ -2031,7 +2031,7 @@ exports[`disable nft on landing page does not render nft information and card 1`
> >
<a <a
class="c2 c25 c56 c57" class="c2 c25 c56 c57"
href="#/swap" href="/swap"
> >
<p <p
class="0 c58" class="0 c58"
@ -2098,7 +2098,7 @@ exports[`disable nft on landing page does not render nft information and card 1`
> >
<a <a
class="c64" class="c64"
href="#/swap" href="/swap"
> >
<div <div
class="c65" class="c65"
@ -2178,7 +2178,7 @@ exports[`disable nft on landing page does not render nft information and card 1`
</a> </a>
<a <a
class="c70" class="c70"
href="#/pools" href="/pools"
> >
<div <div
class="c65" class="c65"
@ -2363,19 +2363,19 @@ exports[`disable nft on landing page does not render nft information and card 1`
</span> </span>
<a <a
class="1 c94 c95" class="1 c94 c95"
href="#/swap" href="/swap"
> >
Swap Swap
</a> </a>
<a <a
class="1 c94 c95" class="1 c94 c95"
href="#/tokens" href="/tokens"
> >
Tokens Tokens
</a> </a>
<a <a
class="1 c94 c95" class="1 c94 c95"
href="#/pools" href="/pools"
> >
Pools Pools
</a> </a>
@ -4315,7 +4315,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
> >
<a <a
class="c2" class="c2"
href="#/swap" href="/swap"
> >
<div <div
class="c3" class="c3"
@ -4605,7 +4605,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
> >
<a <a
class="c2 c25 c56 c57" class="c2 c25 c56 c57"
href="#/swap" href="/swap"
> >
<p <p
class="0 c58" class="0 c58"
@ -4672,7 +4672,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
> >
<a <a
class="c64" class="c64"
href="#/swap" href="/swap"
> >
<div <div
class="c65" class="c65"
@ -4698,7 +4698,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
</a> </a>
<a <a
class="c69" class="c69"
href="#/nfts" href="/nfts"
> >
<div <div
class="c65" class="c65"
@ -4778,7 +4778,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
</a> </a>
<a <a
class="c71" class="c71"
href="#/pools" href="/pools"
> >
<div <div
class="c65" class="c65"
@ -4963,25 +4963,25 @@ exports[`disable nft on landing page renders nft information and card 1`] = `
</span> </span>
<a <a
class="1 c95 c96" class="1 c95 c96"
href="#/swap" href="/swap"
> >
Swap Swap
</a> </a>
<a <a
class="1 c95 c96" class="1 c95 c96"
href="#/tokens" href="/tokens"
> >
Tokens Tokens
</a> </a>
<a <a
class="1 c95 c96" class="1 c95 c96"
href="#/nfts" href="/nfts"
> >
NFTs NFTs
</a> </a>
<a <a
class="1 c95 c96" class="1 c95 c96"
href="#/pools" href="/pools"
> >
Pools Pools
</a> </a>

@ -3,7 +3,7 @@ import type { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { InterfacePageName } from '@uniswap/analytics-events' import { InterfacePageName } from '@uniswap/analytics-events'
import { formatPrice, NumberType } from '@uniswap/conedison/format' import { formatPrice, NumberType } from '@uniswap/conedison/format'
import { ChainId, Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk' import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { Trace } from 'analytics' import { Trace } from 'analytics'
@ -30,12 +30,12 @@ import { useV3PositionFees } from 'hooks/useV3PositionFees'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions' import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { useSingleCallResult } from 'lib/hooks/multicall' import { useSingleCallResult } from 'lib/hooks/multicall'
import useNativeCurrency from 'lib/hooks/useNativeCurrency' import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useCallback, useMemo, useRef, useState } from 'react' import { PropsWithChildren, useCallback, useMemo, useRef, useState } from 'react'
import { Link, useParams } from 'react-router-dom' import { Link, useParams } from 'react-router-dom'
import { Bound } from 'state/mint/v3/actions' import { Bound } from 'state/mint/v3/actions'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks' import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
import { ExternalLink, HideExtraSmall, HideSmall, ThemedText } from 'theme' import { ExternalLink, HideExtraSmall, HideSmall, StyledRouterLink, ThemedText } from 'theme'
import { currencyId } from 'utils/currencyId' import { currencyId } from 'utils/currencyId'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { formatTickPrice } from 'utils/formatTickPrice' import { formatTickPrice } from 'utils/formatTickPrice'
@ -52,15 +52,6 @@ import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink' import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { LoadingRows } from './styleds' import { LoadingRows } from './styleds'
const getTokenLink = (chainId: ChainId, address: string) => {
if (isGqlSupportedChain(chainId)) {
const chainName = CHAIN_IDS_TO_NAMES[chainId]
return `${window.location.origin}/#/tokens/${chainName}/${address}`
} else {
return getExplorerLink(chainId, address, ExplorerDataType.TOKEN)
}
}
const PositionPageButtonPrimary = styled(ButtonPrimary)` const PositionPageButtonPrimary = styled(ButtonPrimary)`
width: 228px; width: 228px;
height: 40px; height: 40px;
@ -210,17 +201,31 @@ function CurrentPriceCard({
) )
} }
const TokenLink = ({
children,
chainId,
address,
}: PropsWithChildren<{ chainId: keyof typeof CHAIN_IDS_TO_NAMES; address: string }>) => {
const chainName = CHAIN_IDS_TO_NAMES[chainId]
return <StyledRouterLink to={`/tokens/${chainName}/${address}`}>{children}</StyledRouterLink>
}
const ExternalTokenLink = ({ children, chainId, address }: PropsWithChildren<{ chainId: number; address: string }>) => {
return <ExternalLink href={getExplorerLink(chainId, address, ExplorerDataType.TOKEN)}>{children}</ExternalLink>
}
function LinkedCurrency({ chainId, currency }: { chainId?: number; currency?: Currency }) { function LinkedCurrency({ chainId, currency }: { chainId?: number; currency?: Currency }) {
const address = (currency as Token)?.address const address = (currency as Token)?.address
if (typeof chainId === 'number' && address) { if (typeof chainId === 'number' && address) {
const Link = isGqlSupportedChain(chainId) ? TokenLink : ExternalTokenLink
return ( return (
<ExternalLink href={getTokenLink(chainId, address)}> <Link chainId={chainId} address={address}>
<RowFixed> <RowFixed>
<CurrencyLogo currency={currency} size="20px" style={{ marginRight: '0.5rem' }} /> <CurrencyLogo currency={currency} size="20px" style={{ marginRight: '0.5rem' }} />
<ThemedText.DeprecatedMain>{currency?.symbol} </ThemedText.DeprecatedMain> <ThemedText.DeprecatedMain>{currency?.symbol} </ThemedText.DeprecatedMain>
</RowFixed> </RowFixed>
</ExternalLink> </Link>
) )
} }

@ -17,15 +17,15 @@ describe('document', () => {
[{ request: { mode: 'navigate' }, url: { hostname: 'example.com', pathname: '' } }, false], [{ request: { mode: 'navigate' }, url: { hostname: 'example.com', pathname: '' } }, false],
[{ request: {}, url: { hostname: 'app.uniswap.org', pathname: '' } }, false], [{ request: {}, url: { hostname: 'app.uniswap.org', pathname: '' } }, false],
[{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap.org', pathname: '' } }, true], [{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap.org', pathname: '' } }, true],
[{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap.org', pathname: '/#/swap' } }, true], [{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap.org', pathname: '/swap' } }, true],
[{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap.org', pathname: '/asset.gif' } }, false], [{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap.org', pathname: '/asset.gif' } }, false],
[{ request: {}, url: { hostname: 'app.uniswap-staging.org', pathname: '' } }, false], [{ request: {}, url: { hostname: 'app.uniswap-staging.org', pathname: '' } }, false],
[{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap-staging.org', pathname: '' } }, true], [{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap-staging.org', pathname: '' } }, true],
[{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap-staging.org', pathname: '/#/swap' } }, true], [{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap-staging.org', pathname: '/swap' } }, true],
[{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap-staging.org', pathname: '/asset.gif' } }, false], [{ request: { mode: 'navigate' }, url: { hostname: 'app.uniswap-staging.org', pathname: '/asset.gif' } }, false],
[{ request: {}, url: { hostname: 'localhost', pathname: '' } }, false], [{ request: {}, url: { hostname: 'localhost', pathname: '' } }, false],
[{ request: { mode: 'navigate' }, url: { hostname: 'localhost', pathname: '' } }, true], [{ request: { mode: 'navigate' }, url: { hostname: 'localhost', pathname: '' } }, true],
[{ request: { mode: 'navigate' }, url: { hostname: 'localhost', pathname: '/#/swap' } }, true], [{ request: { mode: 'navigate' }, url: { hostname: 'localhost', pathname: '/swap' } }, true],
[{ request: { mode: 'navigate' }, url: { hostname: 'localhost', pathname: '/asset.gif' } }, false], [{ request: { mode: 'navigate' }, url: { hostname: 'localhost', pathname: '/asset.gif' } }, false],
] as [RouteMatchCallbackOptions, boolean][] ] as [RouteMatchCallbackOptions, boolean][]

@ -10,7 +10,7 @@ import { en } from 'make-plural/plurals'
import { ReactElement, ReactNode } from 'react' import { ReactElement, ReactNode } from 'react'
import { QueryClient, QueryClientProvider } from 'react-query' import { QueryClient, QueryClientProvider } from 'react-query'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { HashRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import store from 'state' import store from 'state'
import ThemeProvider from 'theme' import ThemeProvider from 'theme'
@ -30,7 +30,7 @@ const WithProviders = ({ children }: { children?: ReactNode }) => {
<MockedI18nProvider> <MockedI18nProvider>
<Provider store={store}> <Provider store={store}>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<HashRouter> <BrowserRouter>
{/* {/*
* Web3Provider is mocked through setupTests.ts * Web3Provider is mocked through setupTests.ts
* To test behavior that depends on Web3Provider, use jest.unmock('@web3-react/core') * To test behavior that depends on Web3Provider, use jest.unmock('@web3-react/core')
@ -40,7 +40,7 @@ const WithProviders = ({ children }: { children?: ReactNode }) => {
<ThemeProvider>{children}</ThemeProvider> <ThemeProvider>{children}</ThemeProvider>
</BlockNumberProvider> </BlockNumberProvider>
</MockedProvider> </MockedProvider>
</HashRouter> </BrowserRouter>
</QueryClientProvider> </QueryClientProvider>
</Provider> </Provider>
</MockedI18nProvider> </MockedI18nProvider>

@ -103,7 +103,7 @@ describe('trace', () => {
const errorEvent: ErrorEvent = { const errorEvent: ErrorEvent = {
type: undefined, type: undefined,
request: { request: {
url: 'https://app.uniswap.org/#/pools', url: 'https://app.uniswap.org/pools',
}, },
} }
const eventHint: EventHint = {} const eventHint: EventHint = {}
@ -114,7 +114,7 @@ describe('trace', () => {
const errorEvent: ErrorEvent = { const errorEvent: ErrorEvent = {
type: undefined, type: undefined,
request: { request: {
url: 'https://app.uniswap.org/#', url: 'https://app.uniswap.org/',
}, },
} }
const eventHint: EventHint = {} const eventHint: EventHint = {}

@ -23,6 +23,25 @@ export function isAppUniswapStagingOrg({ hostname }: { hostname: string }): bool
return hostname === 'app.uniswap-staging.org' return hostname === 'app.uniswap-staging.org'
} }
export function isBrowserRouterEnabled(): boolean {
if (isProductionEnv()) {
if (
isAppUniswapOrg(window.location) ||
isAppUniswapStagingOrg(window.location) ||
// Cypress tests
isLocalhost(window.location)
) {
return true
}
return false
}
return true
}
function isLocalhost({ hostname }: { hostname: string }): boolean {
return hostname === 'localhost'
}
export function isSentryEnabled(): boolean { export function isSentryEnabled(): boolean {
// Disable in e2e test environments // Disable in e2e test environments
if (isStagingEnv() && !isAppUniswapStagingOrg(window.location)) return false if (isStagingEnv() && !isAppUniswapStagingOrg(window.location)) return false

@ -5559,11 +5559,6 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/history@*", "@types/history@^4.7.11":
version "4.7.11"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1": "@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
version "3.3.1" version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
@ -5766,23 +5761,6 @@
hoist-non-react-statics "^3.3.0" hoist-non-react-statics "^3.3.0"
redux "^4.0.0" redux "^4.0.0"
"@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
dependencies:
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.16"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.16.tgz#f3ba045fb96634e38b21531c482f9aeb37608a99"
integrity sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-table@^7.7.12": "@types/react-table@^7.7.12":
version "7.7.12" version "7.7.12"
resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.12.tgz#628011d3cb695b07c678704a61f2f1d5b8e567fd" resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.12.tgz#628011d3cb695b07c678704a61f2f1d5b8e567fd"