From 2d20fc939d93d228ae4a410555a9591ea540932f Mon Sep 17 00:00:00 2001 From: Jordan Frankfurt Date: Tue, 6 Jul 2021 22:38:47 -0400 Subject: [PATCH] feat(L2): network alert cards (optimism and arbitrum) (#1957) * design review for arbitrum UI * add optimism L2 UI * better gradients, remove v2 links/routes, some minor padding and margin fixes --- src/assets/svg/arbitrum_mask.svg | 14 -- src/assets/svg/optimism_logo.svg | 5 + src/components/Header/NetworkCard.tsx | 63 ++++--- src/components/Menu/index.tsx | 10 +- .../NetworkAlert/MinimalNetworkAlert.tsx | 123 ++++++++++++++ src/components/NetworkAlert/NetworkAlert.tsx | 155 ++++++++++++++++++ src/components/swap/SwapNetworkAlert.tsx | 115 ------------- src/components/vote/ProposalEmptyState.tsx | 60 +++++++ src/connectors/index.ts | 4 + src/constants/chains.ts | 24 +++ src/constants/tokens.ts | 14 ++ src/pages/AddLiquidity/index.tsx | 3 +- src/pages/AddLiquidity/styled.tsx | 2 +- src/pages/App.tsx | 83 +++++----- src/pages/Pool/CTACards.tsx | 69 ++++---- src/pages/Pool/PositionPage.tsx | 18 +- src/pages/Pool/index.tsx | 100 +++++------ src/pages/Swap/index.tsx | 4 +- src/pages/Vote/index.tsx | 74 +++------ src/theme/RadialGradientByChainUpdater.ts | 46 +++++- 20 files changed, 645 insertions(+), 341 deletions(-) delete mode 100644 src/assets/svg/arbitrum_mask.svg create mode 100644 src/assets/svg/optimism_logo.svg create mode 100644 src/components/NetworkAlert/MinimalNetworkAlert.tsx create mode 100644 src/components/NetworkAlert/NetworkAlert.tsx delete mode 100644 src/components/swap/SwapNetworkAlert.tsx create mode 100644 src/components/vote/ProposalEmptyState.tsx diff --git a/src/assets/svg/arbitrum_mask.svg b/src/assets/svg/arbitrum_mask.svg deleted file mode 100644 index b880f736c8..0000000000 --- a/src/assets/svg/arbitrum_mask.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/assets/svg/optimism_logo.svg b/src/assets/svg/optimism_logo.svg new file mode 100644 index 0000000000..2e5ce3103d --- /dev/null +++ b/src/assets/svg/optimism_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/Header/NetworkCard.tsx b/src/components/Header/NetworkCard.tsx index 36cd72c029..0e381aa4d7 100644 --- a/src/components/Header/NetworkCard.tsx +++ b/src/components/Header/NetworkCard.tsx @@ -1,20 +1,27 @@ import { Trans } from '@lingui/macro' -import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg' import { YellowCard } from 'components/Card' import { useOnClickOutside } from 'hooks/useOnClickOutside' import { useActiveWeb3React } from 'hooks/web3' -import { transparentize } from 'polished' -import React, { useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { ArrowDownCircle } from 'react-feather' import { ApplicationModal } from 'state/application/actions' import { useModalOpen, useToggleModal } from 'state/application/hooks' import styled, { css } from 'styled-components' -import { ExternalLink } from 'theme' +import { ExternalLink, MEDIA_WIDTHS } from 'theme' import { switchToNetwork } from 'utils/switchToNetwork' -import { NETWORK_LABELS, SupportedChainId } from '../../constants/chains' +import { L2_CHAIN_IDS, L2_INFO, NETWORK_LABELS, SupportedChainId } from '../../constants/chains' + +const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) and (max-width: ${ + MEDIA_WIDTHS.upToMedium + 400 +}px)` const BaseWrapper = css` position: relative; + ${StopOverflowQuery} { + position: absolute; + top: 80px; + right: 20px; + } ${({ theme }) => theme.mediaWidth.upToMedium` margin-left: 12px; `}; @@ -26,12 +33,12 @@ const BaseWrapper = css` flex-shrink: 1; `}; ` -const ArbitrumWrapper = styled.div` +const L2Wrapper = styled.div` ${BaseWrapper} ` const BaseMenuItem = css` align-items: center; - background-color: ${({ theme }) => transparentize(0.9, theme.primary1)}; + background-color: transparent; border-radius: 12px; color: ${({ theme }) => theme.text2}; cursor: pointer; @@ -41,7 +48,6 @@ const BaseMenuItem = css` font-size: 14px; font-weight: 400; justify-content: space-between; - padding: 12px; :hover { color: ${({ theme }) => theme.text1}; text-decoration: none; @@ -64,6 +70,7 @@ const DisabledMenuItem = styled.div` ` const FallbackWrapper = styled(YellowCard)` ${BaseWrapper} + width: auto; border-radius: 12px; padding: 8px 12px; ` @@ -74,8 +81,8 @@ const L1Tag = styled.div` color: #c4d9f8; opacity: 40%; ` -const L2Tag = styled.div` - background-color: ${({ theme }) => theme.primary1}; +const L2Tag = styled.div<{ chainId: SupportedChainId }>` + background-color: ${({ chainId }) => (chainId === SupportedChainId.ARBITRUM_ONE ? '#28A0F0' : '#FF0420')}; border-radius: 6px; color: white; font-size: 12px; @@ -87,7 +94,7 @@ const MenuFlyout = styled.span` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); border-radius: 20px; - padding: 8px; + padding: 12px; display: flex; flex-direction: column; font-size: 1rem; @@ -97,16 +104,20 @@ const MenuFlyout = styled.span` z-index: 100; width: 237px; ${({ theme }) => theme.mediaWidth.upToMedium` - top: -14.25rem; + top: -10.25rem; `}; > { padding: 12px; } > :not(:first-child) { - margin-top: 4px; + margin-top: 8px; } > :not(:last-child) { - margin-bottom: 4px; + margin-bottom: 8px; + } + ${StopOverflowQuery} { + left: unset; + right: 0rem; } ` const LinkOutCircle = styled(ArrowDownCircle)` @@ -145,6 +156,7 @@ const NetworkInfo = styled.button` background-color: ${({ theme }) => theme.bg3}; } ` + export default function NetworkCard() { const { chainId, library } = useActiveWeb3React() const node = useRef(null) @@ -169,29 +181,30 @@ export default function NetworkCard() { return null } - if (chainId === SupportedChainId.ARBITRUM_ONE) { + if (L2_CHAIN_IDS.includes(chainId)) { + const info = L2_INFO[chainId] return ( - + - - Arbitrum - L2 Alpha + + {NETWORK_LABELS[chainId]} + L2 Alpha {open && ( - +
- Arbitrum Token Bridge + {NETWORK_LABELS[chainId]} Bridge
- +
- Arbitrum Explorer + {NETWORK_LABELS[chainId]} Explorer
- +
Learn more
@@ -211,7 +224,7 @@ export default function NetworkCard() { )}
)} -
+ ) } diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 852435013a..b5f60e5117 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -8,9 +8,9 @@ import { useOnClickOutside } from '../../hooks/useOnClickOutside' import { ApplicationModal } from '../../state/application/actions' import { useModalOpen, useToggleModal } from '../../state/application/hooks' import { Trans } from '@lingui/macro' - import { ExternalLink } from '../../theme' import { ButtonPrimary } from '../Button' +import { L2_CHAIN_IDS } from 'constants/chains' export enum FlyoutAlignment { LEFT = 'LEFT', @@ -86,7 +86,8 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>` left: 0rem; `}; ${({ theme }) => theme.mediaWidth.upToMedium` - top: -17.25rem; + top: unset; + bottom: 3em `}; ` @@ -124,13 +125,14 @@ const InternalMenuItem = styled(Link)` const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface' export default function Menu() { - const { account } = useActiveWeb3React() + const { account, chainId } = useActiveWeb3React() const node = useRef() const open = useModalOpen(ApplicationModal.MENU) const toggle = useToggleModal(ApplicationModal.MENU) useOnClickOutside(node, open ? toggle : undefined) const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM) + const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId)) return ( // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 @@ -171,7 +173,7 @@ export default function Menu() { Analytics - {account && ( + {showUNIClaimOption && ( Claim UNI diff --git a/src/components/NetworkAlert/MinimalNetworkAlert.tsx b/src/components/NetworkAlert/MinimalNetworkAlert.tsx new file mode 100644 index 0000000000..f5bd58289e --- /dev/null +++ b/src/components/NetworkAlert/MinimalNetworkAlert.tsx @@ -0,0 +1,123 @@ +import { Trans } from '@lingui/macro' +import { + ArbitrumWrapperBackgroundDarkMode, + ArbitrumWrapperBackgroundLightMode, + OptimismWrapperBackgroundDarkMode, + OptimismWrapperBackgroundLightMode, +} from 'components/NetworkAlert/NetworkAlert' +import { L2_CHAIN_IDS, L2_INFO, NETWORK_LABELS, SupportedChainId } from 'constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import { ArrowDownCircle } from 'react-feather' +import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks' +import styled from 'styled-components/macro' +import { MEDIA_WIDTHS } from 'theme' + +const L2Icon = styled.img` + display: none; + height: 40px; + margin: auto 20px auto 4px; + width: 40px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + display: block; + } +` +const DesktopTextBreak = styled.div` + display: none; + @media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) { + display: block; + } +` +const Wrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string }>` + ${({ chainId, darkMode }) => + chainId === SupportedChainId.OPTIMISM + ? darkMode + ? OptimismWrapperBackgroundDarkMode + : OptimismWrapperBackgroundLightMode + : darkMode + ? ArbitrumWrapperBackgroundDarkMode + : ArbitrumWrapperBackgroundLightMode}; + border-radius: 20px; + display: flex; + flex-direction: column; + overflow: hidden; + padding: 12px; + position: relative; + width: 100%; + + :before { + background-image: url(${({ logoUrl }) => logoUrl}); + background-repeat: no-repeat; + background-size: 300px; + content: ''; + height: 300px; + opacity: 0.1; + position: absolute; + transform: rotate(25deg) translate(-90px, -40px); + width: 300px; + z-index: -1; + } + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + flex-direction: row; + padding: 16px 20px; + } +` +const Body = styled.div` + line-height: 143%; + margin: 12px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + margin: 16px 20px 31px; + flex: 1 1 auto; + margin: 0; + } +` +const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 20px; + height: 20px; +` +const LinkOutToBridge = styled.a` + align-items: center; + background-color: black; + border-radius: 16px; + color: white; + display: flex; + justify-content: space-between; + margin: 0; + max-height: 47px; + padding: 14px; + text-decoration: none; + width: auto; + :hover, + :focus, + :active { + background-color: black; + } + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + margin: auto 0 auto auto; + padding: 14px 17px; + min-width: 226px; + } +` +export function MinimalNetworkAlert() { + const { chainId } = useActiveWeb3React() + const [darkMode] = useDarkModeManager() + const [arbitrumAlphaAcknowledged] = useArbitrumAlphaAlert() + + if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged) { + return null + } + const info = L2_INFO[chainId] + return ( + + + + This is an alpha release of Uniswap on the {NETWORK_LABELS[chainId]} network. + You must bridge L1 assets to the network to swap them. + + + Deposit to {NETWORK_LABELS[chainId]} + + + + ) +} diff --git a/src/components/NetworkAlert/NetworkAlert.tsx b/src/components/NetworkAlert/NetworkAlert.tsx new file mode 100644 index 0000000000..1b59b3d3a1 --- /dev/null +++ b/src/components/NetworkAlert/NetworkAlert.tsx @@ -0,0 +1,155 @@ +import { Trans } from '@lingui/macro' +import { L2_CHAIN_IDS, NETWORK_LABELS, SupportedChainId } from 'constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import { useCallback, useState } from 'react' +import { ArrowDownCircle, X } from 'react-feather' +import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks' +import { useETHBalances } from 'state/wallet/hooks' +import styled, { css } from 'styled-components/macro' +import { MEDIA_WIDTHS, TYPE } from 'theme' +import { L2_INFO } from '../../constants/chains' + +const L2Icon = styled.img` + width: 40px; + height: 40px; + justify-self: center; +` +const CloseIcon = styled(X)` + cursor: pointer; + position: absolute; + top: 16px; + right: 16px; +` +const ContentWrapper = styled.div` + align-items: center; + display: grid; + grid-gap: 4px; + grid-template-columns: 40px 4fr; + grid-template-rows: auto auto; + margin: 20px 16px; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + grid-template-columns: 42px 4fr; + grid-gap: 8px; + } +` +export const ArbitrumWrapperBackgroundDarkMode = css` + background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%), + radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1); +` +export const ArbitrumWrapperBackgroundLightMode = css` + background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%), + radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1); +` +export const OptimismWrapperBackgroundDarkMode = css` + background: radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%), + radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%); +` +export const OptimismWrapperBackgroundLightMode = css` + background: radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%), + radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5); +` +const RootWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string }>` + ${({ chainId, darkMode }) => + chainId === SupportedChainId.OPTIMISM + ? darkMode + ? OptimismWrapperBackgroundDarkMode + : OptimismWrapperBackgroundLightMode + : darkMode + ? ArbitrumWrapperBackgroundDarkMode + : ArbitrumWrapperBackgroundLightMode}; + border-radius: 20px; + display: flex; + flex-direction: column; + max-width: 480px; + min-height: 218px; + overflow: hidden; + position: relative; + width: 100%; + + :before { + background-image: url(${({ logoUrl }) => logoUrl}); + background-repeat: no-repeat; + background-size: 300px; + content: ''; + height: 300px; + opacity: 0.1; + position: absolute; + transform: rotate(25deg) translate(-90px, -40px); + width: 300px; + z-index: -1; + } +` +const Header = styled(TYPE.largeHeader)` + margin: 0; + padding-right: 30px; +` +const Body = styled.p` + grid-column: 1 / 3; + line-height: 143%; + margin: 0; + @media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) { + grid-column: 2 / 3; + } +` +const LinkOutCircle = styled(ArrowDownCircle)` + transform: rotate(230deg); + width: 20px; + height: 20px; +` +const LinkOutToBridge = styled.a` + align-items: center; + background-color: black; + border-radius: 16px; + color: white; + display: flex; + justify-content: space-between; + margin: 0 18px 18px 18px; + padding: 14px 24px; + text-decoration: none; + width: auto; + :hover, + :focus, + :active { + background-color: black; + } +` +export function NetworkAlert() { + const { account, chainId } = useActiveWeb3React() + const [darkMode] = useDarkModeManager() + const [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged] = useArbitrumAlphaAlert() + const [locallyDismissed, setLocallyDimissed] = useState(false) + const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] + + const dismiss = useCallback(() => { + if (userEthBalance?.greaterThan(0)) { + setArbitrumAlphaAcknowledged(true) + } else { + setLocallyDimissed(true) + } + }, [setArbitrumAlphaAcknowledged, userEthBalance]) + if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged || locallyDismissed) { + return null + } + const info = L2_INFO[chainId] + return ( + + + + +
+ Uniswap on {NETWORK_LABELS[chainId]} +
+ + + This is an alpha release of Uniswap on the {NETWORK_LABELS[chainId]} network. You must bridge L1 assets to + the network to swap them. + + +
+ + Deposit to {NETWORK_LABELS[chainId]} + + +
+ ) +} diff --git a/src/components/swap/SwapNetworkAlert.tsx b/src/components/swap/SwapNetworkAlert.tsx deleted file mode 100644 index 212c3a7e90..0000000000 --- a/src/components/swap/SwapNetworkAlert.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { Trans } from '@lingui/macro' -import arbitrumMaskUrl from 'assets/svg/arbitrum_mask.svg' -import { SupportedChainId } from 'constants/chains' -import { useActiveWeb3React } from 'hooks/web3' -import React, { useCallback, useState } from 'react' -import { ArrowDownCircle, X } from 'react-feather' -import { useArbitrumAlphaAlert } from 'state/user/hooks' -import { useETHBalances } from 'state/wallet/hooks' -import styled from 'styled-components' - -const CloseIcon = styled(X)` - cursor: pointer; - position: absolute; - top: 1em; - right: 1em; -` -const Wrapper = styled.div` - border-radius: 20px; - background: radial-gradient(285.11% 8200.45% at 29.05% 48.94%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%), - radial-gradient(76.02% 75.41% at 1.84% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%); - display: flex; - flex-direction: column; - max-width: 480px; - min-height: 212px; - overflow: hidden; - position: relative; - width: 100%; - - :before { - content: ''; - position: absolute; - width: 100%; - height: 100%; - z-index: -1; - background-image: url(${arbitrumMaskUrl}); - background-repeat: no-repeat; - transform: rotate(15deg), scale(1); - } -` -const ArbitrumTextStyles = styled.span` - font-style: italic; - font-weight: 900; - color: #f3de1e; - background: linear-gradient(to right, #f3de1e, #ffffff); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; -` -const Header = styled.h3` - margin: 0; - padding: 20px 20px 0; -` -const Body = styled.p` - line-height: 143%; - margin: 16px 20px 31px; -` -const LinkOutCircle = styled(ArrowDownCircle)` - transform: rotate(230deg); - width: 20px; - height: 20px; -` -const LinkOutToBridge = styled.a` - align-items: center; - background-color: black; - border-radius: 16px; - color: white; - display: flex; - justify-content: space-between; - margin: 0 18px 18px 18px; - padding: 14px 24px; - text-decoration: none; - width: auto; - :hover, - :focus, - :active { - background-color: black; - } -` -export function SwapNetworkAlert() { - const { account, chainId } = useActiveWeb3React() - const [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged] = useArbitrumAlphaAlert() - const [locallyDismissed, setLocallyDimissed] = useState(false) - const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] - - const dismiss = useCallback(() => { - if (userEthBalance?.greaterThan(0)) { - setArbitrumAlphaAcknowledged(true) - } else { - setLocallyDimissed(true) - } - }, [setArbitrumAlphaAcknowledged, userEthBalance]) - if (chainId !== SupportedChainId.ARBITRUM_ONE || arbitrumAlphaAcknowledged || locallyDismissed) { - return null - } - return ( - - -
- - Uniswap on Arbitrum - -
- - - This is an alpha release of Uniswap on the Arbitrum network. You must bridge L1 assets to the network to swap - them. - - - - Deposit to Arbitrum - - -
- ) -} diff --git a/src/components/vote/ProposalEmptyState.tsx b/src/components/vote/ProposalEmptyState.tsx new file mode 100644 index 0000000000..02304cb10d --- /dev/null +++ b/src/components/vote/ProposalEmptyState.tsx @@ -0,0 +1,60 @@ +import { Trans } from '@lingui/macro' +import { L2_CHAIN_IDS } from 'constants/chains' +import { useActiveWeb3React } from 'hooks/web3' +import styled from 'styled-components/macro' +import { TYPE } from 'theme' + +const EmptyProposals = styled.div` + border: 1px solid ${({ theme }) => theme.text4}; + padding: 16px 12px; + border-radius: 12px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +` +const Sub = styled.i` + align-items: center; + display: flex; + justify-content: center; + text-align: center; +` +interface EmptyStateProps { + HeaderContent: () => JSX.Element + SubHeaderContent: () => JSX.Element +} +const EmptyState = ({ HeaderContent, SubHeaderContent }: EmptyStateProps) => ( + + + + + + + + + + +) + +export default function ProposalEmptyState() { + const { chainId } = useActiveWeb3React() + if (chainId && L2_CHAIN_IDS.includes(chainId)) { + return ( + Please connect to Layer 1 Ethereum} + SubHeaderContent={() => ( + + Uniswap governance is only available on Layer 1. Switch your network to Ethereum Mainnet to view Proposals + and Vote. + + )} + /> + ) + } + return ( + No proposals found.} + SubHeaderContent={() => Proposals submitted by community members will appear here.} + /> + ) +} diff --git a/src/connectors/index.ts b/src/connectors/index.ts index 2d904994ee..fc86264d3e 100644 --- a/src/connectors/index.ts +++ b/src/connectors/index.ts @@ -29,6 +29,8 @@ const NETWORK_URLS: { [SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.ARBITRUM_ONE]: `https://arb1.arbitrum.io/rpc`, [SupportedChainId.ARBITRUM_RINKEBY]: `https://rinkeby.arbitrum.io/rpc`, + [SupportedChainId.OPTIMISM]: `https://mainnet.optimism.io`, + [SupportedChainId.OPTIMISTIC_KOVAN]: `https://kovan.optimism.io`, } const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ @@ -39,6 +41,8 @@ const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ SupportedChainId.ROPSTEN, SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY, + SupportedChainId.OPTIMISM, + SupportedChainId.OPTIMISTIC_KOVAN, ] export const network = new NetworkConnector({ diff --git a/src/constants/chains.ts b/src/constants/chains.ts index eb73a2eebd..1c7d432f15 100644 --- a/src/constants/chains.ts +++ b/src/constants/chains.ts @@ -1,3 +1,6 @@ +import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg' +import optimismLogoUrl from 'assets/svg/optimism_logo.svg' + export enum SupportedChainId { MAINNET = 1, ROPSTEN = 3, @@ -6,6 +9,25 @@ export enum SupportedChainId { KOVAN = 42, ARBITRUM_ONE = 42161, ARBITRUM_RINKEBY = 421611, + OPTIMISM = 10, + OPTIMISTIC_KOVAN = 69, +} + +export const L2_CHAIN_IDS = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.OPTIMISM] + +export const L2_INFO: Record = { + [SupportedChainId.OPTIMISM]: { + bridge: 'https://gateway.optimism.io/', + docs: 'https://optimism.io/', + explorer: 'https://optimistic.etherscan.io/', + logoUrl: optimismLogoUrl, + }, + [SupportedChainId.ARBITRUM_ONE]: { + bridge: 'https://bridge.arbitrum.io/', + explorer: 'https://explorer.arbitrum.io/', + docs: 'https://offchainlabs.com/', + logoUrl: arbitrumLogoUrl, + }, } export const NETWORK_LABELS: { [chainId in SupportedChainId | number]: string } = { @@ -16,4 +38,6 @@ export const NETWORK_LABELS: { [chainId in SupportedChainId | number]: string } [SupportedChainId.KOVAN]: 'Kovan', [SupportedChainId.ARBITRUM_ONE]: 'Arbitrum', [SupportedChainId.ARBITRUM_RINKEBY]: 'Arbitrum Testnet', + [SupportedChainId.OPTIMISM]: 'Optimism', + [SupportedChainId.OPTIMISTIC_KOVAN]: 'Optimism Testnet', } diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 775a8bcf0e..5a3960b31b 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -132,6 +132,20 @@ export const WETH9_EXTENDED: { [chainId: number]: Token } = { 'WETH', 'Wrapped Ether' ), + [SupportedChainId.OPTIMISM]: new Token( + SupportedChainId.OPTIMISM, + '0x4200000000000000000000000000000000000006', + 18, + 'WETH', + 'Wrapped Ether' + ), + [SupportedChainId.OPTIMISTIC_KOVAN]: new Token( + SupportedChainId.OPTIMISTIC_KOVAN, + '0x4200000000000000000000000000000000000006', + 18, + 'WETH', + 'Wrapped Ether' + ), } export class ExtendedEther extends Ether { diff --git a/src/pages/AddLiquidity/index.tsx b/src/pages/AddLiquidity/index.tsx index 7e3903189b..957019fc63 100644 --- a/src/pages/AddLiquidity/index.tsx +++ b/src/pages/AddLiquidity/index.tsx @@ -28,7 +28,7 @@ import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallbac import useTransactionDeadline from '../../hooks/useTransactionDeadline' import { useWalletModalToggle } from '../../state/application/hooks' import { Field, Bound } from '../../state/mint/v3/actions' - +import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' import { useTransactionAdder } from '../../state/transactions/hooks' import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks' import { TYPE, ExternalLink } from '../../theme' @@ -388,6 +388,7 @@ export default function AddLiquidity({ return ( <> + ` diff --git a/src/pages/App.tsx b/src/pages/App.tsx index fad44382a6..962173ae88 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -29,6 +29,8 @@ import { PositionPage } from './Pool/PositionPage' import AddLiquidity from './AddLiquidity' import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader' import CreateProposal from './CreateProposal' +import { useActiveWeb3React } from 'hooks/web3' +import { L2_CHAIN_IDS } from 'constants/chains' const AppWrapper = styled.div` display: flex; @@ -69,6 +71,46 @@ function TopLevelModals() { return } +const Routes = () => { + const { chainId } = useActiveWeb3React() + + const HIDE_ON_L2 = Boolean(chainId && L2_CHAIN_IDS.includes(chainId)) + return ( + + + + + {HIDE_ON_L2 && } + {HIDE_ON_L2 && } + + + + + + {HIDE_ON_L2 && } + {HIDE_ON_L2 && } + + + + {HIDE_ON_L2 && ( + + )} + + + + + {HIDE_ON_L2 && } + + + {HIDE_ON_L2 && } + {HIDE_ON_L2 && } + + + + + ) +} + export default function App() { return ( @@ -84,46 +126,7 @@ export default function App() { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/pages/Pool/CTACards.tsx b/src/pages/Pool/CTACards.tsx index d082a93f7f..4c8a6cfce4 100644 --- a/src/pages/Pool/CTACards.tsx +++ b/src/pages/Pool/CTACards.tsx @@ -1,16 +1,18 @@ +import { Trans } from '@lingui/macro' +import { AutoColumn } from 'components/Column' +import { MinimalNetworkAlert } from 'components/NetworkAlert/MinimalNetworkAlert' +import { RowBetween } from 'components/Row' import styled from 'styled-components/macro' import { TYPE } from 'theme' -import { Trans } from '@lingui/macro' -import { ExternalLink } from '../../theme' -import { AutoColumn } from 'components/Column' -import Squiggle from '../../assets/images/squiggle.png' import Texture from '../../assets/images/sandtexture.webp' -import { RowBetween } from 'components/Row' +import Squiggle from '../../assets/images/squiggle.png' +import { ExternalLink } from '../../theme' const CTASection = styled.section` display: grid; grid-template-columns: 2fr 1fr; gap: 8px; + margin-top: 8px; ${({ theme }) => theme.mediaWidth.upToSmall` grid-template-columns: auto; @@ -118,32 +120,35 @@ const StyledImage = styled.img` export default function CTACards() { return ( - - - - - Uniswap V3 is here! - - - Check out our v3 LP walkthrough and migration guides. - - - - - - - - - - - Top pools - - - Explore popular pools on Uniswap Analytics. - - - - - +
+ + + + + + Uniswap V3 is here! + + + Check out our v3 LP walkthrough and migration guides. + + + + + + + + + + + Top pools + + + Explore popular pools on Uniswap Analytics. + + + + + +
) } diff --git a/src/pages/Pool/PositionPage.tsx b/src/pages/Pool/PositionPage.tsx index 53f13fcf8e..e642bb822d 100644 --- a/src/pages/Pool/PositionPage.tsx +++ b/src/pages/Pool/PositionPage.tsx @@ -473,6 +473,15 @@ export function PositionPage({ ) } + const showCollectAsWeth = Boolean( + ownsNFT && + (feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0)) && + currency0 && + currency1 && + (currency0.isNative || currency1.isNative) && + !collectMigrationHash + ) + return loading || poolState === PoolState.LOADING || !feeAmount ? (
@@ -721,12 +730,7 @@ export function PositionPage({ - {ownsNFT && - (feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0)) && - currency0 && - currency1 && - (currency0.isNative || currency1.isNative) && - !collectMigrationHash ? ( + {showCollectAsWeth && ( @@ -739,7 +743,7 @@ export function PositionPage({ /> - ) : null} + )} diff --git a/src/pages/Pool/index.tsx b/src/pages/Pool/index.tsx index 9f9a84bfbd..17c6d11d94 100644 --- a/src/pages/Pool/index.tsx +++ b/src/pages/Pool/index.tsx @@ -1,25 +1,25 @@ -import { useContext } from 'react' +import { Trans } from '@lingui/macro' import { ButtonGray, ButtonOutlined, ButtonPrimary } from 'components/Button' import { AutoColumn } from 'components/Column' import { FlyoutAlignment, NewMenu } from 'components/Menu' import { SwapPoolTabs } from 'components/NavigationTabs' import PositionList from 'components/PositionList' import { RowBetween, RowFixed } from 'components/Row' -import { useActiveWeb3React } from 'hooks/web3' +import { SwitchLocaleLink } from 'components/SwitchLocaleLink' +import Toggle from 'components/Toggle' +import { L2_CHAIN_IDS } from 'constants/chains' import { useV3Positions } from 'hooks/useV3Positions' -import { BookOpen, ChevronDown, Download, Inbox, PlusCircle, ChevronsRight, Layers } from 'react-feather' -import { Trans } from '@lingui/macro' +import { useActiveWeb3React } from 'hooks/web3' +import { useContext } from 'react' +import { BookOpen, ChevronDown, ChevronsRight, Download, Inbox, Layers, PlusCircle } from 'react-feather' import { Link } from 'react-router-dom' import { useWalletModalToggle } from 'state/application/hooks' +import { useUserHideClosedPositions } from 'state/user/hooks' import styled, { ThemeContext } from 'styled-components' import { HideSmall, TYPE } from 'theme' -import { SwitchLocaleLink } from '../../components/SwitchLocaleLink' -import { LoadingRows } from './styleds' -import Toggle from 'components/Toggle' -import { useUserHideClosedPositions } from 'state/user/hooks' - -import CTACards from './CTACards' import { PositionDetails } from 'types/position' +import CTACards from './CTACards' +import { LoadingRows } from './styleds' const PageWrapper = styled(AutoColumn)` max-width: 870px; @@ -107,7 +107,7 @@ const ShowInactiveToggle = styled.div` ` export default function Pool() { - const { account } = useActiveWeb3React() + const { account, chainId } = useActiveWeb3React() const toggleWalletModal = useWalletModalToggle() const theme = useContext(ThemeContext) @@ -124,6 +124,8 @@ export default function Pool() { ) ?? [[], []] const filteredPositions = [...openPositions, ...(userHideClosedPositions ? [] : closedPositions)] + const showConnectAWallet = Boolean(!account) + const showV2Features = !!chainId && !L2_CHAIN_IDS.includes(chainId) const menuItems = [ { @@ -146,16 +148,6 @@ export default function Pool() { link: '/migrate/v2', external: false, }, - { - content: ( - - - V2 liquidity - - ), - link: '/pool/v2', - external: false, - }, { content: ( @@ -168,6 +160,19 @@ export default function Pool() { }, ] + if (showV2Features) { + menuItems.splice(2, 0, { + content: ( + + + V2 liquidity + + ), + link: '/pool/v2', + external: false, + }) + } + return ( <> @@ -241,11 +246,12 @@ export default function Pool() { Your V3 liquidity positions will appear here.
- {!account ? ( + {showConnectAWallet && ( Connect a wallet - ) : ( + )} + {showV2Features && ( )} - - - - - View V2 Liquidity - - {positions && positions.length > 0 && ( + {showV2Features && ( + - + - Migrate Liquidity + View V2 Liquidity - )} - + {positions && positions.length > 0 && ( + + + + Migrate Liquidity + + )} + + )} diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index 57843d59da..343a129e87 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -2,8 +2,8 @@ import { Trans } from '@lingui/macro' import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' +import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails' -import { SwapNetworkAlert } from 'components/swap/SwapNetworkAlert' import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter' import { MouseoverTooltip, MouseoverTooltipContent } from 'components/Tooltip' import JSBI from 'jsbi' @@ -358,7 +358,7 @@ export default function Swap({ history }: RouteComponentProps) { onConfirm={handleConfirmTokenWarning} onDismiss={handleDismissTokenWarning} /> - + diff --git a/src/pages/Vote/index.tsx b/src/pages/Vote/index.tsx index cdaa092fa3..45f8a1231c 100644 --- a/src/pages/Vote/index.tsx +++ b/src/pages/Vote/index.tsx @@ -1,29 +1,30 @@ -import { AutoColumn } from '../../components/Column' -import styled from 'styled-components/macro' -import { SwitchLocaleLink } from '../../components/SwitchLocaleLink' -import { UNI } from '../../constants/tokens' -import { ExternalLink, TYPE } from '../../theme' -import { AutoRow, RowBetween, RowFixed } from '../../components/Row' -import { Link } from 'react-router-dom' -import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink' -import { ProposalStatus } from './styled' -import { ButtonPrimary } from '../../components/Button' -import { Button } from 'rebass/styled-components' -import { darken } from 'polished' -import { CardBGImage, CardNoise, CardSection, DataCard } from '../../components/earn/styled' -import { ProposalData, useAllProposalData, useUserDelegatee, useUserVotes } from '../../state/governance/hooks' -import DelegateModal from '../../components/vote/DelegateModal' -import { useTokenBalance } from '../../state/wallet/hooks' -import { useActiveWeb3React } from '../../hooks/web3' -import { ZERO_ADDRESS } from '../../constants/misc' -import { Token, CurrencyAmount } from '@uniswap/sdk-core' -import JSBI from 'jsbi' -import { shortenAddress } from '../../utils' -import Loader from '../../components/Loader' -import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount' -import { useModalOpen, useToggleDelegateModal } from '../../state/application/hooks' -import { ApplicationModal } from '../../state/application/actions' import { Trans } from '@lingui/macro' +import { CurrencyAmount, Token } from '@uniswap/sdk-core' +import ProposalEmptyState from 'components/vote/ProposalEmptyState' +import JSBI from 'jsbi' +import { darken } from 'polished' +import { Link } from 'react-router-dom' +import { Button } from 'rebass/styled-components' +import styled from 'styled-components/macro' +import { ButtonPrimary } from 'components/Button' +import { AutoColumn } from 'components/Column' +import { CardBGImage, CardNoise, CardSection, DataCard } from 'components/earn/styled' +import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount' +import Loader from 'components/Loader' +import { AutoRow, RowBetween, RowFixed } from 'components/Row' +import { SwitchLocaleLink } from 'components/SwitchLocaleLink' +import DelegateModal from 'components/vote/DelegateModal' +import { ZERO_ADDRESS } from '../../constants/misc' +import { UNI } from '../../constants/tokens' +import { useActiveWeb3React } from 'hooks/web3' +import { ApplicationModal } from 'state/application/actions' +import { useModalOpen, useToggleDelegateModal } from 'state/application/hooks' +import { ProposalData, useAllProposalData, useUserDelegatee, useUserVotes } from 'state/governance/hooks' +import { useTokenBalance } from 'state/wallet/hooks' +import { ExternalLink, TYPE } from 'theme' +import { shortenAddress } from 'utils' +import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink' +import { ProposalStatus } from './styled' const PageWrapper = styled(AutoColumn)`` @@ -95,16 +96,6 @@ const StyledExternalLink = styled(ExternalLink)` color: ${({ theme }) => theme.text1}; ` -const EmptyProposals = styled.div` - border: 1px solid ${({ theme }) => theme.text4}; - padding: 16px 12px; - border-radius: 12px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -` - export default function Vote() { const { account, chainId } = useActiveWeb3React() @@ -239,18 +230,7 @@ export default function Vote() { )} )} - {allProposals?.length === 0 && ( - - - No proposals found. - - - - Proposals submitted by community members will appear here. - - - - )} + {allProposals?.length === 0 && } {allProposals ?.slice(0) ?.reverse() diff --git a/src/theme/RadialGradientByChainUpdater.ts b/src/theme/RadialGradientByChainUpdater.ts index 266353a2a2..7865183431 100644 --- a/src/theme/RadialGradientByChainUpdater.ts +++ b/src/theme/RadialGradientByChainUpdater.ts @@ -1,22 +1,54 @@ +import { useActiveWeb3React } from 'hooks/web3' import { useEffect } from 'react' +import { useDarkModeManager } from 'state/user/hooks' import { SupportedChainId } from '../constants/chains' -import { useActiveWeb3React } from '../hooks/web3' + +const initialStyles = { + width: '200vw', + height: '200vh', + transform: 'translate(-50vw, -100vh)', +} +const backgroundResetStyles = { + width: '100vw', + height: '100vh', + transform: 'unset', +} + +type TargetBackgroundStyles = typeof initialStyles | typeof backgroundResetStyles const backgroundRadialGradientElement = document.getElementById('background-radial-gradient') +const setBackground = (newValues: TargetBackgroundStyles) => + Object.entries(newValues).forEach(([key, value]) => { + if (backgroundRadialGradientElement) { + backgroundRadialGradientElement.style[key as keyof typeof backgroundResetStyles] = value + } + }) export default function RadialGradientByChainUpdater(): null { const { chainId } = useActiveWeb3React() + const [darkMode] = useDarkModeManager() // manage background color useEffect(() => { if (!backgroundRadialGradientElement) { return } - if (chainId === SupportedChainId.ARBITRUM_ONE) { - backgroundRadialGradientElement.style.background = - 'radial-gradient(96.19% 96.19% at 50% -5.43%, hsla(204, 87%, 55%, 0.2) 0%, hsla(227, 0%, 0%, 0) 100%)' - } else { - backgroundRadialGradientElement.style.background = '' + switch (chainId) { + case SupportedChainId.ARBITRUM_ONE: + setBackground(backgroundResetStyles) + const arbitrumLightGradient = 'radial-gradient(150% 100% at 50% 0%, #CDE8FB 0%, #FCF3F9 50%, #FFFFFF 100%)' + const arbitrumDarkGradient = 'radial-gradient(150% 100% at 50% 0%, #0A294B 0%, #221E30 50%, #1F2128 100%)' + backgroundRadialGradientElement.style.background = darkMode ? arbitrumDarkGradient : arbitrumLightGradient + break + case SupportedChainId.OPTIMISM: + setBackground(backgroundResetStyles) + const optimismLightGradient = 'radial-gradient(150% 100% at 50% 0%, #FFFBF2 2%, #FFF4F9 53%, #FFFFFF 100%)' + const optimismDarkGradient = 'radial-gradient(150% 100% at 50% 0%, #3E2E38 2%, #2C1F2D 53%, #1F2128 100%)' + backgroundRadialGradientElement.style.background = darkMode ? optimismDarkGradient : optimismLightGradient + break + default: + setBackground(initialStyles) + backgroundRadialGradientElement.style.background = '' } - }, [chainId]) + }, [darkMode, chainId]) return null }