ci(release): publish latest release

parent 7658b558
......@@ -38,7 +38,7 @@ apps/mobile/android
# extension
apps/stretch/dev
apps/extension/dev
# packages
......
IPFS hash of the deployment:
- CIDv0: `QmY1uds14DFpDG7TsuCqiH5965u1GCVZPZ4GQ9AgcMCjcb`
- CIDv1: `bafybeiepy63dfywcra662v3iuz2ym2aiz7smx6syyiaga6yttjjgf53oca`
- CIDv0: `QmaNrzdiCZfezB1GYBZprZVoAh4KGsaZjzVH9qSmDCvZDv`
- CIDv1: `bafybeifs3vfz27oqei35fu5fg6pfgzz3eacp3rdyhg7lvm5mp63iiauomu`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
......@@ -10,15 +10,41 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeiepy63dfywcra662v3iuz2ym2aiz7smx6syyiaga6yttjjgf53oca.ipfs.dweb.link/
- https://bafybeiepy63dfywcra662v3iuz2ym2aiz7smx6syyiaga6yttjjgf53oca.ipfs.cf-ipfs.com/
- [ipfs://QmY1uds14DFpDG7TsuCqiH5965u1GCVZPZ4GQ9AgcMCjcb/](ipfs://QmY1uds14DFpDG7TsuCqiH5965u1GCVZPZ4GQ9AgcMCjcb/)
- https://bafybeifs3vfz27oqei35fu5fg6pfgzz3eacp3rdyhg7lvm5mp63iiauomu.ipfs.dweb.link/
- https://bafybeifs3vfz27oqei35fu5fg6pfgzz3eacp3rdyhg7lvm5mp63iiauomu.ipfs.cf-ipfs.com/
- [ipfs://QmaNrzdiCZfezB1GYBZprZVoAh4KGsaZjzVH9qSmDCvZDv/](ipfs://QmaNrzdiCZfezB1GYBZprZVoAh4KGsaZjzVH9qSmDCvZDv/)
### 5.38.1 (2024-07-03)
## 5.39.0 (2024-07-10)
### Features
* **web:** [ext-gtm] make drawer a modal when uni extension is installed (#9560) 9f17d08
* **web:** [ext-gtm] remove extra miniport content when extension is connected (#9678) e78e6d8
* **web:** activity Row for FOR transactions (#9874) cea60c1
* **web:** add error state for FOR connection modal (#9597) 5bd5da8
* **web:** deeplinks for extension (#9782) 9c8c5bf
* **web:** empty wallet state (#9641) 67e82cc
* **web:** fiat on ramp design polish (#9860) 40ba8bc
* **web:** FOR transaction status polling (#9793) 147db24
* **web:** open FOR widget in onClick (#9826) 5658b80
* **web:** redux slice for FOR transactions (#9575) 5985d7c
* **web:** reorganize fiat on ramp components (incl. snapshots) (#9625) 60d8ebe
### Bug Fixes
* **web:** hotfix for rive not loading (#9859) 9c6a007
* **web:** avoid datadog duplicate logs and slowdowns in web test (#9626) 2031800
* **web:** fix incorrect isToken check (#9765) 7ef1d18
* **web:** fix rive not loading in prod (#9856) 681219d
* **web:** memoize FOR transactions to avoid rerenders - hotfix staging (#9917) e4d272e
* **web:** Show chart error state if priceHistory is an empty array [staging] (#9926) 4dc66f3
* **web:** use sentence case miniP (#9759) 79ce461
* **web:** useSyncChainQuery for input currencies without chain param (#9521) 565c985
### Continuous Integration
* **web:** update sitemaps fd342d7
web/5.38.1
\ No newline at end of file
web/5.39.0
\ No newline at end of file
// Copied from:
// https://gist.github.com/phcbarros/bd90825863c3573cc0a28e90db17d1a4
const RNN = require('@react-navigation/native')
let listeners = {}
const setOptions = jest.fn()
const navigate = jest.fn()
const navigation = {
setOptions,
navigate,
addListener: jest.fn((name, l) => (listeners[name] = l)),
getListener: (name) => listeners[name],
triggerListener: (name, ...params) => listeners[name](...params),
resetListeners: () => {
listeners = {}
},
}
const useNavigation = () => navigation
let params = {}
const useRoute = () => ({
params,
})
module.exports = {
...RNN,
useNavigation,
useRoute,
setParams: (p) => (params = { ...params, ...p }),
}
import { CreateNewWallet } from 'e2e/usecases/onboarding/CreateNewWallet'
import { ImportWallet } from 'e2e/usecases/onboarding/ImportWallet'
import { WatchWallet } from 'e2e/usecases/onboarding/WatchWallet'
describe('Onboarding', () => {
......@@ -14,5 +13,7 @@ describe('Onboarding', () => {
it('creates a new wallet', CreateNewWallet)
it('watches wallet', WatchWallet)
it('imports a testing wallet using recovery phrase', ImportWallet)
// TODO: find the way to test native input
// eslint-disable-next-line jest/no-commented-out-tests
// it('imports a testing wallet using recovery phrase', ImportWallet)
})
import { by, element, expect } from 'detox'
import { TestWallet } from 'e2e/utils/fixtures'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
export function CreateNewWallet(): void {
it('creates a new wallet', async () => {
// Selects "Create a new wallet" option on the landing screen
await element(by.id(ElementName.CreateAccount)).tap()
// Skips unitag flow
await element(by.id(ElementName.Skip)).tap()
// Taps "Let's keep it safe" on QRAnimation screen
await element(by.id(ElementName.Next)).tap()
// Check is both manual and cloud backup options are available on BackupScreen
await expect(element(by.id(ElementName.AddCloudBackup))).toBeVisible()
await expect(element(by.id(ElementName.AddManualBackup))).toBeVisible()
// Picks "Manual backup" option
await element(by.id(ElementName.AddManualBackup)).tap()
// Checks if ManualBackupScreen warning displays and taps "I'm ready" button
await expect(element(by.id(ElementName.Confirm))).toBeVisible()
await element(by.id(ElementName.Confirm)).tap()
// Taps continue on ManualBackupScreen
await element(by.id(ElementName.Next)).tap()
// Taps continue on manual backup confirmation screen. It is replaced by mock because detox
// can't interact with native screens
await element(by.id(ElementName.Continue)).tap()
// Skips notification setup by tapping "Maybe later" button
await element(by.id(ElementName.Skip)).tap()
// Skips biometrics setup by tapping "Maybe later" button
await element(by.id(ElementName.Skip)).tap()
// Confirms by tapping "Skip" on warning modal
await element(by.id(ElementName.Confirm)).tap()
// Confirms if user successfuly finished create new wallet flow by checking if provided wallet name is
// displayed and other
await expect(element(by.text(TestWallet.name))).toBeVisible()
await expect(element(by.id(ElementName.Swap))).toBeVisible()
await expect(element(by.id(ElementName.SearchTokensAndWallets))).toBeVisible()
})
}
import { by, element, expect } from 'detox'
import { TestWallet } from 'e2e/utils/fixtures'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export async function CreateNewWallet(): Promise<void> {
// Selects "Create a new wallet" option on the landing screen
await element(by.id(ElementName.CreateAccount)).tap()
await element(by.id(TestID.CreateAccount)).tap()
// Skips unitag flow
await element(by.id(ElementName.Skip)).tap()
await element(by.id(TestID.Skip)).tap()
// Taps "Let's keep it safe" on QRAnimation screen
await element(by.id(ElementName.Next)).tap()
await element(by.id(TestID.Next)).tap()
// Check is both manual and cloud backup options are available on BackupScreen
await expect(element(by.id(ElementName.AddCloudBackup))).toBeVisible()
await expect(element(by.id(ElementName.AddManualBackup))).toBeVisible()
await expect(element(by.id(TestID.AddCloudBackup))).toBeVisible()
await expect(element(by.id(TestID.AddManualBackup))).toBeVisible()
// Picks "Manual backup" option
await element(by.id(ElementName.AddManualBackup)).tap()
await element(by.id(TestID.AddManualBackup)).tap()
// Checks if ManualBackupScreen warning displays and taps "I'm ready" button
await expect(element(by.id(ElementName.Confirm))).toBeVisible()
await element(by.id(ElementName.Confirm)).tap()
await expect(element(by.id(TestID.Confirm))).toBeVisible()
await element(by.id(TestID.Confirm)).tap()
// Taps continue on ManualBackupScreen
await element(by.id(ElementName.Next)).tap()
await element(by.id(TestID.Next)).tap()
// Taps continue on manual backup confirmation screen. It is replaced by mock because detox
// can't interact with native screens
await element(by.id(ElementName.Continue)).tap()
await element(by.id(TestID.Continue)).tap()
// Skips notification setup by tapping "Maybe later" button
await element(by.id(ElementName.Skip)).tap()
await element(by.id(TestID.Skip)).tap()
// Skips biometrics setup by tapping "Maybe later" button
await element(by.id(ElementName.Skip)).tap()
await element(by.id(TestID.Skip)).tap()
// Confirms by tapping "Skip" on warning modal
await element(by.id(ElementName.Confirm)).tap()
await element(by.id(TestID.Confirm)).tap()
// Confirms if user successfuly finished create new wallet flow by checking if provided wallet name is
// displayed and other
await expect(element(by.text(TestWallet.name))).toBeVisible()
await expect(element(by.id(ElementName.Swap))).toBeVisible()
await expect(element(by.id(ElementName.SearchTokensAndWallets))).toBeVisible()
await expect(element(by.id(TestID.Swap))).toBeVisible()
await expect(element(by.id(TestID.SearchTokensAndWallets))).toBeVisible()
}
import { by, element, expect } from 'detox'
import { TestWallet } from 'e2e/utils/fixtures'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export async function ImportWallet(): Promise<void> {
// Selects "Add an existing wallet" option on the landing screen
await element(by.id(ElementName.ImportAccount)).tap()
await element(by.id(TestID.ImportAccount)).tap()
// Picks Import a wallet by recovery phase option
await element(by.id(ElementName.OnboardingImportSeedPhrase)).tap()
await element(by.id(TestID.OnboardingImportSeedPhrase)).tap()
// Checks if recovery phase input is in focus and types recovery phrase in
await expect(element(by.id(ElementName.ImportAccountInput))).toBeFocused()
await element(by.id(ElementName.ImportAccountInput)).typeText(TestWallet.recoveryPhrase)
await expect(element(by.id(TestID.ImportAccountInput))).toBeFocused()
await element(by.id(TestID.ImportAccountInput)).typeText(TestWallet.recoveryPhrase)
// Taps continue navigating to SelectWalletScreen
await element(by.id(ElementName.Continue)).tap()
await element(by.id(TestID.Continue)).tap()
// Taps continue on SelectWalletScreen
await waitFor(element(by.id(`${ElementName.WalletCard}-1`)))
await waitFor(element(by.id(`${TestID.WalletCard}-1`)))
.toBeVisible()
.withTimeout(10000)
await element(by.id(ElementName.Next)).tap()
await element(by.id(TestID.Next)).tap()
// Skips cloud backup step on BackupScreen by clicking "Maybe later"
await expect(element(by.id(ElementName.AddCloudBackup))).toBeVisible()
await element(by.id(ElementName.Next)).tap()
await expect(element(by.id(TestID.AddCloudBackup))).toBeVisible()
await element(by.id(TestID.Next)).tap()
// Skips notification setup by tapping "Maybe later" button
await element(by.id(ElementName.Skip)).tap()
await element(by.id(TestID.Skip)).tap()
// Skips biometrics setup by tapping "Maybe later" button
await element(by.id(ElementName.Skip)).tap()
await element(by.id(TestID.Skip)).tap()
// Confirms by tapping "Skip" on warning modal
await element(by.id(ElementName.Confirm)).tap()
await element(by.id(TestID.Confirm)).tap()
// Confirms if user successfuly finished create new wallet flow by checking if provided wallet name is
// displayed and other
await expect(element(by.text(TestWallet.name))).toBeVisible()
await expect(element(by.id(ElementName.Swap))).toBeVisible()
await expect(element(by.id(ElementName.SearchTokensAndWallets))).toBeVisible()
await expect(element(by.id(TestID.Swap))).toBeVisible()
await expect(element(by.id(TestID.SearchTokensAndWallets))).toBeVisible()
}
import { by, element, expect } from 'detox'
import { TestWatchedWallet } from 'e2e/utils/fixtures'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export async function WatchWallet(): Promise<void> {
// Selects "Add an existing wallet" option on the landing screen
await element(by.id(ElementName.ImportAccount)).tap()
await element(by.id(TestID.ImportAccount)).tap()
// Picks Watch a wallet option on ImportMethodScreen
await element(by.id(ElementName.WatchWallet)).tap()
await element(by.id(TestID.WatchWallet)).tap()
// Checks if wallet name is in focus and types recovery phrase in
await expect(element(by.id(ElementName.ImportAccountInput))).toBeFocused()
await element(by.id(ElementName.ImportAccountInput)).typeText(TestWatchedWallet.ens)
await expect(element(by.id(TestID.ImportAccountInput))).toBeFocused()
await element(by.id(TestID.ImportAccountInput)).typeText(TestWatchedWallet.ens)
// Confirms the entered wallet name by tapping "continue"
await element(by.id(ElementName.Next)).tap()
await element(by.id(TestID.Next)).tap()
// Checks if Home screen is displayed with a proper user name
await expect(element(by.text(TestWatchedWallet.displayName))).toBeVisible()
await expect(element(by.id(ElementName.Swap))).toBeVisible()
await expect(element(by.id(ElementName.SearchTokensAndWallets))).toBeVisible()
await expect(element(by.id(TestID.Swap))).toBeVisible()
await expect(element(by.id(TestID.SearchTokensAndWallets))).toBeVisible()
}
import { by, element, expect } from 'detox'
import { TestWatchedWallet } from 'e2e/utils/fixtures'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export async function SwapBasicInteractions(): Promise<void> {
// Navigate to swap screen
await element(by.id(ElementName.Swap)).tap()
await element(by.id(TestID.Swap)).tap()
// Checks if currency input is selected
await expect(element(by.id(ElementName.AmountInputIn))).toBeFocused()
await expect(element(by.id(TestID.AmountInputIn))).toBeFocused()
// Checks if "Max" button is available
await expect(element(by.id(ElementName.SetMaxInput))).toBeVisible()
await expect(element(by.id(TestID.SetMaxInput))).toBeVisible()
// Opens token selector modal on Swap screen
await element(by.id(ElementName.ChooseOutputToken)).tap()
await element(by.id(TestID.ChooseOutputToken)).tap()
// Picks usdc output token
await element(by.text('USDC')).atIndex(0).tap()
......@@ -34,53 +34,53 @@ export async function SwapBasicInteractions(): Promise<void> {
await element(by.id('decimal-pad-backspace')).tap()
// Checks if expected input expected value: "12345678.09"
await expect(element(by.id(ElementName.AmountInputIn))).toHaveValue('12345678.09')
await expect(element(by.id(TestID.AmountInputIn))).toHaveValue('12345678.09')
// Checks if expected error is displayed
await expect(element(by.text('You don’t have enough ETH'))).toBeVisible()
// Checks if expected output expected value: "0"
await expect(element(by.id(ElementName.AmountInputOut))).not.toHaveValue('0')
await expect(element(by.id(TestID.AmountInputOut))).not.toHaveValue('0')
// Swaps input and output currencies
await element(by.id(ElementName.SwitchCurrenciesButton)).tap()
await element(by.id(TestID.SwitchCurrenciesButton)).tap()
// Checks if expected input expected value: "0"
await expect(element(by.id(ElementName.AmountInputIn))).toHaveValue('0')
await expect(element(by.id(TestID.AmountInputIn))).toHaveValue('0')
// Checks if expected error is displayed
await expect(element(by.text('Not enough liquidity'))).toBeVisible()
// Checks if expected output expected value: "12345678.09"
await expect(element(by.id(ElementName.AmountInputOut))).toHaveValue('12345678.09')
await expect(element(by.id(TestID.AmountInputOut))).toHaveValue('12345678.09')
// Swaps input and output currencies
await element(by.id(ElementName.SwitchCurrenciesButton)).tap()
await element(by.id(TestID.SwitchCurrenciesButton)).tap()
// Selects currency output
await element(by.id(ElementName.AmountInputOut)).tap()
await element(by.id(TestID.AmountInputOut)).tap()
// Clears the output field
await element(by.id(ElementName.AmountInputOut)).clearText()
await element(by.id(TestID.AmountInputOut)).clearText()
await element(by.id('decimal-pad-1')).tap()
await element(by.id('decimal-pad-2')).tap()
await element(by.id('decimal-pad-3')).tap()
// Checks if output has expected value: "123"
await expect(element(by.id(ElementName.AmountInputOut))).toHaveValue('123')
await expect(element(by.id(TestID.AmountInputOut))).toHaveValue('123')
// Checks if expected input value to be cleared
await expect(element(by.id(ElementName.AmountInputIn))).not.toHaveValue('0')
await expect(element(by.id(TestID.AmountInputIn))).not.toHaveValue('0')
// Checks dollar value to be visible
await expect(element(by.text('$123.00'))).toBeVisible()
// Swipes swap modal by dragging down SwapFormHeader
await element(by.id(ElementName.SwapFormHeader)).swipe('down', 'fast', 0.75)
await element(by.id(TestID.SwapFormHeader)).swipe('down', 'fast', 0.75)
// Checks if Home screen is visible and not covered
await expect(element(by.text(TestWatchedWallet.displayName))).toBeVisible()
await expect(element(by.id(ElementName.Swap))).toBeVisible()
await expect(element(by.id(ElementName.SearchTokensAndWallets))).toBeVisible()
await expect(element(by.id(TestID.Swap))).toBeVisible()
await expect(element(by.id(TestID.SearchTokensAndWallets))).toBeVisible()
}
......@@ -81,8 +81,8 @@ class RNEthersRS: NSObject {
resolve(res)
return
}
let err = NSError.init()
reject("error", "error", err)
reject("Unable to import new mnemonic", "Failed store new mnemonic in ethers library", nil)
return
}
......@@ -192,8 +192,7 @@ class RNEthersRS: NSObject {
let mnemonic = retrieveMnemonic(mnemonicId: mnemonicId)
if (mnemonic == nil) {
let err = NSError.init()
reject("Mnemonic not found", "Could not find mnemonic for given mnemonicId", err)
reject("Mnemonic not found", "Could not find mnemonic for given mnemonicId", nil)
return
}
......
......@@ -13,8 +13,9 @@ import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions'
import { spacing } from 'ui/src/theme'
import { ActionSheetModal, MenuItemProp } from 'uniswap/src/components/modals/ActionSheetModal'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ElementName, ModalName, WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { isAndroid } from 'utilities/src/platform'
......@@ -110,12 +111,12 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme
}),
)
// Log analytics event
sendAnalyticsEvent(MobileEventName.WalletAdded, {
sendAnalyticsEvent(WalletEventName.WalletAdded, {
wallet_type: ImportType.CreateAdditional,
accounts_imported_count: 1,
wallets_imported: [newAccount.address],
cloud_backup_used: newAccount.backups?.includes(BackupType.Cloud) ?? false,
modal: ModalName.AccountSwitcher,
})
}
......@@ -263,7 +264,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme
variant="subheading1"
/>
<Flex px="$spacing24">
<Button size="small" testID={ElementName.WalletSettings} theme="secondary" onPress={onManageWallet}>
<Button size="small" testID={TestID.WalletSettings} theme="secondary" onPress={onManageWallet}>
{t('account.wallet.button.manage')}
</Button>
</Flex>
......
......@@ -21,6 +21,7 @@ import { ReceiveCryptoModal } from 'src/screens/ReceiveCryptoModal'
import { SettingsFiatCurrencyModal } from 'src/screens/SettingsFiatCurrencyModal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { SettingsLanguageModal } from 'wallet/src/components/settings/language/SettingsLanguageModal'
import { QueuedOrderModal } from 'wallet/src/features/transactions/swap/modals/QueuedOrderModal'
import { useAppDispatch } from 'wallet/src/state'
export function AppModals(): JSX.Element {
......@@ -78,6 +79,8 @@ export function AppModals(): JSX.Element {
<AccountSwitcherModal />
</LazyModalRenderer>
<QueuedOrderModal />
<LazyModalRenderer name={ModalName.RemoveWallet}>
<RemoveWalletModal />
</LazyModalRenderer>
......
......@@ -339,7 +339,7 @@ exports[`AccountSwitcher renders correctly 1`] = `
"paddingTop": 8,
}
}
testID="WalletSettings"
testID="wallet-settings"
>
<Text
maxFontSizeMultiplier={1.2}
......
......@@ -30,12 +30,13 @@ import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import { borderRadii, fonts } from 'ui/src/theme'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { opacify } from 'uniswap/src/utils/colors'
import { isAndroid, isIOS } from 'utilities/src/platform'
import { useHighestBalanceNativeCurrencyId } from 'wallet/src/features/dataApi/balances'
import { prepareSwapFormState } from 'wallet/src/features/transactions/swap/utils'
import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks'
import { opacify } from 'wallet/src/utils/colors'
export const NAV_BAR_HEIGHT_XS = 52
export const NAV_BAR_HEIGHT_SM = 72
......@@ -222,7 +223,7 @@ function ExploreTabBarButton({ activeScale = 0.98 }: ExploreTabBarButtonProps):
style={[styles.searchBar, { borderRadius: borderRadii.roundedFull }]}
onPress={onPress}
>
<TapGestureHandler testID={ElementName.SearchTokensAndWallets} onGestureEvent={onGestureEvent}>
<TapGestureHandler testID={TestID.SearchTokensAndWallets} onGestureEvent={onGestureEvent}>
<AnimatedFlex borderRadius="$roundedFull" overflow="hidden" style={animatedStyle}>
<BlurView intensity={isIOS ? 100 : 0}>
<Flex
......
import { BackButton } from 'src/components/buttons/BackButton'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
export const renderHeaderBackButton = (): JSX.Element => <BackButton color="$neutral2" size={iconSizes.icon28} />
export const renderHeaderBackImage = (): JSX.Element => (
<RotatableChevron color="$neutral2" height={iconSizes.icon28} width={iconSizes.icon28} />
)
......@@ -3,6 +3,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { createStackNavigator, TransitionPresets } from '@react-navigation/stack'
import React from 'react'
import { useAppSelector } from 'src/app/hooks'
import { renderHeaderBackButton, renderHeaderBackImage } from 'src/app/navigation/components'
import {
AppStackParamList,
AppStackScreenProp,
......@@ -12,7 +13,6 @@ import {
SettingsStackParamList,
UnitagStackParamList,
} from 'src/app/navigation/types'
import { BackButton } from 'src/components/buttons/BackButton'
import { HorizontalEdgeGestureTarget } from 'src/components/layout/screens/EdgeGestureTarget'
import { useBiometricCheck } from 'src/features/biometrics/useBiometricCheck'
import { FiatOnRampProvider } from 'src/features/fiatOnRamp/FiatOnRampContext'
......@@ -65,7 +65,6 @@ import { SettingsWalletManageConnection } from 'src/screens/SettingsWalletManage
import { TokenDetailsScreen } from 'src/screens/TokenDetailsScreen'
import { WebViewScreen } from 'src/screens/WebViewScreen'
import { useDeviceInsets, useSporeColors } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
......@@ -201,8 +200,6 @@ export function FiatOnRampStackNavigator(): JSX.Element {
)
}
const renderHeaderBackButton = (): JSX.Element => <BackButton color="$neutral2" size={28} />
export function OnboardingStackNavigator(): JSX.Element {
const colors = useSporeColors()
const seedPhraseRefactorEnabled = useFeatureFlag(FeatureFlags.SeedPhraseRefactorNative)
......@@ -216,7 +213,8 @@ export function OnboardingStackNavigator(): JSX.Element {
<OnboardingStack.Group
screenOptions={{
headerTitle: '',
headerBackTitleVisible: false,
gestureEnabled: true,
headerBackVisible: false,
headerLeft: renderHeaderBackButton,
headerTransparent: true,
headerTintColor: colors.neutral2.val,
......@@ -287,8 +285,6 @@ export function OnboardingStackNavigator(): JSX.Element {
)
}
const renderHeaderBackImage = (): JSX.Element => <RotatableChevron color="$neutral2" height={28} width={28} />
export function UnitagStackNavigator(): JSX.Element {
const colors = useSporeColors()
const insets = useDeviceInsets()
......
......@@ -9,8 +9,8 @@ import {
useTokenPriceHistoryQuery,
} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { GqlResult } from 'uniswap/src/data/types'
import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils'
import { isError, isNonPollingRequestInFlight } from 'wallet/src/data/utils'
import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
export type TokenSpotData = {
......
......@@ -10,7 +10,8 @@ import Scan from 'ui/src/assets/icons/receive.svg'
import ScanQRIcon from 'ui/src/assets/icons/scan.svg'
import { iconSizes } from 'ui/src/theme'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { WalletQRCode } from 'wallet/src/components/QRCodeScanner/WalletQRCode'
import { selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors'
......@@ -81,7 +82,7 @@ export function RecipientScanModal({ onSelectRecipient, onClose }: Props): JSX.E
p="$spacing16"
paddingEnd="$spacing24"
style={{ backgroundColor: colors.DEP_backgroundOverlay.val }}
testID={ElementName.QRCodeModalToggle}
testID={TestID.QRCodeModalToggle}
onPress={onPressBottomToggle}
>
<Flex row alignItems="center" gap="$spacing12">
......
import React, { memo, useCallback, useState } from 'react'
import React, { memo, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Keyboard } from 'react-native'
import { Keyboard, TextInput } from 'react-native'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import { RecipientScanModal } from 'src/components/RecipientSelect/RecipientScanModal'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
......@@ -8,22 +8,23 @@ import ScanQRIcon from 'ui/src/assets/icons/scan.svg'
import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import { iconSizes } from 'ui/src/theme'
import { useBottomSheetContext } from 'uniswap/src/components/modals/BottomSheetContext'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { RecipientList } from 'wallet/src/components/RecipientSearch/RecipientList'
import { useFilteredRecipientSections } from 'wallet/src/components/RecipientSearch/hooks'
import { SearchBar } from 'wallet/src/features/search/SearchBar'
interface RecipientSelectProps {
onSelectRecipient: (newRecipientAddress: string) => void
onToggleShowRecipientSelector: () => void
onHideRecipientSelector: () => void
recipient?: string
focusInput?: boolean
}
function QRScannerIconButton({ onPress }: { onPress: () => void }): JSX.Element {
const colors = useSporeColors()
return (
<TouchableArea hapticFeedback testID={ElementName.SelectRecipient} onPress={onPress}>
<TouchableArea hapticFeedback testID={TestID.SelectRecipient} onPress={onPress}>
<ScanQRIcon color={colors.neutral2.get()} height={iconSizes.icon20} width={iconSizes.icon20} />
</TouchableArea>
)
......@@ -31,16 +32,26 @@ function QRScannerIconButton({ onPress }: { onPress: () => void }): JSX.Element
export function _RecipientSelect({
onSelectRecipient,
onToggleShowRecipientSelector,
onHideRecipientSelector,
recipient,
focusInput,
}: RecipientSelectProps): JSX.Element {
const { t } = useTranslation()
const { isSheetReady } = useBottomSheetContext()
const inputRef = useRef<TextInput>(null)
const [pattern, setPattern] = useState('')
const [showQRScanner, setShowQRScanner] = useState(false)
const sections = useFilteredRecipientSections(pattern)
useEffect(() => {
if (focusInput) {
inputRef.current?.focus()
} else {
inputRef.current?.blur()
}
}, [focusInput])
const onPressQRScanner = useCallback(() => {
Keyboard.dismiss()
setShowQRScanner(true)
......@@ -57,12 +68,12 @@ export function _RecipientSelect({
<Text variant="subheading1">{t('qrScanner.recipient.label.send')}</Text>
</Flex>
<SearchBar
autoFocus
ref={inputRef}
backgroundColor="$surface2"
endAdornment={<QRScannerIconButton onPress={onPressQRScanner} />}
placeholder={t('qrScanner.recipient.input.placeholder')}
value={pattern ?? ''}
onBack={recipient ? onToggleShowRecipientSelector : undefined}
onBack={recipient ? onHideRecipientSelector : undefined}
onChangeText={setPattern}
/>
{!sections.length ? (
......
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Button, CheckBox, Flex, SpinningLoader, Text } from 'ui/src'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export function RemoveLastMnemonicWalletFooter({
onPress,
......@@ -35,7 +35,7 @@ export function RemoveLastMnemonicWalletFooter({
fill
disabled={!checkBoxAccepted}
icon={inProgress ? <SpinningLoader color="$statusCritical" /> : undefined}
testID={ElementName.Confirm}
testID={TestID.Confirm}
theme="detrimental"
onPress={onPress}
>
......
import React, { useCallback, useState } from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useAnimatedStyle, withTiming } from 'react-native-reanimated'
import { useAppDispatch, useAppSelector } from 'src/app/hooks'
......@@ -11,11 +11,12 @@ import { Delay } from 'src/components/layout/Delayed'
import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks'
import { closeModal } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import { Button, ColorTokens, Flex, SpinningLoader, Text, ThemeKeys, useSporeColors } from 'ui/src'
import { Button, Flex, SpinningLoader, Text, ThemeKeys, useSporeColors } from 'ui/src'
import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import { iconSizes, opacify } from 'ui/src/theme'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ElementName, ModalName, WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { logger } from 'utilities/src/logger/logger'
......@@ -101,6 +102,10 @@ export function RemoveWalletModal(): JSX.Element | null {
)
}
sendAnalyticsEvent(WalletEventName.WalletRemoved, {
wallets_removed: accountsToRemove.map((a) => a.address),
})
onClose()
setInProgress(false)
}, [account, associatedAccounts, dispatch, isReplacing, hasAccountsLeft, onClose])
......@@ -161,7 +166,6 @@ export function RemoveWalletModal(): JSX.Element | null {
const { title, description, Icon, iconColorLabel, actionButtonTheme, actionButtonLabel } = modalContent
// TODO(MOB-1420): clean up types
const labelColor: ThemeKeys = iconColorLabel
return (
......@@ -208,14 +212,7 @@ export function RemoveWalletModal(): JSX.Element | null {
)}
<Button
fill
icon={
inProgress ? (
<SpinningLoader
// TODO(MOB-1420): clean up types (as ColorTokens)
color={`$${labelColor}` as ColorTokens}
/>
) : undefined
}
icon={inProgress ? <SpinningLoader color={`$${labelColor}`} /> : undefined}
testID={isRemovingRecoveryPhrase ? ElementName.Continue : ElementName.Remove}
theme={actionButtonTheme}
width="100%"
......
......@@ -7,7 +7,8 @@ import { Button, Flex, Text, useSporeColors } from 'ui/src'
import LockIcon from 'ui/src/assets/icons/lock.svg'
import { iconSizes, opacify } from 'ui/src/theme'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobile'
......@@ -54,7 +55,7 @@ export function RestoreWalletModal(): JSX.Element | null {
<Button fill theme="tertiary" onPress={onDismiss}>
{t('common.button.dismiss')}
</Button>
<Button fill testID={ElementName.RestoreWallet} theme="primary" onPress={onRestore}>
<Button fill testID={TestID.RestoreWallet} theme="primary" onPress={onRestore}>
{t('common.button.restore')}
</Button>
</Flex>
......
......@@ -13,10 +13,10 @@ import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
import { Switch } from 'wallet/src/components/buttons/Switch'
import { Arrow } from 'wallet/src/components/icons/Arrow'
import { useAppDispatch } from 'wallet/src/state'
import { openUri } from 'wallet/src/utils/linking'
export interface SettingsSection {
subTitle: string
......
import { SharedEventName } from '@uniswap/analytics-events'
import React from 'react'
import { SvgProps } from 'react-native-svg'
import { useAppDispatch } from 'src/app/hooks'
......@@ -5,11 +6,13 @@ import { Flex, IconProps, Text, TouchableArea, useSporeColors } from 'ui/src'
import CopyIcon from 'ui/src/assets/icons/copy-sheets.svg'
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementNameType } from 'uniswap/src/features/telemetry/constants'
import { ElementName, ElementNameType } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types'
import { setClipboard } from 'wallet/src/utils/clipboard'
import { openUri } from 'wallet/src/utils/linking'
export enum LinkButtonType {
Copy = 'copy',
......@@ -44,6 +47,10 @@ export function LinkButton({
copyType: CopyNotificationType.Address,
}),
)
sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, {
element: ElementName.CopyAddress,
screen: MobileScreens.TokenDetails,
})
}
const onPress = async (): Promise<void> => {
......
......@@ -2,7 +2,7 @@ import React from 'react'
import { Flex, TouchableArea } from 'ui/src'
import SendIcon from 'ui/src/assets/icons/send-action.svg'
import { iconSizes } from 'ui/src/theme'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
type Props = {
onPress: () => void
......@@ -12,7 +12,7 @@ type Props = {
export function SendButton({ onPress, color, size = iconSizes.icon24 }: Props): JSX.Element {
return (
<TouchableArea hapticFeedback hitSlop={16} p="$spacing4" testID={ElementName.Send} onPress={onPress}>
<TouchableArea hapticFeedback hitSlop={16} p="$spacing4" testID={TestID.Send} onPress={onPress}>
<Flex centered row gap="$spacing8">
<SendIcon color={color} height={size} width={size} />
</Flex>
......
......@@ -5,13 +5,13 @@ import { useTokenDetailsNavigation } from 'src/components/TokenDetails/hooks'
import { Flex, Separator, Text, TouchableArea, useSporeColors } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo'
import { InlineNetworkPill } from 'uniswap/src/components/network/NetworkPill'
import { PortfolioBalance } from 'uniswap/src/features/dataApi/types'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { MobileEventName } from 'uniswap/src/features/telemetry/constants'
import { CurrencyId } from 'uniswap/src/types/currency'
import { getSymbolDisplayText } from 'uniswap/src/utils/currency'
import { NumberType } from 'utilities/src/format/types'
import { InlineNetworkPill } from 'wallet/src/components/network/NetworkPill'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { AccountType } from 'wallet/src/features/wallet/accounts/types'
import { useActiveAccount, useDisplayName } from 'wallet/src/features/wallet/hooks'
......
......@@ -4,7 +4,7 @@ import { Button, Flex, useSporeColors } from 'ui/src'
import { opacify, validColor } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ElementNameType, SectionName } from 'uniswap/src/features/telemetry/constants'
import { getContrastPassingTextColor } from 'wallet/src/utils/colors'
import { getContrastPassingTextColor } from 'uniswap/src/utils/colors'
function CTAButton({
title,
......
import { useCrossChainBalances, useTokenDetailsNavigation } from 'src/components/TokenDetails/hooks'
import { preloadedMobileState } from 'src/test/fixtures'
import { act, renderHook, waitFor } from 'src/test/test-utils'
import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils'
import {
SAMPLE_CURRENCY_ID_1,
portfolio,
......
......@@ -7,9 +7,9 @@ import {
} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { fromGraphQLChain } from 'uniswap/src/features/chains/utils'
import { PortfolioBalance } from 'uniswap/src/features/dataApi/types'
import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils'
import { CurrencyId } from 'uniswap/src/types/currency'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils'
import { buildCurrencyId, buildNativeCurrencyId, currencyIdToChain } from 'wallet/src/utils/currencyId'
/** Helper hook to retrieve balances across chains for a given currency, for the active account. */
......
......@@ -9,6 +9,7 @@ import { UniverseChainId } from 'uniswap/src/types/chains'
import { CurrencyId } from 'uniswap/src/types/currency'
import { TokenOptionItem } from 'wallet/src/components/TokenSelector/TokenOptionItem'
import { useBottomSheetFocusHook } from 'wallet/src/components/modals/hooks'
import { useTokenWarningDismissed } from 'wallet/src/features/tokens/safetyHooks'
interface Props {
onSelectCurrency: (currency: FiatOnRampCurrency) => void
......@@ -34,6 +35,7 @@ function TokenOptionItemWrapper({
[currencyInfo],
)
const onPress = useCallback(() => onSelectCurrency?.(currency), [currency, onSelectCurrency])
const { tokenWarningDismissed, dismissWarningCallback } = useTokenWarningDismissed(currencyInfo?.currencyId)
if (!option) {
return null
......@@ -41,9 +43,11 @@ function TokenOptionItemWrapper({
return (
<TokenOptionItem
dismissWarningCallback={dismissWarningCallback}
option={option}
showNetworkPill={currencyInfo?.currency.chainId !== UniverseChainId.Mainnet}
showWarnings={true}
tokenWarningDismissed={tokenWarningDismissed}
onPress={onPress}
/>
)
......
......@@ -12,7 +12,7 @@ import { WalletConnectSession, removeSession } from 'src/features/walletConnect/
import { disableOnPress } from 'src/utils/disableOnPress'
import { AnimatedTouchableArea, Flex, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { iconSizes, spacing } from 'ui/src/theme'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { WalletConnectEvent } from 'uniswap/src/types/walletConnect'
import { logger } from 'utilities/src/logger/logger'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
......@@ -124,7 +124,7 @@ export function DappConnectionItem({
<TouchableArea
hapticFeedback
hapticStyle={ImpactFeedbackStyle.Medium}
testID={ElementName.WCDappNetworks}
testID={TestID.WCDappNetworks}
onLongPress={disableOnPress}
onPress={(): void => onPressChangeNetwork(session)}
>
......
......@@ -17,7 +17,7 @@ import { Button, Flex, useDeviceInsets } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { BottomSheetModalProps } from 'uniswap/src/components/modals/BottomSheetModalProps'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
const MEASURE_LAYOUT_TIMEOUT = 100
......@@ -183,10 +183,10 @@ function ModalFooter({
pt="$spacing12"
px="$spacing24"
>
<Button fill size="medium" testID={ElementName.Cancel} theme="tertiary" onPress={onReject}>
<Button fill size="medium" testID={TestID.Cancel} theme="tertiary" onPress={onReject}>
{t('common.button.cancel')}
</Button>
<Button fill disabled={!confirmationEnabled} size="medium" testID={ElementName.Confirm} onPress={onConfirm}>
<Button fill disabled={!confirmationEnabled} size="medium" testID={TestID.Confirm} onPress={onConfirm}>
{confirmationButtonText ?? t('common.button.accept')}
</Button>
</Flex>
......
......@@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next'
import { ScrollView } from 'react-native-gesture-handler'
import { LinkButton } from 'src/components/buttons/LinkButton'
import { SignRequest, WalletConnectRequest, isTransactionRequest } from 'src/features/walletConnect/walletConnectSlice'
import { useNoYoloParser } from 'src/utils/useNoYoloParser'
import { Flex, Text, useSporeColors } from 'ui/src'
import { TextVariantTokens, iconSizes } from 'ui/src/theme'
import { toSupportedChainId } from 'uniswap/src/features/chains/utils'
......@@ -17,6 +16,7 @@ import { useENS } from 'wallet/src/features/ens/useENS'
import { ContentRow } from 'wallet/src/features/transactions/TransactionRequest/ContentRow'
import { SpendingDetails } from 'wallet/src/features/transactions/TransactionRequest/SpendingDetails'
import { ExplorerDataType, getExplorerLink } from 'wallet/src/utils/linking'
import { useNoYoloParser } from 'wallet/src/utils/useNoYoloParser'
const getStrMessage = (request: WalletConnectRequest): string => {
if (request.type === EthMethod.PersonalSign || request.type === EthMethod.EthSign) {
......
......@@ -10,6 +10,7 @@ import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo'
import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { NativeCurrency } from 'uniswap/src/features/tokens/NativeCurrency'
import { buildCurrencyId } from 'uniswap/src/utils/currencyId'
import { NumberType } from 'utilities/src/format/types'
import { NetworkFee } from 'wallet/src/components/network/NetworkFee'
......@@ -17,7 +18,6 @@ import { GasFeeResult } from 'wallet/src/features/gas/types'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api'
import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo'
import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks'
......
......@@ -16,12 +16,12 @@ import { Flex, Text, useSporeColors } from 'ui/src'
import AlertTriangle from 'ui/src/assets/icons/alert-triangle.svg'
import { iconSizes } from 'ui/src/theme'
import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard'
import { NativeCurrency } from 'uniswap/src/features/tokens/NativeCurrency'
import { EthMethod, isPrimaryTypePermit } from 'uniswap/src/types/walletConnect'
import { buildCurrencyId } from 'uniswap/src/utils/currencyId'
import { logger } from 'utilities/src/logger/logger'
import { useUSDValue } from 'wallet/src/features/gas/hooks'
import { GasFeeResult } from 'wallet/src/features/gas/types'
import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { AddressFooter } from 'wallet/src/features/transactions/TransactionRequest/AddressFooter'
import { NetworkFeeFooter } from 'wallet/src/features/transactions/TransactionRequest/NetworkFeeFooter'
import { BlockedAddressWarning } from 'wallet/src/features/trm/BlockedAddressWarning'
......
import { useMemo } from 'react'
import { NativeCurrency } from 'uniswap/src/features/tokens/NativeCurrency'
import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains'
import { GasFeeResult } from 'wallet/src/features/gas/types'
import { useOnChainNativeCurrencyBalance } from 'wallet/src/features/portfolio/api'
import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { hasSufficientFundsIncludingGas } from 'wallet/src/features/transactions/utils'
import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount'
......
......@@ -21,8 +21,9 @@ import {
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Check, RotatableChevron, X } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { ElementName, MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { WalletChainId } from 'uniswap/src/types/chains'
import { WCEventType, WCRequestOutcome, WalletConnectEvent } from 'uniswap/src/types/walletConnect'
import { formatDappURL } from 'utilities/src/format/urls'
......@@ -137,7 +138,7 @@ const SwitchAccountRow = ({ activeAddress, setModalState }: SwitchAccountProps):
}, [setModalState])
return (
<TouchableArea disabled={!accountIsSwitchable} m="$none" testID={ElementName.WCDappSwitchAccount} onPress={onPress}>
<TouchableArea disabled={!accountIsSwitchable} m="$none" testID={TestID.WCDappSwitchAccount} onPress={onPress}>
<Flex row justifyContent="space-between">
<AddressFooter activeAccountAddress={activeAddress} />
{accountIsSwitchable && <RotatableChevron color="$neutral2" direction="down" height={16} width={16} />}
......
......@@ -29,6 +29,7 @@ import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { EthMethod, UwULinkMethod, UwULinkRequest } from 'uniswap/src/types/walletConnect'
import { logger } from 'utilities/src/logger/logger'
import { WalletQRCode } from 'wallet/src/components/QRCodeScanner/WalletQRCode'
......@@ -337,7 +338,7 @@ export function WalletConnectModal({
p="$spacing16"
paddingEnd="$spacing24"
style={{ backgroundColor: colors.DEP_backgroundOverlay.val }}
testID={ElementName.QRCodeModalToggle}
testID={TestID.QRCodeModalToggle}
onPress={onPressBottomToggle}
>
<Flex row alignItems="center" gap="$spacing12">
......
import { SharedEventName } from '@uniswap/analytics-events'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import ContextMenu from 'react-native-context-menu-view'
......@@ -8,7 +9,8 @@ import { closeModal, openModal } from 'src/features/modals/modalSlice'
import { disableOnPress } from 'src/utils/disableOnPress'
import { Flex, HapticFeedback, Text, TouchableArea } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { NumberType } from 'utilities/src/format/types'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
......@@ -82,6 +84,10 @@ export function AccountCardItem({
copyType: CopyNotificationType.Address,
}),
)
sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, {
element: ElementName.CopyAddress,
modal: ModalName.AccountSwitcher,
})
}
const onPressWalletSettings = (): void => {
......
import { SharedEventName } from '@uniswap/analytics-events'
import React, { useCallback, useEffect } from 'react'
import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { navigate } from 'src/app/navigation/rootNavigation'
......@@ -5,7 +6,9 @@ import { openModal } from 'src/features/modals/modalSlice'
import { Flex, HapticFeedback, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { CopyAlt, Settings } from 'ui/src/components/icons'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { MobileUserPropertyName, setUserProperty } from 'uniswap/src/features/telemetry/user'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses'
import { isDevEnv } from 'utilities/src/environment'
......@@ -59,6 +62,10 @@ export function AccountHeader(): JSX.Element {
copyType: CopyNotificationType.Address,
}),
)
sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, {
element: ElementName.CopyAddress,
screen: MobileScreens.Home,
})
}
}
......@@ -76,7 +83,7 @@ export function AccountHeader(): JSX.Element {
flexDirection="row"
hapticStyle={ImpactFeedbackStyle.Medium}
hitSlop={20}
testID={ElementName.Manage}
testID={TestID.Manage}
onLongPress={async (): Promise<void> => {
if (isDevEnv()) {
await HapticFeedback.selection()
......
......@@ -2,7 +2,7 @@ import { LinkButton } from 'src/components/buttons/LinkButton'
import { fireEvent, render } from 'src/test/test-utils'
import { ON_PRESS_EVENT_PAYLOAD } from 'uniswap/src/test/fixtures'
jest.mock('wallet/src/utils/linking')
jest.mock('uniswap/src/utils/linking')
describe(LinkButton, () => {
it('renders without error', () => {
......@@ -38,7 +38,7 @@ describe(LinkButton, () => {
const button = getByText('link text')
fireEvent.press(button, ON_PRESS_EVENT_PAYLOAD)
expect(require('wallet/src/utils/linking').openUri).toHaveBeenCalledWith(
expect(require('uniswap/src/utils/linking').openUri).toHaveBeenCalledWith(
'https://example.com',
openExternalBrowser,
isSafeUri,
......
......@@ -2,7 +2,7 @@ import React, { useMemo } from 'react'
import { Flex, FlexProps, Text, TouchableArea, TouchableAreaProps, useSporeColors } from 'ui/src'
import ExternalLinkIcon from 'ui/src/assets/icons/external-link.svg'
import { TextVariantTokens, iconSizes } from 'ui/src/theme'
import { openUri } from 'wallet/src/utils/linking'
import { openUri } from 'uniswap/src/utils/linking'
interface LinkButtonProps extends Omit<TouchableAreaProps, 'onPress'> {
label: string
......
......@@ -26,10 +26,10 @@ import {
useExploreTokensTabQuery,
} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { fromGraphQLChain } from 'uniswap/src/features/chains/utils'
import { usePersistedError } from 'uniswap/src/features/dataApi/utils'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { areAddressesEqual } from 'uniswap/src/utils/addresses'
import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId'
import { usePersistedError } from 'wallet/src/features/dataApi/utils'
import { selectHasFavoriteTokens, selectHasWatchedWallets } from 'wallet/src/features/favorites/selectors'
import { selectTokensOrderBy } from 'wallet/src/features/wallet/selectors'
......
import { FavoriteHeaderRow } from 'src/components/explore/FavoriteHeaderRow'
import { fireEvent, render } from 'src/test/test-utils'
import { ON_PRESS_EVENT_PAYLOAD } from 'uniswap/src/test/fixtures'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
const defaultProps = {
title: 'Title',
......@@ -27,8 +28,8 @@ describe(FavoriteHeaderRow, () => {
it('renders favorite button', () => {
const { queryByTestId } = render(<FavoriteHeaderRow {...defaultProps} />)
const favoriteButton = queryByTestId('favorite-header-row/favorite-button')
const doneButton = queryByTestId('favorite-header-row/done-button')
const favoriteButton = queryByTestId(TestID.Edit)
const doneButton = queryByTestId(TestID.Done)
expect(favoriteButton).toBeTruthy()
expect(doneButton).toBeFalsy()
......@@ -37,7 +38,7 @@ describe(FavoriteHeaderRow, () => {
it('calls onPress when favorite icon pressed', () => {
const { getByTestId } = render(<FavoriteHeaderRow {...defaultProps} />)
const favoriteButton = getByTestId('favorite-header-row/favorite-button')
const favoriteButton = getByTestId(TestID.Edit)
fireEvent.press(favoriteButton, ON_PRESS_EVENT_PAYLOAD)
expect(defaultProps.onPress).toHaveBeenCalledTimes(1)
......@@ -61,8 +62,8 @@ describe(FavoriteHeaderRow, () => {
it('renders done button', () => {
const { queryByTestId } = render(<FavoriteHeaderRow {...defaultProps} isEditing />)
const favoriteButton = queryByTestId('favorite-header-row/favorite-button')
const doneButton = queryByTestId('favorite-header-row/done-button')
const favoriteButton = queryByTestId(TestID.Edit)
const doneButton = queryByTestId(TestID.Done)
expect(favoriteButton).toBeFalsy()
expect(doneButton).toBeTruthy()
......@@ -71,7 +72,7 @@ describe(FavoriteHeaderRow, () => {
it('calls onPress when done button pressed', () => {
const { getByTestId } = render(<FavoriteHeaderRow {...defaultProps} isEditing />)
const doneButton = getByTestId('favorite-header-row/done-button')
const doneButton = getByTestId(TestID.Done)
fireEvent.press(doneButton, ON_PRESS_EVENT_PAYLOAD)
expect(defaultProps.onPress).toHaveBeenCalledTimes(1)
......
......@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
import { Flex, Text, TouchableArea } from 'ui/src'
import { TripleDots } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export function FavoriteHeaderRow({
title,
......@@ -22,12 +23,12 @@ export function FavoriteHeaderRow({
{isEditing ? editingTitle : title}
</Text>
{!isEditing ? (
<TouchableArea hapticFeedback hitSlop={16} testID="favorite-header-row/favorite-button" onPress={onPress}>
<TouchableArea hapticFeedback hitSlop={16} testID={TestID.Edit} onPress={onPress}>
<TripleDots color="$neutral2" size={iconSizes.icon20} strokeLinecap="round" strokeWidth={1} />
</TouchableArea>
) : (
<TouchableArea hitSlop={16} onPress={onPress}>
<Text color="$accent1" testID="favorite-header-row/done-button" variant="buttonLabel3">
<Text color="$accent1" testID={TestID.Done} variant="buttonLabel3">
{t('common.button.done')}
</Text>
</TouchableArea>
......
......@@ -17,13 +17,13 @@ import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo'
import { PollingInterval } from 'uniswap/src/constants/misc'
import { useFavoriteTokenCardQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { fromGraphQLChain } from 'uniswap/src/features/chains/utils'
import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils'
import { SectionName } from 'uniswap/src/features/telemetry/constants'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { getSymbolDisplayText } from 'uniswap/src/utils/currency'
import { NumberType } from 'utilities/src/format/types'
import { RelativeChange } from 'wallet/src/components/text/RelativeChange'
import { isNonPollingRequestInFlight } from 'wallet/src/data/utils'
import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils'
import { removeFavoriteToken } from 'wallet/src/features/favorites/slice'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
......
......@@ -67,7 +67,7 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = `
}
}
suppressHighlighting={true}
testID="favorite-header-row/done-button"
testID="done"
>
Done
</Text>
......@@ -128,7 +128,7 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
],
}
}
testID="favorite-header-row/favorite-button"
testID="edit"
>
<RNSVGSvgView
align="xMidYMid"
......
......@@ -7,7 +7,7 @@ import { Resolvers } from 'uniswap/src/data/graphql/uniswap-data-api/__generated
import { SectionName } from 'uniswap/src/features/telemetry/constants'
import { FavoritesState } from 'wallet/src/features/favorites/slice'
import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types'
import { SAMPLE_SEED_ADDRESS_1 } from 'wallet/src/test/fixtures/constants'
import { SAMPLE_SEED_ADDRESS_1 } from 'wallet/src/test/fixtures'
import { cleanup } from 'wallet/src/test/test-utils'
const tokenId = SAMPLE_SEED_ADDRESS_1
......
......@@ -3,13 +3,14 @@ import { useAppDispatch } from 'src/app/hooks'
import { getBlockExplorerIcon } from 'src/components/icons/BlockExplorerIcon'
import { Flex, ImpactFeedbackStyle, Text, TouchableArea, useSporeColors } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { shortenAddress } from 'uniswap/src/utils/addresses'
import { openUri } from 'uniswap/src/utils/linking'
import { Arrow } from 'wallet/src/components/icons/Arrow'
import { EtherscanSearchResult } from 'wallet/src/features/search/SearchResult'
import { addToSearchHistory } from 'wallet/src/features/search/searchHistorySlice'
import { ExplorerDataType, getExplorerLink, openUri } from 'wallet/src/utils/linking'
import { ExplorerDataType, getExplorerLink } from 'wallet/src/utils/linking'
type SearchEtherscanItemProps = {
etherscanResult: EtherscanSearchResult
......@@ -37,7 +38,7 @@ export function SearchEtherscanItem({ etherscanResult }: SearchEtherscanItemProp
<TouchableArea
hapticFeedback
hapticStyle={ImpactFeedbackStyle.Light}
testID={ElementName.SearchEtherscanItem}
testID={TestID.SearchEtherscanItem}
onPress={onPressViewEtherscan}
>
<Flex row alignItems="center" gap="$spacing12" justifyContent="space-between" px="$spacing8" py="$spacing12">
......
......@@ -4,8 +4,9 @@ import { useAppStackNavigation } from 'src/app/navigation/types'
import { Flex, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { Verified } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { ElementName, MobileEventName } from 'uniswap/src/features/telemetry/constants'
import { MobileEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { NFTViewer } from 'wallet/src/features/images/NFTViewer'
import { SearchContext } from 'wallet/src/features/search/SearchContext'
......@@ -58,7 +59,7 @@ export function SearchNFTCollectionItem({ collection, searchContext }: NFTCollec
<TouchableArea
hapticFeedback
hapticStyle={ImpactFeedbackStyle.Light}
testID={ElementName.SearchNFTCollectionItem}
testID={TestID.SearchNFTCollectionItem}
onPress={onPress}
>
<Flex row alignItems="center" gap="$spacing8" justifyContent="flex-start" px="$spacing8" py="$spacing12">
......
......@@ -8,8 +8,9 @@ import { Flex, ImpactFeedbackStyle, Text, TouchableArea, useIsDarkMode } from 'u
import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo'
import WarningIcon from 'uniswap/src/components/icons/WarningIcon'
import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { ElementName, MobileEventName, SectionName } from 'uniswap/src/features/telemetry/constants'
import { MobileEventName, SectionName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { shortenAddress } from 'uniswap/src/utils/addresses'
import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId'
import { SearchContext } from 'wallet/src/features/search/SearchContext'
......@@ -70,7 +71,7 @@ export function SearchTokenItem({ token, searchContext }: SearchTokenItemProps):
<TouchableArea
hapticFeedback
hapticStyle={ImpactFeedbackStyle.Light}
testID={ElementName.SearchTokenItem}
testID={TestID.SearchTokenItem}
onLongPress={disableOnPress}
onPress={onPress}
>
......
......@@ -9,11 +9,11 @@ import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal
import { DynamicConfigs } from 'uniswap/src/features/gating/configs'
import { useDynamicConfig } from 'uniswap/src/features/gating/hooks'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { openUri } from 'uniswap/src/utils/linking'
import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal'
import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types'
import { SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types'
import { useSignerAccounts } from 'wallet/src/features/wallet/hooks'
import { openUri } from 'wallet/src/utils/linking'
export function ForceUpgradeModal(): JSX.Element {
const { t } = useTranslation()
......
......@@ -7,7 +7,8 @@ import { MnemonicDisplay } from 'src/components/mnemonic/MnemonicDisplay'
import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks'
import { useWalletRestore } from 'src/features/wallet/hooks'
import { Button, Flex } from 'ui/src'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal'
import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types'
......@@ -68,7 +69,7 @@ export function SeedPhraseDisplay({ mnemonicId, onDismiss, walletNeedsRestore }:
)}
</Flex>
<Flex borderTopColor="$surface3" borderTopWidth={1} pt="$spacing12" px="$spacing16">
<Button testID={ElementName.Next} theme="secondary" onPress={(): void => setShowSeedPhrase(!showSeedPhrase)}>
<Button testID={TestID.Next} theme="secondary" onPress={(): void => setShowSeedPhrase(!showSeedPhrase)}>
{showSeedPhrase ? t('setting.recoveryPhrase.action.hide') : t('setting.recoveryPhrase.account.show')}
</Button>
</Flex>
......
......@@ -4,7 +4,7 @@ import { LayoutChangeEvent } from 'react-native'
import Markdown, { MarkdownProps } from 'react-native-markdown-display'
import { Flex, SpaceTokens, Text, useSporeColors } from 'ui/src'
import { fonts } from 'ui/src/theme'
import { openUri } from 'wallet/src/utils/linking'
import { openUri } from 'uniswap/src/utils/linking'
type LongMarkdownTextProps = {
initialDisplayedLines?: number
......
......@@ -8,10 +8,11 @@ import { AlertTriangle } from 'ui/src/components/icons'
import { fonts, spacing } from 'ui/src/theme'
import { TextInput } from 'uniswap/src/components/input/TextInput'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { useUnitagUpdater } from 'uniswap/src/features/unitags/context'
import { UnitagErrorCodes } from 'uniswap/src/features/unitags/types'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { logger } from 'utilities/src/logger/logger'
import { isIOS } from 'utilities/src/platform'
import { useAsyncData } from 'utilities/src/react/hooks'
......@@ -268,7 +269,7 @@ export function ChangeUnitagModal({
<Button
fill
disabled={isSubmitButtonDisabled}
testID={ElementName.Confirm}
testID={TestID.Confirm}
theme="primary"
onPress={onPressSaveChanges}
>
......@@ -315,10 +316,10 @@ function ChangeUnitagConfirmModal({
{t('unitags.editUsername.confirm.subtitle')}
</Text>
<Flex centered row gap="$spacing12" pt="$spacing24">
<Button fill testID={ElementName.Remove} theme="secondary" onPress={onClose}>
<Button fill testID={TestID.Remove} theme="secondary" onPress={onClose}>
{t('common.button.back')}
</Button>
<Button fill testID={ElementName.Remove} theme="detrimental" onPress={onChangeSubmit}>
<Button fill testID={TestID.Remove} theme="detrimental" onPress={onChangeSubmit}>
{t('common.button.confirm')}
</Button>
</Flex>
......
......@@ -6,9 +6,10 @@ import { Button, Flex, Text, useSporeColors } from 'ui/src'
import { AlertTriangle } from 'ui/src/components/icons'
import { fonts } from 'ui/src/theme'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { useUnitagUpdater } from 'uniswap/src/features/unitags/context'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { logger } from 'utilities/src/logger/logger'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types'
......@@ -101,7 +102,7 @@ export function DeleteUnitagModal({
{t('unitags.delete.confirm.subtitle')}
</Text>
<Flex centered row gap="$spacing12" pt="$spacing24">
<Button fill disabled={isDeleting} testID={ElementName.Remove} theme="detrimental" onPress={onDelete}>
<Button fill disabled={isDeleting} testID={TestID.Remove} theme="detrimental" onPress={onDelete}>
{isDeleting ? (
<Flex height={fonts.buttonLabel1.lineHeight}>
<ActivityIndicator color={colors.sporeWhite.val} />
......
......@@ -8,8 +8,9 @@ import { Flex, Image, Text, TouchableArea, TouchableAreaProps, useIsDarkMode, us
import { UNITAGS_BANNER_VERTICAL_DARK, UNITAGS_BANNER_VERTICAL_LIGHT } from 'ui/src/assets'
import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions'
import { iconSizes } from 'ui/src/theme'
import { ElementName, ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { MobileScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile'
import { selectHasCompletedUnitagsIntroModal } from 'wallet/src/features/behaviorHistory/selectors'
import { setHasSkippedUnitagPrompt } from 'wallet/src/features/behaviorHistory/slice'
......@@ -122,7 +123,7 @@ export function UnitagBanner({
</Flex>
<Flex row gap="$spacing2">
{/* TODO: replace with Button when it's extensible enough to accommodate designs */}
<TouchableArea {...baseButtonStyle} testID={ElementName.Confirm} onPress={onPressClaimNow}>
<TouchableArea {...baseButtonStyle} testID={TestID.Confirm} onPress={onPressClaimNow}>
<Text color="white" variant="buttonLabel4">
{t('unitags.banner.button.claim')}
</Text>
......@@ -130,7 +131,7 @@ export function UnitagBanner({
<TouchableArea
{...baseButtonStyle}
backgroundColor="$transparent"
testID={ElementName.Cancel}
testID={TestID.Cancel}
onPress={onPressMaybeLater}
>
<Text color="$neutral2" variant="buttonLabel4">
......
......@@ -6,7 +6,7 @@ import { PasswordError } from 'src/features/onboarding/PasswordError'
import { Button, Flex, Text } from 'ui/src'
import { DiamondExclamation } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { useDebounce } from 'utilities/src/time/timing'
import {
PASSWORD_VALIDATION_DEBOUNCE_MS,
......@@ -118,7 +118,7 @@ export function CloudBackupPasswordForm({
</Flex>
)}
</Flex>
<Button disabled={isButtonDisabled} testID={ElementName.Next} onPress={onPressNext}>
<Button disabled={isButtonDisabled} testID={TestID.Next} onPress={onPressNext}>
{t('common.button.continue')}
</Button>
</>
......
......@@ -7,6 +7,8 @@ import { FeatureFlags, WALLET_FEATURE_FLAG_NAMES } from 'uniswap/src/features/ga
import { Statsig } from 'uniswap/src/features/gating/sdk/statsig'
import { MobileEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import i18n from 'uniswap/src/i18n/i18n'
import { openUri } from 'uniswap/src/utils/linking'
import { logger } from 'utilities/src/logger/logger'
import { isAndroid } from 'utilities/src/platform'
import { ONE_DAY_MS, ONE_SECOND_MS } from 'utilities/src/time/time'
......@@ -15,7 +17,6 @@ import { TransactionStatus, TransactionType } from 'wallet/src/features/transact
import { selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors'
import { setAppRating } from 'wallet/src/features/wallet/slice'
import { appSelect } from 'wallet/src/state'
import { openUri } from 'wallet/src/utils/linking'
// at most once per reminder period (120 days)
const MIN_PROMPT_REMINDER_MS = 120 * ONE_DAY_MS
......@@ -136,14 +137,14 @@ function* maybeRequestAppRating() {
*/
async function openRatingOptionsAlert() {
return new Promise((resolve) => {
Alert.alert('Enjoying Uniswap Wallet?', "Let us know if you're having a good experience with this app", [
Alert.alert(i18n.t('mobile.appRating.title'), i18n.t('mobile.appRating.description'), [
{
text: 'Not really',
text: i18n.t('mobile.appRating.button.decline'),
onPress: () => resolve(false),
style: 'cancel',
},
{
text: 'Yes',
text: i18n.t('common.button.yes'),
onPress: () => {
openNativeReviewModal().catch((e) =>
logger.error(e, {
......@@ -161,9 +162,9 @@ async function openRatingOptionsAlert() {
/** Opens feedback request modal which will redirect to our feedback form. */
async function openFeedbackRequestAlert() {
return new Promise((resolve) => {
Alert.alert("We're sorry to hear that.", 'Let us know how we can improve your experience', [
Alert.alert(i18n.t('mobile.appRating.feedback.title'), i18n.t('mobile.appRating.feedback.description'), [
{
text: 'Send feedback',
text: i18n.t('mobile.appRating.feedback.button.send'),
onPress: () => {
openUri(APP_FEEDBACK_LINK).catch((e) =>
logger.error(e, { tags: { file: 'appRating/saga', function: 'openFeedbackAlert' } }),
......@@ -172,7 +173,11 @@ async function openFeedbackRequestAlert() {
},
isPreferred: true,
},
{ text: 'Maybe later', onPress: () => resolve(false), style: 'cancel' },
{
text: i18n.t('mobile.appRating.feedback.button.cancel'),
onPress: () => resolve(false),
style: 'cancel',
},
])
})
}
......
......@@ -29,11 +29,12 @@ import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { ShareableEntity } from 'uniswap/src/types/sharing'
import { WidgetType } from 'uniswap/src/types/widgets'
import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId'
import { openUri } from 'uniswap/src/utils/linking'
import { logger } from 'utilities/src/logger/logger'
import { ScantasticParams } from 'wallet/src/features/scantastic/types'
import { selectAccounts, selectActiveAccount, selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors'
import { setAccountAsActive } from 'wallet/src/features/wallet/slice'
import { UNISWAP_APP_NATIVE_TOKEN, openUri } from 'wallet/src/utils/linking'
import { UNISWAP_APP_NATIVE_TOKEN } from 'wallet/src/utils/linking'
export interface DeepLink {
url: string
......
......@@ -2,9 +2,9 @@ import { URL } from 'react-native-url-polyfill'
import { expectSaga } from 'redux-saga-test-plan'
import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga'
import { openModal } from 'src/features/modals/modalSlice'
import { DAI, UNI } from 'uniswap/src/constants/tokens'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains'
import { DAI, UNI } from 'wallet/src/constants/tokens'
import { AssetType } from 'wallet/src/entities/assets'
import {
CurrencyField,
......
import { SharedEventName } from '@uniswap/analytics-events'
import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { NativeSyntheticEvent, Share } from 'react-native'
......@@ -9,16 +10,18 @@ import { Flex, HapticFeedback, TouchableArea } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { ElementName, WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { ShareableEntity } from 'uniswap/src/types/sharing'
import { openUri } from 'uniswap/src/utils/linking'
import { logger } from 'utilities/src/logger/logger'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types'
import { setClipboard } from 'wallet/src/utils/clipboard'
import { ExplorerDataType, getExplorerLink, getProfileUrl, openUri } from 'wallet/src/utils/linking'
import { ExplorerDataType, getExplorerLink, getProfileUrl } from 'wallet/src/utils/linking'
type MenuAction = {
title: string
......@@ -38,6 +41,10 @@ export function ProfileContextMenu({ address }: { address: Address }): JSX.Eleme
await HapticFeedback.impact()
await setClipboard(address)
dispatch(pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address }))
sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, {
element: ElementName.CopyAddress,
screen: MobileScreens.ExternalProfile,
})
}, [address, dispatch])
const openExplorerLink = useCallback(async () => {
......
......@@ -26,14 +26,15 @@ import { ENS_LOGO } from 'ui/src/assets'
import { SendAction, XTwitter } from 'ui/src/components/icons'
import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import { iconSizes, imageSizes } from 'ui/src/theme'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { openUri } from 'uniswap/src/utils/linking'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
import { useENSDescription, useENSName, useENSTwitterUsername } from 'wallet/src/features/ens/api'
import { selectWatchedAddressSet } from 'wallet/src/features/favorites/selectors'
import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types'
import { useAvatar, useDisplayName } from 'wallet/src/features/wallet/hooks'
import { DisplayNameType } from 'wallet/src/features/wallet/types'
import { openUri } from 'wallet/src/utils/linking'
const HEADER_GRADIENT_HEIGHT = 144
const HEADER_ICON_SIZE = 72
......@@ -217,7 +218,7 @@ export const ProfileHeader = memo(function ProfileHeader({ address }: ProfileHea
p="$spacing12"
shadowColor="$neutral1"
style={styles.buttonShadow}
testID={ElementName.Favorite}
testID={TestID.Favorite}
onPress={onPressFavorite}
>
<Favorite isFavorited={isFavorited} size={iconSizes.icon20} />
......@@ -234,7 +235,7 @@ export const ProfileHeader = memo(function ProfileHeader({ address }: ProfileHea
px="$spacing12"
shadowColor={isDarkMode ? '$surface2' : '$neutral3'}
style={styles.buttonShadow}
testID={ElementName.Send}
testID={TestID.Send}
onPress={onPressSend}
>
<Flex row alignItems="center" gap="$spacing8">
......
......@@ -16,6 +16,7 @@ import { fonts, spacing } from 'ui/src/theme'
import { Pill } from 'uniswap/src/components/pill/Pill'
import { SelectTokenButton } from 'uniswap/src/features/fiatOnRamp/SelectTokenButton'
import { FiatCurrencyInfo, FiatOnRampCurrency } from 'uniswap/src/features/fiatOnRamp/types'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { usePrevious } from 'utilities/src/react/hooks'
import { DEFAULT_DELAY, useDebounce } from 'utilities/src/time/timing'
import { AmountInput } from 'wallet/src/components/input/AmountInput'
......@@ -191,6 +192,7 @@ export function FiatOnRampAmountSection({
formattedAmount={formattedAmount}
loading={selectTokenLoading}
selectedCurrencyInfo={currency.currencyInfo}
testID={TestID.TokenSelectorToggle}
onPress={onTokenSelectorPress}
/>
)}
......
......@@ -24,6 +24,7 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { UniverseEventProperties } from 'uniswap/src/features/telemetry/types'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { buildCurrencyId } from 'uniswap/src/utils/currencyId'
import { openUri } from 'uniswap/src/utils/linking'
import { NumberType } from 'utilities/src/format/types'
import { useTimeout } from 'utilities/src/time/timing'
import { DecimalPadLegacy } from 'wallet/src/components/legacy/DecimalPadLegacy'
......@@ -31,7 +32,6 @@ import { useLocalFiatToUSDConverter } from 'wallet/src/features/fiatCurrency/hoo
import { useMoonpayFiatCurrencySupportInfo } from 'wallet/src/features/fiatOnRamp/hooks'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo'
import { openUri } from 'wallet/src/utils/linking'
const MOONPAY_UNSUPPORTED_REGION_HELP_URL =
'https://support.uniswap.org/hc/en-us/articles/11306664890381-Why-isn-t-MoonPay-available-in-my-region-'
......
......@@ -3,7 +3,7 @@ import { LayoutChangeEvent } from 'react-native'
import { InputWithSuffixProps } from 'src/features/import/InputWIthSuffixProps'
import { Flex } from 'ui/src'
import { TextInput } from 'uniswap/src/components/input/TextInput'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { isIOS } from 'utilities/src/platform'
const EPS = 1
......@@ -96,7 +96,7 @@ export default function InputWithSuffix({
returnKeyType="done"
scrollEnabled={false}
spellCheck={false}
testID={ElementName.ImportAccountInput}
testID={TestID.ImportAccountInput}
textAlign={isInputEmpty ? 'left' : textInputAlignment}
textAlignVertical={isInputEmpty ? 'center' : 'bottom'}
value={value}
......
import { InputWithSuffixProps } from 'src/features/import/InputWIthSuffixProps'
import { Flex } from 'ui/src'
import { TextInput } from 'uniswap/src/components/input/TextInput'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
export default function InputWithSuffix({
alwaysShowInputSuffix = false,
......@@ -39,7 +39,7 @@ export default function InputWithSuffix({
returnKeyType="done"
scrollEnabled={false}
spellCheck={false}
testID={ElementName.ImportAccountInput}
testID={TestID.ImportAccountInput}
textAlign={isInputEmpty ? 'left' : textInputAlignment}
textAlignVertical="bottom"
value={value}
......
......@@ -25,6 +25,7 @@ export interface ModalsState {
[ModalName.FiatOnRampAggregator]: AppModalState<undefined>
[ModalName.ReceiveCryptoModal]: AppModalState<ReceiveCryptoModalState>
[ModalName.LanguageSelector]: AppModalState<undefined>
[ModalName.QueuedOrderModal]: AppModalState<undefined>
[ModalName.RemoveWallet]: AppModalState<RemoveWalletModalState>
[ModalName.RestoreWallet]: AppModalState<undefined>
[ModalName.Scantastic]: AppModalState<ScantasticModalState>
......
......@@ -171,6 +171,10 @@ export const initialModalsState: ModalsState = {
isOpen: false,
initialState: undefined,
},
[ModalName.QueuedOrderModal]: {
isOpen: false,
initialState: undefined,
},
}
const slice = createSlice({
......
......@@ -10,8 +10,9 @@ import { iconSizes, spacing } from 'ui/src/theme'
import { WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { ShareableEntity } from 'uniswap/src/types/sharing'
import { openUri } from 'uniswap/src/utils/linking'
import { logger } from 'utilities/src/logger/logger'
import { getNftCollectionUrl, getTwitterLink, openUri } from 'wallet/src/utils/linking'
import { getNftCollectionUrl, getTwitterLink } from 'wallet/src/utils/linking'
type MenuOption = {
title: string
......
......@@ -76,10 +76,14 @@ exports[`renders collection preview card 1`] = `
"alignItems": "center",
"aspectRatio": 1,
"backgroundColor": "#F9F9F9",
"flex": 1,
"flexDirection": "column",
"justifyContent": "center",
"maxHeight": 60,
"width": "100%",
"paddingBottom": 8,
"paddingLeft": 8,
"paddingRight": 8,
"paddingTop": 8,
}
}
>
......@@ -89,10 +93,10 @@ exports[`renders collection preview card 1`] = `
style={
{
"color": "#7D7D7D",
"flex": 0,
"fontFamily": "Basel-Book",
"fontSize": 17,
"lineHeight": 24,
"textAlign": "center",
}
}
suppressHighlighting={true}
......
import { useFocusEffect } from '@react-navigation/core'
import { useHeaderHeight } from '@react-navigation/elements'
import React, { PropsWithChildren } from 'react'
import { KeyboardAvoidingView, StyleSheet } from 'react-native'
import React, { PropsWithChildren, useCallback } from 'react'
import { BackHandler, KeyboardAvoidingView, StyleSheet } from 'react-native'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import { renderHeaderBackButton } from 'src/app/navigation/components'
import { useOnboardingStackNavigation } from 'src/app/navigation/types'
import { SHORT_SCREEN_HEADER_HEIGHT_RATIO, Screen } from 'src/components/layout/Screen'
import { Flex, SpaceTokens, Text, useDeviceInsets, useMedia } from 'ui/src'
import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
......@@ -14,6 +17,7 @@ type OnboardingScreenProps = {
paddingTop?: SpaceTokens
childrenGap?: SpaceTokens
keyboardAvoidingViewEnabled?: boolean
disableGoBack?: boolean
}
export function OnboardingScreen({
......@@ -22,13 +26,29 @@ export function OnboardingScreen({
children,
paddingTop = '$none',
keyboardAvoidingViewEnabled = true,
disableGoBack = false,
}: PropsWithChildren<OnboardingScreenProps>): JSX.Element {
const navigation = useOnboardingStackNavigation()
const headerHeight = useHeaderHeight()
const insets = useDeviceInsets()
const media = useMedia()
const gapSize = media.short ? '$none' : '$spacing16'
useFocusEffect(
useCallback(() => {
navigation.setOptions({
headerLeft: disableGoBack ? (): null => null : renderHeaderBackButton,
gestureEnabled: !disableGoBack,
})
const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
return disableGoBack
})
return subscription.remove
}, [navigation, disableGoBack]),
)
return (
<Screen
$short={{ pt: headerHeight * SHORT_SCREEN_HEADER_HEIGHT_RATIO }}
......
......@@ -3,6 +3,7 @@ import { Flex, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementNameType } from 'uniswap/src/features/telemetry/constants'
import { TestIDType } from 'uniswap/src/test/fixtures/testIDs'
export function OptionCard({
title,
......@@ -14,12 +15,14 @@ export function OptionCard({
opacity,
badgeText,
hapticFeedback,
testID,
}: {
title: string
blurb: string
icon: React.ReactNode
onPress: () => void
elementName: ElementNameType
testID: TestIDType
disabled?: boolean
opacity?: number
badgeText?: string | undefined
......@@ -38,7 +41,7 @@ export function OptionCard({
hapticFeedback={hapticFeedback}
opacity={disabled ? 0.5 : opacity}
p="$spacing16"
testID={elementName}
testID={testID}
onPress={onPress}
>
<Flex row alignContent="center" alignItems="flex-start" gap="$spacing16">
......
......@@ -29,7 +29,7 @@ export function useCompleteOnboardingCallback({
return async () => {
// Run all shared onboarding completion logic
await finishOnboarding(importType)
await finishOnboarding({ importType })
// Send appsflyer event for mobile attribution
if (entryPoint === OnboardingEntryPoint.FreshInstallOrReplace) {
......
// eslint-disable-next-line no-restricted-imports
import { OriginApplication } from '@uniswap/analytics'
import DeviceInfo, { getUniqueId } from 'react-native-device-info'
import { call, delay, fork, select, takeEvery } from 'typed-redux-saga'
import { call, delay, fork, select } from 'typed-redux-saga'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { ApplicationTransport } from 'utilities/src/telemetry/analytics/ApplicationTransport'
import { selectAllowAnalytics } from 'wallet/src/features/telemetry/selectors'
// eslint-disable-next-line no-restricted-imports
import { analytics } from 'utilities/src/telemetry/analytics/analytics'
import { transactionActions } from 'wallet/src/features/transactions/slice'
import { logTransactionEvent } from 'wallet/src/features/transactions/transactionWatcherSaga'
import { watchTransactionEvents } from 'wallet/src/features/transactions/transactionWatcherSaga'
export function* telemetrySaga() {
yield* delay(1)
......@@ -27,8 +26,3 @@ export function* telemetrySaga() {
)
yield* fork(watchTransactionEvents)
}
function* watchTransactionEvents() {
// Watch for finalized transactions to send analytics events
yield* takeEvery(transactionActions.finalizeTransaction.type, logTransactionEvent)
}
......@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
import { StatusAnimation } from 'src/features/transactions/TransactionPending/StatusAnimation'
import { Button, Flex, Text, TouchableArea } from 'ui/src'
import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { WalletChainId } from 'uniswap/src/types/chains'
import { TransactionDetails, TransactionStatus, isFinalizedTx } from 'wallet/src/features/transactions/types'
import { openTransactionLink } from 'wallet/src/utils/linking'
......@@ -60,7 +60,7 @@ export function TransactionPending({
{t('swap.button.view')}
</Button>
) : null}
<Button testID={ElementName.OK} onPress={onNext}>
<Button testID={TestID.OK} onPress={onNext}>
{t('common.button.close')}
</Button>
</Flex>
......
......@@ -36,7 +36,7 @@ import { TransferReview } from 'wallet/src/features/transactions/transfer/Transf
import { TransferTokenForm } from 'wallet/src/features/transactions/transfer/TransferTokenForm'
import { useDerivedTransferInfo } from 'wallet/src/features/transactions/transfer/hooks/useDerivedTransferInfo'
import { useOnSelectRecipient } from 'wallet/src/features/transactions/transfer/hooks/useOnSelectRecipient'
import { useOnToggleShowRecipientSelector } from 'wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector'
import { useSetShowRecipientSelector } from 'wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector'
import {
useTransferERC20Callback,
useTransferNFTCallback,
......@@ -67,7 +67,11 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS
const { showRecipientSelector } = state
const onSelectRecipient = useOnSelectRecipient(dispatch)
const onToggleShowRecipientSelector = useOnToggleShowRecipientSelector(dispatch)
const onSetShowRecipientSelector = useSetShowRecipientSelector(dispatch)
const onHideRecipientSelector = useCallback(() => {
onSetShowRecipientSelector(false)
}, [onSetShowRecipientSelector])
const txRequest = useTransferTransactionRequest(derivedTransferInfo)
const warnings = useTransferWarnings(t, derivedTransferInfo)
......@@ -153,9 +157,10 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS
<Flex fill>
<Animated.View style={[styles.screen, recipientScreenStyle]}>
<RecipientSelect
focusInput={showRecipientSelector}
recipient={recipient}
onHideRecipientSelector={onHideRecipientSelector}
onSelectRecipient={onSelectRecipient}
onToggleShowRecipientSelector={onToggleShowRecipientSelector}
/>
</Animated.View>
......
......@@ -11,8 +11,8 @@ import { UnitagName } from 'src/features/unitags/UnitagName'
import { Button, Flex, Text, useIsDarkMode, useSporeColors } from 'ui/src'
import { Pen } from 'ui/src/components/icons'
import { fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { UnitagClaimSource } from 'uniswap/src/features/unitags/types'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { MobileScreens, OnboardingScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile'
......@@ -129,7 +129,7 @@ export function ChooseProfilePictureScreen({
p="$spacing4"
position="absolute"
right={-spacing.spacing2}
testID={ElementName.Edit}
testID={TestID.Edit}
>
<Flex backgroundColor={isDarkMode ? '$neutral3' : '$neutral2'} borderRadius="$roundedFull" p={8}>
<Pen color={isDarkMode ? '$neutral1' : '$surface1'} size={iconSizes.icon16} />
......@@ -148,7 +148,7 @@ export function ChooseProfilePictureScreen({
<Button
disabled={!!claimError || isClaiming}
size="medium"
testID={ElementName.Continue}
testID={TestID.Continue}
theme="primary"
onPress={onPressContinue}
>
......
......@@ -16,17 +16,18 @@ import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import { fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme'
import { TextInput } from 'uniswap/src/components/input/TextInput'
import { Pill } from 'uniswap/src/components/pill/Pill'
import { LearnMoreLink } from 'uniswap/src/components/text/LearnMoreLink'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ModalName, UnitagEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { MobileScreens, OnboardingScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile'
import { shortenAddress } from 'uniswap/src/utils/addresses'
import { logger } from 'utilities/src/logger/logger'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal'
import { LearnMoreLink } from 'wallet/src/components/text/LearnMoreLink'
import {
useCreateOnboardingAccountIfNone,
useOnboardingContext,
......@@ -282,7 +283,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element {
placeholder={inputPlaceholder}
placeholderTextColor="$neutral3"
returnKeyType="done"
testID={ElementName.WalletNameInput}
testID={TestID.WalletNameInput}
textAlign="left"
value={unitagInputValue}
onChangeText={onChangeTextInput}
......@@ -342,7 +343,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element {
<Flex gap="$spacing24" justifyContent="flex-end">
{entryPoint === OnboardingScreens.Landing && (
<Trace logPress element={ElementName.Skip}>
<TouchableArea testID={ElementName.Skip} onPress={onPressMaybeLater}>
<TouchableArea testID={TestID.Skip} onPress={onPressMaybeLater}>
<Text color="$accent1" textAlign="center" variant="buttonLabel2">
{t('common.button.later')}
</Text>
......@@ -357,7 +358,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element {
shouldBlockContinue
}
size="medium"
testID={ElementName.Continue}
testID={TestID.Continue}
theme="primary"
onPress={onPressContinue}
>
......
import { NativeModules } from 'react-native'
import { getItem, reloadAllTimelines, setItem } from 'react-native-widgetkit'
import { getBuildVariant } from 'src/utils/version'
import { currencyIdToContractInput } from 'uniswap/src/features/dataApi/utils'
import { MobileEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { CurrencyId } from 'uniswap/src/types/currency'
......@@ -8,7 +9,6 @@ import { WidgetEvent } from 'uniswap/src/types/widgets'
import { isAndroid } from 'utilities/src/platform'
// eslint-disable-next-line no-restricted-imports
import { analytics } from 'utilities/src/telemetry/analytics/analytics'
import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils'
import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types'
const APP_GROUP = 'group.com.uniswap.widgets'
......
......@@ -96,7 +96,7 @@ function useFinishAutomatedRecovery(navigation: Props['navigation']): {
entryPoint: OnboardingEntryPoint.FreshInstallOrReplace,
})
} else {
await finishOnboarding(ImportType.OnDeviceRecovery)
await finishOnboarding({ importType: ImportType.OnDeviceRecovery })
dispatch(setFinishedOnboarding({ finishedOnboarding: true }))
}
},
......
......@@ -13,13 +13,13 @@ import { getServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils'
import { InstitutionTransferEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { openUri } from 'uniswap/src/utils/linking'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useTimeout } from 'utilities/src/time/timing'
import { ImageUri } from 'wallet/src/features/images/ImageUri'
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
......
......@@ -19,6 +19,7 @@ import { FiatOnRampEventName, ModalName } from 'uniswap/src/features/telemetry/c
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { FiatOnRampScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useTimeout } from 'utilities/src/time/timing'
import { ImageUri } from 'wallet/src/features/images/ImageUri'
......@@ -27,7 +28,6 @@ import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types'
import { forceFetchFiatOnRampTransactions } from 'wallet/src/features/transactions/slice'
import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks'
import { openUri } from 'wallet/src/utils/linking'
// Design decision
const CONNECTING_TIMEOUT = 2 * ONE_SECOND_MS
......
......@@ -43,7 +43,6 @@ import {
import { UnitagBanner } from 'src/components/unitags/UnitagBanner'
import { openModal } from 'src/features/modals/modalSlice'
import { selectSomeModalOpen } from 'src/features/modals/selectSomeModalOpen'
import { useHeartbeatReporter, useLastBalancesReporter } from 'src/features/telemetry/hooks'
import { useWalletRestore } from 'src/features/wallet/hooks'
import { removePendingSession } from 'src/features/walletConnect/walletConnectSlice'
import { HomeScreenTabIndex } from 'src/screens/HomeScreenTabIndex'
......@@ -68,8 +67,7 @@ import {
SectionNameType,
} from 'uniswap/src/features/telemetry/constants'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useInterval, useTimeout } from 'utilities/src/time/timing'
import { useTimeout } from 'utilities/src/time/timing'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { selectHasSkippedUnitagPrompt } from 'wallet/src/features/behaviorHistory/selectors'
import { useCexTransferProviders } from 'wallet/src/features/fiatOnRamp/api'
......@@ -77,6 +75,7 @@ import { useSelectAddressHasNotifications } from 'wallet/src/features/notificati
import { setNotificationStatus } from 'wallet/src/features/notifications/slice'
import { PortfolioBalance } from 'wallet/src/features/portfolio/PortfolioBalance'
import { TokenBalanceListRow } from 'wallet/src/features/portfolio/TokenBalanceListContext'
import { useHeartbeatReporter, useLastBalancesReporter } from 'wallet/src/features/telemetry/hooks'
import { useCanActiveAddressClaimUnitag, useShowExtensionPromoBanner } from 'wallet/src/features/unitags/hooks'
import { AccountType } from 'wallet/src/features/wallet/accounts/types'
import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks'
......@@ -107,12 +106,9 @@ export function HomeScreen(props?: AppStackScreenProp<MobileScreens.Home>): JSX.
useWalletRestore({ openModalImmediately: true })
// Record a heartbeat for anonymous user DAU
const heartbeatReporter = useHeartbeatReporter()
useInterval(heartbeatReporter, ONE_SECOND_MS * 15, true)
useHeartbeatReporter()
// Report balances at most every 24 hours, checking every 15 seconds when app is open
const lastBalancesReporter = useLastBalancesReporter()
useInterval(lastBalancesReporter, ONE_SECOND_MS * 15, true)
useLastBalancesReporter()
const [tabIndex, setTabIndex] = useState(props?.route?.params?.tab ?? HomeScreenTabIndex.Tokens)
// Necessary to declare these as direct dependencies due to race condition with initializing react-i18next and useMemo
......
......@@ -15,6 +15,7 @@ import { AppTFunction } from 'ui/src/i18n/types'
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ElementNameType } from 'uniswap/src/features/telemetry/constants'
import { TestID, TestIDType } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { isAndroid } from 'utilities/src/platform'
......@@ -27,6 +28,7 @@ interface ImportMethodOption {
nav: OnboardingScreens
importType: ImportType
name: ElementNameType
testID: TestIDType
}
const options: ImportMethodOption[] = [
......@@ -37,6 +39,7 @@ const options: ImportMethodOption[] = [
nav: OnboardingScreens.SeedPhraseInput,
importType: ImportType.SeedPhrase,
name: ElementName.OnboardingImportSeedPhrase,
testID: TestID.OnboardingImportSeedPhrase,
},
{
title: (t: AppTFunction) => t('onboarding.import.method.restore.title'),
......@@ -48,6 +51,7 @@ const options: ImportMethodOption[] = [
nav: OnboardingScreens.RestoreCloudBackup,
importType: ImportType.Restore,
name: ElementName.RestoreFromCloud,
testID: TestID.RestoreFromCloud,
},
]
......@@ -116,20 +120,21 @@ export function ImportMethodScreen({ navigation, route: { params } }: Props): JS
shadowColor="$surface3"
shadowRadius={!isDarkMode ? '$spacing8' : undefined}
>
{importOptions.map(({ title, blurb, icon, nav, importType, name }, i) => (
{importOptions.map(({ title, blurb, icon, nav, importType, name, testID }, i) => (
<OptionCard
key={'connection-option-' + name + i}
hapticFeedback
blurb={blurb(t)}
elementName={name}
icon={icon}
testID={testID}
title={title(t)}
onPress={(): Promise<void> => handleOnPress(nav, importType)}
/>
))}
</Flex>
<Trace logPress element={ElementName.OnboardingImportBackup}>
<TouchableArea alignItems="center" hitSlop={16} mb="$spacing12" testID={ElementName.WatchWallet}>
<TouchableArea alignItems="center" hitSlop={16} mb="$spacing12" testID={TestID.WatchWallet}>
<Flex row alignItems="center" gap="$spacing8">
<EyeIcon
color={colors.accent1.get()}
......
......@@ -19,7 +19,8 @@ import { iconSizes } from 'ui/src/theme'
import { DynamicConfigs } from 'uniswap/src/features/gating/configs'
import { useDynamicConfig } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { logger } from 'utilities/src/logger/logger'
......@@ -214,7 +215,7 @@ export function OnDeviceRecoveryScreen({
<Text color="$neutral3" variant="body3" onPress={onPressOtherWallet}>
{t('onboarding.import.onDeviceRecovery.other_options.label')}
</Text>
<TouchableArea alignItems="center" hitSlop={16} mb="$spacing12" testID={ElementName.WatchWallet}>
<TouchableArea alignItems="center" hitSlop={16} mb="$spacing12" testID={TestID.WatchWallet}>
<Text color="$accent1" variant="buttonLabel3" onPress={onPressOtherWallet}>
{t('onboarding.import.onDeviceRecovery.other_options')}
</Text>
......
......@@ -18,7 +18,7 @@ import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { PasswordError } from 'src/features/onboarding/PasswordError'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Button, Flex, Text, TouchableArea } from 'ui/src'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
......@@ -169,7 +169,7 @@ export function RestoreCloudBackupPasswordScreen({ navigation, route: { params }
</Text>
</TouchableArea>
)}
<Button disabled={!enteredPassword || isLockedOut} testID={ElementName.Submit} onPress={onPasswordSubmit}>
<Button disabled={!enteredPassword || isLockedOut} testID={TestID.Continue} onPress={onPasswordSubmit}>
{t('common.button.continue')}
</Button>
</Flex>
......
......@@ -13,11 +13,11 @@ import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { ImportType } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring'
import { BackupType } from 'wallet/src/features/wallet/accounts/types'
import { useSignerAccounts } from 'wallet/src/features/wallet/hooks'
import { openUri } from 'wallet/src/utils/linking'
import {
MnemonicValidationError,
translateMnemonicErrorMessage,
......
......@@ -21,10 +21,10 @@ import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { ImportType } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
import { BackupType } from 'wallet/src/features/wallet/accounts/types'
import { useSignerAccounts } from 'wallet/src/features/wallet/hooks'
import { openUri } from 'wallet/src/utils/linking'
type Props = NativeStackScreenProps<OnboardingStackParamList, OnboardingScreens.SeedPhraseInput>
......
......@@ -8,6 +8,7 @@ import { Button, Flex, Loader } from 'ui/src'
import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard'
import { useSelectWalletScreenQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
......@@ -137,7 +138,7 @@ export function SelectWalletScreen({ navigation, route: { params } }: Props): JS
hideSelectionCircle={isOnlyOneAccount}
name={ElementName.WalletCard}
selected={selectedAddresses.includes(ownerAddress)}
testID={`${ElementName.WalletCard}-${i + 1}`}
testID={`${TestID.WalletCard}-${i + 1}`}
onSelect={toggleAddressSelection}
/>
)
......@@ -148,7 +149,7 @@ export function SelectWalletScreen({ navigation, route: { params } }: Props): JS
<Flex opacity={showError ? 0 : 1}>
<Button
disabled={isImportingAccounts || isLoading || !!showError || selectedAddresses.length === 0}
testID={ElementName.Next}
testID={TestID.Next}
onPress={onSubmit}
>
{t('common.button.continue')}
......
......@@ -14,6 +14,7 @@ import { Button, Flex, Text } from 'ui/src'
import { GraduationCap } from 'ui/src/components/icons'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { areAddressesEqual, getValidAddress } from 'uniswap/src/utils/addresses'
......@@ -184,7 +185,7 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX
</Text>
</Flex>
</Flex>
<Button disabled={!isValid} testID={ElementName.Next} onPress={onSubmit}>
<Button disabled={!isValid} testID={TestID.Next} onPress={onSubmit}>
{t('common.button.continue')}
</Button>
</SafeKeyboardOnboardingScreen>
......
......@@ -304,7 +304,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
"paddingTop": 12,
}
}
testID="submit"
testID="continue"
userSelect="none"
>
<Text
......
......@@ -35,6 +35,7 @@ import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { UniverseChainId } from 'uniswap/src/types/chains'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { areAddressesEqual } from 'uniswap/src/utils/addresses'
import { MIN_COLOR_CONTRAST_THRESHOLD, useNearestThemeColorFromImageUri } from 'uniswap/src/utils/colors'
import { isAndroid, isIOS } from 'utilities/src/platform'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
import { NFTViewer } from 'wallet/src/features/images/NFTViewer'
......@@ -44,7 +45,6 @@ import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types'
import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks'
import { setClipboardImage } from 'wallet/src/utils/clipboard'
import { MIN_COLOR_CONTRAST_THRESHOLD, useNearestThemeColorFromImageUri } from 'wallet/src/utils/colors'
const MAX_NFT_IMAGE_HEIGHT = 375
......
......@@ -16,6 +16,7 @@ import { OSDynamicCloudIcon, QuestionInCircleFilled } from 'ui/src/components/ic
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType } from 'uniswap/src/types/onboarding'
import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
......@@ -116,7 +117,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem
}
const showSkipOption =
!hasBackup(address) && (params?.importType === ImportType.SeedPhrase || params?.importType === ImportType.Restore)
hasBackup(address) && (params?.importType === ImportType.SeedPhrase || params?.importType === ImportType.Restore)
const hasCloudBackup = hasBackup(address, BackupType.Cloud)
const hasManualBackup = hasBackup(address, BackupType.Manual)
......@@ -131,6 +132,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem
disabled={hasCloudBackup}
elementName={ElementName.AddCloudBackup}
icon={<OSDynamicCloudIcon color="$accent1" size="$icon.16" />}
testID={TestID.AddCloudBackup}
title={t('onboarding.backup.option.cloud.title', {
cloudProviderName: getCloudProviderName(),
})}
......@@ -145,6 +147,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem
disabled={hasManualBackup}
elementName={ElementName.AddManualBackup}
icon={<PaperIcon color={colors.accent1.get()} height={iconSizes.icon16} />}
testID={TestID.AddManualBackup}
title={t('onboarding.backup.option.manual.title')}
onPress={onPressManualBackup}
/>,
......@@ -165,7 +168,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem
{isCreatingNew && <RecoveryPhraseTooltip onPressEducationButton={onPressEducationButton} />}
{showSkipOption && (
<Trace logPress element={ElementName.Next}>
<Button testID={ElementName.Next} theme="tertiary" onPress={onPressNext}>
<Button testID={TestID.Next} theme="tertiary" onPress={onPressNext}>
{t('common.button.later')}
</Button>
</Trace>
......
......@@ -12,6 +12,7 @@ import { Flex, HapticFeedback, Text, TouchableArea } from 'ui/src'
import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { OnboardingScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile'
import { isDevEnv } from 'utilities/src/environment'
......@@ -82,6 +83,7 @@ export function LandingScreen({ navigation }: Props): JSX.Element {
shadowColor="$accent1"
shadowOpacity={0.4}
shadowRadius="$spacing8"
testID={TestID.CreateAccount}
onPress={onPressCreateWallet}
>
<Text color="$sporeWhite" variant="buttonLabel2">
......@@ -95,7 +97,7 @@ export function LandingScreen({ navigation }: Props): JSX.Element {
hapticFeedback
alignItems="center"
hitSlop={16}
testID={ElementName.ImportAccount}
testID={TestID.ImportAccount}
onLongPress={async (): Promise<void> => {
if (isDevEnv()) {
await HapticFeedback.selection()
......
......@@ -13,8 +13,9 @@ import { Button, Flex, Text, useMedia, useSporeColors } from 'ui/src'
import LockIcon from 'ui/src/assets/icons/lock.svg'
import { iconSizes } from 'ui/src/theme'
import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ManualPageViewScreen, OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
......@@ -106,7 +107,7 @@ export function ManualBackupScreen({ navigation, route: { params } }: Props): JS
{seedWarningAcknowledged ? <MnemonicDisplay mnemonicId={mnemonicId} /> : <HiddenMnemonicWordView />}
</Flex>
<Flex justifyContent="flex-end">
<Button testID={ElementName.Next} onPress={nextView}>
<Button testID={TestID.Next} onPress={nextView}>
{t('common.button.continue')}
</Button>
</Flex>
......@@ -131,7 +132,7 @@ export function ManualBackupScreen({ navigation, route: { params } }: Props): JS
/>
</Flex>
<Flex justifyContent="flex-end">
<Button disabled={!continueButtonEnabled} testID={ElementName.Continue} onPress={onValidationSuccessful}>
<Button disabled={!continueButtonEnabled} testID={TestID.Continue} onPress={onValidationSuccessful}>
{t('common.button.continue')}
</Button>
</Flex>
......@@ -162,14 +163,7 @@ const SeedWarningModal = ({ onPress }: { onPress: () => void }): JSX.Element =>
<Text color="$neutral2" textAlign="center" variant="body2">
{t('onboarding.recoveryPhrase.warning.final.message')}
</Text>
<Button
flexGrow={1}
mt="$spacing16"
testID={ElementName.Confirm}
theme="primary"
width="100%"
onPress={onPress}
>
<Button flexGrow={1} mt="$spacing16" testID={TestID.Confirm} theme="primary" width="100%" onPress={onPress}>
{t('onboarding.recoveryPhrase.warning.final.button')}
</Button>
</Flex>
......
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { useCallback, useEffect } from 'react'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Alert, Image, Platform, StyleSheet } from 'react-native'
import { OnboardingStackParamList } from 'src/app/navigation/types'
import { BackButton } from 'src/components/buttons/BackButton'
import { useBiometricContext } from 'src/features/biometrics/context'
import { useBiometricAppSettings } from 'src/features/biometrics/hooks'
import { promptPushPermission } from 'src/features/notifications/Onesignal'
......@@ -14,7 +13,8 @@ import { ONBOARDING_NOTIFICATIONS_DARK, ONBOARDING_NOTIFICATIONS_LIGHT } from 'u
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import i18n from 'uniswap/src/i18n/i18n'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { isIOS } from 'utilities/src/platform'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
......@@ -45,29 +45,6 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop
const onCompleteOnboarding = useCompleteOnboardingCallback(params)
const renderBackButton = useCallback(
(nav: OnboardingScreens): JSX.Element => (
<BackButton onPressBack={(): void => navigation.navigate({ name: nav, params, merge: true })} />
),
[navigation, params],
)
/* For some screens, we want to override the back button to go to a different screen.
* This helps avoid re-visiting loading states or confirmation views.
*/
useEffect(() => {
const shouldOverrideBackButton = [ImportType.SeedPhrase, ImportType.Restore, ImportType.CreateNew].includes(
params.importType,
)
if (shouldOverrideBackButton) {
const nextScreen =
params.importType === ImportType.Restore ? OnboardingScreens.RestoreCloudBackup : OnboardingScreens.Backup
navigation.setOptions({
headerLeft: () => renderBackButton(nextScreen),
})
}
}, [navigation, params, renderBackButton])
const navigateToNextScreen = useCallback(async () => {
// Skip security setup if already enabled or already imported seed phrase
if (
......@@ -90,13 +67,17 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop
}, [enableNotifications, navigateToNextScreen])
return (
<OnboardingScreen subtitle={t('onboarding.notification.subtitle')} title={t('onboarding.notification.title')}>
<OnboardingScreen
disableGoBack
subtitle={t('onboarding.notification.subtitle')}
title={t('onboarding.notification.title')}
>
<Flex centered shrink py={isIOS ? '$spacing60' : '$spacing16'}>
<NotificationsBackgroundImage />
</Flex>
<Flex gap="$spacing24">
<Trace logPress element={ElementName.Skip}>
<TouchableArea testID={ElementName.Skip} onPress={navigateToNextScreen}>
<TouchableArea testID={TestID.Skip} onPress={navigateToNextScreen}>
<Text color="$accent1" textAlign="center" variant="buttonLabel2">
{t('common.button.later')}
</Text>
......
......@@ -23,10 +23,11 @@ import FingerprintIcon from 'ui/src/assets/icons/fingerprint.svg'
import { borderRadii, imageSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ElementName } from 'uniswap/src/features/telemetry/constants'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { opacify } from 'uniswap/src/utils/colors'
import { isIOS } from 'utilities/src/platform'
import { opacify } from 'wallet/src/utils/colors'
import { openSettings } from 'wallet/src/utils/linking'
type Props = NativeStackScreenProps<OnboardingStackParamList, OnboardingScreens.Security>
......@@ -140,7 +141,7 @@ export function SecuritySetupScreen({ route: { params } }: Props): JSX.Element {
</Flex>
<Flex gap="$spacing24">
<Trace logPress element={ElementName.Skip}>
<TouchableArea testID={ElementName.Skip} onPress={onMaybeLaterPressed}>
<TouchableArea testID={TestID.Skip} onPress={onMaybeLaterPressed}>
<Text color="$accent1" textAlign="center" variant="buttonLabel2">
{t('common.button.later')}
</Text>
......
import { Trans } from 'react-i18next'
import { Text } from 'ui/src'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { openUri } from 'wallet/src/utils/linking'
import { openUri } from 'uniswap/src/utils/linking'
export function TermsOfService(): JSX.Element {
return (
......
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.
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