ci(release): publish latest release

parent 168fd79b
* @uniswap/web-admins
IPFS hash of the deployment: Excited to share some new updates! Here’s what’s new:
- CIDv0: `QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV`
- CIDv1: `bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna` 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.
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). Improved Unicons — We gave your wallet’s unique Unicon a makeover. Check out the rest of your accounts to see your upgraded icons.
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)
web/5.30.1 mobile/1.28.2
\ No newline at end of file \ No newline at end of file
...@@ -176,7 +176,18 @@ These are some tools you might want to familiarize yourself with to understand t ...@@ -176,7 +176,18 @@ These are some tools you might want to familiarize yourself with to understand t
## Migrations ## 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 ## Troubleshooting
......
...@@ -136,12 +136,12 @@ android { ...@@ -136,12 +136,12 @@ android {
} }
beta { beta {
applicationIdSuffix ".beta" applicationIdSuffix ".beta"
versionName "1.28" versionName "1.28.2"
dimension "variant" dimension "variant"
} }
prod { prod {
dimension "variant" dimension "variant"
versionName "1.28" versionName "1.28.2"
} }
} }
......
...@@ -2536,7 +2536,7 @@ ...@@ -2536,7 +2536,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
...@@ -2628,7 +2628,7 @@ ...@@ -2628,7 +2628,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
...@@ -2713,7 +2713,7 @@ ...@@ -2713,7 +2713,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
...@@ -2799,7 +2799,7 @@ ...@@ -2799,7 +2799,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
...@@ -2873,7 +2873,7 @@ ...@@ -2873,7 +2873,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3103,7 +3103,7 @@ ...@@ -3103,7 +3103,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
...@@ -3207,7 +3207,7 @@ ...@@ -3207,7 +3207,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3278,7 +3278,7 @@ ...@@ -3278,7 +3278,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
......
...@@ -9,6 +9,17 @@ import { localizeMock as mockRNLocalize } from 'react-native-localize/mock' ...@@ -9,6 +9,17 @@ import { localizeMock as mockRNLocalize } from 'react-native-localize/mock'
import { AppearanceSettingType } from 'wallet/src/features/appearance/slice' import { AppearanceSettingType } from 'wallet/src/features/appearance/slice'
import { mockLocalizationContext } from 'wallet/src/test/mocks/utils' 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 // Mock Sentry crash reporting
jest.mock('@sentry/react-native', () => ({ jest.mock('@sentry/react-native', () => ({
init: () => jest.fn(), init: () => jest.fn(),
......
...@@ -104,7 +104,6 @@ import { ...@@ -104,7 +104,6 @@ import {
} from 'wallet/src/features/wallet/accounts/types' } from 'wallet/src/features/wallet/accounts/types'
import { initialWalletState, SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { initialWalletState, SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { createMigrate } from 'wallet/src/state/createMigrate' import { createMigrate } from 'wallet/src/state/createMigrate'
import { getAllKeysOfNestedObject } from 'wallet/src/state/testUtils'
import { import {
fiatPurchaseTransactionInfo, fiatPurchaseTransactionInfo,
signerMnemonicAccount, signerMnemonicAccount,
...@@ -125,6 +124,26 @@ const fiatOnRampTxDetailsFailed = transactionDetails({ ...@@ -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', () => { describe('Redux state migrations', () => {
it('is able to perform all migrations starting from the initial schema', async () => { it('is able to perform all migrations starting from the initial schema', async () => {
const initialSchemaStub = { const initialSchemaStub = {
...@@ -185,10 +204,6 @@ describe('Redux state migrations', () => { ...@@ -185,10 +204,6 @@ describe('Redux state migrations', () => {
}, },
} }
if (!migratedSchema) {
throw new Error('Migrated schema is undefined')
}
const migratedSchemaKeys = new Set(getAllKeysOfNestedObject(migratedSchema)) const migratedSchemaKeys = new Set(getAllKeysOfNestedObject(migratedSchema))
const latestSchemaKeys = new Set(getAllKeysOfNestedObject(getSchema())) const latestSchemaKeys = new Set(getAllKeysOfNestedObject(getSchema()))
const initialStateKeys = new Set(getAllKeysOfNestedObject(initialState)) const initialStateKeys = new Set(getAllKeysOfNestedObject(initialState))
......
...@@ -19,7 +19,6 @@ import { ...@@ -19,7 +19,6 @@ import {
} from 'wallet/src/features/transactions/types' } from 'wallet/src/features/transactions/types'
import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types'
import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { removeWalletIsUnlockedState } from 'wallet/src/state/sharedMigrations'
export const OLD_DEMO_ACCOUNT_ADDRESS = '0xdd0E380579dF30E38524F9477808d9eE37E2dEa6' export const OLD_DEMO_ACCOUNT_ADDRESS = '0xdd0E380579dF30E38524F9477808d9eE37E2dEa6'
...@@ -877,7 +876,10 @@ export const migrations = { ...@@ -877,7 +876,10 @@ export const migrations = {
return newState 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' ...@@ -3,7 +3,7 @@ import { isRejectedWithValue } from '@reduxjs/toolkit'
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
import { MMKV } from 'react-native-mmkv' import { MMKV } from 'react-native-mmkv'
import { Storage, persistReducer, persistStore } from 'redux-persist' 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 { isNonJestDev } from 'utilities/src/environment'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api' import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api'
...@@ -66,7 +66,7 @@ export const persistConfig = { ...@@ -66,7 +66,7 @@ export const persistConfig = {
key: 'root', key: 'root',
storage: reduxStorage, storage: reduxStorage,
whitelist, whitelist,
version: MOBILE_STATE_VERSION, version: 63,
migrate: createMigrate(migrations), migrate: createMigrate(migrations),
} }
......
...@@ -11,9 +11,11 @@ import { ...@@ -11,9 +11,11 @@ import {
NativeScrollEvent, NativeScrollEvent,
NativeSyntheticEvent, NativeSyntheticEvent,
ScrollView, ScrollView,
StyleProp,
View, View,
ViewStyle,
} from 'react-native' } 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 { ScrollDownOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay'
import { Button, Flex, useDeviceInsets } from 'ui/src' import { Button, Flex, useDeviceInsets } from 'ui/src'
import { spacing } from 'ui/src/theme' import { spacing } from 'ui/src/theme'
...@@ -30,6 +32,7 @@ type ModalWithOverlayProps = PropsWithChildren< ...@@ -30,6 +32,7 @@ type ModalWithOverlayProps = PropsWithChildren<
onReject: () => void onReject: () => void
onConfirm: () => void onConfirm: () => void
disableConfirm?: boolean disableConfirm?: boolean
contentContainerStyle?: StyleProp<AnimatedStyle<StyleProp<ViewStyle>>>
} }
> >
...@@ -48,6 +51,7 @@ export function ModalWithOverlay({ ...@@ -48,6 +51,7 @@ export function ModalWithOverlay({
onReject, onReject,
onConfirm, onConfirm,
disableConfirm, disableConfirm,
contentContainerStyle,
...bottomSheetModalProps ...bottomSheetModalProps
}: ModalWithOverlayProps): JSX.Element { }: ModalWithOverlayProps): JSX.Element {
const scrollViewRef = useRef<ScrollView>(null) const scrollViewRef = useRef<ScrollView>(null)
...@@ -114,10 +118,12 @@ export function ModalWithOverlay({ ...@@ -114,10 +118,12 @@ export function ModalWithOverlay({
<BottomSheetModal overrideInnerContainer {...bottomSheetModalProps}> <BottomSheetModal overrideInnerContainer {...bottomSheetModalProps}>
<BottomSheetScrollView <BottomSheetScrollView
ref={scrollViewRef} ref={scrollViewRef}
contentContainerStyle={{ contentContainerStyle={
paddingHorizontal: spacing.spacing24, contentContainerStyle ?? {
paddingTop: spacing.spacing36, paddingHorizontal: spacing.spacing24,
}} paddingTop: spacing.spacing36,
}
}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
onLayout={handleScrollViewLayout} onLayout={handleScrollViewLayout}
onScroll={handleScroll}> 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 ...@@ -194,7 +194,7 @@ function isSignTypedDataRequest(request: WalletConnectRequest): request is SignR
return request.type === EthMethod.SignTypedData || request.type === EthMethod.SignTypedDataV4 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() const { t } = useTranslation()
if (isSignTypedDataRequest(request)) { if (isSignTypedDataRequest(request)) {
......
...@@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next' ...@@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next'
import Animated, { useAnimatedStyle } from 'react-native-reanimated' import Animated, { useAnimatedStyle } from 'react-native-reanimated'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay' import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice' import { UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Text } from 'ui/src' import { Flex, Text, useIsDarkMode } from 'ui/src'
import { iconSizes } from 'ui/src/theme' import { iconSizes, spacing } from 'ui/src/theme'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { NumberType } from 'utilities/src/format/types'
import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo' import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo'
import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader' import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader'
import { NetworkFee } from 'wallet/src/components/network/NetworkFee' import { NetworkFee } from 'wallet/src/components/network/NetworkFee'
import { CHAIN_INFO } from 'wallet/src/constants/chains' import { CHAIN_INFO } from 'wallet/src/constants/chains'
import { GasFeeResult } from 'wallet/src/features/gas/types' 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 { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api'
import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo'
...@@ -48,6 +51,10 @@ export function UwULinkErc20SendModal({ ...@@ -48,6 +51,10 @@ export function UwULinkErc20SendModal({
return ( return (
<ModalWithOverlay <ModalWithOverlay
confirmationButtonText={t('common.button.pay')} confirmationButtonText={t('common.button.pay')}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing8,
}}
disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds} disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds}
name={ModalName.UwULinkErc20SendModal} name={ModalName.UwULinkErc20SendModal}
scrollDownButtonText={t('walletConnect.request.button.scrollDown')} scrollDownButtonText={t('walletConnect.request.button.scrollDown')}
...@@ -82,10 +89,12 @@ function UwULinkErc20SendModalContent({ ...@@ -82,10 +89,12 @@ function UwULinkErc20SendModalContent({
currencyInfo: Maybe<CurrencyInfo> currencyInfo: Maybe<CurrencyInfo>
}): JSX.Element { }): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
const isDarkMode = useIsDarkMode()
const { animatedFooterHeight } = useBottomSheetInternal() const { animatedFooterHeight } = useBottomSheetInternal()
const bottomSpacerStyle = useAnimatedStyle(() => ({ const bottomSpacerStyle = useAnimatedStyle(() => ({
height: animatedFooterHeight.value, height: animatedFooterHeight.value,
})) }))
const { convertFiatAmountFormatted } = useLocalizationContext()
const { chainId, isStablecoin } = request const { chainId, isStablecoin } = request
const nativeCurrency = chainId && NativeCurrency.onChain(chainId) const nativeCurrency = chainId && NativeCurrency.onChain(chainId)
...@@ -104,10 +113,22 @@ function UwULinkErc20SendModalContent({ ...@@ -104,10 +113,22 @@ function UwULinkErc20SendModalContent({
currency: { name, symbol, decimals }, currency: { name, symbol, decimals },
} = currencyInfo } = 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 ( return (
<Flex centered gap="$spacing12" justifyContent="space-between"> <Flex centered gap="$spacing12" justifyContent="space-between">
<Text variant="subheading1">{request.recipient.name}</Text> {recipientLogoUrl ? (
<Flex centered flex={1} gap="$spacing12" py="$spacing16"> <RemoteImage height={50} uri={recipientLogoUrl} width={200} />
) : (
<Text variant="subheading1">{request.recipient.name}</Text>
)}
<Flex centered flex={1} gap="$spacing12" py="$spacing36">
{!hasSufficientTokenFunds && ( {!hasSufficientTokenFunds && (
<Text color="red"> <Text color="red">
{t('uwulink.error.insufficientTokens', { {t('uwulink.error.insufficientTokens', {
...@@ -116,11 +137,10 @@ function UwULinkErc20SendModalContent({ ...@@ -116,11 +137,10 @@ function UwULinkErc20SendModalContent({
})} })}
</Text> </Text>
)} )}
<Text fontSize={64} my="$spacing4" pt={42}>{`${isStablecoin ? '$' : ''}${formatUnits( <Text fontSize={64} my="$spacing4" pt={42}>
request.amount, {formattedTokenAmount}
decimals </Text>
)}`}</Text> <Flex row gap="$spacing8">
<Flex row gap="$spacing4">
<TokenLogo <TokenLogo
chainId={chainId} chainId={chainId}
name={name} name={name}
...@@ -128,7 +148,9 @@ function UwULinkErc20SendModalContent({ ...@@ -128,7 +148,9 @@ function UwULinkErc20SendModalContent({
symbol={symbol} symbol={symbol}
url={logoUrl} url={logoUrl}
/> />
<Text>{symbol}</Text> <Text color="$neutral2">
{formatUnits(request.amount, decimals)} {symbol}
</Text>
</Flex> </Flex>
</Flex> </Flex>
<Flex alignSelf="stretch" borderTopColor="$surface3" borderTopWidth={1} pt="$spacing16"> <Flex alignSelf="stretch" borderTopColor="$surface3" borderTopWidth={1} pt="$spacing16">
......
...@@ -5,6 +5,7 @@ import React, { useMemo, useRef } from 'react' ...@@ -5,6 +5,7 @@ import React, { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay' import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { KidSuperCheckinModal } from 'src/components/WalletConnect/RequestModal/KidSuperCheckinModal'
import { import {
WalletConnectRequestModalContent, WalletConnectRequestModalContent,
methodCostsGas, methodCostsGas,
...@@ -238,6 +239,18 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem ...@@ -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 ( return (
<ModalWithOverlay <ModalWithOverlay
confirmationButtonText={ confirmationButtonText={
......
...@@ -226,6 +226,7 @@ export function WalletConnectModal({ ...@@ -226,6 +226,7 @@ export function WalletConnectModal({
recipient: { recipient: {
address: parsedUwulinkRequest.recipient, address: parsedUwulinkRequest.recipient,
name: tokenRecipient?.name ?? '', name: tokenRecipient?.name ?? '',
logo: tokenRecipient?.logo,
}, },
amount: parsedUwulinkRequest.amount, amount: parsedUwulinkRequest.amount,
tokenAddress: parsedUwulinkRequest.tokenAddress, tokenAddress: parsedUwulinkRequest.tokenAddress,
......
...@@ -51,7 +51,10 @@ type UwULinkAllowlistItem = { ...@@ -51,7 +51,10 @@ type UwULinkAllowlistItem = {
chainId: number chainId: number
address: string address: string
name: string name: string
icon?: string logo?: {
dark?: string
light?: string
}
} }
type UwULinkAllowlist = { type UwULinkAllowlist = {
......
...@@ -55,6 +55,16 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams ...@@ -55,6 +55,16 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams
let signature = '' let signature = ''
if (method === EthMethod.PersonalSign || method === EthMethod.EthSign) { if (method === EthMethod.PersonalSign || method === EthMethod.EthSign) {
signature = yield* call(signMessage, params.message, account, signerManager) 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) { } else if (method === EthMethod.SignTypedData || method === EthMethod.SignTypedDataV4) {
signature = yield* call(signTypedDataMessage, params.message, account, signerManager) signature = yield* call(signTypedDataMessage, params.message, account, signerManager)
} else if ( } else if (
......
...@@ -51,6 +51,10 @@ export interface UwuLinkErc20Request extends BaseRequest { ...@@ -51,6 +51,10 @@ export interface UwuLinkErc20Request extends BaseRequest {
recipient: { recipient: {
address: string address: string
name: string name: string
logo?: {
dark?: string
light?: string
}
} }
tokenAddress: string tokenAddress: string
amount: string amount: string
......
...@@ -292,7 +292,15 @@ describe('UniswapX v1', () => { ...@@ -292,7 +292,15 @@ describe('UniswapX v1', () => {
submitUniswapXOrder() submitUniswapXOrder()
cy.get(getTestSelector('confirmation-close-icon')).click() 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 // Expect balances to refetch after filling
cy.wait('@orderStatusOpen') cy.wait('@orderStatusOpen')
......
...@@ -287,7 +287,15 @@ describe('UniswapX v2', () => { ...@@ -287,7 +287,15 @@ describe('UniswapX v2', () => {
submitUniswapXOrder() submitUniswapXOrder()
cy.get(getTestSelector('confirmation-close-icon')).click() 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 // Expect balances to refetch after filling
cy.wait('@orderStatusOpen') cy.wait('@orderStatusOpen')
......
...@@ -80,10 +80,9 @@ describe('Token details', () => { ...@@ -80,10 +80,9 @@ describe('Token details', () => {
cy.contains(shortenAddress('0x1eFBB78C8b917f67986BcE54cE575069c0143681')).should('exist') cy.contains(shortenAddress('0x1eFBB78C8b917f67986BcE54cE575069c0143681')).should('exist')
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e)) // 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-message"]')
cy.get('[data-cy="token-safety-description"]').contains( .should('include.text', 'Warning')
/This token isn’t traded on leading U.S. centralized exchanges or frequently swapped on Uniswap./ .and('include.text', "This token isn't traded on leading U.S. centralized exchanges")
)
}) })
describe('swapping', () => { describe('swapping', () => {
......
...@@ -645,14 +645,4 @@ ...@@ -645,14 +645,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod> <lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.7</priority> <priority>0.7</priority>
</url> </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> </urlset>
\ No newline at end of file
...@@ -4355,219 +4355,4 @@ ...@@ -4355,219 +4355,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod> <lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.8</priority> <priority>0.8</priority>
</url> </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> </urlset>
\ No newline at end of file
This diff is collapsed.
...@@ -47,7 +47,7 @@ function useCancelLimitsDialogContent( ...@@ -47,7 +47,7 @@ function useCancelLimitsDialogContent(
<Plural <Plural
value={orders.length} value={orders.length}
one={t('common.limit.cancel')} 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 />, icon: <Slash />,
...@@ -143,7 +143,7 @@ export function CancelLimitsDialog( ...@@ -143,7 +143,7 @@ export function CancelLimitsDialog(
} }
buttonsConfig={{ buttonsConfig={{
left: { left: {
title: <Trans i18nKey="common.neverMind" />, title: <Trans i18nKey="common.nevermind" />,
onClick: onCancel, onClick: onCancel,
textColor: 'neutral1', textColor: 'neutral1',
}, },
......
...@@ -2,7 +2,7 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activ ...@@ -2,7 +2,7 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activ
import Column from 'components/Column' import Column from 'components/Column'
import { TimeForwardIcon } from 'components/Icons/TimeForward' import { TimeForwardIcon } from 'components/Icons/TimeForward'
import Row from 'components/Row' import Row from 'components/Row'
import { Plural, Trans, t } from 'i18n' import { Plural, t, Trans } from 'i18n'
import { ChevronRight } from 'react-feather' import { ChevronRight } from 'react-feather'
import styled, { useTheme } from 'styled-components' import styled, { useTheme } from 'styled-components'
import { ClickableStyle, ThemedText } from 'theme/components' import { ClickableStyle, ThemedText } from 'theme/components'
......
...@@ -3,7 +3,7 @@ import Row from 'components/Row' ...@@ -3,7 +3,7 @@ import Row from 'components/Row'
import Tooltip, { TooltipSize } from 'components/Tooltip' import Tooltip, { TooltipSize } from 'components/Tooltip'
import { useScreenSize } from 'hooks/screenSize' import { useScreenSize } from 'hooks/screenSize'
import useCopyClipboard from 'hooks/useCopyClipboard' import useCopyClipboard from 'hooks/useCopyClipboard'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { Copy } from 'react-feather' import { Copy } from 'react-feather'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
......
...@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme/components' ...@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme/components'
const StyledExternalLink = styled(ExternalLink)` const StyledExternalLink = styled(ExternalLink)`
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
......
...@@ -247,7 +247,7 @@ function CandlestickTooltip({ data }: { data: PriceChartData }) { ...@@ -247,7 +247,7 @@ function CandlestickTooltip({ data }: { data: PriceChartData }) {
<div>{formatFiatPrice({ price: data.low })}</div> <div>{formatFiatPrice({ price: data.low })}</div>
</RowBetween> </RowBetween>
<RowBetween gap="sm"> <RowBetween gap="sm">
<Trans i18nKey="common.close" /> <Trans i18nKey="chart.price.close" />
<div>{formatFiatPrice({ price: data.close })}</div> <div>{formatFiatPrice({ price: data.close })}</div>
</RowBetween> </RowBetween>
</TooltipText> </TooltipText>
......
...@@ -14,7 +14,7 @@ describe('ConfirmSwapModal/Error', () => { ...@@ -14,7 +14,7 @@ describe('ConfirmSwapModal/Error', () => {
['limit order', PendingModalError.CONFIRMATION_ERROR, LIMIT_ORDER_TRADE, 'Limit failed'], ['limit order', PendingModalError.CONFIRMATION_ERROR, LIMIT_ORDER_TRADE, 'Limit failed'],
['limit order', PendingModalError.WRAP_ERROR, LIMIT_ORDER_TRADE, 'Wrap failed'], ['limit order', PendingModalError.WRAP_ERROR, LIMIT_ORDER_TRADE, 'Wrap failed'],
])('renders %p correctly, with error= %p', async (testCaseName, errorType, trade, expectedError) => { ])('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(asFragment()).toMatchSnapshot()
expect(screen.getByText(expectedError)).toBeInTheDocument() expect(screen.getByText(expectedError)).toBeInTheDocument()
}) })
......
...@@ -4,6 +4,7 @@ import { SwapResult } from 'hooks/useSwapCallback' ...@@ -4,6 +4,7 @@ import { SwapResult } from 'hooks/useSwapCallback'
import { Trans } from 'i18n' import { Trans } from 'i18n'
import { InterfaceTrade, TradeFillType } from 'state/routing/types' import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTrade } from 'state/routing/utils' import { isLimitTrade, isUniswapXTrade } from 'state/routing/utils'
import { useTheme } from 'styled-components'
import { TradeSummary } from 'components/ConfirmSwapModal/TradeSummary' import { TradeSummary } from 'components/ConfirmSwapModal/TradeSummary'
import { DialogButtonType, DialogContent } from 'components/Dialog/Dialog' import { DialogButtonType, DialogContent } from 'components/Dialog/Dialog'
...@@ -23,7 +24,6 @@ export enum PendingModalError { ...@@ -23,7 +24,6 @@ export enum PendingModalError {
interface ErrorModalContentProps { interface ErrorModalContentProps {
errorType: PendingModalError errorType: PendingModalError
trade?: InterfaceTrade trade?: InterfaceTrade
showTrade?: boolean
swapResult?: SwapResult swapResult?: SwapResult
onRetry: () => void onRetry: () => void
} }
...@@ -49,7 +49,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t ...@@ -49,7 +49,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
case PendingModalError.XV2_HARD_QUOTE_ERROR: case PendingModalError.XV2_HARD_QUOTE_ERROR:
return { return {
title: <Trans i18nKey="common.swap.failed" />, title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.uniswapX" />, message: <Trans i18nKey="common.swap.failed.uniswapX" />,
supportArticleURL: SupportArticleURL.UNISWAP_X_FAILURE, supportArticleURL: SupportArticleURL.UNISWAP_X_FAILURE,
} }
case PendingModalError.CONFIRMATION_ERROR: case PendingModalError.CONFIRMATION_ERROR:
...@@ -61,7 +61,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t ...@@ -61,7 +61,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
} else { } else {
return { return {
title: <Trans i18nKey="common.swap.failed" />, title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.message" />, message: <Trans i18nKey="common.swap.failed.message" />,
supportArticleURL: isUniswapXTrade(trade) supportArticleURL: isUniswapXTrade(trade)
? SupportArticleURL.UNISWAP_X_FAILURE ? SupportArticleURL.UNISWAP_X_FAILURE
: SupportArticleURL.TRANSACTION_FAILURE, : SupportArticleURL.TRANSACTION_FAILURE,
...@@ -81,18 +81,19 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t ...@@ -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 }) const { title, message, supportArticleURL } = getErrorContent({ errorType, trade })
return ( return (
<DialogContent <DialogContent
isVisible={true} 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} title={title}
description={message} description={message}
body={ body={
<ColumnCenter gap="sm"> <ColumnCenter gap="md">
{showTrade && trade && <TradeSummary trade={trade} />} {trade && <TradeSummary trade={trade} />}
{supportArticleURL && ( {supportArticleURL && (
<ExternalLink href={supportArticleURL}> <ExternalLink href={supportArticleURL}>
<Trans i18nKey="common.learnMore.link" /> <Trans i18nKey="common.learnMore.link" />
...@@ -114,7 +115,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry ...@@ -114,7 +115,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry
} }
buttonsConfig={{ buttonsConfig={{
left: { left: {
type: DialogButtonType.Primary, type: DialogButtonType.Accent,
title: <Trans i18nKey="common.tryAgain.error" />, title: <Trans i18nKey="common.tryAgain.error" />,
onClick: onRetry, onClick: onRetry,
}, },
......
...@@ -5,7 +5,7 @@ import Row from 'components/Row' ...@@ -5,7 +5,7 @@ import Row from 'components/Row'
import { SupportArticleURL } from 'constants/supportArticles' import { SupportArticleURL } from 'constants/supportArticles'
import { SwapResult } from 'hooks/useSwapCallback' import { SwapResult } from 'hooks/useSwapCallback'
import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation' import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { ReactNode, useMemo, useRef } from 'react' import { ReactNode, useMemo, useRef } from 'react'
import { InterfaceTrade, TradeFillType } from 'state/routing/types' import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTradeType } from 'state/routing/utils' import { isLimitTrade, isUniswapXTradeType } from 'state/routing/utils'
...@@ -26,8 +26,8 @@ import { ...@@ -26,8 +26,8 @@ import {
LoadingIndicatorOverlay, LoadingIndicatorOverlay,
LogoContainer, LogoContainer,
} from '../AccountDrawer/MiniPortfolio/Activity/Logos' } from '../AccountDrawer/MiniPortfolio/Activity/Logos'
import { TradeSummary } from './TradeSummary'
import { slideInAnimation, slideOutAnimation } from './animations' import { slideInAnimation, slideOutAnimation } from './animations'
import { TradeSummary } from './TradeSummary'
const Container = styled(ColumnCenter)` const Container = styled(ColumnCenter)`
margin: 48px 0 8px; margin: 48px 0 8px;
......
...@@ -130,7 +130,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a Limit order 1`] = ` ...@@ -130,7 +130,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a Limit order 1`] = `
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
...@@ -380,7 +380,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a classic swap 1`] = ...@@ -380,7 +380,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a classic swap 1`] =
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
......
...@@ -272,22 +272,19 @@ export function ConfirmSwapModal({ ...@@ -272,22 +272,19 @@ export function ConfirmSwapModal({
)} )}
{/* Error screen handles all error types with custom messaging and retry logic */} {/* Error screen handles all error types with custom messaging and retry logic */}
{errorType && showError && ( {errorType && showError && (
<Container $padding="16px"> <SwapError
<SwapError trade={trade}
trade={trade} swapResult={swapResult}
showTrade={errorType !== PendingModalError.XV2_HARD_QUOTE_ERROR} errorType={errorType}
swapResult={swapResult} onRetry={() => {
errorType={errorType} if (errorType === PendingModalError.XV2_HARD_QUOTE_ERROR) {
onRetry={() => { onXV2RetryWithClassic?.()
if (errorType === PendingModalError.XV2_HARD_QUOTE_ERROR) { resetToReviewScreen()
onXV2RetryWithClassic?.() } else {
resetToReviewScreen() startSwapFlow()
} else { }
startSwapFlow() }}
} />
}}
/>
</Container>
)} )}
</SwapModal> </SwapModal>
</ThemeProvider> </ThemeProvider>
......
...@@ -97,14 +97,12 @@ export interface DialogProps { ...@@ -97,14 +97,12 @@ export interface DialogProps {
export function DialogContent({ icon, title, description, body, buttonsConfig }: DialogProps) { export function DialogContent({ icon, title, description, body, buttonsConfig }: DialogProps) {
const { left, right, gap } = buttonsConfig ?? {} const { left, right, gap } = buttonsConfig ?? {}
return ( return (
<ColumnCenter gap="lg"> <>
<ColumnCenter gap="16px"> <ColumnCenter gap="md">
<IconContainer>{icon}</IconContainer> <IconContainer>{icon}</IconContainer>
<ColumnCenter gap="sm"> <TitleText>{title}</TitleText>
<TitleText>{title}</TitleText> <DescriptionText>{description}</DescriptionText>
<DescriptionText>{description}</DescriptionText> {body}
{body}
</ColumnCenter>
</ColumnCenter> </ColumnCenter>
<Row align="center" justify="center" gap={gap ?? 'md'}> <Row align="center" justify="center" gap={gap ?? 'md'}>
{left && ( {left && (
...@@ -130,7 +128,7 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }: ...@@ -130,7 +128,7 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }:
</StyledButton> </StyledButton>
)} )}
</Row> </Row>
</ColumnCenter> </>
) )
} }
......
...@@ -5,11 +5,14 @@ import { StyledSVG } from './shared' ...@@ -5,11 +5,14 @@ import { StyledSVG } from './shared'
export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) { export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) {
const theme = useTheme() const theme = useTheme()
return ( return (
<StyledSVG fill={theme.neutral2} viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" size={size} {...rest}> <StyledSVG
<path viewBox="0 0 16 16"
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={theme.deprecated_accentWarning}
fill="#9b9b9b" 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> </StyledSVG>
) )
} }
...@@ -10,7 +10,7 @@ import useDebounce from 'hooks/useDebounce' ...@@ -10,7 +10,7 @@ import useDebounce from 'hooks/useDebounce'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes' import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage' import { useIsNftPage } from 'hooks/useIsNftPage'
import { useOnClickOutside } from 'hooks/useOnClickOutside' import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useTranslation } from 'i18n/useTranslation' import { useTranslation } from 'i18n'
import { organizeSearchResults } from 'lib/utils/searchBar' import { organizeSearchResults } from 'lib/utils/searchBar'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex' import { Column, Row } from 'nft/components/Flex'
......
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { useOpenModal } from 'state/application/hooks' import { useOpenModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components' import styled from 'styled-components'
......
...@@ -3,7 +3,7 @@ import SettingsTab from 'components/Settings' ...@@ -3,7 +3,7 @@ import SettingsTab from 'components/Settings'
import { Trans } from 'i18n' import { Trans } from 'i18n'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { ArrowLeft } from 'react-feather' import { ArrowLeft } from 'react-feather'
import { Link, useLocation } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import { Box } from 'rebass' import { Box } from 'rebass'
import { useAppDispatch } from 'state/hooks' import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions' import { resetMintState } from 'state/mint/actions'
...@@ -68,13 +68,11 @@ export function AddRemoveTabs({ ...@@ -68,13 +68,11 @@ export function AddRemoveTabs({
adding, adding,
creating, creating,
autoSlippage, autoSlippage,
positionID,
children, children,
}: { }: {
adding: boolean adding: boolean
creating: boolean creating: boolean
autoSlippage: Percent autoSlippage: Percent
positionID?: string
showBackLink?: boolean showBackLink?: boolean
children?: ReactNode children?: ReactNode
}) { }) {
...@@ -82,23 +80,17 @@ export function AddRemoveTabs({ ...@@ -82,23 +80,17 @@ export function AddRemoveTabs({
const theme = useTheme() const theme = useTheme()
// reset states on back // reset states on back
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { state } = useLocation() const navigate = useNavigate()
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
return ( return (
<Tabs> <Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }} align="center"> <RowBetween style={{ padding: '1rem 1rem 0 1rem' }} align="center">
<StyledLink <StyledLink
to={target} to=".."
onClick={() => { onClick={(e) => {
e.preventDefault()
navigate(-1)
if (adding) { if (adding) {
// not 100% sure both of these are needed // not 100% sure both of these are needed
dispatch(resetMintState()) dispatch(resetMintState())
......
...@@ -60,18 +60,14 @@ interface InputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'on ...@@ -60,18 +60,14 @@ interface InputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'on
maxDecimals?: number 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>( const Input = forwardRef<HTMLInputElement, InputProps>(
({ value, onUserInput, placeholder, prependSymbol, maxDecimals, ...rest }: InputProps, ref) => { ({ value, onUserInput, placeholder, prependSymbol, maxDecimals, ...rest }: InputProps, ref) => {
const { formatterLocale } = useFormatterLocales() const { formatterLocale } = useFormatterLocales()
const enforcer = (nextUserInput: string) => { const enforcer = (nextUserInput: string) => {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) { if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
if (isInputGreaterThanDecimals(nextUserInput, maxDecimals)) { const decimalGroups = nextUserInput.split('.')
if (maxDecimals && decimalGroups.length > 1 && decimalGroups[1].length > maxDecimals) {
return return
} }
......
...@@ -54,18 +54,15 @@ const StyledChart: typeof Chart = styled(Chart)` ...@@ -54,18 +54,15 @@ const StyledChart: typeof Chart = styled(Chart)`
const PDPChartTypeSelector = ({ const PDPChartTypeSelector = ({
chartType, chartType,
onChartTypeChange, onChartTypeChange,
disabledOption,
}: { }: {
chartType: PoolsDetailsChartType chartType: PoolsDetailsChartType
onChartTypeChange: (c: PoolsDetailsChartType) => void onChartTypeChange: (c: PoolsDetailsChartType) => void
disabledOption?: PoolsDetailsChartType
}) => ( }) => (
<ChartTypeSelectorContainer> <ChartTypeSelectorContainer>
<ChartTypeDropdown <ChartTypeDropdown
options={PDP_CHART_SELECTOR_OPTIONS} options={PDP_CHART_SELECTOR_OPTIONS}
currentChartType={chartType} currentChartType={chartType}
onSelectOption={onChartTypeChange} onSelectOption={onChartTypeChange}
disabledOption={disabledOption}
/> />
</ChartTypeSelectorContainer> </ChartTypeSelectorContainer>
) )
...@@ -199,17 +196,11 @@ export default function ChartSection(props: ChartSectionProps) { ...@@ -199,17 +196,11 @@ export default function ChartSection(props: ChartSectionProps) {
return DEFAULT_PILL_TIME_SELECTOR_OPTIONS return DEFAULT_PILL_TIME_SELECTOR_OPTIONS
}, [activeQuery.chartType, setTimePeriod, timePeriod]) }, [activeQuery.chartType, setTimePeriod, timePeriod])
const disabledChartOption = props.poolData?.protocolVersion === ProtocolVersion.V2 ? ChartType.LIQUIDITY : undefined
return ( return (
<div data-testid="pdp-chart-container"> <div data-testid="pdp-chart-container">
{ChartBody} {ChartBody}
<ChartActionsContainer> <ChartActionsContainer>
<PDPChartTypeSelector <PDPChartTypeSelector chartType={activeQuery.chartType} onChartTypeChange={setChartType} />
chartType={activeQuery.chartType}
onChartTypeChange={setChartType}
disabledOption={disabledChartOption}
/>
{activeQuery.chartType !== ChartType.LIQUIDITY && ( {activeQuery.chartType !== ChartType.LIQUIDITY && (
<TimePeriodSelectorContainer> <TimePeriodSelectorContainer>
<PillMultiToggle <PillMultiToggle
......
...@@ -19,7 +19,7 @@ import { Trans } from 'i18n' ...@@ -19,7 +19,7 @@ import { Trans } from 'i18n'
import { Swap } from 'pages/Swap' import { Swap } from 'pages/Swap'
import { useMemo, useReducer } from 'react' import { useMemo, useReducer } from 'react'
import { Plus, X } from 'react-feather' 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 styled from 'styled-components'
import { BREAKPOINTS } from 'theme' import { BREAKPOINTS } from 'theme'
import { ClickableStyle, ThemedText } from 'theme/components' import { ClickableStyle, ThemedText } from 'theme/components'
...@@ -159,7 +159,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load ...@@ -159,7 +159,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
const tokenId = position?.details.tokenId const tokenId = position?.details.tokenId
const switchChain = useSwitchChain() const switchChain = useSwitchChain()
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation()
const currency0 = token0 && gqlToCurrency(token0) const currency0 = token0 && gqlToCurrency(token0)
const currency1 = token1 && gqlToCurrency(token1) const currency1 = token1 && gqlToCurrency(token1)
...@@ -198,9 +197,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load ...@@ -198,9 +197,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
if (walletChainId !== chainId && chainId) { if (walletChainId !== chainId && chainId) {
await switchChain(chainId) await switchChain(chainId)
} }
navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`, { navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`)
state: { from: location.pathname },
})
} }
} }
const [swapModalOpen, toggleSwapModalOpen] = useReducer((state) => !state, false) const [swapModalOpen, toggleSwapModalOpen] = useReducer((state) => !state, false)
......
import { Settings } from 'components/Icons/Settings' import { Settings } from 'components/Icons/Settings'
import Row from 'components/Row' import Row from 'components/Row'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { InterfaceTrade } from 'state/routing/types' import { InterfaceTrade } from 'state/routing/types'
import { isUniswapXTrade } from 'state/routing/utils' import { isUniswapXTrade } from 'state/routing/utils'
import { useUserSlippageTolerance } from 'state/user/hooks' import { useUserSlippageTolerance } from 'state/user/hooks'
......
import { useTranslation } from 'i18n/useTranslation' import { useTranslation } from 'i18n'
/** /**
* Displays the time as a human-readable string. * Displays the time as a human-readable string.
......
...@@ -58,15 +58,15 @@ export default function TokenSafetyMessage({ ...@@ -58,15 +58,15 @@ export default function TokenSafetyMessage({
const { heading, description } = getWarningCopy(warning, plural, tokenSymbol) const { heading, description } = getWarningCopy(warning, plural, tokenSymbol)
return ( return (
<Label color={textColor} backgroundColor={backgroundColor}> <Label data-cy="token-safety-message" color={textColor} backgroundColor={backgroundColor}>
{displayWarningLabel(warning) && ( {displayWarningLabel(warning) && (
<TitleRow data-cy="token-safety-message"> <TitleRow>
{warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />} {warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />}
<Title marginLeft="7px">{warning.message}</Title> <Title marginLeft="7px">{warning.message}</Title>
</TitleRow> </TitleRow>
)} )}
<DetailsRow data-cy="token-safety-description"> <DetailsRow>
{heading} {heading}
{Boolean(heading) && ' '} {Boolean(heading) && ' '}
{description} {description}
......
...@@ -214,7 +214,7 @@ export function TransactionsTable({ ...@@ -214,7 +214,7 @@ export function TransactionsTable({
header: () => ( header: () => (
<Cell minWidth={160} justifyContent="flex-end"> <Cell minWidth={160} justifyContent="flex-end">
<ThemedText.BodySecondary> <ThemedText.BodySecondary>
<Trans i18nKey="common.for" /> <Trans i18nKey="common.for.label" />
</ThemedText.BodySecondary> </ThemedText.BodySecondary>
</Cell> </Cell>
), ),
......
...@@ -122,7 +122,7 @@ export function useTokenTransactions( ...@@ -122,7 +122,7 @@ export function useTokenTransactions(
if (!tx) { if (!tx) {
return false 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() const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase()
return ( return (
tx.type === PoolTransactionType.Swap && tx.type === PoolTransactionType.Swap &&
...@@ -133,7 +133,7 @@ export function useTokenTransactions( ...@@ -133,7 +133,7 @@ export function useTokenTransactions(
if (!tx) { if (!tx) {
return false 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() const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase()
return ( return (
tx.type === PoolTransactionType.Swap && tx.type === PoolTransactionType.Swap &&
......
...@@ -3,13 +3,13 @@ import { useMemo } from 'react' ...@@ -3,13 +3,13 @@ import { useMemo } from 'react'
import store from 'state' import store from 'state'
import { useUserLocale } from 'state/user/hooks' 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 * Given a locale string (e.g. from user agent), return the best match for corresponding SupportedLocale
* @param maybeSupportedLocale the fuzzy locale identifier * @param maybeSupportedLocale the fuzzy locale identifier
*/ */
export function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined { function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
if (typeof maybeSupportedLocale !== 'string') { if (typeof maybeSupportedLocale !== 'string') {
return undefined return undefined
} }
...@@ -36,10 +36,13 @@ export function navigatorLocale(): SupportedLocale | undefined { ...@@ -36,10 +36,13 @@ export function navigatorLocale(): SupportedLocale | undefined {
return parseLocale(language) return parseLocale(language)
} }
export function storeLocale(): SupportedLocale | undefined { function storeLocale(): SupportedLocale | undefined {
return store.getState().user.userLocale ?? undefined return store.getState().user.userLocale ?? undefined
} }
export const initialLocale =
parseLocale(parsedQueryString().lng) ?? storeLocale() ?? navigatorLocale() ?? DEFAULT_LOCALE
function useUrlLocale() { function useUrlLocale() {
const parsed = useParsedQueryString() const parsed = useParsedQueryString()
return parseLocale(parsed.lng) 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