Commit 504e09d3 authored by eddie's avatar eddie Committed by GitHub

feat: new review design (#6451)

* test: swap flow cypress tests

* fix: use default parameter

* feat: use Swap Component on TDP

* feat: auto nav for TDP tokens

* chore: merge

* chore: merge

* chore: merge

* chore: merge

* fix: remove extra inputCurrency URL parsing logic

* fix: undo last change

* fix: pass expected chain id to swap component

* fix: search for default tokens on unconnected networks if needed

* test: e2e test for l2 token

* fix: delete irrelevant tests

* fix: address comments

* fix: lint error

* test: update TDP e2e tests

* fix: use pageChainId for filter

* fix: rename chainId

* fix: typecheck

* fix: chainId bug

* fix: chainId required fixes

* fix: bad merge in e2e test

* fix: remove unused test util

* fix: remove unnecessary variable

* fix: token defaults

* fix: address comments

* fix: address comments and fix tests

* fix: e2e test formatting, remove Maybe<>

* fix: remove unused variable

* fix: use feature flag for swap component on TDP

* fix: back button

* feat: copy review screen UI from widgetg

* fix: modal padding

* feat: add final detail row

* fix: remove widget comment

* fix: update unit tests

* fix: code style consistency

* fix: remove padding from AutoColumn

* fix: update snapshots

* fix: use semantic gaps

* fix: more px and gaps

* fix: design feedbacks

* fix: button radius in summary modal

* fix: design nits

* feat: update design of summary modal

* fix: font weight and vertical spacing

* fix: update snapshots

* fix: css nits

* fix: modal flicker when refetching trade

* fix: comments

* fix: code style improvements

* feat: require trade to be defined

* fix: remove extra props from ThemedTexts

* fix: one more trans

* fix: remove unused export

* feat: remove undefined checks and other fixes

* fix: update test

* fix: add missing dollar sign

* fix: remove null check and update test

* fix: remove max width from detail row value

* fix: remove isOpen prop

* fix: isopen
parent 1f755e8b
...@@ -296,7 +296,7 @@ export function ButtonConfirmed({ ...@@ -296,7 +296,7 @@ export function ButtonConfirmed({
} }
} }
export function ButtonError({ error, ...rest }: { error?: boolean } & ButtonProps) { export function ButtonError({ error, ...rest }: { error?: boolean } & BaseButtonProps) {
if (error) { if (error) {
return <ButtonErrorStyle {...rest} /> return <ButtonErrorStyle {...rest} />
} else { } else {
......
import styled, { DefaultTheme } from 'styled-components/macro' import styled from 'styled-components/macro'
import { Gap } from 'theme'
type Gap = keyof DefaultTheme['grids']
export const Column = styled.div<{ export const Column = styled.div<{
gap?: Gap gap?: Gap
......
import { Box } from 'rebass/styled-components' import { Box } from 'rebass/styled-components'
import styled, { DefaultTheme } from 'styled-components/macro' import styled from 'styled-components/macro'
import { Gap } from 'theme'
type Gap = keyof DefaultTheme['grids']
// TODO(WEB-3289): // TODO(WEB-3289):
// Setting `width: 100%` by default prevents composability in complex flex layouts. // Setting `width: 100%` by default prevents composability in complex flex layouts.
...@@ -14,7 +13,7 @@ const Row = styled(Box)<{ ...@@ -14,7 +13,7 @@ const Row = styled(Box)<{
padding?: string padding?: string
border?: string border?: string
borderRadius?: string borderRadius?: string
gap?: string gap?: Gap | string
}>` }>`
width: ${({ width }) => width ?? '100%'}; width: ${({ width }) => width ?? '100%'};
display: flex; display: flex;
......
...@@ -9,6 +9,7 @@ import { useRef } from 'react' ...@@ -9,6 +9,7 @@ import { useRef } from 'react'
import { useModalIsOpen, useToggleSettingsMenu } from 'state/application/hooks' import { useModalIsOpen, useToggleSettingsMenu } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { Divider } from 'theme'
import MaxSlippageSettings from './MaxSlippageSettings' import MaxSlippageSettings from './MaxSlippageSettings'
import MenuButton from './MenuButton' import MenuButton from './MenuButton'
...@@ -40,14 +41,6 @@ const MenuFlyout = styled(AutoColumn)` ...@@ -40,14 +41,6 @@ const MenuFlyout = styled(AutoColumn)`
padding: 1rem; padding: 1rem;
` `
const Divider = styled.div`
width: 100%;
height: 1px;
border-width: 0;
margin: 0;
background-color: ${({ theme }) => theme.backgroundOutline};
`
export default function SettingsTab({ autoSlippage }: { autoSlippage: Percent }) { export default function SettingsTab({ autoSlippage }: { autoSlippage: Percent }) {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const showDeadlineSettings = Boolean(chainId && !L2_CHAIN_IDS.includes(chainId)) const showDeadlineSettings = Boolean(chainId && !L2_CHAIN_IDS.includes(chainId))
......
...@@ -20,7 +20,7 @@ import { TransactionSummary } from '../AccountDetails/TransactionSummary' ...@@ -20,7 +20,7 @@ import { TransactionSummary } from '../AccountDetails/TransactionSummary'
import { ButtonLight, ButtonPrimary } from '../Button' import { ButtonLight, ButtonPrimary } from '../Button'
import { AutoColumn, ColumnCenter } from '../Column' import { AutoColumn, ColumnCenter } from '../Column'
import Modal from '../Modal' import Modal from '../Modal'
import { RowBetween, RowFixed } from '../Row' import Row, { RowBetween, RowFixed } from '../Row'
import AnimatedConfirmation from './AnimatedConfirmation' import AnimatedConfirmation from './AnimatedConfirmation'
const Wrapper = styled.div` const Wrapper = styled.div`
...@@ -28,16 +28,12 @@ const Wrapper = styled.div` ...@@ -28,16 +28,12 @@ const Wrapper = styled.div`
border-radius: 20px; border-radius: 20px;
outline: 1px solid ${({ theme }) => theme.backgroundOutline}; outline: 1px solid ${({ theme }) => theme.backgroundOutline};
width: 100%; width: 100%;
padding: 1rem; padding: 16px;
`
const Section = styled(AutoColumn)<{ inline?: boolean }>`
padding: ${({ inline }) => (inline ? '0' : '0')};
` `
const BottomSection = styled(Section)` const BottomSection = styled(AutoColumn)`
border-bottom-left-radius: 20px; border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px; border-bottom-right-radius: 20px;
padding-bottom: 10px;
` `
const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>` const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>`
...@@ -50,6 +46,10 @@ const StyledLogo = styled.img` ...@@ -50,6 +46,10 @@ const StyledLogo = styled.img`
margin-left: 6px; margin-left: 6px;
` `
const ConfirmationModalContentWrapper = styled(AutoColumn)`
padding-bottom: 12px;
`
function ConfirmationPendingContent({ function ConfirmationPendingContent({
onDismiss, onDismiss,
pendingText, pendingText,
...@@ -59,8 +59,6 @@ function ConfirmationPendingContent({ ...@@ -59,8 +59,6 @@ function ConfirmationPendingContent({
pendingText: ReactNode pendingText: ReactNode
inline?: boolean // not in modal inline?: boolean // not in modal
}) { }) {
const theme = useTheme()
return ( return (
<Wrapper> <Wrapper>
<AutoColumn gap="md"> <AutoColumn gap="md">
...@@ -74,15 +72,15 @@ function ConfirmationPendingContent({ ...@@ -74,15 +72,15 @@ function ConfirmationPendingContent({
<CustomLightSpinner src={Circle} alt="loader" size={inline ? '40px' : '90px'} /> <CustomLightSpinner src={Circle} alt="loader" size={inline ? '40px' : '90px'} />
</ConfirmedIcon> </ConfirmedIcon>
<AutoColumn gap="md" justify="center"> <AutoColumn gap="md" justify="center">
<Text fontWeight={500} fontSize={20} color={theme.textPrimary} textAlign="center"> <ThemedText.SubHeaderLarge color="textPrimary" textAlign="center">
<Trans>Waiting for confirmation</Trans> <Trans>Waiting for confirmation</Trans>
</Text> </ThemedText.SubHeaderLarge>
<Text fontWeight={600} fontSize={16} color={theme.textPrimary} textAlign="center"> <ThemedText.SubHeader color="textPrimary" textAlign="center">
{pendingText} {pendingText}
</Text> </ThemedText.SubHeader>
<Text fontWeight={400} fontSize={12} color={theme.textSecondary} textAlign="center" marginBottom="12px"> <ThemedText.SubHeaderSmall color="textSecondary" textAlign="center" marginBottom="12px">
<Trans>Confirm this transaction in your wallet</Trans> <Trans>Confirm this transaction in your wallet</Trans>
</Text> </ThemedText.SubHeaderSmall>
</AutoColumn> </AutoColumn>
</AutoColumn> </AutoColumn>
</Wrapper> </Wrapper>
...@@ -125,7 +123,7 @@ function TransactionSubmittedContent({ ...@@ -125,7 +123,7 @@ function TransactionSubmittedContent({
return ( return (
<Wrapper> <Wrapper>
<Section inline={inline}> <AutoColumn>
{!inline && ( {!inline && (
<RowBetween> <RowBetween>
<div /> <div />
...@@ -135,7 +133,7 @@ function TransactionSubmittedContent({ ...@@ -135,7 +133,7 @@ function TransactionSubmittedContent({
<ConfirmedIcon inline={inline}> <ConfirmedIcon inline={inline}>
<ArrowUpCircle strokeWidth={1} size={inline ? '40px' : '75px'} color={theme.accentActive} /> <ArrowUpCircle strokeWidth={1} size={inline ? '40px' : '75px'} color={theme.accentActive} />
</ConfirmedIcon> </ConfirmedIcon>
<AutoColumn gap="md" justify="center" style={{ paddingBottom: '12px' }}> <ConfirmationModalContentWrapper gap="md" justify="center">
<ThemedText.MediumHeader textAlign="center"> <ThemedText.MediumHeader textAlign="center">
<Trans>Transaction submitted</Trans> <Trans>Transaction submitted</Trans>
</ThemedText.MediumHeader> </ThemedText.MediumHeader>
...@@ -154,19 +152,19 @@ function TransactionSubmittedContent({ ...@@ -154,19 +152,19 @@ function TransactionSubmittedContent({
</ButtonLight> </ButtonLight>
)} )}
<ButtonPrimary onClick={onDismiss} style={{ margin: '20px 0 0 0' }} data-testid="dismiss-tx-confirmation"> <ButtonPrimary onClick={onDismiss} style={{ margin: '20px 0 0 0' }} data-testid="dismiss-tx-confirmation">
<Text fontWeight={600} fontSize={20} color={theme.accentTextLightPrimary}> <ThemedText.HeadlineSmall color={theme.accentTextLightPrimary}>
{inline ? <Trans>Return</Trans> : <Trans>Close</Trans>} {inline ? <Trans>Return</Trans> : <Trans>Close</Trans>}
</Text> </ThemedText.HeadlineSmall>
</ButtonPrimary> </ButtonPrimary>
{chainId && hash && ( {chainId && hash && (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}> <ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
<Text fontWeight={600} fontSize={14} color={theme.accentAction}> <ThemedText.Link color={theme.accentAction}>
<Trans>View on {chainId === SupportedChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}</Trans> <Trans>View on {chainId === SupportedChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}</Trans>
</Text> </ThemedText.Link>
</ExternalLink> </ExternalLink>
)} )}
</AutoColumn> </ConfirmationModalContentWrapper>
</Section> </AutoColumn>
</Wrapper> </Wrapper>
) )
} }
...@@ -184,15 +182,15 @@ export function ConfirmationModalContent({ ...@@ -184,15 +182,15 @@ export function ConfirmationModalContent({
}) { }) {
return ( return (
<Wrapper> <Wrapper>
<Section> <AutoColumn gap="sm">
<RowBetween> <Row>
<Text fontWeight={500} fontSize={16}> <Row justify="center" marginLeft="24px">
{title} <ThemedText.SubHeader>{title}</ThemedText.SubHeader>
</Text> </Row>
<CloseIcon onClick={onDismiss} data-cy="confirmation-close-icon" /> <CloseIcon onClick={onDismiss} data-cy="confirmation-close-icon" />
</RowBetween> </Row>
{topContent()} {topContent()}
</Section> </AutoColumn>
{bottomContent && <BottomSection gap="12px">{bottomContent()}</BottomSection>} {bottomContent && <BottomSection gap="12px">{bottomContent()}</BottomSection>}
</Wrapper> </Wrapper>
) )
...@@ -202,7 +200,7 @@ export function TransactionErrorContent({ message, onDismiss }: { message: React ...@@ -202,7 +200,7 @@ export function TransactionErrorContent({ message, onDismiss }: { message: React
const theme = useTheme() const theme = useTheme()
return ( return (
<Wrapper> <Wrapper>
<Section> <AutoColumn>
<RowBetween> <RowBetween>
<Text fontWeight={600} fontSize={16}> <Text fontWeight={600} fontSize={16}>
<Trans>Error</Trans> <Trans>Error</Trans>
...@@ -213,7 +211,7 @@ export function TransactionErrorContent({ message, onDismiss }: { message: React ...@@ -213,7 +211,7 @@ export function TransactionErrorContent({ message, onDismiss }: { message: React
<AlertTriangle color={theme.accentCritical} style={{ strokeWidth: 1 }} size={90} /> <AlertTriangle color={theme.accentCritical} style={{ strokeWidth: 1 }} size={90} />
<ThemedText.MediumHeader textAlign="center">{message}</ThemedText.MediumHeader> <ThemedText.MediumHeader textAlign="center">{message}</ThemedText.MediumHeader>
</AutoColumn> </AutoColumn>
</Section> </AutoColumn>
<BottomSection gap="12px"> <BottomSection gap="12px">
<ButtonPrimary onClick={onDismiss}> <ButtonPrimary onClick={onDismiss}>
<Trans>Dismiss</Trans> <Trans>Dismiss</Trans>
...@@ -252,7 +250,7 @@ function L2Content({ ...@@ -252,7 +250,7 @@ function L2Content({
return ( return (
<Wrapper> <Wrapper>
<Section inline={inline}> <AutoColumn>
{!inline && ( {!inline && (
<RowBetween mb="16px"> <RowBetween mb="16px">
<Badge> <Badge>
...@@ -277,7 +275,7 @@ function L2Content({ ...@@ -277,7 +275,7 @@ function L2Content({
)} )}
</ConfirmedIcon> </ConfirmedIcon>
<AutoColumn gap="md" justify="center"> <AutoColumn gap="md" justify="center">
<Text fontWeight={500} fontSize={20} textAlign="center"> <ThemedText.SubHeaderLarge textAlign="center">
{!hash ? ( {!hash ? (
<Trans>Confirm transaction in wallet</Trans> <Trans>Confirm transaction in wallet</Trans>
) : !confirmed ? ( ) : !confirmed ? (
...@@ -287,20 +285,20 @@ function L2Content({ ...@@ -287,20 +285,20 @@ function L2Content({
) : ( ) : (
<Trans>Error</Trans> <Trans>Error</Trans>
)} )}
</Text> </ThemedText.SubHeaderLarge>
<Text fontWeight={400} fontSize={16} textAlign="center"> <ThemedText.BodySecondary textAlign="center">
{transaction ? <TransactionSummary info={transaction.info} /> : pendingText} {transaction ? <TransactionSummary info={transaction.info} /> : pendingText}
</Text> </ThemedText.BodySecondary>
{chainId && hash ? ( {chainId && hash ? (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}> <ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
<Text fontWeight={500} fontSize={14} color={theme.accentAction}> <ThemedText.SubHeaderSmall color={theme.accentAction}>
<Trans>View on Explorer</Trans> <Trans>View on Explorer</Trans>
</Text> </ThemedText.SubHeaderSmall>
</ExternalLink> </ExternalLink>
) : ( ) : (
<div style={{ height: '17px' }} /> <div style={{ height: '17px' }} />
)} )}
<Text color={theme.textTertiary} style={{ margin: '20px 0 0 0' }} fontSize="14px"> <ThemedText.SubHeaderSmall color={theme.textTertiary} marginTop="20px">
{!secondsToConfirm ? ( {!secondsToConfirm ? (
<div style={{ height: '24px' }} /> <div style={{ height: '24px' }} />
) : ( ) : (
...@@ -311,14 +309,14 @@ function L2Content({ ...@@ -311,14 +309,14 @@ function L2Content({
</span> </span>
</div> </div>
)} )}
</Text> </ThemedText.SubHeaderSmall>
<ButtonPrimary onClick={onDismiss} style={{ margin: '4px 0 0 0' }}> <ButtonPrimary onClick={onDismiss} style={{ margin: '4px 0 0 0' }}>
<Text fontWeight={500} fontSize={20}> <ThemedText.SubHeaderLarge>
{inline ? <Trans>Return</Trans> : <Trans>Close</Trans>} {inline ? <Trans>Return</Trans> : <Trans>Close</Trans>}
</Text> </ThemedText.SubHeaderLarge>
</ButtonPrimary> </ButtonPrimary>
</AutoColumn> </AutoColumn>
</Section> </AutoColumn>
</Wrapper> </Wrapper>
) )
} }
......
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics' import { sendAnalyticsEvent, Trace } from '@uniswap/analytics'
import { InterfaceModalName } from '@uniswap/analytics-events' import { InterfaceModalName, SwapEventName, SwapPriceUpdateUserResponse } from '@uniswap/analytics-events'
import { Percent } from '@uniswap/sdk-core' import { Percent } from '@uniswap/sdk-core'
import { ReactNode, useCallback, useMemo, useState } from 'react' import { getPriceUpdateBasisPoints } from 'lib/utils/analytics'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { InterfaceTrade } from 'state/routing/types' import { InterfaceTrade } from 'state/routing/types'
import { formatSwapPriceUpdatedEventProperties } from 'utils/loggingFormatters'
import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer' import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'
import TransactionConfirmationModal, { import TransactionConfirmationModal, {
...@@ -20,21 +22,17 @@ export default function ConfirmSwapModal({ ...@@ -20,21 +22,17 @@ export default function ConfirmSwapModal({
allowedSlippage, allowedSlippage,
onConfirm, onConfirm,
onDismiss, onDismiss,
recipient,
swapErrorMessage, swapErrorMessage,
isOpen,
attemptingTxn, attemptingTxn,
txHash, txHash,
swapQuoteReceivedDate, swapQuoteReceivedDate,
fiatValueInput, fiatValueInput,
fiatValueOutput, fiatValueOutput,
}: { }: {
isOpen: boolean trade: InterfaceTrade
trade: InterfaceTrade | undefined
originalTrade: InterfaceTrade | undefined originalTrade: InterfaceTrade | undefined
attemptingTxn: boolean attemptingTxn: boolean
txHash: string | undefined txHash: string | undefined
recipient: string | null
allowedSlippage: Percent allowedSlippage: Percent
onAcceptChanges: () => void onAcceptChanges: () => void
onConfirm: () => void onConfirm: () => void
...@@ -44,35 +42,34 @@ export default function ConfirmSwapModal({ ...@@ -44,35 +42,34 @@ export default function ConfirmSwapModal({
fiatValueInput: { data?: number; isLoading: boolean } fiatValueInput: { data?: number; isLoading: boolean }
fiatValueOutput: { data?: number; isLoading: boolean } fiatValueOutput: { data?: number; isLoading: boolean }
}) { }) {
// shouldLogModalCloseEvent lets the child SwapModalHeader component know when modal has been closed
// and an event triggered by modal closing should be logged.
const [shouldLogModalCloseEvent, setShouldLogModalCloseEvent] = useState(false)
const showAcceptChanges = useMemo( const showAcceptChanges = useMemo(
() => Boolean(trade && originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)), () => Boolean(originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)),
[originalTrade, trade] [originalTrade, trade]
) )
const [lastExecutionPrice, setLastExecutionPrice] = useState(trade?.executionPrice)
const [priceUpdate, setPriceUpdate] = useState<number>()
useEffect(() => {
if (lastExecutionPrice && !trade.executionPrice.equalTo(lastExecutionPrice)) {
setPriceUpdate(getPriceUpdateBasisPoints(lastExecutionPrice, trade.executionPrice))
setLastExecutionPrice(trade.executionPrice)
}
}, [lastExecutionPrice, setLastExecutionPrice, trade])
const onModalDismiss = useCallback(() => { const onModalDismiss = useCallback(() => {
if (isOpen) setShouldLogModalCloseEvent(true) sendAnalyticsEvent(
SwapEventName.SWAP_PRICE_UPDATE_ACKNOWLEDGED,
formatSwapPriceUpdatedEventProperties(trade, priceUpdate, SwapPriceUpdateUserResponse.REJECTED)
)
onDismiss() onDismiss()
}, [isOpen, onDismiss]) }, [onDismiss, priceUpdate, trade])
const modalHeader = useCallback(() => { const modalHeader = useCallback(() => {
return trade ? ( return <SwapModalHeader trade={trade} allowedSlippage={allowedSlippage} />
<SwapModalHeader }, [allowedSlippage, trade])
trade={trade}
shouldLogModalCloseEvent={shouldLogModalCloseEvent}
setShouldLogModalCloseEvent={setShouldLogModalCloseEvent}
allowedSlippage={allowedSlippage}
recipient={recipient}
showAcceptChanges={showAcceptChanges}
onAcceptChanges={onAcceptChanges}
/>
) : null
}, [allowedSlippage, onAcceptChanges, recipient, showAcceptChanges, trade, shouldLogModalCloseEvent])
const modalBottom = useCallback(() => { const modalBottom = useCallback(() => {
return trade ? ( return (
<SwapModalFooter <SwapModalFooter
onConfirm={onConfirm} onConfirm={onConfirm}
trade={trade} trade={trade}
...@@ -83,25 +80,28 @@ export default function ConfirmSwapModal({ ...@@ -83,25 +80,28 @@ export default function ConfirmSwapModal({
swapQuoteReceivedDate={swapQuoteReceivedDate} swapQuoteReceivedDate={swapQuoteReceivedDate}
fiatValueInput={fiatValueInput} fiatValueInput={fiatValueInput}
fiatValueOutput={fiatValueOutput} fiatValueOutput={fiatValueOutput}
showAcceptChanges={showAcceptChanges}
onAcceptChanges={onAcceptChanges}
/> />
) : null )
}, [ }, [
trade,
onConfirm, onConfirm,
txHash,
allowedSlippage,
showAcceptChanges, showAcceptChanges,
swapErrorMessage, swapErrorMessage,
trade,
allowedSlippage,
txHash,
swapQuoteReceivedDate, swapQuoteReceivedDate,
fiatValueInput, fiatValueInput,
fiatValueOutput, fiatValueOutput,
onAcceptChanges,
]) ])
// text to show while loading // text to show while loading
const pendingText = ( const pendingText = (
<Trans> <Trans>
Swapping {trade?.inputAmount?.toSignificant(6)} {trade?.inputAmount?.currency?.symbol} for{' '} Swapping {trade.inputAmount.toSignificant(6)} {trade.inputAmount.currency?.symbol} for{' '}
{trade?.outputAmount?.toSignificant(6)} {trade?.outputAmount?.currency?.symbol} {trade.outputAmount.toSignificant(6)} {trade.outputAmount.currency?.symbol}
</Trans> </Trans>
) )
...@@ -111,7 +111,7 @@ export default function ConfirmSwapModal({ ...@@ -111,7 +111,7 @@ export default function ConfirmSwapModal({
<TransactionErrorContent onDismiss={onModalDismiss} message={swapErrorMessage} /> <TransactionErrorContent onDismiss={onModalDismiss} message={swapErrorMessage} />
) : ( ) : (
<ConfirmationModalContent <ConfirmationModalContent
title={<Trans>Confirm Swap</Trans>} title={<Trans>Review Swap</Trans>}
onDismiss={onModalDismiss} onDismiss={onModalDismiss}
topContent={modalHeader} topContent={modalHeader}
bottomContent={modalBottom} bottomContent={modalBottom}
...@@ -123,13 +123,13 @@ export default function ConfirmSwapModal({ ...@@ -123,13 +123,13 @@ export default function ConfirmSwapModal({
return ( return (
<Trace modal={InterfaceModalName.CONFIRM_SWAP}> <Trace modal={InterfaceModalName.CONFIRM_SWAP}>
<TransactionConfirmationModal <TransactionConfirmationModal
isOpen={isOpen} isOpen
onDismiss={onModalDismiss} onDismiss={onModalDismiss}
attemptingTxn={attemptingTxn} attemptingTxn={attemptingTxn}
hash={txHash} hash={txHash}
content={confirmationContent} content={confirmationContent}
pendingText={pendingText} pendingText={pendingText}
currencyToAdd={trade?.outputAmount.currency} currencyToAdd={trade.outputAmount.currency}
/> />
</Trace> </Trace>
) )
......
import { TEST_ALLOWED_SLIPPAGE, TEST_TRADE_EXACT_INPUT } from 'test-utils/constants' import { TEST_ALLOWED_SLIPPAGE, TEST_TRADE_EXACT_INPUT, TEST_TRADE_EXACT_OUTPUT } from 'test-utils/constants'
import { render, screen } from 'test-utils/render' import { render, screen, within } from 'test-utils/render'
import SwapModalFooter from './SwapModalFooter' import SwapModalFooter from './SwapModalFooter'
const swapErrorMessage = 'swap error'
const fiatValue = { data: 123, isLoading: false }
describe('SwapModalFooter.tsx', () => { describe('SwapModalFooter.tsx', () => {
it('renders with a disabled button with no account', () => { it('matches base snapshot, test trade exact input', () => {
const { asFragment } = render( const { asFragment } = render(
<SwapModalFooter <SwapModalFooter
trade={TEST_TRADE_EXACT_INPUT} trade={TEST_TRADE_EXACT_INPUT}
allowedSlippage={TEST_ALLOWED_SLIPPAGE} allowedSlippage={TEST_ALLOWED_SLIPPAGE}
hash={undefined} hash={undefined}
onConfirm={() => null} onConfirm={jest.fn()}
disabledConfirm swapErrorMessage={undefined}
swapErrorMessage={swapErrorMessage} disabledConfirm={false}
swapQuoteReceivedDate={undefined} swapQuoteReceivedDate={undefined}
fiatValueInput={fiatValue} fiatValueInput={{
fiatValueOutput={fiatValue} data: undefined,
isLoading: false,
}}
fiatValueOutput={{
data: undefined,
isLoading: false,
}}
showAcceptChanges={false}
onAcceptChanges={jest.fn()}
/> />
) )
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
expect(screen.getByTestId('confirm-swap-button')).toBeDisabled()
expect(
screen.getByText(
'The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert.'
)
).toBeInTheDocument()
expect(
screen.getByText('The fee paid to miners who process your transaction. This must be paid in $ETH.')
).toBeInTheDocument()
expect(screen.getByText('The impact your trade has on the market price of this pool.')).toBeInTheDocument()
})
it('shows accept changes section when available', () => {
const mockAcceptChanges = jest.fn()
render(
<SwapModalFooter
trade={TEST_TRADE_EXACT_INPUT}
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
hash={undefined}
onConfirm={jest.fn()}
swapErrorMessage={undefined}
disabledConfirm={false}
swapQuoteReceivedDate={undefined}
fiatValueInput={{
data: undefined,
isLoading: false,
}}
fiatValueOutput={{
data: undefined,
isLoading: false,
}}
showAcceptChanges={true}
onAcceptChanges={mockAcceptChanges}
/>
)
const showAcceptChanges = screen.getByTestId('show-accept-changes')
expect(showAcceptChanges).toBeInTheDocument()
expect(within(showAcceptChanges).getByText('Price updated')).toBeVisible()
expect(within(showAcceptChanges).getByText('Accept')).toBeVisible()
})
it('test trade exact output, no recipient', () => {
render(
<SwapModalFooter
trade={TEST_TRADE_EXACT_OUTPUT}
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
hash={undefined}
onConfirm={jest.fn()}
swapErrorMessage={undefined}
disabledConfirm={false}
swapQuoteReceivedDate={undefined}
fiatValueInput={{
data: undefined,
isLoading: false,
}}
fiatValueOutput={{
data: undefined,
isLoading: false,
}}
showAcceptChanges={true}
onAcceptChanges={jest.fn()}
/>
)
expect(
screen.getByText(
'The maximum amount you are guaranteed to spend. If the price slips any further, your transaction will revert.'
)
).toBeInTheDocument()
expect(
screen.getByText('The fee paid to miners who process your transaction. This must be paid in $ETH.')
).toBeInTheDocument()
expect(screen.getByText('The impact your trade has on the market price of this pool.')).toBeInTheDocument()
}) })
}) })
This diff is collapsed.
import { sendAnalyticsEvent } from '@uniswap/analytics' import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { import { TEST_ALLOWED_SLIPPAGE, TEST_TRADE_EXACT_INPUT, TEST_TRADE_EXACT_OUTPUT } from 'test-utils/constants'
TEST_ALLOWED_SLIPPAGE, import { render, screen } from 'test-utils/render'
TEST_RECIPIENT_ADDRESS,
TEST_TRADE_EXACT_INPUT,
TEST_TRADE_EXACT_OUTPUT,
} from 'test-utils/constants'
import { render, screen, within } from 'test-utils/render'
import noop from 'utils/noop'
import SwapModalHeader from './SwapModalHeader' import SwapModalHeader from './SwapModalHeader'
jest.mock('@uniswap/analytics')
const mockSendAnalyticsEvent = sendAnalyticsEvent as jest.MockedFunction<typeof sendAnalyticsEvent>
describe('SwapModalHeader.tsx', () => { describe('SwapModalHeader.tsx', () => {
let sendAnalyticsEventMock: jest.Mock<any, any> it('matches base snapshot, test trade exact input', () => {
beforeAll(() => {
sendAnalyticsEventMock = jest.fn()
})
it('matches base snapshot for test trade with exact input', () => {
const { asFragment } = render( const { asFragment } = render(
<SwapModalHeader <SwapModalHeader trade={TEST_TRADE_EXACT_INPUT} allowedSlippage={TEST_ALLOWED_SLIPPAGE} />
trade={TEST_TRADE_EXACT_INPUT}
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
shouldLogModalCloseEvent={false}
showAcceptChanges={false}
setShouldLogModalCloseEvent={noop}
onAcceptChanges={noop}
recipient={TEST_RECIPIENT_ADDRESS}
/>
) )
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
}) expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument()
expect(screen.getByTestId('INPUT-amount')).toHaveTextContent(
it('shows accept changes section and logs amplitude event', () => { `${formatCurrencyAmount(TEST_TRADE_EXACT_INPUT.inputAmount, NumberType.TokenTx)} ${
const setShouldLogModalCloseEventFn = jest.fn() TEST_TRADE_EXACT_INPUT.inputAmount.currency.symbol ?? ''
mockSendAnalyticsEvent.mockImplementation(sendAnalyticsEventMock) }`
render( )
<SwapModalHeader expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(
trade={TEST_TRADE_EXACT_INPUT} `${formatCurrencyAmount(TEST_TRADE_EXACT_INPUT.outputAmount, NumberType.TokenTx)} ${
allowedSlippage={TEST_ALLOWED_SLIPPAGE} TEST_TRADE_EXACT_INPUT.outputAmount.currency.symbol ?? ''
shouldLogModalCloseEvent }`
showAcceptChanges
setShouldLogModalCloseEvent={setShouldLogModalCloseEventFn}
onAcceptChanges={noop}
recipient={TEST_RECIPIENT_ADDRESS}
/>
) )
expect(setShouldLogModalCloseEventFn).toHaveBeenCalledWith(false)
const showAcceptChanges = screen.getByTestId('show-accept-changes')
expect(showAcceptChanges).toBeInTheDocument()
expect(within(showAcceptChanges).getByText('Price Updated')).toBeVisible()
expect(within(showAcceptChanges).getByText('Accept')).toBeVisible()
expect(sendAnalyticsEventMock).toHaveBeenCalledTimes(1)
}) })
it('renders correctly for test trade with exact output and no recipient', () => { it('test trade exact output, no recipient', () => {
const rendered = render( const { asFragment } = render(
<SwapModalHeader <SwapModalHeader trade={TEST_TRADE_EXACT_OUTPUT} allowedSlippage={TEST_ALLOWED_SLIPPAGE} />
trade={TEST_TRADE_EXACT_OUTPUT}
allowedSlippage={TEST_ALLOWED_SLIPPAGE}
shouldLogModalCloseEvent={false}
showAcceptChanges={false}
setShouldLogModalCloseEvent={noop}
onAcceptChanges={noop}
recipient={null}
/>
) )
expect(rendered.queryByTestId('recipient-info')).toBeNull() expect(asFragment()).toMatchSnapshot()
expect(screen.getByText(/Input is estimated. You will sell at most/i)).toBeInTheDocument() expect(screen.getByText(/Input is estimated. You will sell at most/i)).toBeInTheDocument()
expect(screen.getByTestId('input-symbol')).toHaveTextContent(
TEST_TRADE_EXACT_OUTPUT.inputAmount.currency.symbol ?? '' expect(screen.getByTestId('INPUT-amount')).toHaveTextContent(
`${formatCurrencyAmount(TEST_TRADE_EXACT_OUTPUT.inputAmount, NumberType.TokenTx)} ${
TEST_TRADE_EXACT_OUTPUT.inputAmount.currency.symbol ?? ''
}`
) )
expect(screen.getByTestId('output-symbol')).toHaveTextContent( expect(screen.getByTestId('OUTPUT-amount')).toHaveTextContent(
TEST_TRADE_EXACT_OUTPUT.outputAmount.currency.symbol ?? '' `${formatCurrencyAmount(TEST_TRADE_EXACT_OUTPUT.outputAmount, NumberType.TokenTx)} ${
TEST_TRADE_EXACT_OUTPUT.outputAmount.currency.symbol ?? ''
}`
) )
expect(screen.getByTestId('input-amount')).toHaveTextContent(TEST_TRADE_EXACT_OUTPUT.inputAmount.toExact())
expect(screen.getByTestId('output-amount')).toHaveTextContent(TEST_TRADE_EXACT_OUTPUT.outputAmount.toExact())
}) })
}) })
This diff is collapsed.
import { formatCurrencyAmount, formatNumber, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import Column from 'components/Column'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import Row from 'components/Row'
import { MouseoverTooltip } from 'components/Tooltip'
import { useWindowSize } from 'hooks/useWindowSize'
import { PropsWithChildren, ReactNode } from 'react'
import { TextProps } from 'rebass'
import { Field } from 'state/swap/actions'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme'
const MAX_AMOUNT_STR_LENGTH = 9
export const Label = styled(ThemedText.BodySmall)<{ cursor?: string }>`
cursor: ${({ cursor }) => cursor};
color: ${({ theme }) => theme.textSecondary};
margin-right: 8px;
`
const ResponsiveHeadline = ({ children, ...textProps }: PropsWithChildren<TextProps>) => {
const { width } = useWindowSize()
if (width && width < BREAKPOINTS.xs) {
return <ThemedText.HeadlineMedium {...textProps}>{children}</ThemedText.HeadlineMedium>
}
return <ThemedText.HeadlineLarge {...textProps}>{children}</ThemedText.HeadlineLarge>
}
interface AmountProps {
field: Field
tooltipText?: ReactNode
label: ReactNode
amount: CurrencyAmount<Currency> | undefined
usdAmount?: number
}
export function SwapModalHeaderAmount({ tooltipText, label, amount, usdAmount, field }: AmountProps) {
let formattedAmount = formatCurrencyAmount(amount, NumberType.TokenTx)
if (formattedAmount.length > MAX_AMOUNT_STR_LENGTH) {
formattedAmount = formatCurrencyAmount(amount, NumberType.SwapTradeAmount)
}
return (
<Row align="center" justify="space-between" gap="md">
<Column gap="xs">
<ThemedText.BodySecondary>
<MouseoverTooltip text={tooltipText} disabled={!tooltipText}>
<Label cursor="help">{label}</Label>
</MouseoverTooltip>
</ThemedText.BodySecondary>
<Column gap="xs">
<ResponsiveHeadline data-testid={`${field}-amount`}>
{formattedAmount} {amount?.currency.symbol}
</ResponsiveHeadline>
{usdAmount && (
<ThemedText.BodySmall color="textTertiary">
{formatNumber(usdAmount, NumberType.FiatTokenQuantity)}
</ThemedText.BodySmall>
)}
</Column>
</Column>
{amount?.currency && <CurrencyLogo currency={amount.currency} size="36px" />}
</Row>
)
}
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SwapModalFooter.tsx renders with a disabled button with no account 1`] = ` exports[`SwapModalFooter.tsx matches base snapshot, test trade exact input 1`] = `
<DocumentFragment> <DocumentFragment>
.c3 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}
.c4 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0;
-webkit-align-items: flex-start;
-webkit-box-align: flex-start;
-ms-flex-align: flex-start;
align-items: flex-start;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
gap: 8px;
}
.c2 {
color: #0D111C;
}
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 12px;
}
.c7 {
display: inline-block;
height: inherit;
}
.c5 {
color: #7780A0;
margin-right: 8px;
}
.c8 {
cursor: help;
color: #7780A0;
margin-right: 8px;
}
.c1 {
padding: 0 8px;
}
.c6 {
text-align: right;
overflow-wrap: break-word;
}
<div
class="c0 c1"
>
<div
class="c2 css-zhpkf8"
>
<div
class="c3 c4"
>
<div
class="c2 c5 css-zhpkf8"
>
Exchange rate
</div>
<div
class="c2 c6 css-zhpkf8"
>
1 DEF = 1.00 ABC
</div>
</div>
</div>
<div
class="c2 css-zhpkf8"
>
<div
class="c3 c4"
>
<div
class="c7"
>
<div>
<div
class="c2 c8 css-zhpkf8"
cursor="help"
>
Network fee
</div>
</div>
</div>
<div
class="c2 c6 css-zhpkf8"
>
~$1.00
</div>
</div>
</div>
<div
class="c2 css-zhpkf8"
>
<div
class="c3 c4"
>
<div
class="c7"
>
<div>
<div
class="c2 c8 css-zhpkf8"
cursor="help"
>
Price impact
</div>
</div>
</div>
<div
class="c6 css-zhpkf8"
>
105566.373%
</div>
</div>
</div>
<div
class="c2 css-zhpkf8"
>
<div
class="c3 c4"
>
<div
class="c7"
>
<div>
<div
class="c2 c8 css-zhpkf8"
cursor="help"
>
Minimum received
</div>
</div>
</div>
<div
class="c2 c6 css-zhpkf8"
>
0.00000000000000098 DEF
</div>
</div>
</div>
</div>
.c0 { .c0 {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
...@@ -58,6 +223,10 @@ exports[`SwapModalFooter.tsx renders with a disabled button with no account 1`] ...@@ -58,6 +223,10 @@ exports[`SwapModalFooter.tsx renders with a disabled button with no account 1`]
margin: !important; margin: !important;
} }
.c7 {
color: #F5F6FC;
}
.c4 { .c4 {
padding: 16px; padding: 16px;
width: 100%; width: 100%;
...@@ -146,106 +315,24 @@ exports[`SwapModalFooter.tsx renders with a disabled button with no account 1`] ...@@ -146,106 +315,24 @@ exports[`SwapModalFooter.tsx renders with a disabled button with no account 1`]
} }
.c6 { .c6 {
background-color: rgba(250,43,57,0.1); height: 56px;
border-radius: 1rem; margin-top: 10px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 0.825rem;
width: 100%;
padding: 3rem 1.25rem 1rem 1rem;
margin-top: -2rem;
color: #FA2B39;
z-index: -1;
}
.c6 p {
padding: 0;
margin: 0;
font-weight: 500;
}
.c7 {
background-color: rgba(250,43,57,0.1);
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
margin-right: 12px;
border-radius: 12px;
min-width: 48px;
height: 48px;
} }
<div <div
class="c0 c1 c2" class="c0 c1 c2"
> >
<button <button
class="c3 c4 c5" class="c3 c4 c5 c6"
data-testid="confirm-swap-button" data-testid="confirm-swap-button"
disabled=""
id="confirm-swap-or-send" id="confirm-swap-or-send"
style="margin: 10px 0px 0px 0px;"
> >
<div <div
class="css-10ob8xa" class="c7 css-iapcxi"
> >
Confirm Swap Swap
</div> </div>
</button> </button>
<div
class="c6"
>
<div
class="c7"
>
<svg
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
</div>
<p
style="word-break: break-word;"
>
swap error
</p>
</div>
</div> </div>
</DocumentFragment> </DocumentFragment>
`; `;
...@@ -2,7 +2,6 @@ import { SupportedChainId } from 'constants/chains' ...@@ -2,7 +2,6 @@ import { SupportedChainId } from 'constants/chains'
import { transparentize } from 'polished' import { transparentize } from 'polished'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { AlertTriangle } from 'react-feather' import { AlertTriangle } from 'react-feather'
import { Text } from 'rebass'
import styled, { css } from 'styled-components/macro' import styled, { css } from 'styled-components/macro'
import { Z_INDEX } from 'theme/zIndex' import { Z_INDEX } from 'theme/zIndex'
...@@ -64,13 +63,6 @@ export const ArrowWrapper = styled.div<{ clickable: boolean }>` ...@@ -64,13 +63,6 @@ export const ArrowWrapper = styled.div<{ clickable: boolean }>`
: null} : null}
` `
export const TruncatedText = styled(Text)`
text-overflow: ellipsis;
max-width: 220px;
overflow: hidden;
text-align: right;
`
// styles // styles
export const Dots = styled.span` export const Dots = styled.span`
&::after { &::after {
...@@ -136,7 +128,7 @@ export function SwapCallbackError({ error }: { error: ReactNode }) { ...@@ -136,7 +128,7 @@ export function SwapCallbackError({ error }: { error: ReactNode }) {
export const SwapShowAcceptChanges = styled(AutoColumn)` export const SwapShowAcceptChanges = styled(AutoColumn)`
background-color: ${({ theme }) => transparentize(0.95, theme.deprecated_primary3)}; background-color: ${({ theme }) => transparentize(0.95, theme.deprecated_primary3)};
color: ${({ theme }) => theme.accentAction}; color: ${({ theme }) => theme.accentAction};
padding: 0.5rem; padding: 12px;
border-radius: 12px; border-radius: 12px;
margin-top: 8px; margin-top: 8px;
` `
...@@ -46,7 +46,7 @@ import useIsArgentWallet from '../../hooks/useIsArgentWallet' ...@@ -46,7 +46,7 @@ import useIsArgentWallet from '../../hooks/useIsArgentWallet'
import { useTotalSupply } from '../../hooks/useTotalSupply' import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useTokenBalance } from '../../state/connection/hooks' import { useTokenBalance } from '../../state/connection/hooks'
import { TransactionType } from '../../state/transactions/types' import { TransactionType } from '../../state/transactions/types'
import { BackArrow, ExternalLink, ThemedText } from '../../theme' import { BackArrowLink, ExternalLink, ThemedText } from '../../theme'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { calculateGasMargin } from '../../utils/calculateGasMargin' import { calculateGasMargin } from '../../utils/calculateGasMargin'
import { currencyId } from '../../utils/currencyId' import { currencyId } from '../../utils/currencyId'
...@@ -725,7 +725,7 @@ export default function MigrateV2Pair() { ...@@ -725,7 +725,7 @@ export default function MigrateV2Pair() {
<BodyWrapper style={{ padding: 24 }}> <BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px"> <AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px"> <AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/migrate/v2" /> <BackArrowLink to="/migrate/v2" />
<ThemedText.DeprecatedMediumHeader> <ThemedText.DeprecatedMediumHeader>
<Trans>Migrate V2 Liquidity</Trans> <Trans>Migrate V2 Liquidity</Trans>
</ThemedText.DeprecatedMediumHeader> </ThemedText.DeprecatedMediumHeader>
......
...@@ -20,7 +20,7 @@ import { Dots } from '../../components/swap/styleds' ...@@ -20,7 +20,7 @@ import { Dots } from '../../components/swap/styleds'
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses' import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
import { useTokenBalancesWithLoadingIndicator } from '../../state/connection/hooks' import { useTokenBalancesWithLoadingIndicator } from '../../state/connection/hooks'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks' import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
import { BackArrow, StyledInternalLink, ThemedText } from '../../theme' import { BackArrowLink, StyledInternalLink, ThemedText } from '../../theme'
import { BodyWrapper } from '../AppBody' import { BodyWrapper } from '../AppBody'
function EmptyState({ message }: { message: ReactNode }) { function EmptyState({ message }: { message: ReactNode }) {
...@@ -116,7 +116,7 @@ export default function MigrateV2() { ...@@ -116,7 +116,7 @@ export default function MigrateV2() {
<BodyWrapper style={{ padding: 24 }}> <BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px"> <AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px"> <AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/pools" /> <BackArrowLink to="/pools" />
<ThemedText.DeprecatedMediumHeader> <ThemedText.DeprecatedMediumHeader>
<Trans>Migrate V2 Liquidity</Trans> <Trans>Migrate V2 Liquidity</Trans>
</ThemedText.DeprecatedMediumHeader> </ThemedText.DeprecatedMediumHeader>
......
...@@ -571,22 +571,22 @@ export function Swap({ ...@@ -571,22 +571,22 @@ export function Swap({
showCancel={true} showCancel={true}
/> />
<SwapHeader autoSlippage={autoSlippage} /> <SwapHeader autoSlippage={autoSlippage} />
<ConfirmSwapModal {trade && showConfirm && (
isOpen={showConfirm} <ConfirmSwapModal
trade={trade} trade={trade}
originalTrade={tradeToConfirm} originalTrade={tradeToConfirm}
onAcceptChanges={handleAcceptChanges} onAcceptChanges={handleAcceptChanges}
attemptingTxn={attemptingTxn} attemptingTxn={attemptingTxn}
txHash={txHash} txHash={txHash}
recipient={recipient} allowedSlippage={allowedSlippage}
allowedSlippage={allowedSlippage} onConfirm={handleSwap}
onConfirm={handleSwap} swapErrorMessage={swapErrorMessage}
swapErrorMessage={swapErrorMessage} onDismiss={handleConfirmDismiss}
onDismiss={handleConfirmDismiss} swapQuoteReceivedDate={swapQuoteReceivedDate}
swapQuoteReceivedDate={swapQuoteReceivedDate} fiatValueInput={fiatValueTradeInput}
fiatValueInput={fiatValueTradeInput} fiatValueOutput={fiatValueTradeOutput}
fiatValueOutput={fiatValueTradeOutput} />
/> )}
<div style={{ display: 'relative' }}> <div style={{ display: 'relative' }}>
<SwapSection> <SwapSection>
......
...@@ -467,14 +467,15 @@ export const SpinnerSVG = styled.svg` ...@@ -467,14 +467,15 @@ export const SpinnerSVG = styled.svg`
${SpinnerCss} ${SpinnerCss}
` `
const BackArrowLink = styled(StyledInternalLink)` const BackArrowIcon = styled(ArrowLeft)`
color: ${({ theme }) => theme.textPrimary}; color: ${({ theme }) => theme.textPrimary};
` `
export function BackArrow({ to }: { to: string }) {
export function BackArrowLink({ to }: { to: string }) {
return ( return (
<BackArrowLink to={to}> <StyledInternalLink to={to}>
<ArrowLeft /> <BackArrowIcon />
</BackArrowLink> </StyledInternalLink>
) )
} }
...@@ -523,3 +524,11 @@ export const GlowEffect = styled.div` ...@@ -523,3 +524,11 @@ export const GlowEffect = styled.div`
export const CautionTriangle = styled(AlertTriangle)` export const CautionTriangle = styled(AlertTriangle)`
color: ${({ theme }) => theme.accentWarning}; color: ${({ theme }) => theme.accentWarning};
` `
export const Divider = styled.div`
width: 100%;
height: 1px;
border-width: 0;
margin: 0;
background-color: ${({ theme }) => theme.backgroundOutline};
`
...@@ -52,6 +52,9 @@ export const ThemedText = { ...@@ -52,6 +52,9 @@ export const ThemedText = {
MediumHeader(props: TextProps) { MediumHeader(props: TextProps) {
return <TextWrapper fontWeight={400} fontSize={20} color="textPrimary" {...props} /> return <TextWrapper fontWeight={400} fontSize={20} color="textPrimary" {...props} />
}, },
SubHeaderLarge(props: TextProps) {
return <TextWrapper fontWeight={500} fontSize={20} color="textPrimary" {...props} />
},
SubHeader(props: TextProps) { SubHeader(props: TextProps) {
return <TextWrapper fontWeight={500} fontSize={16} color="textPrimary" lineHeight="24px" {...props} /> return <TextWrapper fontWeight={500} fontSize={16} color="textPrimary" lineHeight="24px" {...props} />
}, },
......
...@@ -67,15 +67,18 @@ const fonts = { ...@@ -67,15 +67,18 @@ const fonts = {
code: 'courier, courier new, serif', code: 'courier, courier new, serif',
} }
const gapValues = {
xs: '4px',
sm: '8px',
md: '12px',
lg: '24px',
xl: '32px',
}
export type Gap = keyof typeof gapValues
function getSettings(darkMode: boolean) { function getSettings(darkMode: boolean) {
return { return {
grids: { grids: gapValues,
xs: '4px',
sm: '8px',
md: '12px',
lg: '24px',
xl: '32px',
},
fonts, fonts,
// shadows // shadows
...@@ -118,7 +121,7 @@ export const ThemedGlobalStyle = createGlobalStyle` ...@@ -118,7 +121,7 @@ export const ThemedGlobalStyle = createGlobalStyle`
background-color: ${({ theme }) => theme.background} !important; background-color: ${({ theme }) => theme.background} !important;
} }
summary::-webkit-details-marker { summary::-webkit-details-marker {
display:none; display:none;
} }
......
import { SwapPriceUpdateUserResponse } from '@uniswap/analytics-events'
import { Percent } from '@uniswap/sdk-core'
import {
formatPercentInBasisPointsNumber,
formatPercentNumber,
formatToDecimal,
getDurationFromDateMilliseconds,
getDurationUntilTimestampSeconds,
getTokenAddress,
} from 'lib/utils/analytics'
import { InterfaceTrade } from 'state/routing/types'
import { RoutingDiagramEntry } from './getRoutingDiagramEntries'
import { computeRealizedPriceImpact } from './prices'
const formatRoutesEventProperties = (routes: RoutingDiagramEntry[]) => {
const routesEventProperties: Record<string, any[]> = {
routes_percentages: [],
routes_protocols: [],
}
routes.forEach((route, index) => {
routesEventProperties['routes_percentages'].push(formatPercentNumber(route.percent))
routesEventProperties['routes_protocols'].push(route.protocol)
routesEventProperties[`route_${index}_input_currency_symbols`] = route.path.map(
(pathStep) => pathStep[0].symbol ?? ''
)
routesEventProperties[`route_${index}_output_currency_symbols`] = route.path.map(
(pathStep) => pathStep[1].symbol ?? ''
)
routesEventProperties[`route_${index}_input_currency_addresses`] = route.path.map((pathStep) =>
getTokenAddress(pathStep[0])
)
routesEventProperties[`route_${index}_output_currency_addresses`] = route.path.map((pathStep) =>
getTokenAddress(pathStep[1])
)
routesEventProperties[`route_${index}_fee_amounts_hundredths_of_bps`] = route.path.map((pathStep) => pathStep[2])
})
return routesEventProperties
}
export const formatSwapPriceUpdatedEventProperties = (
trade: InterfaceTrade,
priceUpdate: number | undefined,
response: SwapPriceUpdateUserResponse
) => ({
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
? trade.inputAmount.currency.chainId
: undefined,
response,
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
price_update_basis_points: priceUpdate,
})
interface AnalyticsEventProps {
trade: InterfaceTrade
hash: string | undefined
allowedSlippage: Percent
transactionDeadlineSecondsSinceEpoch: number | undefined
isAutoSlippage: boolean
isAutoRouterApi: boolean
swapQuoteReceivedDate: Date | undefined
routes: RoutingDiagramEntry[]
fiatValueInput?: number
fiatValueOutput?: number
}
export const formatSwapButtonClickEventProperties = ({
trade,
hash,
allowedSlippage,
transactionDeadlineSecondsSinceEpoch,
isAutoSlippage,
isAutoRouterApi,
swapQuoteReceivedDate,
routes,
fiatValueInput,
fiatValueOutput,
}: AnalyticsEventProps) => ({
estimated_network_fee_usd: trade.gasUseEstimateUSD,
transaction_hash: hash,
transaction_deadline_seconds: getDurationUntilTimestampSeconds(transactionDeadlineSecondsSinceEpoch),
token_in_address: trade ? getTokenAddress(trade.inputAmount.currency) : undefined,
token_out_address: trade ? getTokenAddress(trade.outputAmount.currency) : undefined,
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: trade ? formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals) : undefined,
token_out_amount: trade ? formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals) : undefined,
token_in_amount_usd: fiatValueInput,
token_out_amount_usd: fiatValueOutput,
price_impact_basis_points: trade ? formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)) : undefined,
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
is_auto_router_api: isAutoRouterApi,
is_auto_slippage: isAutoSlippage,
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
? trade.inputAmount.currency.chainId
: undefined,
duration_from_first_quote_to_swap_submission_milliseconds: swapQuoteReceivedDate
? getDurationFromDateMilliseconds(swapQuoteReceivedDate)
: undefined,
swap_quote_block_number: trade.blockNumber,
...formatRoutesEventProperties(routes),
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment