ci(release): publish latest release

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