Commit bd8113d0 authored by Tina's avatar Tina Committed by GitHub

chore: Goodbye widget :( (#6543)

* goodbye widget :(

* remove unused function

* modify trace tests

* fix lint

* is github down?
parent 14e3ef04
...@@ -8,6 +8,5 @@ updates: ...@@ -8,6 +8,5 @@ updates:
allow: allow:
- dependency-name: '@uniswap/default-token-list' - dependency-name: '@uniswap/default-token-list'
- dependency-name: '@uniswap/token-lists' - dependency-name: '@uniswap/token-lists'
- dependency-name: '@uniswap/widgets'
reviewers: reviewers:
- 'Uniswap/dependabot-reviewers' - 'Uniswap/dependabot-reviewers'
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core' import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
import { UNI } from '../../src/constants/tokens' import { UNI } from '../../src/constants/tokens'
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
import { getTestSelector } from '../utils' import { getTestSelector } from '../utils'
const UNI_MAINNET = UNI[SupportedChainId.MAINNET] const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
...@@ -96,7 +95,6 @@ describe('Token details', () => { ...@@ -96,7 +95,6 @@ describe('Token details', () => {
cy.viewport(1200, 800) cy.viewport(1200, 800)
cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`, { cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`, {
ethereum: 'hardhat', ethereum: 'hardhat',
featureFlags: [FeatureFlag.removeWidget],
}).then(() => { }).then(() => {
cy.wait('@eth_blockNumber') cy.wait('@eth_blockNumber')
cy.scrollTo('top') cy.scrollTo('top')
...@@ -121,7 +119,7 @@ describe('Token details', () => { ...@@ -121,7 +119,7 @@ describe('Token details', () => {
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI') cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI')
cy.get(`#swap-currency-input .open-currency-select-button`).click() cy.get(`#swap-currency-input .open-currency-select-button`).click()
cy.contains('WETH').click() cy.contains('WETH').click()
cy.visit('/swap', { featureFlags: [FeatureFlag.removeWidget] }) cy.visit('/swap')
cy.contains('UNI').should('not.exist') cy.contains('UNI').should('not.exist')
cy.contains('WETH').should('not.exist') cy.contains('WETH').should('not.exist')
}) })
...@@ -147,7 +145,7 @@ describe('Token details', () => { ...@@ -147,7 +145,7 @@ describe('Token details', () => {
}) })
it('should show a L2 token even if the user is connected to a different network', () => { it('should show a L2 token even if the user is connected to a different network', () => {
cy.visit('/tokens', { ethereum: 'hardhat', featureFlags: [FeatureFlag.removeWidget] }) cy.visit('/tokens', { ethereum: 'hardhat' })
cy.get(getTestSelector('tokens-network-filter-selected')).click() cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click() cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum') cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum')
......
...@@ -98,8 +98,8 @@ ...@@ -98,8 +98,8 @@
"@types/ua-parser-js": "^0.7.35", "@types/ua-parser-js": "^0.7.35",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@types/wcag-contrast": "^3.0.0", "@types/wcag-contrast": "^3.0.0",
"@uniswap/eslint-config": "^1.2.0",
"@uniswap/default-token-list": "^9.4.0", "@uniswap/default-token-list": "^9.4.0",
"@uniswap/eslint-config": "^1.2.0",
"@vanilla-extract/babel-plugin": "^1.1.7", "@vanilla-extract/babel-plugin": "^1.1.7",
"@vanilla-extract/jest-transform": "^1.1.1", "@vanilla-extract/jest-transform": "^1.1.1",
"@vanilla-extract/webpack-plugin": "^2.1.11", "@vanilla-extract/webpack-plugin": "^2.1.11",
...@@ -169,7 +169,6 @@ ...@@ -169,7 +169,6 @@
"@uniswap/v3-core": "1.0.0", "@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1", "@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0", "@uniswap/v3-sdk": "^3.9.0",
"@uniswap/widgets": "^2.49.0",
"@vanilla-extract/css": "^1.7.2", "@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2", "@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2", "@vanilla-extract/dynamic": "^2.0.2",
...@@ -254,18 +253,6 @@ ...@@ -254,18 +253,6 @@
"workbox-routing": "^6.1.0", "workbox-routing": "^6.1.0",
"zustand": "^4.3.6" "zustand": "^4.3.6"
}, },
"resolutions": {
"@web3-react/coinbase-wallet": "^8.2.0",
"@web3-react/core": "^8.2.0",
"@web3-react/eip1193": "^8.2.0",
"@web3-react/empty": "^8.2.0",
"@web3-react/gnosis-safe": "^8.2.0",
"@web3-react/metamask": "^8.2.0",
"@web3-react/network": "^8.2.0",
"@web3-react/types": "^8.2.0",
"@web3-react/url": "^8.2.0",
"@web3-react/walletconnect": "^8.2.0"
},
"engines": { "engines": {
"npm": "please-use-yarn", "npm": "please-use-yarn",
"node": "14", "node": "14",
......
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags' import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { DetailsV2Variant, useDetailsV2Flag } from 'featureFlags/flags/nftDetails' import { DetailsV2Variant, useDetailsV2Flag } from 'featureFlags/flags/nftDetails'
import { useWidgetRemovalFlag, WidgetRemovalVariant } from 'featureFlags/flags/removeWidgetTdp'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc' import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useUpdateAtom } from 'jotai/utils' import { useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react' import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
...@@ -208,12 +207,6 @@ export default function FeatureFlagModal() { ...@@ -208,12 +207,6 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.detailsV2} featureFlag={FeatureFlag.detailsV2}
label="Use the new details page for nfts" label="Use the new details page for nfts"
/> />
<FeatureFlagOption
variant={WidgetRemovalVariant}
value={useWidgetRemovalFlag()}
featureFlag={FeatureFlag.removeWidget}
label="Swap Component on TDP"
/>
<FeatureFlagGroup name="Debug"> <FeatureFlagGroup name="Debug">
<FeatureFlagOption <FeatureFlagOption
variant={TraceJsonRpcVariant} variant={TraceJsonRpcVariant}
......
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics' import { Trace } from '@uniswap/analytics'
import { InterfacePageName } from '@uniswap/analytics-events' import { InterfacePageName } from '@uniswap/analytics-events'
import { Currency } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo' import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { AboutSection } from 'components/Tokens/TokenDetails/About' import { AboutSection } from 'components/Tokens/TokenDetails/About'
...@@ -22,19 +21,14 @@ import TokenDetailsSkeleton, { ...@@ -22,19 +21,14 @@ import TokenDetailsSkeleton, {
import StatsSection from 'components/Tokens/TokenDetails/StatsSection' import StatsSection from 'components/Tokens/TokenDetails/StatsSection'
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage' import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal' import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Widget from 'components/Widget'
import { SwapTokens } from 'components/Widget/inputs'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens' import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { checkWarning } from 'constants/tokenSafety' import { checkWarning } from 'constants/tokenSafety'
import { useWidgetRemovalEnabled } from 'featureFlags/flags/removeWidgetTdp'
import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks' import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
import { Chain, TokenQuery, TokenQueryData } from 'graphql/data/Token' import { Chain, TokenQuery, TokenQueryData } from 'graphql/data/Token'
import { QueryToken } from 'graphql/data/Token' import { QueryToken } from 'graphql/data/Token'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util' import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { useIsUserAddedTokenOnChain } from 'hooks/Tokens'
import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch' import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency' import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency'
import { getTokenAddress } from 'lib/utils/analytics'
import { Swap } from 'pages/Swap' import { Swap } from 'pages/Swap'
import { useCallback, useMemo, useState, useTransition } from 'react' import { useCallback, useMemo, useState, useTransition } from 'react'
import { ArrowLeft } from 'react-feather' import { ArrowLeft } from 'react-feather'
...@@ -130,12 +124,10 @@ export default function TokenDetails({ ...@@ -130,12 +124,10 @@ export default function TokenDetails({
) )
const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData) const { token: detailedToken, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData)
const { token: widgetInputToken } = useRelevantToken(inputTokenAddress, pageChainId, undefined)
const tokenWarning = address ? checkWarning(address) : null const tokenWarning = address ? checkWarning(address) : null
const isBlockedToken = tokenWarning?.canProceed === false const isBlockedToken = tokenWarning?.canProceed === false
const navigate = useNavigate() const navigate = useNavigate()
const widgetRemovalEnabled = useWidgetRemovalEnabled()
// Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again. // Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again.
const [isPending, startTokenTransition] = useTransition() const [isPending, startTokenTransition] = useTransition()
...@@ -152,22 +144,6 @@ export default function TokenDetails({ ...@@ -152,22 +144,6 @@ export default function TokenDetails({
[address, crossChainMap, didFetchFromChain, navigate, detailedToken?.isNative] [address, crossChainMap, didFetchFromChain, navigate, detailedToken?.isNative]
) )
useOnGlobalChainSwitch(navigateToTokenForChain) useOnGlobalChainSwitch(navigateToTokenForChain)
const navigateToWidgetSelectedToken = useCallback(
(tokens: SwapTokens) => {
const newDefaultToken = tokens[Field.OUTPUT] ?? tokens.default
const address = newDefaultToken?.isNative ? NATIVE_CHAIN_ID : newDefaultToken?.address
startTokenTransition(() =>
navigate(
getTokenDetailsURL({
address,
chain,
inputAddress: tokens[Field.INPUT] ? getTokenAddress(tokens[Field.INPUT] as Currency) : null,
})
)
)
},
[chain, navigate]
)
const handleCurrencyChange = useCallback( const handleCurrencyChange = useCallback(
(tokens: Pick<SwapState, Field.INPUT | Field.OUTPUT>) => { (tokens: Pick<SwapState, Field.INPUT | Field.OUTPUT>) => {
...@@ -202,12 +178,6 @@ export default function TokenDetails({ ...@@ -202,12 +178,6 @@ export default function TokenDetails({
const [openTokenSafetyModal, setOpenTokenSafetyModal] = useState(false) const [openTokenSafetyModal, setOpenTokenSafetyModal] = useState(false)
const shouldShowSpeedbump = !useIsUserAddedTokenOnChain(address, pageChainId) && tokenWarning !== null
const onReviewSwapClick = useCallback(
() => new Promise<boolean>((resolve) => (shouldShowSpeedbump ? setContinueSwap({ resolve }) : resolve(true))),
[shouldShowSpeedbump]
)
const onResolveSwap = useCallback( const onResolveSwap = useCallback(
(value: boolean) => { (value: boolean) => {
continueSwap?.resolve(value) continueSwap?.resolve(value)
...@@ -268,7 +238,6 @@ export default function TokenDetails({ ...@@ -268,7 +238,6 @@ export default function TokenDetails({
<RightPanel onClick={() => isBlockedToken && setOpenTokenSafetyModal(true)}> <RightPanel onClick={() => isBlockedToken && setOpenTokenSafetyModal(true)}>
<div style={{ pointerEvents: isBlockedToken ? 'none' : 'auto' }}> <div style={{ pointerEvents: isBlockedToken ? 'none' : 'auto' }}>
{widgetRemovalEnabled ? (
<Swap <Swap
chainId={pageChainId} chainId={pageChainId}
prefilledState={{ prefilledState={{
...@@ -278,16 +247,6 @@ export default function TokenDetails({ ...@@ -278,16 +247,6 @@ export default function TokenDetails({
onCurrencyChange={handleCurrencyChange} onCurrencyChange={handleCurrencyChange}
disableTokenInputs={pageChainId !== connectedChainId} disableTokenInputs={pageChainId !== connectedChainId}
/> />
) : (
<Widget
defaultTokens={{
[Field.INPUT]: widgetInputToken ?? undefined,
default: detailedToken ?? undefined,
}}
onDefaultTokenChange={navigateToWidgetSelectedToken}
onReviewSwapClick={onReviewSwapClick}
/>
)}
</div> </div>
{tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />} {tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />}
{detailedToken && <BalanceSummary token={detailedToken} />} {detailedToken && <BalanceSummary token={detailedToken} />}
......
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import {
InterfaceEventName,
InterfaceSectionName,
SwapEventName,
SwapPriceUpdateUserResponse,
} from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk'
import { Currency, TradeType } from '@uniswap/sdk-core'
import {
AddEthereumChainParameter,
DialogAnimationType,
EMPTY_TOKEN_LIST,
OnReviewSwapClick,
SwapWidget,
SwapWidgetSkeleton,
} from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import { useToggleAccountDrawer } from 'components/AccountDrawer'
import { useActiveLocale } from 'hooks/useActiveLocale'
import {
formatPercentInBasisPointsNumber,
formatSwapQuoteReceivedEventProperties,
formatToDecimal,
getDurationFromDateMilliseconds,
getPriceUpdateBasisPoints,
getTokenAddress,
} from 'lib/utils/analytics'
import { useCallback, useState } from 'react'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
import { computeRealizedPriceImpact } from 'utils/prices'
import { switchChain } from 'utils/switchChain'
import { DefaultTokens, SwapTokens, useSyncWidgetInputs } from './inputs'
import { useSyncWidgetSettings } from './settings'
import { DARK_THEME, LIGHT_THEME } from './theme'
import { useSyncWidgetTransactions } from './transactions'
const DEFAULT_WIDGET_WIDTH = 360
const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
function useWidgetTheme() {
return useIsDarkMode() ? DARK_THEME : LIGHT_THEME
}
interface WidgetProps {
defaultTokens: DefaultTokens
width?: number | string
onDefaultTokenChange?: (tokens: SwapTokens) => void
onReviewSwapClick?: OnReviewSwapClick
}
// TODO: Remove this component once the TDP is fully migrated to the swap component.
// eslint-disable-next-line import/no-unused-modules
export default function Widget({
defaultTokens,
width = DEFAULT_WIDGET_WIDTH,
onDefaultTokenChange,
onReviewSwapClick,
}: WidgetProps) {
const { connector, provider, chainId } = useWeb3React()
const locale = useActiveLocale()
const theme = useWidgetTheme()
const { inputs, tokenSelector } = useSyncWidgetInputs({
defaultTokens,
onDefaultTokenChange,
})
const { settings } = useSyncWidgetSettings()
const { transactions } = useSyncWidgetTransactions()
const toggleWalletDrawer = useToggleAccountDrawer()
const onConnectWalletClick = useCallback(() => {
toggleWalletDrawer()
return false // prevents the in-widget wallet modal from opening
}, [toggleWalletDrawer])
const onSwitchChain = useCallback(
// TODO(WEB-1757): Widget should not break if this rejects - upstream the catch to ignore it.
({ chainId }: AddEthereumChainParameter) => switchChain(connector, Number(chainId)).catch(() => undefined),
[connector]
)
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const [initialQuoteDate, setInitialQuoteDate] = useState<Date>()
const onInitialSwapQuote = useCallback(
(trade: Trade<Currency, Currency, TradeType>) => {
setInitialQuoteDate(new Date())
const eventProperties = {
// TODO(1416): Include undefined values.
...formatSwapQuoteReceivedEventProperties(
trade,
/* gasUseEstimateUSD= */ undefined,
/* fetchingSwapQuoteStartTime= */ undefined
),
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, eventProperties)
},
[trace]
)
const onApproveToken = useCallback(() => {
const input = inputs.value.INPUT
if (!input) return
const eventProperties = {
chain_id: input.chainId,
token_symbol: input.symbol,
token_address: getTokenAddress(input),
...trace,
}
sendAnalyticsEvent(InterfaceEventName.APPROVE_TOKEN_TXN_SUBMITTED, eventProperties)
}, [inputs.value.INPUT, trace])
const onExpandSwapDetails = useCallback(() => {
sendAnalyticsEvent(SwapEventName.SWAP_DETAILS_EXPANDED, { ...trace })
}, [trace])
const onSwapPriceUpdateAck = useCallback(
(stale: Trade<Currency, Currency, TradeType>, update: Trade<Currency, Currency, TradeType>) => {
const eventProperties = {
chain_id: update.inputAmount.currency.chainId,
response: SwapPriceUpdateUserResponse.ACCEPTED,
token_in_symbol: update.inputAmount.currency.symbol,
token_out_symbol: update.outputAmount.currency.symbol,
price_update_basis_points: getPriceUpdateBasisPoints(stale.executionPrice, update.executionPrice),
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_PRICE_UPDATE_ACKNOWLEDGED, eventProperties)
},
[trace]
)
const onSubmitSwapClick = useCallback(
(trade: Trade<Currency, Currency, TradeType>) => {
const eventProperties = {
// TODO(1416): Include undefined values.
estimated_network_fee_usd: undefined,
transaction_deadline_seconds: undefined,
token_in_address: getTokenAddress(trade.inputAmount.currency),
token_out_address: getTokenAddress(trade.outputAmount.currency),
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
token_in_amount_usd: undefined,
token_out_amount_usd: undefined,
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
allowed_slippage_basis_points: undefined,
is_auto_router_api: undefined,
is_auto_slippage: undefined,
chain_id: trade.inputAmount.currency.chainId,
duration_from_first_quote_to_swap_submission_milliseconds: getDurationFromDateMilliseconds(initialQuoteDate),
swap_quote_block_number: undefined,
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_SUBMITTED_BUTTON_CLICKED, eventProperties)
},
[initialQuoteDate, trace]
)
if (!(inputs.value.INPUT || inputs.value.OUTPUT)) {
return <WidgetSkeleton />
}
return (
<>
<div style={{ zIndex: 1, position: 'relative' }}>
<SwapWidget
hideConnectionUI
brandedFooter={false}
permit2
routerUrl={WIDGET_ROUTER_URL}
locale={locale}
theme={theme}
width={width}
defaultChainId={chainId}
onConnectWalletClick={onConnectWalletClick}
provider={provider}
onSwitchChain={onSwitchChain}
tokenList={EMPTY_TOKEN_LIST} // prevents loading the default token list, as we use our own token selector UI
{...inputs}
{...settings}
{...transactions}
onExpandSwapDetails={onExpandSwapDetails}
onReviewSwapClick={onReviewSwapClick}
onSubmitSwapClick={onSubmitSwapClick}
onSwapApprove={onApproveToken}
onInitialSwapQuote={onInitialSwapQuote}
onSwapPriceUpdateAck={onSwapPriceUpdateAck}
dialogOptions={{
pageCentered: true,
animationType: DialogAnimationType.FADE,
}}
onError={(error, errorInfo) => {
sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { error, errorInfo, ...trace })
}}
/>
</div>
{tokenSelector}
</>
)
}
function WidgetSkeleton({ width = DEFAULT_WIDGET_WIDTH }: { width?: number | string }) {
const theme = useWidgetTheme()
return <SwapWidgetSkeleton theme={theme} width={width} />
}
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import { isSupportedChain } from 'constants/chains'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useMemo, useState } from 'react'
const EMPTY_AMOUNT = ''
type SwapValue = Required<SwapController>['value']
export type SwapTokens = Pick<SwapValue, Field.INPUT | Field.OUTPUT> & { default?: Currency }
export type DefaultTokens = Partial<SwapTokens>
function missingDefaultToken(tokens: SwapTokens) {
if (!tokens.default) return false
return !tokens[Field.INPUT]?.equals(tokens.default) && !tokens[Field.OUTPUT]?.equals(tokens.default)
}
function currenciesEqual(a: Currency | undefined, b: Currency | undefined) {
if (a && b) {
return a.equals(b)
} else {
return !a && !b
}
}
function tokensEqual(a: SwapTokens | undefined, b: SwapTokens | undefined) {
if (!a || !b) {
return !a && !b
}
return (
currenciesEqual(a[Field.INPUT], b[Field.INPUT]) &&
currenciesEqual(a[Field.OUTPUT], b[Field.OUTPUT]) &&
currenciesEqual(a.default, b.default)
)
}
/**
* Integrates the Widget's inputs.
* Treats the Widget as a controlled component, using the app's own token selector for selection.
* Enforces that token is a part of the returned value.
*/
export function useSyncWidgetInputs({
defaultTokens,
onDefaultTokenChange,
}: {
defaultTokens: DefaultTokens
onDefaultTokenChange?: (tokens: SwapTokens) => void
}) {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const { chainId } = useWeb3React()
const previousChainId = usePrevious(chainId)
const [type, setType] = useState<SwapValue['type']>(TradeType.EXACT_INPUT)
const [amount, setAmount] = useState<SwapValue['amount']>(EMPTY_AMOUNT)
const [tokens, setTokens] = useState<SwapTokens>({
...defaultTokens,
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? defaultTokens.default,
})
// The most recent set of defaults, which can be used to check when the defaults are actually changing.
const baseTokens = usePrevious(defaultTokens)
useEffect(() => {
if (!tokensEqual(baseTokens, defaultTokens)) {
const input = defaultTokens[Field.INPUT]
const output = defaultTokens[Field.OUTPUT] ?? defaultTokens.default
setTokens({
...defaultTokens,
[Field.OUTPUT]: currenciesEqual(output, input) ? undefined : output,
})
}
}, [baseTokens, defaultTokens])
/**
* Clear the tokens if the chain changes.
*/
useEffect(() => {
if (chainId !== previousChainId && !!previousChainId && isSupportedChain(chainId)) {
setTokens({
...defaultTokens,
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? defaultTokens.default,
})
setAmount(EMPTY_AMOUNT)
}
}, [chainId, defaultTokens, previousChainId, tokens])
const onAmountChange = useCallback(
(field: Field, amount: string, origin?: 'max') => {
if (origin === 'max') {
sendAnalyticsEvent(SwapEventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED, { ...trace })
}
setType(field === Field.INPUT ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT)
setAmount(amount)
},
[trace]
)
const onSwitchTokens = useCallback(() => {
sendAnalyticsEvent(SwapEventName.SWAP_TOKENS_REVERSED, { ...trace })
setType((type) => invertTradeType(type))
setTokens((tokens) => ({
[Field.INPUT]: tokens[Field.OUTPUT],
[Field.OUTPUT]: tokens[Field.INPUT],
default: tokens.default,
}))
}, [trace])
const [selectingField, setSelectingField] = useState<Field>()
const onTokenSelectorClick = useCallback((field: Field) => {
setSelectingField(field)
return false
}, [])
const onTokenSelect = useCallback(
(selectingToken: Currency) => {
if (selectingField === undefined) return
const otherField = invertField(selectingField)
const isFlip = tokens[otherField]?.equals(selectingToken)
const update: SwapTokens = {
[selectingField]: selectingToken,
[otherField]: isFlip ? tokens[selectingField] : tokens[otherField],
default: tokens.default,
}
setType((type) => {
// If flipping the tokens, also flip the type/amount.
if (isFlip) {
return invertTradeType(type)
}
// Setting a new token should clear its amount, if it is set.
const activeField = type === TradeType.EXACT_INPUT ? Field.INPUT : Field.OUTPUT
if (selectingField === activeField) {
setAmount(() => EMPTY_AMOUNT)
}
return type
})
if (missingDefaultToken(update)) {
onDefaultTokenChange?.({
...update,
default: update[Field.OUTPUT] ?? selectingToken,
})
return
}
setTokens(update)
},
[onDefaultTokenChange, selectingField, tokens]
)
const tokenSelector = (
<CurrencySearchModal
isOpen={selectingField !== undefined}
onDismiss={() => setSelectingField(undefined)}
selectedCurrency={selectingField && tokens[selectingField]}
otherSelectedCurrency={selectingField && tokens[invertField(selectingField)]}
onCurrencySelect={onTokenSelect}
showCommonBases
/>
)
const value: SwapValue = useMemo(
() => ({
type,
amount,
// If the initial state has not yet been set, preemptively disable the widget by passing no tokens. Effectively,
// this resets the widget - avoiding rendering stale state - because with no tokens the skeleton will be rendered.
...(tokens[Field.INPUT] || tokens[Field.OUTPUT] ? tokens : undefined),
}),
[amount, tokens, type]
)
const valueHandlers: SwapEventHandlers = useMemo(
() => ({ onAmountChange, onSwitchTokens, onTokenSelectorClick }),
[onAmountChange, onSwitchTokens, onTokenSelectorClick]
)
return { inputs: { value, ...valueHandlers }, tokenSelector }
}
// TODO(zzmp): Move to @uniswap/widgets.
function invertField(field: Field) {
switch (field) {
case Field.INPUT:
return Field.OUTPUT
case Field.OUTPUT:
return Field.INPUT
}
}
// TODO(zzmp): Include in @uniswap/sdk-core (on TradeType, if possible).
function invertTradeType(tradeType: TradeType) {
switch (tradeType) {
case TradeType.EXACT_INPUT:
return TradeType.EXACT_OUTPUT
case TradeType.EXACT_OUTPUT:
return TradeType.EXACT_INPUT
}
}
import { Percent } from '@uniswap/sdk-core'
import { RouterPreference, Slippage, SwapController, SwapEventHandlers } from '@uniswap/widgets'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import { useCallback, useMemo, useState } from 'react'
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
import { SlippageTolerance } from 'state/user/types'
/**
* Integrates the Widget's settings, keeping the widget and app settings in sync.
* NB: This acts as an integration layer, so certain values are duplicated in order to translate
* between app and widget representations.
*/
export function useSyncWidgetSettings() {
const [appTtl, setAppTtl] = useUserTransactionTTL()
const [widgetTtl, setWidgetTtl] = useState<number | undefined>(appTtl / 60)
const onTransactionDeadlineChange = useCallback(
(widgetTtl: number | undefined) => {
setWidgetTtl(widgetTtl)
const appTtl = widgetTtl === undefined ? widgetTtl : widgetTtl * 60
setAppTtl(appTtl ?? DEFAULT_DEADLINE_FROM_NOW)
},
[setAppTtl]
)
const [appSlippage, setAppSlippage] = useUserSlippageTolerance()
const [widgetSlippage, setWidgetSlippage] = useState<string | undefined>(
appSlippage === SlippageTolerance.Auto ? undefined : appSlippage.toFixed(2)
)
const onSlippageChange = useCallback(
(widgetSlippage: Slippage) => {
setWidgetSlippage(widgetSlippage.max)
if (widgetSlippage.auto || !widgetSlippage.max) {
setAppSlippage(SlippageTolerance.Auto)
} else {
setAppSlippage(new Percent(Math.floor(Number(widgetSlippage.max) * 100), 10_000))
}
},
[setAppSlippage]
)
const [routerPreference, onRouterPreferenceChange] = useState(RouterPreference.API)
const onSettingsReset = useCallback(() => {
setWidgetTtl(undefined)
setAppTtl(DEFAULT_DEADLINE_FROM_NOW)
setWidgetSlippage(undefined)
setAppSlippage(SlippageTolerance.Auto)
}, [setAppSlippage, setAppTtl])
const settings: SwapController['settings'] = useMemo(() => {
const auto = appSlippage === SlippageTolerance.Auto
return {
slippage: { auto, max: widgetSlippage },
transactionTtl: widgetTtl,
routerPreference,
}
}, [appSlippage, widgetSlippage, widgetTtl, routerPreference])
const settingsHandlers: SwapEventHandlers = useMemo(
() => ({ onSettingsReset, onSlippageChange, onTransactionDeadlineChange, onRouterPreferenceChange }),
[onSettingsReset, onSlippageChange, onTransactionDeadlineChange, onRouterPreferenceChange]
)
return { settings: { settings, ...settingsHandlers } }
}
import { Theme } from '@uniswap/widgets'
import { darkTheme, lightTheme } from 'theme/colors'
import { Z_INDEX } from 'theme/zIndex'
const zIndex = {
modal: Z_INDEX.modal,
}
const fonts = {
fontFamily: 'Inter custom',
}
export const LIGHT_THEME: Theme = {
// surface
accent: lightTheme.accentAction,
accentSoft: lightTheme.accentActionSoft,
container: lightTheme.backgroundSurface,
module: lightTheme.backgroundModule,
interactive: lightTheme.backgroundInteractive,
outline: lightTheme.backgroundOutline,
dialog: lightTheme.backgroundBackdrop,
scrim: lightTheme.backgroundScrim,
// text
onAccent: lightTheme.white,
primary: lightTheme.textPrimary,
secondary: lightTheme.textSecondary,
hint: lightTheme.textTertiary,
onInteractive: lightTheme.accentTextDarkPrimary,
// shadow
deepShadow: lightTheme.deepShadow,
networkDefaultShadow: lightTheme.networkDefaultShadow,
// state
success: lightTheme.accentSuccess,
warning: lightTheme.accentWarning,
error: lightTheme.accentCritical,
...fonts,
zIndex,
}
export const DARK_THEME: Theme = {
// surface
accent: darkTheme.accentAction,
accentSoft: darkTheme.accentActionSoft,
container: darkTheme.backgroundSurface,
module: darkTheme.backgroundModule,
interactive: darkTheme.backgroundInteractive,
outline: darkTheme.backgroundOutline,
dialog: darkTheme.backgroundBackdrop,
scrim: darkTheme.backgroundScrim,
// text
onAccent: darkTheme.white,
primary: darkTheme.textPrimary,
secondary: darkTheme.textSecondary,
hint: darkTheme.textTertiary,
onInteractive: darkTheme.accentTextLightPrimary,
// shadow
deepShadow: darkTheme.deepShadow,
networkDefaultShadow: darkTheme.networkDefaultShadow,
// state
success: darkTheme.accentSuccess,
warning: darkTheme.accentWarning,
error: darkTheme.accentCritical,
...fonts,
zIndex,
}
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceEventName, InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
import {
OnTxSuccess,
TradeType,
Transaction,
TransactionEventHandlers,
TransactionInfo,
TransactionType,
TransactionType as WidgetTransactionType,
} from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import {
formatPercentInBasisPointsNumber,
formatSwapSignedAnalyticsEventProperties,
formatToDecimal,
getTokenAddress,
} from 'lib/utils/analytics'
import { useCallback, useMemo } from 'react'
import { useTransactionAdder } from 'state/transactions/hooks'
import {
ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo,
TransactionType as AppTransactionType,
WrapTransactionInfo,
} from 'state/transactions/types'
import { currencyId } from 'utils/currencyId'
import { computeRealizedPriceImpact } from 'utils/prices'
interface AnalyticsEventProps {
trade: Trade<Currency, Currency, TradeType>
gasUsed: string | undefined
blockNumber: number | undefined
hash: string | undefined
allowedSlippage: Percent
succeeded: boolean
}
const formatAnalyticsEventProperties = ({
trade,
hash,
allowedSlippage,
succeeded,
gasUsed,
blockNumber,
}: AnalyticsEventProps) => ({
estimated_network_fee_usd: gasUsed,
transaction_hash: hash,
token_in_address: getTokenAddress(trade.inputAmount.currency),
token_out_address: getTokenAddress(trade.outputAmount.currency),
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
? trade.inputAmount.currency.chainId
: undefined,
swap_quote_block_number: blockNumber,
succeeded,
})
/** Integrates the Widget's transactions, showing the widget's transactions in the app. */
export function useSyncWidgetTransactions() {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const { chainId } = useWeb3React()
const addTransaction = useTransactionAdder()
const onTxSubmit = useCallback(
(_hash: string, transaction: Transaction<TransactionInfo>) => {
const { type, response } = transaction.info
if (!type || !response) {
return
} else if (type === WidgetTransactionType.WRAP || type === WidgetTransactionType.UNWRAP) {
const { type, amount: transactionAmount } = transaction.info
const eventProperties = {
// get this info from widget handlers
token_in_address: getTokenAddress(transactionAmount.currency),
token_out_address: getTokenAddress(transactionAmount.currency.wrapped),
token_in_symbol: transactionAmount.currency.symbol,
token_out_symbol: transactionAmount.currency.wrapped.symbol,
chain_id: transactionAmount.currency.chainId,
amount: transactionAmount
? formatToDecimal(transactionAmount, transactionAmount?.currency.decimals)
: undefined,
type: type === WidgetTransactionType.WRAP ? TransactionType.WRAP : TransactionType.UNWRAP,
...trace,
}
sendAnalyticsEvent(InterfaceEventName.WRAP_TOKEN_TXN_SUBMITTED, eventProperties)
const { amount } = transaction.info
addTransaction(response, {
type: AppTransactionType.WRAP,
unwrapped: type === WidgetTransactionType.UNWRAP,
currencyAmountRaw: amount.quotient.toString(),
chainId,
} as WrapTransactionInfo)
} else if (type === WidgetTransactionType.SWAP) {
const { slippageTolerance, trade, tradeType } = transaction.info
const eventProperties = {
...formatSwapSignedAnalyticsEventProperties({
trade,
// TODO: add once Widgets adds fiat values to callback
fiatValues: { amountIn: undefined, amountOut: undefined },
txHash: transaction.receipt?.transactionHash ?? '',
}),
...trace,
}
sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, eventProperties)
const baseTxInfo = {
type: AppTransactionType.SWAP,
tradeType,
inputCurrencyId: currencyId(trade.inputAmount.currency),
outputCurrencyId: currencyId(trade.outputAmount.currency),
}
if (tradeType === TradeType.EXACT_OUTPUT) {
addTransaction(response, {
...baseTxInfo,
maximumInputCurrencyAmountRaw: trade.maximumAmountIn(slippageTolerance).quotient.toString(),
outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
} as ExactOutputSwapTransactionInfo)
} else {
addTransaction(response, {
...baseTxInfo,
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(slippageTolerance).quotient.toString(),
} as ExactInputSwapTransactionInfo)
}
}
},
[addTransaction, chainId, trace]
)
const onTxSuccess: OnTxSuccess = useCallback((hash: string, tx) => {
if (tx.info.type === TransactionType.SWAP) {
const { trade, slippageTolerance } = tx.info
sendAnalyticsEvent(
SwapEventName.SWAP_TRANSACTION_COMPLETED,
formatAnalyticsEventProperties({
trade,
hash,
gasUsed: tx.receipt?.gasUsed?.toString(),
blockNumber: tx.receipt?.blockNumber,
allowedSlippage: slippageTolerance,
succeeded: tx.receipt?.status === 1,
})
)
}
}, [])
const txHandlers: TransactionEventHandlers = useMemo(() => ({ onTxSubmit, onTxSuccess }), [onTxSubmit, onTxSuccess])
return { transactions: { ...txHandlers } }
}
...@@ -6,5 +6,4 @@ export enum FeatureFlag { ...@@ -6,5 +6,4 @@ export enum FeatureFlag {
permit2 = 'permit2', permit2 = 'permit2',
fiatOnRampButtonOnSwap = 'fiat_on_ramp_button_on_swap_page', fiatOnRampButtonOnSwap = 'fiat_on_ramp_button_on_swap_page',
detailsV2 = 'details_v2', detailsV2 = 'details_v2',
removeWidget = 'remove_widget_tdp',
} }
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
export function useWidgetRemovalFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.removeWidget, BaseVariant.Control)
}
export function useWidgetRemovalEnabled(): boolean {
return useWidgetRemovalFlag() === BaseVariant.Enabled
}
export { BaseVariant as WidgetRemovalVariant }
...@@ -12,7 +12,7 @@ import { isL2ChainId } from 'utils/chains' ...@@ -12,7 +12,7 @@ import { isL2ChainId } from 'utils/chains'
import { useAllLists, useCombinedActiveList, useCombinedTokenMapFromUrls } from '../state/lists/hooks' import { useAllLists, useCombinedActiveList, useCombinedTokenMapFromUrls } from '../state/lists/hooks'
import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo' import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo'
import { deserializeToken, useUserAddedTokens, useUserAddedTokensOnChain } from '../state/user/hooks' import { deserializeToken, useUserAddedTokens } from '../state/user/hooks'
import { useUnsupportedTokenList } from './../state/lists/hooks' import { useUnsupportedTokenList } from './../state/lists/hooks'
type Maybe<T> = T | null | undefined type Maybe<T> = T | null | undefined
...@@ -182,20 +182,6 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool ...@@ -182,20 +182,6 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
return !!userAddedTokens.find((token) => currency.equals(token)) return !!userAddedTokens.find((token) => currency.equals(token))
} }
// Check if currency on specific chain is included in custom list from user storage
export function useIsUserAddedTokenOnChain(
address: string | undefined | null,
chain: number | undefined | null
): boolean {
const userAddedTokens = useUserAddedTokensOnChain(chain)
if (!address || !chain) {
return false
}
return !!userAddedTokens.find((token) => token.address === address)
}
// undefined if invalid or does not exist // undefined if invalid or does not exist
// null if loading or null was passed // null if loading or null was passed
// otherwise returns the token // otherwise returns the token
......
...@@ -52,9 +52,7 @@ export function useUniversalRouterSwapCallback( ...@@ -52,9 +52,7 @@ export function useUniversalRouterSwapCallback(
const analyticsContext = useTrace() const analyticsContext = useTrace()
return useCallback(async (): Promise<TransactionResponse> => { return useCallback(async (): Promise<TransactionResponse> => {
return trace( return trace('swap.send', async ({ setTraceData, setTraceStatus, setTraceError }) => {
'swap.send',
async ({ setTraceData, setTraceStatus, setTraceError }) => {
try { try {
if (!account) throw new Error('missing account') if (!account) throw new Error('missing account')
if (!chainId) throw new Error('missing chainId') if (!chainId) throw new Error('missing chainId')
...@@ -120,9 +118,7 @@ export function useUniversalRouterSwapCallback( ...@@ -120,9 +118,7 @@ export function useUniversalRouterSwapCallback(
throw new Error(swapErrorToUserReadableMessage(swapError)) throw new Error(swapErrorToUserReadableMessage(swapError))
} }
}, })
{ tags: { is_widget: false } }
)
}, [ }, [
account, account,
analyticsContext, analyticsContext,
......
...@@ -6,7 +6,6 @@ import { InterfacePageName } from '@uniswap/analytics-events' ...@@ -6,7 +6,6 @@ import { InterfacePageName } from '@uniswap/analytics-events'
import { formatPrice, NumberType } from '@uniswap/conedison/format' import { formatPrice, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Fraction, Percent, Price, Token } from '@uniswap/sdk-core'
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk' import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
import { SupportedChainId } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics' import { sendEvent } from 'components/analytics'
import Badge from 'components/Badge' import Badge from 'components/Badge'
...@@ -20,7 +19,7 @@ import { RowBetween, RowFixed } from 'components/Row' ...@@ -20,7 +19,7 @@ import { RowBetween, RowFixed } from 'components/Row'
import { Dots } from 'components/swap/styleds' import { Dots } from 'components/swap/styleds'
import Toggle from 'components/Toggle' import Toggle from 'components/Toggle'
import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal' import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal'
import { CHAIN_IDS_TO_NAMES, isSupportedChain } from 'constants/chains' import { CHAIN_IDS_TO_NAMES, isSupportedChain, SupportedChainId } from 'constants/chains'
import { isGqlSupportedChain } from 'graphql/data/util' import { isGqlSupportedChain } from 'graphql/data/util'
import { useToken } from 'hooks/Tokens' import { useToken } from 'hooks/Tokens'
import { useV3NFTPositionManagerContract } from 'hooks/useContract' import { useV3NFTPositionManagerContract } from 'hooks/useContract'
......
...@@ -115,7 +115,6 @@ export const routingApi = createApi({ ...@@ -115,7 +115,6 @@ export const routingApi = createApi({
isAutoRouter: isAutoRouter:
args.routerPreference === RouterPreference.AUTO || args.routerPreference === RouterPreference.API, args.routerPreference === RouterPreference.AUTO || args.routerPreference === RouterPreference.API,
}, },
tags: { is_widget: false },
} }
) )
}, },
......
...@@ -209,7 +209,7 @@ export function useAddUserToken(): (token: Token) => void { ...@@ -209,7 +209,7 @@ export function useAddUserToken(): (token: Token) => void {
) )
} }
export function useUserAddedTokensOnChain(chainId: number | undefined | null): Token[] { function useUserAddedTokensOnChain(chainId: number | undefined | null): Token[] {
const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens) const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens)
return useMemo(() => { return useMemo(() => {
......
...@@ -38,12 +38,13 @@ describe('trace', () => { ...@@ -38,12 +38,13 @@ describe('trace', () => {
}) })
it('records transaction', async () => { it('records transaction', async () => {
const metadata = { data: { a: 'a', b: 2 }, tags: { is_widget: true } } const metadata = { data: { a: 'a', b: 2 }, tags: { test_tag: true } }
// @ts-ignore test_tag is not an expected key for `tags` but force it for testing purposes
await trace('test', () => Promise.resolve(), metadata) await trace('test', () => Promise.resolve(), metadata)
const transaction = getTransaction() const transaction = getTransaction()
expect(transaction.name).toBe('test') expect(transaction.name).toBe('test')
expect(transaction.data).toEqual({ a: 'a', b: 2 }) expect(transaction.data).toEqual({ a: 'a', b: 2 })
expect(transaction.tags).toEqual({ is_widget: true }) expect(transaction.tags).toEqual({ test_tag: true })
}) })
describe('defaults status', () => { describe('defaults status', () => {
...@@ -77,11 +78,12 @@ describe('trace', () => { ...@@ -77,11 +78,12 @@ describe('trace', () => {
describe('setTraceTag', () => { describe('setTraceTag', () => {
it('sets a transaction tag', async () => { it('sets a transaction tag', async () => {
await trace('test', ({ setTraceTag }) => { await trace('test', ({ setTraceTag }) => {
setTraceTag('is_widget', true) // @ts-ignore test_tag is not an expected key for `tags` but force it for testing purposes
setTraceTag('test_tag', true)
return Promise.resolve() return Promise.resolve()
}) })
const transaction = getTransaction() const transaction = getTransaction()
expect(transaction.tags).toEqual({ is_widget: true }) expect(transaction.tags).toEqual({ test_tag: true })
}) })
}) })
...@@ -166,7 +168,8 @@ describe('trace', () => { ...@@ -166,7 +168,8 @@ describe('trace', () => {
describe('traceChild', () => { describe('traceChild', () => {
it('starts a span under a transaction', async () => { it('starts a span under a transaction', async () => {
await trace('test', ({ traceChild }) => { await trace('test', ({ traceChild }) => {
traceChild('child', () => Promise.resolve(), { data: { e: 'e' }, tags: { is_widget: true } }) // @ts-ignore test_tag is not an expected key for `tags` but force it for testing purposes
traceChild('child', () => Promise.resolve(), { data: { e: 'e' }, tags: { test_tag: true } })
return Promise.resolve() return Promise.resolve()
}) })
const transaction = getTransaction() const transaction = getTransaction()
...@@ -174,7 +177,7 @@ describe('trace', () => { ...@@ -174,7 +177,7 @@ describe('trace', () => {
assert(span) assert(span)
expect(span.op).toBe('child') expect(span.op).toBe('child')
expect(span.data).toEqual({ e: 'e' }) expect(span.data).toEqual({ e: 'e' })
expect(span.tags).toEqual({ is_widget: true }) expect(span.tags).toEqual({ test_tag: true })
}) })
}) })
}) })
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
import { Span, SpanStatusType } from '@sentry/tracing' import { Span, SpanStatusType } from '@sentry/tracing'
type TraceTags = { // Modify this type if you want to add any Trace tags.
is_widget: boolean type TraceTags = Record<string, never>
}
interface TraceMetadata { interface TraceMetadata {
/** Arbitrary data stored on a trace. */ /** Arbitrary data stored on a trace. */
......
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