feat: vertical LP (#7000)

* Re-organize add liquidity page

* fix title alignment

* Update styles

* Update logic for disable/enable input on creation

* lint and clean up a couple small things

* lint and clean up a couple small things

* Tweak UI

* clean up code

* add back range selector

* remove inline styles

---------

Co-authored-by: Callil Capuozzo <callil@uniswap.org>
This commit is contained in:
Jordan Frankfurt 2023-08-07 14:22:45 -05:00 committed by GitHub
parent dc2225a2bc
commit a1b3776686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 400 additions and 505 deletions

@ -4,12 +4,10 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
import { TraceEvent } from 'analytics'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
import { isSupportedChain } from 'constants/chains'
import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
@ -36,18 +34,6 @@ const InputPanel = styled.div<{ hideInput?: boolean }>`
will-change: height;
`
const FixedContainer = styled.div`
width: 100%;
height: 100%;
position: absolute;
border-radius: 16px;
background-color: ${({ theme }) => theme.backgroundInteractive};
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
`
const Container = styled.div<{ hideInput: boolean; disabled: boolean }>`
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
border: 1px solid ${({ theme }) => theme.backgroundSurface};
@ -226,110 +212,99 @@ export default function CurrencyInputPanel({
return (
<InputPanel id={id} hideInput={hideInput} {...rest}>
{locked && (
<FixedContainer>
<AutoColumn gap="sm" justify="center">
<Lock />
<ThemedText.DeprecatedLabel fontSize="12px" textAlign="center" padding="0 12px">
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
</ThemedText.DeprecatedLabel>
</AutoColumn>
</FixedContainer>
)}
<Container hideInput={hideInput} disabled={!chainAllowed}>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
{!hideInput && (
<StyledNumericalInput
className="token-amount-input"
value={value}
onUserInput={onUserInput}
disabled={!chainAllowed}
$loading={loading}
/>
)}
<CurrencySelect
disabled={!chainAllowed}
visible={currency !== undefined}
selected={!!currency}
hideInput={hideInput}
className="open-currency-select-button"
onClick={() => {
if (onCurrencySelect) {
setModalOpen(true)
}
}}
pointerEvents={!onCurrencySelect ? 'none' : undefined}
>
<Aligner>
<RowFixed>
{pair ? (
<span style={{ marginRight: '0.5rem' }}>
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
</span>
) : currency ? (
<CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size="24px" />
) : null}
{pair ? (
<StyledTokenName className="pair-name-container">
{pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName>
) : (
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
{(currency && currency.symbol && currency.symbol.length > 20
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || <Trans>Select a token</Trans>}
</StyledTokenName>
)}
</RowFixed>
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
</Aligner>
</CurrencySelect>
</InputRow>
{!hideInput && !hideBalance && currency && (
<FiatRow>
<RowBetween>
<LoadingOpacityContainer $loading={loading}>
{fiatValue && <FiatValue fiatValue={fiatValue} />}
</LoadingOpacityContainer>
{account ? (
<RowFixed style={{ height: '17px' }}>
<ThemedText.DeprecatedBody
onClick={onMax}
color={theme.textTertiary}
fontWeight={500}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
{!hideBalance && currency && selectedCurrencyBalance ? (
renderBalance ? (
renderBalance(selectedCurrencyBalance)
) : (
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
)
) : null}
</ThemedText.DeprecatedBody>
{showMaxButton && selectedCurrencyBalance ? (
<TraceEvent
events={[BrowserEvent.onClick]}
name={SwapEventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
element={InterfaceElementName.MAX_TOKEN_AMOUNT_BUTTON}
>
<StyledBalanceMax onClick={onMax}>
<Trans>MAX</Trans>
</StyledBalanceMax>
</TraceEvent>
) : null}
</RowFixed>
) : (
<span />
{!locked && (
<>
<Container hideInput={hideInput} disabled={!chainAllowed}>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
{!hideInput && (
<StyledNumericalInput
className="token-amount-input"
value={value}
onUserInput={onUserInput}
disabled={!chainAllowed}
$loading={loading}
/>
)}
</RowBetween>
</FiatRow>
)}
</Container>
<CurrencySelect
disabled={!chainAllowed}
visible={currency !== undefined}
selected={!!currency}
hideInput={hideInput}
className="open-currency-select-button"
onClick={() => {
if (onCurrencySelect) {
setModalOpen(true)
}
}}
pointerEvents={!onCurrencySelect ? 'none' : undefined}
>
<Aligner>
<RowFixed>
{pair ? (
<span style={{ marginRight: '0.5rem' }}>
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
</span>
) : (
currency && <CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size="24px" />
)}
{pair ? (
<StyledTokenName className="pair-name-container">
{pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName>
) : (
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
{(currency && currency.symbol && currency.symbol.length > 20
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || <Trans>Select a token</Trans>}
</StyledTokenName>
)}
</RowFixed>
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
</Aligner>
</CurrencySelect>
</InputRow>
{Boolean(!hideInput && !hideBalance && currency) && (
<FiatRow>
<RowBetween>
<LoadingOpacityContainer $loading={loading}>
{fiatValue && <FiatValue fiatValue={fiatValue} />}
</LoadingOpacityContainer>
{account && (
<RowFixed style={{ height: '17px' }}>
<ThemedText.DeprecatedBody
onClick={onMax}
color={theme.textTertiary}
fontWeight={500}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
{Boolean(!hideBalance && currency && selectedCurrencyBalance) &&
(renderBalance?.(selectedCurrencyBalance as CurrencyAmount<Currency>) || (
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
))}
</ThemedText.DeprecatedBody>
{Boolean(showMaxButton && selectedCurrencyBalance) && (
<TraceEvent
events={[BrowserEvent.onClick]}
name={SwapEventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
element={InterfaceElementName.MAX_TOKEN_AMOUNT_BUTTON}
>
<StyledBalanceMax onClick={onMax}>
<Trans>MAX</Trans>
</StyledBalanceMax>
</TraceEvent>
)}
</RowFixed>
)}
</RowBetween>
</FiatRow>
)}
</Container>
</>
)}
{onCurrencySelect && (
<CurrencySearchModal
isOpen={modalOpen}

@ -25,9 +25,7 @@ const pulse = (color: string) => keyframes`
`
const InputRow = styled.div`
display: grid;
grid-template-columns: 30px 1fr 30px;
display: flex;
`
const SmallButton = styled(ButtonGray)`
@ -43,18 +41,17 @@ const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boo
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
background-color: transparent;
text-align: center;
width: 100%;
font-weight: 500;
padding: 0 10px;
text-align: left;
width: 100%;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
font-size: 16px;
`};
`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall`
font-size: 12px;
`};
const InputColumn = styled(AutoColumn)`
width: 100%;
`
const InputTitle = styled(ThemedText.DeprecatedSmall)`
@ -142,20 +139,11 @@ const StepCounter = ({
return (
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
<AutoColumn gap="6px">
<InputTitle fontSize={12} textAlign="center">
{title}
</InputTitle>
<InputRow>
{!locked && (
<SmallButton data-testid="decrement-price-range" onClick={handleDecrement} disabled={decrementDisabled}>
<ButtonLabel disabled={decrementDisabled} fontSize="12px">
<Minus size={18} />
</ButtonLabel>
</SmallButton>
)}
<InputRow>
<InputColumn justify="flex-start">
<InputTitle fontSize={12} textAlign="center">
{title}
</InputTitle>
<StyledInput
className="rate-input-0"
value={localValue}
@ -165,7 +153,14 @@ const StepCounter = ({
setLocalValue(val)
}}
/>
<InputTitle fontSize={12} textAlign="left">
<Trans>
{tokenB} per {tokenA}
</Trans>
</InputTitle>
</InputColumn>
<AutoColumn gap="8px">
{!locked && (
<SmallButton data-testid="increment-price-range" onClick={handleIncrement} disabled={incrementDisabled}>
<ButtonLabel disabled={incrementDisabled} fontSize="12px">
@ -173,14 +168,15 @@ const StepCounter = ({
</ButtonLabel>
</SmallButton>
)}
</InputRow>
<InputTitle fontSize={12} textAlign="center">
<Trans>
{tokenB} per {tokenA}
</Trans>
</InputTitle>
</AutoColumn>
{!locked && (
<SmallButton data-testid="decrement-price-range" onClick={handleDecrement} disabled={decrementDisabled}>
<ButtonLabel disabled={decrementDisabled} fontSize="12px">
<Minus size={18} />
</ButtonLabel>
</SmallButton>
)}
</AutoColumn>
</InputRow>
</FocusedOutlineCard>
)
}

@ -12,7 +12,7 @@ const Wrapper = styled.div<{ count: number }>`
grid-gap: 6px;
position: absolute;
top: -75px;
top: -32px;
right: 0;
`

@ -6,7 +6,7 @@ import Loader from 'components/Icons/LoadingSpinner'
import { format } from 'd3'
import { useColor } from 'hooks/useColor'
import { saturate } from 'polished'
import React, { ReactNode, useCallback, useMemo } from 'react'
import { ReactNode, useCallback, useMemo } from 'react'
import { BarChart2, CloudOff, Inbox } from 'react-feather'
import { batch } from 'react-redux'
import { Bound } from 'state/mint/v3/actions'
@ -46,7 +46,8 @@ const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
const ChartWrapper = styled.div`
position: relative;
width: 100%;
max-height: 200px;
justify-content: center;
align-content: center;
`
@ -180,7 +181,7 @@ export default function LiquidityChartRangeInput({
<ChartWrapper>
<Chart
data={{ series: formattedData, current: price }}
dimensions={{ width: 400, height: 200 }}
dimensions={{ width: 560, height: 200 }}
margins={{ top: 10, right: 2, bottom: 20, left: 0 }}
styles={{
area: {

@ -3,7 +3,7 @@ import { Percent } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { ReactNode } from 'react'
import { ArrowLeft } from 'react-feather'
import { Link as HistoryLink, useLocation } from 'react-router-dom'
import { Link, useLocation } from 'react-router-dom'
import { Box } from 'rebass'
import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions'
@ -12,7 +12,7 @@ import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { flexRowNoWrap } from 'theme/styles'
import Row, { RowBetween } from '../Row'
import { RowBetween } from '../Row'
import SettingsTab from '../Settings'
const Tabs = styled.div`
@ -22,7 +22,7 @@ const Tabs = styled.div`
justify-content: space-evenly;
`
const StyledHistoryLink = styled(HistoryLink)<{ flex?: string }>`
const StyledLink = styled(Link)<{ flex?: string }>`
flex: ${({ flex }) => flex ?? 'none'};
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
@ -31,9 +31,10 @@ const StyledHistoryLink = styled(HistoryLink)<{ flex?: string }>`
`};
`
const ActiveText = styled.div`
font-weight: 500;
font-size: 20px;
const FindPoolTabsText = styled(ThemedText.SubHeaderLarge)`
position: absolute;
left: 50%;
transform: translateX(-50%);
`
const StyledArrowLeft = styled(ArrowLeft)`
@ -44,17 +45,22 @@ export function FindPoolTabs({ origin }: { origin: string }) {
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem', position: 'relative' }}>
<HistoryLink to={origin}>
<Link to={origin}>
<StyledArrowLeft />
</HistoryLink>
<ActiveText style={{ position: 'absolute', left: '50%', transform: 'translateX(-50%)' }}>
</Link>
<FindPoolTabsText>
<Trans>Import V2 Pool</Trans>
</ActiveText>
</FindPoolTabsText>
</RowBetween>
</Tabs>
)
}
const AddRemoveTitleText = styled(ThemedText.SubHeaderLarge)`
flex: 1;
margin: auto;
`
export function AddRemoveTabs({
adding,
creating,
@ -83,7 +89,7 @@ export function AddRemoveTabs({
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<StyledHistoryLink
<StyledLink
to={poolLink}
onClick={() => {
if (adding) {
@ -95,12 +101,8 @@ export function AddRemoveTabs({
flex={children ? '1' : undefined}
>
<StyledArrowLeft stroke={theme.textSecondary} />
</StyledHistoryLink>
<ThemedText.DeprecatedMediumHeader
fontWeight={500}
fontSize={20}
style={{ flex: '1', margin: 'auto', textAlign: children ? 'start' : 'center' }}
>
</StyledLink>
<AddRemoveTitleText textAlign={children ? 'start' : 'center'}>
{creating ? (
<Trans>Create a pair</Trans>
) : adding ? (
@ -108,23 +110,10 @@ export function AddRemoveTabs({
) : (
<Trans>Remove Liquidity</Trans>
)}
</ThemedText.DeprecatedMediumHeader>
<Box style={{ marginRight: '.5rem' }}>{children}</Box>
</AddRemoveTitleText>
{children && <Box style={{ marginRight: '.5rem' }}>{children}</Box>}
<SettingsTab autoSlippage={autoSlippage} chainId={chainId} />
</RowBetween>
</Tabs>
)
}
export function CreateProposalTabs() {
return (
<Tabs>
<Row style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink to="/vote">
<StyledArrowLeft />
</HistoryLink>
<ActiveText style={{ marginLeft: 'auto', marginRight: 'auto' }}>Create Proposal</ActiveText>
</Row>
</Tabs>
)
}

@ -5,7 +5,7 @@ import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
const Button = styled(ButtonOutlined).attrs(() => ({
padding: '8px',
padding: '6px',
$borderRadius: '8px',
}))`
color: ${({ theme }) => theme.textPrimary};

@ -1,8 +1,7 @@
import { Trans } from '@lingui/macro'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import { AutoColumn } from 'components/Column'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row'
import { AutoRow } from 'components/Row'
import { Bound } from 'state/mint/v3/actions'
// currencyA is the base token
@ -41,37 +40,33 @@ export default function RangeSelector({
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return (
<AutoColumn gap="md">
<RowBetween>
<StepCounter
value={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] ? '0' : leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLeftRangeInput}
width="48%"
decrement={isSorted ? getDecrementLower : getIncrementUpper}
increment={isSorted ? getIncrementLower : getDecrementUpper}
decrementDisabled={leftPrice === undefined || ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
incrementDisabled={leftPrice === undefined || ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
feeAmount={feeAmount}
label={leftPrice ? `${currencyB?.symbol}` : '-'}
title={<Trans>Min Price</Trans>}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
/>
<StepCounter
value={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] ? '∞' : rightPrice?.toSignificant(5) ?? ''}
onUserInput={onRightRangeInput}
width="48%"
decrement={isSorted ? getDecrementUpper : getIncrementLower}
increment={isSorted ? getIncrementUpper : getDecrementLower}
incrementDisabled={rightPrice === undefined || ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
decrementDisabled={rightPrice === undefined || ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
feeAmount={feeAmount}
label={rightPrice ? `${currencyB?.symbol}` : '-'}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
title={<Trans>Max Price</Trans>}
/>
</RowBetween>
</AutoColumn>
<AutoRow gap="md">
<StepCounter
value={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] ? '0' : leftPrice?.toSignificant(8) ?? ''}
onUserInput={onLeftRangeInput}
decrement={isSorted ? getDecrementLower : getIncrementUpper}
increment={isSorted ? getIncrementLower : getDecrementUpper}
decrementDisabled={leftPrice === undefined || ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
incrementDisabled={leftPrice === undefined || ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
feeAmount={feeAmount}
label={leftPrice ? `${currencyB?.symbol}` : '-'}
title={<Trans>Low price</Trans>}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
/>
<StepCounter
value={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] ? '∞' : rightPrice?.toSignificant(8) ?? ''}
onUserInput={onRightRangeInput}
decrement={isSorted ? getDecrementUpper : getIncrementLower}
increment={isSorted ? getIncrementUpper : getDecrementLower}
incrementDisabled={rightPrice === undefined || ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
decrementDisabled={rightPrice === undefined || ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
feeAmount={feeAmount}
label={rightPrice ? `${currencyB?.symbol}` : '-'}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
title={<Trans>High price</Trans>}
/>
</AutoRow>
)
}

@ -13,6 +13,7 @@ import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter
import { isSupportedChain } from 'constants/chains'
import usePrevious from 'hooks/usePrevious'
import { useSingleCallResult } from 'lib/hooks/multicall'
import { BodyWrapper } from 'pages/AppBody'
import { PositionPageUnsupportedContent } from 'pages/Pool/PositionPage'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { AlertTriangle } from 'react-feather'
@ -24,7 +25,7 @@ import {
useV3MintActionHandlers,
useV3MintState,
} from 'state/mint/v3/hooks'
import { useTheme } from 'styled-components/macro'
import styled, { useTheme } from 'styled-components/macro'
import { addressesAreEquivalent } from 'utils/addressesAreEquivalent'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonText } from '../../components/Button'
@ -39,7 +40,7 @@ import { PositionPreview } from '../../components/PositionPreview'
import RangeSelector from '../../components/RangeSelector'
import PresetsButtons from '../../components/RangeSelector/PresetsButtons'
import RateToggle from '../../components/RateToggle'
import Row, { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import Row, { RowBetween, RowFixed } from '../../components/Row'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import { ZERO_PERCENT } from '../../constants/misc'
@ -67,20 +68,20 @@ import { Review } from './Review'
import {
CurrencyDropdown,
DynamicSection,
HideMedium,
MediumOnly,
PageWrapper,
ResponsiveTwoColumns,
RightContainer,
ScrollablePage,
StackedContainer,
StackedItem,
StyledInput,
Wrapper,
} from './styled'
const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
const StyledBodyWrapper = styled(BodyWrapper)<{ $hasExistingPosition: boolean }>`
padding: ${({ $hasExistingPosition }) => ($hasExistingPosition ? '10px' : 0)};
max-width: 640px;
`
export default function AddLiquidityWrapper() {
const { chainId } = useWeb3React()
if (isSupportedChain(chainId)) {
@ -97,7 +98,12 @@ function AddLiquidity() {
currencyIdB,
feeAmount: feeAmountFromUrl,
tokenId,
} = useParams<{ currencyIdA?: string; currencyIdB?: string; feeAmount?: string; tokenId?: string }>()
} = useParams<{
currencyIdA?: string
currencyIdB?: string
feeAmount?: string
tokenId?: string
}>()
const { account, chainId, provider } = useWeb3React()
const theme = useTheme()
@ -600,7 +606,7 @@ function AddLiquidity() {
)}
pendingText={pendingText}
/>
<PageWrapper wide={!hasExistingPosition}>
<StyledBodyWrapper $hasExistingPosition={hasExistingPosition}>
<AddRemoveTabs
creating={false}
adding={true}
@ -611,28 +617,12 @@ function AddLiquidity() {
{!hasExistingPosition && (
<Row justifyContent="flex-end" style={{ width: 'fit-content', minWidth: 'fit-content' }}>
<MediumOnly>
<ButtonText onClick={clearAll} margin="0 15px 0 0">
<ButtonText onClick={clearAll}>
<ThemedText.DeprecatedBlue fontSize="12px">
<Trans>Clear All</Trans>
</ThemedText.DeprecatedBlue>
</ButtonText>
</MediumOnly>
{baseCurrency && quoteCurrency ? (
<RateToggle
currencyA={baseCurrency}
currencyB={quoteCurrency}
handleRateToggle={() => {
if (!ticksAtLimit[Bound.LOWER] && !ticksAtLimit[Bound.UPPER]) {
onLeftRangeInput((invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '')
onRightRangeInput((invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '')
onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? '')
}
navigate(
`/add/${currencyIdB as string}/${currencyIdA as string}${feeAmount ? '/' + feeAmount : ''}`
)
}}
/>
) : null}
</Row>
)}
</AddRemoveTabs>
@ -698,10 +688,190 @@ function AddLiquidity() {
/>
)}
</AutoColumn>
{!hasExistingPosition && (
<>
<DynamicSection gap="md" disabled={!feeAmount || invalidPool}>
<RowBetween>
<ThemedText.DeprecatedLabel>
<Trans>Set Price Range</Trans>
</ThemedText.DeprecatedLabel>
{Boolean(baseCurrency && quoteCurrency) && (
<RowFixed gap="8px">
<PresetsButtons onSetFullRange={handleSetFullRange} />
<RateToggle
currencyA={baseCurrency as Currency}
currencyB={quoteCurrency as Currency}
handleRateToggle={() => {
if (!ticksAtLimit[Bound.LOWER] && !ticksAtLimit[Bound.UPPER]) {
onLeftRangeInput(
(invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? ''
)
onRightRangeInput(
(invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? ''
)
onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? '')
}
navigate(
`/add/${currencyIdB as string}/${currencyIdA as string}${
feeAmount ? '/' + feeAmount : ''
}`
)
}}
/>
</RowFixed>
)}
</RowBetween>
<RangeSelector
priceLower={priceLower}
priceUpper={priceUpper}
getDecrementLower={getDecrementLower}
getIncrementLower={getIncrementLower}
getDecrementUpper={getDecrementUpper}
getIncrementUpper={getIncrementUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
currencyA={baseCurrency}
currencyB={quoteCurrency}
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
/>
{outOfRange && (
<YellowCard padding="8px 12px" $borderRadius="12px">
<RowBetween>
<AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
<ThemedText.DeprecatedYellow ml="12px" fontSize="12px">
<Trans>
Your position will not earn fees or be used in trades until the market price moves into
your range.
</Trans>
</ThemedText.DeprecatedYellow>
</RowBetween>
</YellowCard>
)}
{invalidRange && (
<YellowCard padding="8px 12px" $borderRadius="12px">
<RowBetween>
<AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
<ThemedText.DeprecatedYellow ml="12px" fontSize="12px">
<Trans>Invalid range selected. The min price must be lower than the max price.</Trans>
</ThemedText.DeprecatedYellow>
</RowBetween>
</YellowCard>
)}
</DynamicSection>
<DynamicSection gap="md" disabled={!feeAmount || invalidPool}>
{!noLiquidity ? (
<>
{Boolean(price && baseCurrency && quoteCurrency && !noLiquidity) && (
<AutoColumn gap="2px" style={{ marginTop: '0.5rem' }}>
<Trans>
<ThemedText.DeprecatedMain fontWeight={500} fontSize={12} color="text1">
Current Price:
</ThemedText.DeprecatedMain>
<ThemedText.DeprecatedBody fontWeight={500} fontSize={20} color="text1">
{price && (
<HoverInlineText
maxCharacters={20}
text={invertPrice ? price.invert().toSignificant(6) : price.toSignificant(6)}
/>
)}
</ThemedText.DeprecatedBody>
{baseCurrency && (
<ThemedText.DeprecatedBody color="text2" fontSize={12}>
{quoteCurrency?.symbol} per {baseCurrency.symbol}
</ThemedText.DeprecatedBody>
)}
</Trans>
</AutoColumn>
)}
<LiquidityChartRangeInput
currencyA={baseCurrency ?? undefined}
currencyB={quoteCurrency ?? undefined}
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
price={
price ? parseFloat((invertPrice ? price.invert() : price).toSignificant(8)) : undefined
}
priceLower={priceLower}
priceUpper={priceUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
interactive={!hasExistingPosition}
/>
</>
) : (
<AutoColumn gap="md">
{noLiquidity && (
<BlueCard
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
padding: '1rem 1rem',
}}
>
<ThemedText.DeprecatedBody
fontSize={14}
style={{ fontWeight: 500 }}
textAlign="left"
color={theme.accentAction}
>
<Trans>
This pool must be initialized before you can add liquidity. To initialize, select a
starting price for the pool. Then, enter your liquidity price range and deposit amount.
Gas fees will be higher than usual due to the initialization transaction.
</Trans>
</ThemedText.DeprecatedBody>
</BlueCard>
)}
<OutlineCard padding="12px">
<StyledInput
className="start-price-input"
value={startPriceTypedValue}
onUserInput={onStartPriceInput}
/>
</OutlineCard>
<RowBetween
style={{
backgroundColor: theme.deprecated_bg1,
padding: '12px',
borderRadius: '12px',
}}
>
<ThemedText.DeprecatedMain>
<Trans>Starting {baseCurrency?.symbol} Price:</Trans>
</ThemedText.DeprecatedMain>
<ThemedText.DeprecatedMain>
{price ? (
<ThemedText.DeprecatedMain>
<RowFixed>
<HoverInlineText
maxCharacters={20}
text={invertPrice ? price?.invert()?.toSignificant(8) : price?.toSignificant(8)}
/>{' '}
<span style={{ marginLeft: '4px' }}>
{quoteCurrency?.symbol} per {baseCurrency?.symbol}
</span>
</RowFixed>
</ThemedText.DeprecatedMain>
) : (
'-'
)}
</ThemedText.DeprecatedMain>
</RowBetween>
</AutoColumn>
)}
</DynamicSection>
</>
)}
<div>
<DynamicSection
disabled={tickLower === undefined || tickUpper === undefined || invalidPool || invalidRange}
>
<DynamicSection disabled={invalidPool || invalidRange || (noLiquidity && !startPriceTypedValue)}>
<AutoColumn gap="md">
<ThemedText.DeprecatedLabel>
{hasExistingPosition ? <Trans>Add more liquidity</Trans> : <Trans>Deposit Amounts</Trans>}
@ -737,199 +907,10 @@ function AddLiquidity() {
</AutoColumn>
</DynamicSection>
</div>
{!hasExistingPosition ? (
<>
<HideMedium>
<Buttons />
</HideMedium>
<RightContainer gap="lg">
<DynamicSection gap="md" disabled={!feeAmount || invalidPool}>
{!noLiquidity ? (
<>
<RowBetween>
<ThemedText.DeprecatedLabel>
<Trans>Set Price Range</Trans>
</ThemedText.DeprecatedLabel>
</RowBetween>
{price && baseCurrency && quoteCurrency && !noLiquidity && (
<AutoRow gap="4px" justify="center" style={{ marginTop: '0.5rem' }}>
<Trans>
<ThemedText.DeprecatedMain
fontWeight={500}
textAlign="center"
fontSize={12}
color="text1"
>
Current Price:
</ThemedText.DeprecatedMain>
<ThemedText.DeprecatedBody
fontWeight={500}
textAlign="center"
fontSize={12}
color="text1"
>
<HoverInlineText
maxCharacters={20}
text={invertPrice ? price.invert().toSignificant(6) : price.toSignificant(6)}
/>
</ThemedText.DeprecatedBody>
<ThemedText.DeprecatedBody color="text2" fontSize={12}>
{quoteCurrency?.symbol} per {baseCurrency.symbol}
</ThemedText.DeprecatedBody>
</Trans>
</AutoRow>
)}
<LiquidityChartRangeInput
currencyA={baseCurrency ?? undefined}
currencyB={quoteCurrency ?? undefined}
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
price={
price ? parseFloat((invertPrice ? price.invert() : price).toSignificant(8)) : undefined
}
priceLower={priceLower}
priceUpper={priceUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
interactive={!hasExistingPosition}
/>
</>
) : (
<AutoColumn gap="md">
<RowBetween>
<ThemedText.DeprecatedLabel>
<Trans>Set Starting Price</Trans>
</ThemedText.DeprecatedLabel>
</RowBetween>
{noLiquidity && (
<BlueCard
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
padding: '1rem 1rem',
}}
>
<ThemedText.DeprecatedBody
fontSize={14}
style={{ fontWeight: 500 }}
textAlign="left"
color={theme.accentAction}
>
<Trans>
This pool must be initialized before you can add liquidity. To initialize, select a
starting price for the pool. Then, enter your liquidity price range and deposit
amount. Gas fees will be higher than usual due to the initialization transaction.
</Trans>
</ThemedText.DeprecatedBody>
</BlueCard>
)}
<OutlineCard padding="12px">
<StyledInput
className="start-price-input"
value={startPriceTypedValue}
onUserInput={onStartPriceInput}
/>
</OutlineCard>
<RowBetween
style={{ backgroundColor: theme.deprecated_bg1, padding: '12px', borderRadius: '12px' }}
>
<ThemedText.DeprecatedMain>
<Trans>Current {baseCurrency?.symbol} Price:</Trans>
</ThemedText.DeprecatedMain>
<ThemedText.DeprecatedMain>
{price ? (
<ThemedText.DeprecatedMain>
<RowFixed>
<HoverInlineText
maxCharacters={20}
text={invertPrice ? price?.invert()?.toSignificant(5) : price?.toSignificant(5)}
/>{' '}
<span style={{ marginLeft: '4px' }}>{quoteCurrency?.symbol}</span>
</RowFixed>
</ThemedText.DeprecatedMain>
) : (
'-'
)}
</ThemedText.DeprecatedMain>
</RowBetween>
</AutoColumn>
)}
</DynamicSection>
<DynamicSection
gap="md"
disabled={!feeAmount || invalidPool || (noLiquidity && !startPriceTypedValue)}
>
<StackedContainer>
<StackedItem>
<AutoColumn gap="md">
{noLiquidity && (
<RowBetween>
<ThemedText.DeprecatedLabel>
<Trans>Set Price Range</Trans>
</ThemedText.DeprecatedLabel>
</RowBetween>
)}
<RangeSelector
priceLower={priceLower}
priceUpper={priceUpper}
getDecrementLower={getDecrementLower}
getIncrementLower={getIncrementLower}
getDecrementUpper={getDecrementUpper}
getIncrementUpper={getIncrementUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
currencyA={baseCurrency}
currencyB={quoteCurrency}
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
/>
<PresetsButtons onSetFullRange={handleSetFullRange} />
</AutoColumn>
</StackedItem>
</StackedContainer>
{outOfRange ? (
<YellowCard padding="8px 12px" $borderRadius="12px">
<RowBetween>
<AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
<ThemedText.DeprecatedYellow ml="12px" fontSize="12px">
<Trans>
Your position will not earn fees or be used in trades until the market price moves into
your range.
</Trans>
</ThemedText.DeprecatedYellow>
</RowBetween>
</YellowCard>
) : null}
{invalidRange ? (
<YellowCard padding="8px 12px" $borderRadius="12px">
<RowBetween>
<AlertTriangle stroke={theme.deprecated_yellow3} size="16px" />
<ThemedText.DeprecatedYellow ml="12px" fontSize="12px">
<Trans>Invalid range selected. The min price must be lower than the max price.</Trans>
</ThemedText.DeprecatedYellow>
</RowBetween>
</YellowCard>
) : null}
</DynamicSection>
<MediumOnly>
<Buttons />
</MediumOnly>
</RightContainer>
</>
) : (
<Buttons />
)}
<Buttons />
</ResponsiveTwoColumns>
</Wrapper>
</PageWrapper>
</StyledBodyWrapper>
{showOwnershipWarning && <OwnershipWarning ownerAddress={owner} />}
{addIsUnsupported && (
<UnsupportedCurrencyFooter

@ -1,42 +1,20 @@
import { AutoColumn } from 'components/Column'
import CurrencyInputPanel from 'components/CurrencyInputPanel'
import Input from 'components/NumericalInput'
import { BodyWrapper } from 'pages/AppBody'
import styled from 'styled-components/macro'
export const PageWrapper = styled(BodyWrapper)<{ wide: boolean }>`
max-width: ${({ wide }) => (wide ? '880px' : '480px')};
width: 100%;
padding: ${({ wide }) => (wide ? '10px' : '0')};
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
max-width: 480px;
`};
`
export const Wrapper = styled.div`
position: relative;
padding: 26px 16px;
min-width: 480px;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
min-width: 400px;
`};
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToExtraSmall`
min-width: 340px;
`};
`
export const ScrollablePage = styled.div`
padding: 68px 8px 0px;
padding: 20px 8px 0px;
position: relative;
display: flex;
flex-direction: column;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
max-width: 480px;
margin: 0 auto;
`};
@ -67,56 +45,19 @@ export const StyledInput = styled(Input)`
/* two-column layout where DepositAmount is moved at the very end on mobile. */
export const ResponsiveTwoColumns = styled.div<{ wide: boolean }>`
display: grid;
grid-column-gap: 50px;
grid-row-gap: 15px;
grid-template-columns: ${({ wide }) => (wide ? '1fr 1fr' : '1fr')};
grid-template-rows: max-content;
grid-auto-flow: row;
display: flex;
flex-direction: column;
gap: 20px;
padding-top: 20px;
border-top: 1px solid ${({ theme }) => theme.backgroundInteractive};
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
grid-template-columns: 1fr;
margin-top: 0;
`};
`
export const RightContainer = styled(AutoColumn)`
grid-row: 1 / 3;
grid-column: 2;
height: fit-content;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
grid-row: 2 / 3;
grid-column: 1;
`};
`
export const StackedContainer = styled.div`
display: grid;
`
export const StackedItem = styled.div<{ zIndex?: number }>`
grid-column: 1;
grid-row: 1;
height: 100%;
z-index: ${({ zIndex }) => zIndex};
`
export const MediumOnly = styled.div`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
display: none;
`};
`
export const HideMedium = styled.div`
display: none;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
display: block;
`};
`

@ -1,4 +1,4 @@
import React, { PropsWithChildren } from 'react'
import { PropsWithChildren } from 'react'
import styled from 'styled-components/macro'
import { Z_INDEX } from 'theme/zIndex'

@ -12,6 +12,8 @@ import JSBI from 'jsbi'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { Wrapper } from 'pages/Pool/styleds'
import { useCallback, useMemo, useState } from 'react'
import { ArrowLeft } from 'react-feather'
import { Link } from 'react-router-dom'
import {
CreateProposalData,
ProposalState,
@ -24,7 +26,6 @@ import {
import styled from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { CreateProposalTabs } from '../../components/NavigationTabs'
import { LATEST_GOVERNOR_INDEX } from '../../constants/governance'
import { UNI } from '../../constants/tokens'
import AppBody from '../AppBody'
@ -45,6 +46,19 @@ const PageWrapper = styled(AutoColumn)`
}
`
const BackArrow = styled(ArrowLeft)`
cursor: pointer;
color: ${({ theme }) => theme.textPrimary};
`
const Nav = styled(Link)`
align-items: center;
display: flex;
flex-direction: row;
justify-content: flex-start;
margin: 1em 0 0 1em;
text-decoration: none;
`
const CreateProposalButton = ({
proposalThreshold,
hasActiveOrPendingProposal,
@ -242,7 +256,10 @@ ${bodyValue}
<Trace page={InterfacePageName.VOTE_PAGE} shouldLogImpression>
<PageWrapper>
<AppBody $maxWidth="800px">
<CreateProposalTabs />
<Nav to="/vote">
<BackArrow />
<ThemedText.SubHeaderLarge>Create Proposal</ThemedText.SubHeaderLarge>
</Nav>
<CreateProposalWrapper>
<BlueCard>
<AutoColumn gap="10px">