Commit cb094a1f authored by lynn's avatar lynn Committed by GitHub

feat: implement token selector events (#4067)

* init commit

* add amplitude ts sdk to package.json

* add more comments and documentation

* respond to vm comments

* respond to cmcewen comments

* fix: remove unused constants

* init commit

* adapt to web

* add optional event properties to trace

* correct telemetry to analytics

* change telemetry to analytics in doc

* fix: respond to cmcewen comments + initialize analytics in app.tsx + add missing return statement

* init commit

* respond to zzmp comments

* add token selected event

* fixes

* eliminate unnecessary state

* respond to part of zzmp comments

* respond to zzmp comments round 2

* fixes

* respond to zzmp comments

* add imported token event and other fixes

* also log onKeyPress for suggested tokens

* respond to cmcewen comments
parent d05fefc2
...@@ -7,9 +7,11 @@ export interface ITraceContext { ...@@ -7,9 +7,11 @@ export interface ITraceContext {
// Highest order context: eg Swap or Explore. // Highest order context: eg Swap or Explore.
page?: PageName page?: PageName
// Enclosed section name. Can be as wide or narrow as necessary to // Enclosed section name. For contexts with modals, refers to the
// provide context. // section of the page from which the user triggered the modal.
section?: SectionName | ModalName section?: SectionName
modal?: ModalName
// Element name mostly used to identify events sources // Element name mostly used to identify events sources
// Does not need to be unique given the higher order page and section. // Does not need to be unique given the higher order page and section.
......
...@@ -30,7 +30,7 @@ export const TraceEvent = memo((props: PropsWithChildren<TraceEventProps>) => { ...@@ -30,7 +30,7 @@ export const TraceEvent = memo((props: PropsWithChildren<TraceEventProps>) => {
return child return child
} }
// For each child, augment event handlers defined in `actionNames` with event tracing // For each child, augment event handlers defined in `events` with event tracing.
return cloneElement(child, getEventHandlers(child, traceContext, events, name, properties)) return cloneElement(child, getEventHandlers(child, traceContext, events, name, properties))
}) })
} }
...@@ -42,7 +42,7 @@ export const TraceEvent = memo((props: PropsWithChildren<TraceEventProps>) => { ...@@ -42,7 +42,7 @@ export const TraceEvent = memo((props: PropsWithChildren<TraceEventProps>) => {
TraceEvent.displayName = 'TraceEvent' TraceEvent.displayName = 'TraceEvent'
/** /**
* Given a set of child element and action props, returns a spreadable * Given a set of child element and event props, returns a spreadable
* object of the event handlers augmented with analytics logging. * object of the event handlers augmented with analytics logging.
*/ */
function getEventHandlers( function getEventHandlers(
...@@ -61,7 +61,7 @@ function getEventHandlers( ...@@ -61,7 +61,7 @@ function getEventHandlers(
child.props[event]?.apply(child, args) child.props[event]?.apply(child, args)
// augment handler with analytics logging // augment handler with analytics logging
sendAnalyticsEvent(name, { ...traceContext, ...properties }) sendAnalyticsEvent(name, { ...traceContext, ...properties, action: event })
} }
} }
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
export enum EventName { export enum EventName {
PAGE_VIEWED = 'Page Viewed', PAGE_VIEWED = 'Page Viewed',
SWAP_SUBMITTED = 'Swap Submitted', SWAP_SUBMITTED = 'Swap Submitted',
TOKEN_IMPORTED = 'Token Imported',
TOKEN_SELECTED = 'Token Selected',
TOKEN_SELECTOR_OPENED = 'Token Selector Opened',
// alphabetize additional event names. // alphabetize additional event names.
} }
...@@ -25,12 +28,14 @@ export const enum PageName { ...@@ -25,12 +28,14 @@ export const enum PageName {
*/ */
export const enum SectionName { export const enum SectionName {
CURRENCY_INPUT_PANEL = 'swap-currency-input', CURRENCY_INPUT_PANEL = 'swap-currency-input',
CURRENCY_OUTPUT_PANEL = 'swap-currency-output',
// alphabetize additional section names. // alphabetize additional section names.
} }
/** Known modals for analytics purposes. */ /** Known modals for analytics purposes. */
export const enum ModalName { export const enum ModalName {
SWAP = 'swap-modal', SWAP = 'swap-modal',
TOKEN_SELECTOR = 'token-selector-modal',
// alphabetize additional modal names. // alphabetize additional modal names.
} }
...@@ -39,8 +44,11 @@ export const enum ModalName { ...@@ -39,8 +44,11 @@ export const enum ModalName {
* Use to identify low-level components given a TraceContext * Use to identify low-level components given a TraceContext
*/ */
export const enum ElementName { export const enum ElementName {
COMMON_BASES_CURRENCY_BUTTON = 'common-bases-currency-button',
CONFIRM_SWAP_BUTTON = 'confirm-swap-or-send', CONFIRM_SWAP_BUTTON = 'confirm-swap-or-send',
IMPORT_TOKEN_BUTTON = 'import-token-button',
SWAP_BUTTON = 'swap-button', SWAP_BUTTON = 'swap-button',
TOKEN_SELECTOR_ROW = 'token-selector-row',
// alphabetize additional element names. // alphabetize additional element names.
} }
...@@ -51,5 +59,7 @@ export const enum ElementName { ...@@ -51,5 +59,7 @@ export const enum ElementName {
*/ */
export enum Event { export enum Event {
onClick = 'onClick', onClick = 'onClick',
onKeyPress = 'onKeyPress',
onSelect = 'onSelect',
// alphabetize additional events. // alphabetize additional events.
} }
...@@ -34,7 +34,7 @@ export function initializeAnalytics(isDevEnvironment = process.env.NODE_ENV === ...@@ -34,7 +34,7 @@ export function initializeAnalytics(isDevEnvironment = process.env.NODE_ENV ===
/** Sends an event to Amplitude. */ /** Sends an event to Amplitude. */
export function sendAnalyticsEvent(eventName: string, eventProperties?: Record<string, unknown>) { export function sendAnalyticsEvent(eventName: string, eventProperties?: Record<string, unknown>) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
console.debug(`[amplitude(${eventName})]: ${JSON.stringify(eventProperties)}`) console.log(`[amplitude(${eventName})]: ${JSON.stringify(eventProperties)}`)
return return
} }
......
import { Currency } from '@uniswap/sdk-core' import { Currency, Token } from '@uniswap/sdk-core'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo' import CurrencyLogo from 'components/CurrencyLogo'
import { AutoRow } from 'components/Row' import { AutoRow } from 'components/Row'
...@@ -31,14 +33,35 @@ const BaseWrapper = styled.div<{ disable?: boolean }>` ...@@ -31,14 +33,35 @@ const BaseWrapper = styled.div<{ disable?: boolean }>`
filter: ${({ disable }) => disable && 'grayscale(1)'}; filter: ${({ disable }) => disable && 'grayscale(1)'};
` `
const formatAnalyticsEventProperties = (
currency: Currency,
tokenAddress: string | undefined,
searchQuery: string,
isAddressSearch: string | false
) => ({
token_symbol: currency?.symbol,
token_chain_id: currency?.chainId,
...(tokenAddress ? { token_address: tokenAddress } : {}),
is_suggested_token: true,
is_selected_from_list: false,
is_imported_by_user: false,
...(isAddressSearch === false
? { search_token_symbol_input: searchQuery }
: { search_token_address_input: isAddressSearch }),
})
export default function CommonBases({ export default function CommonBases({
chainId, chainId,
onSelect, onSelect,
selectedCurrency, selectedCurrency,
searchQuery,
isAddressSearch,
}: { }: {
chainId?: number chainId?: number
selectedCurrency?: Currency | null selectedCurrency?: Currency | null
onSelect: (currency: Currency) => void onSelect: (currency: Currency) => void
searchQuery: string
isAddressSearch: string | false
}) { }) {
const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : [] const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : []
...@@ -47,7 +70,16 @@ export default function CommonBases({ ...@@ -47,7 +70,16 @@ export default function CommonBases({
<AutoRow gap="4px"> <AutoRow gap="4px">
{bases.map((currency: Currency) => { {bases.map((currency: Currency) => {
const isSelected = selectedCurrency?.equals(currency) const isSelected = selectedCurrency?.equals(currency)
const tokenAddress = currency instanceof Token ? currency?.address : undefined
return ( return (
<TraceEvent
events={[Event.onClick, Event.onKeyPress]}
name={EventName.TOKEN_SELECTED}
properties={formatAnalyticsEventProperties(currency, tokenAddress, searchQuery, isAddressSearch)}
element={ElementName.COMMON_BASES_CURRENCY_BUTTON}
key={currencyId(currency)}
>
<BaseWrapper <BaseWrapper
tabIndex={0} tabIndex={0}
onKeyPress={(e) => !isSelected && e.key === 'Enter' && onSelect(currency)} onKeyPress={(e) => !isSelected && e.key === 'Enter' && onSelect(currency)}
...@@ -60,6 +92,7 @@ export default function CommonBases({ ...@@ -60,6 +92,7 @@ export default function CommonBases({
{currency.symbol} {currency.symbol}
</Text> </Text>
</BaseWrapper> </BaseWrapper>
</TraceEvent>
) )
})} })}
</AutoRow> </AutoRow>
......
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { LightGreyCard } from 'components/Card' import { LightGreyCard } from 'components/Card'
import QuestionHelper from 'components/QuestionHelper' import QuestionHelper from 'components/QuestionHelper'
import useTheme from 'hooks/useTheme' import useTheme from 'hooks/useTheme'
...@@ -106,6 +108,7 @@ function CurrencyRow({ ...@@ -106,6 +108,7 @@ function CurrencyRow({
otherSelected, otherSelected,
style, style,
showCurrencyAmount, showCurrencyAmount,
eventProperties,
}: { }: {
currency: Currency currency: Currency
onSelect: () => void onSelect: () => void
...@@ -113,6 +116,7 @@ function CurrencyRow({ ...@@ -113,6 +116,7 @@ function CurrencyRow({
otherSelected: boolean otherSelected: boolean
style: CSSProperties style: CSSProperties
showCurrencyAmount?: boolean showCurrencyAmount?: boolean
eventProperties: Record<string, unknown>
}) { }) {
const { account } = useWeb3React() const { account } = useWeb3React()
const key = currencyKey(currency) const key = currencyKey(currency)
...@@ -123,6 +127,12 @@ function CurrencyRow({ ...@@ -123,6 +127,12 @@ function CurrencyRow({
// only show add or remove buttons if not on selected list // only show add or remove buttons if not on selected list
return ( return (
<TraceEvent
events={[Event.onClick, Event.onKeyPress]}
name={EventName.TOKEN_SELECTED}
properties={{ is_imported_by_user: customAdded, ...eventProperties }}
element={ElementName.TOKEN_SELECTOR_ROW}
>
<MenuItem <MenuItem
tabIndex={0} tabIndex={0}
style={style} style={style}
...@@ -152,6 +162,7 @@ function CurrencyRow({ ...@@ -152,6 +162,7 @@ function CurrencyRow({
</RowFixed> </RowFixed>
)} )}
</MenuItem> </MenuItem>
</TraceEvent>
) )
} }
...@@ -186,6 +197,25 @@ function BreakLineComponent({ style }: { style: CSSProperties }) { ...@@ -186,6 +197,25 @@ function BreakLineComponent({ style }: { style: CSSProperties }) {
) )
} }
const formatAnalyticsEventProperties = (
token: Token,
index: number,
data: any[],
searchQuery: string,
isAddressSearch: string | false
) => ({
token_symbol: token?.symbol,
token_address: token?.address,
is_suggested_token: false,
is_selected_from_list: true,
scroll_position: '',
token_list_index: index,
token_list_length: data.length,
...(isAddressSearch === false
? { search_token_symbol_input: searchQuery }
: { search_token_address_input: isAddressSearch }),
})
export default function CurrencyList({ export default function CurrencyList({
height, height,
currencies, currencies,
...@@ -198,6 +228,8 @@ export default function CurrencyList({ ...@@ -198,6 +228,8 @@ export default function CurrencyList({
setImportToken, setImportToken,
showCurrencyAmount, showCurrencyAmount,
isLoading, isLoading,
searchQuery,
isAddressSearch,
}: { }: {
height: number height: number
currencies: Currency[] currencies: Currency[]
...@@ -210,6 +242,8 @@ export default function CurrencyList({ ...@@ -210,6 +242,8 @@ export default function CurrencyList({
setImportToken: (token: Token) => void setImportToken: (token: Token) => void
showCurrencyAmount?: boolean showCurrencyAmount?: boolean
isLoading: boolean isLoading: boolean
searchQuery: string
isAddressSearch: string | false
}) { }) {
const itemData: (Currency | BreakLine)[] = useMemo(() => { const itemData: (Currency | BreakLine)[] = useMemo(() => {
if (otherListTokens && otherListTokens?.length > 0) { if (otherListTokens && otherListTokens?.length > 0) {
...@@ -257,6 +291,7 @@ export default function CurrencyList({ ...@@ -257,6 +291,7 @@ export default function CurrencyList({
onSelect={handleSelect} onSelect={handleSelect}
otherSelected={otherSelected} otherSelected={otherSelected}
showCurrencyAmount={showCurrencyAmount} showCurrencyAmount={showCurrencyAmount}
eventProperties={formatAnalyticsEventProperties(token, index, data, searchQuery, isAddressSearch)}
/> />
) )
} else { } else {
...@@ -272,6 +307,8 @@ export default function CurrencyList({ ...@@ -272,6 +307,8 @@ export default function CurrencyList({
showImportView, showImportView,
showCurrencyAmount, showCurrencyAmount,
isLoading, isLoading,
isAddressSearch,
searchQuery,
] ]
) )
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
import { t, Trans } from '@lingui/macro' import { t, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core' import { Currency, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { EventName, ModalName } from 'components/AmplitudeAnalytics/constants'
import { Trace } from 'components/AmplitudeAnalytics/Trace'
import { sendEvent } from 'components/analytics' import { sendEvent } from 'components/analytics'
import useDebounce from 'hooks/useDebounce' import useDebounce from 'hooks/useDebounce'
import { useOnClickOutside } from 'hooks/useOnClickOutside' import { useOnClickOutside } from 'hooks/useOnClickOutside'
...@@ -189,6 +191,7 @@ export function CurrencySearch({ ...@@ -189,6 +191,7 @@ export function CurrencySearch({
}, []) }, [])
return ( return (
<Trace name={EventName.TOKEN_SELECTOR_OPENED} modal={ModalName.TOKEN_SELECTOR} shouldLogImpression={true}>
<ContentWrapper> <ContentWrapper>
<PaddedColumn gap="16px"> <PaddedColumn gap="16px">
<RowBetween> <RowBetween>
...@@ -210,7 +213,13 @@ export function CurrencySearch({ ...@@ -210,7 +213,13 @@ export function CurrencySearch({
/> />
</Row> </Row>
{showCommonBases && ( {showCommonBases && (
<CommonBases chainId={chainId} onSelect={handleCurrencySelect} selectedCurrency={selectedCurrency} /> <CommonBases
chainId={chainId}
onSelect={handleCurrencySelect}
selectedCurrency={selectedCurrency}
searchQuery={searchQuery}
isAddressSearch={isAddressSearch}
/>
)} )}
</PaddedColumn> </PaddedColumn>
<Separator /> <Separator />
...@@ -234,6 +243,8 @@ export function CurrencySearch({ ...@@ -234,6 +243,8 @@ export function CurrencySearch({
setImportToken={setImportToken} setImportToken={setImportToken}
showCurrencyAmount={showCurrencyAmount} showCurrencyAmount={showCurrencyAmount}
isLoading={balancesIsLoading && !tokenLoaderTimerElapsed} isLoading={balancesIsLoading && !tokenLoaderTimerElapsed}
searchQuery={searchQuery}
isAddressSearch={isAddressSearch}
/> />
)} )}
</AutoSizer> </AutoSizer>
...@@ -260,5 +271,6 @@ export function CurrencySearch({ ...@@ -260,5 +271,6 @@ export function CurrencySearch({
</Row> </Row>
</Footer> </Footer>
</ContentWrapper> </ContentWrapper>
</Trace>
) )
} }
import { Plural, Trans } from '@lingui/macro' import { Plural, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core' import { Currency, Token } from '@uniswap/sdk-core'
import { TokenList } from '@uniswap/token-lists' import { TokenList } from '@uniswap/token-lists'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { ButtonPrimary } from 'components/Button' import { ButtonPrimary } from 'components/Button'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import { RowBetween } from 'components/Row' import { RowBetween } from 'components/Row'
...@@ -30,6 +32,12 @@ interface ImportProps { ...@@ -30,6 +32,12 @@ interface ImportProps {
handleCurrencySelect?: (currency: Currency) => void handleCurrencySelect?: (currency: Currency) => void
} }
const formatAnalyticsEventProperties = (tokens: Token[]) => ({
token_symbols: tokens.map((token) => token?.symbol),
token_addresses: tokens.map((token) => token?.address),
token_chain_ids: tokens.map((token) => token?.chainId),
})
export function ImportToken(props: ImportProps) { export function ImportToken(props: ImportProps) {
const { tokens, list, onBack, onDismiss, handleCurrencySelect } = props const { tokens, list, onBack, onDismiss, handleCurrencySelect } = props
const theme = useTheme() const theme = useTheme()
...@@ -42,6 +50,7 @@ export function ImportToken(props: ImportProps) { ...@@ -42,6 +50,7 @@ export function ImportToken(props: ImportProps) {
if (intersection.size > 0) { if (intersection.size > 0) {
return <BlockedToken onBack={onBack} onDismiss={onDismiss} blockedTokens={Array.from(intersection)} /> return <BlockedToken onBack={onBack} onDismiss={onDismiss} blockedTokens={Array.from(intersection)} />
} }
return ( return (
<Wrapper> <Wrapper>
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}> <PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
...@@ -67,6 +76,12 @@ export function ImportToken(props: ImportProps) { ...@@ -67,6 +76,12 @@ export function ImportToken(props: ImportProps) {
{tokens.map((token) => ( {tokens.map((token) => (
<TokenImportCard token={token} list={list} key={'import' + token.address} /> <TokenImportCard token={token} list={list} key={'import' + token.address} />
))} ))}
<TraceEvent
events={[Event.onClick]}
name={EventName.TOKEN_IMPORTED}
properties={formatAnalyticsEventProperties(tokens)}
element={ElementName.IMPORT_TOKEN_BUTTON}
>
<ButtonPrimary <ButtonPrimary
altDisabledStyle={true} altDisabledStyle={true}
$borderRadius="20px" $borderRadius="20px"
...@@ -79,6 +94,7 @@ export function ImportToken(props: ImportProps) { ...@@ -79,6 +94,7 @@ export function ImportToken(props: ImportProps) {
> >
<Trans>Import</Trans> <Trans>Import</Trans>
</ButtonPrimary> </ButtonPrimary>
</TraceEvent>
</AutoColumn> </AutoColumn>
</Wrapper> </Wrapper>
) )
......
...@@ -4,6 +4,8 @@ import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core' ...@@ -4,6 +4,8 @@ import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { PageName, SectionName } from 'components/AmplitudeAnalytics/constants'
import { Trace } from 'components/AmplitudeAnalytics/Trace'
import { sendEvent } from 'components/analytics' import { sendEvent } from 'components/analytics'
import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert'
import SwapDetailsDropdown from 'components/swap/SwapDetailsDropdown' import SwapDetailsDropdown from 'components/swap/SwapDetailsDropdown'
...@@ -396,6 +398,7 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -396,6 +398,7 @@ export default function Swap({ history }: RouteComponentProps) {
const priceImpactTooHigh = priceImpactSeverity > 3 && !isExpertMode const priceImpactTooHigh = priceImpactSeverity > 3 && !isExpertMode
return ( return (
<Trace page={PageName.SWAP_PAGE} shouldLogImpression={false}>
<> <>
<TokenWarningModal <TokenWarningModal
isOpen={importTokensNotInDefault.length > 0 && !dismissTokenWarning} isOpen={importTokensNotInDefault.length > 0 && !dismissTokenWarning}
...@@ -422,9 +425,14 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -422,9 +425,14 @@ export default function Swap({ history }: RouteComponentProps) {
<AutoColumn gap={'sm'}> <AutoColumn gap={'sm'}>
<div style={{ display: 'relative' }}> <div style={{ display: 'relative' }}>
<Trace section={SectionName.CURRENCY_INPUT_PANEL}>
<CurrencyInputPanel <CurrencyInputPanel
label={ label={
independentField === Field.OUTPUT && !showWrap ? <Trans>From (at most)</Trans> : <Trans>From</Trans> independentField === Field.OUTPUT && !showWrap ? (
<Trans>From (at most)</Trans>
) : (
<Trans>From</Trans>
)
} }
value={formattedAmounts[Field.INPUT]} value={formattedAmounts[Field.INPUT]}
showMaxButton={showMaxButton} showMaxButton={showMaxButton}
...@@ -435,9 +443,10 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -435,9 +443,10 @@ export default function Swap({ history }: RouteComponentProps) {
onCurrencySelect={handleInputSelect} onCurrencySelect={handleInputSelect}
otherCurrency={currencies[Field.OUTPUT]} otherCurrency={currencies[Field.OUTPUT]}
showCommonBases={true} showCommonBases={true}
id="swap-currency-input" id={SectionName.CURRENCY_INPUT_PANEL}
loading={independentField === Field.OUTPUT && routeIsSyncing} loading={independentField === Field.OUTPUT && routeIsSyncing}
/> />
</Trace>
<ArrowWrapper clickable> <ArrowWrapper clickable>
<ArrowDown <ArrowDown
size="16" size="16"
...@@ -448,10 +457,13 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -448,10 +457,13 @@ export default function Swap({ history }: RouteComponentProps) {
color={currencies[Field.INPUT] && currencies[Field.OUTPUT] ? theme.text1 : theme.text3} color={currencies[Field.INPUT] && currencies[Field.OUTPUT] ? theme.text1 : theme.text3}
/> />
</ArrowWrapper> </ArrowWrapper>
<Trace section={SectionName.CURRENCY_OUTPUT_PANEL}>
<CurrencyInputPanel <CurrencyInputPanel
value={formattedAmounts[Field.OUTPUT]} value={formattedAmounts[Field.OUTPUT]}
onUserInput={handleTypeOutput} onUserInput={handleTypeOutput}
label={independentField === Field.INPUT && !showWrap ? <Trans>To (at least)</Trans> : <Trans>To</Trans>} label={
independentField === Field.INPUT && !showWrap ? <Trans>To (at least)</Trans> : <Trans>To</Trans>
}
showMaxButton={false} showMaxButton={false}
hideBalance={false} hideBalance={false}
fiatValue={fiatValueOutput ?? undefined} fiatValue={fiatValueOutput ?? undefined}
...@@ -460,9 +472,10 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -460,9 +472,10 @@ export default function Swap({ history }: RouteComponentProps) {
onCurrencySelect={handleOutputSelect} onCurrencySelect={handleOutputSelect}
otherCurrency={currencies[Field.INPUT]} otherCurrency={currencies[Field.INPUT]}
showCommonBases={true} showCommonBases={true}
id="swap-currency-output" id={SectionName.CURRENCY_OUTPUT_PANEL}
loading={independentField === Field.INPUT && routeIsSyncing} loading={independentField === Field.INPUT && routeIsSyncing}
/> />
</Trace>
</div> </div>
{recipient !== null && !showWrap ? ( {recipient !== null && !showWrap ? (
...@@ -539,7 +552,8 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -539,7 +552,8 @@ export default function Swap({ history }: RouteComponentProps) {
style={{ marginRight: '8px', flexShrink: 0 }} style={{ marginRight: '8px', flexShrink: 0 }}
/> />
{/* we need to shorten this string on mobile */} {/* we need to shorten this string on mobile */}
{approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED ? ( {approvalState === ApprovalState.APPROVED ||
signatureState === UseERC20PermitState.SIGNED ? (
<Trans>You can now trade {currencies[Field.INPUT]?.symbol}</Trans> <Trans>You can now trade {currencies[Field.INPUT]?.symbol}</Trans>
) : ( ) : (
<Trans>Allow the Uniswap Protocol to use your {currencies[Field.INPUT]?.symbol}</Trans> <Trans>Allow the Uniswap Protocol to use your {currencies[Field.INPUT]?.symbol}</Trans>
...@@ -651,5 +665,6 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -651,5 +665,6 @@ export default function Swap({ history }: RouteComponentProps) {
/> />
)} )}
</> </>
</Trace>
) )
} }
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