ci(release): publish latest release

parent 168fd79b
* @uniswap/web-admins
IPFS hash of the deployment:
- CIDv0: `QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV`
- CIDv1: `bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
You can also access the Uniswap Interface from an IPFS gateway.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna.ipfs.dweb.link/
- https://bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna.ipfs.cf-ipfs.com/
- [ipfs://QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV/](ipfs://QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV/)
### 5.30.1 (2024-05-31)
Excited to share some new updates! Here’s what’s new:
Expanded support for NFTs — In addition to mainnet Ethereum, you are now able to see NFTs across all other networks that we currently support, including: Polygon, Arbitrum, Optimism, Base, BSC, and Blast.
Improved Unicons — We gave your wallet’s unique Unicon a makeover. Check out the rest of your accounts to see your upgraded icons.
web/5.30.1
\ No newline at end of file
mobile/1.28.2
\ No newline at end of file
......@@ -176,7 +176,18 @@ These are some tools you might want to familiarize yourself with to understand t
## Migrations
We use `redux-persist` to persist the Redux state between user sessions. Most of this state is shared between the mobile app and the extension. Please review the [Wallet Migrations README](../../packages/wallet/src/state//README.md) for details on how to write migrations when you add or remove anything from the Redux state structure.
We use `redux-persist` to persist Redux state between user sessions. When the Redux state schema is altered, a migration may be needed to transfer the existing persisted state to the new Redux schema. Failing to define a migration results in the app defaulting to the persisted schema, which will very likely cause `undefined` errors because the code has references to Redux state properties that were dropped in favor the persisted schema.
### When to define a migration
Anytime a required property is added or any property is renamed or deleted to/from Redux state. Migrations are not necessary when optional properties are added to an existing slice. Make sure to always add new required properties to the `schema.ts` file as well.
### How to migrate
1. Increment the `version` of `persistConfig` defined within `store.ts`
2. Create a migration function within `migrations.ts`. The migration key should be the same as the `version` defined in the previous step
3. Write a test for your migration within `migrations.test.ts`
4. Create a new schema within `schema.ts` and ensure it is being exported by the `getSchema` function at the bottom of the file
## Troubleshooting
......
......@@ -136,12 +136,12 @@ android {
}
beta {
applicationIdSuffix ".beta"
versionName "1.28"
versionName "1.28.2"
dimension "variant"
}
prod {
dimension "variant"
versionName "1.28"
versionName "1.28.2"
}
}
......
......@@ -2536,7 +2536,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
......@@ -2628,7 +2628,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
......@@ -2713,7 +2713,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
......@@ -2799,7 +2799,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
......@@ -2873,7 +2873,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3103,7 +3103,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
......@@ -3207,7 +3207,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3278,7 +3278,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
......
......@@ -9,6 +9,17 @@ import { localizeMock as mockRNLocalize } from 'react-native-localize/mock'
import { AppearanceSettingType } from 'wallet/src/features/appearance/slice'
import { mockLocalizationContext } from 'wallet/src/test/mocks/utils'
// avoids polluting console in test runs, while keeping important log levels
global.console = {
...console,
// uncomment to ignore a specific log level
log: jest.fn(),
debug: jest.fn(),
info: jest.fn(),
// warn: jest.fn(),
// error: jest.fn(),
}
// Mock Sentry crash reporting
jest.mock('@sentry/react-native', () => ({
init: () => jest.fn(),
......
......@@ -104,7 +104,6 @@ import {
} from 'wallet/src/features/wallet/accounts/types'
import { initialWalletState, SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { createMigrate } from 'wallet/src/state/createMigrate'
import { getAllKeysOfNestedObject } from 'wallet/src/state/testUtils'
import {
fiatPurchaseTransactionInfo,
signerMnemonicAccount,
......@@ -125,6 +124,26 @@ const fiatOnRampTxDetailsFailed = transactionDetails({
}),
})
// helps with object assignment
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getAllKeysOfNestedObject = (obj: any, prefix = ''): string[] => {
const keys = Object.keys(obj)
if (!keys.length && prefix !== '') {
return [prefix.slice(0, -1)]
}
return keys.reduce<string[]>((res, el) => {
if (Array.isArray(obj[el])) {
return [...res]
}
if (typeof obj[el] === 'object' && obj[el] !== null) {
return [...res, ...getAllKeysOfNestedObject(obj[el], prefix + el + '.')]
}
return [...res, prefix + el]
}, [])
}
describe('Redux state migrations', () => {
it('is able to perform all migrations starting from the initial schema', async () => {
const initialSchemaStub = {
......@@ -185,10 +204,6 @@ describe('Redux state migrations', () => {
},
}
if (!migratedSchema) {
throw new Error('Migrated schema is undefined')
}
const migratedSchemaKeys = new Set(getAllKeysOfNestedObject(migratedSchema))
const latestSchemaKeys = new Set(getAllKeysOfNestedObject(getSchema()))
const initialStateKeys = new Set(getAllKeysOfNestedObject(initialState))
......
......@@ -19,7 +19,6 @@ import {
} from 'wallet/src/features/transactions/types'
import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types'
import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { removeWalletIsUnlockedState } from 'wallet/src/state/sharedMigrations'
export const OLD_DEMO_ACCOUNT_ADDRESS = '0xdd0E380579dF30E38524F9477808d9eE37E2dEa6'
......@@ -877,7 +876,10 @@ export const migrations = {
return newState
},
63: removeWalletIsUnlockedState,
}
63: function removeWalletIsUnlockedState(state: any) {
const newState = { ...state }
delete newState.wallet.isUnlocked
export const MOBILE_STATE_VERSION = 63
return newState
},
}
......@@ -3,7 +3,7 @@ import { isRejectedWithValue } from '@reduxjs/toolkit'
import * as Sentry from '@sentry/react'
import { MMKV } from 'react-native-mmkv'
import { Storage, persistReducer, persistStore } from 'redux-persist'
import { MOBILE_STATE_VERSION, migrations } from 'src/app/migrations'
import { migrations } from 'src/app/migrations'
import { isNonJestDev } from 'utilities/src/environment'
import { logger } from 'utilities/src/logger/logger'
import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api'
......@@ -66,7 +66,7 @@ export const persistConfig = {
key: 'root',
storage: reduxStorage,
whitelist,
version: MOBILE_STATE_VERSION,
version: 63,
migrate: createMigrate(migrations),
}
......
......@@ -11,9 +11,11 @@ import {
NativeScrollEvent,
NativeSyntheticEvent,
ScrollView,
StyleProp,
View,
ViewStyle,
} from 'react-native'
import { useDerivedValue } from 'react-native-reanimated'
import { AnimatedStyle, useDerivedValue } from 'react-native-reanimated'
import { ScrollDownOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay'
import { Button, Flex, useDeviceInsets } from 'ui/src'
import { spacing } from 'ui/src/theme'
......@@ -30,6 +32,7 @@ type ModalWithOverlayProps = PropsWithChildren<
onReject: () => void
onConfirm: () => void
disableConfirm?: boolean
contentContainerStyle?: StyleProp<AnimatedStyle<StyleProp<ViewStyle>>>
}
>
......@@ -48,6 +51,7 @@ export function ModalWithOverlay({
onReject,
onConfirm,
disableConfirm,
contentContainerStyle,
...bottomSheetModalProps
}: ModalWithOverlayProps): JSX.Element {
const scrollViewRef = useRef<ScrollView>(null)
......@@ -114,10 +118,12 @@ export function ModalWithOverlay({
<BottomSheetModal overrideInnerContainer {...bottomSheetModalProps}>
<BottomSheetScrollView
ref={scrollViewRef}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing36,
}}
contentContainerStyle={
contentContainerStyle ?? {
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing36,
}
}
showsVerticalScrollIndicator={false}
onLayout={handleScrollViewLayout}
onScroll={handleScroll}>
......
import { useBottomSheetInternal } from '@gorhom/bottom-sheet'
import { useTranslation } from 'react-i18next'
import Animated, { useAnimatedStyle } from 'react-native-reanimated'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { RequestDetailsContent } from 'src/components/WalletConnect/RequestModal/RequestDetails'
import { useUwuLinkContractAllowlist } from 'src/components/WalletConnect/ScanSheet/util'
import { SignRequest } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, useIsDarkMode } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
type Props = {
onClose: () => void
onConfirm: () => void
onReject: () => void
request: SignRequest
}
export function KidSuperCheckinModal({
onClose,
onConfirm,
onReject,
request,
}: Props): JSX.Element {
const { t } = useTranslation()
return (
<ModalWithOverlay
confirmationButtonText={t('common.button.checkin')}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing8,
}}
name={ModalName.UwULinkErc20SendModal}
scrollDownButtonText={t('walletConnect.request.button.scrollDown')}
onClose={onClose}
onConfirm={onConfirm}
onReject={onReject}>
<KidSuperCheckinModalContent request={request} />
</ModalWithOverlay>
)
}
function useUniswapCafeLogo(): string | undefined {
const isDarkMode = useIsDarkMode()
const uwuLinkContractAllowlist = useUwuLinkContractAllowlist()
const logos = uwuLinkContractAllowlist.tokenRecipients.find(
(recipient) => recipient.name === 'Uniswap Cafe'
)?.logo
if (!logos) {
return
}
return isDarkMode ? logos.dark : logos.light
}
function KidSuperCheckinModalContent({ request }: { request: SignRequest }): JSX.Element {
const { animatedFooterHeight } = useBottomSheetInternal()
const bottomSpacerStyle = useAnimatedStyle(() => ({
height: animatedFooterHeight.value,
}))
const logo = useUniswapCafeLogo()
return (
<Flex centered gap="$spacing12" justifyContent="space-between" pb="$spacing12">
<Flex centered gap="$spacing20">
{logo && <RemoteImage height={50} uri={logo} width={200} />}
<Flex
centered
borderColor="$surface3"
borderRadius="$rounded20"
borderWidth={1}
gap="$spacing12"
px="$spacing24"
py="$spacing24">
<RequestDetailsContent request={request} />
</Flex>
</Flex>
<Animated.View style={bottomSpacerStyle} />
</Flex>
)
}
......@@ -194,7 +194,7 @@ function isSignTypedDataRequest(request: WalletConnectRequest): request is SignR
return request.type === EthMethod.SignTypedData || request.type === EthMethod.SignTypedDataV4
}
function RequestDetailsContent({ request }: Props): JSX.Element {
export function RequestDetailsContent({ request }: Props): JSX.Element {
const { t } = useTranslation()
if (isSignTypedDataRequest(request)) {
......
......@@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next'
import Animated, { useAnimatedStyle } from 'react-native-reanimated'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Text } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { Flex, Text, useIsDarkMode } from 'ui/src'
import { iconSizes, spacing } from 'ui/src/theme'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { NumberType } from 'utilities/src/format/types'
import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo'
import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader'
import { NetworkFee } from 'wallet/src/components/network/NetworkFee'
import { CHAIN_INFO } from 'wallet/src/constants/chains'
import { GasFeeResult } from 'wallet/src/features/gas/types'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api'
import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo'
......@@ -48,6 +51,10 @@ export function UwULinkErc20SendModal({
return (
<ModalWithOverlay
confirmationButtonText={t('common.button.pay')}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing8,
}}
disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds}
name={ModalName.UwULinkErc20SendModal}
scrollDownButtonText={t('walletConnect.request.button.scrollDown')}
......@@ -82,10 +89,12 @@ function UwULinkErc20SendModalContent({
currencyInfo: Maybe<CurrencyInfo>
}): JSX.Element {
const { t } = useTranslation()
const isDarkMode = useIsDarkMode()
const { animatedFooterHeight } = useBottomSheetInternal()
const bottomSpacerStyle = useAnimatedStyle(() => ({
height: animatedFooterHeight.value,
}))
const { convertFiatAmountFormatted } = useLocalizationContext()
const { chainId, isStablecoin } = request
const nativeCurrency = chainId && NativeCurrency.onChain(chainId)
......@@ -104,10 +113,22 @@ function UwULinkErc20SendModalContent({
currency: { name, symbol, decimals },
} = currencyInfo
const recipientLogoUrl = isDarkMode
? request?.recipient?.logo?.dark
: request?.recipient?.logo?.light
const formattedTokenAmount = isStablecoin
? convertFiatAmountFormatted(formatUnits(request.amount, decimals), NumberType.FiatStandard)
: formatUnits(request.amount, decimals)
return (
<Flex centered gap="$spacing12" justifyContent="space-between">
<Text variant="subheading1">{request.recipient.name}</Text>
<Flex centered flex={1} gap="$spacing12" py="$spacing16">
{recipientLogoUrl ? (
<RemoteImage height={50} uri={recipientLogoUrl} width={200} />
) : (
<Text variant="subheading1">{request.recipient.name}</Text>
)}
<Flex centered flex={1} gap="$spacing12" py="$spacing36">
{!hasSufficientTokenFunds && (
<Text color="red">
{t('uwulink.error.insufficientTokens', {
......@@ -116,11 +137,10 @@ function UwULinkErc20SendModalContent({
})}
</Text>
)}
<Text fontSize={64} my="$spacing4" pt={42}>{`${isStablecoin ? '$' : ''}${formatUnits(
request.amount,
decimals
)}`}</Text>
<Flex row gap="$spacing4">
<Text fontSize={64} my="$spacing4" pt={42}>
{formattedTokenAmount}
</Text>
<Flex row gap="$spacing8">
<TokenLogo
chainId={chainId}
name={name}
......@@ -128,7 +148,9 @@ function UwULinkErc20SendModalContent({
symbol={symbol}
url={logoUrl}
/>
<Text>{symbol}</Text>
<Text color="$neutral2">
{formatUnits(request.amount, decimals)} {symbol}
</Text>
</Flex>
</Flex>
<Flex alignSelf="stretch" borderTopColor="$surface3" borderTopWidth={1} pt="$spacing16">
......
......@@ -5,6 +5,7 @@ import React, { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { KidSuperCheckinModal } from 'src/components/WalletConnect/RequestModal/KidSuperCheckinModal'
import {
WalletConnectRequestModalContent,
methodCostsGas,
......@@ -238,6 +239,18 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem
)
}
// KidSuper Uniswap Cafe check-in screen
if (request.type === EthMethod.PersonalSign && request.dapp.name === 'Uniswap Cafe') {
return (
<KidSuperCheckinModal
request={request}
onClose={handleClose}
onConfirm={onConfirmPress}
onReject={onReject}
/>
)
}
return (
<ModalWithOverlay
confirmationButtonText={
......
......@@ -226,6 +226,7 @@ export function WalletConnectModal({
recipient: {
address: parsedUwulinkRequest.recipient,
name: tokenRecipient?.name ?? '',
logo: tokenRecipient?.logo,
},
amount: parsedUwulinkRequest.amount,
tokenAddress: parsedUwulinkRequest.tokenAddress,
......
......@@ -51,7 +51,10 @@ type UwULinkAllowlistItem = {
chainId: number
address: string
name: string
icon?: string
logo?: {
dark?: string
light?: string
}
}
type UwULinkAllowlist = {
......
......@@ -55,6 +55,16 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams
let signature = ''
if (method === EthMethod.PersonalSign || method === EthMethod.EthSign) {
signature = yield* call(signMessage, params.message, account, signerManager)
// TODO: add `isCheckIn` type to uwulink request info so that this can be generalized
if (params.dapp.source === 'uwulink' && params.dapp.name === 'Uniswap Cafe') {
yield* put(
pushNotification({
type: AppNotificationType.Success,
title: 'Checked in',
})
)
}
} else if (method === EthMethod.SignTypedData || method === EthMethod.SignTypedDataV4) {
signature = yield* call(signTypedDataMessage, params.message, account, signerManager)
} else if (
......
......@@ -51,6 +51,10 @@ export interface UwuLinkErc20Request extends BaseRequest {
recipient: {
address: string
name: string
logo?: {
dark?: string
light?: string
}
}
tokenAddress: string
amount: string
......
......@@ -292,7 +292,15 @@ describe('UniswapX v1', () => {
submitUniswapXOrder()
cy.get(getTestSelector('confirmation-close-icon')).click()
cy.interceptGraphqlOperation('PortfolioBalancesWeb', 'mini-portfolio/tokens.json')
cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === 'PortfolioBalancesWeb') {
req.alias = 'PortfolioBalancesWeb'
// Reply with a fixture to speed up test
req.reply({ fixture: 'mini-portfolio/tokens.json' })
} else {
req.continue()
}
})
// Expect balances to refetch after filling
cy.wait('@orderStatusOpen')
......
......@@ -287,7 +287,15 @@ describe('UniswapX v2', () => {
submitUniswapXOrder()
cy.get(getTestSelector('confirmation-close-icon')).click()
cy.interceptGraphqlOperation('PortfolioBalancesWeb', 'mini-portfolio/tokens.json')
cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === 'PortfolioBalancesWeb') {
req.alias = 'PortfolioBalancesWeb'
// Reply with a fixture to speed up test
req.reply({ fixture: 'mini-portfolio/tokens.json' })
} else {
req.continue()
}
})
// Expect balances to refetch after filling
cy.wait('@orderStatusOpen')
......
......@@ -80,10 +80,9 @@ describe('Token details', () => {
cy.contains(shortenAddress('0x1eFBB78C8b917f67986BcE54cE575069c0143681')).should('exist')
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
cy.get('[data-cy="token-safety-message"]').contains(/Warning/)
cy.get('[data-cy="token-safety-description"]').contains(
/This token isn’t traded on leading U.S. centralized exchanges or frequently swapped on Uniswap./
)
cy.get('[data-cy="token-safety-message"]')
.should('include.text', 'Warning')
.and('include.text', "This token isn't traded on leading U.S. centralized exchanges")
})
describe('swapping', () => {
......
......@@ -645,14 +645,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x273f7f8e6489682df756151f5525576e322d51a3</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x77372a4cc66063575b05b44481f059be356964a4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.7</priority>
</url>
</urlset>
\ No newline at end of file
......@@ -4355,219 +4355,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x7baece5d47f1bc5e1953fbe0e9931d54dab6d810</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x83abecf7204d5afc1bea5df734f085f2535a9976</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0xb2eb5849e2606f99fc492e9add0103c667f806d3</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0x53c6ca2597711ca7a73b6921faf4031eedf71339</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0xd35937ecd47b04a1474f8569f457fc5ac395921a</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x6b75f2189f0e11c52e814e09e280eb1a9a8a094a</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0xb372b5abdb7c2ab8ad9e614be9835a42d0009153</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0xf369277650ad6654f25412ea8bfbd5942733babc</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x4898cf312fbff8814cab80a8d7f6ee5ad0dc73fb</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x5e78afc6c804d4382bede3a0712d210e657e9b4f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x86b211ca7915a0c8d4659dd98242d9e801d88ab4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0xb637f7c82fd774c280e23cebc725e7cd807c66d0</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0xd249c43faabc58d6dd4b0a4de598b5a956c5d8d7</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x1fbae785ce68b79f7ed4f7b27c3af3ef0e0bc3d4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x3c1376fb8487da57d4ffb263d9d01b578c7b586b</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x7b24bed19856f4bb1d4c0421cfb328026cd936bd</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x7cf887a863d81e6a483ee947dee05cb51914923c</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x588c8cf031809486f015908864ee8699b44017e4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x3987d38a4ff8520a8ef6bcc6f98d6da8bcd69b89</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0xde67d05242b18af00b28678db34feec883cc9cd6</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0x4a5a8b0108f446df7c1c8a459fcfb54e844b7343</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0xf6ba006abf768ab2d1b5bba2d22d9f13eb1269d4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
</urlset>
\ No newline at end of file
This diff is collapsed.
......@@ -47,7 +47,7 @@ function useCancelLimitsDialogContent(
<Plural
value={orders.length}
one={t('common.limit.cancel')}
other={t(`common.limit.cancel.amount`, { count: orders.length })}
other={t(`common.limit.cancel.amount`, { amount: orders.length })}
/>
),
icon: <Slash />,
......@@ -143,7 +143,7 @@ export function CancelLimitsDialog(
}
buttonsConfig={{
left: {
title: <Trans i18nKey="common.neverMind" />,
title: <Trans i18nKey="common.nevermind" />,
onClick: onCancel,
textColor: 'neutral1',
},
......
......@@ -2,7 +2,7 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activ
import Column from 'components/Column'
import { TimeForwardIcon } from 'components/Icons/TimeForward'
import Row from 'components/Row'
import { Plural, Trans, t } from 'i18n'
import { Plural, t, Trans } from 'i18n'
import { ChevronRight } from 'react-feather'
import styled, { useTheme } from 'styled-components'
import { ClickableStyle, ThemedText } from 'theme/components'
......
......@@ -3,7 +3,7 @@ import Row from 'components/Row'
import Tooltip, { TooltipSize } from 'components/Tooltip'
import { useScreenSize } from 'hooks/screenSize'
import useCopyClipboard from 'hooks/useCopyClipboard'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { useCallback, useState } from 'react'
import { Copy } from 'react-feather'
import { Link } from 'react-router-dom'
......
......@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme/components'
const StyledExternalLink = styled(ExternalLink)`
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......
......@@ -247,7 +247,7 @@ function CandlestickTooltip({ data }: { data: PriceChartData }) {
<div>{formatFiatPrice({ price: data.low })}</div>
</RowBetween>
<RowBetween gap="sm">
<Trans i18nKey="common.close" />
<Trans i18nKey="chart.price.close" />
<div>{formatFiatPrice({ price: data.close })}</div>
</RowBetween>
</TooltipText>
......
......@@ -14,7 +14,7 @@ describe('ConfirmSwapModal/Error', () => {
['limit order', PendingModalError.CONFIRMATION_ERROR, LIMIT_ORDER_TRADE, 'Limit failed'],
['limit order', PendingModalError.WRAP_ERROR, LIMIT_ORDER_TRADE, 'Wrap failed'],
])('renders %p correctly, with error= %p', async (testCaseName, errorType, trade, expectedError) => {
const { asFragment } = render(<Error errorType={errorType} trade={trade} onRetry={jest.fn()} showTrade={true} />)
const { asFragment } = render(<Error errorType={errorType} trade={trade} onRetry={jest.fn()} />)
expect(asFragment()).toMatchSnapshot()
expect(screen.getByText(expectedError)).toBeInTheDocument()
})
......
......@@ -4,6 +4,7 @@ import { SwapResult } from 'hooks/useSwapCallback'
import { Trans } from 'i18n'
import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTrade } from 'state/routing/utils'
import { useTheme } from 'styled-components'
import { TradeSummary } from 'components/ConfirmSwapModal/TradeSummary'
import { DialogButtonType, DialogContent } from 'components/Dialog/Dialog'
......@@ -23,7 +24,6 @@ export enum PendingModalError {
interface ErrorModalContentProps {
errorType: PendingModalError
trade?: InterfaceTrade
showTrade?: boolean
swapResult?: SwapResult
onRetry: () => void
}
......@@ -49,7 +49,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
case PendingModalError.XV2_HARD_QUOTE_ERROR:
return {
title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.uniswapX" />,
message: <Trans i18nKey="common.swap.failed.uniswapX" />,
supportArticleURL: SupportArticleURL.UNISWAP_X_FAILURE,
}
case PendingModalError.CONFIRMATION_ERROR:
......@@ -61,7 +61,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
} else {
return {
title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.message" />,
message: <Trans i18nKey="common.swap.failed.message" />,
supportArticleURL: isUniswapXTrade(trade)
? SupportArticleURL.UNISWAP_X_FAILURE
: SupportArticleURL.TRANSACTION_FAILURE,
......@@ -81,18 +81,19 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
}
}
export default function Error({ errorType, trade, showTrade, swapResult, onRetry }: ErrorModalContentProps) {
export default function Error({ errorType, trade, swapResult, onRetry }: ErrorModalContentProps) {
const theme = useTheme()
const { title, message, supportArticleURL } = getErrorContent({ errorType, trade })
return (
<DialogContent
isVisible={true}
icon={<AlertTriangleFilled data-testid="pending-modal-failure-icon" size="24px" />}
icon={<AlertTriangleFilled data-testid="pending-modal-failure-icon" fill={theme.neutral2} size="24px" />}
title={title}
description={message}
body={
<ColumnCenter gap="sm">
{showTrade && trade && <TradeSummary trade={trade} />}
<ColumnCenter gap="md">
{trade && <TradeSummary trade={trade} />}
{supportArticleURL && (
<ExternalLink href={supportArticleURL}>
<Trans i18nKey="common.learnMore.link" />
......@@ -114,7 +115,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry
}
buttonsConfig={{
left: {
type: DialogButtonType.Primary,
type: DialogButtonType.Accent,
title: <Trans i18nKey="common.tryAgain.error" />,
onClick: onRetry,
},
......
......@@ -5,7 +5,7 @@ import Row from 'components/Row'
import { SupportArticleURL } from 'constants/supportArticles'
import { SwapResult } from 'hooks/useSwapCallback'
import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { ReactNode, useMemo, useRef } from 'react'
import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTradeType } from 'state/routing/utils'
......@@ -26,8 +26,8 @@ import {
LoadingIndicatorOverlay,
LogoContainer,
} from '../AccountDrawer/MiniPortfolio/Activity/Logos'
import { TradeSummary } from './TradeSummary'
import { slideInAnimation, slideOutAnimation } from './animations'
import { TradeSummary } from './TradeSummary'
const Container = styled(ColumnCenter)`
margin: 48px 0 8px;
......
......@@ -130,7 +130,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a Limit order 1`] = `
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......@@ -380,7 +380,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a classic swap 1`] =
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......
......@@ -272,22 +272,19 @@ export function ConfirmSwapModal({
)}
{/* Error screen handles all error types with custom messaging and retry logic */}
{errorType && showError && (
<Container $padding="16px">
<SwapError
trade={trade}
showTrade={errorType !== PendingModalError.XV2_HARD_QUOTE_ERROR}
swapResult={swapResult}
errorType={errorType}
onRetry={() => {
if (errorType === PendingModalError.XV2_HARD_QUOTE_ERROR) {
onXV2RetryWithClassic?.()
resetToReviewScreen()
} else {
startSwapFlow()
}
}}
/>
</Container>
<SwapError
trade={trade}
swapResult={swapResult}
errorType={errorType}
onRetry={() => {
if (errorType === PendingModalError.XV2_HARD_QUOTE_ERROR) {
onXV2RetryWithClassic?.()
resetToReviewScreen()
} else {
startSwapFlow()
}
}}
/>
)}
</SwapModal>
</ThemeProvider>
......
......@@ -97,14 +97,12 @@ export interface DialogProps {
export function DialogContent({ icon, title, description, body, buttonsConfig }: DialogProps) {
const { left, right, gap } = buttonsConfig ?? {}
return (
<ColumnCenter gap="lg">
<ColumnCenter gap="16px">
<>
<ColumnCenter gap="md">
<IconContainer>{icon}</IconContainer>
<ColumnCenter gap="sm">
<TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText>
{body}
</ColumnCenter>
<TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText>
{body}
</ColumnCenter>
<Row align="center" justify="center" gap={gap ?? 'md'}>
{left && (
......@@ -130,7 +128,7 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }:
</StyledButton>
)}
</Row>
</ColumnCenter>
</>
)
}
......
......@@ -5,11 +5,14 @@ import { StyledSVG } from './shared'
export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) {
const theme = useTheme()
return (
<StyledSVG fill={theme.neutral2} viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" size={size} {...rest}>
<path
d="m25.2086 20.0103-7.7081-14.41555c-1.4933-2.793-5.5055-2.793-7 0l-7.70806 14.41555c-1.36966 2.562.49005 5.6559 3.40088 5.6559h15.61438c2.9097 0 4.7706-3.095 3.4009-5.6559zm-12.0831-8.3441c0-.483.392-.875.875-.875s.875.392.875.875v4.6667c0 .483-.392.875-.875.875s-.875-.392-.875-.875zm.8984 9.3333c-.644 0-1.1727-.5226-1.1727-1.1666s.517-1.1667 1.161-1.1667h.0117c.6452 0 1.1667.5227 1.1667 1.1667s-.5227 1.1666-1.1667 1.1666z"
fill="#9b9b9b"
/>
<StyledSVG
viewBox="0 0 16 16"
fill={theme.deprecated_accentWarning}
xmlns="http://www.w3.org/2000/svg"
size={size}
{...rest}
>
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2a1 1 0 0 1 0-2z" />
</StyledSVG>
)
}
......@@ -10,7 +10,7 @@ import useDebounce from 'hooks/useDebounce'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useTranslation } from 'i18n/useTranslation'
import { useTranslation } from 'i18n'
import { organizeSearchResults } from 'lib/utils/searchBar'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
......
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { useOpenModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components'
......
......@@ -3,7 +3,7 @@ import SettingsTab from 'components/Settings'
import { Trans } from 'i18n'
import { ReactNode } from 'react'
import { ArrowLeft } from 'react-feather'
import { Link, useLocation } from 'react-router-dom'
import { Link, useNavigate } from 'react-router-dom'
import { Box } from 'rebass'
import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions'
......@@ -68,13 +68,11 @@ export function AddRemoveTabs({
adding,
creating,
autoSlippage,
positionID,
children,
}: {
adding: boolean
creating: boolean
autoSlippage: Percent
positionID?: string
showBackLink?: boolean
children?: ReactNode
}) {
......@@ -82,23 +80,17 @@ export function AddRemoveTabs({
const theme = useTheme()
// reset states on back
const dispatch = useAppDispatch()
const { state } = useLocation()
const location = useLocation()
// detect if back should redirect to v3 or v2 pool page
const poolLink = location.pathname.includes('add/v2')
? '/pools/v2'
: '/pools' + (positionID ? `/${positionID.toString()}` : '')
// If the 'from' state is set by the previous page route back to the previous page, if not, route back to base pool
const target = state?.from ?? poolLink
const navigate = useNavigate()
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }} align="center">
<StyledLink
to={target}
onClick={() => {
to=".."
onClick={(e) => {
e.preventDefault()
navigate(-1)
if (adding) {
// not 100% sure both of these are needed
dispatch(resetMintState())
......
......@@ -60,18 +60,14 @@ interface InputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'on
maxDecimals?: number
}
export function isInputGreaterThanDecimals(value: string, maxDecimals?: number): boolean {
const decimalGroups = value.split('.')
return !!maxDecimals && decimalGroups.length > 1 && decimalGroups[1].length > maxDecimals
}
const Input = forwardRef<HTMLInputElement, InputProps>(
({ value, onUserInput, placeholder, prependSymbol, maxDecimals, ...rest }: InputProps, ref) => {
const { formatterLocale } = useFormatterLocales()
const enforcer = (nextUserInput: string) => {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
if (isInputGreaterThanDecimals(nextUserInput, maxDecimals)) {
const decimalGroups = nextUserInput.split('.')
if (maxDecimals && decimalGroups.length > 1 && decimalGroups[1].length > maxDecimals) {
return
}
......
......@@ -54,18 +54,15 @@ const StyledChart: typeof Chart = styled(Chart)`
const PDPChartTypeSelector = ({
chartType,
onChartTypeChange,
disabledOption,
}: {
chartType: PoolsDetailsChartType
onChartTypeChange: (c: PoolsDetailsChartType) => void
disabledOption?: PoolsDetailsChartType
}) => (
<ChartTypeSelectorContainer>
<ChartTypeDropdown
options={PDP_CHART_SELECTOR_OPTIONS}
currentChartType={chartType}
onSelectOption={onChartTypeChange}
disabledOption={disabledOption}
/>
</ChartTypeSelectorContainer>
)
......@@ -199,17 +196,11 @@ export default function ChartSection(props: ChartSectionProps) {
return DEFAULT_PILL_TIME_SELECTOR_OPTIONS
}, [activeQuery.chartType, setTimePeriod, timePeriod])
const disabledChartOption = props.poolData?.protocolVersion === ProtocolVersion.V2 ? ChartType.LIQUIDITY : undefined
return (
<div data-testid="pdp-chart-container">
{ChartBody}
<ChartActionsContainer>
<PDPChartTypeSelector
chartType={activeQuery.chartType}
onChartTypeChange={setChartType}
disabledOption={disabledChartOption}
/>
<PDPChartTypeSelector chartType={activeQuery.chartType} onChartTypeChange={setChartType} />
{activeQuery.chartType !== ChartType.LIQUIDITY && (
<TimePeriodSelectorContainer>
<PillMultiToggle
......
......@@ -19,7 +19,7 @@ import { Trans } from 'i18n'
import { Swap } from 'pages/Swap'
import { useMemo, useReducer } from 'react'
import { Plus, X } from 'react-feather'
import { useLocation, useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { BREAKPOINTS } from 'theme'
import { ClickableStyle, ThemedText } from 'theme/components'
......@@ -159,7 +159,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
const tokenId = position?.details.tokenId
const switchChain = useSwitchChain()
const navigate = useNavigate()
const location = useLocation()
const currency0 = token0 && gqlToCurrency(token0)
const currency1 = token1 && gqlToCurrency(token1)
......@@ -198,9 +197,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
if (walletChainId !== chainId && chainId) {
await switchChain(chainId)
}
navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`, {
state: { from: location.pathname },
})
navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`)
}
}
const [swapModalOpen, toggleSwapModalOpen] = useReducer((state) => !state, false)
......
import { Settings } from 'components/Icons/Settings'
import Row from 'components/Row'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { InterfaceTrade } from 'state/routing/types'
import { isUniswapXTrade } from 'state/routing/utils'
import { useUserSlippageTolerance } from 'state/user/hooks'
......
import { useTranslation } from 'i18n/useTranslation'
import { useTranslation } from 'i18n'
/**
* Displays the time as a human-readable string.
......
......@@ -58,15 +58,15 @@ export default function TokenSafetyMessage({
const { heading, description } = getWarningCopy(warning, plural, tokenSymbol)
return (
<Label color={textColor} backgroundColor={backgroundColor}>
<Label data-cy="token-safety-message" color={textColor} backgroundColor={backgroundColor}>
{displayWarningLabel(warning) && (
<TitleRow data-cy="token-safety-message">
<TitleRow>
{warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />}
<Title marginLeft="7px">{warning.message}</Title>
</TitleRow>
)}
<DetailsRow data-cy="token-safety-description">
<DetailsRow>
{heading}
{Boolean(heading) && ' '}
{description}
......
......@@ -214,7 +214,7 @@ export function TransactionsTable({
header: () => (
<Cell minWidth={160} justifyContent="flex-end">
<ThemedText.BodySecondary>
<Trans i18nKey="common.for" />
<Trans i18nKey="common.for.label" />
</ThemedText.BodySecondary>
</Cell>
),
......
......@@ -122,7 +122,7 @@ export function useTokenTransactions(
if (!tx) {
return false
}
const tokenBeingSold = parseFloat(tx.token0Quantity) > 0 ? tx.token0 : tx.token1
const tokenBeingSold = parseFloat(tx.token0Quantity) < 0 ? tx.token0 : tx.token1
const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase()
return (
tx.type === PoolTransactionType.Swap &&
......@@ -133,7 +133,7 @@ export function useTokenTransactions(
if (!tx) {
return false
}
const tokenBeingSold = parseFloat(tx.token0Quantity) > 0 ? tx.token0 : tx.token1
const tokenBeingSold = parseFloat(tx.token0Quantity) < 0 ? tx.token0 : tx.token1
const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase()
return (
tx.type === PoolTransactionType.Swap &&
......
......@@ -3,13 +3,13 @@ import { useMemo } from 'react'
import store from 'state'
import { useUserLocale } from 'state/user/hooks'
import useParsedQueryString from './useParsedQueryString'
import useParsedQueryString, { parsedQueryString } from './useParsedQueryString'
/**
* Given a locale string (e.g. from user agent), return the best match for corresponding SupportedLocale
* @param maybeSupportedLocale the fuzzy locale identifier
*/
export function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
if (typeof maybeSupportedLocale !== 'string') {
return undefined
}
......@@ -36,10 +36,13 @@ export function navigatorLocale(): SupportedLocale | undefined {
return parseLocale(language)
}
export function storeLocale(): SupportedLocale | undefined {
function storeLocale(): SupportedLocale | undefined {
return store.getState().user.userLocale ?? undefined
}
export const initialLocale =
parseLocale(parsedQueryString().lng) ?? storeLocale() ?? navigatorLocale() ?? DEFAULT_LOCALE
function useUrlLocale() {
const parsed = useParsedQueryString()
return parseLocale(parsed.lng)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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