ci(release): publish latest release

parent 569072d5
---
description:
globs:
alwaysApply: true
---
# Frontend Testing Strategy
When writing tests for our frontend monorepo, follow these principles:
## Core Philosophy
- Test behaviors, not implementations
- Optimize for confidence, readability, and maintainability
- Follow the testing pyramid: unit tests > integration tests > E2E tests
- Reference existing tests for examples of our team's best practices. For example: [useBooleanState.test.ts](mdc:packages/utilities/src/react/useBooleanState.test.ts) [useSwapNetworkNotification.test.ts](mdc:packages/uniswap/src/features/transactions/swap/form/hooks/useSwapNetworkNotification.test.ts)
## Test Structure & Patterns
### Unit Tests
- Test individual functions, hooks, and components in isolation
- Use Jest and React Testing Library/React Native Testing Library
- Follow Arrange-Act-Assert pattern
- Mock dependencies appropriately
- Name tests descriptively: `it('should [behavior] when [condition]')`
```typescript
// Component test example
test('should display error when form submission fails', async () => {
// Arrange
const errorMessage = 'Invalid credentials';
const mockSubmit = jest.fn().mockRejectedValue(new Error(errorMessage));
render(<LoginForm onSubmit={mockSubmit} />);
// Act
await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com');
await userEvent.type(screen.getByLabelText(/password/i), 'password123');
await userEvent.click(screen.getByRole('button', { name: /login/i }));
// Assert
expect(await screen.findByText(errorMessage)).toBeInTheDocument();
expect(mockSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123'
});
});
// Hook test example
test('should increment counter when increment function is called', () => {
// Arrange
const { result } = renderHook(() => useCounter(0));
// Act
act(() => {
result.current.increment();
});
// Assert
expect(result.current.count).toBe(1);
});
```
### Integration Tests
- Test interactions between multiple components
- Focus on user workflows and component communication
- Use React Testing Library's user-event
- Minimize mocking to test actual integrations
## Best Practices
### Test Data Management
- Create factory functions for generating test data
- Use TypeScript to ensure test data matches expected interfaces
```typescript
const createUser = (overrides = {}): User => ({
id: uuid(),
name: 'Test User',
email: 'test@example.com',
role: 'user',
...overrides
});
```
### Mocking Strategy
- Prefer manual mocks over automatic mocks
- Mock at the boundary of your system (API calls, third-party services)
- For React Native, mock native modules using Jest's mock functions
```typescript
jest.mock('packages/uniswap/...', () => ({
fetchUserData: jest.fn().mockResolvedValue({
id: '123',
name: 'Test User',
email: 'test@example.com'
})
}));
```
### Coverage Guidelines
- Aim for 80% coverage overall, 90%+ for critical business logic
- When requested to write UI tests, cover all user-facing components with at least basic rendering tests
- Test error states and edge cases; when unceratin about these states and cases, stop and ask for clarification before continuing
- Focus on quality over quantity - 5 well-written tests > 20 poor tests
## Monorepo Considerations
- Use the same path import strategy found in other files; everything should pass linting
- Respect the testing configuration of each package
- For shared components, test in their own package, not in consuming packages
## Implementation Checklist
When implementing tests, verify:
1. ✅ Tests focus on component/function behavior
2. ✅ Tests are isolated and don't depend on each other
3. ✅ Mocks are used appropriately and documented
4. ✅ Error states and edge cases are covered
5. ✅ Tests are readable and maintainable
6. ✅ Tests fail when they should (verify with temporary bug)
7. ✅ Test file structure matches source code structure
8. ✅ Import paths follow monorepo conventions
\ No newline at end of file
IPFS hash of the deployment:
- CIDv0: `QmezmXSutzznthvc6pSCrb7JgxjGqXFNCWCZLjADjg6Tf8`
- CIDv1: `bafybeihxp3cmskdb4nr76bocjhdgq32ykbfquxwakbdx33iehf44jxwuim`
- CIDv0: `QmainvWX1paa5SAQXowSshKypn1tC5d59gLN8w2coWzJwx`
- CIDv1: `bafybeifx7bho67bfj5b2bn7nw452bovpbv4iia5ndwi4kwvra5fcswhjr4`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
......@@ -10,14 +10,95 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeihxp3cmskdb4nr76bocjhdgq32ykbfquxwakbdx33iehf44jxwuim.ipfs.dweb.link/
- [ipfs://QmezmXSutzznthvc6pSCrb7JgxjGqXFNCWCZLjADjg6Tf8/](ipfs://QmezmXSutzznthvc6pSCrb7JgxjGqXFNCWCZLjADjg6Tf8/)
- https://bafybeifx7bho67bfj5b2bn7nw452bovpbv4iia5ndwi4kwvra5fcswhjr4.ipfs.dweb.link/
- [ipfs://QmainvWX1paa5SAQXowSshKypn1tC5d59gLN8w2coWzJwx/](ipfs://QmainvWX1paa5SAQXowSshKypn1tC5d59gLN8w2coWzJwx/)
### 5.83.7 (2025-05-13)
## 5.84.0 (2025-05-14)
### Features
* **web:** add an e2e test for the sign up flow (#19146) c05749e
* **web:** add connector_id to lp txs (#19207) 50b933e
* **web:** add connector_id to send events (#19208) 7e4a30a
* **web:** add connector_id to swap analytics (#19206) 9d734f0
* **web:** add connector_id to wallet disconnect event (#19205) 6631d02
* **web:** add passkey mgmt analytics (#19172) 9d12f00
* **web:** detect delegations and log them (#19368) d22a9de
* **web:** display min/max/market for custom range position (#19485) cc4437b
* **web:** PUX add expandable swap details with tooltips (#18669) 21c0110
* **web:** PUX adding token rate to currency input (#18736) d7abe9f
* **web:** PUX adding user quote amount to trades (#18511) 10d6f6f
* **web:** PUX FOT (#18804) ae89a01
* **web:** PUX handle bridging (#19089) b7375af
* **web:** PUX max slippage shield animation (#18972) 4d635a4
* **web:** PUX max slippage tooltip (#18708) 23d955b
* **web:** PUX network cost tooltip (#18685) dff4cc1
* **web:** PUX price and route tooltips (#18681) f77ad8a
* **web:** PUX price diff (#18738) 859bca1
* **web:** PUX price impact warning (#18740) 9809ff3
* **web:** PUX you receive panel (#18664) 326c5b1
* **web:** retry with standard swap if batched fails (#19295) dbffc2b
* **web:** swap tracking (#19328) 4185c10
* **web:** track clicks of download app ctas (#19232) fbc651c
* **web:** track user prop delegate status (#19286) f2f3e52
* **web:** Update existing user cta text and remove experiment (#19349) f13e553
### Bug Fixes
* **web:** only support batch swaps on supported status (#19566) da9a5ec
* **web:** [lp] fix native buffer experiment (#18777) 7a03b16
* **web:** add create/increase params to analytic events (#19388) b2a4177
* **web:** add error handling for rewards calcs (#18988) 1923ffe
* **web:** add google fonts url to CSP style-src (#19248) f64259e
* **web:** add migration 24 for wallet capabilities (#19464) 3ffc371
* **web:** add providers to web storybook to fix AnimatedNumber story (#18632) 12b4874
* **web:** allow press on disabled web3-status-connected (#19535) b7c4ede
* **web:** avoid fetching calldata for invalid range (#19384) 552a144
* **web:** clicking swap setting icon should close settings modal if open (#19472) c248622
* **web:** correct delegation logging (#19415) ed73ce2
* **web:** correct some analytics (#19391) 26016ce
* **web:** dedupe and cache wallet capabilities calls (#19739) (#19750) ad67756
* **web:** delete DeprecatedWebButtons (#19213) 799b1c1
* **web:** downgrade two errors to warnings (#19344) 726ba31
* **web:** fix approval call for create + increase (#19223) f73b4dd
* **web:** fix key open search modal (#19332) eb41c21
* **web:** fix mweb chart panning (#19373) f3ab207
* **web:** handle isTokenOptionArray check for bridging section (#19363) de14b83
* **web:** icon bugs + lp incentive improvements (#19606) 4c7c09e
* **web:** limits mobile bug (#19271) 9d49bf8
* **web:** logging for mismatch and isDelegatedEOA (#19346) 53b3a5a
* **web:** migrate BreadcrumbNav to tamagui (#19212) 734de02
* **web:** migrate NFTTab to tamagui (#19216) d940820
* **web:** migrate OpenLimitOrdersButton to tamagui (#19217) a74ae36
* **web:** migrate TokensTab to tamagui (#19214) 622e711
* **web:** migrate UniExtensionPoolsMenu to tamagui (#19215) 6250c24
* **web:** missing permit tx (#19326) 915e983
* **web:** only support batch swaps on supported status (#19580) ce53969
* **web:** persist query client and remove extra provider (#19219) f2236dc
* **web:** preload WalletConnect provider to avoid waterfall requests (#19195) ea3bc3f
* **web:** price ux revert remove inline warning (#19439) a5574a3
* **web:** PUX auto slippage (#18805) 598d9d3
* **web:** pux design fixes (#19430) a1c6311
* **web:** remove disconnect logic in useConnect hook (#19190) 7e2b0bc
* **web:** tamagui LimitDetailActivityRow & PortfolioTabRow (#19220) 084eced
* **web:** tamagui LimitsMenu / LimitDisclaimer (#19218) 28fbc8e
* **web:** temp remove disabled prop from button (#19675) 17f177e
* **web:** update icon props to be compatible w react-feather (#19209) 2dd772b
* **web:** update url for EW sign in test (#19470) 2d1a87e
* **web:** usd amount hover state currency panel (#18841) 38a3d0c
* **web:** Use "Log In" in auth pop up (#19385) 9ea148b
* **web:** Use sign up for New User button (#19478) a0b6690
### Continuous Integration
* **web:** update sitemaps c080e0b
### Tests
* **web:** handle v2, v3, v4 pool not found graphql (#19348) 4898d1c
* **web:** use different test account (#19431) 014d910
web/5.83.7
\ No newline at end of file
web/5.84.0
\ No newline at end of file
// Simple mock for expo-blur's BlurView
// This is needed to avoid the Storybook Web compilation error related to `expo-blur`, something like:
// Module parse failed: Unexpected token (29:12)
// node_modules/expo-blur/build/BlurView.web.js 29:12
// You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
// We don't actually need to use `expo-blur` in the Web App, as we just use CSS; so, we can mock it out.
export const BlurView = (props) => <div {...props} />;
export default BlurView;
import { useCallback, useEffect, useMemo } from 'react'
import { CopyButton } from 'src/app/components/buttons/CopyButton'
import { Flex, Text, useMedia } from 'ui/src'
import { logger } from 'utilities/src/logger/logger'
const ROW_SIZE = 3
export const MnemonicViewer = ({ mnemonic }: { mnemonic?: string[] }): JSX.Element => {
const media = useMedia()
const px = media.xxs ? '$spacing12' : '$spacing32'
const onCopyPress = useCallback(async () => {
if (!mnemonic) {
return
}
const mnemonicString = mnemonic.join(' ')
try {
if (mnemonicString) {
await navigator.clipboard.writeText(mnemonicString)
}
} catch (error) {
logger.error(error, {
tags: { file: 'MnemonicViewer.tsx', function: 'onCopyPress' },
})
}
}, [mnemonic])
useEffect(() => {
return () => {
navigator.clipboard.writeText('').catch((error) => {
logger.error(error, {
tags: { file: 'MnemonicViewer.tsx', function: 'MnemonicViewer#useEffect' },
})
})
}
}, [])
const rows = useMemo(() => {
if (!mnemonic) {
return null
}
const elements = []
for (let i = 0; i < mnemonic.length; i += ROW_SIZE) {
elements.push(<SeedPhraseRow key={i} startIndex={i + 1} words={mnemonic.slice(i, i + ROW_SIZE)} />)
}
return elements
}, [mnemonic])
return (
<Flex
backgroundColor="$surface2"
borderColor="$surface3"
borderRadius="$rounded20"
borderWidth="$spacing1"
gap="$spacing12"
position="relative"
px={px}
py="$spacing24"
width="100%"
>
{rows}
<Flex alignItems="center" left="50%" position="absolute" top={-16} transform="translateX(-50%)">
<CopyButton onCopyPress={onCopyPress} />
</Flex>
</Flex>
)
}
function SeedPhraseRow({ words, startIndex }: { words: string[]; startIndex: number }): JSX.Element {
return (
<Flex fill row>
{words.map((word, index) => (
<SeedPhraseWord key={index} index={index + startIndex} word={word} />
))}
</Flex>
)
}
function SeedPhraseWord({ index, word }: { index: number; word: string }): JSX.Element {
const media = useMedia()
const fontVariant = 'body3'
const gap = media.xxs ? '$spacing4' : '$spacing8'
return (
<Flex fill row flexBasis={0} gap={gap}>
<Text color="$neutral3" minWidth={16} variant={fontVariant}>
{index}
</Text>
<Text variant={fontVariant}>{word}</Text>
</Flex>
)
}
......@@ -60,6 +60,9 @@ export function TraceUserProperties(): null {
if (!activeAccount) {
return
}
if (activeAccount.backups) {
setUserProperty(ExtensionUserPropertyName.BackupTypes, activeAccount.backups)
}
setUserProperty(ExtensionUserPropertyName.ActiveWalletAddress, activeAccount.address)
setUserProperty(ExtensionUserPropertyName.ActiveWalletType, activeAccount.type)
setUserProperty(ExtensionUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances)
......
......@@ -21,10 +21,7 @@ import {
} from 'src/app/features/onboarding/OnboardingSteps'
import { OnboardingWrapper } from 'src/app/features/onboarding/OnboardingWrapper'
import { PasswordImport } from 'src/app/features/onboarding/PasswordImport'
import { NameWallet } from 'src/app/features/onboarding/create/NameWallet'
import { PasswordCreate } from 'src/app/features/onboarding/create/PasswordCreate'
import { TestMnemonic } from 'src/app/features/onboarding/create/TestMnemonic'
import { ViewMnemonic } from 'src/app/features/onboarding/create/ViewMnemonic'
import { ImportMnemonic } from 'src/app/features/onboarding/import/ImportMnemonic'
import { InitiatePasskeyAuth } from 'src/app/features/onboarding/import/InitiatePasskeyAuth'
import { PasskeyImport } from 'src/app/features/onboarding/import/PasskeyImport'
......@@ -68,26 +65,9 @@ const allRoutes = [
element: (
<OnboardingStepsProvider
key={OnboardingRoutes.Create}
steps={{
[CreateOnboardingSteps.Password]: <PasswordCreate />,
[CreateOnboardingSteps.ViewMnemonic]: <ViewMnemonic />,
[CreateOnboardingSteps.TestMnemonic]: <TestMnemonic />,
[CreateOnboardingSteps.Naming]: <NameWallet />,
[CreateOnboardingSteps.Complete]: <Complete flow={ExtensionOnboardingFlow.New} />,
}}
/>
),
},
{
path: OnboardingRoutes.Claim,
element: (
<OnboardingStepsProvider
key={OnboardingRoutes.Claim}
steps={{
[CreateOnboardingSteps.ClaimUnitag]: <ClaimUnitagScreen />,
[CreateOnboardingSteps.Password]: <PasswordCreate />,
[CreateOnboardingSteps.ViewMnemonic]: <ViewMnemonic />,
[CreateOnboardingSteps.TestMnemonic]: <TestMnemonic />,
[CreateOnboardingSteps.Complete]: <Complete tryToClaimUnitag flow={ExtensionOnboardingFlow.New} />,
}}
/>
......@@ -113,7 +93,6 @@ const allRoutes = [
steps={{
[ImportPasskeySteps.InitiatePasskeyAuth]: <InitiatePasskeyAuth />,
[ImportPasskeySteps.PasskeyImport]: <PasskeyImport />,
// TODO(WALL-6383): modify this flow to ask user to verify their seed phrase.
[ImportOnboardingSteps.Password]: <PasswordImport flow={ExtensionOnboardingFlow.Passkey} />,
[ImportOnboardingSteps.Select]: <SelectWallets flow={ExtensionOnboardingFlow.Passkey} />,
[ImportOnboardingSteps.Complete]: <Complete flow={ExtensionOnboardingFlow.Passkey} />,
......
......@@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux'
import { RouterProvider, createHashRouter } from 'react-router-dom'
import { PersistGate } from 'redux-persist/integration/react'
import { ErrorElement } from 'src/app/components/ErrorElement'
import { ScreenHeader } from 'src/app/components/layout/ScreenHeader'
import { BaseAppContainer } from 'src/app/core/BaseAppContainer'
import { DatadogAppNameTag } from 'src/app/datadog'
import { AccountSwitcherScreen } from 'src/app/features/accounts/AccountSwitcherScreen'
......@@ -37,14 +38,18 @@ import {
import { BackgroundToSidePanelRequestType } from 'src/background/messagePassing/types/requests'
import { PrimaryAppInstanceDebuggerLazy } from 'src/store/PrimaryAppInstanceDebuggerLazy'
import { getReduxPersistor } from 'src/store/store'
import { TouchableArea } from 'ui/src'
import { QuestionInCircleFilled } from 'ui/src/components/icons'
import { useResetUnitagsQueries } from 'uniswap/src/data/apiClients/unitagsApi/useResetUnitagsQueries'
import { syncAppWithDeviceLanguage } from 'uniswap/src/features/settings/slice'
import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import i18n from 'uniswap/src/i18n'
import { isDevEnv } from 'utilities/src/environment/env'
import { logger } from 'utilities/src/logger/logger'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useInterval } from 'utilities/src/time/timing'
import { SmartWalletSettings } from 'wallet/src/features/smartWallet/SmartWalletSettings'
import { useTestnetModeForLoggingAndAnalytics } from 'wallet/src/features/testnetMode/hooks/useTestnetModeForLoggingAndAnalytics'
const router = createHashRouter([
......@@ -104,6 +109,28 @@ const router = createHashRouter([
path: SettingsRoutes.ManageConnections,
element: <SettingsManageConnectionsScreen />,
},
{
path: SettingsRoutes.SmartWallet,
element: (
<SmartWalletSettings
Header={
<ScreenHeader
title={i18n.t('settings.setting.smartWallet.action.smartWallet')}
rightColumn={
<TouchableArea
alignItems="center"
alignSelf="center"
py="$spacing12"
// TODO: add modal + event
>
<QuestionInCircleFilled color="$neutral2" size="$icon.20" />
</TouchableArea>
}
/>
}
/>
),
},
],
},
{
......
......@@ -4,11 +4,14 @@ import { Flex, Text } from 'ui/src'
import { AlertTriangleFilled } from 'ui/src/components/icons'
import { LearnMoreLink } from 'uniswap/src/components/text/LearnMoreLink'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { WalletEventName } from 'uniswap/src/features/telemetry/constants'
import Trace from 'uniswap/src/features/telemetry/Trace'
export function ActionCanNotBeCompletedContent(): JSX.Element {
const { t } = useTranslation()
return (
<Trace logImpression eventOnTrigger={WalletEventName.MismatchAccountSignatureRequestBlocked}>
<DappRequestContent title={t('dapp.request.actionCannotBeCompleted.header')}>
<Flex
backgroundColor="$statusCritical2"
......@@ -30,10 +33,15 @@ export function ActionCanNotBeCompletedContent(): JSX.Element {
<Text color="$neutral2" variant="body4">
{t('dapp.request.actionCannotBeCompleted.description')}
</Text>
<LearnMoreLink textVariant="buttonLabel4" url={uniswapUrls.helpArticleUrls.mismatchedImports} />
<LearnMoreLink
textVariant="buttonLabel4"
url={uniswapUrls.helpArticleUrls.mismatchedImports}
textColor="$neutral1"
/>
</Flex>
</Flex>
</Flex>
</DappRequestContent>
</Trace>
)
}
......@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { KeyboardKey } from 'src/app/features/onboarding/KeyboardKey'
import { MainContentWrapper } from 'src/app/features/onboarding/intro/MainContentWrapper'
import { useFinishExtensionOnboarding } from 'src/app/features/onboarding/useFinishExtensionOnboarding'
import { useOpeningKeyboardShortCut } from 'src/app/hooks/useOpeningKeyboardShortCut'
import { getCurrentTabAndWindowId } from 'src/app/navigation/utils'
import { onboardingMessageChannel } from 'src/background/messagePassing/messageChannels'
......@@ -15,7 +16,7 @@ import { iconSizes } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { ExtensionOnboardingFlow } from 'uniswap/src/types/screens/extension'
import { logger } from 'utilities/src/logger/logger'
import { useFinishOnboarding, useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
export function Complete({
flow,
......@@ -43,7 +44,11 @@ export function Complete({
}, [existingClaim, address, tryToClaimUnitag, unitagClaimAttempted, addUnitagClaim])
// Activates onboarding accounts on component mount
useFinishOnboarding(terminateStoreSynchronization, flow, tryToClaimUnitag && !unitagClaimAttempted)
useFinishExtensionOnboarding({
callback: terminateStoreSynchronization,
extensionOnboardingFlow: flow,
skip: tryToClaimUnitag && !unitagClaimAttempted,
})
useEffect(() => {
const onSidebarOpenedListener = onboardingMessageChannel.addMessageListener(
......
......@@ -4,9 +4,6 @@ import { OnboardingScreenProps } from 'src/app/features/onboarding/OnboardingScr
export enum CreateOnboardingSteps {
ClaimUnitag = 'claimUnitag',
Password = 'password',
ViewMnemonic = 'mnemonic',
TestMnemonic = 'testMnemonic',
Naming = 'naming',
Complete = 'complete',
}
......
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Input } from 'src/app/components/Input'
import { saveDappConnection } from 'src/app/features/dapp/actions'
import { OnboardingScreen } from 'src/app/features/onboarding/OnboardingScreen'
import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps'
import { Flex, Text, Unicon } from 'ui/src'
import { fonts, iconSizes } from 'ui/src/theme'
import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension'
import { shortenAddress } from 'utilities/src/addresses'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
export function NameWallet(): JSX.Element {
const { t } = useTranslation()
const { getOnboardingAccount, setPendingWalletName } = useOnboardingContext()
const onboardingAccount = getOnboardingAccount()
const { goToNextStep, goToPreviousStep } = useOnboardingSteps()
const [walletName, setWalletName] = useState('')
const onboardingAccountAddress = onboardingAccount?.address
const onSubmit = async (): Promise<void> => {
if (walletName) {
setPendingWalletName(walletName)
}
if (onboardingAccount) {
await saveDappConnection(UNISWAP_WEB_URL, onboardingAccount)
}
goToNextStep()
}
return (
<Trace
logImpression
properties={{ flow: ExtensionOnboardingFlow.New }}
screen={ExtensionOnboardingScreens.NameWallet}
>
<OnboardingScreen
Icon={
onboardingAccountAddress ? <Unicon address={onboardingAccountAddress} size={iconSizes.icon64} /> : undefined
}
nextButtonEnabled={true}
nextButtonText={t('onboarding.name.wallet.button.text')}
subtitle={t('onboarding.name.wallet.subtitle')}
title={t('onboarding.name.wallet.title')}
onBack={goToPreviousStep}
onSubmit={onSubmit}
>
<Flex gap="$spacing24" py="$spacing24" width="100%">
<Input
autoFocus
large
backgroundColor="$surface1"
borderRadius="$rounded20"
fontSize={fonts.heading3.fontSize}
placeholder={onboardingAccount?.name}
py="$spacing32"
textAlign="center"
onChangeText={setWalletName}
onSubmitEditing={onSubmit}
/>
<Text color="$neutral3" textAlign="center" variant="subheading2">
{onboardingAccountAddress && shortenAddress(onboardingAccountAddress)}
</Text>
</Flex>
</OnboardingScreen>
</Trace>
)
}
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { OnboardingScreen } from 'src/app/features/onboarding/OnboardingScreen'
import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps'
import { RecoveryPhraseVerification } from 'src/app/features/recoveryPhraseVerification/RecoveryPhraseVerification'
import { NUMBER_OF_TESTS_FOR_RECOVERY_PHRASE_VERIFICATION } from 'src/app/features/settings/BackupRecoveryPhrase/constants'
import { Flex, Square, Text } from 'ui/src'
import { FileListCheck } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension'
import { useEvent } from 'utilities/src/react/hooks'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
import { BackupType } from 'wallet/src/features/wallet/accounts/types'
export function TestMnemonic({
numberOfTests = NUMBER_OF_TESTS_FOR_RECOVERY_PHRASE_VERIFICATION,
}: {
numberOfTests?: number
}): JSX.Element {
const { t } = useTranslation()
const { addBackupMethod, getOnboardingAccountAddress, getOnboardingAccountMnemonic } = useOnboardingContext()
const onboardingAccountAddress = getOnboardingAccountAddress()
const onboardingAccountMnemonic = getOnboardingAccountMnemonic()
const { goToNextStep, goToPreviousStep } = useOnboardingSteps()
const onNext = useEvent((): void => {
if (!onboardingAccountMnemonic || !onboardingAccountAddress) {
return
}
goToNextStep()
})
const [subtitle, setSubtitle] = useState('')
const [numberOfWordsVerified, setNumberOfWordsVerified] = useState(0)
const [hasError, setHasError] = useState(false)
const onComplete = useEvent(() => {
addBackupMethod(BackupType.Manual)
goToNextStep()
})
return (
<Trace
logImpression
properties={{ flow: ExtensionOnboardingFlow.New }}
screen={ExtensionOnboardingScreens.ConfirmSeedPhrase}
>
<OnboardingScreen
Icon={
<Square alignSelf="center" backgroundColor="$surface2" borderRadius="$rounded12" size={iconSizes.icon48}>
<FileListCheck color="$neutral1" size="$icon.24" />
</Square>
}
nextButtonEnabled={false}
nextButtonText={t('onboarding.backup.manual.progress', {
completedStepsCount: numberOfWordsVerified,
totalStepsCount: numberOfTests,
})}
nextButtonVariant="default"
nextButtonEmphasis="secondary"
subtitle={subtitle}
title={t('onboarding.backup.manual.title')}
onBack={goToPreviousStep}
onSkip={onNext}
onSubmit={onNext}
>
<Flex fill gap="$spacing24" mb="$spacing24" width="100%">
{onboardingAccountMnemonic ? (
<RecoveryPhraseVerification
mnemonic={onboardingAccountMnemonic}
numberOfTests={numberOfTests}
onComplete={onComplete}
setHasError={setHasError}
setSubtitle={setSubtitle}
onWordVerified={(numberOfWordsVerified) => setNumberOfWordsVerified(numberOfWordsVerified)}
/>
) : null}
<Text color="$statusCritical" style={{ opacity: hasError ? 1 : 0 }} textAlign="center" variant="body3">
{t('onboarding.backup.manual.error')}
</Text>
</Flex>
</OnboardingScreen>
</Trace>
)
}
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { MnemonicViewer } from 'src/app/components/MnemonicViewer'
import { OnboardingScreen } from 'src/app/features/onboarding/OnboardingScreen'
import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps'
import { useSubmitOnEnter } from 'src/app/features/onboarding/utils'
import { BackupWarningBulletPoints } from 'src/app/features/settings/BackupRecoveryPhrase/BackupWarningBulletPoints'
import { TopLevelRoutes } from 'src/app/navigation/constants'
import { navigate } from 'src/app/navigation/state'
import { Flex, LabeledCheckbox, Square, Text } from 'ui/src'
import { AlertTriangleFilled, FileListLock } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension'
import { logger } from 'utilities/src/logger/logger'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
enum ViewStep {
Info = 0,
View = 1,
}
export function ViewMnemonic(): JSX.Element {
const { t } = useTranslation()
const [viewStep, setViewStep] = useState<ViewStep>(ViewStep.Info)
const { goToNextStep } = useOnboardingSteps()
const [disclaimerChecked, setDisclaimerChecked] = useState(false)
const { getOnboardingAccountAddress, getOnboardingAccountMnemonic, retrieveOnboardingAccountMnemonic } =
useOnboardingContext()
const onboardingAccountAddress = getOnboardingAccountAddress()
const onboardingAccountMnemonic = getOnboardingAccountMnemonic()
useEffect(() => {
if (!onboardingAccountMnemonic) {
retrieveOnboardingAccountMnemonic().catch((e) => {
logger.error(e, {
tags: { file: 'ViewMnemonic', function: 'retrieveOnboardingAccountMnemonic' },
})
})
}
}, [onboardingAccountMnemonic, retrieveOnboardingAccountMnemonic])
// On Info step, next button should be enabled if mnemonic has been created.
// On View step, next button should be enabled if disclaimer is checked and mnemonic has been created.
const shouldEnableNextButton =
viewStep === ViewStep.View ? !!onboardingAccountAddress && disclaimerChecked : !!onboardingAccountAddress
const onSubmit = (): void => {
if (!shouldEnableNextButton) {
return
}
if (viewStep === ViewStep.Info) {
setViewStep(ViewStep.View)
return
}
if (onboardingAccountAddress && disclaimerChecked) {
goToNextStep()
}
}
useSubmitOnEnter(onSubmit)
return (
<Trace
logImpression
properties={{ flow: ExtensionOnboardingFlow.New }}
screen={ExtensionOnboardingScreens.ViewSeedPhrase}
>
<OnboardingScreen
Icon={
<Square
alignContent="center"
backgroundColor={viewStep === ViewStep.View ? '$surface2' : '$statusCritical2'}
borderRadius="$rounded12"
size={iconSizes.icon48}
>
{viewStep === ViewStep.View ? (
<FileListLock color="$neutral1" size="$icon.24" />
) : (
<AlertTriangleFilled color="$statusCritical" size="$icon.24" />
)}
</Square>
}
nextButtonEnabled={shouldEnableNextButton}
nextButtonText={t('common.button.continue')}
subtitle={
viewStep === ViewStep.View
? t('onboarding.backup.view.subtitle.message2')
: t('onboarding.backup.view.subtitle.message1')
}
title={t('onboarding.backup.view.title')}
onBack={(): void =>
navigate(`/${TopLevelRoutes.Onboarding}`, {
replace: true,
})
}
onSubmit={onSubmit}
>
{viewStep === ViewStep.Info ? (
<Flex my="$spacing24">
<BackupWarningBulletPoints />
</Flex>
) : (
<Flex gap="$spacing16" my="$spacing24" pt="$spacing8" width="100%">
<MnemonicViewer mnemonic={onboardingAccountMnemonic} />
<Flex backgroundColor="$surface2" borderRadius="$rounded16" p="$spacing12" overflow="hidden">
<LabeledCheckbox
checked={disclaimerChecked}
text={<Text variant="body3">{t('onboarding.backup.view.disclaimer')}</Text>}
onCheckPressed={(currentValue: boolean): void => setDisclaimerChecked(!currentValue)}
/>
</Flex>
</Flex>
)}
</OnboardingScreen>
</Trace>
)
}
......@@ -41,7 +41,7 @@ export function SelectImportMethod(): JSX.Element {
title={t('onboarding.import.selectMethod.title')}
onBack={(): void => navigate(`/${TopLevelRoutes.Onboarding}`, { replace: true })}
>
<Flex gap="$spacing16" mt="$spacing24" width="100%">
<Flex gap="$spacing12" mt="$spacing24" width="100%">
{showErrorMessage && (
<Flex mb="$spacing8">
<Text color="$statusCritical" variant="body3" textAlign="center" width="100%">
......
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SelectWalletsSkeleton } from 'src/app/components/loading/SelectWalletSkeleton'
import { saveDappConnection } from 'src/app/features/dapp/actions'
import { OnboardingScreen } from 'src/app/features/onboarding/OnboardingScreen'
import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps'
import { useSubmitOnEnter } from 'src/app/features/onboarding/utils'
import { Flex, ScrollView, SpinningLoader, Square, Text } from 'ui/src'
import { WalletFilled } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension'
import { useAsyncData, useEvent } from 'utilities/src/react/hooks'
......@@ -22,7 +18,6 @@ import { BackupType } from 'wallet/src/features/wallet/accounts/types'
export function SelectWallets({ flow }: { flow: ExtensionOnboardingFlow }): JSX.Element {
const { t } = useTranslation()
const shouldAutoConnect = useFeatureFlag(FeatureFlags.ExtensionAutoConnect)
const [buttonClicked, setButtonClicked] = useState(false)
const { goToNextStep, goToPreviousStep } = useOnboardingSteps()
......@@ -42,17 +37,11 @@ export function SelectWallets({ flow }: { flow: ExtensionOnboardingFlow }): JSX.
}
setButtonClicked(true)
const importedAccounts = await generateAccountsAndImportAddresses({
await generateAccountsAndImportAddresses({
selectedAddresses,
backupType: flow === ExtensionOnboardingFlow.Passkey ? BackupType.Passkey : BackupType.Manual,
})
// TODO(EXT-1375): figure out how to better auto connect existing wallets that may have connected via WC or some other method.
// Once that's solved the feature flag can be turned on/removed.
if (shouldAutoConnect && importedAccounts?.[0]) {
await saveDappConnection(UNISWAP_WEB_URL, importedAccounts[0])
}
goToNextStep()
setButtonClicked(false)
})
......
......@@ -44,7 +44,7 @@ export function IntroScreen(): JSX.Element {
<Flex row backgroundColor="$surface1" borderRadius="$rounded16">
<Button
variant="branded"
onPress={(): void => navigate(`/${TopLevelRoutes.Onboarding}/${OnboardingRoutes.Claim}`)}
onPress={(): void => navigate(`/${TopLevelRoutes.Onboarding}/${OnboardingRoutes.Create}`)}
>
{isPasskeyImportEnabled
? t('onboarding.landing.button.createAccount')
......@@ -61,7 +61,7 @@ export function IntroScreen(): JSX.Element {
}
>
{isPasskeyImportEnabled
? t('onboarding.intro.button.signInOrImport')
? t('onboarding.intro.button.logInOrImport')
: t('onboarding.intro.button.alreadyHave')}
</Button>
</Flex>
......
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
import { useFinishExtensionOnboarding } from 'src/app/features/onboarding/useFinishExtensionOnboarding'
import { terminateStoreSynchronization } from 'src/store/storeSynchronization'
import { Flex, Text } from 'ui/src'
import { Check, GraduationCap } from 'ui/src/components/icons'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { useFinishOnboarding } from 'wallet/src/features/onboarding/OnboardingContext'
export function ResetComplete(): JSX.Element {
const { t } = useTranslation()
// Activates onboarding accounts on component mount
useFinishOnboarding(terminateStoreSynchronization)
useFinishExtensionOnboarding({ callback: terminateStoreSynchronization })
return (
<>
......
import { useEffect, useRef } from 'react'
import { saveDappConnection } from 'src/app/features/dapp/actions'
import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls'
import { ImportType } from 'uniswap/src/types/onboarding'
import { ExtensionOnboardingFlow } from 'uniswap/src/types/screens/extension'
import { logger } from 'utilities/src/logger/logger'
import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext'
/**
* Activates onboarding accounts on component mount,
* and auto-connects to app.uniswap.org.
*/
export function useFinishExtensionOnboarding({
callback,
extensionOnboardingFlow,
skip,
}: {
callback?: () => void
extensionOnboardingFlow?: ExtensionOnboardingFlow
skip?: boolean
}): void {
const { finishOnboarding, getOnboardingOrImportedAccount, getOnboardingAccountAddress } = useOnboardingContext()
const importType = getImportType(getOnboardingAccountAddress(), extensionOnboardingFlow)
const isFinishedRef = useRef(false)
useEffect(() => {
if (skip || isFinishedRef.current) {
return
}
const runFinishOnboarding = async () => {
isFinishedRef.current = true
await finishOnboarding({ importType, extensionOnboardingFlow })
const account = getOnboardingOrImportedAccount()
if (account) {
await saveDappConnection(UNISWAP_WEB_URL, account)
}
callback?.()
}
runFinishOnboarding().catch((e) => {
logger.error(e, {
tags: { file: 'useFinishExtensionOnboarding.ts', function: 'finishOnboarding' },
})
})
}, [finishOnboarding, importType, callback, extensionOnboardingFlow, skip, getOnboardingOrImportedAccount])
}
function getImportType(
onboardingAccountAddress: string | undefined,
extensionOnboardingFlow: ExtensionOnboardingFlow | undefined,
): ImportType {
if (extensionOnboardingFlow === ExtensionOnboardingFlow.Passkey) {
return ImportType.Passkey
}
if (onboardingAccountAddress) {
return ImportType.CreateNew
}
return ImportType.RestoreMnemonic
}
......@@ -23,13 +23,18 @@ export function SettingsDropdown({ selected, items, disableDropdown, onSelect }:
return (
<Flex>
<Popover open={isOpen} stayInFrame={true} onOpenChange={(open) => setIsOpen(open)}>
<Popover open={isOpen} stayInFrame={true} onOpenChange={setIsOpen}>
<Popover.Trigger disabled={disableDropdown}>
<Flex row backgroundColor="$surface3" borderRadius="$roundedFull" cursor="pointer" p="$spacing8" gap="$gap4">
<Text color="$neutral1" variant="buttonLabel4">
{selected}
</Text>
<RotatableChevron color="$neutral1" direction="down" height={iconSizes.icon16} width={iconSizes.icon20} />
<RotatableChevron
color="$neutral1"
direction={isOpen ? 'up' : 'down'}
height={iconSizes.icon16}
width={iconSizes.icon20}
/>
</Flex>
</Popover.Trigger>
<Popover.Content zIndex={zIndexes.popover} backgroundColor="$transparent" disableRemoveScroll={false}>
......
......@@ -5,6 +5,7 @@ import { Link } from 'react-router-dom'
import { ScreenHeader } from 'src/app/components/layout/ScreenHeader'
import { SCREEN_ITEM_HORIZONTAL_PAD } from 'src/app/constants'
import { SettingsItemWithDropdown } from 'src/app/features/settings/SettingsItemWithDropdown'
import ThemeToggle from 'src/app/features/settings/ThemeToggle'
import { AppRoutes, SettingsRoutes } from 'src/app/navigation/constants'
import { useExtensionNavigation } from 'src/app/navigation/utils'
import { getIsDefaultProviderFromStorage, setIsDefaultProviderToStorage } from 'src/app/utils/provider'
......@@ -47,7 +48,6 @@ import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { useCurrentLanguageInfo } from 'uniswap/src/features/language/hooks'
import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal'
import { setCurrentFiatCurrency, setIsTestnetModeEnabled } from 'uniswap/src/features/settings/slice'
import { SmartWalletAdvancedSettingsModal } from 'uniswap/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import Trace from 'uniswap/src/features/telemetry/Trace'
import { WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
......@@ -65,6 +65,7 @@ import { authActions } from 'wallet/src/features/auth/saga'
import { AuthActionType } from 'wallet/src/features/auth/types'
import { selectHasViewedConnectionMigration } from 'wallet/src/features/behaviorHistory/selectors'
import { resetWalletBehaviorHistory, setHasViewedConnectionMigration } from 'wallet/src/features/behaviorHistory/slice'
import { SmartWalletAdvancedSettingsModal } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import { BackupType } from 'wallet/src/features/wallet/accounts/types'
import { hasBackup } from 'wallet/src/features/wallet/accounts/utils'
import { useSignerAccounts } from 'wallet/src/features/wallet/hooks'
......@@ -124,6 +125,11 @@ export function SettingsScreen(): JSX.Element {
const handleAdvancedModalClose = useCallback(() => setIsAdvancedModalOpen(false), [])
const handleSmartWalletPress = useCallback(() => {
navigateTo(`${AppRoutes.Settings}/${SettingsRoutes.SmartWallet}`)
setIsAdvancedModalOpen(false)
}, [navigateTo])
useEffect(() => {
getIsDefaultProviderFromStorage()
.then((newIsDefaultProvider) => setIsDefaultProvider(newIsDefaultProvider))
......@@ -162,6 +168,7 @@ export function SettingsScreen(): JSX.Element {
onTestnetModeToggled={handleTestnetModeToggle}
isOpen={isAdvancedModalOpen}
onClose={handleAdvancedModalClose}
onPressSmartWallet={handleSmartWalletPress}
/>
{hasPasskeyBackup && (
<PasskeyManagementModal
......@@ -196,6 +203,7 @@ export function SettingsScreen(): JSX.Element {
/>
)}
</>
<ThemeToggle />
<SettingsItemWithDropdown
Icon={Coins}
items={ORDERED_CURRENCIES.map((currency) => {
......
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { SCREEN_ITEM_HORIZONTAL_PAD } from 'src/app/constants'
import { Flex, SegmentedControl, Text } from 'ui/src'
import { Contrast, Moon, Sun } from 'ui/src/components/icons'
import { useCurrentAppearanceSetting } from 'wallet/src/features/appearance/hooks'
import { AppearanceSettingType, setSelectedAppearanceSettings } from 'wallet/src/features/appearance/slice'
export default function ThemeToggle(): JSX.Element {
const dispatch = useDispatch()
const { t } = useTranslation()
const currentAppearanceSetting = useCurrentAppearanceSetting()
const defaultOptions = [
{
value: AppearanceSettingType.System,
display: (
<Text variant="buttonLabel4" color="$neutral1">
{t('settings.setting.appearance.option.auto')}
</Text>
),
},
{
value: AppearanceSettingType.Light,
display: <Sun size="$icon.20" color="$neutral2" />,
},
{
value: AppearanceSettingType.Dark,
display: <Moon size="$icon.20" color="$neutral2" />,
},
]
const switchMode = useCallback(
(mode: AppearanceSettingType) => dispatch(setSelectedAppearanceSettings(mode)),
[dispatch],
)
return (
<Flex
alignItems="center"
flexDirection="row"
gap="$spacing16"
justifyContent="space-between"
px={SCREEN_ITEM_HORIZONTAL_PAD}
py="$spacing4"
>
<Flex row gap="$spacing12">
<Contrast color="$neutral2" size="$icon.24" />
<Text>{t('settings.setting.appearance.title')}</Text>
</Flex>
<Flex>
<SegmentedControl
options={defaultOptions}
selectedOption={currentAppearanceSetting}
onSelectOption={switchMode}
/>
</Flex>
</Flex>
)
}
......@@ -6,7 +6,6 @@ export enum TopLevelRoutes {
}
export enum OnboardingRoutes {
Claim = 'claim',
Create = 'create',
Import = 'import',
ImportPasskey = 'import-passkey',
......@@ -43,6 +42,7 @@ export enum SettingsRoutes {
BackupRecoveryPhrase = 'backup-recovery-phrase',
RemoveRecoveryPhrase = 'remove-recovery-phrase',
ManageConnections = 'manage-connections',
SmartWallet = 'smart-wallet',
}
export enum RemoveRecoveryPhraseRoutes {
......
......@@ -10,9 +10,5 @@
*/
setTimeout(() => {
const script = document.createElement('script')
script.type = 'text/javascript'
script.async = true
script.src = './sidebar.js'
document.body.appendChild(script)
import('src/entry/sidebar')
}, 10)
......@@ -12,6 +12,7 @@ import {
readDeprecatedReduxedChromeStorage,
} from 'src/store/reduxedChromeStorageToReduxPersistMigration'
import { fiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api'
import { delegationListenerMiddleware } from 'uniswap/src/features/smartWallet/delegation/slice'
import { createDatadogReduxEnhancer } from 'utilities/src/logger/datadog/Datadog'
import { createStore } from 'wallet/src/state'
import { createMigrate } from 'wallet/src/state/createMigrate'
......@@ -39,7 +40,7 @@ const setupStore = (preloadedState?: PreloadedState<ExtensionState>): ReturnType
preloadedState,
additionalSagas: [rootExtensionSaga],
middlewareBefore: __DEV__ ? [loggerMiddleware] : [],
middlewareAfter: [fiatOnRampAggregatorApi.middleware],
middlewareAfter: [fiatOnRampAggregatorApi.middleware, delegationListenerMiddleware.middleware],
enhancers: [dataDogReduxEnhancer],
})
}
......
......@@ -111,13 +111,14 @@ const {
}
: {},
compress: false,
hot: true, // Enable HMR
static: {
directory: path.join(__dirname, '../dev'),
},
client: {
// logging: "info",
progress: true,
reconnect: false,
reconnect: true,
overlay: {
errors: true,
warnings: false,
......@@ -281,6 +282,7 @@ module.exports = (env) => {
'react-native-vector-icons$': 'react-native-vector-icons/dist',
src: path.resolve(__dirname, 'src'), // absolute imports in apps/web
'react-native-gesture-handler$': require.resolve('react-native-gesture-handler'),
'expo-blur': require.resolve('./__mocks__/expo-blur.js'),
},
// Add support for web-based extensions so we can share code between mobile/extension
extensions: [
......
......@@ -9,7 +9,29 @@
<locale android:name="ja"/>
<locale android:name="pt"/>
<locale android:name="vi"/>
<locale android:name="es-ES"/>
<locale android:name="es-US"/>
<!-- Spanish locales that use `,` as the decimal separator -->
<locale android:name="es-419"/>
<locale android:name="es-BZ"/>
<locale android:name="es-CU"/>
<locale android:name="es-DO"/>
<locale android:name="es-GT"/>
<locale android:name="es-HN"/>
<locale android:name="es-MX"/>
<locale android:name="es-NI"/>
<locale android:name="es-PA"/>
<locale android:name="es-PE"/>
<locale android:name="es-PR"/>
<locale android:name="es-SV"/>
<locale android:name="es-US"/>
<!-- Spanish locales that use `.` as the decimal separator -->
<locale android:name="es-AR"/>
<locale android:name="es-BO"/>
<locale android:name="es-CL"/>
<locale android:name="es-CO"/>
<locale android:name="es-CR"/>
<locale android:name="es-EC"/>
<locale android:name="es-ES"/>
<locale android:name="es-PY"/>
<locale android:name="es-UY"/>
<locale android:name="es-VE"/>
</locale-config>
......@@ -1253,7 +1253,7 @@ PODS:
- Firebase/Firestore (11.2.0):
- Firebase/CoreOnly
- FirebaseFirestore (~> 11.2.0)
- FirebaseAppCheckInterop (11.7.0)
- FirebaseAppCheckInterop (11.12.0)
- FirebaseAuth (11.2.0):
- FirebaseAppCheckInterop (~> 11.0)
- FirebaseAuthInterop (~> 11.0)
......@@ -1263,14 +1263,14 @@ PODS:
- GoogleUtilities/Environment (~> 8.0)
- GTMSessionFetcher/Core (~> 3.4)
- RecaptchaInterop (~> 100.0)
- FirebaseAuthInterop (11.7.0)
- FirebaseAuthInterop (11.12.0)
- FirebaseCore (11.2.0):
- FirebaseCoreInternal (~> 11.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Logger (~> 8.0)
- FirebaseCoreExtension (11.4.1):
- FirebaseCore (~> 11.0)
- FirebaseCoreInternal (11.7.0):
- FirebaseCoreInternal (11.12.0):
- "GoogleUtilities/NSData+zlib (~> 8.0)"
- FirebaseFirestore (11.2.0):
- FirebaseCore (~> 11.0)
......@@ -1292,28 +1292,28 @@ PODS:
- gRPC-Core (~> 1.65.0)
- leveldb-library (~> 1.22)
- nanopb (~> 3.30910.0)
- FirebaseSharedSwift (11.7.0)
- FirebaseSharedSwift (11.12.0)
- fmt (11.0.2)
- glog (0.3.5)
- GoogleUtilities/AppDelegateSwizzler (8.0.2):
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.0.2):
- GoogleUtilities/Environment (8.1.0):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.0.2):
- GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.0.2):
- GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.0.2)":
- "GoogleUtilities/NSData+zlib (8.1.0)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.0.2)
- GoogleUtilities/Reachability (8.0.2):
- GoogleUtilities/Privacy (8.1.0)
- GoogleUtilities/Reachability (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- "gRPC-C++ (1.65.5)":
......@@ -1411,21 +1411,21 @@ PODS:
- hermes-engine/Pre-built (= 0.76.9)
- hermes-engine/Pre-built (0.76.9)
- leveldb-library (1.22.6)
- libwebp (1.3.2):
- libwebp/demux (= 1.3.2)
- libwebp/mux (= 1.3.2)
- libwebp/sharpyuv (= 1.3.2)
- libwebp/webp (= 1.3.2)
- libwebp/demux (1.3.2):
- libwebp (1.5.0):
- libwebp/demux (= 1.5.0)
- libwebp/mux (= 1.5.0)
- libwebp/sharpyuv (= 1.5.0)
- libwebp/webp (= 1.5.0)
- libwebp/demux (1.5.0):
- libwebp/webp
- libwebp/mux (1.3.2):
- libwebp/mux (1.5.0):
- libwebp/demux
- libwebp/sharpyuv (1.3.2)
- libwebp/webp (1.3.2):
- libwebp/sharpyuv (1.5.0)
- libwebp/webp (1.5.0):
- libwebp/sharpyuv
- MMKV (2.0.2):
- MMKVCore (~> 2.0.2)
- MMKVCore (2.0.2)
- MMKV (2.2.2):
- MMKVCore (~> 2.2.2)
- MMKVCore (2.2.2)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
......@@ -3861,26 +3861,26 @@ SPEC CHECKSUMS:
fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6
FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45
Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c
FirebaseAppCheckInterop: 2376d3ec5cb4267facad4fe754ab4f301a5a519b
FirebaseAppCheckInterop: 73b173e5ec45192e2d522ad43f526a82ad10b852
FirebaseAuth: 2a198b8cdbbbd457f08d74df7040feb0a0e7777a
FirebaseAuthInterop: a6973d72aa242ea88ffb6be9c9b06c65455071da
FirebaseAuthInterop: b583210c039a60ed3f1e48865e1f3da44a796595
FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da
FirebaseCoreExtension: f1bc67a4702931a7caa097d8e4ac0a1b0d16720e
FirebaseCoreInternal: d6c17dafc8dc33614733a8b52df78fcb4394c881
FirebaseCoreInternal: 480d23e21f699cc7bcbc3d8eaa301f15d1e3ffaf
FirebaseFirestore: 62708adbc1dfcd6d165a7c0a202067b441912dc9
FirebaseFirestoreInternal: ad9b9ee2d3d430c8f31333a69b3b6737a7206232
FirebaseSharedSwift: a45efd84d60ebbfdcdbaebc66948af3630459e62
FirebaseSharedSwift: d2475748a2d2a36242ed13baa34b2acda846c925
fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
"gRPC-C++": 2fa52b3141e7789a28a737f251e0c45b4cb20a87
gRPC-Core: a27c294d6149e1c39a7d173527119cfbc3375ce4
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
MMKV: 3eacda84cd1c4fc95cf848d3ecb69d85ed56006c
MMKVCore: 508b4d3a8ce031f1b5c8bd235f0517fb3f4c73a9
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
MMKV: b4802ebd5a7c68fc0c4a5ccb4926fbdfb62d68e0
MMKVCore: a255341a3746955f50da2ad9121b18cb2b346e61
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774
OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104
......
......@@ -23,9 +23,31 @@
<string>ja</string>
<string>pt</string>
<string>vi</string>
<string>es-ES</string>
<string>es-US</string>
<!-- Spanish locales that use `,` as the decimal separator -->
<string>es-419</string>
<string>es-BZ</string>
<string>es-CU</string>
<string>es-DO</string>
<string>es-GT</string>
<string>es-HN</string>
<string>es-MX</string>
<string>es-NI</string>
<string>es-PA</string>
<string>es-PE</string>
<string>es-PR</string>
<string>es-SV</string>
<string>es-US</string>
<!-- Spanish locales that use `.` as the decimal separator -->
<string>es-AR</string>
<string>es-BO</string>
<string>es-CL</string>
<string>es-CO</string>
<string>es-CR</string>
<string>es-EC</string>
<string>es-ES</string>
<string>es-PY</string>
<string>es-UY</string>
<string>es-VE</string>
</array>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
......
b5838bdddb0692a13d0a061696f7ea1af4f7b16152b8824052498e1a494a4988
\ No newline at end of file
7a03144e3423ea77bd883f7e79a0e3d6a3c54ea3e98424ac288e5f817f88931c
\ No newline at end of file
#!/bin/bash
MAX_SIZE=21.1
MAX_SIZE=23
MAX_BUFFER=0.5
# Check OS type and use appropriate stat command
......
......@@ -16,6 +16,7 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { ShareableEntity } from 'uniswap/src/types/sharing'
import { buildCurrencyId } from 'uniswap/src/utils/currencyId'
import { closeKeyboardBeforeCallback } from 'utilities/src/device/keyboard/dismissNativeKeyboard'
import { logger } from 'utilities/src/logger/logger'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import {
......@@ -120,7 +121,9 @@ function useNavigateToHomepageTab(tab: HomeScreenTabIndex): () => void {
const { navigate } = useAppStackNavigation()
return useCallback((): void => {
closeKeyboardBeforeCallback(() => {
navigate(MobileScreens.Home, { tab })
})
}, [navigate, tab])
}
......@@ -128,7 +131,9 @@ function useNavigateToReceive(): () => void {
const dispatch = useDispatch()
return useCallback((): void => {
closeKeyboardBeforeCallback(() => {
dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }))
})
}, [dispatch])
}
......@@ -137,8 +142,10 @@ function useNavigateToSend(): (args: NavigateToSendFlowArgs) => void {
return useCallback(
(args: NavigateToSendFlowArgs) => {
closeKeyboardBeforeCallback(() => {
const initialSendState = getNavigateToSendFlowArgsInitialState(args)
dispatch(openModal({ name: ModalName.Send, initialState: initialSendState }))
})
},
[dispatch],
)
......@@ -151,6 +158,7 @@ function useNavigateToSwapFlow(): (args: NavigateToSwapFlowArgs) => void {
return useCallback(
(args: NavigateToSwapFlowArgs): void => {
closeKeyboardBeforeCallback(() => {
const initialState = getNavigateToSwapFlowArgsInitialState(args, defaultChainId)
// If no prefilled token, go directly to swap
......@@ -171,6 +179,7 @@ function useNavigateToSwapFlow(): (args: NavigateToSwapFlowArgs) => void {
},
},
})
})
},
[dispatch, defaultChainId, navigate],
)
......@@ -182,6 +191,7 @@ function useNavigateToTokenDetails(): (currencyId: string) => void {
return useCallback(
(currencyId: string): void => {
closeKeyboardBeforeCallback(() => {
dispatch(closeModal({ name: ModalName.Swap }))
dispatch(closeAllModals())
if (exploreNavigationRef.current && exploreNavigationRef.isFocused()) {
......@@ -189,6 +199,7 @@ function useNavigateToTokenDetails(): (currencyId: string) => void {
} else {
appNavigation.navigate(MobileScreens.TokenDetails, { currencyId })
}
})
},
[appNavigation, dispatch],
)
......@@ -199,6 +210,7 @@ function useNavigateToNftDetails(): (args: NavigateToNftItemArgs) => void {
return useCallback(
({ owner, address, tokenId, isSpam, fallbackData }: NavigateToNftItemArgs): void => {
closeKeyboardBeforeCallback(() => {
navigation.navigate(MobileScreens.NFTItem, {
owner,
address,
......@@ -206,6 +218,7 @@ function useNavigateToNftDetails(): (args: NavigateToNftItemArgs) => void {
isSpam,
fallbackData,
})
})
},
[navigation],
)
......@@ -216,6 +229,7 @@ function useNavigateToNftCollection(): (args: NavigateToNftCollectionArgs) => vo
return useCallback(
({ collectionAddress }: NavigateToNftCollectionArgs): void => {
closeKeyboardBeforeCallback(() => {
if (exploreNavigationRef.current && exploreNavigationRef.isFocused()) {
exploreNavigationRef.navigate(MobileScreens.NFTCollection, {
collectionAddress,
......@@ -225,6 +239,7 @@ function useNavigateToNftCollection(): (args: NavigateToNftCollectionArgs) => vo
collectionAddress,
})
}
})
},
[appNavigation],
)
......@@ -242,6 +257,7 @@ function useNavigateToBuyOrReceiveWithEmptyWallet(): () => void {
)
return useCallback((): void => {
closeKeyboardBeforeCallback(() => {
dispatch(closeModal({ name: ModalName.Send }))
if (forAggregatorEnabled) {
......@@ -254,6 +270,7 @@ function useNavigateToBuyOrReceiveWithEmptyWallet(): () => void {
}),
)
}
})
}, [dispatch, forAggregatorEnabled])
}
......@@ -262,7 +279,9 @@ function useNavigateToFiatOnRamp(): (args: NavigateToFiatOnRampArgs) => void {
return useCallback(
({ prefilledCurrency, isOfframp }: NavigateToFiatOnRampArgs): void => {
closeKeyboardBeforeCallback(() => {
dispatch(openModal({ name: ModalName.FiatOnRampAggregator, initialState: { prefilledCurrency, isOfframp } }))
})
},
[dispatch],
)
......@@ -273,11 +292,13 @@ function useNavigateToExternalProfile(): (args: NavigateToExternalProfileArgs) =
return useCallback(
({ address }: NavigateToExternalProfileArgs): void => {
closeKeyboardBeforeCallback(() => {
if (exploreNavigationRef.isFocused()) {
exploreNavigationRef.navigate(MobileScreens.ExternalProfile, { address })
} else {
appNavigation.navigate(MobileScreens.ExternalProfile, { address })
}
})
},
[appNavigation],
)
......
......@@ -150,18 +150,33 @@ exports[`AccountSwitcher renders correctly 1`] = `
</View>
</View>
<View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={16}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -174,6 +189,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
testID="copy"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -187,6 +204,12 @@ exports[`AccountSwitcher renders correctly 1`] = `
"borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999,
"borderTopRightRadius": 999999,
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 4,
"justifyContent": "center",
......@@ -201,6 +224,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -439,25 +464,32 @@ exports[`AccountSwitcher renders correctly 1`] = `
</View>
</View>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"marginTop": 16,
"opacity": 1,
......@@ -470,9 +502,17 @@ exports[`AccountSwitcher renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 8,
"marginLeft": 24,
......@@ -480,6 +520,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -601,6 +643,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.2}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......
......@@ -261,7 +261,11 @@ function ExploreTabBarButton({ activeScale = 0.98, onLayout, isNarrow }: Explore
const Wrapper = isIOS ? BlurView : Flex
return (
<TouchableArea activeOpacity={1} style={[styles.searchBar, { borderRadius: borderRadii.roundedFull }]}>
<TouchableArea
activeOpacity={1}
style={[styles.searchBar, { borderRadius: borderRadii.roundedFull }]}
dd-action-name={TestID.SearchTokensAndWallets}
>
<TapGestureHandler testID={TestID.SearchTokensAndWallets} onGestureEvent={onGestureEvent}>
<AnimatedFlex
borderRadius="$roundedFull"
......
......@@ -108,6 +108,7 @@ import {
UnitagStackParamList,
} from 'uniswap/src/types/screens/mobile'
import { OnboardingContextProvider } from 'wallet/src/features/onboarding/OnboardingContext'
import { SmartWalletSettings } from 'wallet/src/features/smartWallet/SmartWalletSettings'
import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks'
import { selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors'
......@@ -147,6 +148,7 @@ function SettingsStackGroup(): JSX.Element {
name={MobileScreens.SettingsCloudBackupProcessing}
/>
<SettingsStack.Screen component={SettingsCloudBackupStatus} name={MobileScreens.SettingsCloudBackupStatus} />
<SettingsStack.Screen component={SmartWalletSettings} name={MobileScreens.SettingsSmartWallet} />
<SettingsStack.Screen component={SettingsPrivacyScreen} name={MobileScreens.SettingsPrivacy} />
<SettingsStack.Screen component={SettingsNotificationsScreen} name={MobileScreens.SettingsNotifications} />
<SettingsStack.Group screenOptions={navNativeStackOptions.presentationBottomSheet}>
......
......@@ -18,7 +18,6 @@ import { HomeScreenTabIndex } from 'src/screens/HomeScreen/HomeScreenTabIndex'
import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState'
import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types'
import { PasskeyManagementModalState } from 'uniswap/src/features/passkey/PasskeyManagementModal'
import { SmartWalletAdvancedSettingsModalState } from 'uniswap/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestnetModeModalState } from 'uniswap/src/features/testnets/TestnetModeModal'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
......@@ -30,6 +29,7 @@ import {
UnitagStackParamList,
} from 'uniswap/src/types/screens/mobile'
import { NFTItem } from 'wallet/src/features/nfts/types'
import { SmartWalletAdvancedSettingsModalState } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
type NFTItemScreenParams = {
owner?: Address
......@@ -90,6 +90,7 @@ export type SettingsStackParamList = {
[MobileScreens.SettingsLanguage]: undefined
[MobileScreens.SettingsNotifications]: undefined
[MobileScreens.SettingsPrivacy]: undefined
[MobileScreens.SettingsSmartWallet]: { Wrapper: React.FC<{ children: React.ReactNode }> }
[MobileScreens.SettingsViewSeedPhrase]: { address: Address; walletNeedsRestore?: boolean }
[MobileScreens.SettingsWallet]: { address: Address }
[MobileScreens.SettingsWalletEdit]: { address: Address }
......
......@@ -5,6 +5,7 @@ import { MOBILE_STATE_VERSION, migrations } from 'src/app/migrations'
import { MobileState, mobilePersistedStateList, mobileReducer } from 'src/app/mobileReducer'
import { rootMobileSaga } from 'src/app/saga'
import { fiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api'
import { delegationListenerMiddleware } from 'uniswap/src/features/smartWallet/delegation/slice'
import { isNonJestDev } from 'utilities/src/environment/constants'
import { createDatadogReduxEnhancer } from 'utilities/src/logger/datadog/Datadog'
import { createStore } from 'wallet/src/state'
......@@ -51,7 +52,7 @@ if (isNonJestDev) {
enhancers.push(reactotron.createEnhancer())
}
const middlewares: Middleware[] = [fiatOnRampAggregatorApi.middleware]
const middlewares: Middleware[] = [fiatOnRampAggregatorApi.middleware, delegationListenerMiddleware.middleware]
const setupStore = (
preloadedState?: PreloadedState<MobileState>,
......
......@@ -18,10 +18,10 @@ import { Flex, Skeleton, Switch, Text, TouchableArea, useSporeColors } from 'ui/
import { Arrow } from 'ui/src/components/arrow/Arrow'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { SmartWalletAdvancedSettingsModalState } from 'uniswap/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking'
import { SmartWalletAdvancedSettingsModalState } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
export const SETTINGS_ROW_HEIGHT = 60
......
......@@ -128,6 +128,7 @@ describe('TraceUserProperties', () => {
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletAddress, 'address', undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletType, AccountType.SignerMnemonic, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsCloudBackedUp, true, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.BackupTypes, [BackupType.Cloud], undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsPushEnabled, true, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSmallBalancesEnabled, false, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSpamTokensEnabled, true, undefined)
......@@ -148,7 +149,7 @@ describe('TraceUserProperties', () => {
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Language, 'English', undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Currency, 'USD', undefined)
expect(mocked).toHaveBeenCalledTimes(20)
expect(mocked).toHaveBeenCalledTimes(21)
})
it('sets user properties without active account', async () => {
......
......@@ -115,11 +115,13 @@ export function TraceUserProperties(): null {
if (!activeAccount) {
return
}
if (activeAccount.backups) {
setUserProperty(MobileUserPropertyName.BackupTypes, activeAccount.backups)
}
setUserProperty(MobileUserPropertyName.ActiveWalletAddress, activeAccount.address)
setUserProperty(MobileUserPropertyName.ActiveWalletType, activeAccount.type)
setUserProperty(MobileUserPropertyName.IsCloudBackedUp, hasBackup(BackupType.Cloud, activeAccount))
setUserProperty(MobileUserPropertyName.IsPushEnabled, Boolean(activeAccount.pushNotificationsEnabled))
setUserProperty(MobileUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances)
setUserProperty(MobileUserPropertyName.IsHideSpamTokensEnabled, hideSpamTokens)
}, [allowAnalytics, activeAccount, hideSmallBalances, hideSpamTokens])
......
......@@ -137,6 +137,7 @@ export function AccountHeader(): JSX.Element {
flexDirection="row"
hitSlop={20}
testID={TestID.AccountHeaderAvatar}
dd-action-name={TestID.AccountHeaderAvatar}
onLongPress={async (): Promise<void> => {
if (isDevEnv()) {
navigate(ModalName.Experiments)
......@@ -180,7 +181,12 @@ export function AccountHeader(): JSX.Element {
)}
</Flex>
<Flex row alignItems="flex-start" gap="$spacing16">
<TouchableArea scaleTo={SCAN_ICON_ACTIVE_SCALE} activeOpacity={1} onPress={onPressScan}>
<TouchableArea
scaleTo={SCAN_ICON_ACTIVE_SCALE}
activeOpacity={1}
dd-action-name="Scan"
onPress={onPressScan}
>
<ScanHome color="$neutral2" size="$icon.28" />
</TouchableArea>
<RotatingSettingsIcon onPressSettings={onPressSettings} />
......
......@@ -14,25 +14,32 @@ exports[`AccountCardItem renders correctly 1`] = `
onPress={[Function]}
>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"paddingBottom": 12,
......@@ -48,9 +55,17 @@ exports[`AccountCardItem renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "flex-start",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 16,
}
......@@ -58,6 +73,8 @@ exports[`AccountCardItem renders correctly 1`] = `
testID="account-item/0x82D56A352367453f74FC0dC7B071b311da373Fa6"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flex": 1,
......@@ -66,6 +83,8 @@ exports[`AccountCardItem renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -77,6 +96,8 @@ exports[`AccountCardItem renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -85,6 +106,8 @@ exports[`AccountCardItem renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -133,6 +156,8 @@ exports[`AccountCardItem renders correctly 1`] = `
testID="account-icon"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -144,6 +169,8 @@ exports[`AccountCardItem renders correctly 1`] = `
</View>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -153,6 +180,8 @@ exports[`AccountCardItem renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "row",
......@@ -161,6 +190,8 @@ exports[`AccountCardItem renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -176,6 +207,8 @@ exports[`AccountCardItem renders correctly 1`] = `
ellipsizeMode="tail"
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -204,6 +237,8 @@ exports[`AccountCardItem renders correctly 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......
......@@ -44,19 +44,35 @@ exports[`AccountHeader renders correctly 1`] = `
}
>
<View
collapsable={false}
dd-action-name="account-header-avatar"
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={20}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"alignItems": "center",
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "row",
"opacity": 1,
"transform": [
......@@ -69,6 +85,8 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-header-avatar"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -117,6 +135,8 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-icon"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -140,18 +160,33 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-header/display-name"
>
<View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={20}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"flexGrow": 1,
"opacity": 1,
......@@ -164,6 +199,8 @@ exports[`AccountHeader renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -173,6 +210,8 @@ exports[`AccountHeader renders correctly 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "row",
......@@ -185,6 +224,8 @@ exports[`AccountHeader renders correctly 1`] = `
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -214,25 +255,32 @@ exports[`AccountHeader renders correctly 1`] = `
</Text>
</View>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -245,9 +293,17 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-header-copy-address"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 4,
}
......@@ -257,6 +313,8 @@ exports[`AccountHeader renders correctly 1`] = `
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -356,25 +414,33 @@ exports[`AccountHeader renders correctly 1`] = `
}
>
<View
hitSlop={
collapsable={false}
dd-action-name="Scan"
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......
......@@ -86,25 +86,32 @@ exports[`AccountList renders without error 1`] = `
onPress={[Function]}
>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"paddingBottom": 12,
......@@ -120,9 +127,17 @@ exports[`AccountList renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "flex-start",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 16,
}
......@@ -130,6 +145,8 @@ exports[`AccountList renders without error 1`] = `
testID="account-item/0x82D56A352367453f74FC0dC7B071b311da373Fa6"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flex": 1,
......@@ -138,6 +155,8 @@ exports[`AccountList renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -149,6 +168,8 @@ exports[`AccountList renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -157,6 +178,8 @@ exports[`AccountList renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -205,6 +228,8 @@ exports[`AccountList renders without error 1`] = `
testID="account-icon"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -216,6 +241,8 @@ exports[`AccountList renders without error 1`] = `
</View>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -225,6 +252,8 @@ exports[`AccountList renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "row",
......@@ -233,6 +262,8 @@ exports[`AccountList renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -248,6 +279,8 @@ exports[`AccountList renders without error 1`] = `
ellipsizeMode="tail"
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -276,6 +309,8 @@ exports[`AccountList renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......
......@@ -20,7 +20,14 @@ export function BackButton({ onPressBack, size, color, showButtonLabel, ...rest
navigation.goBack()
}
return (
<TouchableArea alignItems="center" hitSlop={24} testID={TestID.Back} onPress={goBack} {...rest}>
<TouchableArea
alignItems="center"
hitSlop={24}
testID={TestID.Back}
dd-action-name={TestID.Back}
onPress={goBack}
{...rest}
>
<BackButtonView color={color} showButtonLabel={showButtonLabel} size={size} />
</TouchableArea>
)
......
......@@ -2,19 +2,35 @@
exports[`BackButton renders without error 1`] = `
<View
collapsable={false}
dd-action-name="back"
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={24}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"alignItems": "center",
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -27,6 +43,8 @@ exports[`BackButton renders without error 1`] = `
testID="back"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -53,6 +71,8 @@ exports[`BackButton renders without error 1`] = `
},
}
}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -87,7 +107,7 @@ exports[`BackButton renders without error 1`] = `
"borderWidth": 0,
},
{
"color": "rgba(19, 19, 19, 0.63)",
"color": "#222222",
"height": 24,
"width": 24,
},
......@@ -102,7 +122,7 @@ exports[`BackButton renders without error 1`] = `
vbWidth={24}
>
<RNSVGGroup
color="rgba(19, 19, 19, 0.63)"
color="#222222"
fill={null}
propList={
[
......@@ -134,6 +154,8 @@ exports[`BackButton renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......
......@@ -2,25 +2,32 @@
exports[`CloseButton renders without error 1`] = `
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -49,6 +56,7 @@ exports[`CloseButton renders without error 1`] = `
"borderWidth": 0,
},
{
"color": "#222222",
"height": 20,
"width": 20,
},
......@@ -63,6 +71,7 @@ exports[`CloseButton renders without error 1`] = `
vbWidth={16}
>
<RNSVGGroup
color="#222222"
fill={null}
propList={
[
......
......@@ -14,9 +14,7 @@ export default function RemoveButton({ visible = true, ...rest }: RemoveButtonPr
disabled={!visible}
height={imageSizes.image24}
justifyContent="center"
style={{
opacity: visible ? 1 : 0,
}}
opacity={visible ? 1 : 0}
testID="explore/remove-button"
width={imageSizes.image24}
zIndex="$tooltip"
......
......@@ -36,18 +36,33 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = `
Editing Title
</Text>
<View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={16}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -61,6 +76,8 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.2}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -120,18 +137,33 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
Title
</Text>
<View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={16}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......
......@@ -36,31 +36,27 @@ exports[`FavoriteTokenCard renders without error 1`] = `
>
<View
collapsable={false}
hitSlop={
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": {
"dynamic": {
"dark": "#131313",
"light": "#FFFFFF",
},
},
"backgroundColor": "#FFFFFF",
"borderBottomColor": "rgba(34,34,34,0.05)",
"borderBottomLeftRadius": 16,
"borderBottomRightRadius": 16,
......@@ -94,9 +90,17 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="token-box-undefined"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "flex-start",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "column",
"gap": 8,
"paddingBottom": 12,
......@@ -107,6 +111,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "row",
......@@ -116,6 +122,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -126,6 +134,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -136,6 +146,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="shimmer-placeholder"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -143,6 +155,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -166,6 +180,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -184,25 +200,22 @@ exports[`FavoriteTokenCard renders without error 1`] = `
/>
</View>
<View
aria-disabled={true}
collapsable={false}
disabled={true}
hitSlop={
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onLayout={[Function]}
pointerEvents="box-none"
role="button"
style={
{
"alignItems": "center",
"backgroundColor": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.38)",
"light": "rgba(19, 19, 19, 0.35)",
},
},
"backgroundColor": "rgba(19, 19, 19, 0.35)",
"borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999,
......@@ -220,21 +233,31 @@ exports[`FavoriteTokenCard renders without error 1`] = `
"zIndex": 1080,
}
}
tabIndex={-1}
testID="explore/remove-button"
userSelect="none"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
"dynamic": {
"dark": "#131313",
"light": "#FFFFFF",
"dark": "#1F1F1F",
"light": "#F9F9F9",
},
},
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"color": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.65)",
"light": "rgba(19, 19, 19, 0.63)",
},
},
"flexDirection": "column",
"height": 2,
"width": 10,
......@@ -244,6 +267,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
</View>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -252,6 +277,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -262,6 +289,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="shimmer-placeholder"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -269,6 +298,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -291,6 +322,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
</View>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -301,6 +334,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="shimmer-placeholder"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -308,6 +343,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......
......@@ -23,32 +23,28 @@ exports[`FavoriteWalletCard renders without error 1`] = `
}
>
<View
disabled={false}
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": {
"dynamic": {
"dark": "#131313",
"light": "#FFFFFF",
},
},
"backgroundColor": "#FFFFFF",
"borderBottomColor": "rgba(34,34,34,0.05)",
"borderBottomLeftRadius": 16,
"borderBottomRightRadius": 16,
......@@ -86,8 +82,16 @@ exports[`FavoriteWalletCard renders without error 1`] = `
testID="favorite-wallet-card"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 4,
"justifyContent": "space-between",
......@@ -99,6 +103,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -109,6 +115,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -157,6 +165,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
testID="account-icon"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -167,6 +177,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
/>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -179,6 +191,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -201,25 +215,22 @@ exports[`FavoriteWalletCard renders without error 1`] = `
</View>
</View>
<View
aria-disabled={true}
collapsable={false}
disabled={true}
hitSlop={
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onLayout={[Function]}
pointerEvents="box-none"
role="button"
style={
{
"alignItems": "center",
"backgroundColor": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.38)",
"light": "rgba(19, 19, 19, 0.35)",
},
},
"backgroundColor": "rgba(19, 19, 19, 0.35)",
"borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999,
......@@ -237,21 +248,31 @@ exports[`FavoriteWalletCard renders without error 1`] = `
"zIndex": 1080,
}
}
tabIndex={-1}
testID="explore/remove-button"
userSelect="none"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
"dynamic": {
"dark": "#131313",
"light": "#FFFFFF",
"dark": "#1F1F1F",
"light": "#F9F9F9",
},
},
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"color": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.65)",
"light": "rgba(19, 19, 19, 0.63)",
},
},
"flexDirection": "column",
"height": 2,
"width": 10,
......
......@@ -3,33 +3,28 @@
exports[`RemoveButton renders without error 1`] = `
<View
collapsable={false}
disabled={false}
hitSlop={
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"alignItems": "center",
"backgroundColor": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.38)",
"light": "rgba(19, 19, 19, 0.35)",
},
},
"backgroundColor": "rgba(19, 19, 19, 0.35)",
"borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999,
......@@ -50,6 +45,8 @@ exports[`RemoveButton renders without error 1`] = `
testID="explore/remove-button"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": {
......@@ -62,6 +59,12 @@ exports[`RemoveButton renders without error 1`] = `
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "column",
"height": 2,
"width": 10,
......
......@@ -25,25 +25,32 @@ exports[`SortButton renders without error 1`] = `
}
>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -56,9 +63,17 @@ exports[`SortButton renders without error 1`] = `
>
<View
collapsable={false}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 8,
"justifyContent": "center",
......@@ -73,6 +88,8 @@ exports[`SortButton renders without error 1`] = `
lineBreakMode="clip"
maxFontSizeMultiplier={1.2}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -110,6 +127,8 @@ exports[`SortButton renders without error 1`] = `
},
}
}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......
......@@ -6,25 +6,32 @@ exports[`TokenItem renders without error 1`] = `
onPress={[MockFunction]}
>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -38,9 +45,17 @@ exports[`TokenItem renders without error 1`] = `
>
<View
collapsable={false}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"flexGrow": 1,
"gap": 12,
......@@ -52,6 +67,8 @@ exports[`TokenItem renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -62,6 +79,8 @@ exports[`TokenItem renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -73,6 +92,8 @@ exports[`TokenItem renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -93,6 +114,8 @@ exports[`TokenItem renders without error 1`] = `
</Text>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
pointerEvents="auto"
style={
{
......@@ -107,6 +130,8 @@ exports[`TokenItem renders without error 1`] = `
testID="token-logo"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"backgroundColor": "#FFFFFF",
......@@ -148,6 +173,8 @@ exports[`TokenItem renders without error 1`] = `
</View>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flex": 1,
......@@ -161,6 +188,8 @@ exports[`TokenItem renders without error 1`] = `
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -183,6 +212,8 @@ exports[`TokenItem renders without error 1`] = `
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -202,6 +233,8 @@ exports[`TokenItem renders without error 1`] = `
/>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]}
style={
{
......@@ -212,6 +245,8 @@ exports[`TokenItem renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "row",
......@@ -219,6 +254,8 @@ exports[`TokenItem renders without error 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "flex-end",
......@@ -231,6 +268,8 @@ exports[`TokenItem renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -251,6 +290,8 @@ exports[`TokenItem renders without error 1`] = `
-
</Text>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -262,6 +303,8 @@ exports[`TokenItem renders without error 1`] = `
testID="relative-change"
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -271,6 +314,8 @@ exports[`TokenItem renders without error 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......
......@@ -38,7 +38,7 @@ function LegacyExploreSearchResultsList({
ListHeaderComponent={<SearchEmptySection selectedChain={chainFilter} />}
data={[]}
keyExtractor={(): string => 'search-empty-section-container'}
keyboardShouldPersistTaps="always"
keyboardShouldPersistTaps="always" // TODO(WALL-6724): does not actually work; behaves as default/"never"
renderItem={null}
scrollEventThrottle={16}
showsHorizontalScrollIndicator={false}
......
import { AppStackScreenProp } from 'src/app/navigation/types'
import { ReactNavigationModal } from 'src/components/modals/ReactNavigationModals/ReactNavigationModal'
import { SmartWalletAdvancedSettingsModal } from 'uniswap/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { SmartWalletAdvancedSettingsModal } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
export const AdvancedSettingsModal = (
props: AppStackScreenProp<typeof ModalName.SmartWalletAdvancedSettingsModal>,
......
......@@ -4,10 +4,10 @@ import { useReactNavigationModal } from 'src/components/modals/useReactNavigatio
import type { GetProps } from 'ui/src'
import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal'
import { PasskeysHelpModal } from 'uniswap/src/features/passkey/PasskeysHelpModal'
import { SmartWalletAdvancedSettingsModal } from 'uniswap/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestnetModeModal } from 'uniswap/src/features/testnets/TestnetModeModal'
import { HiddenTokenInfoModal } from 'uniswap/src/features/transactions/modals/HiddenTokenInfoModal'
import { SmartWalletAdvancedSettingsModal } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
// Define names of shared modals we're explicitly supporting on mobile
type ValidModalNames = keyof Pick<
......
......@@ -46,5 +46,9 @@ export function useLogMissingMnemonic(): void {
tags: { file: 'useLogMissingMnemonic.ts', function: 'logMissingMnemonic' },
})
})
}, [mnemonicId, signerMnemonicAccounts])
// There's a lot of content in the signerMnemonicAccounts array,
// so we don't want to re-run this effect on every render, just when the
// count of accounts changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mnemonicId, signerMnemonicAccounts.length])
}
import { Alert } from 'react-native'
import { navigate } from 'src/app/navigation/rootNavigation'
import { openModal } from 'src/features/modals/modalSlice'
import { dismissInAppBrowser } from 'src/utils/linking'
......@@ -9,6 +10,7 @@ import { FiatOffRampEventName, ModalName } from 'uniswap/src/features/telemetry/
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TransactionScreen } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModalContext'
import { forceFetchFiatOnRampTransactions } from 'uniswap/src/features/transactions/slice'
import i18n from 'uniswap/src/i18n'
import { CurrencyField } from 'uniswap/src/types/currency'
import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { createTransactionId } from 'uniswap/src/utils/createTransactionId'
......@@ -19,9 +21,7 @@ export function* handleOffRampReturnLink(url: URL) {
try {
yield* call(_handleOffRampReturnLink, url)
} catch (error) {
// TODO: handle error in UI
// Alert.alert(i18n.t('walletConnect.error.general.title'), i18n.t('walletConnect.error.general.message'))
// yield* put(openModal({ name: ModalName.Send, initialState: initialSendState }))
Alert.alert(i18n.t('fiatOffRamp.error.populateSend.title'), i18n.t('fiatOffRamp.error.populateSend.description'))
}
}
......@@ -54,7 +54,13 @@ function* _handleOffRampReturnLink(url: URL) {
throw new Error('Failed to fetch offramp transfer details')
}
if (!offRampTransferDetails) {
if (
!offRampTransferDetails ||
!offRampTransferDetails.tokenAddress ||
!offRampTransferDetails.baseCurrencyCode ||
!offRampTransferDetails.depositWalletAddress ||
!!offRampTransferDetails.errorCode
) {
throw new Error('Missing offRampTransferDetails in fiat offramp deep link')
}
......
......@@ -223,31 +223,28 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
}
>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": {
"dynamic": {
"dark": "rgba(255, 55, 199, 0.08)",
"light": "rgba(255, 55, 199, 0.08)",
},
},
"backgroundColor": "rgba(255, 55, 199, 0.08)",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
......@@ -267,9 +264,17 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 4,
"justifyContent": "center",
......@@ -279,6 +284,8 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.2}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......
......@@ -2,26 +2,32 @@
exports[`renders collection preview card 1`] = `
<View
disabled={false}
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"transform": [
......@@ -33,6 +39,8 @@ exports[`renders collection preview card 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -41,6 +49,12 @@ exports[`renders collection preview card 1`] = `
"borderBottomRightRadius": 16,
"borderTopLeftRadius": 16,
"borderTopRightRadius": 16,
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row",
"gap": 8,
"justifyContent": "space-between",
......@@ -52,6 +66,8 @@ exports[`renders collection preview card 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -63,6 +79,8 @@ exports[`renders collection preview card 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"borderBottomLeftRadius": 999999,
......@@ -77,6 +95,8 @@ exports[`renders collection preview card 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -101,6 +121,8 @@ exports[`renders collection preview card 1`] = `
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": {
......@@ -123,6 +145,8 @@ exports[`renders collection preview card 1`] = `
</View>
</View>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -131,6 +155,8 @@ exports[`renders collection preview card 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......@@ -141,6 +167,8 @@ exports[`renders collection preview card 1`] = `
}
>
<View
onBlur={[Function]}
onFocus={[Function]}
style={
{
"flexDirection": "column",
......@@ -152,6 +180,8 @@ exports[`renders collection preview card 1`] = `
allowFontScaling={true}
maxFontSizeMultiplier={1.4}
numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"color": "#131313",
......@@ -297,6 +327,8 @@ exports[`renders collection preview card 1`] = `
},
}
}
onBlur={[Function]}
onFocus={[Function]}
style={
{
"alignItems": "center",
......
......@@ -88,6 +88,7 @@ function SendFormScreenContent({ hideContent }: { hideContent: boolean }): JSX.E
<TransactionModalInnerContainer fullscreen bottomSheetViewStyles={[bottomSheetViewStyles]}>
{showRecipientSelectBottomSheet && (
<Modal
extendOnKeyboardVisible
fullScreen
backgroundColor={colors.surface1.val}
name={ModalName.TokenSelector}
......
......@@ -42,15 +42,117 @@ export function loadLocaleData(langCode) {
case 'es':
require('@formatjs/intl-pluralrules/locale-data/es').default
switch (langCode) {
// Spanish locales that use `,` as the decimal separator
case 'es-419':
require('@formatjs/intl-numberformat/locale-data/es-419').default
require('@formatjs/intl-datetimeformat/locale-data/es-419').default
require('@formatjs/intl-relativetimeformat/locale-data/es-419').default
break
case 'es-BZ':
require('@formatjs/intl-numberformat/locale-data/es-BZ').default
require('@formatjs/intl-datetimeformat/locale-data/es-BZ').default
require('@formatjs/intl-relativetimeformat/locale-data/es-BZ').default
break
case 'es-CU':
require('@formatjs/intl-numberformat/locale-data/es-CU').default
require('@formatjs/intl-datetimeformat/locale-data/es-CU').default
require('@formatjs/intl-relativetimeformat/locale-data/es-CU').default
break
case 'es-DO':
require('@formatjs/intl-numberformat/locale-data/es-DO').default
require('@formatjs/intl-datetimeformat/locale-data/es-DO').default
require('@formatjs/intl-relativetimeformat/locale-data/es-DO').default
break
case 'es-GT':
require('@formatjs/intl-numberformat/locale-data/es-GT').default
require('@formatjs/intl-datetimeformat/locale-data/es-GT').default
require('@formatjs/intl-relativetimeformat/locale-data/es-GT').default
break
case 'es-HN':
require('@formatjs/intl-numberformat/locale-data/es-HN').default
require('@formatjs/intl-datetimeformat/locale-data/es-HN').default
require('@formatjs/intl-relativetimeformat/locale-data/es-HN').default
break
case 'es-MX':
require('@formatjs/intl-numberformat/locale-data/es-MX').default
require('@formatjs/intl-datetimeformat/locale-data/es-MX').default
require('@formatjs/intl-relativetimeformat/locale-data/es-MX').default
break
case 'es-NI':
require('@formatjs/intl-numberformat/locale-data/es-NI').default
require('@formatjs/intl-datetimeformat/locale-data/es-NI').default
require('@formatjs/intl-relativetimeformat/locale-data/es-NI').default
break
case 'es-PA':
require('@formatjs/intl-numberformat/locale-data/es-PA').default
require('@formatjs/intl-datetimeformat/locale-data/es-PA').default
require('@formatjs/intl-relativetimeformat/locale-data/es-PA').default
break
case 'es-PE':
require('@formatjs/intl-numberformat/locale-data/es-PE').default
require('@formatjs/intl-datetimeformat/locale-data/es-PE').default
require('@formatjs/intl-relativetimeformat/locale-data/es-PE').default
break
case 'es-PR':
require('@formatjs/intl-numberformat/locale-data/es-PR').default
require('@formatjs/intl-datetimeformat/locale-data/es-PR').default
require('@formatjs/intl-relativetimeformat/locale-data/es-PR').default
break
case 'es-SV':
require('@formatjs/intl-numberformat/locale-data/es-SV').default
require('@formatjs/intl-datetimeformat/locale-data/es-SV').default
require('@formatjs/intl-relativetimeformat/locale-data/es-SV').default
break
case 'es-US':
require('@formatjs/intl-numberformat/locale-data/es-US').default
require('@formatjs/intl-datetimeformat/locale-data/es-US').default
require('@formatjs/intl-relativetimeformat/locale-data/es-US').default
break
case 'es-419':
require('@formatjs/intl-numberformat/locale-data/es-419').default
require('@formatjs/intl-datetimeformat/locale-data/es-419').default
require('@formatjs/intl-relativetimeformat/locale-data/es-419').default
// Spanish locales that use `.` as the decimal separator
case 'es-AR':
require('@formatjs/intl-numberformat/locale-data/es-AR').default
require('@formatjs/intl-datetimeformat/locale-data/es-AR').default
require('@formatjs/intl-relativetimeformat/locale-data/es-AR').default
break
case 'es-BO':
require('@formatjs/intl-numberformat/locale-data/es-BO').default
require('@formatjs/intl-datetimeformat/locale-data/es-BO').default
require('@formatjs/intl-relativetimeformat/locale-data/es-BO').default
break
case 'es-CL':
require('@formatjs/intl-numberformat/locale-data/es-CL').default
require('@formatjs/intl-datetimeformat/locale-data/es-CL').default
require('@formatjs/intl-relativetimeformat/locale-data/es-CL').default
break
case 'es-CO':
require('@formatjs/intl-numberformat/locale-data/es-CO').default
require('@formatjs/intl-datetimeformat/locale-data/es-CO').default
require('@formatjs/intl-relativetimeformat/locale-data/es-CO').default
break
case 'es-CR':
require('@formatjs/intl-numberformat/locale-data/es-CR').default
require('@formatjs/intl-datetimeformat/locale-data/es-CR').default
require('@formatjs/intl-relativetimeformat/locale-data/es-CR').default
break
case 'es-EC':
require('@formatjs/intl-numberformat/locale-data/es-EC').default
require('@formatjs/intl-datetimeformat/locale-data/es-EC').default
require('@formatjs/intl-relativetimeformat/locale-data/es-EC').default
break
case 'es-PY':
require('@formatjs/intl-numberformat/locale-data/es-PY').default
require('@formatjs/intl-datetimeformat/locale-data/es-PY').default
require('@formatjs/intl-relativetimeformat/locale-data/es-PY').default
break
case 'es-UY':
require('@formatjs/intl-numberformat/locale-data/es-UY').default
require('@formatjs/intl-datetimeformat/locale-data/es-UY').default
require('@formatjs/intl-relativetimeformat/locale-data/es-UY').default
break
case 'es-VE':
require('@formatjs/intl-numberformat/locale-data/es-VE').default
require('@formatjs/intl-datetimeformat/locale-data/es-VE').default
require('@formatjs/intl-relativetimeformat/locale-data/es-VE').default
break
default:
require('@formatjs/intl-numberformat/locale-data/es').default
......
......@@ -93,7 +93,7 @@ export function HomeScreenQuickActions(): JSX.Element {
<Flex centered row gap="$spacing8" px="$spacing12">
{actions.map(({ eventName, name, label, Icon, onPress }) => (
<Trace key={name} logPress element={name} eventOnTrigger={eventName}>
<TouchableArea flex={1} scaleTo={activeScale} onPress={onPress}>
<TouchableArea flex={1} dd-action-name={name} scaleTo={activeScale} onPress={onPress}>
<Flex
fill
backgroundColor="$accent2"
......
......@@ -25,6 +25,7 @@ import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { logger } from 'utilities/src/logger/logger'
const options: ImportMethodOption[] = [seedPhraseImportOption, importFromCloudBackupOption, passKeySignInOption]
......@@ -63,7 +64,12 @@ export function ImportMethodScreen({ navigation, route: { params } }: Props): JS
if (importType === ImportType.Passkey) {
setIsLoadingPasskey(true)
const credential = await authenticateWithPasskeyForSeedPhraseExport()
let credential: string | undefined
try {
credential = await authenticateWithPasskeyForSeedPhraseExport()
} catch (error) {
logger.warn('ImportMethodScreen', 'handleOnPress', 'Error authenticating with passkey', { error })
}
if (!credential) {
navigate(ModalName.PasskeysHelp)
......
......@@ -392,25 +392,32 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
}
>
<View
hitSlop={
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{
"bottom": 5,
"left": 5,
"right": 5,
"top": 5,
"value": {},
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
role="button"
style={
{
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column",
"opacity": 1,
"paddingBottom": 4,
......@@ -502,6 +509,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
}
>
<View
aria-disabled={true}
collapsable={false}
dd-action-name="Continue"
focusVisibleStyle={
......@@ -526,7 +534,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
pointerEvents="none"
pointerEvents="box-none"
style={
{
"alignItems": "center",
......@@ -557,6 +565,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
"paddingTop": 16,
}
}
tabIndex={-1}
testID="continue"
userSelect="none"
>
......
......@@ -132,7 +132,7 @@ export function LandingScreen({ navigation }: Props): JSX.Element {
variant="buttonLabel1"
>
{isEmbeddedWalletEnabled
? t('onboarding.intro.button.signInOrImport')
? t('onboarding.intro.button.logInOrImport')
: t('onboarding.landing.button.add')}
</Text>
</TouchableArea>
......
import { FlashList } from '@shopify/flash-list'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { useReactNavigationModal } from 'src/components/modals/useReactNavigationModal'
import { Flex, Text, TouchableArea } from 'ui/src'
import { AnimatedBottomSheetFlashList } from 'ui/src/components/AnimatedFlashList/AnimatedFlashList'
import { Check } from 'ui/src/components/icons'
import { Modal } from 'uniswap/src/components/modals/Modal'
import { FiatCurrency, ORDERED_CURRENCIES } from 'uniswap/src/features/fiatCurrency/constants'
......@@ -29,11 +29,7 @@ export function SettingsFiatCurrencyModal(): JSX.Element {
<Text pb="$spacing12" textAlign="center" variant="subheading1">
{t('settings.setting.currency.title')}
</Text>
<AnimatedBottomSheetFlashList
data={ORDERED_CURRENCIES}
keyExtractor={(item: FiatCurrency) => item}
renderItem={renderItem}
/>
<FlashList data={ORDERED_CURRENCIES} keyExtractor={(item: FiatCurrency) => item} renderItem={renderItem} />
</Modal>
)
}
......
......@@ -27,7 +27,7 @@ import {
} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled'
import { useWalletRestore } from 'src/features/wallet/useWalletRestore'
import { useHapticFeedback } from 'src/utils/haptics/useHapticFeedback'
import { Flex, IconProps, Text, useSporeColors } from 'ui/src'
import { Flex, IconProps, Text, TouchableArea, useSporeColors } from 'ui/src'
import {
Bell,
BookOpen,
......@@ -44,6 +44,7 @@ import {
Lock,
MessageQuestion,
Passkey,
QuestionInCircleFilled,
Sliders,
TouchId,
UniswapLogo,
......@@ -151,6 +152,27 @@ export function SettingsScreen(): JSX.Element {
[navigation],
)
const SmartWalletWrapper = useCallback(
({ children }: { children: React.ReactNode }): JSX.Element => (
<ScreenWithHeader
centerElement={<Text variant="subheading1">{t('settings.setting.smartWallet.action.smartWallet')}</Text>}
rightElement={
<TouchableArea
alignItems="center"
alignSelf="center"
py="$spacing12"
// TODO: add modal + event
>
<QuestionInCircleFilled color="$neutral2" size="$icon.20" />
</TouchableArea>
}
>
{children}
</ScreenWithHeader>
),
[t],
)
const sections: SettingsSection[] = useMemo((): SettingsSection[] => {
const svgProps: IconProps = {
color: colors.neutral2.get(),
......@@ -225,7 +247,12 @@ export function SettingsScreen(): JSX.Element {
icon: <Sliders {...iconProps} />,
navigationProps: {
isTestnetEnabled: isTestnetModeEnabled,
onTestnetModeToggled: () => handleTestnetModeToggle(),
onTestnetModeToggled: (): void => handleTestnetModeToggle(),
onPressSmartWallet: (): void => {
navigation.navigate(MobileScreens.SettingsSmartWallet, {
Wrapper: SmartWalletWrapper,
})
},
},
},
]
......@@ -380,6 +407,7 @@ export function SettingsScreen(): JSX.Element {
hasPasskeyBackup,
isTestnetModeEnabled,
isSmartWalletEnabled,
SmartWalletWrapper,
handleTestnetModeToggle,
notificationOSPermission,
navigation,
......
/* eslint-disable max-lines */
/**
* Util to format numbers inside reanimated worklets.
*
......@@ -132,9 +133,29 @@ const transformForLocale = {
en: commaThousDotDec,
'en-GB': commaThousDotDec,
'en-US': commaThousDotDec,
'es-ES': dotThousCommaDec,
'es-US': commaThousDotDec,
'es-419': commaThousDotDec,
'es-BZ': commaThousDotDec,
'es-CU': commaThousDotDec,
'es-DO': commaThousDotDec,
'es-GT': commaThousDotDec,
'es-HN': commaThousDotDec,
'es-MX': commaThousDotDec,
'es-NI': commaThousDotDec,
'es-PA': commaThousDotDec,
'es-PE': commaThousDotDec,
'es-PR': commaThousDotDec,
'es-SV': commaThousDotDec,
'es-US': commaThousDotDec,
'es-AR': dotThousCommaDec,
'es-BO': dotThousCommaDec,
'es-CL': dotThousCommaDec,
'es-CO': dotThousCommaDec,
'es-CR': dotThousCommaDec,
'es-EC': dotThousCommaDec,
'es-ES': dotThousCommaDec,
'es-PY': dotThousCommaDec,
'es-UY': dotThousCommaDec,
'es-VE': dotThousCommaDec,
'fi-FI': spaceThousCommaDec,
fr: spaceThousCommaDec,
'fr-FR': spaceThousCommaDec,
......@@ -187,9 +208,29 @@ const currencyFormatMap = {
en: 'pre',
'en-GB': 'pre',
'en-US': 'pre',
'es-ES': 'post',
'es-US': 'pre',
'es-419': 'pre',
'es-BZ': 'pre',
'es-CU': 'pre',
'es-DO': 'pre',
'es-GT': 'pre',
'es-HN': 'pre',
'es-MX': 'pre',
'es-NI': 'pre',
'es-PA': 'pre',
'es-PE': 'pre',
'es-PR': 'pre',
'es-SV': 'pre',
'es-US': 'pre',
'es-AR': 'pre',
'es-BO': 'pre',
'es-CL': 'pre',
'es-CO': 'pre',
'es-CR': 'pre',
'es-EC': 'pre',
'es-ES': 'post',
'es-PY': 'pre',
'es-UY': 'pre',
'es-VE': 'pre',
'fi-FI': 'post',
fr: 'post',
'fr-FR': 'post',
......
ignores: [
# Dependencies that depcheck thinks are unused but are actually used
'wrangler',
'@web3-react/eip1193',
'@web3-react/empty',
'@swc/core',
......
// Simple mock for expo-blur's BlurView
// This is needed to avoid the Storybook Web compilation error related to `expo-blur`, something like:
// Module parse failed: Unexpected token (29:12)
// node_modules/expo-blur/build/BlurView.web.js 29:12
// You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
// We don't actually need to use `expo-blur` in the Web App, as we just use CSS; so, we can mock it out.
export const BlurView = (props) => <div {...props} />;
export default BlurView;
......@@ -76,11 +76,18 @@ const config: StorybookConfig = {
config.resolve ??= {}
// Add fallback for Node.js 'os' module
config.resolve.fallback = {
...(config.resolve.fallback || {}),
os: false,
}
config.resolve = {
...config.resolve,
alias: {
...config?.resolve?.alias,
'react-native$': 'react-native-web',
'expo-blur': require.resolve('./__mocks__/expo-blur.js'),
},
}
......
import type { Preview } from '@storybook/react'
import { Provider } from 'react-redux'
import store from 'state'
import { ReactRouterUrlProvider } from 'uniswap/src/contexts/UrlContext'
import { TamaguiProvider } from '../src/theme/tamaguiProvider'
import '@reach/dialog/styles.css'
import { MemoryRouter } from 'react-router-dom'
import '../src/global.css'
import '../src/polyfills'
const preview: Preview = {
decorators: [
(Story) => (
<MemoryRouter>
<ReactRouterUrlProvider>
<Provider store={store}>
<TamaguiProvider>
{/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */}
<Story />
</TamaguiProvider>
</Provider>
</ReactRouterUrlProvider>
</MemoryRouter>
),
],
parameters: {
......
......@@ -175,6 +175,7 @@ module.exports = {
crypto: require.resolve('expo-crypto'),
'react-native-gesture-handler$': require.resolve('react-native-gesture-handler'),
'react-native$': 'react-native-web',
'expo-blur': require.resolve('./.storybook/__mocks__/expo-blur.js'),
},
plugins: webpackConfig.resolve.plugins,
// Webpack 5 does not resolve node modules, so we do so for those necessary:
......@@ -310,7 +311,9 @@ module.exports = {
usedExports: true,
sideEffects: true,
// Optimize over all chunks, instead of async chunks (the default), so that initial chunks are also included.
splitChunks: { chunks: 'all' },
splitChunks: {
chunks: 'all',
},
}
: {}
)
......
......@@ -2,7 +2,7 @@
exports[`should inject metadata for valid pools 1`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......@@ -131,7 +131,7 @@ exports[`should inject metadata for valid pools 1`] = `
exports[`should inject metadata for valid pools 2`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......@@ -260,7 +260,7 @@ exports[`should inject metadata for valid pools 2`] = `
exports[`should inject metadata for valid pools 3`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......
......@@ -2,7 +2,7 @@
exports[`should inject metadata for valid tokens 1`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......@@ -131,7 +131,7 @@ exports[`should inject metadata for valid tokens 1`] = `
exports[`should inject metadata for valid tokens 2`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......@@ -260,7 +260,7 @@ exports[`should inject metadata for valid tokens 2`] = `
exports[`should inject metadata for valid tokens 3`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......@@ -389,7 +389,7 @@ exports[`should inject metadata for valid tokens 3`] = `
exports[`should inject metadata for valid tokens 4`] = `
"<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;">
<html translate="no">
<head>
<meta charset="utf-8" />
......
......@@ -11,7 +11,7 @@
"check:circular": "concurrently \"../../scripts/check-circular-imports.sh ./src/pages/App.tsx 2\" \"../../scripts/check-circular-imports.sh ./src/setupTests.ts 0\"",
"sitemap:generate": "node scripts/generate-sitemap.js",
"start": "craco start",
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 REACT_APP_SKIP_CSP=1 npx wrangler pages dev --compatibility-flags=nodejs_compat --compatibility-date=2023-08-01 --proxy=3001 --port=3000 -- yarn start",
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 REACT_APP_SKIP_CSP=1 npx wrangler@4.14.4 pages dev --compatibility-flags=nodejs_compat --compatibility-date=2023-08-01 --proxy=3001 --port=3000 -- yarn start",
"build:production": "NODE_OPTIONS=--max-old-space-size=8192 craco build",
"build:production:analyze": "UNISWAP_ANALYZE_BUNDLE_SIZE=static craco build",
"analyze": "source-map-explorer 'build/static/js/*.js' --no-border-checks --gzip",
......@@ -198,7 +198,9 @@
"@tamagui/core": "1.125.17",
"@tamagui/portal": "1.125.17",
"@tamagui/react-native-svg": "1.125.17",
"@tanstack/query-sync-storage-persister": "5.75.0",
"@tanstack/react-query": "5.51.16",
"@tanstack/react-query-persist-client": "5.75.2",
"@tanstack/react-table": "8.21.2",
"@types/react-scroll-sync": "0.9.0",
"@uniswap/analytics": "1.7.0",
......
......@@ -7,7 +7,7 @@ if (process.env.CI !== 'true') {
}
export default defineConfig({
testDir: './src/pages',
testDir: './src',
testMatch: '**/*.e2e.test.ts',
// TODO: WEB-7311 - Increase number of workers
workers: 1,
......
......@@ -9,7 +9,13 @@
],
"styleSrc": [
"'self'",
"'unsafe-inline'"
"'unsafe-inline'",
"https://fonts.googleapis.com"
],
"fontSrc": [
"'self'",
"https://fonts.googleapis.com",
"https://fonts.gstatic.com"
],
"imgSrc": [
"*",
......
......@@ -33,7 +33,7 @@
<% } %>
<meta
http-equiv="Content-Security-Policy"
content="default-src <%= cspConfig.defaultSrc.join(' ') %>; script-src <%= cspConfig.scriptSrc.join(' ') %>; style-src <%= cspConfig.styleSrc.concat(['nonce-'+cspStyleNonce]).join(' ') %>; img-src <%= cspConfig.imgSrc.join(' ') %>; frame-src <%= cspConfig.frameSrc.join(' ') %>; connect-src <%= cspConfig.connectSrc.join(' ') %>; worker-src <%= cspConfig.workerSrc.join(' ') %>; media-src <%= cspConfig.mediaSrc.join(' ') %>;"
content="default-src <%= cspConfig.defaultSrc.join(' ') %>; script-src <%= cspConfig.scriptSrc.join(' ') %>; style-src <%= cspConfig.styleSrc.concat(['nonce-'+cspStyleNonce]).join(' ') %>; img-src <%= cspConfig.imgSrc.join(' ') %>; frame-src <%= cspConfig.frameSrc.join(' ') %>; connect-src <%= cspConfig.connectSrc.join(' ') %>; worker-src <%= cspConfig.workerSrc.join(' ') %>; media-src <%= cspConfig.mediaSrc.join(' ') %>; font-src <%= cspConfig.fontSrc.join(' ') %>;"
>
<% } %>
......
......@@ -11170,4 +11170,54 @@
<lastmod>2025-05-03T01:25:23.563Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x57a63adebf02680c996a89413c324901dc0df801</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/unichain/0xe5028e8e8fb3488c2003c09fffc00876bc974b1a</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/unichain/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/avalanche/0xdfea50f83fd27967741f2220110449d8663a1b4f</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/avalanche/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/blast/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/soneium/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/worldchain/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/zksync/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/zora/0x704ad8d95c12d7fea531738faa94402725acb035</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
</urlset>
\ No newline at end of file
......@@ -10345,4 +10345,19 @@
<lastmod>2025-05-03T01:25:23.563Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/tokens/ethereum/0x7eb4db4dddb16a329c5ade17a8a0178331267e28</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/tokens/worldchain/NATIVE</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/tokens/worldchain/0x79a02482a880bce3f13e09da970dc34db4cd24d1</loc>
<lastmod>2025-05-09T19:57:11.515Z</lastmod>
<priority>0.8</priority>
</url>
</urlset>
\ No newline at end of file
......@@ -15,7 +15,6 @@ import { Settings } from 'components/Icons/Settings'
import StatusIcon from 'components/Identicon/StatusIcon'
import { ReceiveCryptoModal } from 'components/ReceiveCryptoModal'
import DelegationMismatchModal from 'components/delegation/DelegationMismatchModal'
import Row from 'components/deprecated/Row'
import { useAccount } from 'hooks/useAccount'
import { useDisconnect } from 'hooks/useDisconnect'
import { useIsUniExtensionConnected } from 'hooks/useIsUniExtensionConnected'
......@@ -69,8 +68,9 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
const shouldShowBuyFiatButton = !isPathBlocked('/buy')
const isUniExtensionConnected = useIsUniExtensionConnected()
const { isTestnetModeEnabled } = useEnabledChains()
const connectedAccount = useAccount()
const connectedWithEmbeddedWallet =
useAccount().connector?.id === CONNECTION_PROVIDER_IDS.EMBEDDED_WALLET_CONNECTOR_ID
connectedAccount.connector?.id === CONNECTION_PROVIDER_IDS.EMBEDDED_WALLET_CONNECTOR_ID
const { signOutWithPasskey } = useSignOutWithPasskey()
const isRightToLeft = i18next.dir() === 'rtl'
......@@ -143,7 +143,11 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
onClick={openSettings}
Icon={Settings}
/>
<Trace logPress element={InterfaceElementName.DISCONNECT_WALLET_BUTTON}>
<Trace
logPress
element={InterfaceElementName.DISCONNECT_WALLET_BUTTON}
properties={{ connector_id: connectedAccount.connector?.id }}
>
<IconWithConfirmTextButton
data-testid="wallet-disconnect"
onConfirm={handleDisconnect}
......@@ -208,15 +212,13 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
/>
) : (
<>
<Row gap="8px">
<Flex row gap="$gap8">
{shouldShowBuyFiatButton && (
<ActionTile
dataTestId={TestID.WalletBuyCrypto}
Icon={<Bank size={24} color="$accent1" />}
name={t('common.buy.label')}
onClick={handleBuyCryptoClick}
errorMessage={t('common.restricted.region')}
errorTooltip={t('moonpay.restricted.region')}
/>
)}
<ActionTile
......@@ -225,7 +227,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
name={t('common.receive')}
onClick={openReceiveCryptoModal}
/>
</Row>
</Flex>
<MiniPortfolio account={account} />
</>
)}
......
......@@ -25,12 +25,14 @@ import { isHash } from 'viem'
const ActivityRowDescriptor = styled(ThemedText.BodySmall)`
color: ${({ theme }) => theme.neutral2};
${EllipsisStyle}
max-width: 100%;
`
const StyledTimestamp = styled(ThemedText.BodySmall)`
color: ${({ theme }) => theme.neutral2};
line-height: 24px;
font-variant: small;
padding-right: 8px;
font-feature-settings:
'tnum' on,
'lnum' on,
......
......@@ -2,7 +2,7 @@ import { ActivityRow } from 'components/AccountDrawer/MiniPortfolio/Activity/Act
import { useAllActivities } from 'components/AccountDrawer/MiniPortfolio/Activity/hooks'
import { createGroups } from 'components/AccountDrawer/MiniPortfolio/Activity/utils'
import { OpenLimitOrdersButton } from 'components/AccountDrawer/MiniPortfolio/Limits/OpenLimitOrdersButton'
import { PortfolioSkeleton, PortfolioTabWrapper } from 'components/AccountDrawer/MiniPortfolio/PortfolioRow'
import { PortfolioSkeleton } from 'components/AccountDrawer/MiniPortfolio/PortfolioRow'
import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
import { MenuState, miniPortfolioMenuStateAtom } from 'components/AccountDrawer/constants'
import { LoadingBubble } from 'components/Tokens/loading'
......@@ -12,6 +12,7 @@ import styled from 'lib/styled-components'
import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent'
import { useMemo } from 'react'
import { ThemedText } from 'theme/components'
import { AnimatePresence, Flex } from 'ui/src'
import { useHideSpamTokensSetting } from 'uniswap/src/features/settings/hooks'
const ActivityGroupWrapper = styled(Column)`
......@@ -54,21 +55,21 @@ export function ActivityTab({ account }: { account: string }) {
<>
{/* OpenLimitOrdersActivityButton is rendered outside of the wrapper to avoid the flash on loading */}
<OpenLimitOrdersActivityButton openLimitsMenu={() => setMenu(MenuState.LIMITS)} account={account} />
<PortfolioTabWrapper>
<AnimatePresence>
{activityGroups.map((activityGroup) => (
<ActivityGroupWrapper key={activityGroup.title}>
<ThemedText.SubHeader color="neutral2" marginLeft="16px">
{activityGroup.title}
</ThemedText.SubHeader>
<Column data-testid="activity-content">
<Flex data-testid="activity-content" width="100%">
{activityGroup.transactions.map(
(activity) =>
!(hideSpam && activity.isSpam) && <ActivityRow key={activity.hash} activity={activity} />,
)}
</Column>
</Flex>
</ActivityGroupWrapper>
))}
</PortfolioTabWrapper>
</AnimatePresence>
</>
)
}
......
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