ci(release): publish latest release

parent ad632eb4
* @uniswap/web-admins
IPFS hash of the deployment: We are back with another (small) round of updates. Check out what is new below:
- CIDv0: `QmNxB7dgMsLA6wASBs8xCSjKa4XAkeZNiiaf7uAG7KxPme`
- CIDv1: `bafybeiajdg2uit3dy2c5dbxclr7w6subg6t4fxnegfifpiskay2qlyys5u`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). Token Details Page Improvements — We took a pass at simplifying the core flows of token detail pages. We clarified the language around contract addresses and made it simpler than ever to copy them to your clipboard. In addition, we added a quick and easy route to our ‘receive’ flow from any given token details page. Share (and send) tokens easier than ever with our app!
You can also access the Uniswap Interface from an IPFS gateway.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeiajdg2uit3dy2c5dbxclr7w6subg6t4fxnegfifpiskay2qlyys5u.ipfs.dweb.link/
- https://bafybeiajdg2uit3dy2c5dbxclr7w6subg6t4fxnegfifpiskay2qlyys5u.ipfs.cf-ipfs.com/
- [ipfs://QmNxB7dgMsLA6wASBs8xCSjKa4XAkeZNiiaf7uAG7KxPme/](ipfs://QmNxB7dgMsLA6wASBs8xCSjKa4XAkeZNiiaf7uAG7KxPme/)
## 5.12.0 (2024-02-22)
### Features
* **web:** hotfix to prod for unitags launch (#6465) e22ab94
Other notable changes:
- Gas estimation and approval bug fix
- Updated Twitter icons to ‘X’
- Unsupported language bug fix
- Android bug fixes
web/5.12.0 mobile/1.21.1
\ No newline at end of file \ No newline at end of file
...@@ -125,17 +125,17 @@ android { ...@@ -125,17 +125,17 @@ android {
dev { dev {
isDefault(true) isDefault(true)
applicationIdSuffix ".dev" applicationIdSuffix ".dev"
versionName "1.22" versionName "1.21.1"
dimension "variant" dimension "variant"
} }
beta { beta {
applicationIdSuffix ".beta" applicationIdSuffix ".beta"
versionName "1.22" versionName "1.21.1"
dimension "variant" dimension "variant"
} }
prod { prod {
dimension "variant" dimension "variant"
versionName "1.22" versionName "1.21.1"
} }
} }
......
...@@ -2450,7 +2450,7 @@ ...@@ -2450,7 +2450,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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";
...@@ -2496,7 +2496,7 @@ ...@@ -2496,7 +2496,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -2542,7 +2542,7 @@ ...@@ -2542,7 +2542,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -2588,7 +2588,7 @@ ...@@ -2588,7 +2588,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -2630,7 +2630,7 @@ ...@@ -2630,7 +2630,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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";
...@@ -2673,7 +2673,7 @@ ...@@ -2673,7 +2673,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -2716,7 +2716,7 @@ ...@@ -2716,7 +2716,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -2759,7 +2759,7 @@ ...@@ -2759,7 +2759,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -2795,7 +2795,7 @@ ...@@ -2795,7 +2795,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -2833,7 +2833,7 @@ ...@@ -2833,7 +2833,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3003,7 +3003,7 @@ ...@@ -3003,7 +3003,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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";
...@@ -3047,7 +3047,7 @@ ...@@ -3047,7 +3047,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -3143,7 +3143,7 @@ ...@@ -3143,7 +3143,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3214,7 +3214,7 @@ ...@@ -3214,7 +3214,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
...@@ -3310,7 +3310,7 @@ ...@@ -3310,7 +3310,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3381,7 +3381,7 @@ ...@@ -3381,7 +3381,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.22; MARKETING_VERSION = 1.21.1;
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;
......
...@@ -78,7 +78,7 @@ ...@@ -78,7 +78,7 @@
"@uniswap/analytics": "1.7.0", "@uniswap/analytics": "1.7.0",
"@uniswap/analytics-events": "2.31.0", "@uniswap/analytics-events": "2.31.0",
"@uniswap/ethers-rs-mobile": "0.0.5", "@uniswap/ethers-rs-mobile": "0.0.5",
"@uniswap/sdk-core": "4.1.2", "@uniswap/sdk-core": "4.0.7",
"@uniswap/v3-sdk": "3.10.2", "@uniswap/v3-sdk": "3.10.2",
"@walletconnect/core": "2.10.1", "@walletconnect/core": "2.10.1",
"@walletconnect/react-native-compat": "2.10.1", "@walletconnect/react-native-compat": "2.10.1",
......
...@@ -47,7 +47,7 @@ import { useAsyncData } from 'utilities/src/react/hooks' ...@@ -47,7 +47,7 @@ import { useAsyncData } from 'utilities/src/react/hooks'
import { AnalyticsNavigationContextProvider } from 'utilities/src/telemetry/trace/AnalyticsNavigationContext' import { AnalyticsNavigationContextProvider } from 'utilities/src/telemetry/trace/AnalyticsNavigationContext'
import { config } from 'wallet/src/config' import { config } from 'wallet/src/config'
import { uniswapUrls } from 'wallet/src/constants/urls' import { uniswapUrls } from 'wallet/src/constants/urls'
import { initFirebaseAppCheck } from 'wallet/src/features/appCheck' import { initFirebaseAppCheck } from 'wallet/src/features/appCheck/utils'
import { useCurrentAppearanceSetting } from 'wallet/src/features/appearance/hooks' import { useCurrentAppearanceSetting } from 'wallet/src/features/appearance/hooks'
import { EXPERIMENT_NAMES, FEATURE_FLAGS } from 'wallet/src/features/experiments/constants' import { EXPERIMENT_NAMES, FEATURE_FLAGS } from 'wallet/src/features/experiments/constants'
import { selectFavoriteTokens } from 'wallet/src/features/favorites/selectors' import { selectFavoriteTokens } from 'wallet/src/features/favorites/selectors'
...@@ -125,8 +125,6 @@ function App(): JSX.Element | null { ...@@ -125,8 +125,6 @@ function App(): JSX.Element | null {
tier: getStatsigEnvironmentTier(), tier: getStatsigEnvironmentTier(),
}, },
api: uniswapUrls.statsigProxyUrl, api: uniswapUrls.statsigProxyUrl,
disableAutoMetricsLogging: true,
disableErrorLogging: true,
}, },
sdkKey: DUMMY_STATSIG_SDK_KEY, sdkKey: DUMMY_STATSIG_SDK_KEY,
user: deviceId ? { userID: deviceId } : {}, user: deviceId ? { userID: deviceId } : {},
......
import React, { ErrorInfo, PropsWithChildren } from 'react' import React, { ErrorInfo, PropsWithChildren } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Image, StyleSheet } from 'react-native'
import RNRestart from 'react-native-restart' import RNRestart from 'react-native-restart'
import { useAppDispatch } from 'src/app/hooks' import { useAppDispatch } from 'src/app/hooks'
import { Button, Flex, Text } from 'ui/src' import { Button, Flex, Text } from 'ui/src'
import { DEAD_LUNI } from 'ui/src/assets' import DeadLuni from 'ui/src/assets/graphics/dead-luni.svg'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
import { useAccounts } from 'wallet/src/features/wallet/hooks' import { useAccounts } from 'wallet/src/features/wallet/hooks'
import { setFinishedOnboarding } from 'wallet/src/features/wallet/slice' import { setFinishedOnboarding } from 'wallet/src/features/wallet/slice'
...@@ -62,15 +61,9 @@ function ErrorScreen({ error }: { error: Error }): JSX.Element { ...@@ -62,15 +61,9 @@ function ErrorScreen({ error }: { error: Error }): JSX.Element {
} }
return ( return (
<Flex <Flex centered fill gap="$spacing16" px="$spacing16" py="$spacing48">
centered
fill
backgroundColor="$surface1"
gap="$spacing16"
px="$spacing16"
py="$spacing48">
<Flex centered grow gap="$spacing36"> <Flex centered grow gap="$spacing36">
<Image source={DEAD_LUNI} style={styles.errorImage} /> <DeadLuni />
<Flex centered gap="$spacing8"> <Flex centered gap="$spacing8">
<Text variant="subheading1">{t('Uh oh!')}</Text> <Text variant="subheading1">{t('Uh oh!')}</Text>
<Text variant="body2">{t('Something crashed.')}</Text> <Text variant="body2">{t('Something crashed.')}</Text>
...@@ -88,11 +81,3 @@ function ErrorScreen({ error }: { error: Error }): JSX.Element { ...@@ -88,11 +81,3 @@ function ErrorScreen({ error }: { error: Error }): JSX.Element {
</Flex> </Flex>
) )
} }
const styles = StyleSheet.create({
errorImage: {
height: 150,
resizeMode: 'contain',
width: 150,
},
})
import { PropsWithChildren, useCallback } from 'react' import { PropsWithChildren, useCallback } from 'react'
import { useAppDispatch } from 'src/app/hooks' import { useAppDispatch } from 'src/app/hooks'
import { useAppStackNavigation } from 'src/app/navigation/types' import { useAppStackNavigation } from 'src/app/navigation/types'
import { ScannerModalState } from 'src/components/QRCodeScanner/constants'
import { closeModal, openModal } from 'src/features/modals/modalSlice' import { closeModal, openModal } from 'src/features/modals/modalSlice'
import { HomeScreenTabIndex } from 'src/screens/HomeScreenTabIndex' import { HomeScreenTabIndex } from 'src/screens/HomeScreenTabIndex'
import { Screens } from 'src/screens/Screens' import { Screens } from 'src/screens/Screens'
...@@ -9,13 +8,11 @@ import { ...@@ -9,13 +8,11 @@ import {
NavigateToSwapFlowArgs, NavigateToSwapFlowArgs,
WalletNavigationProvider, WalletNavigationProvider,
} from 'wallet/src/contexts/WalletNavigationContext' } from 'wallet/src/contexts/WalletNavigationContext'
import { useFiatOnRampIpAddressQuery } from 'wallet/src/features/fiatOnRamp/api'
import { ModalName } from 'wallet/src/telemetry/constants' import { ModalName } from 'wallet/src/telemetry/constants'
export function MobileWalletNavigationProvider({ children }: PropsWithChildren): JSX.Element { export function MobileWalletNavigationProvider({ children }: PropsWithChildren): JSX.Element {
const navigateToAccountActivityList = useNavigateToHomepageTab(HomeScreenTabIndex.Activity)
const navigateToAccountTokenList = useNavigateToHomepageTab(HomeScreenTabIndex.Tokens) const navigateToAccountTokenList = useNavigateToHomepageTab(HomeScreenTabIndex.Tokens)
const navigateToBuyOrReceiveWithEmptyWallet = useNavigateToBuyOrReceiveWithEmptyWallet() const navigateToAccountActivityList = useNavigateToHomepageTab(HomeScreenTabIndex.Activity)
const navigateToSwapFlow = useNavigateToSwapFlow() const navigateToSwapFlow = useNavigateToSwapFlow()
const navigateToTokenDetails = useNavigateToTokenDetails() const navigateToTokenDetails = useNavigateToTokenDetails()
...@@ -23,7 +20,6 @@ export function MobileWalletNavigationProvider({ children }: PropsWithChildren): ...@@ -23,7 +20,6 @@ export function MobileWalletNavigationProvider({ children }: PropsWithChildren):
<WalletNavigationProvider <WalletNavigationProvider
navigateToAccountActivityList={navigateToAccountActivityList} navigateToAccountActivityList={navigateToAccountActivityList}
navigateToAccountTokenList={navigateToAccountTokenList} navigateToAccountTokenList={navigateToAccountTokenList}
navigateToBuyOrReceiveWithEmptyWallet={navigateToBuyOrReceiveWithEmptyWallet}
navigateToSwapFlow={navigateToSwapFlow} navigateToSwapFlow={navigateToSwapFlow}
navigateToTokenDetails={navigateToTokenDetails}> navigateToTokenDetails={navigateToTokenDetails}>
{children} {children}
...@@ -63,25 +59,3 @@ function useNavigateToTokenDetails(): (currencyId: string) => void { ...@@ -63,25 +59,3 @@ function useNavigateToTokenDetails(): (currencyId: string) => void {
[navigation] [navigation]
) )
} }
function useNavigateToBuyOrReceiveWithEmptyWallet(): () => void {
const dispatch = useAppDispatch()
const { data } = useFiatOnRampIpAddressQuery()
const fiatOnRampEligible = Boolean(data?.isBuyAllowed)
return useCallback((): void => {
dispatch(closeModal({ name: ModalName.Send }))
if (fiatOnRampEligible) {
dispatch(openModal({ name: ModalName.FiatOnRamp }))
} else {
dispatch(
openModal({
name: ModalName.WalletConnectScan,
initialState: ScannerModalState.WalletQr,
})
)
}
}, [dispatch, fiatOnRampEligible])
}
...@@ -2,18 +2,17 @@ import React from 'react' ...@@ -2,18 +2,17 @@ import React from 'react'
import { AccountSwitcherModal } from 'src/app/modals/AccountSwitcherModal' import { AccountSwitcherModal } from 'src/app/modals/AccountSwitcherModal'
import { ExperimentsModal } from 'src/app/modals/ExperimentsModal' import { ExperimentsModal } from 'src/app/modals/ExperimentsModal'
import { ExploreModal } from 'src/app/modals/ExploreModal' import { ExploreModal } from 'src/app/modals/ExploreModal'
import { FiatOnRampAggregatorModal } from 'src/app/modals/FiatOnRampModalAggregator'
import { SwapModal } from 'src/app/modals/SwapModal' import { SwapModal } from 'src/app/modals/SwapModal'
import { TransferTokenModal } from 'src/app/modals/TransferTokenModal' import { TransferTokenModal } from 'src/app/modals/TransferTokenModal'
import { ViewOnlyExplainerModal } from 'src/app/modals/ViewOnlyExplainerModal'
import { LazyModalRenderer } from 'src/app/modals/utils' import { LazyModalRenderer } from 'src/app/modals/utils'
import { ViewOnlyExplainerModal } from 'src/app/modals/ViewOnlyExplainerModal'
import { ForceUpgradeModal } from 'src/components/forceUpgrade/ForceUpgradeModal'
import { RemoveWalletModal } from 'src/components/RemoveWallet/RemoveWalletModal' import { RemoveWalletModal } from 'src/components/RemoveWallet/RemoveWalletModal'
import { RestoreWalletModal } from 'src/components/RestoreWalletModal/RestoreWalletModal' import { RestoreWalletModal } from 'src/components/RestoreWalletModal/RestoreWalletModal'
import { WalletConnectModals } from 'src/components/WalletConnect/WalletConnectModals'
import { ForceUpgradeModal } from 'src/components/forceUpgrade/ForceUpgradeModal'
import { UnitagsIntroModal } from 'src/components/unitags/UnitagsIntroModal' import { UnitagsIntroModal } from 'src/components/unitags/UnitagsIntroModal'
import { WalletConnectModals } from 'src/components/WalletConnect/WalletConnectModals'
import { LockScreenModal } from 'src/features/authentication/LockScreenModal' import { LockScreenModal } from 'src/features/authentication/LockScreenModal'
import { ExchangeTransferModal } from 'src/features/fiatOnRamp/ExchangeTransferModal'
import { FiatOnRampAggregatorModal } from 'src/features/fiatOnRamp/FiatOnRampAggregatorModal'
import { FiatOnRampModal } from 'src/features/fiatOnRamp/FiatOnRampModal' import { FiatOnRampModal } from 'src/features/fiatOnRamp/FiatOnRampModal'
import { ScantasticModal } from 'src/features/scantastic/ScantasticModal' import { ScantasticModal } from 'src/features/scantastic/ScantasticModal'
import { ReceiveCryptoModal } from 'src/screens/ReceiveCryptoModal' import { ReceiveCryptoModal } from 'src/screens/ReceiveCryptoModal'
...@@ -24,10 +23,6 @@ import { ModalName } from 'wallet/src/telemetry/constants' ...@@ -24,10 +23,6 @@ import { ModalName } from 'wallet/src/telemetry/constants'
export function AppModals(): JSX.Element { export function AppModals(): JSX.Element {
return ( return (
<> <>
<LazyModalRenderer name={ModalName.ExchangeTransferModal}>
<ExchangeTransferModal />
</LazyModalRenderer>
<LazyModalRenderer name={ModalName.Experiments}> <LazyModalRenderer name={ModalName.Experiments}>
<ExperimentsModal /> <ExperimentsModal />
</LazyModalRenderer> </LazyModalRenderer>
......
...@@ -269,26 +269,37 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -269,26 +269,37 @@ exports[`AccountSwitcher renders correctly 1`] = `
</View> </View>
</View> </View>
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={16}
minPressDuration={0} onClick={[Function]}
onBlur={[Function]} onResponderGrant={[Function]}
onFocus={[Function]} onResponderMove={[Function]}
onMouseEnter={[Function]} onResponderRelease={[Function]}
onMouseLeave={[Function]} onResponderTerminate={[Function]}
onPress={[Function]} onResponderTerminationRequest={[Function]}
onPressIn={[Function]} onStartShouldSetResponder={[Function]}
onPressOut={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
testID="copy" testID="copy"
...@@ -507,27 +518,45 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -507,27 +518,45 @@ exports[`AccountSwitcher renders correctly 1`] = `
ExpoLinearGradient ExpoLinearGradient
</View> </View>
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onMouseEnter={[Function]} "right": 5,
onMouseLeave={[Function]} "top": 5,
onPress={[Function]} }
onPressIn={[Function]} }
onPressOut={[Function]} onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"flexDirection": "column",
"marginTop": 16, "marginTop": 16,
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
......
...@@ -33,28 +33,39 @@ exports[`AccountHeader renders without error 1`] = ` ...@@ -33,28 +33,39 @@ exports[`AccountHeader renders without error 1`] = `
} }
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={20}
minPressDuration={0} onClick={[Function]}
onBlur={[Function]} onResponderGrant={[Function]}
onFocus={[Function]} onResponderMove={[Function]}
onLongPress={[Function]} onResponderRelease={[Function]}
onMouseEnter={[Function]} onResponderTerminate={[Function]}
onMouseLeave={[Function]} onResponderTerminationRequest={[Function]}
onPress={[Function]} onStartShouldSetResponder={[Function]}
onPressIn={[Function]}
onPressOut={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"flexDirection": "row", "flexDirection": "row",
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
testID="manage" testID="manage"
...@@ -323,26 +334,37 @@ exports[`AccountHeader renders without error 1`] = ` ...@@ -323,26 +334,37 @@ exports[`AccountHeader renders without error 1`] = `
</View> </View>
</View> </View>
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={20}
minPressDuration={0} onClick={[Function]}
onBlur={[Function]} onResponderGrant={[Function]}
onFocus={[Function]} onResponderMove={[Function]}
onMouseEnter={[Function]} onResponderRelease={[Function]}
onMouseLeave={[Function]} onResponderTerminate={[Function]}
onPress={[Function]} onResponderTerminationRequest={[Function]}
onPressIn={[Function]} onStartShouldSetResponder={[Function]}
onPressOut={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
...@@ -418,27 +440,38 @@ exports[`AccountHeader renders without error 1`] = ` ...@@ -418,27 +440,38 @@ exports[`AccountHeader renders without error 1`] = `
} }
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={20}
minPressDuration={0} onClick={[Function]}
onBlur={[Function]} onResponderGrant={[Function]}
onFocus={[Function]} onResponderMove={[Function]}
onMouseEnter={[Function]} onResponderRelease={[Function]}
onMouseLeave={[Function]} onResponderTerminate={[Function]}
onPress={[Function]} onResponderTerminationRequest={[Function]}
onPressIn={[Function]} onStartShouldSetResponder={[Function]}
onPressOut={[Function]}
style={ style={
{ {
"flexDirection": "column",
"flexShrink": 1, "flexShrink": 1,
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
...@@ -532,27 +565,38 @@ exports[`AccountHeader renders without error 1`] = ` ...@@ -532,27 +565,38 @@ exports[`AccountHeader renders without error 1`] = `
</Text> </Text>
</View> </View>
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={20}
minPressDuration={0} onClick={[Function]}
onBlur={[Function]} onResponderGrant={[Function]}
onFocus={[Function]} onResponderMove={[Function]}
onMouseEnter={[Function]} onResponderRelease={[Function]}
onMouseLeave={[Function]} onResponderTerminate={[Function]}
onPress={[Function]} onResponderTerminationRequest={[Function]}
onPressIn={[Function]} onStartShouldSetResponder={[Function]}
onPressOut={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"paddingLeft": 8, "paddingLeft": 8,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
......
...@@ -108,31 +108,48 @@ exports[`AccountList renders without error 1`] = ` ...@@ -108,31 +108,48 @@ exports[`AccountList renders without error 1`] = `
onPress={[Function]} onPress={[Function]}
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onLongPress={[Function]} "right": 5,
onMouseEnter={[Function]} "top": 5,
onMouseLeave={[Function]} }
onPress={[Function]} }
onPressIn={[Function]} onClick={[Function]}
onPressOut={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"paddingBottom": 12, "paddingBottom": 12,
"paddingLeft": 24, "paddingLeft": 24,
"paddingRight": 24, "paddingRight": 24,
"paddingTop": 8, "paddingTop": 8,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
......
...@@ -264,7 +264,7 @@ function FavoritesSection(props: FavoritesSectionProps): JSX.Element | null { ...@@ -264,7 +264,7 @@ function FavoritesSection(props: FavoritesSectionProps): JSX.Element | null {
px="$spacing12" px="$spacing12"
zIndex={1}> zIndex={1}>
{hasFavoritedTokens && <FavoriteTokensGrid {...props} />} {hasFavoritedTokens && <FavoriteTokensGrid {...props} />}
{hasFavoritedWallets && <FavoriteWalletsGrid {...props} />} {hasFavoritedWallets && <FavoriteWalletsGrid showLoading={props.showLoading} />}
</Flex> </Flex>
) )
} }
......
...@@ -2,11 +2,18 @@ import { ImpactFeedbackStyle } from 'expo-haptics' ...@@ -2,11 +2,18 @@ import { ImpactFeedbackStyle } from 'expo-haptics'
import React, { memo, useCallback } from 'react' import React, { memo, useCallback } from 'react'
import { ViewProps } from 'react-native' import { ViewProps } from 'react-native'
import ContextMenu from 'react-native-context-menu-view' import ContextMenu from 'react-native-context-menu-view'
import { FadeIn, SharedValue } from 'react-native-reanimated' import {
FadeIn,
SharedValue,
interpolate,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
} from 'react-native-reanimated'
import { useAppDispatch } from 'src/app/hooks' import { useAppDispatch } from 'src/app/hooks'
import { useTokenDetailsNavigation } from 'src/components/TokenDetails/hooks' import { useTokenDetailsNavigation } from 'src/components/TokenDetails/hooks'
import RemoveButton from 'src/components/explore/RemoveButton' import RemoveButton from 'src/components/explore/RemoveButton'
import { useAnimatedCardDragStyle, useExploreTokenContextMenu } from 'src/components/explore/hooks' import { useExploreTokenContextMenu } from 'src/components/explore/hooks'
import { Loader } from 'src/components/loading' import { Loader } from 'src/components/loading'
import { disableOnPress } from 'src/utils/disableOnPress' import { disableOnPress } from 'src/utils/disableOnPress'
import { usePollOnFocusOnly } from 'src/utils/hooks' import { usePollOnFocusOnly } from 'src/utils/hooks'
...@@ -48,6 +55,8 @@ function FavoriteTokenCard({ ...@@ -48,6 +55,8 @@ function FavoriteTokenCard({
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const tokenDetailsNavigation = useTokenDetailsNavigation() const tokenDetailsNavigation = useTokenDetailsNavigation()
const { convertFiatAmountFormatted } = useLocalizationContext() const { convertFiatAmountFormatted } = useLocalizationContext()
const dragAnimationProgress = useSharedValue(0)
const wasTouched = useSharedValue(false)
const { data, networkStatus, startPolling, stopPolling } = useFavoriteTokenCardQuery({ const { data, networkStatus, startPolling, stopPolling } = useFavoriteTokenCardQuery({
variables: currencyIdToContractInput(currencyId), variables: currencyIdToContractInput(currencyId),
...@@ -94,14 +103,45 @@ function FavoriteTokenCard({ ...@@ -94,14 +103,45 @@ function FavoriteTokenCard({
tokenDetailsNavigation.navigate(currencyId) tokenDetailsNavigation.navigate(currencyId)
} }
const animatedDragStyle = useAnimatedCardDragStyle(isTouched, dragActivationProgress) useAnimatedReaction(
() => dragActivationProgress.value,
(activationProgress, prev) => {
const prevActivationProgress = prev ?? 0
// If the activation progress is increasing (the user is touching one of the cards)
if (activationProgress > prevActivationProgress) {
if (isTouched.value) {
// If the current card is the one being touched, reset the animation progress
wasTouched.value = true
dragAnimationProgress.value = 0
} else {
// Otherwise, animate the card
wasTouched.value = false
dragAnimationProgress.value = activationProgress
}
}
// If the activation progress is decreasing (the user is no longer touching one of the cards)
else {
if (isTouched.value || wasTouched.value) {
// If the current card is the one that was being touched, reset the animation progress
dragAnimationProgress.value = 0
} else {
// Otherwise, animate the card
dragAnimationProgress.value = activationProgress
}
}
}
)
const animatedStyle = useAnimatedStyle(() => ({
opacity: interpolate(dragAnimationProgress.value, [0, 1], [1, 0.5]),
}))
if (isNonPollingRequestInFlight(networkStatus)) { if (isNonPollingRequestInFlight(networkStatus)) {
return <Loader.Favorite height={FAVORITE_TOKEN_CARD_LOADER_HEIGHT} /> return <Loader.Favorite height={FAVORITE_TOKEN_CARD_LOADER_HEIGHT} />
} }
return ( return (
<AnimatedFlex style={animatedDragStyle}> <AnimatedFlex style={animatedStyle}>
<ContextMenu <ContextMenu
actions={menuActions} actions={menuActions}
disabled={isEditing} disabled={isEditing}
......
import { ImpactFeedbackStyle } from 'expo-haptics' import { ImpactFeedbackStyle } from 'expo-haptics'
import { memo, useCallback, useMemo } from 'react' import { default as React, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ViewProps } from 'react-native' import { ViewProps } from 'react-native'
import ContextMenu from 'react-native-context-menu-view' import ContextMenu from 'react-native-context-menu-view'
import { SharedValue } from 'react-native-reanimated'
import { useAppDispatch } from 'src/app/hooks' import { useAppDispatch } from 'src/app/hooks'
import { useEagerExternalProfileNavigation } from 'src/app/navigation/hooks' import { useEagerExternalProfileNavigation } from 'src/app/navigation/hooks'
import { useAnimatedCardDragStyle } from 'src/components/explore/hooks'
import RemoveButton from 'src/components/explore/RemoveButton' import RemoveButton from 'src/components/explore/RemoveButton'
import { disableOnPress } from 'src/utils/disableOnPress' import { disableOnPress } from 'src/utils/disableOnPress'
import { AnimatedFlex, Flex, TouchableArea } from 'ui/src' import { Flex, TouchableArea } from 'ui/src'
import { borderRadii, iconSizes } from 'ui/src/theme' import { borderRadii, iconSizes } from 'ui/src/theme'
import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon'
import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText'
...@@ -21,16 +19,12 @@ import { DisplayNameType } from 'wallet/src/features/wallet/types' ...@@ -21,16 +19,12 @@ import { DisplayNameType } from 'wallet/src/features/wallet/types'
type FavoriteWalletCardProps = { type FavoriteWalletCardProps = {
address: Address address: Address
isEditing?: boolean isEditing?: boolean
isTouched: SharedValue<boolean>
dragActivationProgress: SharedValue<number>
setIsEditing: (update: boolean) => void setIsEditing: (update: boolean) => void
} & ViewProps } & ViewProps
function FavoriteWalletCard({ export default function FavoriteWalletCard({
address, address,
isEditing, isEditing,
isTouched,
dragActivationProgress,
setIsEditing, setIsEditing,
...rest ...rest
}: FavoriteWalletCardProps): JSX.Element { }: FavoriteWalletCardProps): JSX.Element {
...@@ -57,60 +51,51 @@ function FavoriteWalletCard({ ...@@ -57,60 +51,51 @@ function FavoriteWalletCard({
] ]
}, [t]) }, [t])
const animatedDragStyle = useAnimatedCardDragStyle(isTouched, dragActivationProgress)
return ( return (
<AnimatedFlex style={animatedDragStyle}> <ContextMenu
<ContextMenu actions={menuActions}
actions={menuActions} disabled={isEditing}
disabled={isEditing} style={{ borderRadius: borderRadii.rounded16 }}
style={{ borderRadius: borderRadii.rounded16 }} onPress={(e): void => {
onPress={(e): void => { // Emitted index based on order of menu action array
// Emitted index based on order of menu action array // remove favorite action
// remove favorite action if (e.nativeEvent.index === 0) {
if (e.nativeEvent.index === 0) { onRemove()
onRemove() }
} // Edit mode toggle action
// Edit mode toggle action if (e.nativeEvent.index === 1) {
if (e.nativeEvent.index === 1) { setIsEditing(true)
setIsEditing(true) }
} }}
{...rest}>
<TouchableArea
hapticFeedback
borderRadius="$rounded16"
hapticStyle={ImpactFeedbackStyle.Light}
m="$spacing4"
onLongPress={disableOnPress}
onPress={(): void => {
navigate(address)
}} }}
{...rest}> onPressIn={async (): Promise<void> => {
<TouchableArea await preload(address)
hapticFeedback }}>
activeOpacity={isEditing ? 1 : undefined} <BaseCard.Shadow>
backgroundColor="$surface2" <Flex row gap="$spacing4" justifyContent="space-between">
borderRadius="$rounded16" <Flex row shrink alignItems="center" gap="$spacing8">
disabled={isEditing} {icon}
hapticStyle={ImpactFeedbackStyle.Light} <DisplayNameText
m="$spacing4" displayName={displayName}
onLongPress={disableOnPress} textProps={{
onPress={(): void => { adjustsFontSizeToFit: displayName?.type === DisplayNameType.Address,
navigate(address) variant: 'body1',
}} }}
onPressIn={async (): Promise<void> => { />
await preload(address)
}}>
<BaseCard.Shadow>
<Flex row gap="$spacing4" justifyContent="space-between">
<Flex row shrink alignItems="center" gap="$spacing8">
{icon}
<DisplayNameText
displayName={displayName}
textProps={{
adjustsFontSizeToFit: displayName?.type === DisplayNameType.Address,
variant: 'body1',
}}
/>
</Flex>
<RemoveButton visible={isEditing} onPress={onRemove} />
</Flex> </Flex>
</BaseCard.Shadow> <RemoveButton visible={isEditing} onPress={onRemove} />
</TouchableArea> </Flex>
</ContextMenu> </BaseCard.Shadow>
</AnimatedFlex> </TouchableArea>
</ContextMenu>
) )
} }
export default memo(FavoriteWalletCard)
import { default as React, useCallback, useEffect, useMemo, useState } from 'react' import { default as React, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { FadeIn, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { FadeIn } from 'react-native-reanimated'
import { useAppSelector } from 'src/app/hooks' import { useAppSelector } from 'src/app/hooks'
import { FavoriteHeaderRow } from 'src/components/explore/FavoriteHeaderRow' import { FavoriteHeaderRow } from 'src/components/explore/FavoriteHeaderRow'
import FavoriteWalletCard from 'src/components/explore/FavoriteWalletCard' import FavoriteWalletCard from 'src/components/explore/FavoriteWalletCard'
import { Loader } from 'src/components/loading' import { Loader } from 'src/components/loading'
import {
AutoScrollProps,
SortableGrid,
SortableGridChangeEvent,
SortableGridRenderItem,
} from 'src/components/sortableGrid'
import { AnimatedFlex, Flex } from 'ui/src' import { AnimatedFlex, Flex } from 'ui/src'
import { selectWatchedAddressSet } from 'wallet/src/features/favorites/selectors' import { selectWatchedAddressSet } from 'wallet/src/features/favorites/selectors'
import { setFavoriteWallets } from 'wallet/src/features/favorites/slice'
import { useAppDispatch } from 'wallet/src/state'
const NUM_COLUMNS = 2 const NUM_COLUMNS = 2
const ITEM_FLEX = { flex: 1 / NUM_COLUMNS } const ITEM_FLEX = { flex: 1 / NUM_COLUMNS }
const HALF_WIDTH = { width: '50%' }
type FavoriteWalletsGridProps = AutoScrollProps & {
showLoading: boolean
}
/** Renders the favorite wallets section on the Explore tab */ /** Renders the favorite wallets section on the Explore tab */
export function FavoriteWalletsGrid({ export function FavoriteWalletsGrid({ showLoading }: { showLoading: boolean }): JSX.Element {
showLoading,
...rest
}: FavoriteWalletsGridProps): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
const dispatch = useAppDispatch()
const [isEditing, setIsEditing] = useState(false) const [isEditing, setIsEditing] = useState(false)
const isTokenDragged = useSharedValue(false)
const watchedWalletsSet = useAppSelector(selectWatchedAddressSet) const watchedWalletsSet = useAppSelector(selectWatchedAddressSet)
const watchedWalletsList = useMemo(() => Array.from(watchedWalletsSet), [watchedWalletsSet]) const watchedWalletsList = useMemo(() => Array.from(watchedWalletsSet), [watchedWalletsSet])
...@@ -43,33 +27,8 @@ export function FavoriteWalletsGrid({ ...@@ -43,33 +27,8 @@ export function FavoriteWalletsGrid({
} }
}, [watchedWalletsSet.size]) }, [watchedWalletsSet.size])
const handleOrderChange = useCallback(
({ data }: SortableGridChangeEvent<string>) => {
dispatch(setFavoriteWallets({ addresses: data }))
},
[dispatch]
)
const renderItem = useCallback<SortableGridRenderItem<string>>(
({ item: address, isTouched, dragActivationProgress }): JSX.Element => (
<FavoriteWalletCard
key={address}
address={address}
dragActivationProgress={dragActivationProgress}
isEditing={isEditing}
isTouched={isTouched}
setIsEditing={setIsEditing}
/>
),
[isEditing]
)
const animatedStyle = useAnimatedStyle(() => ({
zIndex: isTokenDragged.value ? 1 : 0,
}))
return ( return (
<AnimatedFlex entering={FadeIn} style={animatedStyle}> <AnimatedFlex entering={FadeIn}>
<FavoriteHeaderRow <FavoriteHeaderRow
editingTitle={t('Edit favorite wallets')} editingTitle={t('Edit favorite wallets')}
isEditing={isEditing} isEditing={isEditing}
...@@ -79,21 +38,17 @@ export function FavoriteWalletsGrid({ ...@@ -79,21 +38,17 @@ export function FavoriteWalletsGrid({
{showLoading ? ( {showLoading ? (
<FavoriteWalletsGridLoader /> <FavoriteWalletsGridLoader />
) : ( ) : (
<SortableGrid <Flex row flexWrap="wrap">
{...rest} {watchedWalletsList.map((address) => (
activeItemOpacity={1} <FavoriteWalletCard
data={watchedWalletsList} key={address}
editable={isEditing} address={address}
numColumns={NUM_COLUMNS} isEditing={isEditing}
renderItem={renderItem} setIsEditing={setIsEditing}
onChange={handleOrderChange} style={HALF_WIDTH}
onDragEnd={(): void => { />
isTokenDragged.value = false ))}
}} </Flex>
onDragStart={(): void => {
isTokenDragged.value = true
}}
/>
)} )}
</AnimatedFlex> </AnimatedFlex>
) )
......
import { SharedEventName } from '@uniswap/analytics-events' import { SharedEventName } from '@uniswap/analytics-events'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { NativeSyntheticEvent, Share, ViewStyle } from 'react-native' import { NativeSyntheticEvent, Share } from 'react-native'
import { ContextMenuAction, ContextMenuOnPressNativeEvent } from 'react-native-context-menu-view' import { ContextMenuAction, ContextMenuOnPressNativeEvent } from 'react-native-context-menu-view'
import {
AnimateStyle,
SharedValue,
interpolate,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
} from 'react-native-reanimated'
import { ScannerModalState } from 'src/components/QRCodeScanner/constants' import { ScannerModalState } from 'src/components/QRCodeScanner/constants'
import { useSelectHasTokenFavorited, useToggleFavoriteCallback } from 'src/features/favorites/hooks' import { useSelectHasTokenFavorited, useToggleFavoriteCallback } from 'src/features/favorites/hooks'
import { openModal } from 'src/features/modals/modalSlice' import { openModal } from 'src/features/modals/modalSlice'
...@@ -156,44 +148,3 @@ export function useExploreTokenContextMenu({ ...@@ -156,44 +148,3 @@ export function useExploreTokenContextMenu({
return { menuActions, onContextMenuPress } return { menuActions, onContextMenuPress }
} }
export function useAnimatedCardDragStyle(
isTouched: SharedValue<boolean>,
dragActivationProgress: SharedValue<number>
): AnimateStyle<ViewStyle> {
const wasTouched = useSharedValue(false)
const dragAnimationProgress = useSharedValue(0)
useAnimatedReaction(
() => dragActivationProgress.value,
(activationProgress, prev) => {
const prevActivationProgress = prev ?? 0
// If the activation progress is increasing (the user is touching one of the cards)
if (activationProgress > prevActivationProgress) {
if (isTouched.value) {
// If the current card is the one being touched, reset the animation progress
wasTouched.value = true
dragAnimationProgress.value = 0
} else {
// Otherwise, animate the card
wasTouched.value = false
dragAnimationProgress.value = activationProgress
}
}
// If the activation progress is decreasing (the user is no longer touching one of the cards)
else {
if (isTouched.value || wasTouched.value) {
// If the current card is the one that was being touched, reset the animation progress
dragAnimationProgress.value = 0
} else {
// Otherwise, animate the card
dragAnimationProgress.value = activationProgress
}
}
}
)
return useAnimatedStyle(() => ({
opacity: interpolate(dragAnimationProgress.value, [0, 1], [1, 0.5]),
}))
}
...@@ -468,27 +468,44 @@ exports[`SearchPopularTokens renders without error 2`] = ` ...@@ -468,27 +468,44 @@ exports[`SearchPopularTokens renders without error 2`] = `
onPress={[Function]} onPress={[Function]}
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onLongPress={[Function]} "right": 5,
onMouseEnter={[Function]} "top": 5,
onMouseLeave={[Function]} }
onPress={[Function]} }
onPressIn={[Function]} onClick={[Function]}
onPressOut={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
testID="search-token-item" testID="search-token-item"
...@@ -650,27 +667,44 @@ exports[`SearchPopularTokens renders without error 2`] = ` ...@@ -650,27 +667,44 @@ exports[`SearchPopularTokens renders without error 2`] = `
onPress={[Function]} onPress={[Function]}
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onLongPress={[Function]} "right": 5,
onMouseEnter={[Function]} "top": 5,
onMouseLeave={[Function]} }
onPress={[Function]} }
onPressIn={[Function]} onClick={[Function]}
onPressOut={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
testID="search-token-item" testID="search-token-item"
......
...@@ -2,10 +2,10 @@ import { Currency } from '@uniswap/sdk-core' ...@@ -2,10 +2,10 @@ import { Currency } from '@uniswap/sdk-core'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { StyleSheet } from 'react-native' import { StyleSheet } from 'react-native'
import { useFiatOnRampLogoUrl } from 'src/components/fiatOnRamp/hooks'
import { Loader } from 'src/components/loading' import { Loader } from 'src/components/loading'
import { useFormatExactCurrencyAmount } from 'src/features/fiatOnRamp/hooks' import { useFormatExactCurrencyAmount } from 'src/features/fiatOnRamp/hooks'
import { getServiceProviderLogo } from 'src/features/fiatOnRamp/utils' import { Flex, Icons, Text, TouchableArea } from 'ui/src'
import { Flex, Icons, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { fonts, iconSizes } from 'ui/src/theme' import { fonts, iconSizes } from 'ui/src/theme'
import { FiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { FiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks'
import { FORQuote, FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types' import { FORQuote, FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
...@@ -13,12 +13,6 @@ import { ImageUri } from 'wallet/src/features/images/ImageUri' ...@@ -13,12 +13,6 @@ import { ImageUri } from 'wallet/src/features/images/ImageUri'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { getSymbolDisplayText } from 'wallet/src/utils/currency' import { getSymbolDisplayText } from 'wallet/src/utils/currency'
function LogoLoader(): JSX.Element {
return (
<Loader.Box borderRadius="$roundedFull" height={iconSizes.icon40} width={iconSizes.icon40} />
)
}
export function FORQuoteItem({ export function FORQuoteItem({
quote, quote,
serviceProvider, serviceProvider,
...@@ -52,8 +46,7 @@ export function FORQuoteItem({ ...@@ -52,8 +46,7 @@ export function FORQuoteItem({
currencySymbol: baseCurrency.symbol, currencySymbol: baseCurrency.symbol,
}) })
const isDarkMode = useIsDarkMode() const logoUrl = useFiatOnRampLogoUrl(serviceProvider?.logos)
const logoUrl = getServiceProviderLogo(serviceProvider?.logos, isDarkMode)
return ( return (
<TouchableArea onPress={onPress}> <TouchableArea onPress={onPress}>
...@@ -69,17 +62,11 @@ export function FORQuoteItem({ ...@@ -69,17 +62,11 @@ export function FORQuoteItem({
<QuoteLoader showCarret={showCarret} /> <QuoteLoader showCarret={showCarret} />
) : ( ) : (
<Flex row alignItems="center" gap="$spacing12"> <Flex row alignItems="center" gap="$spacing12">
<Flex> <Loader.Box
{logoUrl ? ( borderRadius="$roundedFull"
<ImageUri height={iconSizes.icon40}
fallback={<LogoLoader />} width={iconSizes.icon40}
imageStyle={ServiceProviderLogoStyles.icon} />
uri={logoUrl}
/>
) : (
<LogoLoader />
)}
</Flex>
<Flex shrink gap="$spacing4"> <Flex shrink gap="$spacing4">
<Text color="$neutral1" variant="subheading2"> <Text color="$neutral1" variant="subheading2">
{serviceProvider?.name} {serviceProvider?.name}
...@@ -109,6 +96,23 @@ export function FORQuoteItem({ ...@@ -109,6 +96,23 @@ export function FORQuoteItem({
<Flex /> <Flex />
)} )}
</Flex> </Flex>
{
// TODO: Enable once https://linear.app/uniswap/issue/MOB-2565/implement-service-providers-logo-once-meld-has-added-them-on-their is unblocked
false && logoUrl && (
<ImageUri
fallback={
<Loader.Box
borderRadius="$roundedFull"
height={iconSizes.icon40}
width={iconSizes.icon40}
/>
}
imageStyle={ServiceProviderLogoStyles.icon}
resizeMode="contain"
uri={logoUrl}
/>
)
}
</Flex> </Flex>
)} )}
</Flex> </Flex>
......
import { useIsDarkMode } from 'ui/src'
import { FORLogo } from 'wallet/src/features/fiatOnRamp/types'
export function useFiatOnRampLogoUrl(logos: FORLogo | undefined): string | undefined {
const isDarkMode = useIsDarkMode()
if (!logos) {
return
}
return isDarkMode ? logos.darkLogo : logos.lightLogo
}
import React from 'react'
import { WalletLoader } from 'src/components/loading/WalletLoader'
import { render } from 'src/test/test-utils'
it('renders wallet loader', () => {
const tree = render(<WalletLoader opacity={1} />)
expect(tree).toMatchSnapshot()
})
import { Flex } from 'ui/src/components/layout/Flex' import React from 'react'
import { Text } from 'ui/src/components/text/Text' import { ADDRESS_WRAPPER_HEIGHT } from 'src/features/import/WalletPreviewCard'
import { Flex, Text } from 'ui/src'
interface Props { interface Props {
opacity: number opacity: number
} }
export const ADDRESS_WRAPPER_HEIGHT = 36
export function WalletLoader({ opacity }: Props): JSX.Element { export function WalletLoader({ opacity }: Props): JSX.Element {
return ( return (
<Flex <Flex
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders wallet loader 1`] = `
<View
sentry-label="WalletLoader"
style={
{
"alignItems": "center",
"borderBottomColor": "#CECECE",
"borderBottomLeftRadius": 20,
"borderBottomRightRadius": 20,
"borderBottomWidth": 1,
"borderLeftColor": "#CECECE",
"borderLeftWidth": 1,
"borderRightColor": "#CECECE",
"borderRightWidth": 1,
"borderStyle": "solid",
"borderTopColor": "#CECECE",
"borderTopLeftRadius": 20,
"borderTopRightRadius": 20,
"borderTopWidth": 1,
"flexDirection": "row",
"justifyContent": "flex-start",
"opacity": 1,
"overflow": "hidden",
"paddingBottom": 16,
"paddingLeft": 16,
"paddingRight": 16,
"paddingTop": 16,
}
}
>
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
"gap": 12,
"height": 36,
}
}
>
<View
style={
{
"backgroundColor": "#CECECE",
"borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999,
"borderTopRightRadius": 999999,
"flexDirection": "column",
"height": 32,
"width": 32,
}
}
/>
<View
style={
{
"alignItems": "flex-start",
"flexDirection": "column",
"width": "100%",
}
}
>
<View
onLayout={[Function]}
style={
{
"flexDirection": "column",
"opacity": 0,
}
}
testID="shimmer-placeholder"
>
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
}
}
testID="text-placeholder"
>
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
"position": "relative",
}
}
>
<View
accessibilityElementsHidden={true}
importantForAccessibility="no-hide-descendants"
>
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
style={
{
"color": "transparent",
"fontFamily": "Basel-Book",
"fontSize": 19,
"lineHeight": 24,
"opacity": 0,
}
}
suppressHighlighting={true}
>
Wallet Nickname
</Text>
</View>
<View
style={
{
"backgroundColor": "#F9F9F9",
"borderBottomLeftRadius": 4,
"borderBottomRightRadius": 4,
"borderTopLeftRadius": 4,
"borderTopRightRadius": 4,
"bottom": "5%",
"flexDirection": "column",
"left": 0,
"position": "absolute",
"right": 0,
"top": "5%",
}
}
/>
</View>
</View>
</View>
<View
onLayout={[Function]}
style={
{
"flexDirection": "column",
"opacity": 0,
}
}
testID="shimmer-placeholder"
>
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
}
}
testID="text-placeholder"
>
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
"position": "relative",
}
}
>
<View
accessibilityElementsHidden={true}
importantForAccessibility="no-hide-descendants"
>
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
style={
{
"color": "transparent",
"fontFamily": "Basel-Book",
"fontSize": 17,
"lineHeight": 24,
"opacity": 0,
}
}
suppressHighlighting={true}
>
0xaaaa...aaaa
</Text>
</View>
<View
style={
{
"backgroundColor": "#F9F9F9",
"borderBottomLeftRadius": 4,
"borderBottomRightRadius": 4,
"borderTopLeftRadius": 4,
"borderTopRightRadius": 4,
"bottom": "5%",
"flexDirection": "column",
"left": 0,
"position": "absolute",
"right": 0,
"top": "5%",
}
}
/>
</View>
</View>
</View>
</View>
</View>
</View>
`;
import React, { memo } from 'react' import React, { memo } from 'react'
import { TransactionLoader } from 'src/components/loading/TransactionLoader' import { TransactionLoader } from 'src/components/loading/TransactionLoader'
import { WalletLoader } from 'src/components/loading/WalletLoader'
import { WaveLoader } from 'src/components/loading/WaveLoader' import { WaveLoader } from 'src/components/loading/WaveLoader'
import { Flex, FlexLoader, FlexLoaderProps, getToken, Skeleton } from 'ui/src' import { Flex, FlexLoader, FlexLoaderProps, getToken, Skeleton } from 'ui/src'
...@@ -11,6 +12,20 @@ function Graph(): JSX.Element { ...@@ -11,6 +12,20 @@ function Graph(): JSX.Element {
) )
} }
function Wallets({ repeat = 1 }: { repeat?: number }): JSX.Element {
return (
<Skeleton>
<Flex gap="$spacing12">
{new Array(repeat).fill(null).map((_, i, { length }) => (
<React.Fragment key={i}>
<WalletLoader opacity={(length - i) / length} />
</React.Fragment>
))}
</Flex>
</Skeleton>
)
}
export const Transaction = memo(function _Transaction({ export const Transaction = memo(function _Transaction({
repeat = 1, repeat = 1,
}: { }: {
...@@ -57,6 +72,7 @@ function Favorite({ height, contrast }: { height?: number; contrast?: boolean }) ...@@ -57,6 +72,7 @@ function Favorite({ height, contrast }: { height?: number; contrast?: boolean })
export const Loader = { export const Loader = {
Box, Box,
Transaction, Transaction,
Wallets,
Graph, Graph,
Image, Image,
Favorite, Favorite,
......
...@@ -30,9 +30,6 @@ export function useAnimatedZIndex(renderIndex: number): SharedValue<number> { ...@@ -30,9 +30,6 @@ export function useAnimatedZIndex(renderIndex: number): SharedValue<number> {
previousActiveIndex: previousActiveIndexValue.value, previousActiveIndex: previousActiveIndexValue.value,
}), }),
({ touchedIndex, previousActiveIndex }) => { ({ touchedIndex, previousActiveIndex }) => {
if (touchedIndex === null) {
return null
}
if (renderIndex === touchedIndex) { if (renderIndex === touchedIndex) {
// Display the currently touched item on top of all other items // Display the currently touched item on top of all other items
zIndexValue.value = 10000 zIndexValue.value = 10000
......
import React, { useRef, useState } from 'react' import React, { useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Keyboard, TextInput } from 'react-native' import { Keyboard, TextInput } from 'react-native'
import { PasswordInput } from 'src/components/input/PasswordInput' import { PasswordInput } from 'src/components/input/PasswordInput'
import { PasswordError } from 'src/features/onboarding/PasswordError' import { PasswordError } from 'src/features/onboarding/PasswordError'
import { Button, Flex, Icons, Text } from 'ui/src' import { Button, Flex, Icons, Text } from 'ui/src'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { useDebounce } from 'utilities/src/time/timing'
import { ElementName } from 'wallet/src/telemetry/constants' import { ElementName } from 'wallet/src/telemetry/constants'
import { import { validatePassword } from 'wallet/src/utils/password'
PASSWORD_VALIDATION_DEBOUNCE_MS,
PasswordStrength,
getPasswordStrength,
getPasswordStrengthTextAndColor,
isPasswordStrongEnough,
} from 'wallet/src/utils/password'
export enum PasswordErrors { export enum PasswordErrors {
WeakPassword = 'WeakPassword', WeakPassword = 'WeakPassword',
...@@ -36,17 +29,9 @@ export function CloudBackupPasswordForm({ ...@@ -36,17 +29,9 @@ export function CloudBackupPasswordForm({
const passwordInputRef = useRef<TextInput>(null) const passwordInputRef = useRef<TextInput>(null)
const [password, setPassword] = useState('') const [password, setPassword] = useState('')
const [error, setError] = useState<PasswordErrors | undefined>(undefined) const [error, setError] = useState<PasswordErrors | string | undefined>(undefined)
const [passwordStrength, setPasswordStrength] = useState(PasswordStrength.NONE) const isButtonDisabled = !!error || password.length === 0
const debouncedPasswordStrength = useDebounce(passwordStrength, PASSWORD_VALIDATION_DEBOUNCE_MS)
const isStrongPassword = isPasswordStrongEnough({
minStrength: PasswordStrength.MEDIUM,
currentStrength: passwordStrength,
})
const isButtonDisabled =
!!error || password.length === 0 || (!isConfirmation && !isStrongPassword)
const onPasswordChangeText = (newPassword: string): void => { const onPasswordChangeText = (newPassword: string): void => {
if (isConfirmation && newPassword === password) { if (isConfirmation && newPassword === password) {
...@@ -54,15 +39,15 @@ export function CloudBackupPasswordForm({ ...@@ -54,15 +39,15 @@ export function CloudBackupPasswordForm({
} }
// always reset error if not confirmation // always reset error if not confirmation
if (!isConfirmation) { if (!isConfirmation) {
setPasswordStrength(getPasswordStrength(newPassword))
setError(undefined) setError(undefined)
} }
setPassword(newPassword) setPassword(newPassword)
} }
const onPasswordSubmitEditing = (): void => { const onPasswordSubmitEditing = (): void => {
if (!isConfirmation && !isStrongPassword) { const { valid, validationErrorString } = validatePassword(password)
setError(PasswordErrors.WeakPassword) if (!isConfirmation && !valid) {
setError(validationErrorString || PasswordErrors.WeakPassword)
return return
} }
if (isConfirmation && passwordToConfirm !== password) { if (isConfirmation && passwordToConfirm !== password) {
...@@ -74,8 +59,9 @@ export function CloudBackupPasswordForm({ ...@@ -74,8 +59,9 @@ export function CloudBackupPasswordForm({
} }
const onPressNext = (): void => { const onPressNext = (): void => {
if (!isConfirmation && !isStrongPassword) { const { valid, validationErrorString } = validatePassword(password)
setError(PasswordErrors.WeakPassword) if (!isConfirmation && !valid) {
setError(validationErrorString || PasswordErrors.WeakPassword)
return return
} }
if (isConfirmation && passwordToConfirm !== password) { if (isConfirmation && passwordToConfirm !== password) {
...@@ -113,7 +99,6 @@ export function CloudBackupPasswordForm({ ...@@ -113,7 +99,6 @@ export function CloudBackupPasswordForm({
}} }}
onSubmitEditing={onPasswordSubmitEditing} onSubmitEditing={onPasswordSubmitEditing}
/> />
{!isConfirmation && <PasswordStrengthText strength={debouncedPasswordStrength} />}
{error ? <PasswordError errorText={errorText} /> : null} {error ? <PasswordError errorText={errorText} /> : null}
</Flex> </Flex>
{!isConfirmation && ( {!isConfirmation && (
...@@ -133,17 +118,3 @@ export function CloudBackupPasswordForm({ ...@@ -133,17 +118,3 @@ export function CloudBackupPasswordForm({
</> </>
) )
} }
function PasswordStrengthText({ strength }: { strength: PasswordStrength }): JSX.Element {
const { text, color } = getPasswordStrengthTextAndColor(strength)
const hasPassword = strength !== PasswordStrength.NONE
return (
<Flex centered row opacity={hasPassword ? 1 : 0} pt="$spacing12" px="$spacing8">
<Text color={color} variant="body3">
<Trans>This is a {text.toLowerCase()} password</Trans>
</Text>
</Flex>
)
}
import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { closeModal } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import { ExchangeTransferConnecting } from 'src/screens/ExchangeTransferConnecting'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { ModalName } from 'wallet/src/telemetry/constants'
export function ExchangeTransferModal(): JSX.Element | null {
const dispatch = useAppDispatch()
const onClose = (): void => {
dispatch(closeModal({ name: ModalName.ExchangeTransferModal }))
}
const { initialState } = useAppSelector(selectModalState(ModalName.ExchangeTransferModal))
const serviceProvider = initialState?.serviceProvider
return serviceProvider ? (
<BottomSheetModal
fullScreen
hideHandlebar
hideKeyboardOnDismiss
renderBehindTopInset
name={ModalName.ExchangeTransferModal}
onClose={onClose}>
<ExchangeTransferConnecting serviceProvider={serviceProvider} onClose={onClose} />
</BottomSheetModal>
) : null
}
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
export interface ExchangeTransferModalState {
serviceProvider: FORTransferInstitution
}
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
export const SERVICE_PROVIDER_ICON_SIZE = 90 export const SERVICE_PROVIDER_ICON_SIZE = 90
export const SERVICE_PROVIDER_ICON_BORDER_RADIUS = 20
export function FiatOnRampConnectingView({ export function FiatOnRampConnectingView({
amount, amount,
...@@ -19,7 +18,7 @@ export function FiatOnRampConnectingView({ ...@@ -19,7 +18,7 @@ export function FiatOnRampConnectingView({
serviceProviderName, serviceProviderName,
serviceProviderLogo, serviceProviderLogo,
}: { }: {
amount?: string amount: string
quoteCurrencyCode?: string quoteCurrencyCode?: string
serviceProviderName: string serviceProviderName: string
serviceProviderLogo?: JSX.Element serviceProviderLogo?: JSX.Element
...@@ -49,7 +48,7 @@ export function FiatOnRampConnectingView({ ...@@ -49,7 +48,7 @@ export function FiatOnRampConnectingView({
<Text variant="subheading1"> <Text variant="subheading1">
{t('Connecting you to {{serviceProvider}}', { serviceProvider: serviceProviderName })} {t('Connecting you to {{serviceProvider}}', { serviceProvider: serviceProviderName })}
</Text> </Text>
{quoteCurrencyCode && amount && ( {quoteCurrencyCode && (
<Text color="$neutral2" variant="body2"> <Text color="$neutral2" variant="body2">
{t('Buying {{amount}} worth of {{quoteCurrencyCode}}', { {t('Buying {{amount}} worth of {{quoteCurrencyCode}}', {
amount, amount,
...@@ -74,7 +73,7 @@ const styles = StyleSheet.create({ ...@@ -74,7 +73,7 @@ const styles = StyleSheet.create({
}, },
uniswapLogoWrapper: { uniswapLogoWrapper: {
backgroundColor: '#FFEFF8', // #FFD8EF with 40% opacity on a white background backgroundColor: '#FFEFF8', // #FFD8EF with 40% opacity on a white background
borderRadius: SERVICE_PROVIDER_ICON_BORDER_RADIUS, borderRadius: 20,
height: SERVICE_PROVIDER_ICON_SIZE, height: SERVICE_PROVIDER_ICON_SIZE,
width: SERVICE_PROVIDER_ICON_SIZE, width: SERVICE_PROVIDER_ICON_SIZE,
}, },
......
...@@ -5,14 +5,11 @@ import { useTranslation } from 'react-i18next' ...@@ -5,14 +5,11 @@ import { useTranslation } from 'react-i18next'
import { ListRenderItemInfo } from 'react-native' import { ListRenderItemInfo } from 'react-native'
import { getCountry } from 'react-native-localize' import { getCountry } from 'react-native-localize'
import { FadeIn, FadeOut } from 'react-native-reanimated' import { FadeIn, FadeOut } from 'react-native-reanimated'
import { useAppDispatch } from 'src/app/hooks'
import { openModal } from 'src/features/modals/modalSlice'
import { AnimatedFlex, Flex, Loader, Text, TouchableArea } from 'ui/src' import { AnimatedFlex, Flex, Loader, Text, TouchableArea } from 'ui/src'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { useFiatOnRampAggregatorTransferInstitutionsQuery } from 'wallet/src/features/fiatOnRamp/api' import { useFiatOnRampAggregatorTransferInstitutionsQuery } from 'wallet/src/features/fiatOnRamp/api'
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types' import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage' import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { ModalName } from 'wallet/src/telemetry/constants'
function key(item: FORTransferInstitution): string { function key(item: FORTransferInstitution): string {
return item.id as string return item.id as string
...@@ -64,24 +61,15 @@ function CEXItemWrapper({ ...@@ -64,24 +61,15 @@ function CEXItemWrapper({
) )
} }
export function TransferInstitutionSelector({ onClose }: { onClose: () => void }): JSX.Element { export function TransferInstitutionSelector(): JSX.Element {
const dispatch = useAppDispatch()
const { data, isLoading } = useFiatOnRampAggregatorTransferInstitutionsQuery({ const { data, isLoading } = useFiatOnRampAggregatorTransferInstitutionsQuery({
countryCode: getCountry(), countryCode: getCountry(),
}) })
const onSelectTransferInstitution = useCallback( // eslint-disable-next-line @typescript-eslint/no-unused-vars
(transferInstitution: FORTransferInstitution) => { const onSelectTransferInstitution = useCallback((transferInstitution: FORTransferInstitution) => {
dispatch( //TODO(MOB-2603): fetch widget and launch transfer flow
openModal({ }, [])
name: ModalName.ExchangeTransferModal,
initialState: { serviceProvider: transferInstitution },
})
)
onClose()
},
[dispatch, onClose]
)
const renderItem = useCallback( const renderItem = useCallback(
({ item: institution }: ListRenderItemInfo<FORTransferInstitution>) => ( ({ item: institution }: ListRenderItemInfo<FORTransferInstitution>) => (
......
...@@ -19,7 +19,6 @@ import { ...@@ -19,7 +19,6 @@ import {
isInvalidRequestAmountTooLow, isInvalidRequestAmountTooLow,
} from 'wallet/src/features/fiatOnRamp/utils' } from 'wallet/src/features/fiatOnRamp/utils'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { useActiveAccountAddress } from 'wallet/src/features/wallet/hooks'
// TODO: https://linear.app/uniswap/issue/MOB-2532/implement-fetching-of-available-fiat-currencies-from-meld // TODO: https://linear.app/uniswap/issue/MOB-2532/implement-fetching-of-available-fiat-currencies-from-meld
const MELD_FIAT_CURRENCY_CODES = ['usd', 'eur'] const MELD_FIAT_CURRENCY_CODES = ['usd', 'eur']
...@@ -61,7 +60,6 @@ export function useFiatOnRampQuotes({ ...@@ -61,7 +60,6 @@ export function useFiatOnRampQuotes({
quotes: FORQuote[] | undefined quotes: FORQuote[] | undefined
} { } {
const debouncedBaseCurrencyAmount = useDebounce(baseCurrencyAmount, Delay.Short) const debouncedBaseCurrencyAmount = useDebounce(baseCurrencyAmount, Delay.Short)
const walletAddress = useActiveAccountAddress()
const { const {
currentData: quotesResponse, currentData: quotesResponse,
...@@ -74,7 +72,6 @@ export function useFiatOnRampQuotes({ ...@@ -74,7 +72,6 @@ export function useFiatOnRampQuotes({
sourceCurrencyCode: baseCurrencyCode, sourceCurrencyCode: baseCurrencyCode,
destinationCurrencyCode: quoteCurrencyCode, destinationCurrencyCode: quoteCurrencyCode,
countryCode, countryCode,
walletAddress: walletAddress ?? '',
} }
: skipToken, : skipToken,
{ {
......
import { FORLogo, FORQuote, FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types' import { FORQuote, FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
export function getServiceProviderForQuote( export function getServiceProviderForQuote(
quote: FORQuote | undefined, quote: FORQuote | undefined,
...@@ -6,14 +6,3 @@ export function getServiceProviderForQuote( ...@@ -6,14 +6,3 @@ export function getServiceProviderForQuote(
): FORServiceProvider | undefined { ): FORServiceProvider | undefined {
return serviceProviders?.find((sp) => sp.serviceProvider === quote?.serviceProvider) return serviceProviders?.find((sp) => sp.serviceProvider === quote?.serviceProvider)
} }
export function getServiceProviderLogo(
logos: FORLogo | undefined,
isDarkMode: boolean
): string | undefined {
if (!logos) {
return
}
return isDarkMode ? logos.darkLogo : logos.lightLogo
}
...@@ -5,7 +5,7 @@ import { ...@@ -5,7 +5,7 @@ import {
TextInput as NativeTextInput, TextInput as NativeTextInput,
TextInputContentSizeChangeEventData, TextInputContentSizeChangeEventData,
} from 'react-native' } from 'react-native'
import { ColorTokens, Flex } from 'ui/src' import { ColorTokens, Flex, useSporeColors } from 'ui/src'
import { spacing } from 'ui/src/theme' import { spacing } from 'ui/src/theme'
import { TextInput } from 'wallet/src/components/input/TextInput' import { TextInput } from 'wallet/src/components/input/TextInput'
import { isAndroid } from 'wallet/src/utils/platform' import { isAndroid } from 'wallet/src/utils/platform'
...@@ -56,6 +56,7 @@ function Inputs({ ...@@ -56,6 +56,7 @@ function Inputs({
layerType, layerType,
...inputProps ...inputProps
}: Props & { layerType?: 'foreground' | 'background' }): JSX.Element { }: Props & { layerType?: 'foreground' | 'background' }): JSX.Element {
const colors = useSporeColors()
const [isMultiline, setIsMultiline] = useState(false) const [isMultiline, setIsMultiline] = useState(false)
const handleContentSizeChange = useCallback( const handleContentSizeChange = useCallback(
...@@ -119,7 +120,7 @@ function Inputs({ ...@@ -119,7 +120,7 @@ function Inputs({
py="$none" py="$none"
returnKeyType="done" returnKeyType="done"
scrollEnabled={false} scrollEnabled={false}
selectionColor="$neutral1" selectionColor={colors.neutral1.get()}
spellCheck={false} spellCheck={false}
testID="import_account_form/input" testID="import_account_form/input"
textAlign={isInputEmpty ? 'left' : backgroundTextAlignment} textAlign={isInputEmpty ? 'left' : backgroundTextAlignment}
......
import React from 'react'
import WalletPreviewCard from 'src/features/import/WalletPreviewCard'
import { render } from 'src/test/test-utils'
import { SAMPLE_SEED_ADDRESS_1 } from 'wallet/src/test/fixtures' import { SAMPLE_SEED_ADDRESS_1 } from 'wallet/src/test/fixtures'
import { render } from 'wallet/src/test/test-utils'
import WalletPreviewCard from './WalletPreviewCard'
it('renders wallet preview card', () => { it('renders wallet preview card', () => {
const tree = render( const tree = render(
......
import React from 'react'
import { Flex, Icons, Text, TouchableArea, Unicon, UniconV2 } from 'ui/src' import { Flex, Icons, Text, TouchableArea, Unicon, UniconV2 } from 'ui/src'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { NumberType } from 'utilities/src/format/types' import { NumberType } from 'utilities/src/format/types'
......
...@@ -158,17 +158,41 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = ` ...@@ -158,17 +158,41 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
} }
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onMouseEnter={[Function]} "right": 5,
onMouseLeave={[Function]} "top": 5,
onPress={[Function]} }
onPressIn={[Function]} }
onPressOut={[Function]} onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"backgroundColor": "#FFEFFF", "backgroundColor": "#FFEFFF",
...@@ -176,17 +200,12 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = ` ...@@ -176,17 +200,12 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
"borderBottomRightRadius": 12, "borderBottomRightRadius": 12,
"borderTopLeftRadius": 12, "borderTopLeftRadius": 12,
"borderTopRightRadius": 12, "borderTopRightRadius": 12,
"flexDirection": "column", "onPressIn": undefined,
"opacity": 1, "opacity": 1,
"paddingBottom": 8, "paddingBottom": 8,
"paddingLeft": 8, "paddingLeft": 8,
"paddingRight": 8, "paddingRight": 8,
"paddingTop": 8, "paddingTop": 8,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
......
...@@ -3,7 +3,6 @@ import { ScannerModalState } from 'src/components/QRCodeScanner/constants' ...@@ -3,7 +3,6 @@ import { ScannerModalState } from 'src/components/QRCodeScanner/constants'
import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState' import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState'
import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState' import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState'
import { Screens } from 'src/screens/Screens' import { Screens } from 'src/screens/Screens'
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { TransactionState } from 'wallet/src/features/transactions/transactionState/types'
import { ModalName } from 'wallet/src/telemetry/constants' import { ModalName } from 'wallet/src/telemetry/constants'
...@@ -14,9 +13,6 @@ export interface AppModalState<T> { ...@@ -14,9 +13,6 @@ export interface AppModalState<T> {
export interface ModalsState { export interface ModalsState {
[ModalName.AccountSwitcher]: AppModalState<undefined> [ModalName.AccountSwitcher]: AppModalState<undefined>
[ModalName.ExchangeTransferModal]: AppModalState<{
serviceProvider: FORTransferInstitution
}>
[ModalName.Experiments]: AppModalState<undefined> [ModalName.Experiments]: AppModalState<undefined>
[ModalName.Explore]: AppModalState<ExploreModalState> [ModalName.Explore]: AppModalState<ExploreModalState>
[ModalName.FiatCurrencySelector]: AppModalState<undefined> [ModalName.FiatCurrencySelector]: AppModalState<undefined>
......
...@@ -2,7 +2,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' ...@@ -2,7 +2,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { ExploreModalState } from 'src/app/modals/ExploreModalState' import { ExploreModalState } from 'src/app/modals/ExploreModalState'
import { ScannerModalState } from 'src/components/QRCodeScanner/constants' import { ScannerModalState } from 'src/components/QRCodeScanner/constants'
import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState' import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState'
import { ExchangeTransferModalState } from 'src/features/fiatOnRamp/ExchangeTransferModalState'
import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState' import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState'
import { Screens } from 'src/screens/Screens' import { Screens } from 'src/screens/Screens'
import { getKeys } from 'utilities/src/primitives/objects' import { getKeys } from 'utilities/src/primitives/objects'
...@@ -15,11 +14,6 @@ type AccountSwitcherModalParams = { ...@@ -15,11 +14,6 @@ type AccountSwitcherModalParams = {
initialState?: undefined initialState?: undefined
} }
type ExchangeTransferModalParams = {
name: typeof ModalName.ExchangeTransferModal
initialState?: ExchangeTransferModalState
}
type ExperimentsModalParams = { name: typeof ModalName.Experiments; initialState?: undefined } type ExperimentsModalParams = { name: typeof ModalName.Experiments; initialState?: undefined }
type ExploreModalParams = { type ExploreModalParams = {
...@@ -82,7 +76,6 @@ type ViewOnlyExplainerParams = { ...@@ -82,7 +76,6 @@ type ViewOnlyExplainerParams = {
export type OpenModalParams = export type OpenModalParams =
| AccountSwitcherModalParams | AccountSwitcherModalParams
| ExchangeTransferModalParams
| ExperimentsModalParams | ExperimentsModalParams
| ExploreModalParams | ExploreModalParams
| FiatCurrencySelectorParams | FiatCurrencySelectorParams
...@@ -102,10 +95,6 @@ export type OpenModalParams = ...@@ -102,10 +95,6 @@ export type OpenModalParams =
export type CloseModalParams = { name: keyof ModalsState } export type CloseModalParams = { name: keyof ModalsState }
export const initialModalState: ModalsState = { export const initialModalState: ModalsState = {
[ModalName.ExchangeTransferModal]: {
isOpen: false,
initialState: undefined,
},
[ModalName.FiatOnRamp]: { [ModalName.FiatOnRamp]: {
isOpen: false, isOpen: false,
initialState: undefined, initialState: undefined,
......
...@@ -2,27 +2,44 @@ ...@@ -2,27 +2,44 @@
exports[`renders collection preview card 1`] = ` exports[`renders collection preview card 1`] = `
<View <View
cancelable={true} accessibilityState={
disabled={false} {
"busy": undefined,
"checked": undefined,
"disabled": false,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onMouseEnter={[Function]} "right": 5,
onMouseLeave={[Function]} "top": 5,
onPress={[Function]} }
onPressIn={[Function]} }
onPressOut={[Function]} onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
......
import { useCallback } from 'react'
import { ScannerModalState } from 'src/components/QRCodeScanner/constants'
import { closeModal, openModal } from 'src/features/modals/modalSlice'
import { useFiatOnRampIpAddressQuery } from 'wallet/src/features/fiatOnRamp/api'
import { useAppDispatch } from 'wallet/src/state'
import { ModalName } from 'wallet/src/telemetry/constants'
export function useOnSendEmptyActionPress(): () => void {
const { data } = useFiatOnRampIpAddressQuery()
const dispatch = useAppDispatch()
const fiatOnRampEligible = Boolean(data?.isBuyAllowed)
return useCallback((): void => {
dispatch(closeModal({ name: ModalName.Send }))
if (fiatOnRampEligible) {
dispatch(openModal({ name: ModalName.FiatOnRamp }))
} else {
dispatch(
openModal({
name: ModalName.WalletConnectScan,
initialState: ScannerModalState.WalletQr,
})
)
}
}, [dispatch, fiatOnRampEligible])
}
...@@ -8,6 +8,7 @@ import { RecipientSelect } from 'src/components/RecipientSelect/RecipientSelect' ...@@ -8,6 +8,7 @@ import { RecipientSelect } from 'src/components/RecipientSelect/RecipientSelect'
import Trace from 'src/components/Trace/Trace' import Trace from 'src/components/Trace/Trace'
import { Screen } from 'src/components/layout/Screen' import { Screen } from 'src/components/layout/Screen'
import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks' import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks'
import { useOnSendEmptyActionPress } from 'src/features/transactions/hooks/useOnSendEmptyActionPress'
import { TransferHeader } from 'src/features/transactions/transfer/TransferHeader' import { TransferHeader } from 'src/features/transactions/transfer/TransferHeader'
import { TransferStatus } from 'src/features/transactions/transfer/TransferStatus' import { TransferStatus } from 'src/features/transactions/transfer/TransferStatus'
import { useWalletRestore } from 'src/features/wallet/hooks' import { useWalletRestore } from 'src/features/wallet/hooks'
...@@ -106,6 +107,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS ...@@ -106,6 +107,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS
dispatch, dispatch,
TokenSelectorFlow.Transfer TokenSelectorFlow.Transfer
) )
const onSendEmptyActionPress = useOnSendEmptyActionPress()
// optimization for not rendering InnerContent initially, // optimization for not rendering InnerContent initially,
// when modal is opened with recipient or token selector presented // when modal is opened with recipient or token selector presented
...@@ -209,6 +211,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS ...@@ -209,6 +211,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS
variation={TokenSelectorVariation.BalancesOnly} variation={TokenSelectorVariation.BalancesOnly}
onClose={onHideTokenSelector} onClose={onHideTokenSelector}
onSelectCurrency={onSelectCurrency} onSelectCurrency={onSelectCurrency}
onSendEmptyActionPress={onSendEmptyActionPress}
/> />
)} )}
</> </>
......
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { getCountry } from 'react-native-localize'
import { useAppDispatch } from 'src/app/hooks'
import {
FiatOnRampConnectingView,
SERVICE_PROVIDER_ICON_BORDER_RADIUS,
SERVICE_PROVIDER_ICON_SIZE,
} from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { useFiatOnRampTransactionCreator } from 'src/features/fiatOnRamp/hooks'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useTimeout } from 'utilities/src/time/timing'
import { useFiatOnRampAggregatorTransferWidgetQuery } from 'wallet/src/features/fiatOnRamp/api'
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types'
import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks'
import { openUri } from 'wallet/src/utils/linking'
// Design decision
const CONNECTING_TIMEOUT = 2 * ONE_SECOND_MS
const DEFAULT_TRANSFER_AMOUNT = 1
const DEFAULT_TRANSFER_CURRENCY = 'ETH'
export function ExchangeTransferConnecting({
serviceProvider,
onClose,
}: {
serviceProvider: FORTransferInstitution
onClose: () => void
}): JSX.Element {
const { t } = useTranslation()
const dispatch = useAppDispatch()
const activeAccountAddress = useActiveAccountAddressWithThrow()
const [timeoutElapsed, setTimeoutElapsed] = useState(false)
const { externalTransactionId, dispatchAddTransaction } =
useFiatOnRampTransactionCreator(activeAccountAddress)
const onError = useCallback((): void => {
dispatch(
pushNotification({
type: AppNotificationType.Error,
errorMessage: t('Something went wrong.'),
})
)
onClose()
}, [dispatch, onClose, t])
useTimeout(() => {
setTimeoutElapsed(true)
}, CONNECTING_TIMEOUT)
const {
data: widgetData,
isLoading: widgetLoading,
error: widgetError,
} = useFiatOnRampAggregatorTransferWidgetQuery({
sourceAmount: DEFAULT_TRANSFER_AMOUNT,
sourceCurrencyCode: DEFAULT_TRANSFER_CURRENCY,
countryCode: getCountry(),
institutionId: serviceProvider.id,
walletAddress: activeAccountAddress,
externalSessionId: externalTransactionId,
})
useEffect(() => {
if (widgetError) {
onError()
return
}
if (timeoutElapsed && !widgetLoading && widgetData) {
onClose()
openUri(widgetData.widgetUrl).catch(onError)
// TODO: Uncomment this when https://linear.app/uniswap/issue/MOB-2585/implement-polling-of-transaction-once-user-has-checked-out is implemented
// dispatchAddTransaction()
}
}, [
dispatchAddTransaction,
onClose,
onError,
timeoutElapsed,
widgetData,
widgetLoading,
widgetError,
])
return (
<FiatOnRampConnectingView
serviceProviderLogo={
<RemoteImage
borderRadius={SERVICE_PROVIDER_ICON_BORDER_RADIUS}
height={SERVICE_PROVIDER_ICON_SIZE}
uri={serviceProvider.icon}
width={SERVICE_PROVIDER_ICON_SIZE}
/>
}
serviceProviderName={serviceProvider.name}
/>
)
}
...@@ -50,7 +50,7 @@ export function ExploreScreen(): JSX.Element { ...@@ -50,7 +50,7 @@ export function ExploreScreen(): JSX.Element {
const [isSearchMode, setIsSearchMode] = useState<boolean>(false) const [isSearchMode, setIsSearchMode] = useState<boolean>(false)
const textInputRef = useRef<TextInput>(null) const textInputRef = useRef<TextInput>(null)
const onSearchChangeText = (newSearchFilter: string): void => { const onChangeSearchFilter = (newSearchFilter: string): void => {
setSearchQuery(newSearchFilter) setSearchQuery(newSearchFilter)
} }
...@@ -83,8 +83,9 @@ export function ExploreScreen(): JSX.Element { ...@@ -83,8 +83,9 @@ export function ExploreScreen(): JSX.Element {
backgroundColor={isSearchMode ? contrastBackgroundColor : searchBarBackgroundColor} backgroundColor={isSearchMode ? contrastBackgroundColor : searchBarBackgroundColor}
placeholder={t('Search tokens and wallets')} placeholder={t('Search tokens and wallets')}
showShadow={!isSearchMode} showShadow={!isSearchMode}
value={searchQuery}
onCancel={onSearchCancel} onCancel={onSearchCancel}
onChangeText={onSearchChangeText} onChangeText={onChangeSearchFilter}
onFocus={onSearchFocus} onFocus={onSearchFocus}
/> />
</Flex> </Flex>
......
...@@ -2,7 +2,6 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' ...@@ -2,7 +2,6 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { ComponentProps, useEffect, useRef, useState } from 'react' import React, { ComponentProps, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { TextInput, TextInputProps } from 'react-native' import { TextInput, TextInputProps } from 'react-native'
import FastImage from 'react-native-fast-image'
import { FadeIn, FadeOut, FadeOutDown } from 'react-native-reanimated' import { FadeIn, FadeOut, FadeOutDown } from 'react-native-reanimated'
import { useAppDispatch, useShouldShowNativeKeyboard } from 'src/app/hooks' import { useAppDispatch, useShouldShowNativeKeyboard } from 'src/app/hooks'
import { FiatOnRampStackParamList } from 'src/app/navigation/types' import { FiatOnRampStackParamList } from 'src/app/navigation/types'
...@@ -20,18 +19,17 @@ import { FiatOnRampCountryPicker } from 'src/features/fiatOnRamp/FiatOnRampCount ...@@ -20,18 +19,17 @@ import { FiatOnRampCountryPicker } from 'src/features/fiatOnRamp/FiatOnRampCount
import { FiatOnRampTokenSelectorModal } from 'src/features/fiatOnRamp/FiatOnRampTokenSelector' import { FiatOnRampTokenSelectorModal } from 'src/features/fiatOnRamp/FiatOnRampTokenSelector'
import { useFiatOnRampSupportedTokens } from 'src/features/fiatOnRamp/hooks' import { useFiatOnRampSupportedTokens } from 'src/features/fiatOnRamp/hooks'
import { FiatOnRampCurrency, InitialQuoteSelection } from 'src/features/fiatOnRamp/types' import { FiatOnRampCurrency, InitialQuoteSelection } from 'src/features/fiatOnRamp/types'
import { getServiceProviderLogo } from 'src/features/fiatOnRamp/utils'
import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { MobileEventName } from 'src/features/telemetry/constants' import { MobileEventName } from 'src/features/telemetry/constants'
import { MobileEventProperties } from 'src/features/telemetry/types' import { MobileEventProperties } from 'src/features/telemetry/types'
import { FiatOnRampScreens } from 'src/screens/Screens' import { FiatOnRampScreens } from 'src/screens/Screens'
import { AnimatedFlex, Flex, Text, useIsDarkMode } from 'ui/src' import { AnimatedFlex, Flex, Text } from 'ui/src'
import { usePrevious } from 'utilities/src/react/hooks' import { usePrevious } from 'utilities/src/react/hooks'
import { DecimalPadLegacy } from 'wallet/src/components/legacy/DecimalPadLegacy' import { DecimalPadLegacy } from 'wallet/src/components/legacy/DecimalPadLegacy'
import { useBottomSheetContext } from 'wallet/src/components/modals/BottomSheetContext' import { useBottomSheetContext } from 'wallet/src/components/modals/BottomSheetContext'
import { HandleBar } from 'wallet/src/components/modals/HandleBar' import { HandleBar } from 'wallet/src/components/modals/HandleBar'
import { useFiatOnRampAggregatorServiceProvidersQuery } from 'wallet/src/features/fiatOnRamp/api' import { useFiatOnRampAggregatorServiceProvidersQuery } from 'wallet/src/features/fiatOnRamp/api'
import { FORQuote, FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types' import { FORQuote } from 'wallet/src/features/fiatOnRamp/types'
import { pushNotification } from 'wallet/src/features/notifications/slice' import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types' import { AppNotificationType } from 'wallet/src/features/notifications/types'
...@@ -59,21 +57,9 @@ function selectInitialQuote( ...@@ -59,21 +57,9 @@ function selectInitialQuote(
return { quote: undefined, type: undefined } return { quote: undefined, type: undefined }
} }
function preloadServiceProviderLogos(
serviceProviders: FORServiceProvider[],
isDarkMode: boolean
): void {
FastImage.preload(
serviceProviders
.map((sp) => ({ uri: getServiceProviderLogo(sp.logos, isDarkMode) }))
.filter((sp) => !!sp.uri)
)
}
export function FiatOnRampScreen({ navigation }: Props): JSX.Element { export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const isDarkMode = useIsDarkMode()
const [selection, setSelection] = useState<TextInputProps['selection']>() const [selection, setSelection] = useState<TextInputProps['selection']>()
const [value, setValue] = useState('') const [value, setValue] = useState('')
const [showTokenSelector, setShowTokenSelector] = useState(false) const [showTokenSelector, setShowTokenSelector] = useState(false)
...@@ -123,17 +109,6 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { ...@@ -123,17 +109,6 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
error: serviceProvidersError, error: serviceProvidersError,
} = useFiatOnRampAggregatorServiceProvidersQuery() } = useFiatOnRampAggregatorServiceProvidersQuery()
// preload service provider logos for given quotes for the next screen
useEffect(() => {
if (serviceProvidersResponse?.serviceProviders && quotes) {
const quotesServiceProviderNames = quotes.map((q) => q.serviceProvider)
const serviceProviders = serviceProvidersResponse.serviceProviders.filter(
(sp) => quotesServiceProviderNames.indexOf(sp.serviceProvider) !== -1
)
preloadServiceProviderLogos(serviceProviders, isDarkMode)
}
}, [serviceProvidersResponse, quotes, isDarkMode])
const { errorText, errorColor } = useParseFiatOnRampError( const { errorText, errorColor } = useParseFiatOnRampError(
quotesError || serviceProvidersError, quotesError || serviceProvidersError,
meldSupportedFiatCurrency.code meldSupportedFiatCurrency.code
......
...@@ -6,10 +6,14 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' ...@@ -6,10 +6,14 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Freeze } from 'react-freeze' import { Freeze } from 'react-freeze'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { FlatList, StyleProp, View, ViewProps, ViewStyle } from 'react-native' import { FlatList, StyleProp, View, ViewProps, ViewStyle } from 'react-native'
import { TapGestureHandler, TapGestureHandlerGestureEvent } from 'react-native-gesture-handler'
import Animated, { import Animated, {
FadeIn, FadeIn,
FadeOut, FadeOut,
cancelAnimation,
interpolateColor, interpolateColor,
runOnJS,
useAnimatedGestureHandler,
useAnimatedRef, useAnimatedRef,
useAnimatedScrollHandler, useAnimatedScrollHandler,
useAnimatedStyle, useAnimatedStyle,
...@@ -25,6 +29,7 @@ import { ScannerModalState } from 'src/components/QRCodeScanner/constants' ...@@ -25,6 +29,7 @@ import { ScannerModalState } from 'src/components/QRCodeScanner/constants'
import Trace from 'src/components/Trace/Trace' import Trace from 'src/components/Trace/Trace'
import TraceTabView from 'src/components/Trace/TraceTabView' import TraceTabView from 'src/components/Trace/TraceTabView'
import { AccountHeader } from 'src/components/accounts/AccountHeader' import { AccountHeader } from 'src/components/accounts/AccountHeader'
import { pulseAnimation } from 'src/components/buttons/utils'
import { ACTIVITY_TAB_DATA_DEPENDENCIES, ActivityTab } from 'src/components/home/ActivityTab' import { ACTIVITY_TAB_DATA_DEPENDENCIES, ActivityTab } from 'src/components/home/ActivityTab'
import { FEED_TAB_DATA_DEPENDENCIES, FeedTab } from 'src/components/home/FeedTab' import { FEED_TAB_DATA_DEPENDENCIES, FeedTab } from 'src/components/home/FeedTab'
import { NFTS_TAB_DATA_DEPENDENCIES, NftsTab } from 'src/components/home/NftsTab' import { NFTS_TAB_DATA_DEPENDENCIES, NftsTab } from 'src/components/home/NftsTab'
...@@ -724,25 +729,44 @@ function ActionButton({ ...@@ -724,25 +729,44 @@ function ActionButton({
iconScale?: number iconScale?: number
}): JSX.Element { }): JSX.Element {
const colors = useSporeColors() const colors = useSporeColors()
const scale = useSharedValue(1)
const animatedStyle = useAnimatedStyle(
() => ({
transform: [{ scale: scale.value }],
}),
[scale]
)
const media = useMedia() const media = useMedia()
const iconSize = media.short ? iconSizes.icon24 : iconSizes.icon28 const iconSize = media.short ? iconSizes.icon24 : iconSizes.icon28
const onGestureEvent = useAnimatedGestureHandler<TapGestureHandlerGestureEvent>({
onStart: () => {
cancelAnimation(scale)
scale.value = pulseAnimation(activeScale)
},
onEnd: () => {
runOnJS(onPress)()
},
})
return ( return (
<Trace logPress element={name} pressEvent={eventName}> <Trace logPress element={name} pressEvent={eventName}>
<TouchableArea hapticFeedback flex={flex} scaleTo={activeScale} onPress={onPress}> <TouchableArea hapticFeedback flex={flex} onPress={onPress}>
<AnimatedFlex <TapGestureHandler onGestureEvent={onGestureEvent}>
centered <AnimatedFlex
fill centered
backgroundColor="$DEP_backgroundActionButton" fill
borderRadius="$rounded20" backgroundColor="$DEP_backgroundActionButton"
p="$spacing16"> borderRadius="$rounded20"
<Icon p="$spacing16"
color={colors.accent1.get()} style={animatedStyle}>
height={iconSize * iconScale} <Icon
strokeWidth={2} color={colors.accent1.get()}
width={iconSize * iconScale} height={iconSize * iconScale}
/> strokeWidth={2}
</AnimatedFlex> width={iconSize * iconScale}
/>
</AnimatedFlex>
</TapGestureHandler>
</TouchableArea> </TouchableArea>
</Trace> </Trace>
) )
......
...@@ -4,16 +4,17 @@ import React, { useCallback, useEffect, useState } from 'react' ...@@ -4,16 +4,17 @@ import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useAppDispatch } from 'src/app/hooks' import { useAppDispatch } from 'src/app/hooks'
import { OnboardingStackParamList } from 'src/app/navigation/types' import { OnboardingStackParamList } from 'src/app/navigation/types'
import { Loader } from 'src/components/loading'
import { clearCloudBackups } from 'src/features/CloudBackup/cloudBackupSlice'
import { useCloudBackups } from 'src/features/CloudBackup/hooks'
import { import {
startFetchingCloudStorageBackups, startFetchingCloudStorageBackups,
stopFetchingCloudStorageBackups, stopFetchingCloudStorageBackups,
} from 'src/features/CloudBackup/RNCloudStorageBackupsManager' } from 'src/features/CloudBackup/RNCloudStorageBackupsManager'
import { clearCloudBackups } from 'src/features/CloudBackup/cloudBackupSlice'
import { useCloudBackups } from 'src/features/CloudBackup/hooks'
import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen' import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { OnboardingScreens } from 'src/screens/Screens' import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton' import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Flex, Icons, Loader } from 'ui/src' import { Flex, Icons } from 'ui/src'
import { imageSizes } from 'ui/src/theme' import { imageSizes } from 'ui/src/theme'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
import { ONE_SECOND_MS } from 'utilities/src/time/time' import { ONE_SECOND_MS } from 'utilities/src/time/time'
......
...@@ -4,13 +4,14 @@ import { useTranslation } from 'react-i18next' ...@@ -4,13 +4,14 @@ import { useTranslation } from 'react-i18next'
import { ScrollView } from 'react-native-gesture-handler' import { ScrollView } from 'react-native-gesture-handler'
import { useAppDispatch } from 'src/app/hooks' import { useAppDispatch } from 'src/app/hooks'
import { OnboardingStackParamList } from 'src/app/navigation/types' import { OnboardingStackParamList } from 'src/app/navigation/types'
import { Loader } from 'src/components/loading'
import WalletPreviewCard from 'src/features/import/WalletPreviewCard'
import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen' import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { OnboardingScreens } from 'src/screens/Screens' import { OnboardingScreens } from 'src/screens/Screens'
import { Button, Flex, Loader } from 'ui/src' import { Button, Flex } from 'ui/src'
import { ONE_SECOND_MS } from 'utilities/src/time/time' import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useTimeout } from 'utilities/src/time/timing' import { useTimeout } from 'utilities/src/time/timing'
import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard'
import WalletPreviewCard from 'wallet/src/components/WalletPreviewCard/WalletPreviewCard'
import { useSelectWalletScreenQuery } from 'wallet/src/data/__generated__/types-and-hooks' import { useSelectWalletScreenQuery } from 'wallet/src/data/__generated__/types-and-hooks'
import { ImportType } from 'wallet/src/features/onboarding/types' import { ImportType } from 'wallet/src/features/onboarding/types'
import { import {
......
...@@ -227,30 +227,48 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` ...@@ -227,30 +227,48 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
} }
> >
<View <View
cancelable={true} accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true} focusable={true}
hitSlop={[Function]} hitSlop={
minPressDuration={0} {
onBlur={[Function]} "bottom": 5,
onFocus={[Function]} "left": 5,
onMouseEnter={[Function]} "right": 5,
onMouseLeave={[Function]} "top": 5,
onPress={[Function]} }
onPressIn={[Function]} }
onPressOut={[Function]} onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={ style={
{ {
"flexDirection": "column",
"opacity": 1, "opacity": 1,
"paddingBottom": 4, "paddingBottom": 4,
"paddingLeft": 4, "paddingLeft": 4,
"paddingRight": 4, "paddingRight": 4,
"paddingTop": 4, "paddingTop": 4,
"transform": [
{
"scale": 1,
},
],
} }
} }
> >
......
...@@ -19,7 +19,7 @@ const ACCOUNT_IMAGE_SIZE = 52 ...@@ -19,7 +19,7 @@ const ACCOUNT_IMAGE_SIZE = 52
const ICON_SIZE = 32 const ICON_SIZE = 32
const ICON_BORDER_RADIUS = 100 const ICON_BORDER_RADIUS = 100
function AccountCardItem({ onClose }: { onClose: () => void }): JSX.Element { function AccountCardItem(): JSX.Element {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const activeAccountAddress = useActiveAccountAddressWithThrow() const activeAccountAddress = useActiveAccountAddressWithThrow()
...@@ -35,7 +35,7 @@ function AccountCardItem({ onClose }: { onClose: () => void }): JSX.Element { ...@@ -35,7 +35,7 @@ function AccountCardItem({ onClose }: { onClose: () => void }): JSX.Element {
} }
const onPressShowWalletQr = (): void => { const onPressShowWalletQr = (): void => {
onClose() dispatch(closeModal({ name: ModalName.ReceiveCryptoModal }))
dispatch( dispatch(
openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })
) )
...@@ -122,7 +122,7 @@ export function ReceiveCryptoModal(): JSX.Element { ...@@ -122,7 +122,7 @@ export function ReceiveCryptoModal(): JSX.Element {
{t('Deposit funds from another wallet or an account')} {t('Deposit funds from another wallet or an account')}
</Text> </Text>
</Flex> </Flex>
<AccountCardItem onClose={onClose} /> <AccountCardItem />
<Flex centered row shrink gap="$spacing12" py="$spacing8"> <Flex centered row shrink gap="$spacing12" py="$spacing8">
<Separator /> <Separator />
<Text color="$neutral2" textAlign="center" variant="body3"> <Text color="$neutral2" textAlign="center" variant="body3">
...@@ -130,7 +130,7 @@ export function ReceiveCryptoModal(): JSX.Element { ...@@ -130,7 +130,7 @@ export function ReceiveCryptoModal(): JSX.Element {
</Text> </Text>
<Separator /> <Separator />
</Flex> </Flex>
<TransferInstitutionSelector onClose={onClose} /> <TransferInstitutionSelector />
</Flex> </Flex>
</BottomSheetModal> </BottomSheetModal>
) )
......
...@@ -43,7 +43,6 @@ ignores: [ ...@@ -43,7 +43,6 @@ ignores: [
## Internal packages / workspaces ## Internal packages / workspaces
"wallet", "wallet",
"utilities", "utilities",
"ui",
## Top level local file paths ## Top level local file paths
"abis", "abis",
"analytics", "analytics",
......
...@@ -16,5 +16,4 @@ REACT_APP_TEMP_API_URL="https://temp.gateway.uniswap.org/v1" ...@@ -16,5 +16,4 @@ REACT_APP_TEMP_API_URL="https://temp.gateway.uniswap.org/v1"
REACT_APP_UNISWAP_API_URL="https://interface.gateway.uniswap.org/v2" REACT_APP_UNISWAP_API_URL="https://interface.gateway.uniswap.org/v2"
REACT_APP_UNISWAP_BASE_API_URL="https://interface.gateway.uniswap.org/" REACT_APP_UNISWAP_BASE_API_URL="https://interface.gateway.uniswap.org/"
REACT_APP_UNISWAP_GATEWAY_DNS="https://interface.gateway.uniswap.org/v2" REACT_APP_UNISWAP_GATEWAY_DNS="https://interface.gateway.uniswap.org/v2"
REACT_APP_UNITAGS_API_URL="https://gateway.uniswap.org/v2/unitags"
REACT_APP_WALLET_CONNECT_PROJECT_ID="c6c9bacd35afa3eb9e6cccf6d8464395" REACT_APP_WALLET_CONNECT_PROJECT_ID="c6c9bacd35afa3eb9e6cccf6d8464395"
...@@ -60,7 +60,6 @@ module.exports = { ...@@ -60,7 +60,6 @@ module.exports = {
transformIgnorePatterns: ['d3-array'], transformIgnorePatterns: ['d3-array'],
moduleNameMapper: { moduleNameMapper: {
'd3-array': 'd3-array/dist/d3-array.min.js', 'd3-array': 'd3-array/dist/d3-array.min.js',
'^react-native$': 'react-native-web',
}, },
}) })
}, },
......
...@@ -3,107 +3,13 @@ import { getTestSelector } from '../../utils' ...@@ -3,107 +3,13 @@ import { getTestSelector } from '../../utils'
describe('Uni tags support', () => { describe('Uni tags support', () => {
beforeEach(() => { beforeEach(() => {
const unitagSpy = cy.spy().as('unitagSpy')
cy.intercept(/gateway.uniswap.org\/v2\/address/, (req) => {
unitagSpy(req)
})
cy.visit('/swap', { cy.visit('/swap', {
featureFlags: [{ name: FeatureFlag.uniTags, value: true }], featureFlags: [{ name: FeatureFlag.uniTags, value: true }],
}) })
}) })
it('displays banner in account drawer', () => { it('displays claim banner in account drawer', () => {
cy.get(getTestSelector('web3-status-connected')).click()
cy.contains('Introducing uni.eth usernames')
})
it('displays large banner on page', () => {
cy.get(getTestSelector('large-unitag-banner')).should('be.visible')
})
it('does not display banner on landing page', () => {
cy.visit('/?intro=true', {
featureFlags: [{ name: FeatureFlag.uniTags, value: true }],
})
cy.get(getTestSelector('large-unitag-banner')).should('not.be.visible')
})
it('opens modal and hides itself when accept button is clicked', () => {
cy.get(getTestSelector('large-unitag-banner')).within(() => {
cy.get(getTestSelector('unitag-banner-accept-button')).click()
})
cy.contains('Download the Uniswap app').should('exist')
cy.get(getTestSelector('get-the-app-close-button')).click()
cy.get(getTestSelector('large-unitag-banner')).should('not.be.visible')
cy.get(getTestSelector('web3-status-connected')).click() cy.get(getTestSelector('web3-status-connected')).click()
cy.get('Introducing uni.eth usernames').should('not.exist') cy.contains('Claim your Uniswap username')
})
it('hides itself when reject button is clicked', () => {
cy.get(getTestSelector('large-unitag-banner')).within(() => {
cy.get(getTestSelector('unitag-banner-reject-button')).click()
})
cy.get(getTestSelector('large-unitag-banner')).should('not.be.visible')
cy.get(getTestSelector('web3-status-connected')).click()
cy.get('Introducing uni.eth usernames').should('not.exist')
})
it('shows address if no Unitag or ENS exists', () => {
cy.hardhat().then(() => {
const unusedAccount = '0xF030EaA01aFf57A23483dC8A1c3550d153be69Fb'
cy.get(getTestSelector('web3-status-connected')).click()
cy.window().then((win) => win.ethereum.emit('accountsChanged', [unusedAccount]))
cy.get(getTestSelector('account-drawer-status')).within(() => {
cy.contains('0xF030...69Fb').should('be.visible')
})
})
})
it('shows Unitag, followed by address, if Unitag exists but not ENS', () => {
cy.intercept(/address/, { fixture: 'mini-portfolio/unitag.json' })
cy.hardhat().then(() => {
const accountWithUnitag = '0xF030EaA01aFf57A23483dC8A1c3550d153be69Fb'
cy.get(getTestSelector('web3-status-connected')).click()
cy.window().then((win) => win.ethereum.emit('accountsChanged', [accountWithUnitag]))
cy.get(getTestSelector('account-drawer-status')).within(() => {
cy.contains('hayden').should('be.visible')
cy.contains('0xF030...69Fb').should('be.visible')
})
})
})
it('shows ENS, followed by address, if ENS exists but not Unitag', () => {
cy.hardhat().then(() => {
const haydenAccount = '0x50EC05ADe8280758E2077fcBC08D878D4aef79C3'
const haydenENS = 'hayden.eth'
cy.get(getTestSelector('web3-status-connected')).click()
cy.window().then((win) => win.ethereum.emit('accountsChanged', [haydenAccount]))
cy.get(getTestSelector('account-drawer-status')).within(() => {
cy.contains(haydenENS).should('be.visible')
cy.contains('0x50EC...79C3').should('be.visible')
})
})
})
it('shows Unitag and more option if user has both Unitag and ENS', () => {
cy.intercept(/address/, { fixture: 'mini-portfolio/unitag.json' })
cy.hardhat().then(() => {
const haydenAccount = '0x50EC05ADe8280758E2077fcBC08D878D4aef79C3'
const haydenUnitag = 'hayden'
const haydenENS = 'hayden.eth'
cy.get(getTestSelector('web3-status-connected')).click()
cy.window().then((win) => win.ethereum.emit('accountsChanged', [haydenAccount]))
cy.get(getTestSelector('account-drawer-status')).within(() => {
cy.contains(haydenUnitag).should('be.visible')
cy.contains('0x50EC...79C3').should('be.visible')
})
cy.get(getTestSelector('secondary-identifiers'))
.trigger('mouseover')
.click()
.within(() => {
cy.contains(haydenENS).should('be.visible')
cy.contains('0x50EC...79C3').should('be.visible')
})
})
}) })
}) })
...@@ -199,7 +199,6 @@ describe('Permit2', () => { ...@@ -199,7 +199,6 @@ describe('Permit2', () => {
cy.contains('Approve and swap').click() cy.contains('Approve and swap').click()
// Verify token approval // Verify token approval
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine()) cy.hardhat().then((hardhat) => hardhat.mine())
expectTokenAllowanceForPermit2ToBeMax(DAI) expectTokenAllowanceForPermit2ToBeMax(DAI)
...@@ -232,6 +231,7 @@ describe('Permit2', () => { ...@@ -232,6 +231,7 @@ describe('Permit2', () => {
}) })
it('prompts signature when existing permit approval is expired', () => { it('prompts signature when existing permit approval is expired', () => {
setupInputs(DAI, USDC_MAINNET)
const expiredAllowance = { expiration: Math.floor((Date.now() - 1) / 1000) } const expiredAllowance = { expiration: Math.floor((Date.now() - 1) / 1000) }
cy.hardhat() cy.hardhat()
.then(({ approval, wallet }) => .then(({ approval, wallet }) =>
...@@ -241,8 +241,7 @@ describe('Permit2', () => { ...@@ -241,8 +241,7 @@ describe('Permit2', () => {
]) ])
) )
.then(() => { .then(() => {
setupInputs(DAI, USDC_MAINNET) initiateSwap('Approve and swap')
initiateSwap('Sign and swap')
}) })
// Verify permit2 approval // Verify permit2 approval
...@@ -252,6 +251,7 @@ describe('Permit2', () => { ...@@ -252,6 +251,7 @@ describe('Permit2', () => {
}) })
it('prompts signature when existing permit approval amount is too low', () => { it('prompts signature when existing permit approval amount is too low', () => {
setupInputs(DAI, USDC_MAINNET)
const smallAllowance = { amount: 1 } const smallAllowance = { amount: 1 }
cy.hardhat() cy.hardhat()
.then(({ approval, wallet }) => .then(({ approval, wallet }) =>
...@@ -261,8 +261,7 @@ describe('Permit2', () => { ...@@ -261,8 +261,7 @@ describe('Permit2', () => {
]) ])
) )
.then(() => { .then(() => {
setupInputs(DAI, USDC_MAINNET) initiateSwap('Approve and swap')
initiateSwap('Sign and swap')
}) })
// Verify permit2 approval // Verify permit2 approval
......
...@@ -53,7 +53,6 @@ describe('Swap', () => { ...@@ -53,7 +53,6 @@ describe('Swap', () => {
}) })
it('swaps ETH for USDC', () => { it('swaps ETH for USDC', () => {
cy.interceptGraphqlOperation('Activity', 'mini-portfolio/empty_activity.json')
cy.visit('/swap') cy.visit('/swap')
cy.hardhat({ automine: false }) cy.hardhat({ automine: false })
getBalance(USDC_MAINNET).then((initialBalance) => { getBalance(USDC_MAINNET).then((initialBalance) => {
......
...@@ -163,7 +163,7 @@ describe('Wallet Dropdown', () => { ...@@ -163,7 +163,7 @@ describe('Wallet Dropdown', () => {
it('should dismiss the wallet bottom sheet when clicking buy crypto', () => { it('should dismiss the wallet bottom sheet when clicking buy crypto', () => {
cy.get(getTestSelector('web3-status-connected')).click() cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-buy-crypto')).click() cy.get(getTestSelector('wallet-buy-crypto')).click()
cy.get(getTestSelector('wallet-settings')).should('not.be.visible') cy.contains('Buy crypto').should('not.be.visible')
}) })
it('should use a bottom sheet and dismiss when on a mobile screen size', () => { it('should use a bottom sheet and dismiss when on a mobile screen size', () => {
......
{
"username": "hayden"
}
\ No newline at end of file
...@@ -21,12 +21,6 @@ declare global { ...@@ -21,12 +21,6 @@ declare global {
* @returns {Chainable<Subject>} * @returns {Chainable<Subject>}
*/ */
waitForAmplitudeEvent(eventName: string, requiredProperties?: string[]): Chainable<Subject> waitForAmplitudeEvent(eventName: string, requiredProperties?: string[]): Chainable<Subject>
/**
* Intercepts a specific graphql operation and responds with the given fixture.
* @param {string} operationName - The name of the graphql operation to intercept.
* @param {string} fixturePath - The path to the fixture to respond with.
*/
interceptGraphqlOperation(operationName: string, fixturePath: string): Chainable<Subject>
} }
interface VisitOptions { interface VisitOptions {
serviceWorker?: true serviceWorker?: true
...@@ -102,13 +96,3 @@ Cypress.Commands.add('waitForAmplitudeEvent', (eventName, requiredProperties) => ...@@ -102,13 +96,3 @@ Cypress.Commands.add('waitForAmplitudeEvent', (eventName, requiredProperties) =>
} }
return findAndDiscardEventsUpToTarget() return findAndDiscardEventsUpToTarget()
}) })
Cypress.Commands.add('interceptGraphqlOperation', (operationName, fixturePath) => {
return cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === operationName) {
req.reply({ fixture: fixturePath })
} else {
req.continue()
}
})
})
...@@ -14,7 +14,7 @@ const forks = { ...@@ -14,7 +14,7 @@ const forks = {
[ChainId.MAINNET]: { [ChainId.MAINNET]: {
url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`, url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
// Temporarily hardcoding this to fix e2e tests as we investigate source of swap tests failing on older blocknumbers // Temporarily hardcoding this to fix e2e tests as we investigate source of swap tests failing on older blocknumbers
blockNumber: 19270708, blockNumber: 19164140,
...forkingConfig, ...forkingConfig,
}, },
[ChainId.POLYGON]: { [ChainId.POLYGON]: {
......
...@@ -198,12 +198,12 @@ ...@@ -198,12 +198,12 @@
"@uniswap/merkle-distributor": "1.0.1", "@uniswap/merkle-distributor": "1.0.1",
"@uniswap/permit2-sdk": "1.2.0", "@uniswap/permit2-sdk": "1.2.0",
"@uniswap/redux-multicall": "1.1.8", "@uniswap/redux-multicall": "1.1.8",
"@uniswap/router-sdk": "1.8.0", "@uniswap/router-sdk": "1.7.1",
"@uniswap/sdk-core": "4.1.2", "@uniswap/sdk-core": "4.0.7",
"@uniswap/smart-order-router": "3.17.3", "@uniswap/smart-order-router": "3.17.3",
"@uniswap/token-lists": "1.0.0-beta.33", "@uniswap/token-lists": "1.0.0-beta.33",
"@uniswap/uniswapx-sdk": "1.4.1", "@uniswap/uniswapx-sdk": "1.4.1",
"@uniswap/universal-router-sdk": "1.7.1", "@uniswap/universal-router-sdk": "1.5.8",
"@uniswap/v2-core": "1.0.1", "@uniswap/v2-core": "1.0.1",
"@uniswap/v2-periphery": "1.1.0-beta.0", "@uniswap/v2-periphery": "1.1.0-beta.0",
"@uniswap/v2-sdk": "4.1.0", "@uniswap/v2-sdk": "4.1.0",
......
import { ButtonEmphasis, ButtonSize, LoadingButtonSpinner, ThemeButton } from 'components/Button'
import Column from 'components/Column'
import Row from 'components/Row'
import Tooltip from 'components/Tooltip'
import { SupportArticleURL } from 'constants/supportArticles'
import { ReactNode, useReducer } from 'react'
import { Info } from 'react-feather'
import { Text } from 'rebass'
import styled from 'styled-components'
import { ExternalLink } from 'theme/components'
import { ThemedText } from 'theme/components/text'
const Container = styled(Column)`
position: relative;
height: 100%;
width: 100%;
`
const Tile = styled(ThemeButton)`
height: 100%;
width: 100%;
display: flex;
justify-content: flex-start;
padding: 12px;
border-color: transparent;
border-radius: 16px;
border-style: solid;
border-width: 1px;
`
const StyledLoadingButtonSpinner = styled(LoadingButtonSpinner)`
height: 28px;
width: 28px;
fill: ${({ theme }) => theme.accent1};
`
const ActionName = styled(Text)`
font-size: 16px;
font-style: normal;
font-weight: 535;
line-height: 24px;
`
const ErrorContainer = styled(Row)`
width: 100%;
position: absolute;
bottom: -24px;
display: flex;
justify-content: center;
align-items: center;
`
const ErrorText = styled(ThemedText.LabelMicro)`
color: ${({ theme }) => theme.neutral2};
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`
const ErrorLink = styled(ExternalLink)`
align-items: center;
display: flex;
height: 14px;
justify-content: center;
margin-left: 6px;
width: 14px;
`
const StyledInfoIcon = styled(Info)`
height: 12px;
width: 12px;
flex: 1 1 auto;
stroke: ${({ theme }) => theme.neutral2};
`
export function ActionTile({
dataTestId,
Icon,
name,
onClick,
loading,
disabled,
error,
errorMessage,
errorTooltip,
}: {
dataTestId: string
Icon: ReactNode
name: string
onClick: () => void
loading?: boolean
disabled?: boolean
error?: boolean
errorMessage?: string
errorTooltip?: string
}) {
const [showTooltip, toggleTooltip] = useReducer((isOpen) => !isOpen, false)
return (
<Container>
<Tile
data-testid={dataTestId}
size={ButtonSize.medium}
emphasis={ButtonEmphasis.highSoft}
onClick={onClick}
disabled={disabled}
>
<Column gap="12px">
{loading ? <StyledLoadingButtonSpinner /> : Icon}
<ActionName>{name}</ActionName>
</Column>
</Tile>
{error && (
<ErrorContainer>
<ErrorText>{errorMessage}</ErrorText>
<Tooltip show={showTooltip} text={errorTooltip}>
<ErrorLink
onMouseEnter={toggleTooltip}
onMouseLeave={toggleTooltip}
style={{ color: 'inherit' }}
href={SupportArticleURL.MOONPAY_REGIONAL_AVAILABILITY}
>
<StyledInfoIcon />
</ErrorLink>
</Tooltip>
</ErrorContainer>
)}
</Container>
)
}
...@@ -10,7 +10,7 @@ import { useCallback } from 'react' ...@@ -10,7 +10,7 @@ import { useCallback } from 'react'
import { SignatureType } from 'state/signatures/types' import { SignatureType } from 'state/signatures/types'
import styled from 'styled-components' import styled from 'styled-components'
import { EllipsisStyle, ThemedText } from 'theme/components' import { EllipsisStyle, ThemedText } from 'theme/components'
import { shortenAddress } from 'utilities/src/addresses' import { shortenAddress } from 'utils'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink' import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
import { PortfolioLogo } from '../PortfolioLogo' import { PortfolioLogo } from '../PortfolioLogo'
......
...@@ -4,7 +4,7 @@ import { DetailLineItem, LineItemData } from 'components/swap/DetailLineItem' ...@@ -4,7 +4,7 @@ import { DetailLineItem, LineItemData } from 'components/swap/DetailLineItem'
import TradePrice from 'components/swap/TradePrice' import TradePrice from 'components/swap/TradePrice'
import { UniswapXOrderDetails } from 'state/signatures/types' import { UniswapXOrderDetails } from 'state/signatures/types'
import { ExternalLink } from 'theme/components' import { ExternalLink } from 'theme/components'
import { ellipseMiddle } from 'utilities/src/addresses' import { ellipseMiddle } from 'utils/addresses'
import { NumberType, useFormatter } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
import { formatTimestamp } from '../formatTimestamp' import { formatTimestamp } from '../formatTimestamp'
......
...@@ -446,15 +446,7 @@ exports[`CancelLimitsDialog should render correctly 1`] = ` ...@@ -446,15 +446,7 @@ exports[`CancelLimitsDialog should render correctly 1`] = `
<body> <body>
<div <div
aria-hidden="true" aria-hidden="true"
> />
<span
class="t_light is_inversed _dsp_contents"
>
<span
class=" t_light _dsp_contents is_Theme"
/>
</span>
</div>
<reach-portal> <reach-portal>
<div <div
data-focus-guard="true" data-focus-guard="true"
......
...@@ -82,39 +82,31 @@ exports[`OffchainOrderLineItem should render type EXCHANGE_RATE 1`] = ` ...@@ -82,39 +82,31 @@ exports[`OffchainOrderLineItem should render type EXCHANGE_RATE 1`] = `
user-select: text; user-select: text;
} }
<span <div
class="t_light is_inversed _dsp_contents" class="c0 c1 c2"
> >
<span <div
class=" t_light _dsp_contents is_Theme" class="c3 c4 css-142zc9n"
data-testid="swap-li-label"
> >
<div Rate
class="c0 c1 c2" </div>
<div
class="c3 c5 css-142zc9n"
>
<button
class="c6"
title="1 USDC = <0.00001 DAI "
> >
<div <div
class="c3 c4 css-142zc9n" class="c3 css-142zc9n"
data-testid="swap-li-label"
> >
Rate 1 USDC = &lt;0.00001 DAI
</div> </div>
<div
class="c3 c5 css-142zc9n" </button>
> </div>
<button </div>
class="c6"
title="1 USDC = <0.00001 DAI "
>
<div
class="c3 css-142zc9n"
>
1 USDC = &lt;0.00001 DAI
</div>
</button>
</div>
</div>
</span>
</span>
</DocumentFragment> </DocumentFragment>
`; `;
...@@ -168,31 +160,23 @@ exports[`OffchainOrderLineItem should render type NETWORK_COST 1`] = ` ...@@ -168,31 +160,23 @@ exports[`OffchainOrderLineItem should render type NETWORK_COST 1`] = `
overflow-wrap: break-word; overflow-wrap: break-word;
} }
<span <div
class=" _dsp_contents" class="c0 c1 c2"
> >
<span <div
class=" t_light _dsp_contents is_Theme" class="c3 c4 css-142zc9n"
data-testid="swap-li-label"
> >
<div Network cost
class="c0 c1 c2" </div>
> <div
<div class="c3 c5 css-142zc9n"
class="c3 c4 css-142zc9n" >
data-testid="swap-li-label" <span>
> $0
Network cost </span>
</div> </div>
<div </div>
class="c3 c5 css-142zc9n"
>
<span>
$0
</span>
</div>
</div>
</span>
</span>
</DocumentFragment> </DocumentFragment>
`; `;
...@@ -265,35 +249,27 @@ exports[`OffchainOrderLineItem should render type TRANSACTION_ID 1`] = ` ...@@ -265,35 +249,27 @@ exports[`OffchainOrderLineItem should render type TRANSACTION_ID 1`] = `
overflow-wrap: break-word; overflow-wrap: break-word;
} }
<span <div
class=" _dsp_contents" class="c0 c1 c2"
> >
<span <div
class=" t_light _dsp_contents is_Theme" class="c3 c4 css-142zc9n"
data-testid="swap-li-label"
>
Transaction ID
</div>
<div
class="c3 c5 css-142zc9n"
> >
<div <a
class="c0 c1 c2" class="c6"
href="https://etherscan.io/tx/0x123"
rel="noopener noreferrer"
target="_blank"
> >
<div 0x12...x123
class="c3 c4 css-142zc9n" </a>
data-testid="swap-li-label" </div>
> </div>
Transaction ID
</div>
<div
class="c3 c5 css-142zc9n"
>
<a
class="c6"
href="https://etherscan.io/tx/0x123"
rel="noopener noreferrer"
target="_blank"
>
0x12...x123
</a>
</div>
</div>
</span>
</span>
</DocumentFragment> </DocumentFragment>
`; `;
...@@ -24,7 +24,7 @@ import { ...@@ -24,7 +24,7 @@ import {
TransactionType, TransactionType,
WrapTransactionInfo, WrapTransactionInfo,
} from 'state/transactions/types' } from 'state/transactions/types'
import { isAddress } from 'utilities/src/addresses' import { isAddress } from 'utils'
import { NumberType, useFormatter } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
import { CancelledTransactionTitleTable, getActivityTitle, LimitOrderTextTable, OrderTextTable } from '../constants' import { CancelledTransactionTitleTable, getActivityTitle, LimitOrderTextTable, OrderTextTable } from '../constants'
......
...@@ -28,7 +28,8 @@ import store from 'state' ...@@ -28,7 +28,8 @@ import store from 'state'
import { addSignature } from 'state/signatures/reducer' import { addSignature } from 'state/signatures/reducer'
import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types' import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types'
import { TransactionType as LocalTransactionType } from 'state/transactions/types' import { TransactionType as LocalTransactionType } from 'state/transactions/types'
import { isAddress, isSameAddress } from 'utilities/src/addresses' import { isAddress } from 'utils'
import { isSameAddress } from 'utils/addresses'
import { currencyId } from 'utils/currencyId' import { currencyId } from 'utils/currencyId'
import { NumberType, useFormatter } from 'utils/formatNumbers' import { NumberType, useFormatter } from 'utils/formatNumbers'
......
...@@ -70,7 +70,7 @@ describe('LimitDetailActivityRow', () => { ...@@ -70,7 +70,7 @@ describe('LimitDetailActivityRow', () => {
selected={false} selected={false}
/> />
) )
expect(container.firstChild?.firstChild?.firstChild).toBeNull() expect(container.firstChild).toBeNull()
}) })
it('should not render with invalid amounts', () => { it('should not render with invalid amounts', () => {
...@@ -81,7 +81,7 @@ describe('LimitDetailActivityRow', () => { ...@@ -81,7 +81,7 @@ describe('LimitDetailActivityRow', () => {
order={{ ...mockOrder, offchainOrderDetails: { ...mockOrderDetails, swapInfo: undefined as any } }} order={{ ...mockOrder, offchainOrderDetails: { ...mockOrderDetails, swapInfo: undefined as any } }}
/> />
) )
expect(container.firstChild?.firstChild?.firstChild).toBeNull() expect(container.firstChild).toBeNull()
}) })
it('should render with valid details', () => { it('should render with valid details', () => {
......
...@@ -22,7 +22,7 @@ describe('OpenLimitOrdersButton', () => { ...@@ -22,7 +22,7 @@ describe('OpenLimitOrdersButton', () => {
it('should not render if there are no open limit orders', () => { it('should not render if there are no open limit orders', () => {
mocked(useOpenLimitOrders).mockReturnValue({ openLimitOrders: [], loading: false, refetch: jest.fn() }) mocked(useOpenLimitOrders).mockReturnValue({ openLimitOrders: [], loading: false, refetch: jest.fn() })
const { container } = render(<OpenLimitOrdersButton account="0x123" openLimitsMenu={jest.fn()} />) const { container } = render(<OpenLimitOrdersButton account="0x123" openLimitsMenu={jest.fn()} />)
expect(container.firstChild?.firstChild?.firstChild).toBeNull() expect(container.firstChild).toBeNull()
}) })
it('should render if there are open limit orders', () => { it('should render if there are open limit orders', () => {
mocked(useOpenLimitOrders).mockReturnValue({ mocked(useOpenLimitOrders).mockReturnValue({
...@@ -43,7 +43,7 @@ describe('OpenLimitOrdersButton', () => { ...@@ -43,7 +43,7 @@ describe('OpenLimitOrdersButton', () => {
const clickCallback = jest.fn() const clickCallback = jest.fn()
const { container } = render(<OpenLimitOrdersButton account="0x123" openLimitsMenu={clickCallback} />) const { container } = render(<OpenLimitOrdersButton account="0x123" openLimitsMenu={clickCallback} />)
act(() => { act(() => {
fireEvent.click(container.firstChild?.firstChild?.firstChild as HTMLElement) fireEvent.click(container.firstChild as HTMLElement)
}) })
expect(clickCallback).toHaveBeenCalled() expect(clickCallback).toHaveBeenCalled()
}) })
......
import { ChainId, Token } from '@uniswap/sdk-core' import { ChainId, Token } from '@uniswap/sdk-core'
import { DEFAULT_ERC20_DECIMALS } from 'constants/tokens'
import { Interface } from 'ethers/lib/utils' import { Interface } from 'ethers/lib/utils'
import { isAddress } from 'utilities/src/addresses' import { isAddress } from 'utils'
import { DEFAULT_ERC20_DECIMALS } from 'utilities/src/tokens/constants'
import { arrayToSlices } from 'utils/arrays' import { arrayToSlices } from 'utils/arrays'
import { CurrencyKey, buildCurrencyKey, currencyKey } from 'utils/currencyKey' import { CurrencyKey, buildCurrencyKey, currencyKey } from 'utils/currencyKey'
import ERC20_ABI from 'wallet/src/abis/erc20.json' import ERC20_ABI from 'wallet/src/abis/erc20.json'
......
...@@ -16,7 +16,7 @@ import { ContractInput, useUniswapPricesQuery } from 'graphql/data/__generated__ ...@@ -16,7 +16,7 @@ import { ContractInput, useUniswapPricesQuery } from 'graphql/data/__generated__
import { toContractInput } from 'graphql/data/util' import { toContractInput } from 'graphql/data/util'
import useStablecoinPrice from 'hooks/useStablecoinPrice' import useStablecoinPrice from 'hooks/useStablecoinPrice'
import { useMemo } from 'react' import { useMemo } from 'react'
import { getContract } from 'utilities/src/contracts/getContract' import { getContract } from 'utils'
import { CurrencyKey, currencyKey, currencyKeyFromGraphQL } from 'utils/currencyKey' import { CurrencyKey, currencyKey, currencyKeyFromGraphQL } from 'utils/currencyKey'
import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'wallet/src/abis/types/v3' import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'wallet/src/abis/types/v3'
......
import { ChainId, CurrencyAmount, Token, V3_CORE_FACTORY_ADDRESSES } from '@uniswap/sdk-core' import { ChainId, CurrencyAmount, Token, V3_CORE_FACTORY_ADDRESSES } from '@uniswap/sdk-core'
import IUniswapV3PoolStateJSON from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json' import IUniswapV3PoolStateJSON from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json'
import { Pool, Position, computePoolAddress } from '@uniswap/v3-sdk' import { Pool, Position, computePoolAddress } from '@uniswap/v3-sdk'
import { DEFAULT_ERC20_DECIMALS } from 'constants/tokens'
import { BigNumber } from 'ethers/lib/ethers' import { BigNumber } from 'ethers/lib/ethers'
import { Interface } from 'ethers/lib/utils' import { Interface } from 'ethers/lib/utils'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { PositionDetails } from 'types/position' import { PositionDetails } from 'types/position'
import { DEFAULT_ERC20_DECIMALS } from 'utilities/src/tokens/constants'
import { currencyKey } from 'utils/currencyKey' import { currencyKey } from 'utils/currencyKey'
import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'wallet/src/abis/types/v3' import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'wallet/src/abis/types/v3'
import { UniswapV3PoolInterface } from 'wallet/src/abis/types/v3/UniswapV3Pool' import { UniswapV3PoolInterface } from 'wallet/src/abis/types/v3/UniswapV3Pool'
......
This diff is collapsed.
...@@ -59,7 +59,10 @@ export const Scrim = (props: ScrimBackgroundProps) => { ...@@ -59,7 +59,10 @@ export const Scrim = (props: ScrimBackgroundProps) => {
} }
const AccountDrawerScrollWrapper = styled.div` const AccountDrawerScrollWrapper = styled.div`
overflow-y: auto; overflow: hidden;
&:hover {
overflow-y: auto;
}
${ScrollBarStyles} ${ScrollBarStyles}
......
...@@ -66,7 +66,7 @@ function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): ...@@ -66,7 +66,7 @@ function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme):
case BadgeVariant.WARNING_OUTLINE: case BadgeVariant.WARNING_OUTLINE:
return theme.deprecated_accentWarning return theme.deprecated_accentWarning
default: default:
return theme.neutral2 return readableColor(theme.neutral2)
} }
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment