ci(release): publish latest release

parent d00aa5dd
* @uniswap/web-admins
IPFS hash of the deployment: We are back with some new updates! Here’s the latest:
- CIDv0: `QmSen79xDfziDqosVLQFWT5F1cxK7pYHirdnMMkCjZqtDz`
- CIDv1: `bafybeicacoiqphc6t7aqqtkawiwotwqxwxqdtapccc4iy64xx3ompux7du`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). Manage dapp connections - Users can now see all dapps they’re connected to, and disconnect to one or all of them.
You can also access the Uniswap Interface from an IPFS gateway. Report Spam NFTs - You can now report spam NFTs and hide them from your feed and activity.
**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://bafybeicacoiqphc6t7aqqtkawiwotwqxwxqdtapccc4iy64xx3ompux7du.ipfs.dweb.link/
- https://bafybeicacoiqphc6t7aqqtkawiwotwqxwxqdtapccc4iy64xx3ompux7du.ipfs.cf-ipfs.com/
- [ipfs://QmSen79xDfziDqosVLQFWT5F1cxK7pYHirdnMMkCjZqtDz/](ipfs://QmSen79xDfziDqosVLQFWT5F1cxK7pYHirdnMMkCjZqtDz/)
### 5.48.1 (2024-09-24)
Other changes:
- Added explainers for hidden tokens, popular tokens, and hidden NFTs
- Removed activity feed items related to any hidden NFTs
- Various bug fixes and performance improvements
\ No newline at end of file
web/5.48.1 extension/1.6.0
\ No newline at end of file \ No newline at end of file
...@@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux' ...@@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux'
import { EditLabelModal } from 'src/app/features/accounts/EditLabelModal' import { EditLabelModal } from 'src/app/features/accounts/EditLabelModal'
import { removeAllDappConnectionsForAccount } from 'src/app/features/dapp/actions' import { removeAllDappConnectionsForAccount } from 'src/app/features/dapp/actions'
import { ContextMenu, Flex, MenuContentItem, Text, TouchableArea } from 'ui/src' import { ContextMenu, Flex, MenuContentItem, Text, TouchableArea } from 'ui/src'
import { CopySheets, Edit, Ellipsis, TrashFilled } from 'ui/src/components/icons' import { CopySheets, Edit, TrashFilled, TripleDots } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { WarningModal } from 'uniswap/src/components/modals/WarningModal/WarningModal' import { WarningModal } from 'uniswap/src/components/modals/WarningModal/WarningModal'
import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types' import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types'
...@@ -179,7 +179,7 @@ export function AccountItem({ address, onAccountSelect, balanceUSD }: AccountIte ...@@ -179,7 +179,7 @@ export function AccountItem({ address, onAccountSelect, balanceUSD }: AccountIte
opacity={0} opacity={0}
p="$spacing4" p="$spacing4"
> >
<Ellipsis color="$neutral2" size="$icon.16" /> <TripleDots color="$neutral2" size="$icon.16" />
</Flex> </Flex>
</ContextMenu> </ContextMenu>
</Flex> </Flex>
......
import { cloneDeep } from '@apollo/client/utilities'
import EventEmitter from 'eventemitter3' import EventEmitter from 'eventemitter3'
import { getOrderedConnectedAddresses, isConnectedAccount } from 'src/app/features/dapp/utils' import { getOrderedConnectedAddresses, isConnectedAccount } from 'src/app/features/dapp/utils'
import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains'
...@@ -224,28 +223,31 @@ function removeAccountDappConnections(account: Account): void { ...@@ -224,28 +223,31 @@ function removeAccountDappConnections(account: Account): void {
* @returns the updated state * @returns the updated state
*/ */
function removeDappConnectionHelper(initialState: DappState, dappUrl: string, account?: Account): DappState { function removeDappConnectionHelper(initialState: DappState, dappUrl: string, account?: Account): DappState {
const newState = cloneDeep(initialState) const dappUrlState = initialState[dappUrl]
const dappInfo = newState[dappUrl]
if (!dappInfo) { if (!dappUrlState) {
return initialState return initialState
} }
dappInfo.connectedAccounts = dappInfo.connectedAccounts.filter( const updatedAccounts = account
(existingAccount) => existingAccount.address !== account?.address, ? dappUrlState.connectedAccounts?.filter((existingAccount) => existingAccount.address !== account.address)
) : []
const nextConnectedAccount = dappInfo.connectedAccounts[0]
if (!nextConnectedAccount || !account) { const activeConnected = updatedAccounts[0]
delete newState[dappUrl] if (activeConnected) {
return newState return {
...initialState,
[dappUrl]: {
...dappUrlState,
connectedAccounts: updatedAccounts,
activeConnectedAddress: activeConnected.address,
},
} }
} else {
if (dappInfo.activeConnectedAddress === account.address) { // eslint-disable-next-line @typescript-eslint/no-unused-vars
dappInfo.activeConnectedAddress = nextConnectedAccount.address const { [dappUrl]: _, ...restState } = initialState
return restState
} }
return newState
} }
function removeAllDappConnections(): void { function removeAllDappConnections(): void {
......
...@@ -132,7 +132,7 @@ export function ViewMnemonic(): JSX.Element { ...@@ -132,7 +132,7 @@ export function ViewMnemonic(): JSX.Element {
) : ( ) : (
<Flex gap="$spacing16" my="$spacing24" pt="$spacing8" width="100%"> <Flex gap="$spacing16" my="$spacing24" pt="$spacing8" width="100%">
<MnemonicViewer mnemonic={onboardingAccountMnemonic} /> <MnemonicViewer mnemonic={onboardingAccountMnemonic} />
<Flex backgroundColor="$surface2" borderRadius="$rounded16" p="$spacing12"> <Flex backgroundColor="$surface2" borderRadius="$rounded16" p="$spacing12" overflow="hidden">
<LabeledCheckbox <LabeledCheckbox
checked={disclaimerChecked} checked={disclaimerChecked}
text={<Text variant="body3">{t('onboarding.backup.view.disclaimer')}</Text>} text={<Text variant="body3">{t('onboarding.backup.view.disclaimer')}</Text>}
......
...@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' ...@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { removeAllDappConnectionsForAccount } from 'src/app/features/dapp/actions' import { removeAllDappConnectionsForAccount } from 'src/app/features/dapp/actions'
import { ContextMenu, Flex, TouchableArea } from 'ui/src' import { ContextMenu, Flex, TouchableArea } from 'ui/src'
import { Ellipsis, Power } from 'ui/src/components/icons' import { Power, TripleDots } from 'ui/src/components/icons'
import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants' import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { pushNotification } from 'wallet/src/features/notifications/slice' import { pushNotification } from 'wallet/src/features/notifications/slice'
...@@ -49,7 +49,7 @@ export function EllipsisDropdown(): JSX.Element { ...@@ -49,7 +49,7 @@ export function EllipsisDropdown(): JSX.Element {
onLeftClick={true} onLeftClick={true}
> >
<TouchableArea borderRadius="$roundedFull" hoverStyle={{ backgroundColor: '$surface2Hovered' }} p="$spacing8"> <TouchableArea borderRadius="$roundedFull" hoverStyle={{ backgroundColor: '$surface2Hovered' }} p="$spacing8">
<Ellipsis color="$neutral2" size="$icon.16" /> <TripleDots color="$neutral2" size="$icon.16" />
</TouchableArea> </TouchableArea>
</ContextMenu> </ContextMenu>
) )
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Uniswap Extension", "name": "Uniswap Extension",
"description": "The Uniswap Extension is a self-custody crypto wallet that's built for swapping.", "description": "The Uniswap Extension is a self-custody crypto wallet that's built for swapping.",
"version": "1.7.0", "version": "1.6.0",
"minimum_chrome_version": "116", "minimum_chrome_version": "116",
"icons": { "icons": {
"16": "assets/icon16.png", "16": "assets/icon16.png",
......
...@@ -90,9 +90,9 @@ if (isCI && datadogPropertiesAvailable && !isDetox) { ...@@ -90,9 +90,9 @@ if (isCI && datadogPropertiesAvailable && !isDetox) {
apply from: "../../../../node_modules/@datadog/mobile-react-native/datadog-sourcemaps.gradle" apply from: "../../../../node_modules/@datadog/mobile-react-native/datadog-sourcemaps.gradle"
} }
def devVersionName = "1.37" def devVersionName = "1.36"
def betaVersionName = "1.37" def betaVersionName = "1.36"
def prodVersionName = "1.37" def prodVersionName = "1.36"
android { android {
ndkVersion rootProject.ext.ndkVersion ndkVersion rootProject.ext.ndkVersion
......
...@@ -2167,7 +2167,7 @@ ...@@ -2167,7 +2167,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2220,7 +2220,7 @@ ...@@ -2220,7 +2220,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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 = schemes.WidgetsCore; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
...@@ -2273,7 +2273,7 @@ ...@@ -2273,7 +2273,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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 = schemes.WidgetsCore; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
...@@ -2326,7 +2326,7 @@ ...@@ -2326,7 +2326,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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 = schemes.WidgetsCore; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCore;
...@@ -2364,7 +2364,7 @@ ...@@ -2364,7 +2364,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2400,7 +2400,7 @@ ...@@ -2400,7 +2400,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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 = schemes.WidgetsCoreTests; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
...@@ -2435,7 +2435,7 @@ ...@@ -2435,7 +2435,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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 = schemes.WidgetsCoreTests; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
...@@ -2470,7 +2470,7 @@ ...@@ -2470,7 +2470,7 @@
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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 = schemes.WidgetsCoreTests; PRODUCT_BUNDLE_IDENTIFIER = schemes.WidgetsCoreTests;
...@@ -2517,7 +2517,7 @@ ...@@ -2517,7 +2517,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2563,7 +2563,7 @@ ...@@ -2563,7 +2563,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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;
...@@ -2609,7 +2609,7 @@ ...@@ -2609,7 +2609,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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.dev.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets;
...@@ -2655,7 +2655,7 @@ ...@@ -2655,7 +2655,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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;
...@@ -2697,7 +2697,7 @@ ...@@ -2697,7 +2697,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -2740,7 +2740,7 @@ ...@@ -2740,7 +2740,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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;
...@@ -2783,7 +2783,7 @@ ...@@ -2783,7 +2783,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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.dev.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension;
...@@ -2826,7 +2826,7 @@ ...@@ -2826,7 +2826,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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;
...@@ -2862,7 +2862,7 @@ ...@@ -2862,7 +2862,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -2900,7 +2900,7 @@ ...@@ -2900,7 +2900,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3078,7 +3078,7 @@ ...@@ -3078,7 +3078,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
...@@ -3122,7 +3122,7 @@ ...@@ -3122,7 +3122,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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;
...@@ -3222,7 +3222,7 @@ ...@@ -3222,7 +3222,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3293,7 +3293,7 @@ ...@@ -3293,7 +3293,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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;
...@@ -3393,7 +3393,7 @@ ...@@ -3393,7 +3393,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3464,7 +3464,7 @@ ...@@ -3464,7 +3464,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.37; MARKETING_VERSION = 1.36;
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.dev.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension;
......
...@@ -22,7 +22,7 @@ public class DataQueries { ...@@ -22,7 +22,7 @@ public class DataQueries {
let tokens = graphQLResult.data?.tokens ?? [] let tokens = graphQLResult.data?.tokens ?? []
let tokenResponses = tokens.map { let tokenResponses = tokens.map {
let symbol = $0?.symbol let symbol = $0?.symbol
let name = $0?.name let name = $0?.project?.name
let chain = $0?.chain let chain = $0?.chain
let address = $0?.address let address = $0?.address
return TokenResponse(chain: chain?.rawValue ?? "", address: address, symbol: symbol ?? "", name: name ?? "") return TokenResponse(chain: chain?.rawValue ?? "", address: address, symbol: symbol ?? "", name: name ?? "")
...@@ -43,7 +43,7 @@ public class DataQueries { ...@@ -43,7 +43,7 @@ public class DataQueries {
let topTokens = graphQLResult.data?.topTokens ?? [] let topTokens = graphQLResult.data?.topTokens ?? []
let tokenResponses = topTokens.map { (tokenData) -> TokenResponse in let tokenResponses = topTokens.map { (tokenData) -> TokenResponse in
let symbol = tokenData?.symbol let symbol = tokenData?.symbol
let name = tokenData?.name let name = tokenData?.project?.name
let chain = tokenData?.chain let chain = tokenData?.chain
let address = tokenData?.address let address = tokenData?.address
return TokenResponse(chain: chain?.rawValue ?? "", address: address, symbol: symbol ?? "", name: name ?? "") return TokenResponse(chain: chain?.rawValue ?? "", address: address, symbol: symbol ?? "", name: name ?? "")
...@@ -63,7 +63,7 @@ public class DataQueries { ...@@ -63,7 +63,7 @@ public class DataQueries {
case .success(let graphQLResult): case .success(let graphQLResult):
let token = graphQLResult.data?.token let token = graphQLResult.data?.token
let symbol = token?.symbol let symbol = token?.symbol
let name = token?.name let name = token?.project?.name
let logoUrl = token?.project?.logoUrl ?? nil let logoUrl = token?.project?.logoUrl ?? nil
let markets = token?.project?.markets let markets = token?.project?.markets
let spotPrice = (markets != nil) && !markets!.isEmpty ? markets?[0]?.price?.value : nil let spotPrice = (markets != nil) && !markets!.isEmpty ? markets?[0]?.price?.value : nil
...@@ -109,7 +109,7 @@ public class DataQueries { ...@@ -109,7 +109,7 @@ public class DataQueries {
$0?.tokenBalances?.forEach { tokenBalance in $0?.tokenBalances?.forEach { tokenBalance in
let value = tokenBalance?.denominatedValue?.value let value = tokenBalance?.denominatedValue?.value
let token = tokenBalance?.token let token = tokenBalance?.token
let tokenResponse = TokenResponse(chain: token?.chain.rawValue ?? "", address: token?.address, symbol: token?.symbol ?? "", name: token?.name ?? "") let tokenResponse = TokenResponse(chain: token?.chain.rawValue ?? "", address: token?.address, symbol: token?.symbol ?? "", name: token?.project?.name ?? "")
let isSpam = token?.project?.isSpam ?? false let isSpam = token?.project?.isSpam ?? false
if (!isSpam) { if (!isSpam) {
tokens[tokenResponse] = (tokens[tokenResponse] ?? 0) + (value ?? 0) tokens[tokenResponse] = (tokens[tokenResponse] ?? 0) + (value ?? 0)
......
...@@ -122,7 +122,7 @@ exports[`PriceText renders without error 1`] = ` ...@@ -122,7 +122,7 @@ exports[`PriceText renders without error 1`] = `
jestAnimatedStyle={ jestAnimatedStyle={
{ {
"value": { "value": {
"color": "#BFBFBF", "color": "#CECECE",
"fontSize": 106, "fontSize": 106,
}, },
} }
...@@ -130,7 +130,7 @@ exports[`PriceText renders without error 1`] = ` ...@@ -130,7 +130,7 @@ exports[`PriceText renders without error 1`] = `
maxFontSizeMultiplier={1.2} maxFontSizeMultiplier={1.2}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 106, "fontSize": 106,
"fontWeight": "400", "fontWeight": "400",
......
...@@ -27,7 +27,7 @@ export function TokenDetailsHeader({ ...@@ -27,7 +27,7 @@ export function TokenDetailsHeader({
<Flex gap="$spacing12" mx="$spacing16"> <Flex gap="$spacing12" mx="$spacing16">
<TokenLogo <TokenLogo
chainId={fromGraphQLChain(token?.chain) ?? undefined} chainId={fromGraphQLChain(token?.chain) ?? undefined}
name={token?.name ?? undefined} name={token?.project?.name ?? undefined}
symbol={token?.symbol ?? undefined} symbol={token?.symbol ?? undefined}
url={tokenProject?.logoUrl ?? undefined} url={tokenProject?.logoUrl ?? undefined}
/> />
...@@ -40,7 +40,7 @@ export function TokenDetailsHeader({ ...@@ -40,7 +40,7 @@ export function TokenDetailsHeader({
testID={TestID.TokenDetailsHeaderText} testID={TestID.TokenDetailsHeaderText}
variant="subheading1" variant="subheading1"
> >
{token?.name ?? ''} {tokenProject?.name ?? ''}
</Text> </Text>
{/* Suppress warning icon on low warning level */} {/* Suppress warning icon on low warning level */}
{(tokenProject?.safetyLevel === SafetyLevel.StrongWarning || {(tokenProject?.safetyLevel === SafetyLevel.StrongWarning ||
......
...@@ -349,7 +349,7 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -349,7 +349,7 @@ exports[`AccountHeader renders correctly 1`] = `
jestAnimatedStyle={ jestAnimatedStyle={
{ {
"value": { "value": {
"color": "#BFBFBF", "color": "#CECECE",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
"lineHeight": 24, "lineHeight": 24,
...@@ -360,7 +360,7 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -360,7 +360,7 @@ exports[`AccountHeader renders correctly 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -415,7 +415,7 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -415,7 +415,7 @@ exports[`AccountHeader renders correctly 1`] = `
numberOfLines={1} numberOfLines={1}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 17, "fontSize": 17,
"fontWeight": "400", "fontWeight": "400",
...@@ -443,7 +443,7 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -443,7 +443,7 @@ exports[`AccountHeader renders correctly 1`] = `
"borderWidth": 0, "borderWidth": 0,
}, },
{ {
"color": "#BFBFBF", "color": "#CECECE",
"height": 16, "height": 16,
"width": 16, "width": 16,
}, },
...@@ -454,7 +454,7 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -454,7 +454,7 @@ exports[`AccountHeader renders correctly 1`] = `
}, },
] ]
} }
tintColor="#BFBFBF" tintColor="#CECECE"
vbHeight={16} vbHeight={16}
vbWidth={16} vbWidth={16}
> >
......
...@@ -224,8 +224,8 @@ function gqlTokenToTokenItemData( ...@@ -224,8 +224,8 @@ function gqlTokenToTokenItemData(
return null return null
} }
const { name, symbol, address, chain, project, market } = token const { symbol, address, chain, project, market } = token
const { logoUrl, markets } = project const { logoUrl, markets, name } = project
const tokenProjectMarket = markets?.[0] const tokenProjectMarket = markets?.[0]
const chainId = fromGraphQLChain(chain) const chainId = fromGraphQLChain(chain)
......
import { default as React } from 'react' import { default as React } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Flex, Text, TouchableArea } from 'ui/src' import { Flex, Text, TouchableArea } from 'ui/src'
import { Ellipsis } from 'ui/src/components/icons' import { TripleDots } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { TestID } from 'uniswap/src/test/fixtures/testIDs' import { TestID } from 'uniswap/src/test/fixtures/testIDs'
...@@ -24,7 +24,7 @@ export function FavoriteHeaderRow({ ...@@ -24,7 +24,7 @@ export function FavoriteHeaderRow({
</Text> </Text>
{!isEditing ? ( {!isEditing ? (
<TouchableArea hapticFeedback hitSlop={16} testID={TestID.Edit} onPress={onPress}> <TouchableArea hapticFeedback hitSlop={16} testID={TestID.Edit} onPress={onPress}>
<Ellipsis color="$neutral2" size={iconSizes.icon20} strokeLinecap="round" strokeWidth={1} /> <TripleDots color="$neutral2" size={iconSizes.icon20} strokeLinecap="round" strokeWidth={1} />
</TouchableArea> </TouchableArea>
) : ( ) : (
<TouchableArea hitSlop={16} onPress={onPress}> <TouchableArea hitSlop={16} onPress={onPress}>
......
...@@ -124,7 +124,7 @@ function FavoriteTokenCard({ ...@@ -124,7 +124,7 @@ function FavoriteTokenCard({
<Flex grow row alignItems="center" gap="$spacing8"> <Flex grow row alignItems="center" gap="$spacing8">
<TokenLogo <TokenLogo
chainId={chainId ?? undefined} chainId={chainId ?? undefined}
name={token?.name ?? undefined} name={token?.project?.name ?? undefined}
size={imageSizes.image20} size={imageSizes.image20}
symbol={token?.symbol ?? undefined} symbol={token?.symbol ?? undefined}
url={token?.project?.logoUrl ?? undefined} url={token?.project?.logoUrl ?? undefined}
......
...@@ -136,11 +136,12 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = ` ...@@ -136,11 +136,12 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
align="xMidYMid" align="xMidYMid"
bbHeight={20} bbHeight={20}
bbWidth={20} bbWidth={20}
fill="none" fill="currentColor"
focusable={false} focusable={false}
meetOrSlice={0} meetOrSlice={0}
minX={0} minX={0}
minY={0} minY={0}
stroke="currentColor"
strokeLinecap="round" strokeLinecap="round"
strokeWidth={1} strokeWidth={1}
style={ style={
...@@ -166,19 +167,29 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = ` ...@@ -166,19 +167,29 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
vbWidth={18} vbWidth={18}
> >
<RNSVGGroup <RNSVGGroup
fill={null} fill={
{
"type": 2,
}
}
propList={ propList={
[ [
"fill", "fill",
"stroke",
"strokeWidth", "strokeWidth",
"strokeLinecap", "strokeLinecap",
] ]
} }
stroke={
{
"type": 2,
}
}
strokeLinecap={1} strokeLinecap={1}
strokeWidth={1} strokeWidth={1}
> >
<RNSVGPath <RNSVGPath
d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" d="M9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
fill={ fill={
{ {
"payload": 4278190080, "payload": 4278190080,
...@@ -203,7 +214,7 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = ` ...@@ -203,7 +214,7 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
strokeWidth="2" strokeWidth="2"
/> />
<RNSVGPath <RNSVGPath
d="M16 3C16.5523 3 17 2.55228 17 2C17 1.44772 16.5523 1 16 1C15.4477 1 15 1.44772 15 2C15 2.55228 15.4477 3 16 3Z" d="M16 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
fill={ fill={
{ {
"payload": 4278190080, "payload": 4278190080,
...@@ -228,7 +239,7 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = ` ...@@ -228,7 +239,7 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
strokeWidth="2" strokeWidth="2"
/> />
<RNSVGPath <RNSVGPath
d="M2 3C2.55228 3 3 2.55228 3 2C3 1.44772 2.55228 1 2 1C1.44772 1 1 1.44772 1 2C1 2.55228 1.44772 3 2 3Z" d="M2 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
fill={ fill={
{ {
"payload": 4278190080, "payload": 4278190080,
......
...@@ -255,7 +255,7 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -255,7 +255,7 @@ exports[`FavoriteWalletCard renders without error 1`] = `
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": "#BFBFBF", "backgroundColor": "#CECECE",
"borderBottomLeftRadius": 999999, "borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999, "borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999, "borderTopLeftRadius": 999999,
......
...@@ -25,7 +25,7 @@ exports[`RemoveButton renders without error 1`] = ` ...@@ -25,7 +25,7 @@ exports[`RemoveButton renders without error 1`] = `
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": "#BFBFBF", "backgroundColor": "#CECECE",
"borderBottomLeftRadius": 999999, "borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999, "borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999, "borderTopLeftRadius": 999999,
......
...@@ -95,35 +95,14 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -95,35 +95,14 @@ exports[`TokenItem renders without error 1`] = `
"flexDirection": "column", "flexDirection": "column",
"height": 40, "height": 40,
"justifyContent": "center", "justifyContent": "center",
"position": "relative",
"width": 40, "width": 40,
} }
} }
testID="token-logo" testID="token-logo"
> >
<View
style={
{
"backgroundColor": "#FFFFFF",
"borderBottomLeftRadius": 20,
"borderBottomRightRadius": 20,
"borderTopLeftRadius": 20,
"borderTopRightRadius": 20,
"flexDirection": "column",
"height": "96%",
"left": "2%",
"opacity": 0,
"position": "absolute",
"top": "2%",
"width": "96%",
"zIndex": -1,
}
}
/>
<Image <Image
height={40} height={40}
onError={[Function]} onError={[Function]}
onLoad={[Function]}
source={ source={
{ {
"uri": "https://loremflickr.com/640/480", "uri": "https://loremflickr.com/640/480",
...@@ -132,6 +111,7 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -132,6 +111,7 @@ exports[`TokenItem renders without error 1`] = `
style={ style={
{ {
"aspectRatio": undefined, "aspectRatio": undefined,
"backgroundColor": "#FFFFFF",
"borderRadius": 20, "borderRadius": 20,
"flex": undefined, "flex": undefined,
} }
......
...@@ -5,9 +5,9 @@ query SearchPopularTokens { ...@@ -5,9 +5,9 @@ query SearchPopularTokens {
chain chain
symbol symbol
decimals decimals
name
project { project {
id id
name
logoUrl logoUrl
safetyLevel safetyLevel
} }
......
...@@ -13,7 +13,8 @@ function gqlTokenToTokenSearchResult(token: Maybe<TopToken>): TokenSearchResult ...@@ -13,7 +13,8 @@ function gqlTokenToTokenSearchResult(token: Maybe<TopToken>): TokenSearchResult
return null return null
} }
const { name, chain, address, symbol, project, protectionInfo } = token const { chain, address, symbol, project, protectionInfo } = token
const { name } = project
const chainId = fromGraphQLChain(chain) const chainId = fromGraphQLChain(chain)
if (!chainId || !symbol || !name) { if (!chainId || !symbol || !name) {
return null return null
......
...@@ -7,7 +7,15 @@ import { ...@@ -7,7 +7,15 @@ import {
import { Chain, ExploreSearchQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { Chain, ExploreSearchQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { fromGraphQLChain } from 'uniswap/src/features/chains/utils'
import { SearchResultType } from 'uniswap/src/features/search/SearchResult' import { SearchResultType } from 'uniswap/src/features/search/SearchResult'
import { amount, ethToken, nftCollection, nftContract, token, tokenMarket } from 'uniswap/src/test/fixtures' import {
amount,
ethToken,
nftCollection,
nftContract,
token,
tokenMarket,
tokenProject,
} from 'uniswap/src/test/fixtures'
import { createArray } from 'uniswap/src/test/utils' import { createArray } from 'uniswap/src/test/utils'
type ExploreSearchResult = NonNullable<ExploreSearchQuery> type ExploreSearchResult = NonNullable<ExploreSearchQuery>
...@@ -54,8 +62,8 @@ describe(formatTokenSearchResults, () => { ...@@ -54,8 +62,8 @@ describe(formatTokenSearchResults, () => {
it('sorts results by best search query match', () => { it('sorts results by best search query match', () => {
const data: ExploreSearchResult['searchTokens'] = [ const data: ExploreSearchResult['searchTokens'] = [
token({ name: 'UniswapStartingName' }), ethToken({ project: tokenProject({ name: 'UniswapStartingName' }) }),
token({ name: 'Uniswap' }), ethToken({ project: tokenProject({ name: 'Uniswap' }) }),
] ]
const result = formatTokenSearchResults(data, 'uniswap') const result = formatTokenSearchResults(data, 'uniswap')
...@@ -75,7 +83,7 @@ describe(formatTokenSearchResults, () => { ...@@ -75,7 +83,7 @@ describe(formatTokenSearchResults, () => {
expect(result?.[0]?.type).toEqual(SearchResultType.Token) expect(result?.[0]?.type).toEqual(SearchResultType.Token)
expect(result?.[0]?.chainId).toEqual(fromGraphQLChain(searchToken.chain)) expect(result?.[0]?.chainId).toEqual(fromGraphQLChain(searchToken.chain))
expect(result?.[0]?.address).toEqual(searchToken.address) expect(result?.[0]?.address).toEqual(searchToken.address)
expect(result?.[0]?.name).toEqual(searchToken.name) expect(result?.[0]?.name).toEqual(searchToken.project?.name)
expect(result?.[0]?.symbol).toEqual(searchToken.symbol) expect(result?.[0]?.symbol).toEqual(searchToken.symbol)
expect(result?.[0]?.logoUrl).toEqual(searchToken.project?.logoUrl) expect(result?.[0]?.logoUrl).toEqual(searchToken.project?.logoUrl)
expect(result?.[0]?.safetyLevel).toEqual(searchToken.project?.safetyLevel) expect(result?.[0]?.safetyLevel).toEqual(searchToken.project?.safetyLevel)
......
...@@ -31,14 +31,14 @@ export function formatTokenSearchResults( ...@@ -31,14 +31,14 @@ export function formatTokenSearchResults(
return tokensMap return tokensMap
} }
const { name, chain, address, symbol, project, market, protectionInfo } = token const { chain, address, symbol, project, market, protectionInfo } = token
const chainId = fromGraphQLChain(chain) const chainId = fromGraphQLChain(chain)
if (!chainId || !project) { if (!chainId || !project) {
return tokensMap return tokensMap
} }
const { safetyLevel, logoUrl } = project const { name, safetyLevel, logoUrl } = project
const tokenResult: TokenSearchResult & { volume1D: number } = { const tokenResult: TokenSearchResult & { volume1D: number } = {
type: SearchResultType.Token, type: SearchResultType.Token,
......
...@@ -188,8 +188,8 @@ function gqlTokenToTokenItemData( ...@@ -188,8 +188,8 @@ function gqlTokenToTokenItemData(
return null return null
} }
const { name, symbol, address, chain, project } = token const { symbol, address, chain, project } = token
const { logoUrl, markets } = project const { logoUrl, markets, name } = project
const tokenProjectMarket = markets?.[0] const tokenProjectMarket = markets?.[0]
const chainId = fromGraphQLChain(chain) const chainId = fromGraphQLChain(chain)
......
...@@ -21,7 +21,7 @@ exports[`renders a DecimalNumber 1`] = ` ...@@ -21,7 +21,7 @@ exports[`renders a DecimalNumber 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -57,7 +57,7 @@ exports[`renders a DecimalNumber without a comma separator 1`] = ` ...@@ -57,7 +57,7 @@ exports[`renders a DecimalNumber without a comma separator 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
......
...@@ -46,7 +46,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -46,7 +46,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -62,7 +62,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -62,7 +62,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -158,7 +158,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -158,7 +158,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -174,7 +174,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -174,7 +174,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -190,7 +190,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -190,7 +190,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -206,7 +206,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -206,7 +206,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -222,7 +222,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -222,7 +222,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -238,7 +238,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -238,7 +238,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -254,7 +254,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -254,7 +254,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -270,7 +270,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -270,7 +270,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -286,7 +286,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -286,7 +286,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -302,7 +302,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -302,7 +302,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -318,7 +318,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -318,7 +318,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -334,7 +334,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -334,7 +334,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
...@@ -350,7 +350,7 @@ exports[`renders text with few matches 1`] = ` ...@@ -350,7 +350,7 @@ exports[`renders text with few matches 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 19, "fontSize": 19,
"fontWeight": "400", "fontWeight": "400",
......
...@@ -62,10 +62,10 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = ` ...@@ -62,10 +62,10 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
onChangeText={[Function]} onChangeText={[Function]}
onFocus={[Function]} onFocus={[Function]}
onSubmitEditing={[Function]} onSubmitEditing={[Function]}
placeholderTextColor="#BFBFBF" placeholderTextColor="#CECECE"
returnKeyType="done" returnKeyType="done"
scrollEnabled={false} scrollEnabled={false}
selectionColor="#BFBFBF" selectionColor="#CECECE"
spellCheck={false} spellCheck={false}
style={ style={
{ {
......
...@@ -14,7 +14,7 @@ import { DeleteUnitagModal } from 'src/components/unitags/DeleteUnitagModal' ...@@ -14,7 +14,7 @@ import { DeleteUnitagModal } from 'src/components/unitags/DeleteUnitagModal'
import { UnitagProfilePicture } from 'src/components/unitags/UnitagProfilePicture' import { UnitagProfilePicture } from 'src/components/unitags/UnitagProfilePicture'
import { HeaderRadial, solidHeaderProps } from 'src/features/externalProfile/ProfileHeader' import { HeaderRadial, solidHeaderProps } from 'src/features/externalProfile/ProfileHeader'
import { Button, Flex, LinearGradient, ScrollView, Text, getUniconColors, useIsDarkMode, useSporeColors } from 'ui/src' import { Button, Flex, LinearGradient, ScrollView, Text, getUniconColors, useIsDarkMode, useSporeColors } from 'ui/src'
import { Ellipsis, Pen } from 'ui/src/components/icons' import { Pen, TripleDots } from 'ui/src/components/icons'
import { borderRadii, fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme' import { borderRadii, fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme'
import { useExtractedColors } from 'ui/src/utils/colors' import { useExtractedColors } from 'ui/src/utils/colors'
import { TextInput } from 'uniswap/src/components/input/TextInput' import { TextInput } from 'uniswap/src/components/input/TextInput'
...@@ -271,7 +271,7 @@ export function EditUnitagProfileScreen({ route }: UnitagStackScreenProp<UnitagS ...@@ -271,7 +271,7 @@ export function EditUnitagProfileScreen({ route }: UnitagStackScreenProp<UnitagS
}} }}
> >
<Flex pr="$spacing8"> <Flex pr="$spacing8">
<Ellipsis color="$neutral2" size={iconSizes.icon24} /> <TripleDots color="$neutral2" size={iconSizes.icon24} />
</Flex> </Flex>
</ContextMenu> </ContextMenu>
) : undefined ) : undefined
......
...@@ -274,10 +274,10 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` ...@@ -274,10 +274,10 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
onFocus={[Function]} onFocus={[Function]}
onSubmitEditing={[Function]} onSubmitEditing={[Function]}
placeholder="Enter password" placeholder="Enter password"
placeholderTextColor="#BFBFBF" placeholderTextColor="#CECECE"
returnKeyType="done" returnKeyType="done"
secureTextEntry={true} secureTextEntry={true}
selectionColor="#BFBFBF" selectionColor="#CECECE"
style={ style={
{ {
"backgroundColor": "transparent", "backgroundColor": "transparent",
......
...@@ -737,7 +737,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = ` ...@@ -737,7 +737,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = `
"borderWidth": 0, "borderWidth": 0,
}, },
{ {
"color": "#BFBFBF", "color": "#CECECE",
"height": 20, "height": 20,
"width": 20, "width": 20,
}, },
...@@ -748,7 +748,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = ` ...@@ -748,7 +748,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = `
}, },
] ]
} }
tintColor="#BFBFBF" tintColor="#CECECE"
vbHeight={24} vbHeight={24}
vbWidth={24} vbWidth={24}
> >
...@@ -782,7 +782,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = ` ...@@ -782,7 +782,7 @@ exports[`BackupScreen renders backup options when none are completed 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 17, "fontSize": 17,
"fontWeight": "400", "fontWeight": "400",
...@@ -1539,7 +1539,7 @@ exports[`BackupScreen renders backup options when some are completed 1`] = ` ...@@ -1539,7 +1539,7 @@ exports[`BackupScreen renders backup options when some are completed 1`] = `
"borderWidth": 0, "borderWidth": 0,
}, },
{ {
"color": "#BFBFBF", "color": "#CECECE",
"height": 20, "height": 20,
"width": 20, "width": 20,
}, },
...@@ -1550,7 +1550,7 @@ exports[`BackupScreen renders backup options when some are completed 1`] = ` ...@@ -1550,7 +1550,7 @@ exports[`BackupScreen renders backup options when some are completed 1`] = `
}, },
] ]
} }
tintColor="#BFBFBF" tintColor="#CECECE"
vbHeight={24} vbHeight={24}
vbWidth={24} vbWidth={24}
> >
...@@ -1584,7 +1584,7 @@ exports[`BackupScreen renders backup options when some are completed 1`] = ` ...@@ -1584,7 +1584,7 @@ exports[`BackupScreen renders backup options when some are completed 1`] = `
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
style={ style={
{ {
"color": "#BFBFBF", "color": "#CECECE",
"fontFamily": "Basel Grotesk", "fontFamily": "Basel Grotesk",
"fontSize": 17, "fontSize": 17,
"fontWeight": "400", "fontWeight": "400",
......
...@@ -141,9 +141,9 @@ export function TokenDetailsScreen({ route }: AppStackScreenProp<MobileScreens.T ...@@ -141,9 +141,9 @@ export function TokenDetailsScreen({ route }: AppStackScreenProp<MobileScreens.T
() => ({ () => ({
address: currencyIdToAddress(_currencyId), address: currencyIdToAddress(_currencyId),
chain: currencyIdToChain(_currencyId), chain: currencyIdToChain(_currencyId),
currencyName: data?.token?.name, currencyName: data?.token?.project?.name,
}), }),
[_currencyId, data?.token?.name], [_currencyId, data?.token?.project?.name],
) )
return ( return (
...@@ -186,7 +186,7 @@ function TokenDetails({ ...@@ -186,7 +186,7 @@ function TokenDetails({
const token = data?.token const token = data?.token
const tokenLogoUrl = token?.project?.logoUrl const tokenLogoUrl = token?.project?.logoUrl
const tokenSymbol = token?.name const tokenSymbol = token?.project?.name
const currencyInfo = useCurrencyInfo(_currencyId) const currencyInfo = useCurrencyInfo(_currencyId)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -106,13 +106,13 @@ function TokenRow({ ...@@ -106,13 +106,13 @@ function TokenRow({
element={InterfaceElementName.MINI_PORTFOLIO_TOKEN_ROW} element={InterfaceElementName.MINI_PORTFOLIO_TOKEN_ROW}
properties={{ properties={{
chain_id: currency.chainId, chain_id: currency.chainId,
token_name: token?.name ?? token?.project?.name, token_name: token?.project?.name ?? token?.name,
address: token?.address, address: token?.address,
}} }}
> >
<PortfolioRow <PortfolioRow
left={<PortfolioLogo chainId={currency.chainId} currencies={[currency]} size={40} />} left={<PortfolioLogo chainId={currency.chainId} currencies={[currency]} size={40} />}
title={<TokenNameText>{token?.name ?? token?.project?.name}</TokenNameText>} title={<TokenNameText>{token?.project?.name ?? token?.name}</TokenNameText>}
descriptor={ descriptor={
<TokenBalanceText> <TokenBalanceText>
{formatNumber({ {formatNumber({
......
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { Position } from '@uniswap/client-pools/dist/pools/v1/types_pb' import { Position } from '@uniswap/client-pools/dist/pools/v1/types_pb'
import { BadgeData, LiquidityPositionInfoBadges } from 'components/Liquidity/LiquidityPositionInfoBadges' import { LiquidityPositionInfoBadges } from 'components/Liquidity/LiquidityPositionInfoBadges'
import { LiquidityPositionStatusIndicator } from 'components/Liquidity/LiquidityPositionStatusIndicator' import { LiquidityPositionStatusIndicator } from 'components/Liquidity/LiquidityPositionStatusIndicator'
import { getProtocolVersionLabel, usePositionInfo } from 'components/Liquidity/utils' import { getProtocolVersionLabel, usePositionInfo } from 'components/Liquidity/utils'
import { DoubleCurrencyAndChainLogo } from 'components/Logo/DoubleLogo' import { DoubleCurrencyAndChainLogo } from 'components/Logo/DoubleLogo'
import { Flex, Text } from 'ui/src' import { Flex, Text } from 'ui/src'
import { DocumentList } from 'ui/src/components/icons/DocumentList'
interface LiquidityPositionInfoProps { interface LiquidityPositionInfoProps {
position: Position position: Position
...@@ -33,15 +32,7 @@ export function LiquidityPositionInfo({ position }: LiquidityPositionInfoProps) ...@@ -33,15 +32,7 @@ export function LiquidityPositionInfo({ position }: LiquidityPositionInfoProps)
<Flex row gap={2} alignItems="center"> <Flex row gap={2} alignItems="center">
<LiquidityPositionInfoBadges <LiquidityPositionInfoBadges
size="small" size="small"
badges={ labels={[versionLabel, v4hook, feeTier].filter(Boolean) as string[]}
[
versionLabel ? { label: versionLabel } : undefined,
v4hook
? { label: v4hook, copyable: true, icon: <DocumentList color="$neutral2" size={16} /> }
: undefined,
feeTier ? { label: `${Number(feeTier) / 10000}%` } : undefined,
].filter(Boolean) as BadgeData[]
}
/> />
</Flex> </Flex>
</Flex> </Flex>
......
import { LiquidityPositionInfoBadges } from 'components/Liquidity/LiquidityPositionInfoBadges' import { LiquidityPositionInfoBadges } from 'components/Liquidity/LiquidityPositionInfoBadges'
import { render } from 'test-utils/render' import { render } from 'test-utils/render'
const testBadgeData = [{ label: 'test', copyable: true }, { label: 'test2' }]
describe('LiquidityPositionInfoBadges', () => { describe('LiquidityPositionInfoBadges', () => {
it('should render with default size', () => { it('should render with default size', () => {
const { getByText } = render(<LiquidityPositionInfoBadges badges={testBadgeData} size="default" />) const { getByText } = render(<LiquidityPositionInfoBadges labels={['test']} size="default" />)
expect(getByText('test')).toBeInTheDocument() expect(getByText('test')).toBeInTheDocument()
}) })
it('should render with small size', () => { it('should render with small size', () => {
const { getByText } = render(<LiquidityPositionInfoBadges badges={testBadgeData} size="small" />) const { getByText } = render(<LiquidityPositionInfoBadges labels={['test']} size="small" />)
expect(getByText('test')).toBeInTheDocument() expect(getByText('test')).toBeInTheDocument()
}) })
it('should render with multiple badges', () => { it('should render with multiple labels', () => {
const { getByText } = render(<LiquidityPositionInfoBadges badges={testBadgeData} size="default" />) const { getByText } = render(<LiquidityPositionInfoBadges labels={['test', 'test2']} size="default" />)
expect(getByText('test')).toBeInTheDocument() expect(getByText('test')).toBeInTheDocument()
expect(getByText('test2')).toBeInTheDocument() expect(getByText('test2')).toBeInTheDocument()
}) })
......
import { CopyHelper } from 'theme/components'
import { styled, Text } from 'ui/src' import { styled, Text } from 'ui/src'
import { isAddress, shortenAddress } from 'utilities/src/addresses'
const PositionInfoBadge = styled(Text, { const PositionInfoBadge = styled(Text, {
display: 'flex',
flexDirection: 'row',
gap: '$spacing2',
variant: 'body3', variant: 'body3',
color: '$neutral2', color: '$neutral2',
backgroundColor: '$surface3', backgroundColor: '$surface3',
...@@ -41,41 +36,20 @@ function getPlacement(index: number, length: number): 'start' | 'middle' | 'end' ...@@ -41,41 +36,20 @@ function getPlacement(index: number, length: number): 'start' | 'middle' | 'end'
return length === 1 ? 'only' : index === 0 ? 'start' : index === length - 1 ? 'end' : 'middle' return length === 1 ? 'only' : index === 0 ? 'start' : index === length - 1 ? 'end' : 'middle'
} }
export interface BadgeData {
label: string
copyable?: boolean
icon?: JSX.Element
}
export function LiquidityPositionInfoBadges({ export function LiquidityPositionInfoBadges({
badges, labels,
size = 'default', size = 'default',
}: { }: {
badges: BadgeData[] labels: string[]
size: 'small' | 'default' size: 'small' | 'default'
}): JSX.Element { }): JSX.Element {
return ( return (
<> <>
{badges.map(({ label, copyable, icon }, index) => { {labels.map((label, index) => (
const displayLabel = isAddress(label) ? shortenAddress(label) : label <PositionInfoBadge key={label + index} placement={getPlacement(index, labels.length)} size={size}>
return ( {label}
<PositionInfoBadge
cursor={copyable ? 'pointer' : 'unset'}
key={label + index}
placement={getPlacement(index, badges.length)}
size={size}
>
{icon}
{copyable ? (
<CopyHelper toCopy={label} iconSize={12} iconPosition="right">
{displayLabel}
</CopyHelper>
) : (
displayLabel
)}
</PositionInfoBadge> </PositionInfoBadge>
) ))}
})}
</> </>
) )
} }
...@@ -11,6 +11,7 @@ import { usePool } from 'hooks/usePools' ...@@ -11,6 +11,7 @@ import { usePool } from 'hooks/usePools'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useAppSelector } from 'state/hooks' import { useAppSelector } from 'state/hooks'
import { AppTFunction } from 'ui/src/i18n/types' import { AppTFunction } from 'ui/src/i18n/types'
import { shortenAddress } from 'utilities/src/addresses'
export function getProtocolVersionLabel(version: ProtocolVersion): string | undefined { export function getProtocolVersionLabel(version: ProtocolVersion): string | undefined {
switch (version) { switch (version) {
...@@ -120,7 +121,7 @@ export function usePositionInfo(position?: Position): PositionInfo | undefined { ...@@ -120,7 +121,7 @@ export function usePositionInfo(position?: Position): PositionInfo | undefined {
status: position.status, status: position.status,
feeTier: undefined, feeTier: undefined,
version: position.protocolVersion, version: position.protocolVersion,
v4hook: v4Position.hooks[0]?.address, v4hook: v4Position.hooks[0]?.address ? shortenAddress(v4Position.hooks[0].address) : undefined,
restPosition: position, restPosition: position,
currency0Amount: CurrencyAmount.fromRawAmount( currency0Amount: CurrencyAmount.fromRawAmount(
token0, token0,
......
...@@ -8,6 +8,7 @@ const NavDropdownContent = styled(Flex, { ...@@ -8,6 +8,7 @@ const NavDropdownContent = styled(Flex, {
borderStyle: 'solid', borderStyle: 'solid',
borderColor: '$surface2', borderColor: '$surface2',
backgroundColor: '$surface1', backgroundColor: '$surface1',
overflow: 'scroll',
maxHeight: `calc(100dvh - ${NAV_HEIGHT * 2}px)`, maxHeight: `calc(100dvh - ${NAV_HEIGHT * 2}px)`,
$sm: { $sm: {
width: '100%', width: '100%',
...@@ -16,10 +17,6 @@ const NavDropdownContent = styled(Flex, { ...@@ -16,10 +17,6 @@ const NavDropdownContent = styled(Flex, {
shadowColor: '$transparent', shadowColor: '$transparent',
maxHeight: `calc(100dvh - ${NAV_HEIGHT}px)`, maxHeight: `calc(100dvh - ${NAV_HEIGHT}px)`,
}, },
'$platform-web': {
overflowY: 'auto',
overflowX: 'hidden',
},
}) })
interface NavDropdownProps { interface NavDropdownProps {
......
...@@ -11,7 +11,7 @@ import { getTokenDetailsURL, supportedChainIdFromGQLChain } from 'graphql/data/u ...@@ -11,7 +11,7 @@ import { getTokenDetailsURL, supportedChainIdFromGQLChain } from 'graphql/data/u
import styled, { css } from 'lib/styled-components' import styled, { css } from 'lib/styled-components'
import { searchGenieCollectionToTokenSearchResult, searchTokenToTokenSearchResult } from 'lib/utils/searchBar' import { searchGenieCollectionToTokenSearchResult, searchTokenToTokenSearchResult } from 'lib/utils/searchBar'
import { GenieCollection } from 'nft/types' import { GenieCollection } from 'nft/types'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { Link, useNavigate } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import { EllipsisStyle, ThemedText } from 'theme/components' import { EllipsisStyle, ThemedText } from 'theme/components'
...@@ -23,7 +23,6 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' ...@@ -23,7 +23,6 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { InterfaceSearchResultSelectionProperties } from 'uniswap/src/features/telemetry/types' import { InterfaceSearchResultSelectionProperties } from 'uniswap/src/features/telemetry/types'
import { Trans, useTranslation } from 'uniswap/src/i18n' import { Trans, useTranslation } from 'uniswap/src/i18n'
import { UniverseChainId } from 'uniswap/src/types/chains' import { UniverseChainId } from 'uniswap/src/types/chains'
import { shortenAddress } from 'uniswap/src/utils/addresses'
import { NumberType, useFormatter } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
const PriceChangeContainer = styled.div` const PriceChangeContainer = styled.div`
...@@ -148,14 +147,6 @@ export function SuggestionRow({ ...@@ -148,14 +147,6 @@ export function SuggestionRow({
} }
}, [toggleOpen, isHovered, suggestion, navigate, handleClick, path]) }, [toggleOpen, isHovered, suggestion, navigate, handleClick, path])
const shortenedAddress = useMemo<string | null>(() => {
if (isToken && suggestion.address && suggestion.address !== NATIVE_CHAIN_ID) {
return shortenAddress(suggestion.address)
}
return null
}, [suggestion, isToken])
return ( return (
<StyledLink <StyledLink
to={path} to={path}
...@@ -187,7 +178,6 @@ export function SuggestionRow({ ...@@ -187,7 +178,6 @@ export function SuggestionRow({
<PrimaryText lineHeight="24px">{suggestion.name}</PrimaryText> <PrimaryText lineHeight="24px">{suggestion.name}</PrimaryText>
{isToken ? <TokenSafetyIcon warning={warning} /> : suggestion.isVerified && <Verified size={14} />} {isToken ? <TokenSafetyIcon warning={warning} /> : suggestion.isVerified && <Verified size={14} />}
</Flex> </Flex>
<Flex row gap="$spacing4">
<ThemedText.SubHeaderSmall lineHeight="20px"> <ThemedText.SubHeaderSmall lineHeight="20px">
{isToken {isToken
? suggestion.symbol ? suggestion.symbol
...@@ -195,12 +185,6 @@ export function SuggestionRow({ ...@@ -195,12 +185,6 @@ export function SuggestionRow({
count: suggestion?.stats?.total_supply ?? 0, count: suggestion?.stats?.total_supply ?? 0,
})} })}
</ThemedText.SubHeaderSmall> </ThemedText.SubHeaderSmall>
{shortenedAddress && (
<ThemedText.SubHeaderSmall lineHeight="20px" color="neutral3">
{shortenedAddress}
</ThemedText.SubHeaderSmall>
)}
</Flex>
</PrimaryContainer> </PrimaryContainer>
</Flex> </Flex>
......
...@@ -228,7 +228,7 @@ exports[`disable nft on searchbar dropdown should not render popular nft collect ...@@ -228,7 +228,7 @@ exports[`disable nft on searchbar dropdown should not render popular nft collect
<svg <svg
fill="none" fill="none"
stroke-width="8" stroke-width="8"
style="color: rgb(191, 191, 191); width: 16px; height: 16px;" style="color: rgb(206, 206, 206); width: 16px; height: 16px;"
viewBox="0 0 16 16" viewBox="0 0 16 16"
> >
<path <path
...@@ -612,7 +612,7 @@ exports[`disable nft on searchbar dropdown should render popular nft collections ...@@ -612,7 +612,7 @@ exports[`disable nft on searchbar dropdown should render popular nft collections
<svg <svg
fill="none" fill="none"
stroke-width="8" stroke-width="8"
style="color: rgb(191, 191, 191); width: 16px; height: 16px;" style="color: rgb(206, 206, 206); width: 16px; height: 16px;"
viewBox="0 0 16 16" viewBox="0 0 16 16"
> >
<path <path
...@@ -807,7 +807,7 @@ exports[`disable nft on searchbar dropdown should render popular nft collections ...@@ -807,7 +807,7 @@ exports[`disable nft on searchbar dropdown should render popular nft collections
<svg <svg
fill="none" fill="none"
stroke-width="8" stroke-width="8"
style="color: rgb(191, 191, 191); width: 16px; height: 16px;" style="color: rgb(206, 206, 206); width: 16px; height: 16px;"
viewBox="0 0 16 16" viewBox="0 0 16 16"
> >
<path <path
......
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments' import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName, useExperimentGroupNameWithLoading } from 'uniswap/src/features/gating/hooks' import { useExperimentGroupNameWithLoading } from 'uniswap/src/features/gating/hooks'
import { Trans } from 'uniswap/src/i18n'
export function useIsAccountCTAExperimentControl() { export function useIsAccountCTAExperimentControl() {
const { value: experimentGroupName, isLoading } = useExperimentGroupNameWithLoading(Experiments.AccountCTAs) const { value: experimentGroupName, isLoading } = useExperimentGroupNameWithLoading(Experiments.AccountCTAs)
...@@ -9,17 +8,3 @@ export function useIsAccountCTAExperimentControl() { ...@@ -9,17 +8,3 @@ export function useIsAccountCTAExperimentControl() {
isLoading, isLoading,
} }
} }
export function ConnectWalletButtonText(): JSX.Element {
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
return isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)
}
...@@ -76,7 +76,7 @@ function TokenDescription({ token }: { token: TopToken | TokenStat }) { ...@@ -76,7 +76,7 @@ function TokenDescription({ token }: { token: TopToken | TokenStat }) {
return ( return (
<Flex row gap="$gap8"> <Flex row gap="$gap8">
<QueryTokenLogo token={token} size={28} /> <QueryTokenLogo token={token} size={28} />
<EllipsisText data-testid="token-name">{token?.name ?? token?.project?.name}</EllipsisText> <EllipsisText data-testid="token-name">{token?.project?.name ?? token?.name}</EllipsisText>
<TokenTableText <TokenTableText
$platform-web={{ $platform-web={{
minWidth: 'fit-content', minWidth: 'fit-content',
......
import { QueryClientProvider } from '@tanstack/react-query' import { QueryClientProvider } from '@tanstack/react-query'
import { CustomUserProperties, InterfaceEventName, WalletConnectionResult } from '@uniswap/analytics-events' import { CustomUserProperties, InterfaceEventName, WalletConnectionResult } from '@uniswap/analytics-events'
import { recentConnectorIdAtom } from 'components/Web3Provider/constants' import { recentConnectorIdAtom } from 'components/Web3Provider/constants'
import { queryClient, wagmiConfig } from 'components/Web3Provider/wagmiConfig' import { queryClient, wagmiConfig } from 'components/Web3Provider/wagmi'
import { walletTypeToAmplitudeWalletType } from 'components/Web3Provider/walletConnect' import { walletTypeToAmplitudeWalletType } from 'components/Web3Provider/walletConnect'
import { useIsSupportedChainId } from 'constants/chains' import { useIsSupportedChainId } from 'constants/chains'
import { RPC_PROVIDERS } from 'constants/providers' import { RPC_PROVIDERS } from 'constants/providers'
......
import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import { PositionInfo, useModalLiquidityPositionInfo } from 'components/Liquidity/utils' import { PositionInfo, useModalLiquidityPositionInfo } from 'components/Liquidity/utils'
import { Field } from 'components/addLiquidity/InputForm'
import { useDerivedAddLiquidityInfo } from 'components/addLiquidity/hooks' import { useDerivedAddLiquidityInfo } from 'components/addLiquidity/hooks'
import { Dispatch, PropsWithChildren, SetStateAction, createContext, useContext, useMemo, useState } from 'react' import { Dispatch, PropsWithChildren, SetStateAction, createContext, useContext, useMemo, useState } from 'react'
import { PositionField } from 'types/position'
export interface AddLiquidityState { export interface AddLiquidityState {
position?: PositionInfo position?: PositionInfo
exactField: PositionField exactField: Field
exactAmount?: string exactAmount?: string
} }
const DEFAULT_ADD_LIQUIDITY_STATE = { const DEFAULT_ADD_LIQUIDITY_STATE = {
exactField: PositionField.TOKEN0, exactField: Field.TOKEN0,
} }
export interface AddLiquidityInfo { export interface AddLiquidityInfo {
formattedAmounts?: { [field in PositionField]?: string } formattedAmounts?: { [field in Field]?: string }
currencyBalances?: { [field in PositionField]?: CurrencyAmount<Currency> } currencyBalances?: { [field in Field]?: CurrencyAmount<Currency> }
currencyAmounts?: { [field in PositionField]?: CurrencyAmount<Currency> } currencyAmounts?: { [field in Field]?: CurrencyAmount<Currency> }
currencyAmountsUSDValue?: { [field in PositionField]?: CurrencyAmount<Currency> } currencyAmountsUSDValue?: { [field in Field]?: CurrencyAmount<Currency> }
} }
interface AddLiquidityContextType { interface AddLiquidityContextType {
......
...@@ -2,16 +2,20 @@ import { Currency } from '@uniswap/sdk-core' ...@@ -2,16 +2,20 @@ import { Currency } from '@uniswap/sdk-core'
import { AddLiquidityInfo } from 'components/addLiquidity/AddLiquidityContext' import { AddLiquidityInfo } from 'components/addLiquidity/AddLiquidityContext'
import { useCurrencyInfo } from 'hooks/Tokens' import { useCurrencyInfo } from 'hooks/Tokens'
import { useState } from 'react' import { useState } from 'react'
import { PositionField } from 'types/position'
import { Flex } from 'ui/src' import { Flex } from 'ui/src'
import { CurrencyInputPanel } from 'uniswap/src/components/CurrencyInputPanel/CurrencyInputPanel' import { CurrencyInputPanel } from 'uniswap/src/components/CurrencyInputPanel/CurrencyInputPanel'
import { CurrencyField } from 'uniswap/src/types/currency' import { CurrencyField } from 'uniswap/src/types/currency'
export enum Field {
TOKEN0 = 'TOKEN0',
TOKEN1 = 'TOKEN1',
}
type InputFormProps = { type InputFormProps = {
token0: Currency token0: Currency
token1: Currency token1: Currency
onUserInput: (field: PositionField, newValue: string) => void onUserInput: (field: Field, newValue: string) => void
onSetMax: (field: PositionField, amount: string) => void onSetMax: (field: Field, amount: string) => void
} & AddLiquidityInfo } & AddLiquidityInfo
export function InputForm({ export function InputForm({
...@@ -24,19 +28,19 @@ export function InputForm({ ...@@ -24,19 +28,19 @@ export function InputForm({
onUserInput, onUserInput,
onSetMax, onSetMax,
}: InputFormProps) { }: InputFormProps) {
const [focusedInputField, setFocusedInputField] = useState(PositionField.TOKEN0) const [focusedInputField, setFocusedInputField] = useState(Field.TOKEN0)
// TODO(WEB-4920): when the backend returns the logo info make sure that there is no call being made // TODO(WEB-4920): when the backend returns the logo info make sure that there is no call being made
// to graphql to retrieve it // to graphql to retrieve it
const token0CurrencyInfo = useCurrencyInfo(token0) const token0CurrencyInfo = useCurrencyInfo(token0)
const token1CurrencyInfo = useCurrencyInfo(token1) const token1CurrencyInfo = useCurrencyInfo(token1)
const handleUserInput = (field: PositionField) => { const handleUserInput = (field: Field) => {
return (newValue: string) => { return (newValue: string) => {
onUserInput(field, newValue) onUserInput(field, newValue)
} }
} }
const handleOnSetMax = (field: PositionField) => { const handleOnSetMax = (field: Field) => {
return (amount: string) => { return (amount: string) => {
setFocusedInputField(field) setFocusedInputField(field)
onSetMax(field, amount) onSetMax(field, amount)
...@@ -46,37 +50,37 @@ export function InputForm({ ...@@ -46,37 +50,37 @@ export function InputForm({
<Flex gap="$gap4"> <Flex gap="$gap4">
<CurrencyInputPanel <CurrencyInputPanel
enableInputOnly enableInputOnly
focus={focusedInputField === PositionField.TOKEN0} focus={focusedInputField === Field.TOKEN0}
borderRadius="$rounded20" borderRadius="$rounded20"
backgroundColor="$surface2" backgroundColor="$surface2"
currencyInfo={token0CurrencyInfo} currencyInfo={token0CurrencyInfo}
currencyField={CurrencyField.INPUT} currencyField={CurrencyField.INPUT}
currencyAmount={currencyAmounts?.[PositionField.TOKEN0]} currencyAmount={currencyAmounts?.[Field.TOKEN0]}
currencyBalance={currencyBalances?.[PositionField.TOKEN0]} currencyBalance={currencyBalances?.[Field.TOKEN0]}
onSetExactAmount={handleUserInput(PositionField.TOKEN0)} onSetExactAmount={handleUserInput(Field.TOKEN0)}
onToggleIsFiatMode={() => undefined} onToggleIsFiatMode={() => undefined}
usdValue={currencyAmountsUSDValue?.[PositionField.TOKEN0]} usdValue={currencyAmountsUSDValue?.[Field.TOKEN0]}
onSetMax={handleOnSetMax(PositionField.TOKEN0)} onSetMax={handleOnSetMax(Field.TOKEN0)}
value={formattedAmounts?.[PositionField.TOKEN0]} value={formattedAmounts?.[Field.TOKEN0]}
onPressIn={() => setFocusedInputField(PositionField.TOKEN0)} onPressIn={() => setFocusedInputField(Field.TOKEN0)}
/> />
<CurrencyInputPanel <CurrencyInputPanel
enableInputOnly enableInputOnly
focus={focusedInputField === PositionField.TOKEN1} focus={focusedInputField === Field.TOKEN1}
py="$spacing16" py="$spacing16"
borderRadius="$rounded20" borderRadius="$rounded20"
backgroundColor="$surface2" backgroundColor="$surface2"
currencyInfo={token1CurrencyInfo} currencyInfo={token1CurrencyInfo}
currencyField={CurrencyField.INPUT} currencyField={CurrencyField.INPUT}
currencyAmount={currencyAmounts?.[PositionField.TOKEN1]} currencyAmount={currencyAmounts?.[Field.TOKEN1]}
currencyBalance={currencyBalances?.[PositionField.TOKEN1]} currencyBalance={currencyBalances?.[Field.TOKEN1]}
onSetExactAmount={handleUserInput(PositionField.TOKEN1)} onSetExactAmount={handleUserInput(Field.TOKEN1)}
onToggleIsFiatMode={() => undefined} onToggleIsFiatMode={() => undefined}
usdValue={currencyAmountsUSDValue?.[PositionField.TOKEN1]} usdValue={currencyAmountsUSDValue?.[Field.TOKEN1]}
onSetMax={handleOnSetMax(PositionField.TOKEN1)} onSetMax={handleOnSetMax(Field.TOKEN1)}
value={formattedAmounts?.[PositionField.TOKEN1]} value={formattedAmounts?.[Field.TOKEN1]}
onPressIn={() => setFocusedInputField(PositionField.TOKEN1)} onPressIn={() => setFocusedInputField(Field.TOKEN1)}
/> />
</Flex> </Flex>
) )
......
// eslint-disable-next-line no-restricted-imports
import { PoolPosition } from '@uniswap/client-pools/dist/pools/v1/types_pb'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import { FeeAmount, Position } from '@uniswap/v3-sdk'
import { AddLiquidityInfo, AddLiquidityState } from 'components/addLiquidity/AddLiquidityContext' import { AddLiquidityInfo, AddLiquidityState } from 'components/addLiquidity/AddLiquidityContext'
import { Field } from 'components/addLiquidity/InputForm'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
import { usePool } from 'hooks/usePools'
import { useV2Pair } from 'hooks/useV2Pairs'
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance' import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
import { PositionField } from 'types/position'
import { useUSDCValue } from 'uniswap/src/features/transactions/swap/hooks/useUSDCPrice' import { useUSDCValue } from 'uniswap/src/features/transactions/swap/hooks/useUSDCPrice'
function parseV3FeeTier(feeTier: string | undefined): FeeAmount | undefined {
const parsedFee = parseInt(feeTier || '')
return parsedFee in FeeAmount ? parsedFee : undefined
}
export function useDerivedAddLiquidityInfo(state: AddLiquidityState): AddLiquidityInfo { export function useDerivedAddLiquidityInfo(state: AddLiquidityState): AddLiquidityInfo {
const account = useAccount() const account = useAccount()
const { position: positionInfo, exactAmount, exactField } = state const { position, exactAmount } = state
if (!positionInfo) { if (!position) {
throw new Error('no position available') throw new Error('no position available')
} }
const token0 = positionInfo.currency0Amount.currency const token0 = position.currency0Amount.currency
const token1 = positionInfo.currency1Amount.currency const token1 = position.currency1Amount.currency
const [token0Balance, token1Balance] = useCurrencyBalances(account.address, [token0, token1]) const [token0Balance, token1Balance] = useCurrencyBalances(account.address, [token0, token1])
const token0CurrencyAmount = tryParseCurrencyAmount(exactAmount, token0)
const token0USDValue = useUSDCValue(token0CurrencyAmount) || undefined
const [independentToken, dependentToken] = exactField === PositionField.TOKEN0 ? [token0, token1] : [token1, token0] // TODO: compute the dependent value
const independentAmount = tryParseCurrencyAmount(exactAmount, independentToken)
const [, pool] = usePool(token0, token1, parseV3FeeTier(positionInfo.feeTier))
const [, pair] = useV2Pair(token0, token1)
const dependentAmount: CurrencyAmount<Currency> | undefined = useMemo(() => {
// we wrap the currencies just to get the price in terms of the other token
const wrappedIndependentAmount = independentAmount?.wrapped
if (positionInfo.restPosition.position.case === 'v2Pair') {
const [token0Wrapped, token1Wrapped] = [token0?.wrapped, token1?.wrapped]
if (token0Wrapped && token1Wrapped && wrappedIndependentAmount && pair) {
const dependentTokenAmount =
exactField === PositionField.TOKEN0
? pair.priceOf(token0Wrapped).quote(wrappedIndependentAmount)
: pair.priceOf(token1Wrapped).quote(wrappedIndependentAmount)
return dependentToken?.isNative
? CurrencyAmount.fromRawAmount(dependentToken, dependentTokenAmount.quotient)
: dependentTokenAmount
}
return undefined
}
if (positionInfo.restPosition.position.case === 'v3Position') {
const position: PoolPosition = positionInfo.restPosition.position.value
const { tickLower: tickLowerStr, tickUpper: tickUpperStr } = position
const tickLower = parseInt(tickLowerStr)
const tickUpper = parseInt(tickUpperStr)
if (
independentAmount &&
wrappedIndependentAmount &&
typeof tickLower === 'number' &&
typeof tickUpper === 'number' &&
pool
) {
const position: Position | undefined = wrappedIndependentAmount.currency.equals(pool.token0)
? Position.fromAmount0({
pool,
tickLower,
tickUpper,
amount0: independentAmount.quotient,
useFullPrecision: true, // we want full precision for the theoretical position
})
: Position.fromAmount1({
pool,
tickLower,
tickUpper,
amount1: independentAmount.quotient,
})
const dependentTokenAmount = wrappedIndependentAmount.currency.equals(pool.token0)
? position.amount1
: position.amount0
return dependentToken && CurrencyAmount.fromRawAmount(dependentToken, dependentTokenAmount.quotient)
}
return undefined
}
if (positionInfo.restPosition.position.case === 'v4Position') {
// TODO: calculate for v4
return undefined
}
return undefined
}, [
dependentToken,
independentAmount,
pool,
positionInfo.restPosition.position,
exactField,
pair,
token0.wrapped,
token1.wrapped,
])
const independentTokenUSDValue = useUSDCValue(independentAmount) || undefined
const dependentTokenUSDValue = useUSDCValue(dependentAmount) || undefined
const dependentField = exactField === PositionField.TOKEN0 ? PositionField.TOKEN1 : PositionField.TOKEN0
return { return {
currencyBalances: { [PositionField.TOKEN0]: token0Balance, [PositionField.TOKEN1]: token1Balance }, formattedAmounts: { [Field.TOKEN0]: exactAmount },
formattedAmounts: { [exactField]: exactAmount, [dependentField]: dependentAmount?.toExact() }, currencyBalances: { [Field.TOKEN0]: token0Balance, [Field.TOKEN1]: token1Balance },
currencyAmounts: { [exactField]: independentAmount, [dependentField]: dependentAmount }, currencyAmounts: { [Field.TOKEN0]: token0CurrencyAmount },
currencyAmountsUSDValue: { [exactField]: independentTokenUSDValue, [dependentField]: dependentTokenUSDValue }, currencyAmountsUSDValue: { [Field.TOKEN0]: token0USDValue },
} }
} }
...@@ -17,7 +17,7 @@ describe('Routing', () => { ...@@ -17,7 +17,7 @@ describe('Routing', () => {
}) })
it('contains all coins for polygon', () => { it('contains all coins for polygon', () => {
const symbols = COMMON_BASES[UniverseChainId.Polygon].map((coin) => coin.currency.symbol) const symbols = COMMON_BASES[UniverseChainId.Polygon].map((coin) => coin.currency.symbol)
expect(symbols).toEqual(['POL', 'WETH', 'USDC', 'DAI', 'USDT', 'WBTC']) expect(symbols).toEqual(['MATIC', 'WETH', 'USDC', 'DAI', 'USDT', 'WBTC'])
}) })
it('contains all coins for celo', () => { it('contains all coins for celo', () => {
const symbols = COMMON_BASES[UniverseChainId.Celo].map((coin) => coin.currency.symbol) const symbols = COMMON_BASES[UniverseChainId.Celo].map((coin) => coin.currency.symbol)
......
...@@ -111,7 +111,7 @@ export function gqlToCurrency(token: DeepPartial<GqlToken | TokenStat>): Currenc ...@@ -111,7 +111,7 @@ export function gqlToCurrency(token: DeepPartial<GqlToken | TokenStat>): Currenc
token.address, token.address,
token.decimals ?? 18, token.decimals ?? 18,
token.symbol ?? undefined, token.symbol ?? undefined,
token.name ?? token.project?.name ?? undefined, token.project?.name ?? token.name ?? undefined,
) )
} }
} }
......
...@@ -66,7 +66,7 @@ export function getSortedPortfolioTokens( ...@@ -66,7 +66,7 @@ export function getSortedPortfolioTokens(
address, address,
tokenBalance.token?.decimals, tokenBalance.token?.decimals,
tokenBalance.token?.symbol, tokenBalance.token?.symbol,
tokenBalance.token?.name ?? tokenBalance.token?.project?.name, tokenBalance.token?.project?.name ?? tokenBalance.token?.name,
) )
return portfolioToken return portfolioToken
......
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { DefaultTheme } from 'lib/styled-components' import { DefaultTheme } from 'lib/styled-components'
import { PriceImpact } from 'nft/hooks/usePriceImpact' import { PriceImpact } from 'nft/hooks/usePriceImpact'
import { ReactNode } from 'react' import { ReactNode } from 'react'
...@@ -59,7 +58,7 @@ export function getBuyButtonStateData( ...@@ -59,7 +58,7 @@ export function getBuyButtonStateData(
...defaultBuyButtonState, ...defaultBuyButtonState,
handleClick: handleClickOverride ?? (() => undefined), handleClick: handleClickOverride ?? (() => undefined),
disabled: false, disabled: false,
buttonText: <ConnectWalletButtonText />, buttonText: <Trans i18nKey="common.connectWallet.button" />,
}, },
[BuyButtonStates.NOT_SUPPORTED_CHAIN]: { [BuyButtonStates.NOT_SUPPORTED_CHAIN]: {
...defaultBuyButtonState, ...defaultBuyButtonState,
......
import { InterfacePageName } from '@uniswap/analytics-events' import { InterfacePageName } from '@uniswap/analytics-events'
import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
import { ButtonPrimary } from 'components/Button/buttons' import { ButtonPrimary } from 'components/Button/buttons'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
import useENSName from 'hooks/useENSName' import useENSName from 'hooks/useENSName'
import styled from 'lib/styled-components' import styled from 'lib/styled-components'
...@@ -118,7 +117,7 @@ export default function Profile() { ...@@ -118,7 +117,7 @@ export default function Profile() {
</ThemedText.HeadlineMedium> </ThemedText.HeadlineMedium>
<ConnectWalletButton onClick={accountDrawer.open}> <ConnectWalletButton onClick={accountDrawer.open}>
<ThemedText.SubHeader color="white" lineHeight="20px"> <ThemedText.SubHeader color="white" lineHeight="20px">
<ConnectWalletButtonText /> <Trans i18nKey="common.connectWallet.button" />
</ThemedText.SubHeader> </ThemedText.SubHeader>
</ConnectWalletButton> </ConnectWalletButton>
</Center> </Center>
......
...@@ -2,9 +2,8 @@ import { LiquidityModalDetailRows } from 'components/Liquidity/LiquidityModalDet ...@@ -2,9 +2,8 @@ import { LiquidityModalDetailRows } from 'components/Liquidity/LiquidityModalDet
import { LiquidityModalHeader } from 'components/Liquidity/LiquidityModalHeader' import { LiquidityModalHeader } from 'components/Liquidity/LiquidityModalHeader'
import { LiquidityPositionInfo } from 'components/Liquidity/LiquidityPositionInfo' import { LiquidityPositionInfo } from 'components/Liquidity/LiquidityPositionInfo'
import { AddLiquidityContextProvider, useAddLiquidityContext } from 'components/addLiquidity/AddLiquidityContext' import { AddLiquidityContextProvider, useAddLiquidityContext } from 'components/addLiquidity/AddLiquidityContext'
import { InputForm } from 'components/addLiquidity/InputForm' import { Field, InputForm } from 'components/addLiquidity/InputForm'
import { useCloseModal } from 'state/application/hooks' import { useCloseModal } from 'state/application/hooks'
import { PositionField } from 'types/position'
import { Button, Flex } from 'ui/src' import { Button, Flex } from 'ui/src'
import { Modal } from 'uniswap/src/components/modals/Modal' import { Modal } from 'uniswap/src/components/modals/Modal'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
...@@ -26,7 +25,7 @@ function AddLiquidityModalInner() { ...@@ -26,7 +25,7 @@ function AddLiquidityModalInner() {
const token0 = currency0Amount.currency const token0 = currency0Amount.currency
const token1 = currency1Amount.currency const token1 = currency1Amount.currency
const handleUserInput = (field: PositionField, newValue: string) => { const handleUserInput = (field: Field, newValue: string) => {
setAddLiquidityState((prev) => ({ setAddLiquidityState((prev) => ({
...prev, ...prev,
exactField: field, exactField: field,
...@@ -34,7 +33,7 @@ function AddLiquidityModalInner() { ...@@ -34,7 +33,7 @@ function AddLiquidityModalInner() {
})) }))
} }
const handleOnSetMax = (field: PositionField, amount: string) => { const handleOnSetMax = (field: Field, amount: string) => {
setAddLiquidityState((prev) => ({ setAddLiquidityState((prev) => ({
...prev, ...prev,
exactField: field, exactField: field,
......
...@@ -21,7 +21,6 @@ import CurrencyInputPanel from 'components/CurrencyInputPanel' ...@@ -21,7 +21,6 @@ import CurrencyInputPanel from 'components/CurrencyInputPanel'
import FeeSelector from 'components/FeeSelector' import FeeSelector from 'components/FeeSelector'
import HoverInlineText from 'components/HoverInlineText' import HoverInlineText from 'components/HoverInlineText'
import LiquidityChartRangeInput from 'components/LiquidityChartRangeInput' import LiquidityChartRangeInput from 'components/LiquidityChartRangeInput'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { AddRemoveTabs } from 'components/NavigationTabs' import { AddRemoveTabs } from 'components/NavigationTabs'
import { PositionPreview } from 'components/PositionPreview' import { PositionPreview } from 'components/PositionPreview'
import RangeSelector from 'components/RangeSelector' import RangeSelector from 'components/RangeSelector'
...@@ -85,6 +84,8 @@ import { ThemedText } from 'theme/components' ...@@ -85,6 +84,8 @@ import { ThemedText } from 'theme/components'
import { Text } from 'ui/src' import { Text } from 'ui/src'
import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains'
import { WRAPPED_NATIVE_CURRENCY } from 'uniswap/src/constants/tokens' import { WRAPPED_NATIVE_CURRENCY } from 'uniswap/src/constants/tokens'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { Trans, t } from 'uniswap/src/i18n' import { Trans, t } from 'uniswap/src/i18n'
...@@ -542,6 +543,10 @@ function AddLiquidity() { ...@@ -542,6 +543,10 @@ function AddLiquidity() {
}, [searchParams]) }, [searchParams])
// END: sync values with query string // END: sync values with query string
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
const Buttons = () => const Buttons = () =>
addIsUnsupported ? ( addIsUnsupported ? (
<ButtonPrimary disabled={true} $borderRadius="12px" padding="12px"> <ButtonPrimary disabled={true} $borderRadius="12px" padding="12px">
...@@ -557,7 +562,13 @@ function AddLiquidity() { ...@@ -557,7 +562,13 @@ function AddLiquidity() {
element={InterfaceElementName.CONNECT_WALLET_BUTTON} element={InterfaceElementName.CONNECT_WALLET_BUTTON}
> >
<ButtonLight onClick={accountDrawer.open} $borderRadius="12px" padding="12px"> <ButtonLight onClick={accountDrawer.open} $borderRadius="12px" padding="12px">
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
</Trace> </Trace>
) : ( ) : (
......
...@@ -13,7 +13,6 @@ import { ButtonError, ButtonLight, ButtonPrimary } from 'components/Button/butto ...@@ -13,7 +13,6 @@ import { ButtonError, ButtonLight, ButtonPrimary } from 'components/Button/butto
import { BlueCard, LightCard } from 'components/Card/cards' import { BlueCard, LightCard } from 'components/Card/cards'
import CurrencyInputPanel from 'components/CurrencyInputPanel' import CurrencyInputPanel from 'components/CurrencyInputPanel'
import { DoubleCurrencyLogo } from 'components/Logo/DoubleLogo' import { DoubleCurrencyLogo } from 'components/Logo/DoubleLogo'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { AddRemoveTabs } from 'components/NavigationTabs' import { AddRemoveTabs } from 'components/NavigationTabs'
import { MinimalPositionCard } from 'components/PositionCard' import { MinimalPositionCard } from 'components/PositionCard'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink' import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
...@@ -48,6 +47,8 @@ import { useUserSlippageToleranceWithDefault } from 'state/user/hooks' ...@@ -48,6 +47,8 @@ import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { ThemedText } from 'theme/components' import { ThemedText } from 'theme/components'
import { Text } from 'ui/src' import { Text } from 'ui/src'
import { WRAPPED_NATIVE_CURRENCY } from 'uniswap/src/constants/tokens' import { WRAPPED_NATIVE_CURRENCY } from 'uniswap/src/constants/tokens'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
...@@ -353,6 +354,10 @@ export default function AddLiquidity() { ...@@ -353,6 +354,10 @@ export default function AddLiquidity() {
const addIsUnsupported = useIsSwapUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B) const addIsUnsupported = useIsSwapUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
if (!networkSupportsV2) { if (!networkSupportsV2) {
return <V2Unsupported /> return <V2Unsupported />
} }
...@@ -469,7 +474,13 @@ export default function AddLiquidity() { ...@@ -469,7 +474,13 @@ export default function AddLiquidity() {
element={InterfaceElementName.CONNECT_WALLET_BUTTON} element={InterfaceElementName.CONNECT_WALLET_BUTTON}
> >
<ButtonLight onClick={accountDrawer.open}> <ButtonLight onClick={accountDrawer.open}>
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
</Trace> </Trace>
) : ( ) : (
......
import { Navigate } from 'react-router-dom'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlagWithLoading } from 'uniswap/src/features/gating/hooks'
export const NewPosition = () => {
const { value: v4Enabled, isLoading } = useFeatureFlagWithLoading(FeatureFlags.V4Everywhere)
if (!isLoading && !v4Enabled) {
return <Navigate to="/pools" replace />
}
if (isLoading) {
return null
}
return (
<div>
<h1>New Position</h1>
</div>
)
}
import { AdvancedButton } from 'pages/Pool/Positions/create/shared'
import { useState } from 'react'
import { Button } from 'ui/src'
import { DocumentList } from 'ui/src/components/icons/DocumentList'
import { X } from 'ui/src/components/icons/X'
import { Flex } from 'ui/src/components/layout/Flex'
import { fonts } from 'ui/src/theme'
import { TextInput } from 'uniswap/src/components/input/TextInput'
import { useTranslation } from 'uniswap/src/i18n'
export function AddHook() {
const { t } = useTranslation()
const [hookInputEnabled, setHookInputEnabled] = useState(false)
const [hookAddress, setHookAddress] = useState('')
const handleToggleHookInput = () => {
setHookInputEnabled((prev) => !prev)
setHookAddress('')
}
if (hookInputEnabled) {
return (
<Flex row gap="$spacing4">
<TextInput
autoFocus
placeholder="Enter hook address"
autoCapitalize="none"
color="$neutral1"
fontFamily="$subHeading"
fontSize={fonts.body2.fontSize}
fontWeight={fonts.body2.fontWeight}
lineHeight={24}
maxLength={42}
numberOfLines={1}
px="$spacing16"
py={5}
returnKeyType="done"
width="100%"
borderWidth={1.5}
borderColor="$neutral3"
borderRadius="$rounded12"
focusStyle={{
borderColor: '$neutral3',
}}
hoverStyle={{
borderColor: '$neutral3',
}}
value={hookAddress}
onChangeText={(text: string) => setHookAddress(text)}
/>
<Button theme="secondary" py="$spacing8" px="$spacing12" borderWidth={0} onPress={handleToggleHookInput}>
<X size="$icon.20" color="$neutral1" />
</Button>
</Flex>
)
}
return <AdvancedButton title={t('position.addHook')} Icon={DocumentList} onPress={handleToggleHookInput} />
}
import { PoolProgressIndicator } from 'components/PoolProgressIndicator/PoolProgressIndicator'
import {
CreatePositionContextProvider,
DEFAULT_POSITION_STATE,
DEFAULT_PRICE_RANGE_STATE,
PriceRangeContextProvider,
useCreatePositionContext,
usePriceRangeContext,
} from 'pages/Pool/Positions/create/CreatePositionContext'
import { EditRangeSelectionStep, EditSelectTokensStep } from 'pages/Pool/Positions/create/EditStep'
import { SelectPriceRangeStep } from 'pages/Pool/Positions/create/RangeSelectionStep'
import { SelectTokensStep } from 'pages/Pool/Positions/create/SelectTokenStep'
import { PositionFlowStep } from 'pages/Pool/Positions/create/types'
import { useCallback } from 'react'
import { Navigate } from 'react-router-dom'
import { Button, Flex, Text } from 'ui/src'
import { AngleRightSmall } from 'ui/src/components/icons/AngleRightSmall'
import { RotatableChevron } from 'ui/src/components/icons/RotatableChevron'
import { RotateLeft } from 'ui/src/components/icons/RotateLeft'
import { Settings } from 'ui/src/components/icons/Settings'
import { iconSizes } from 'ui/src/theme/iconSizes'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlagWithLoading } from 'uniswap/src/features/gating/hooks'
import { Trans, useTranslation } from 'uniswap/src/i18n'
function CreatePositionInner() {
const { step, setStep } = useCreatePositionContext()
const handleContinue = useCallback(() => {
setStep((prevStep) => prevStep + 1)
}, [setStep])
return (
<Flex gap="$spacing24">
{step === PositionFlowStep.SELECT_TOKENS ? (
<SelectTokensStep onContinue={handleContinue} />
) : step === PositionFlowStep.PRICE_RANGE ? (
<>
<EditSelectTokensStep />
<SelectPriceRangeStep onContinue={handleContinue} />
</>
) : (
<>
<EditSelectTokensStep />
<EditRangeSelectionStep />
</>
)}
</Flex>
)
}
const Sidebar = () => {
const { t } = useTranslation()
const { step } = useCreatePositionContext()
const PoolProgressSteps = [
{ label: t(`position.step.select`), active: step === PositionFlowStep.SELECT_TOKENS },
{ label: t(`position.step.range`), active: step === PositionFlowStep.PRICE_RANGE },
{ label: t(`position.step.deposit`), active: step == PositionFlowStep.DEPOSIT },
]
return (
<Flex gap={32} width={360}>
<Flex gap="$gap20">
<Flex row alignItems="center">
<Text variant="body3" color="$neutral2">
<Trans i18nKey="pool.positions.title" />
</Text>
<AngleRightSmall color="$neutral2" size={iconSizes.icon24} />
</Flex>
<Text variant="heading2">
<Trans i18nKey="position.new" />
</Text>
</Flex>
<PoolProgressIndicator steps={PoolProgressSteps} />
</Flex>
)
}
const Toolbar = () => {
const { setPositionState, setStep } = useCreatePositionContext()
const { setPriceRangeState } = usePriceRangeContext()
const handleReset = useCallback(() => {
setPositionState(DEFAULT_POSITION_STATE)
setPriceRangeState(DEFAULT_PRICE_RANGE_STATE)
setStep(PositionFlowStep.SELECT_TOKENS)
}, [setPositionState, setPriceRangeState, setStep])
return (
<Flex flexDirection="row-reverse" alignItems="flex-end" height={88} gap="$gap8">
<Button
theme="tertiary"
py="$spacing8"
px="$spacing12"
backgroundColor="$surface1"
borderRadius="$rounded12"
borderColor="$surface3"
borderWidth="$spacing1"
gap="$gap4"
>
<Settings size={iconSizes.icon16} color="$neutral1" />
</Button>
<Button
theme="tertiary"
py={6}
pl="$spacing12"
pr="$spacing8"
alignItems="center"
backgroundColor="$surface1"
borderRadius="$rounded12"
borderColor="$surface3"
borderWidth="$spacing1"
gap={6}
>
<Text variant="buttonLabel4">
<Trans i18nKey="position.protocol" values={{ protocol: 'v4' }} />
</Text>
<RotatableChevron direction="down" color="$neutral2" width={iconSizes.icon20} height={iconSizes.icon20} />
</Button>
<Button
theme="tertiary"
py="$spacing8"
px="$spacing12"
backgroundColor="$surface1"
borderRadius="$rounded12"
borderColor="$surface3"
borderWidth="$spacing1"
gap="$gap4"
onPress={handleReset}
>
<RotateLeft size={iconSizes.icon16} color="$neutral1" />
<Text variant="buttonLabel4">
<Trans i18nKey="common.button.reset" />
</Text>
</Button>
</Flex>
)
}
export function CreatePosition() {
const { value: v4Enabled, isLoading } = useFeatureFlagWithLoading(FeatureFlags.V4Everywhere)
if (!isLoading && !v4Enabled) {
return <Navigate to="/pools" replace />
}
if (isLoading) {
return null
}
return (
<CreatePositionContextProvider>
<PriceRangeContextProvider>
<Flex row gap={60} justifyContent="space-around" mt="$spacing48">
<Sidebar />
<Flex gap={32} width="100%" maxWidth={580}>
<Toolbar />
<CreatePositionInner />
</Flex>
</Flex>
</PriceRangeContextProvider>
</CreatePositionContextProvider>
)
}
import { useDerivedPositionInfo } from 'pages/Pool/Positions/create/hooks'
import {
CreatePositionContextType,
PositionFlowStep,
PositionState,
PriceRangeContextType,
PriceRangeState,
} from 'pages/Pool/Positions/create/types'
import React, { useContext, useState } from 'react'
export const DEFAULT_POSITION_STATE: PositionState = {
tokenInputs: {},
fee: 3000,
hook: undefined,
}
const CreatePositionContext = React.createContext<CreatePositionContextType>({
step: PositionFlowStep.SELECT_TOKENS,
setStep: () => undefined,
positionState: DEFAULT_POSITION_STATE,
setPositionState: () => undefined,
derivedPositionInfo: {
pool: undefined,
},
})
export const useCreatePositionContext = () => {
return useContext(CreatePositionContext)
}
export function CreatePositionContextProvider({ children }: { children: React.ReactNode }) {
const [positionState, setPositionState] = useState<PositionState>(DEFAULT_POSITION_STATE)
const [step, setStep] = useState<PositionFlowStep>(PositionFlowStep.SELECT_TOKENS)
const derivedPositionInfo = useDerivedPositionInfo(positionState)
return (
<CreatePositionContext.Provider value={{ step, setStep, positionState, setPositionState, derivedPositionInfo }}>
{children}
</CreatePositionContext.Provider>
)
}
export const DEFAULT_PRICE_RANGE_STATE: PriceRangeState = {
priceInverted: false,
fullRange: true,
minPrice: '0',
maxPrice: 'INF',
}
const PriceRangeContext = React.createContext<PriceRangeContextType>({
priceRangeState: DEFAULT_PRICE_RANGE_STATE,
setPriceRangeState: () => undefined,
})
export const usePriceRangeContext = () => {
return useContext(PriceRangeContext)
}
export function PriceRangeContextProvider({ children }: { children: React.ReactNode }) {
const [priceRangeState, setPriceRangeState] = useState<PriceRangeState>(DEFAULT_PRICE_RANGE_STATE)
return (
<PriceRangeContext.Provider value={{ priceRangeState, setPriceRangeState }}>{children}</PriceRangeContext.Provider>
)
}
import { DoubleCurrencyLogo } from 'components/Logo/DoubleLogo'
import { useCreatePositionContext, usePriceRangeContext } from 'pages/Pool/Positions/create/CreatePositionContext'
import { Container } from 'pages/Pool/Positions/create/shared'
import { PositionFlowStep } from 'pages/Pool/Positions/create/types'
import { useCallback } from 'react'
import { Button, Flex, Text } from 'ui/src'
import { Edit } from 'ui/src/components/icons/Edit'
import { iconSizes } from 'ui/src/theme'
import { Trans } from 'uniswap/src/i18n'
const EditStep = ({ children, onClick }: { children: JSX.Element; onClick: () => void }) => {
return (
<Container row justifyContent="space-between" alignItems="center" borderRadius="$rounded12">
{children}
<Button theme="secondary" py="$spacing8" px="$spacing12" gap="$gap8" height={36} onPress={onClick}>
<Edit size={iconSizes.icon20} color="$neutral1" />
<Text variant="buttonLabel3">
<Trans i18nKey="common.edit.button" />
</Text>
</Button>
</Container>
)
}
export const EditSelectTokensStep = () => {
const {
positionState: {
tokenInputs: { TOKEN0: token0, TOKEN1: token1 },
},
setStep,
} = useCreatePositionContext()
const currencies = [token0, token1]
const handleEdit = useCallback(() => {
setStep(PositionFlowStep.SELECT_TOKENS)
}, [setStep])
return (
<EditStep onClick={handleEdit}>
<Flex row py="$spacing8" gap="$gap12">
<DoubleCurrencyLogo currencies={currencies} size={iconSizes.icon32} />
<Flex row gap="$gap8">
<Text variant="heading3">{token0?.symbol}</Text>
<Text variant="heading3">/</Text>
<Text variant="heading3">{token1?.symbol}</Text>
</Flex>
</Flex>
</EditStep>
)
}
export const EditRangeSelectionStep = () => {
const {
positionState: {
tokenInputs: { TOKEN0: token0, TOKEN1: token1 },
},
setStep,
} = useCreatePositionContext()
const {
priceRangeState: { priceInverted },
} = usePriceRangeContext()
const baseCurrency = priceInverted ? token1 : token0
const quoteCurrency = priceInverted ? token0 : token1
const handleEdit = useCallback(() => {
setStep(PositionFlowStep.PRICE_RANGE)
}, [setStep])
return (
<EditStep onClick={handleEdit}>
<Flex row gap={10}>
<Text variant="subheading1" width={80}>
<Trans i18nKey="common.range" />
</Text>
<Flex gap="$gap4">
<Flex row gap={10}>
<Text variant="body2" color="$neutral2">
<Trans i18nKey="chart.price.label.low" />
</Text>
<Text variant="body2">{`283,923,000 ${baseCurrency?.symbol + '/' + quoteCurrency?.symbol}`}</Text>
</Flex>
<Flex row gap={10}>
<Text variant="body2" color="$neutral2">
<Trans i18nKey="chart.price.label.high" />
</Text>
<Text variant="body2">{`481,848,481 ${baseCurrency?.symbol + '/' + quoteCurrency?.symbol}`}</Text>
</Flex>
</Flex>
</Flex>
</EditStep>
)
}
import { useCreatePositionContext, usePriceRangeContext } from 'pages/Pool/Positions/create/CreatePositionContext'
import { Container } from 'pages/Pool/Positions/create/shared'
import { useCallback, useMemo } from 'react'
import { Minus, Plus } from 'react-feather'
import { Button, Flex, SegmentedControl, Text, useSporeColors } from 'ui/src'
import { SwapActionButton } from 'ui/src/components/icons/SwapActionButton'
import { fonts } from 'ui/src/theme'
import { AmountInput } from 'uniswap/src/components/CurrencyInputPanel/AmountInput'
import { Trans, useTranslation } from 'uniswap/src/i18n'
enum RangeSelectionInput {
MIN,
MAX,
}
enum RangeSelection {
FULL = 'FULL',
CUSTOM = 'CUSTOM',
}
function RangeControl({ value, active }: { value: string; active: boolean }) {
return (
<Text color={active ? '$neutral1' : '$neutral2'} userSelect="none" variant="buttonLabel3">
{value}
</Text>
)
}
function RangeInput({ input }: { input: RangeSelectionInput }) {
const colors = useSporeColors()
const { t } = useTranslation()
const {
positionState: {
tokenInputs: { TOKEN0: token0, TOKEN1: token1 },
},
} = useCreatePositionContext()
const {
priceRangeState: { minPrice, maxPrice, priceInverted },
setPriceRangeState,
} = usePriceRangeContext()
const baseCurrency = priceInverted ? token1 : token0
const quoteCurrency = priceInverted ? token0 : token1
const handlePriceRangeInput = useCallback(
(input: RangeSelectionInput, value: string) => {
if (input === RangeSelectionInput.MIN) {
setPriceRangeState((prev) => ({ ...prev, minPrice: value }))
} else {
setPriceRangeState((prev) => ({ ...prev, maxPrice: value }))
}
},
[setPriceRangeState],
)
return (
<Flex
row
flex={1}
position="relative"
backgroundColor="$surface2"
borderBottomRightRadius={input === RangeSelectionInput.MIN ? '$none' : '$rounded20'}
borderBottomLeftRadius={input === RangeSelectionInput.MIN ? '$rounded20' : '$none'}
p="$spacing16"
justifyContent="space-between"
overflow="hidden"
>
<Flex gap="$gap4" overflow="hidden" flex={1}>
<Text variant="body3" color="$neutral2">
{input === RangeSelectionInput.MIN ? t(`pool.minPrice`) : t(`pool.maxPrice`)}
</Text>
<AmountInput
backgroundColor="$transparent"
borderWidth={0}
borderRadius="$none"
color="$neutral1"
fontFamily="$heading3"
fontSize={fonts.heading3.fontSize}
fontWeight={fonts.heading3.fontWeight}
maxDecimals={quoteCurrency?.decimals ?? 18}
overflow="visible"
placeholder="0"
placeholderTextColor={colors.neutral3.val}
px="$none"
py="$none"
value={input === RangeSelectionInput.MIN ? minPrice : maxPrice}
onChangeText={(text) => handlePriceRangeInput(input, text)}
/>
<Text variant="body3" color="$neutral2">
<Trans
i18nKey="common.feesEarnedPerBase"
values={{
symbolA: quoteCurrency?.symbol,
symbolB: baseCurrency?.symbol,
}}
/>
</Text>
</Flex>
<Flex gap={10}>
<Button theme="secondary" p="$spacing8" borderRadius="$roundedFull">
<Plus size="16px" color={colors.neutral1.val} />
</Button>
<Button theme="secondary" p="$spacing8" borderRadius="$roundedFull" color="$neutral1">
<Minus size="16px" color={colors.neutral1.val} />
</Button>
</Flex>
</Flex>
)
}
export const SelectPriceRangeStep = ({ onContinue }: { onContinue: () => void }) => {
const { t } = useTranslation()
const {
positionState: {
tokenInputs: { TOKEN0: token0, TOKEN1: token1 },
},
} = useCreatePositionContext()
const {
priceRangeState: { priceInverted, fullRange },
setPriceRangeState,
} = usePriceRangeContext()
const baseCurrency = priceInverted ? token1 : token0
const quoteCurrency = priceInverted ? token0 : token1
const controlOptions = useMemo(() => {
return [{ value: token0?.symbol ?? '' }, { value: token1?.symbol ?? '' }]
}, [token0?.symbol, token1?.symbol])
const handleSelectToken = useCallback(
(option: string) => {
if (option === token0?.symbol) {
setPriceRangeState((prevState) => ({ ...prevState, priceInverted: false }))
} else {
setPriceRangeState((prevState) => ({ ...prevState, priceInverted: true }))
}
},
[token0?.symbol, setPriceRangeState],
)
const handleSelectRange = useCallback(
(option: RangeSelection) => {
if (option === RangeSelection.FULL) {
setPriceRangeState((prevState) => ({ ...prevState, fullRange: true }))
} else {
setPriceRangeState((prevState) => ({ ...prevState, fullRange: false }))
}
},
[setPriceRangeState],
)
const segmentedControlRangeOptions = [
{ display: <RangeControl value={t(`common.fullRange`)} active={fullRange} />, value: RangeSelection.FULL },
{ display: <RangeControl value={t(`common.customRange`)} active={!fullRange} />, value: RangeSelection.CUSTOM },
]
return (
<Container>
<Flex gap="$gap20">
<Flex row alignItems="center">
<Text flex={1} variant="subheading1">
<Trans i18nKey="position.selectRange" />
</Text>
<SegmentedControl
options={controlOptions}
selectedOption={baseCurrency?.symbol ?? ''}
onSelectOption={handleSelectToken}
/>
</Flex>
<SegmentedControl
options={segmentedControlRangeOptions}
selectedOption={fullRange ? RangeSelection.FULL : RangeSelection.CUSTOM}
onSelectOption={handleSelectRange}
fullWidth
size="large"
/>
<Text variant="body3" color="$neutral2">
<Trans i18nKey="position.provide.liquidityDescription" />
</Text>
<Flex gap="$gap4">
<Flex
backgroundColor="$surface2"
p="$padding16"
gap="$gap12"
borderTopLeftRadius="$rounded20"
borderTopRightRadius="$rounded20"
>
<Flex gap="$gap8" row alignItems="center">
<Text variant="body3" color="$neutral2">
<Trans i18nKey="common.currentPrice.label" />
</Text>
<Text variant="body3" color="$neutral1">
<Trans
i18nKey="common.amountPerBase"
// TODO: update values after WEB-4920
values={{
amount: '329,394,000.00',
symbolA: quoteCurrency?.symbol,
symbolB: baseCurrency?.symbol,
}}
/>
</Text>
<SwapActionButton size="$icon.16" color="$neutral2" />
</Flex>
</Flex>
<Flex row gap="$gap4">
<RangeInput input={RangeSelectionInput.MIN} />
<RangeInput input={RangeSelectionInput.MAX} />
</Flex>
</Flex>
</Flex>
<Button
flex={1}
py="$spacing16"
px="$spacing20"
backgroundColor="$accent3"
hoverStyle={{
backgroundColor: undefined,
opacity: 0.8,
}}
pressStyle={{
backgroundColor: undefined,
}}
onPress={onContinue}
>
<Text variant="buttonLabel1" color="$surface1">
<Trans i18nKey="common.button.continue" />
</Text>
</Button>
</Container>
)
}
import { Currency } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import { useCurrencyInfo } from 'hooks/Tokens'
import { AddHook } from 'pages/Pool/Positions/create/AddHook'
import { useCreatePositionContext } from 'pages/Pool/Positions/create/CreatePositionContext'
import { AdvancedButton, Container } from 'pages/Pool/Positions/create/shared'
import { useCallback, useReducer, useState } from 'react'
import { TamaguiClickableStyle } from 'theme/components'
import { PositionField } from 'types/position'
import { Button, Flex, Text, styled } from 'ui/src'
import { CheckCircleFilled } from 'ui/src/components/icons/CheckCircleFilled'
import { Dollar } from 'ui/src/components/icons/Dollar'
import { RotatableChevron } from 'ui/src/components/icons/RotatableChevron'
import { iconSizes } from 'ui/src/theme'
import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo'
import { Trans, useTranslation } from 'uniswap/src/i18n'
import { useFormatter } from 'utils/formatNumbers'
const CurrencySelector = ({ currency, onPress }: { currency?: Currency; onPress: () => void }) => {
const { t } = useTranslation()
// TODO: remove when backend returns token logos in graphql response: WEB-4920
const currencyInfo = useCurrencyInfo(currency)
return (
<Button
flex={1}
width="100%"
onPress={onPress}
py="$spacing12"
pr="$spacing12"
pl="$spacing16"
theme="primary"
backgroundColor={currency ? '$surface3' : '$accent3'}
justifyContent="space-between"
gap="$spacing8"
hoverStyle={{
backgroundColor: undefined,
opacity: 0.8,
}}
pressStyle={{
backgroundColor: undefined,
}}
>
<Flex row gap="$spacing8" alignItems="center">
{currency && (
<TokenLogo
size={iconSizes.icon24}
chainId={currency.chainId}
name={currency.name}
symbol={currency.symbol}
url={currencyInfo?.logoUrl}
/>
)}
<Text variant="buttonLabel2" color={currency ? '$neutral1' : '$surface1'}>
{currency ? currency.symbol : t('fiatOnRamp.button.chooseToken')}
</Text>
</Flex>
<RotatableChevron direction="down" color="$neutral2" width={iconSizes.icon24} height={iconSizes.icon24} />
</Button>
)
}
interface FeeTier {
value: number
title: string
selectionPercent: number
}
const FeeTierContainer = styled(Flex, {
flex: 1,
width: '100%',
p: '$spacing12',
gap: '$spacing8',
justifyContent: 'space-between',
borderRadius: '$rounded12',
borderWidth: 1,
borderColor: '$surface3',
...TamaguiClickableStyle,
})
const FeeTier = ({
feeTier,
selected,
onSelect,
}: {
feeTier: FeeTier
selected: boolean
onSelect: (value: number) => void
}) => {
return (
<FeeTierContainer onPress={() => onSelect(feeTier.value)} background={selected ? '$surface3' : '$surface1'}>
<Flex row gap={10} justifyContent="space-between" alignItems="center">
<Text variant="buttonLabel3">{feeTier.value / 10000}%</Text>
{selected && <CheckCircleFilled size={iconSizes.icon20} />}
</Flex>
<Text variant="body4">{feeTier.title}</Text>
<Text variant="body4" color="$neutral2">
{feeTier.selectionPercent}% select
</Text>
</FeeTierContainer>
)
}
export function SelectTokensStep({ onContinue }: { onContinue: () => void }) {
const { formatDelta } = useFormatter()
const { t } = useTranslation()
const {
positionState: {
tokenInputs: { TOKEN0: token0, TOKEN1: token1 },
fee,
},
setPositionState,
derivedPositionInfo,
} = useCreatePositionContext()
const [currencySearchInputState, setCurrencySearchInputState] = useState<PositionField | undefined>(undefined)
const [isShowMoreFeeTiersEnabled, toggleShowMoreFeeTiersEnabled] = useReducer((state) => !state, false)
const continueButtonEnabled = !!derivedPositionInfo.pool
const handleCurrencySelect = useCallback(
(currency: Currency) => {
switch (currencySearchInputState) {
case PositionField.TOKEN0:
case PositionField.TOKEN1:
setPositionState((prevState) => ({
...prevState,
tokenInputs: { ...prevState.tokenInputs, [currencySearchInputState]: currency },
}))
break
default:
break
}
},
[currencySearchInputState, setPositionState],
)
const handleFeeTierSelect = useCallback(
(feeTier: number) => {
setPositionState((prevState) => ({ ...prevState, fee: feeTier }))
},
[setPositionState],
)
const feeTiers = [
{ value: FeeAmount.LOWEST, title: t(`fee.bestForVeryStable`), selectionPercent: 0 },
{ value: FeeAmount.LOW, title: t(`fee.bestForStablePairs`), selectionPercent: 0 },
{ value: FeeAmount.MEDIUM, title: t(`fee.bestForMost`), selectionPercent: 96 },
{ value: FeeAmount.HIGH, title: t(`fee.bestForExotic`), selectionPercent: 4 },
]
return (
<>
<Container>
<Flex gap="$spacing16">
<Flex gap="$spacing12">
<Flex>
<Text variant="subheading1">
<Trans i18nKey="pool.selectPair" />
</Text>
<Text variant="body3" color="$neutral2">
<Trans i18nKey="position.provide.liquidity" />
</Text>
</Flex>
<Flex row gap="$gap16">
<CurrencySelector currency={token0} onPress={() => setCurrencySearchInputState(PositionField.TOKEN0)} />
<CurrencySelector currency={token1} onPress={() => setCurrencySearchInputState(PositionField.TOKEN1)} />
</Flex>
<AddHook />
</Flex>
</Flex>
<Flex gap="$spacing24">
<Flex>
<Text variant="subheading1">
<Trans i18nKey="fee.tier" />
</Text>
<Text variant="body3" color="$neutral2">
<Trans i18nKey="fee.tier.description" />
</Text>
</Flex>
</Flex>
<Flex gap="$spacing8">
<Flex
row
py="$spacing12"
px="$spacing16"
gap="$spacing24"
justifyContent="space-between"
borderRadius="$rounded12"
borderWidth={1}
borderColor="$surface3"
>
<Flex>
<Flex row>
<Text variant="subheading2" color="$neutral1">
<Trans i18nKey="fee.tierExact" values={{ fee: formatDelta(fee / 10_000) }} />
</Text>
</Flex>
<Text variant="body3" color="$neutral2">
<Trans i18nKey="fee.tier.label" />
</Text>
</Flex>
<Button
py="$spacing8"
px="$spacing12"
gap="$gap4"
theme="secondary"
onPress={toggleShowMoreFeeTiersEnabled}
>
<Text variant="buttonLabel4">{isShowMoreFeeTiersEnabled ? t('common.less') : t('common.more')}</Text>
<RotatableChevron
direction={isShowMoreFeeTiersEnabled ? 'up' : 'down'}
color="$neutral2"
width={iconSizes.icon20}
height={iconSizes.icon20}
/>
</Button>
</Flex>
{isShowMoreFeeTiersEnabled && (
<Flex row gap={10}>
{feeTiers.map((feeTier) => (
<FeeTier
key={feeTier.value}
feeTier={feeTier}
selected={feeTier.value === fee}
onSelect={handleFeeTierSelect}
/>
))}
</Flex>
)}
<AdvancedButton title={t('fee.tier.search')} Icon={Dollar} onPress={() => {}} />
</Flex>
<Button
flex={1}
py="$spacing16"
px="$spacing20"
backgroundColor="$accent3"
hoverStyle={{
backgroundColor: undefined,
opacity: 0.8,
}}
pressStyle={{
backgroundColor: undefined,
}}
onPress={onContinue}
disabled={!continueButtonEnabled}
>
<Text variant="buttonLabel1" color="$surface1">
<Trans i18nKey="common.button.continue" />
</Text>
</Button>
</Container>
<CurrencySearchModal
isOpen={currencySearchInputState !== undefined}
onDismiss={() => setCurrencySearchInputState(undefined)}
onCurrencySelect={handleCurrencySelect}
/>
</>
)
}
import { usePool } from 'hooks/usePools'
import { PositionInfo, PositionState } from 'pages/Pool/Positions/create/types'
import { useMemo } from 'react'
export function useDerivedPositionInfo(state: PositionState): PositionInfo {
const pool = usePool(state.tokenInputs.TOKEN0, state.tokenInputs.TOKEN1, state.fee)[1] ?? undefined
return useMemo(
() => ({
pool,
}),
[pool],
)
}
import { Flex, GeneratedIcon, Text, styled } from 'ui/src'
import { InfoCircleFilled } from 'ui/src/components/icons/InfoCircleFilled'
import { iconSizes } from 'ui/src/theme'
import { useTranslation } from 'uniswap/src/i18n'
export const Container = styled(Flex, {
gap: 32,
p: '$spacing24',
borderRadius: '$rounded20',
borderWidth: '$spacing1',
borderColor: '$surface3',
maxWidth: 580,
})
export function AdvancedButton({ title, Icon, onPress }: { title: string; Icon: GeneratedIcon; onPress: () => void }) {
const { t } = useTranslation()
return (
<Flex row gap="$spacing8" alignItems="center">
<Flex row gap="$spacing4" alignItems="center">
<Icon size={iconSizes.icon16} color="$neutral2" />
<Text
variant="body3"
color="$neutral2"
textDecorationLine="underline"
textDecorationStyle="dashed"
cursor="pointer"
onPress={onPress}
>
{title}
</Text>
</Flex>
<Text variant="body3" color="$neutral3">
({t('common.advanced')})
</Text>
<InfoCircleFilled size={iconSizes.icon16} color="$neutral3" />
</Flex>
)
}
import { Currency } from '@uniswap/sdk-core'
import { Pool } from '@uniswap/v3-sdk'
import { Dispatch, SetStateAction } from 'react'
import { PositionField } from 'types/position'
export enum PositionFlowStep {
SELECT_TOKENS,
PRICE_RANGE,
DEPOSIT,
}
export interface PositionState {
tokenInputs: { [field in PositionField]?: Currency }
fee: number
hook?: string
}
export interface PositionInfo {
pool?: Pool
}
export type CreatePositionContextType = {
step: PositionFlowStep
setStep: Dispatch<SetStateAction<PositionFlowStep>>
positionState: PositionState
setPositionState: Dispatch<SetStateAction<PositionState>>
derivedPositionInfo: PositionInfo
}
export interface PriceRangeState {
priceInverted: boolean
fullRange: boolean
minPrice: string
maxPrice: string
}
export type PriceRangeContextType = {
priceRangeState: PriceRangeState
setPriceRangeState: Dispatch<SetStateAction<PriceRangeState>>
}
...@@ -15,7 +15,6 @@ import { BlueCard, LightCard } from 'components/Card/cards' ...@@ -15,7 +15,6 @@ import { BlueCard, LightCard } from 'components/Card/cards'
import CurrencyInputPanel from 'components/CurrencyInputPanel' import CurrencyInputPanel from 'components/CurrencyInputPanel'
import CurrencyLogo from 'components/Logo/CurrencyLogo' import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { DoubleCurrencyLogo } from 'components/Logo/DoubleLogo' import { DoubleCurrencyLogo } from 'components/Logo/DoubleLogo'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { AddRemoveTabs } from 'components/NavigationTabs' import { AddRemoveTabs } from 'components/NavigationTabs'
import { MinimalPositionCard } from 'components/PositionCard' import { MinimalPositionCard } from 'components/PositionCard'
import Slider from 'components/Slider' import Slider from 'components/Slider'
...@@ -49,6 +48,8 @@ import { useUserSlippageToleranceWithDefault } from 'state/user/hooks' ...@@ -49,6 +48,8 @@ import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { StyledInternalLink, ThemedText } from 'theme/components' import { StyledInternalLink, ThemedText } from 'theme/components'
import { Text } from 'ui/src' import { Text } from 'ui/src'
import { WRAPPED_NATIVE_CURRENCY } from 'uniswap/src/constants/tokens' import { WRAPPED_NATIVE_CURRENCY } from 'uniswap/src/constants/tokens'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
...@@ -502,6 +503,10 @@ function RemoveLiquidity() { ...@@ -502,6 +503,10 @@ function RemoveLiquidity() {
liquidityPercentChangeCallback, liquidityPercentChangeCallback,
) )
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
if (!networkSupportsV2) { if (!networkSupportsV2) {
return <V2Unsupported /> return <V2Unsupported />
} }
...@@ -710,7 +715,13 @@ function RemoveLiquidity() { ...@@ -710,7 +715,13 @@ function RemoveLiquidity() {
element={InterfaceElementName.CONNECT_WALLET_BUTTON} element={InterfaceElementName.CONNECT_WALLET_BUTTON}
> >
<ButtonLight onClick={accountDrawer.open}> <ButtonLight onClick={accountDrawer.open}>
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
</Trace> </Trace>
) : ( ) : (
......
...@@ -8,7 +8,7 @@ import { t } from 'uniswap/src/i18n' ...@@ -8,7 +8,7 @@ import { t } from 'uniswap/src/i18n'
import { isBrowserRouterEnabled } from 'utils/env' import { isBrowserRouterEnabled } from 'utils/env'
// High-traffic pages (index and /swap) should not be lazy-loaded. // High-traffic pages (index and /swap) should not be lazy-loaded.
import Landing from 'pages/Landing' import Landing from 'pages/Landing'
import { CreatePosition } from 'pages/Pool/Positions/create/CreatePosition' import { NewPosition } from 'pages/LegacyPool/NewPosition'
import Swap from 'pages/Swap' import Swap from 'pages/Swap'
const NftExplore = lazy(() => import('nft/pages/explore')) const NftExplore = lazy(() => import('nft/pages/explore'))
...@@ -202,8 +202,8 @@ export const routes: RouteDefinition[] = [ ...@@ -202,8 +202,8 @@ export const routes: RouteDefinition[] = [
}), }),
// Refreshed pool routes // Refreshed pool routes
createRouteDefinition({ createRouteDefinition({
path: '/positions/create', path: '/positions/new',
getElement: () => <CreatePosition />, getElement: () => <NewPosition />,
getTitle: getPositionPageTitle, getTitle: getPositionPageTitle,
getDescription: getPositionPageDescription, getDescription: getPositionPageDescription,
}), }),
......
import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
import { ButtonLight } from 'components/Button/buttons' import { ButtonLight } from 'components/Button/buttons'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { useBuyFormContext } from 'pages/Swap/Buy/BuyFormContext' import { useBuyFormContext } from 'pages/Swap/Buy/BuyFormContext'
import { Button, Flex, SpinningLoader, Text, WidthAnimator } from 'ui/src' import { Button, Flex, SpinningLoader, Text, WidthAnimator } from 'ui/src'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { useTranslation } from 'uniswap/src/i18n' import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Trans, useTranslation } from 'uniswap/src/i18n'
import { useAccount } from 'wagmi' import { useAccount } from 'wagmi'
interface BuyFormButtonProps { interface BuyFormButtonProps {
...@@ -20,10 +21,20 @@ export function BuyFormButton({ forceDisabled }: BuyFormButtonProps) { ...@@ -20,10 +21,20 @@ export function BuyFormButton({ forceDisabled }: BuyFormButtonProps) {
const { inputAmount } = buyFormState const { inputAmount } = buyFormState
const { notAvailableInThisRegion, quotes, fetchingQuotes, error } = derivedBuyFormInfo const { notAvailableInThisRegion, quotes, fetchingQuotes, error } = derivedBuyFormInfo
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
if (!account.isConnected) { if (!account.isConnected) {
return ( return (
<ButtonLight onClick={accountDrawer.open}> <ButtonLight onClick={accountDrawer.open}>
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
) )
} }
......
...@@ -69,7 +69,7 @@ exports[`PredefinedAmount renders correctly with amount= 300 , currentAmount= "1 ...@@ -69,7 +69,7 @@ exports[`PredefinedAmount renders correctly with amount= 300 , currentAmount= "1
<span <span
class="font_button _display-inline _boxSizing-border-box _whiteSpace-pre-wrap _mt-0px _mr-0px _mb-0px _ml-0px _color-843134974 _fontFamily-299667014 _wordWrap-break-word _fontSize-229441189 _lineHeight-222976542 _fontWeight-233016109" class="font_button _display-inline _boxSizing-border-box _whiteSpace-pre-wrap _mt-0px _mr-0px _mb-0px _ml-0px _color-843134974 _fontFamily-299667014 _wordWrap-break-word _fontSize-229441189 _lineHeight-222976542 _fontWeight-233016109"
data-disable-theme="true" data-disable-theme="true"
style="color: rgb(191, 191, 191); padding-top: 1px;" style="color: rgb(206, 206, 206); padding-top: 1px;"
> >
$300 $300
</span> </span>
...@@ -148,7 +148,7 @@ exports[`PredefinedAmount renders correctly with amount= 1000 , currentAmount= " ...@@ -148,7 +148,7 @@ exports[`PredefinedAmount renders correctly with amount= 1000 , currentAmount= "
<span <span
class="font_button _display-inline _boxSizing-border-box _whiteSpace-pre-wrap _mt-0px _mr-0px _mb-0px _ml-0px _color-843134974 _fontFamily-299667014 _wordWrap-break-word _fontSize-229441189 _lineHeight-222976542 _fontWeight-233016109" class="font_button _display-inline _boxSizing-border-box _whiteSpace-pre-wrap _mt-0px _mr-0px _mb-0px _ml-0px _color-843134974 _fontFamily-299667014 _wordWrap-break-word _fontSize-229441189 _lineHeight-222976542 _fontWeight-233016109"
data-disable-theme="true" data-disable-theme="true"
style="color: rgb(191, 191, 191); padding-top: 1px;" style="color: rgb(206, 206, 206); padding-top: 1px;"
> >
$1000 $1000
</span> </span>
......
...@@ -12,7 +12,6 @@ import { ...@@ -12,7 +12,6 @@ import {
useCurrentPriceAdjustment, useCurrentPriceAdjustment,
} from 'components/CurrencyInputPanel/LimitPriceInputPanel/useCurrentPriceAdjustment' } from 'components/CurrencyInputPanel/LimitPriceInputPanel/useCurrentPriceAdjustment'
import SwapCurrencyInputPanel from 'components/CurrencyInputPanel/SwapCurrencyInputPanel' import SwapCurrencyInputPanel from 'components/CurrencyInputPanel/SwapCurrencyInputPanel'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import Column from 'components/deprecated/Column' import Column from 'components/deprecated/Column'
import Row from 'components/deprecated/Row' import Row from 'components/deprecated/Row'
import { Field } from 'components/swap/constants' import { Field } from 'components/swap/constants'
...@@ -41,6 +40,8 @@ import { AlertTriangleFilled } from 'ui/src/components/icons/AlertTriangleFilled ...@@ -41,6 +40,8 @@ import { AlertTriangleFilled } from 'ui/src/components/icons/AlertTriangleFilled
import { colors, validColor } from 'ui/src/theme' import { colors, validColor } from 'ui/src/theme'
import { nativeOnChain } from 'uniswap/src/constants/tokens' import { nativeOnChain } from 'uniswap/src/constants/tokens'
import { uniswapUrls } from 'uniswap/src/constants/urls' import { uniswapUrls } from 'uniswap/src/constants/urls'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Locale } from 'uniswap/src/features/language/constants' import { Locale } from 'uniswap/src/features/language/constants'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, InterfacePageNameLocal } from 'uniswap/src/features/telemetry/constants' import { ElementName, InterfacePageNameLocal } from 'uniswap/src/features/telemetry/constants'
...@@ -464,6 +465,10 @@ function SubmitOrderButton({ ...@@ -464,6 +465,10 @@ function SubmitOrderButton({
const account = useAccount() const account = useAccount()
const { chainId } = useSwapAndLimitContext() const { chainId } = useSwapAndLimitContext()
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
if (!isUniswapXSupportedChain(chainId)) { if (!isUniswapXSupportedChain(chainId)) {
return ( return (
<ButtonError disabled> <ButtonError disabled>
...@@ -475,7 +480,13 @@ function SubmitOrderButton({ ...@@ -475,7 +480,13 @@ function SubmitOrderButton({
if (!account.isConnected) { if (!account.isConnected) {
return ( return (
<ButtonLight onClick={accountDrawer.open} fontWeight={535} $borderRadius="16px"> <ButtonLight onClick={accountDrawer.open} fontWeight={535} $borderRadius="16px">
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
) )
} }
......
import { InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events' import { InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events'
import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
import { ButtonLight, ButtonPrimary } from 'components/Button/buttons' import { ButtonLight, ButtonPrimary } from 'components/Button/buttons'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import Column from 'components/deprecated/Column' import Column from 'components/deprecated/Column'
import { useIsSupportedChainId } from 'constants/chains' import { useIsSupportedChainId } from 'constants/chains'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
...@@ -18,6 +17,8 @@ import { SendContextProvider, useSendContext } from 'state/send/SendContext' ...@@ -18,6 +17,8 @@ import { SendContextProvider, useSendContext } from 'state/send/SendContext'
import { CurrencyState } from 'state/swap/types' import { CurrencyState } from 'state/swap/types'
import { useSwapAndLimitContext } from 'state/swap/useSwapContext' import { useSwapAndLimitContext } from 'state/swap/useSwapContext'
import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { InterfacePageNameLocal } from 'uniswap/src/features/telemetry/constants' import { InterfacePageNameLocal } from 'uniswap/src/features/telemetry/constants'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
...@@ -186,6 +187,10 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor ...@@ -186,6 +187,10 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor
.catch(() => undefined) .catch(() => undefined)
}, [handleModalState, sendCallback, setSendState]) }, [handleModalState, sendCallback, setSendState])
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
return ( return (
<> <>
<Column gap="xs"> <Column gap="xs">
...@@ -198,7 +203,13 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor ...@@ -198,7 +203,13 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor
element={InterfaceElementName.CONNECT_WALLET_BUTTON} element={InterfaceElementName.CONNECT_WALLET_BUTTON}
> >
<ButtonLight onClick={accountDrawer.open} fontWeight={535} $borderRadius="16px"> <ButtonLight onClick={accountDrawer.open} fontWeight={535} $borderRadius="16px">
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
</Trace> </Trace>
) : !multichainUXEnabled && initialChainId && initialChainId !== account.chainId ? ( ) : !multichainUXEnabled && initialChainId && initialChainId !== account.chainId ? (
......
...@@ -12,7 +12,6 @@ import { GrayCard } from 'components/Card/cards' ...@@ -12,7 +12,6 @@ import { GrayCard } from 'components/Card/cards'
import { ConfirmSwapModal } from 'components/ConfirmSwapModal' import { ConfirmSwapModal } from 'components/ConfirmSwapModal'
import SwapCurrencyInputPanel from 'components/CurrencyInputPanel/SwapCurrencyInputPanel' import SwapCurrencyInputPanel from 'components/CurrencyInputPanel/SwapCurrencyInputPanel'
import ErrorIcon from 'components/Icons/Error' import ErrorIcon from 'components/Icons/Error'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal' import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Column, { AutoColumn } from 'components/deprecated/Column' import Column, { AutoColumn } from 'components/deprecated/Column'
import Row from 'components/deprecated/Row' import Row from 'components/deprecated/Row'
...@@ -53,6 +52,8 @@ import { Text } from 'ui/src' ...@@ -53,6 +52,8 @@ import { Text } from 'ui/src'
import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains'
import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace' import Trace from 'uniswap/src/features/telemetry/Trace'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { WrapType } from 'uniswap/src/features/transactions/types/wrap' import { WrapType } from 'uniswap/src/features/transactions/types/wrap'
...@@ -507,6 +508,10 @@ export function SwapForm({ ...@@ -507,6 +508,10 @@ export function SwapForm({
// @ts-ignore // @ts-ignore
const isUsingBlockedExtension = window.ethereum?.['isPocketUniverseZ'] const isUsingBlockedExtension = window.ethereum?.['isPocketUniverseZ']
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
return ( return (
<> <>
<TokenSafetyModal <TokenSafetyModal
...@@ -671,7 +676,13 @@ export function SwapForm({ ...@@ -671,7 +676,13 @@ export function SwapForm({
element={InterfaceElementName.CONNECT_WALLET_BUTTON} element={InterfaceElementName.CONNECT_WALLET_BUTTON}
> >
<ButtonLight onClick={accountDrawer.open} fontWeight={535} $borderRadius="16px"> <ButtonLight onClick={accountDrawer.open} fontWeight={535} $borderRadius="16px">
<ConnectWalletButtonText /> {isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)}
</ButtonLight> </ButtonLight>
</Trace> </Trace>
) : !multichainUXEnabled && initialChainId && initialChainId !== connectedChainId ? ( ) : !multichainUXEnabled && initialChainId && initialChainId !== connectedChainId ? (
......
...@@ -123,7 +123,7 @@ function useCreateTDPContext(): PendingTDPContext | LoadedTDPContext { ...@@ -123,7 +123,7 @@ function useCreateTDPContext(): PendingTDPContext | LoadedTDPContext {
const tokenColor = const tokenColor =
useSrcColor( useSrcColor(
extractedColorSrc, extractedColorSrc,
tokenQuery.data?.token?.name ?? tokenQuery.data?.token?.project?.name, tokenQuery.data?.token?.project?.name ?? tokenQuery.data?.token?.name,
theme.surface2, theme.surface2,
).tokenColor ?? undefined ).tokenColor ?? undefined
......
...@@ -132,7 +132,7 @@ Array [ ...@@ -132,7 +132,7 @@ Array [
"getElement": [Function], "getElement": [Function],
"getTitle": [Function], "getTitle": [Function],
"nestedPaths": Array [], "nestedPaths": Array [],
"path": "/positions/create", "path": "/positions/new",
}, },
Object { Object {
"enabled": [Function], "enabled": [Function],
......
...@@ -30,7 +30,7 @@ export const paths = [ ...@@ -30,7 +30,7 @@ export const paths = [
'/pools', '/pools',
'/pools/:tokenId', '/pools/:tokenId',
'/positions', '/positions',
'/positions/create', '/positions/new',
'/add/v2', '/add/v2',
'/add', '/add',
'/increase', '/increase',
......
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk' import { Pair } from '@uniswap/v2-sdk'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
import { useTotalSupply } from 'hooks/useTotalSupply' import { useTotalSupply } from 'hooks/useTotalSupply'
import { useV2Pair } from 'hooks/useV2Pairs' import { useV2Pair } from 'hooks/useV2Pairs'
...@@ -11,6 +10,8 @@ import { ReactNode, useCallback } from 'react' ...@@ -11,6 +10,8 @@ import { ReactNode, useCallback } from 'react'
import { Field, typeInput } from 'state/burn/actions' import { Field, typeInput } from 'state/burn/actions'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { InterfaceState } from 'state/webReducer' import { InterfaceState } from 'state/webReducer'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
export function useBurnState(): InterfaceState['burn'] { export function useBurnState(): InterfaceState['burn'] {
...@@ -121,9 +122,19 @@ export function useDerivedBurnInfo( ...@@ -121,9 +122,19 @@ export function useDerivedBurnInfo(
: undefined, : undefined,
} }
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
let error: ReactNode | undefined let error: ReactNode | undefined
if (!account.isConnected) { if (!account.isConnected) {
error = <ConnectWalletButtonText /> error = isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)
} }
if (!parsedAmounts[Field.LIQUIDITY] || !parsedAmounts[Field.CURRENCY_A] || !parsedAmounts[Field.CURRENCY_B]) { if (!parsedAmounts[Field.LIQUIDITY] || !parsedAmounts[Field.CURRENCY_A] || !parsedAmounts[Field.CURRENCY_B]) {
......
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { Position } from '@uniswap/v3-sdk' import { Position } from '@uniswap/v3-sdk'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { useToken } from 'hooks/Tokens' import { useToken } from 'hooks/Tokens'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
import { usePool } from 'hooks/usePools' import { usePool } from 'hooks/usePools'
...@@ -10,6 +9,8 @@ import { selectPercent } from 'state/burn/v3/actions' ...@@ -10,6 +9,8 @@ import { selectPercent } from 'state/burn/v3/actions'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { InterfaceState } from 'state/webReducer' import { InterfaceState } from 'state/webReducer'
import { PositionDetails } from 'types/position' import { PositionDetails } from 'types/position'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
import { unwrappedToken } from 'utils/unwrappedToken' import { unwrappedToken } from 'utils/unwrappedToken'
...@@ -74,9 +75,19 @@ export function useDerivedV3BurnInfo( ...@@ -74,9 +75,19 @@ export function useDerivedV3BurnInfo(
const outOfRange = const outOfRange =
pool && position ? pool.tickCurrent < position.tickLower || pool.tickCurrent > position.tickUpper : false pool && position ? pool.tickCurrent < position.tickLower || pool.tickCurrent > position.tickUpper : false
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
let error: ReactNode | undefined let error: ReactNode | undefined
if (!account.isConnected) { if (!account.isConnected) {
error = <ConnectWalletButtonText /> error = isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)
} }
if (percent === 0) { if (percent === 0) {
error = error ?? <Trans i18nKey="burn.input.enterAPercent.error" /> error = error ?? <Trans i18nKey="burn.input.enterAPercent.error" />
......
import { Currency, CurrencyAmount, Percent, Price, Token } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Percent, Price, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk' import { Pair } from '@uniswap/v2-sdk'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
import { useTotalSupply } from 'hooks/useTotalSupply' import { useTotalSupply } from 'hooks/useTotalSupply'
import { PairState, useV2Pair } from 'hooks/useV2Pairs' import { PairState, useV2Pair } from 'hooks/useV2Pairs'
...@@ -11,6 +10,8 @@ import { useCurrencyBalances } from 'state/connection/hooks' ...@@ -11,6 +10,8 @@ import { useCurrencyBalances } from 'state/connection/hooks'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { Field, typeInput } from 'state/mint/actions' import { Field, typeInput } from 'state/mint/actions'
import { InterfaceState } from 'state/webReducer' import { InterfaceState } from 'state/webReducer'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
...@@ -182,9 +183,19 @@ export function useDerivedMintInfo( ...@@ -182,9 +183,19 @@ export function useDerivedMintInfo(
} }
}, [liquidityMinted, totalSupply]) }, [liquidityMinted, totalSupply])
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
let error: ReactNode | undefined let error: ReactNode | undefined
if (!account.isConnected) { if (!account.isConnected) {
error = <ConnectWalletButtonText /> error = isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)
} }
if (pairState === PairState.INVALID) { if (pairState === PairState.INVALID) {
......
...@@ -10,7 +10,6 @@ import { ...@@ -10,7 +10,6 @@ import {
priceToClosestTick, priceToClosestTick,
tickToPrice, tickToPrice,
} from '@uniswap/v3-sdk' } from '@uniswap/v3-sdk'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { BIG_INT_ZERO } from 'constants/misc' import { BIG_INT_ZERO } from 'constants/misc'
import { useAccount } from 'hooks/useAccount' import { useAccount } from 'hooks/useAccount'
import { PoolState, usePool } from 'hooks/usePools' import { PoolState, usePool } from 'hooks/usePools'
...@@ -32,6 +31,8 @@ import { ...@@ -32,6 +31,8 @@ import {
} from 'state/mint/v3/actions' } from 'state/mint/v3/actions'
import { tryParseTick } from 'state/mint/v3/utils' import { tryParseTick } from 'state/mint/v3/utils'
import { InterfaceState } from 'state/webReducer' import { InterfaceState } from 'state/webReducer'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
import { getTickToPrice } from 'utils/getTickToPrice' import { getTickToPrice } from 'utils/getTickToPrice'
...@@ -449,9 +450,19 @@ export function useV3DerivedMintInfo( ...@@ -449,9 +450,19 @@ export function useV3DerivedMintInfo(
tickUpper, tickUpper,
]) ])
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
let errorMessage: ReactNode | undefined let errorMessage: ReactNode | undefined
if (!account.isConnected) { if (!account.isConnected) {
errorMessage = <ConnectWalletButtonText /> errorMessage = isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)
} }
if (poolState === PoolState.INVALID) { if (poolState === PoolState.INVALID) {
......
...@@ -3,7 +3,7 @@ import { parse } from 'qs' ...@@ -3,7 +3,7 @@ import { parse } from 'qs'
import { queryParametersToCurrencyState, useInitialCurrencyState } from 'state/swap/hooks' import { queryParametersToCurrencyState, useInitialCurrencyState } from 'state/swap/hooks'
import { ETH_MAINNET } from 'test-utils/constants' import { ETH_MAINNET } from 'test-utils/constants'
import { renderHook, waitFor } from 'test-utils/render' import { renderHook, waitFor } from 'test-utils/render'
import { UNI, nativeOnChain } from 'uniswap/src/constants/tokens' import { MATIC_POLYGON, UNI } from 'uniswap/src/constants/tokens'
import { UniverseChainId } from 'uniswap/src/types/chains' import { UniverseChainId } from 'uniswap/src/types/chains'
jest.mock('uniswap/src/features/gating/hooks', () => { jest.mock('uniswap/src/features/gating/hooks', () => {
...@@ -186,7 +186,7 @@ describe('hooks', () => { ...@@ -186,7 +186,7 @@ describe('hooks', () => {
{ {
tokenBalances: [ tokenBalances: [
{ {
token: nativeOnChain(UniverseChainId.Polygon), token: MATIC_POLYGON,
denominatedValue: { denominatedValue: {
value: 1000, value: 1000,
}, },
......
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { ConnectWalletButtonText } from 'components/NavBar/accountCTAsExperimentUtils'
import { Field } from 'components/swap/constants' import { Field } from 'components/swap/constants'
import { CHAIN_IDS_TO_NAMES, useSupportedChainId } from 'constants/chains' import { CHAIN_IDS_TO_NAMES, useSupportedChainId } from 'constants/chains'
import { NATIVE_CHAIN_ID } from 'constants/tokens' import { NATIVE_CHAIN_ID } from 'constants/tokens'
...@@ -21,6 +20,8 @@ import { CurrencyState, SerializedCurrencyState, SwapInfo, SwapState } from 'sta ...@@ -21,6 +20,8 @@ import { CurrencyState, SerializedCurrencyState, SwapInfo, SwapState } from 'sta
import { useSwapAndLimitContext, useSwapContext } from 'state/swap/useSwapContext' import { useSwapAndLimitContext, useSwapContext } from 'state/swap/useSwapContext'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks' import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { useTokenProjects } from 'uniswap/src/features/dataApi/tokenProjects' import { useTokenProjects } from 'uniswap/src/features/dataApi/tokenProjects'
import { AccountCTAsExperimentGroup, Experiments } from 'uniswap/src/features/gating/experiments'
import { useExperimentGroupName } from 'uniswap/src/features/gating/hooks'
import { Trans } from 'uniswap/src/i18n' import { Trans } from 'uniswap/src/i18n'
import { InterfaceChainId, UniverseChainId } from 'uniswap/src/types/chains' import { InterfaceChainId, UniverseChainId } from 'uniswap/src/types/chains'
import { areCurrencyIdsEqual, currencyId } from 'uniswap/src/utils/currencyId' import { areCurrencyIdsEqual, currencyId } from 'uniswap/src/utils/currencyId'
...@@ -219,11 +220,22 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { ...@@ -219,11 +220,22 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo {
isClassicTrade(trade.trade) && (nativeCurrencyBalanceUSD ?? 0) < (trade.trade.totalGasUseEstimateUSDWithBuffer ?? 0) isClassicTrade(trade.trade) && (nativeCurrencyBalanceUSD ?? 0) < (trade.trade.totalGasUseEstimateUSDWithBuffer ?? 0)
const { isDisconnected } = useAccount() const { isDisconnected } = useAccount()
const accountsCTAExperimentGroup = useExperimentGroupName(Experiments.AccountCTAs)
const isSignIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.SignInSignUp
const isLogIn = accountsCTAExperimentGroup === AccountCTAsExperimentGroup.LogInCreateAccount
const inputError = useMemo(() => { const inputError = useMemo(() => {
let inputError: ReactNode | undefined let inputError: ReactNode | undefined
if (!account.isConnected) { if (!account.isConnected) {
inputError = isDisconnected ? <ConnectWalletButtonText /> : <Trans i18nKey="common.connectingWallet" /> const disconnectedInputError = isSignIn ? (
<Trans i18nKey="nav.signIn.button" />
) : isLogIn ? (
<Trans i18nKey="nav.logIn.button" />
) : (
<Trans i18nKey="common.connectWallet.button" />
)
inputError = isDisconnected ? disconnectedInputError : <Trans i18nKey="common.connectingWallet" />
} }
if (!currencies[Field.INPUT] || !currencies[Field.OUTPUT]) { if (!currencies[Field.INPUT] || !currencies[Field.OUTPUT]) {
...@@ -268,6 +280,8 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { ...@@ -268,6 +280,8 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo {
currencyBalances, currencyBalances,
trade?.trade, trade?.trade,
allowedSlippage, allowedSlippage,
isSignIn,
isLogIn,
isDisconnected, isDisconnected,
nativeCurrency.symbol, nativeCurrency.symbol,
]) ])
......
...@@ -32,8 +32,8 @@ const entryGzipSize = report.reduce( ...@@ -32,8 +32,8 @@ const entryGzipSize = report.reduce(
0, 0,
) )
// somewhat arbitrary, based on current size (9/19/2024) // somewhat arbitrary, based on current size (9/17/2024)
const limit = 2_185_000 const limit = 2_165_000
if (entryGzipSize > limit) { if (entryGzipSize > limit) {
console.error(`Bundle size has grown too big! Entry JS size is ${entryGzipSize}, over the limit of ${limit}.`) console.error(`Bundle size has grown too big! Entry JS size is ${entryGzipSize}, over the limit of ${limit}.`)
......
import { ClientOptions, ErrorEvent, EventHint } from '@sentry/types' import { ClientOptions, ErrorEvent, EventHint } from '@sentry/types'
import { ProviderRpcError } from '@web3-react/types' import { ProviderRpcError } from '@web3-react/types'
import { wagmiConfig } from 'components/Web3Provider/wagmiConfig' import { wagmiConfig } from 'components/Web3Provider/wagmi'
import { didUserReject } from 'utils/swapErrorToUserReadableMessage' import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
import { getAccount } from 'wagmi/actions' import { getAccount } from 'wagmi/actions'
......
...@@ -15,8 +15,3 @@ export interface PositionDetails { ...@@ -15,8 +15,3 @@ export interface PositionDetails {
tokensOwed0: BigNumber tokensOwed0: BigNumber
tokensOwed1: BigNumber tokensOwed1: BigNumber
} }
export enum PositionField {
TOKEN0 = 'TOKEN0',
TOKEN1 = 'TOKEN1',
}
<svg viewBox="0 0 18 4" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16px" height="16px" viewBox="0 0 18 4" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 3C16.5523 3 17 2.55228 17 2C17 1.44772 16.5523 1 16 1C15.4477 1 15 1.44772 15 2C15 2.55228 15.4477 3 16 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M16 3C16.5523 3 17 2.55228 17 2C17 1.44772 16.5523 1 16 1C15.4477 1 15 1.44772 15 2C15 2.55228 15.4477 3 16 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 3C2.55228 3 3 2.55228 3 2C3 1.44772 2.55228 1 2 1C1.44772 1 1 1.44772 1 2C1 2.55228 1.44772 3 2 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M2 3C2.55228 3 3 2.55228 3 2C3 1.44772 2.55228 1 2 1C1.44772 1 1 1.44772 1 2C1 2.55228 1.44772 3 2 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>
<svg stroke="currentColor" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 4"><path d="M9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M16 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
\ No newline at end of file
...@@ -19,7 +19,6 @@ export function UniversalImage({ ...@@ -19,7 +19,6 @@ export function UniversalImage({
fallback, fallback,
fastImage = false, fastImage = false,
testID, testID,
onLoad,
allowLocalUri = false, allowLocalUri = false,
}: UniversalImageProps): JSX.Element | null { }: UniversalImageProps): JSX.Element | null {
// Allow calculation of fields as needed // Allow calculation of fields as needed
...@@ -129,7 +128,6 @@ export function UniversalImage({ ...@@ -129,7 +128,6 @@ export function UniversalImage({
style={style?.image} style={style?.image}
testID={testID ? `img-${testID}` : undefined} testID={testID ? `img-${testID}` : undefined}
uri={imageHttpUrl} uri={imageHttpUrl}
onLoad={onLoad}
/> />
) )
} }
...@@ -2,7 +2,7 @@ import { useState } from 'react' ...@@ -2,7 +2,7 @@ import { useState } from 'react'
import { Image } from 'react-native' import { Image } from 'react-native'
import { PlainImageProps } from 'ui/src/components/UniversalImage/types' import { PlainImageProps } from 'ui/src/components/UniversalImage/types'
export function PlainImage({ uri, size, fallback, resizeMode, style, testID, onLoad }: PlainImageProps): JSX.Element { export function PlainImage({ uri, size, fallback, resizeMode, style, testID }: PlainImageProps): JSX.Element {
const [hasError, setHasError] = useState(false) const [hasError, setHasError] = useState(false)
if (hasError && fallback) { if (hasError && fallback) {
...@@ -20,7 +20,6 @@ export function PlainImage({ uri, size, fallback, resizeMode, style, testID, onL ...@@ -20,7 +20,6 @@ export function PlainImage({ uri, size, fallback, resizeMode, style, testID, onL
onError={() => { onError={() => {
setHasError(true) setHasError(true)
}} }}
onLoad={onLoad}
/> />
) )
} }
import { PlainImageProps } from 'ui/src/components/UniversalImage/types' import { useState } from 'react'
import { PlatformSplitStubError } from 'utilities/src/errors' import { PlainImageProps, UniversalImageResizeMode } from 'ui/src/components/UniversalImage/types'
import { Flex } from 'ui/src/components/layout/Flex'
import { isTestEnv } from 'utilities/src/environment/env'
export function PlainImage(_props: PlainImageProps): JSX.Element { export function PlainImage({ uri, size, fallback, resizeMode, style, testID }: PlainImageProps): JSX.Element {
throw new PlatformSplitStubError('PlainImage') const [hasError, setHasError] = useState(false)
// TODO cover all cases better
const objectFit =
resizeMode === UniversalImageResizeMode.Contain || resizeMode === UniversalImageResizeMode.Cover
? resizeMode
: 'contain'
const imgElement = (
<img
height={size.height}
src={uri}
style={{ objectFit, aspectRatio: size.aspectRatio, ...style }}
width={size.width}
onError={() => {
setHasError(true)
}}
/>
)
if (hasError && fallback) {
return fallback
}
// TODO(MOB-3485): remove test run special casing
if (isTestEnv()) {
return <Flex testID={testID}>{imgElement}</Flex>
} else {
return imgElement
}
} }
import { useState } from 'react'
import { PlainImageProps, UniversalImageResizeMode } from 'ui/src/components/UniversalImage/types'
import { Flex } from 'ui/src/components/layout/Flex'
import { isTestEnv } from 'utilities/src/environment/env'
export function PlainImage({ uri, size, fallback, resizeMode, style, testID, onLoad }: PlainImageProps): JSX.Element {
const [hasError, setHasError] = useState(false)
// TODO cover all cases better
const objectFit =
resizeMode === UniversalImageResizeMode.Contain || resizeMode === UniversalImageResizeMode.Cover
? resizeMode
: 'contain'
const imgElement = (
<img
height={size.height}
src={uri}
style={{ objectFit, aspectRatio: size.aspectRatio, ...style }}
width={size.width}
onError={() => {
setHasError(true)
}}
onLoad={onLoad}
/>
)
if (hasError && fallback) {
return fallback
}
// TODO(MOB-3485): remove test run special casing
if (isTestEnv()) {
return <Flex testID={testID}>{imgElement}</Flex>
} else {
return imgElement
}
}
...@@ -39,7 +39,6 @@ export interface UniversalImageProps { ...@@ -39,7 +39,6 @@ export interface UniversalImageProps {
fastImage?: boolean fastImage?: boolean
testID?: string testID?: string
allowLocalUri?: boolean allowLocalUri?: boolean
onLoad?: () => void
} }
export interface PlainImageProps { export interface PlainImageProps {
...@@ -49,7 +48,6 @@ export interface PlainImageProps { ...@@ -49,7 +48,6 @@ export interface PlainImageProps {
style?: UniversalImageStyle style?: UniversalImageStyle
resizeMode?: UniversalImageResizeMode resizeMode?: UniversalImageResizeMode
testID?: string testID?: string
onLoad?: () => void
} }
export type FastImageWrapperProps = PlainImageProps & { export type FastImageWrapperProps = PlainImageProps & {
......
...@@ -45,8 +45,8 @@ export function LabeledCheckbox({ ...@@ -45,8 +45,8 @@ export function LabeledCheckbox({
<Flex row alignItems="center" gap={gap} px={px}> <Flex row alignItems="center" gap={gap} px={px}>
{checkboxPosition === 'start' && <Checkbox checked={checked} size={size} variant={variant} onPress={onPress} />} {checkboxPosition === 'start' && <Checkbox checked={checked} size={size} variant={variant} onPress={onPress} />}
{text && ( {text && (
<Flex grow> <Flex grow shrink>
<Flex shrink>{textElement}</Flex> {textElement}
</Flex> </Flex>
)} )}
{checkboxPosition === 'end' && <Checkbox checked={checked} variant={variant} onPress={onPress} />} {checkboxPosition === 'end' && <Checkbox checked={checked} variant={variant} onPress={onPress} />}
......
...@@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' ...@@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon'
export const [Ellipsis, AnimatedEllipsis] = createIcon({ export const [Ellipsis, AnimatedEllipsis] = createIcon({
name: 'Ellipsis', name: 'Ellipsis',
getIcon: (props) => ( getIcon: (props) => (
<Svg viewBox="0 0 18 4" fill="none" {...props}> <Svg width="16" height="16" viewBox="0 0 18 4" fill="none" {...props}>
<Path <Path
d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z"
stroke="currentColor" stroke="currentColor"
......
import { Path, Svg } from 'react-native-svg'
// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths
import { createIcon } from '../factories/createIcon'
export const [TripleDots, AnimatedTripleDots] = createIcon({
name: 'TripleDots',
getIcon: (props) => (
<Svg stroke="currentColor" fill="currentColor" viewBox="0 0 18 4" {...props}>
<Path
d="M9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
<Path
d="M16 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
<Path
d="M2 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
</Svg>
),
})
...@@ -203,6 +203,7 @@ export * from './Trash' ...@@ -203,6 +203,7 @@ export * from './Trash'
export * from './TrashFilled' export * from './TrashFilled'
export * from './TrendDown' export * from './TrendDown'
export * from './TrendUp' export * from './TrendUp'
export * from './TripleDots'
export * from './UniswapLogo' export * from './UniswapLogo'
export * from './UniswapX' export * from './UniswapX'
export * from './UserSquare' export * from './UserSquare'
......
...@@ -157,7 +157,7 @@ const sporeLight = { ...@@ -157,7 +157,7 @@ const sporeLight = {
neutral1Hovered: '#131313', neutral1Hovered: '#131313',
neutral2: '#7D7D7D', neutral2: '#7D7D7D',
neutral2Hovered: '#6B6B6B', neutral2Hovered: '#6B6B6B',
neutral3: '#BFBFBF', neutral3: '#CECECE',
neutral3Hovered: '#ADADAD', neutral3Hovered: '#ADADAD',
surface1: colors.white, surface1: colors.white,
......
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