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,19 +70,29 @@ export default function CommonBases({ ...@@ -47,19 +70,29 @@ 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 (
<BaseWrapper <TraceEvent
tabIndex={0} events={[Event.onClick, Event.onKeyPress]}
onKeyPress={(e) => !isSelected && e.key === 'Enter' && onSelect(currency)} name={EventName.TOKEN_SELECTED}
onClick={() => !isSelected && onSelect(currency)} properties={formatAnalyticsEventProperties(currency, tokenAddress, searchQuery, isAddressSearch)}
disable={isSelected} element={ElementName.COMMON_BASES_CURRENCY_BUTTON}
key={currencyId(currency)} key={currencyId(currency)}
> >
<CurrencyLogoFromList currency={currency} /> <BaseWrapper
<Text fontWeight={500} fontSize={16}> tabIndex={0}
{currency.symbol} onKeyPress={(e) => !isSelected && e.key === 'Enter' && onSelect(currency)}
</Text> onClick={() => !isSelected && onSelect(currency)}
</BaseWrapper> disable={isSelected}
key={currencyId(currency)}
>
<CurrencyLogoFromList currency={currency} />
<Text fontWeight={500} fontSize={16}>
{currency.symbol}
</Text>
</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,35 +127,42 @@ function CurrencyRow({ ...@@ -123,35 +127,42 @@ 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 (
<MenuItem <TraceEvent
tabIndex={0} events={[Event.onClick, Event.onKeyPress]}
style={style} name={EventName.TOKEN_SELECTED}
className={`token-item-${key}`} properties={{ is_imported_by_user: customAdded, ...eventProperties }}
onKeyPress={(e) => (!isSelected && e.key === 'Enter' ? onSelect() : null)} element={ElementName.TOKEN_SELECTOR_ROW}
onClick={() => (isSelected ? null : onSelect())}
disabled={isSelected}
selected={otherSelected}
> >
<CurrencyLogo currency={currency} size={'24px'} /> <MenuItem
<Column> tabIndex={0}
<Text title={currency.name} fontWeight={500}> style={style}
{currency.symbol} className={`token-item-${key}`}
</Text> onKeyPress={(e) => (!isSelected && e.key === 'Enter' ? onSelect() : null)}
<ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}> onClick={() => (isSelected ? null : onSelect())}
{!currency.isNative && !isOnSelectedList && customAdded ? ( disabled={isSelected}
<Trans>{currency.name} • Added by user</Trans> selected={otherSelected}
) : ( >
currency.name <CurrencyLogo currency={currency} size={'24px'} />
)} <Column>
</ThemedText.DarkGray> <Text title={currency.name} fontWeight={500}>
</Column> {currency.symbol}
<TokenTags currency={currency} /> </Text>
{showCurrencyAmount && ( <ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}>
<RowFixed style={{ justifySelf: 'flex-end' }}> {!currency.isNative && !isOnSelectedList && customAdded ? (
{balance ? <Balance balance={balance} /> : account ? <Loader /> : null} <Trans>{currency.name} • Added by user</Trans>
</RowFixed> ) : (
)} currency.name
</MenuItem> )}
</ThemedText.DarkGray>
</Column>
<TokenTags currency={currency} />
{showCurrencyAmount && (
<RowFixed style={{ justifySelf: 'flex-end' }}>
{balance ? <Balance balance={balance} /> : account ? <Loader /> : null}
</RowFixed>
)}
</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,76 +191,86 @@ export function CurrencySearch({ ...@@ -189,76 +191,86 @@ export function CurrencySearch({
}, []) }, [])
return ( return (
<ContentWrapper> <Trace name={EventName.TOKEN_SELECTOR_OPENED} modal={ModalName.TOKEN_SELECTOR} shouldLogImpression={true}>
<PaddedColumn gap="16px"> <ContentWrapper>
<RowBetween> <PaddedColumn gap="16px">
<Text fontWeight={500} fontSize={16}> <RowBetween>
<Trans>Select a token</Trans> <Text fontWeight={500} fontSize={16}>
</Text> <Trans>Select a token</Trans>
<CloseIcon onClick={onDismiss} /> </Text>
</RowBetween> <CloseIcon onClick={onDismiss} />
<Row> </RowBetween>
<SearchInput <Row>
type="text" <SearchInput
id="token-search-input" type="text"
placeholder={t`Search name or paste address`} id="token-search-input"
autoComplete="off" placeholder={t`Search name or paste address`}
value={searchQuery} autoComplete="off"
ref={inputRef as RefObject<HTMLInputElement>} value={searchQuery}
onChange={handleInput} ref={inputRef as RefObject<HTMLInputElement>}
onKeyDown={handleEnter} onChange={handleInput}
/> onKeyDown={handleEnter}
</Row> />
{showCommonBases && ( </Row>
<CommonBases chainId={chainId} onSelect={handleCurrencySelect} selectedCurrency={selectedCurrency} /> {showCommonBases && (
<CommonBases
chainId={chainId}
onSelect={handleCurrencySelect}
selectedCurrency={selectedCurrency}
searchQuery={searchQuery}
isAddressSearch={isAddressSearch}
/>
)}
</PaddedColumn>
<Separator />
{searchToken && !searchTokenIsAdded ? (
<Column style={{ padding: '20px 0', height: '100%' }}>
<ImportRow token={searchToken} showImportView={showImportView} setImportToken={setImportToken} />
</Column>
) : filteredSortedTokens?.length > 0 || filteredInactiveTokens?.length > 0 ? (
<div style={{ flex: '1' }}>
<AutoSizer disableWidth>
{({ height }) => (
<CurrencyList
height={height}
currencies={disableNonToken ? filteredSortedTokens : filteredSortedTokensWithETH}
otherListTokens={filteredInactiveTokens}
onCurrencySelect={handleCurrencySelect}
otherCurrency={otherSelectedCurrency}
selectedCurrency={selectedCurrency}
fixedListRef={fixedList}
showImportView={showImportView}
setImportToken={setImportToken}
showCurrencyAmount={showCurrencyAmount}
isLoading={balancesIsLoading && !tokenLoaderTimerElapsed}
searchQuery={searchQuery}
isAddressSearch={isAddressSearch}
/>
)}
</AutoSizer>
</div>
) : (
<Column style={{ padding: '20px', height: '100%' }}>
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
<Trans>No results found.</Trans>
</ThemedText.Main>
</Column>
)} )}
</PaddedColumn> <Footer>
<Separator /> <Row justify="center">
{searchToken && !searchTokenIsAdded ? ( <ButtonText onClick={showManageView} color={theme.primary1} className="list-token-manage-button">
<Column style={{ padding: '20px 0', height: '100%' }}> <RowFixed>
<ImportRow token={searchToken} showImportView={showImportView} setImportToken={setImportToken} /> <IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
</Column> <Edit />
) : filteredSortedTokens?.length > 0 || filteredInactiveTokens?.length > 0 ? ( </IconWrapper>
<div style={{ flex: '1' }}> <ThemedText.Main color={theme.primaryText1}>
<AutoSizer disableWidth> <Trans>Manage Token Lists</Trans>
{({ height }) => ( </ThemedText.Main>
<CurrencyList </RowFixed>
height={height} </ButtonText>
currencies={disableNonToken ? filteredSortedTokens : filteredSortedTokensWithETH} </Row>
otherListTokens={filteredInactiveTokens} </Footer>
onCurrencySelect={handleCurrencySelect} </ContentWrapper>
otherCurrency={otherSelectedCurrency} </Trace>
selectedCurrency={selectedCurrency}
fixedListRef={fixedList}
showImportView={showImportView}
setImportToken={setImportToken}
showCurrencyAmount={showCurrencyAmount}
isLoading={balancesIsLoading && !tokenLoaderTimerElapsed}
/>
)}
</AutoSizer>
</div>
) : (
<Column style={{ padding: '20px', height: '100%' }}>
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
<Trans>No results found.</Trans>
</ThemedText.Main>
</Column>
)}
<Footer>
<Row justify="center">
<ButtonText onClick={showManageView} color={theme.primary1} className="list-token-manage-button">
<RowFixed>
<IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
<Edit />
</IconWrapper>
<ThemedText.Main color={theme.primaryText1}>
<Trans>Manage Token Lists</Trans>
</ThemedText.Main>
</RowFixed>
</ButtonText>
</Row>
</Footer>
</ContentWrapper>
) )
} }
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,18 +76,25 @@ export function ImportToken(props: ImportProps) { ...@@ -67,18 +76,25 @@ 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} />
))} ))}
<ButtonPrimary <TraceEvent
altDisabledStyle={true} events={[Event.onClick]}
$borderRadius="20px" name={EventName.TOKEN_IMPORTED}
padding="10px 1rem" properties={formatAnalyticsEventProperties(tokens)}
onClick={() => { element={ElementName.IMPORT_TOKEN_BUTTON}
tokens.map((token) => addToken(token))
handleCurrencySelect && handleCurrencySelect(tokens[0])
}}
className=".token-dismiss-button"
> >
<Trans>Import</Trans> <ButtonPrimary
</ButtonPrimary> altDisabledStyle={true}
$borderRadius="20px"
padding="10px 1rem"
onClick={() => {
tokens.map((token) => addToken(token))
handleCurrencySelect && handleCurrencySelect(tokens[0])
}}
className=".token-dismiss-button"
>
<Trans>Import</Trans>
</ButtonPrimary>
</TraceEvent>
</AutoColumn> </AutoColumn>
</Wrapper> </Wrapper>
) )
......
This diff is collapsed.
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