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: IPFS hash of the deployment:
- CIDv0: `QmezmXSutzznthvc6pSCrb7JgxjGqXFNCWCZLjADjg6Tf8` - CIDv0: `QmainvWX1paa5SAQXowSshKypn1tC5d59gLN8w2coWzJwx`
- CIDv1: `bafybeihxp3cmskdb4nr76bocjhdgq32ykbfquxwakbdx33iehf44jxwuim` - CIDv1: `bafybeifx7bho67bfj5b2bn7nw452bovpbv4iia5ndwi4kwvra5fcswhjr4`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
...@@ -10,14 +10,95 @@ You can also access the Uniswap Interface from an IPFS gateway. ...@@ -10,14 +10,95 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs. Your Uniswap settings are never remembered across different URLs.
IPFS gateways: IPFS gateways:
- https://bafybeihxp3cmskdb4nr76bocjhdgq32ykbfquxwakbdx33iehf44jxwuim.ipfs.dweb.link/ - https://bafybeifx7bho67bfj5b2bn7nw452bovpbv4iia5ndwi4kwvra5fcswhjr4.ipfs.dweb.link/
- [ipfs://QmezmXSutzznthvc6pSCrb7JgxjGqXFNCWCZLjADjg6Tf8/](ipfs://QmezmXSutzznthvc6pSCrb7JgxjGqXFNCWCZLjADjg6Tf8/) - [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 ### 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 web/5.84.0
\ No newline at end of file \ 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 { ...@@ -60,6 +60,9 @@ export function TraceUserProperties(): null {
if (!activeAccount) { if (!activeAccount) {
return return
} }
if (activeAccount.backups) {
setUserProperty(ExtensionUserPropertyName.BackupTypes, activeAccount.backups)
}
setUserProperty(ExtensionUserPropertyName.ActiveWalletAddress, activeAccount.address) setUserProperty(ExtensionUserPropertyName.ActiveWalletAddress, activeAccount.address)
setUserProperty(ExtensionUserPropertyName.ActiveWalletType, activeAccount.type) setUserProperty(ExtensionUserPropertyName.ActiveWalletType, activeAccount.type)
setUserProperty(ExtensionUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances) setUserProperty(ExtensionUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances)
......
...@@ -21,10 +21,7 @@ import { ...@@ -21,10 +21,7 @@ import {
} from 'src/app/features/onboarding/OnboardingSteps' } from 'src/app/features/onboarding/OnboardingSteps'
import { OnboardingWrapper } from 'src/app/features/onboarding/OnboardingWrapper' import { OnboardingWrapper } from 'src/app/features/onboarding/OnboardingWrapper'
import { PasswordImport } from 'src/app/features/onboarding/PasswordImport' 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 { 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 { ImportMnemonic } from 'src/app/features/onboarding/import/ImportMnemonic'
import { InitiatePasskeyAuth } from 'src/app/features/onboarding/import/InitiatePasskeyAuth' import { InitiatePasskeyAuth } from 'src/app/features/onboarding/import/InitiatePasskeyAuth'
import { PasskeyImport } from 'src/app/features/onboarding/import/PasskeyImport' import { PasskeyImport } from 'src/app/features/onboarding/import/PasskeyImport'
...@@ -68,26 +65,9 @@ const allRoutes = [ ...@@ -68,26 +65,9 @@ const allRoutes = [
element: ( element: (
<OnboardingStepsProvider <OnboardingStepsProvider
key={OnboardingRoutes.Create} 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={{ steps={{
[CreateOnboardingSteps.ClaimUnitag]: <ClaimUnitagScreen />, [CreateOnboardingSteps.ClaimUnitag]: <ClaimUnitagScreen />,
[CreateOnboardingSteps.Password]: <PasswordCreate />, [CreateOnboardingSteps.Password]: <PasswordCreate />,
[CreateOnboardingSteps.ViewMnemonic]: <ViewMnemonic />,
[CreateOnboardingSteps.TestMnemonic]: <TestMnemonic />,
[CreateOnboardingSteps.Complete]: <Complete tryToClaimUnitag flow={ExtensionOnboardingFlow.New} />, [CreateOnboardingSteps.Complete]: <Complete tryToClaimUnitag flow={ExtensionOnboardingFlow.New} />,
}} }}
/> />
...@@ -113,7 +93,6 @@ const allRoutes = [ ...@@ -113,7 +93,6 @@ const allRoutes = [
steps={{ steps={{
[ImportPasskeySteps.InitiatePasskeyAuth]: <InitiatePasskeyAuth />, [ImportPasskeySteps.InitiatePasskeyAuth]: <InitiatePasskeyAuth />,
[ImportPasskeySteps.PasskeyImport]: <PasskeyImport />, [ImportPasskeySteps.PasskeyImport]: <PasskeyImport />,
// TODO(WALL-6383): modify this flow to ask user to verify their seed phrase.
[ImportOnboardingSteps.Password]: <PasswordImport flow={ExtensionOnboardingFlow.Passkey} />, [ImportOnboardingSteps.Password]: <PasswordImport flow={ExtensionOnboardingFlow.Passkey} />,
[ImportOnboardingSteps.Select]: <SelectWallets flow={ExtensionOnboardingFlow.Passkey} />, [ImportOnboardingSteps.Select]: <SelectWallets flow={ExtensionOnboardingFlow.Passkey} />,
[ImportOnboardingSteps.Complete]: <Complete flow={ExtensionOnboardingFlow.Passkey} />, [ImportOnboardingSteps.Complete]: <Complete flow={ExtensionOnboardingFlow.Passkey} />,
......
...@@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux' ...@@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux'
import { RouterProvider, createHashRouter } from 'react-router-dom' import { RouterProvider, createHashRouter } from 'react-router-dom'
import { PersistGate } from 'redux-persist/integration/react' import { PersistGate } from 'redux-persist/integration/react'
import { ErrorElement } from 'src/app/components/ErrorElement' import { ErrorElement } from 'src/app/components/ErrorElement'
import { ScreenHeader } from 'src/app/components/layout/ScreenHeader'
import { BaseAppContainer } from 'src/app/core/BaseAppContainer' import { BaseAppContainer } from 'src/app/core/BaseAppContainer'
import { DatadogAppNameTag } from 'src/app/datadog' import { DatadogAppNameTag } from 'src/app/datadog'
import { AccountSwitcherScreen } from 'src/app/features/accounts/AccountSwitcherScreen' import { AccountSwitcherScreen } from 'src/app/features/accounts/AccountSwitcherScreen'
...@@ -37,14 +38,18 @@ import { ...@@ -37,14 +38,18 @@ import {
import { BackgroundToSidePanelRequestType } from 'src/background/messagePassing/types/requests' import { BackgroundToSidePanelRequestType } from 'src/background/messagePassing/types/requests'
import { PrimaryAppInstanceDebuggerLazy } from 'src/store/PrimaryAppInstanceDebuggerLazy' import { PrimaryAppInstanceDebuggerLazy } from 'src/store/PrimaryAppInstanceDebuggerLazy'
import { getReduxPersistor } from 'src/store/store' 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 { useResetUnitagsQueries } from 'uniswap/src/data/apiClients/unitagsApi/useResetUnitagsQueries'
import { syncAppWithDeviceLanguage } from 'uniswap/src/features/settings/slice' import { syncAppWithDeviceLanguage } from 'uniswap/src/features/settings/slice'
import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants' import { ExtensionEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import i18n from 'uniswap/src/i18n'
import { isDevEnv } from 'utilities/src/environment/env' import { isDevEnv } from 'utilities/src/environment/env'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
import { ONE_SECOND_MS } from 'utilities/src/time/time' import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useInterval } from 'utilities/src/time/timing' import { useInterval } from 'utilities/src/time/timing'
import { SmartWalletSettings } from 'wallet/src/features/smartWallet/SmartWalletSettings'
import { useTestnetModeForLoggingAndAnalytics } from 'wallet/src/features/testnetMode/hooks/useTestnetModeForLoggingAndAnalytics' import { useTestnetModeForLoggingAndAnalytics } from 'wallet/src/features/testnetMode/hooks/useTestnetModeForLoggingAndAnalytics'
const router = createHashRouter([ const router = createHashRouter([
...@@ -104,6 +109,28 @@ const router = createHashRouter([ ...@@ -104,6 +109,28 @@ const router = createHashRouter([
path: SettingsRoutes.ManageConnections, path: SettingsRoutes.ManageConnections,
element: <SettingsManageConnectionsScreen />, 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,36 +4,44 @@ import { Flex, Text } from 'ui/src' ...@@ -4,36 +4,44 @@ import { Flex, Text } from 'ui/src'
import { AlertTriangleFilled } from 'ui/src/components/icons' import { AlertTriangleFilled } from 'ui/src/components/icons'
import { LearnMoreLink } from 'uniswap/src/components/text/LearnMoreLink' import { LearnMoreLink } from 'uniswap/src/components/text/LearnMoreLink'
import { uniswapUrls } from 'uniswap/src/constants/urls' 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 { export function ActionCanNotBeCompletedContent(): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<DappRequestContent title={t('dapp.request.actionCannotBeCompleted.header')}> <Trace logImpression eventOnTrigger={WalletEventName.MismatchAccountSignatureRequestBlocked}>
<Flex <DappRequestContent title={t('dapp.request.actionCannotBeCompleted.header')}>
backgroundColor="$statusCritical2" <Flex
borderRadius="$rounded16" backgroundColor="$statusCritical2"
flexDirection="column" borderRadius="$rounded16"
gap="$spacing12" flexDirection="column"
p="$spacing16" gap="$spacing12"
position="relative" p="$spacing16"
width="100%" position="relative"
> width="100%"
<Flex flexDirection="row" gap="$gap12"> >
<Flex> <Flex flexDirection="row" gap="$gap12">
<AlertTriangleFilled color="$statusCritical" size="$icon.20" /> <Flex>
</Flex> <AlertTriangleFilled color="$statusCritical" size="$icon.20" />
<Flex gap="$spacing8" flexShrink={1}> </Flex>
<Text color="$statusCritical" variant="buttonLabel3"> <Flex gap="$spacing8" flexShrink={1}>
{t('dapp.request.actionCannotBeCompleted.title')} <Text color="$statusCritical" variant="buttonLabel3">
</Text> {t('dapp.request.actionCannotBeCompleted.title')}
<Text color="$neutral2" variant="body4"> </Text>
{t('dapp.request.actionCannotBeCompleted.description')} <Text color="$neutral2" variant="body4">
</Text> {t('dapp.request.actionCannotBeCompleted.description')}
<LearnMoreLink textVariant="buttonLabel4" url={uniswapUrls.helpArticleUrls.mismatchedImports} /> </Text>
<LearnMoreLink
textVariant="buttonLabel4"
url={uniswapUrls.helpArticleUrls.mismatchedImports}
textColor="$neutral1"
/>
</Flex>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </DappRequestContent>
</DappRequestContent> </Trace>
) )
} }
...@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' ...@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { KeyboardKey } from 'src/app/features/onboarding/KeyboardKey' import { KeyboardKey } from 'src/app/features/onboarding/KeyboardKey'
import { MainContentWrapper } from 'src/app/features/onboarding/intro/MainContentWrapper' 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 { useOpeningKeyboardShortCut } from 'src/app/hooks/useOpeningKeyboardShortCut'
import { getCurrentTabAndWindowId } from 'src/app/navigation/utils' import { getCurrentTabAndWindowId } from 'src/app/navigation/utils'
import { onboardingMessageChannel } from 'src/background/messagePassing/messageChannels' import { onboardingMessageChannel } from 'src/background/messagePassing/messageChannels'
...@@ -15,7 +16,7 @@ import { iconSizes } from 'ui/src/theme' ...@@ -15,7 +16,7 @@ import { iconSizes } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls' import { uniswapUrls } from 'uniswap/src/constants/urls'
import { ExtensionOnboardingFlow } from 'uniswap/src/types/screens/extension' import { ExtensionOnboardingFlow } from 'uniswap/src/types/screens/extension'
import { logger } from 'utilities/src/logger/logger' 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({ export function Complete({
flow, flow,
...@@ -43,7 +44,11 @@ export function Complete({ ...@@ -43,7 +44,11 @@ export function Complete({
}, [existingClaim, address, tryToClaimUnitag, unitagClaimAttempted, addUnitagClaim]) }, [existingClaim, address, tryToClaimUnitag, unitagClaimAttempted, addUnitagClaim])
// Activates onboarding accounts on component mount // Activates onboarding accounts on component mount
useFinishOnboarding(terminateStoreSynchronization, flow, tryToClaimUnitag && !unitagClaimAttempted) useFinishExtensionOnboarding({
callback: terminateStoreSynchronization,
extensionOnboardingFlow: flow,
skip: tryToClaimUnitag && !unitagClaimAttempted,
})
useEffect(() => { useEffect(() => {
const onSidebarOpenedListener = onboardingMessageChannel.addMessageListener( const onSidebarOpenedListener = onboardingMessageChannel.addMessageListener(
......
...@@ -4,9 +4,6 @@ import { OnboardingScreenProps } from 'src/app/features/onboarding/OnboardingScr ...@@ -4,9 +4,6 @@ import { OnboardingScreenProps } from 'src/app/features/onboarding/OnboardingScr
export enum CreateOnboardingSteps { export enum CreateOnboardingSteps {
ClaimUnitag = 'claimUnitag', ClaimUnitag = 'claimUnitag',
Password = 'password', Password = 'password',
ViewMnemonic = 'mnemonic',
TestMnemonic = 'testMnemonic',
Naming = 'naming',
Complete = 'complete', 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 { ...@@ -41,7 +41,7 @@ export function SelectImportMethod(): JSX.Element {
title={t('onboarding.import.selectMethod.title')} title={t('onboarding.import.selectMethod.title')}
onBack={(): void => navigate(`/${TopLevelRoutes.Onboarding}`, { replace: true })} onBack={(): void => navigate(`/${TopLevelRoutes.Onboarding}`, { replace: true })}
> >
<Flex gap="$spacing16" mt="$spacing24" width="100%"> <Flex gap="$spacing12" mt="$spacing24" width="100%">
{showErrorMessage && ( {showErrorMessage && (
<Flex mb="$spacing8"> <Flex mb="$spacing8">
<Text color="$statusCritical" variant="body3" textAlign="center" width="100%"> <Text color="$statusCritical" variant="body3" textAlign="center" width="100%">
......
import { useState } from 'react' import { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { SelectWalletsSkeleton } from 'src/app/components/loading/SelectWalletSkeleton' 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 { OnboardingScreen } from 'src/app/features/onboarding/OnboardingScreen'
import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps' import { useOnboardingSteps } from 'src/app/features/onboarding/OnboardingSteps'
import { useSubmitOnEnter } from 'src/app/features/onboarding/utils' import { useSubmitOnEnter } from 'src/app/features/onboarding/utils'
import { Flex, ScrollView, SpinningLoader, Square, Text } from 'ui/src' import { Flex, ScrollView, SpinningLoader, Square, Text } from 'ui/src'
import { WalletFilled } from 'ui/src/components/icons' import { WalletFilled } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme' 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 Trace from 'uniswap/src/features/telemetry/Trace'
import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension' import { ExtensionOnboardingFlow, ExtensionOnboardingScreens } from 'uniswap/src/types/screens/extension'
import { useAsyncData, useEvent } from 'utilities/src/react/hooks' import { useAsyncData, useEvent } from 'utilities/src/react/hooks'
...@@ -22,7 +18,6 @@ import { BackupType } from 'wallet/src/features/wallet/accounts/types' ...@@ -22,7 +18,6 @@ import { BackupType } from 'wallet/src/features/wallet/accounts/types'
export function SelectWallets({ flow }: { flow: ExtensionOnboardingFlow }): JSX.Element { export function SelectWallets({ flow }: { flow: ExtensionOnboardingFlow }): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
const shouldAutoConnect = useFeatureFlag(FeatureFlags.ExtensionAutoConnect)
const [buttonClicked, setButtonClicked] = useState(false) const [buttonClicked, setButtonClicked] = useState(false)
const { goToNextStep, goToPreviousStep } = useOnboardingSteps() const { goToNextStep, goToPreviousStep } = useOnboardingSteps()
...@@ -42,17 +37,11 @@ export function SelectWallets({ flow }: { flow: ExtensionOnboardingFlow }): JSX. ...@@ -42,17 +37,11 @@ export function SelectWallets({ flow }: { flow: ExtensionOnboardingFlow }): JSX.
} }
setButtonClicked(true) setButtonClicked(true)
const importedAccounts = await generateAccountsAndImportAddresses({ await generateAccountsAndImportAddresses({
selectedAddresses, selectedAddresses,
backupType: flow === ExtensionOnboardingFlow.Passkey ? BackupType.Passkey : BackupType.Manual, 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() goToNextStep()
setButtonClicked(false) setButtonClicked(false)
}) })
......
...@@ -44,7 +44,7 @@ export function IntroScreen(): JSX.Element { ...@@ -44,7 +44,7 @@ export function IntroScreen(): JSX.Element {
<Flex row backgroundColor="$surface1" borderRadius="$rounded16"> <Flex row backgroundColor="$surface1" borderRadius="$rounded16">
<Button <Button
variant="branded" variant="branded"
onPress={(): void => navigate(`/${TopLevelRoutes.Onboarding}/${OnboardingRoutes.Claim}`)} onPress={(): void => navigate(`/${TopLevelRoutes.Onboarding}/${OnboardingRoutes.Create}`)}
> >
{isPasskeyImportEnabled {isPasskeyImportEnabled
? t('onboarding.landing.button.createAccount') ? t('onboarding.landing.button.createAccount')
...@@ -61,7 +61,7 @@ export function IntroScreen(): JSX.Element { ...@@ -61,7 +61,7 @@ export function IntroScreen(): JSX.Element {
} }
> >
{isPasskeyImportEnabled {isPasskeyImportEnabled
? t('onboarding.intro.button.signInOrImport') ? t('onboarding.intro.button.logInOrImport')
: t('onboarding.intro.button.alreadyHave')} : t('onboarding.intro.button.alreadyHave')}
</Button> </Button>
</Flex> </Flex>
......
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { useFinishExtensionOnboarding } from 'src/app/features/onboarding/useFinishExtensionOnboarding'
import { terminateStoreSynchronization } from 'src/store/storeSynchronization' import { terminateStoreSynchronization } from 'src/store/storeSynchronization'
import { Flex, Text } from 'ui/src' import { Flex, Text } from 'ui/src'
import { Check, GraduationCap } from 'ui/src/components/icons' import { Check, GraduationCap } from 'ui/src/components/icons'
import { uniswapUrls } from 'uniswap/src/constants/urls' import { uniswapUrls } from 'uniswap/src/constants/urls'
import { useFinishOnboarding } from 'wallet/src/features/onboarding/OnboardingContext'
export function ResetComplete(): JSX.Element { export function ResetComplete(): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
// Activates onboarding accounts on component mount // Activates onboarding accounts on component mount
useFinishOnboarding(terminateStoreSynchronization) useFinishExtensionOnboarding({ callback: terminateStoreSynchronization })
return ( 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 }: ...@@ -23,13 +23,18 @@ export function SettingsDropdown({ selected, items, disableDropdown, onSelect }:
return ( return (
<Flex> <Flex>
<Popover open={isOpen} stayInFrame={true} onOpenChange={(open) => setIsOpen(open)}> <Popover open={isOpen} stayInFrame={true} onOpenChange={setIsOpen}>
<Popover.Trigger disabled={disableDropdown}> <Popover.Trigger disabled={disableDropdown}>
<Flex row backgroundColor="$surface3" borderRadius="$roundedFull" cursor="pointer" p="$spacing8" gap="$gap4"> <Flex row backgroundColor="$surface3" borderRadius="$roundedFull" cursor="pointer" p="$spacing8" gap="$gap4">
<Text color="$neutral1" variant="buttonLabel4"> <Text color="$neutral1" variant="buttonLabel4">
{selected} {selected}
</Text> </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> </Flex>
</Popover.Trigger> </Popover.Trigger>
<Popover.Content zIndex={zIndexes.popover} backgroundColor="$transparent" disableRemoveScroll={false}> <Popover.Content zIndex={zIndexes.popover} backgroundColor="$transparent" disableRemoveScroll={false}>
......
...@@ -5,6 +5,7 @@ import { Link } from 'react-router-dom' ...@@ -5,6 +5,7 @@ import { Link } from 'react-router-dom'
import { ScreenHeader } from 'src/app/components/layout/ScreenHeader' import { ScreenHeader } from 'src/app/components/layout/ScreenHeader'
import { SCREEN_ITEM_HORIZONTAL_PAD } from 'src/app/constants' import { SCREEN_ITEM_HORIZONTAL_PAD } from 'src/app/constants'
import { SettingsItemWithDropdown } from 'src/app/features/settings/SettingsItemWithDropdown' 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 { AppRoutes, SettingsRoutes } from 'src/app/navigation/constants'
import { useExtensionNavigation } from 'src/app/navigation/utils' import { useExtensionNavigation } from 'src/app/navigation/utils'
import { getIsDefaultProviderFromStorage, setIsDefaultProviderToStorage } from 'src/app/utils/provider' import { getIsDefaultProviderFromStorage, setIsDefaultProviderToStorage } from 'src/app/utils/provider'
...@@ -47,7 +48,6 @@ import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' ...@@ -47,7 +48,6 @@ import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { useCurrentLanguageInfo } from 'uniswap/src/features/language/hooks' import { useCurrentLanguageInfo } from 'uniswap/src/features/language/hooks'
import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal' import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal'
import { setCurrentFiatCurrency, setIsTestnetModeEnabled } from 'uniswap/src/features/settings/slice' 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 Trace from 'uniswap/src/features/telemetry/Trace'
import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { WalletEventName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
...@@ -65,6 +65,7 @@ import { authActions } from 'wallet/src/features/auth/saga' ...@@ -65,6 +65,7 @@ import { authActions } from 'wallet/src/features/auth/saga'
import { AuthActionType } from 'wallet/src/features/auth/types' import { AuthActionType } from 'wallet/src/features/auth/types'
import { selectHasViewedConnectionMigration } from 'wallet/src/features/behaviorHistory/selectors' import { selectHasViewedConnectionMigration } from 'wallet/src/features/behaviorHistory/selectors'
import { resetWalletBehaviorHistory, setHasViewedConnectionMigration } from 'wallet/src/features/behaviorHistory/slice' 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 { BackupType } from 'wallet/src/features/wallet/accounts/types'
import { hasBackup } from 'wallet/src/features/wallet/accounts/utils' import { hasBackup } from 'wallet/src/features/wallet/accounts/utils'
import { useSignerAccounts } from 'wallet/src/features/wallet/hooks' import { useSignerAccounts } from 'wallet/src/features/wallet/hooks'
...@@ -124,6 +125,11 @@ export function SettingsScreen(): JSX.Element { ...@@ -124,6 +125,11 @@ export function SettingsScreen(): JSX.Element {
const handleAdvancedModalClose = useCallback(() => setIsAdvancedModalOpen(false), []) const handleAdvancedModalClose = useCallback(() => setIsAdvancedModalOpen(false), [])
const handleSmartWalletPress = useCallback(() => {
navigateTo(`${AppRoutes.Settings}/${SettingsRoutes.SmartWallet}`)
setIsAdvancedModalOpen(false)
}, [navigateTo])
useEffect(() => { useEffect(() => {
getIsDefaultProviderFromStorage() getIsDefaultProviderFromStorage()
.then((newIsDefaultProvider) => setIsDefaultProvider(newIsDefaultProvider)) .then((newIsDefaultProvider) => setIsDefaultProvider(newIsDefaultProvider))
...@@ -162,6 +168,7 @@ export function SettingsScreen(): JSX.Element { ...@@ -162,6 +168,7 @@ export function SettingsScreen(): JSX.Element {
onTestnetModeToggled={handleTestnetModeToggle} onTestnetModeToggled={handleTestnetModeToggle}
isOpen={isAdvancedModalOpen} isOpen={isAdvancedModalOpen}
onClose={handleAdvancedModalClose} onClose={handleAdvancedModalClose}
onPressSmartWallet={handleSmartWalletPress}
/> />
{hasPasskeyBackup && ( {hasPasskeyBackup && (
<PasskeyManagementModal <PasskeyManagementModal
...@@ -196,6 +203,7 @@ export function SettingsScreen(): JSX.Element { ...@@ -196,6 +203,7 @@ export function SettingsScreen(): JSX.Element {
/> />
)} )}
</> </>
<ThemeToggle />
<SettingsItemWithDropdown <SettingsItemWithDropdown
Icon={Coins} Icon={Coins}
items={ORDERED_CURRENCIES.map((currency) => { 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 { ...@@ -6,7 +6,6 @@ export enum TopLevelRoutes {
} }
export enum OnboardingRoutes { export enum OnboardingRoutes {
Claim = 'claim',
Create = 'create', Create = 'create',
Import = 'import', Import = 'import',
ImportPasskey = 'import-passkey', ImportPasskey = 'import-passkey',
...@@ -43,6 +42,7 @@ export enum SettingsRoutes { ...@@ -43,6 +42,7 @@ export enum SettingsRoutes {
BackupRecoveryPhrase = 'backup-recovery-phrase', BackupRecoveryPhrase = 'backup-recovery-phrase',
RemoveRecoveryPhrase = 'remove-recovery-phrase', RemoveRecoveryPhrase = 'remove-recovery-phrase',
ManageConnections = 'manage-connections', ManageConnections = 'manage-connections',
SmartWallet = 'smart-wallet',
} }
export enum RemoveRecoveryPhraseRoutes { export enum RemoveRecoveryPhraseRoutes {
......
...@@ -10,9 +10,5 @@ ...@@ -10,9 +10,5 @@
*/ */
setTimeout(() => { setTimeout(() => {
const script = document.createElement('script') import('src/entry/sidebar')
script.type = 'text/javascript'
script.async = true
script.src = './sidebar.js'
document.body.appendChild(script)
}, 10) }, 10)
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
readDeprecatedReduxedChromeStorage, readDeprecatedReduxedChromeStorage,
} from 'src/store/reduxedChromeStorageToReduxPersistMigration' } from 'src/store/reduxedChromeStorageToReduxPersistMigration'
import { fiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api' 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 { createDatadogReduxEnhancer } from 'utilities/src/logger/datadog/Datadog'
import { createStore } from 'wallet/src/state' import { createStore } from 'wallet/src/state'
import { createMigrate } from 'wallet/src/state/createMigrate' import { createMigrate } from 'wallet/src/state/createMigrate'
...@@ -39,7 +40,7 @@ const setupStore = (preloadedState?: PreloadedState<ExtensionState>): ReturnType ...@@ -39,7 +40,7 @@ const setupStore = (preloadedState?: PreloadedState<ExtensionState>): ReturnType
preloadedState, preloadedState,
additionalSagas: [rootExtensionSaga], additionalSagas: [rootExtensionSaga],
middlewareBefore: __DEV__ ? [loggerMiddleware] : [], middlewareBefore: __DEV__ ? [loggerMiddleware] : [],
middlewareAfter: [fiatOnRampAggregatorApi.middleware], middlewareAfter: [fiatOnRampAggregatorApi.middleware, delegationListenerMiddleware.middleware],
enhancers: [dataDogReduxEnhancer], enhancers: [dataDogReduxEnhancer],
}) })
} }
......
...@@ -111,13 +111,14 @@ const { ...@@ -111,13 +111,14 @@ const {
} }
: {}, : {},
compress: false, compress: false,
hot: true, // Enable HMR
static: { static: {
directory: path.join(__dirname, '../dev'), directory: path.join(__dirname, '../dev'),
}, },
client: { client: {
// logging: "info", // logging: "info",
progress: true, progress: true,
reconnect: false, reconnect: true,
overlay: { overlay: {
errors: true, errors: true,
warnings: false, warnings: false,
...@@ -281,6 +282,7 @@ module.exports = (env) => { ...@@ -281,6 +282,7 @@ module.exports = (env) => {
'react-native-vector-icons$': 'react-native-vector-icons/dist', 'react-native-vector-icons$': 'react-native-vector-icons/dist',
src: path.resolve(__dirname, 'src'), // absolute imports in apps/web src: path.resolve(__dirname, 'src'), // absolute imports in apps/web
'react-native-gesture-handler$': require.resolve('react-native-gesture-handler'), '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 // Add support for web-based extensions so we can share code between mobile/extension
extensions: [ extensions: [
......
...@@ -9,7 +9,29 @@ ...@@ -9,7 +9,29 @@
<locale android:name="ja"/> <locale android:name="ja"/>
<locale android:name="pt"/> <locale android:name="pt"/>
<locale android:name="vi"/> <locale android:name="vi"/>
<locale android:name="es-ES"/> <!-- Spanish locales that use `,` as the decimal separator -->
<locale android:name="es-US"/>
<locale android:name="es-419"/> <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> </locale-config>
...@@ -1253,7 +1253,7 @@ PODS: ...@@ -1253,7 +1253,7 @@ PODS:
- Firebase/Firestore (11.2.0): - Firebase/Firestore (11.2.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseFirestore (~> 11.2.0) - FirebaseFirestore (~> 11.2.0)
- FirebaseAppCheckInterop (11.7.0) - FirebaseAppCheckInterop (11.12.0)
- FirebaseAuth (11.2.0): - FirebaseAuth (11.2.0):
- FirebaseAppCheckInterop (~> 11.0) - FirebaseAppCheckInterop (~> 11.0)
- FirebaseAuthInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0)
...@@ -1263,14 +1263,14 @@ PODS: ...@@ -1263,14 +1263,14 @@ PODS:
- GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Environment (~> 8.0)
- GTMSessionFetcher/Core (~> 3.4) - GTMSessionFetcher/Core (~> 3.4)
- RecaptchaInterop (~> 100.0) - RecaptchaInterop (~> 100.0)
- FirebaseAuthInterop (11.7.0) - FirebaseAuthInterop (11.12.0)
- FirebaseCore (11.2.0): - FirebaseCore (11.2.0):
- FirebaseCoreInternal (~> 11.0) - FirebaseCoreInternal (~> 11.0)
- GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Logger (~> 8.0) - GoogleUtilities/Logger (~> 8.0)
- FirebaseCoreExtension (11.4.1): - FirebaseCoreExtension (11.4.1):
- FirebaseCore (~> 11.0) - FirebaseCore (~> 11.0)
- FirebaseCoreInternal (11.7.0): - FirebaseCoreInternal (11.12.0):
- "GoogleUtilities/NSData+zlib (~> 8.0)" - "GoogleUtilities/NSData+zlib (~> 8.0)"
- FirebaseFirestore (11.2.0): - FirebaseFirestore (11.2.0):
- FirebaseCore (~> 11.0) - FirebaseCore (~> 11.0)
...@@ -1292,28 +1292,28 @@ PODS: ...@@ -1292,28 +1292,28 @@ PODS:
- gRPC-Core (~> 1.65.0) - gRPC-Core (~> 1.65.0)
- leveldb-library (~> 1.22) - leveldb-library (~> 1.22)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- FirebaseSharedSwift (11.7.0) - FirebaseSharedSwift (11.12.0)
- fmt (11.0.2) - fmt (11.0.2)
- glog (0.3.5) - glog (0.3.5)
- GoogleUtilities/AppDelegateSwizzler (8.0.2): - GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network - GoogleUtilities/Network
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.0.2): - GoogleUtilities/Environment (8.1.0):
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.0.2): - GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Network (8.0.2): - GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib" - "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Reachability - GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.0.2)": - "GoogleUtilities/NSData+zlib (8.1.0)":
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.0.2) - GoogleUtilities/Privacy (8.1.0)
- GoogleUtilities/Reachability (8.0.2): - GoogleUtilities/Reachability (8.1.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- "gRPC-C++ (1.65.5)": - "gRPC-C++ (1.65.5)":
...@@ -1411,21 +1411,21 @@ PODS: ...@@ -1411,21 +1411,21 @@ PODS:
- hermes-engine/Pre-built (= 0.76.9) - hermes-engine/Pre-built (= 0.76.9)
- hermes-engine/Pre-built (0.76.9) - hermes-engine/Pre-built (0.76.9)
- leveldb-library (1.22.6) - leveldb-library (1.22.6)
- libwebp (1.3.2): - libwebp (1.5.0):
- libwebp/demux (= 1.3.2) - libwebp/demux (= 1.5.0)
- libwebp/mux (= 1.3.2) - libwebp/mux (= 1.5.0)
- libwebp/sharpyuv (= 1.3.2) - libwebp/sharpyuv (= 1.5.0)
- libwebp/webp (= 1.3.2) - libwebp/webp (= 1.5.0)
- libwebp/demux (1.3.2): - libwebp/demux (1.5.0):
- libwebp/webp - libwebp/webp
- libwebp/mux (1.3.2): - libwebp/mux (1.5.0):
- libwebp/demux - libwebp/demux
- libwebp/sharpyuv (1.3.2) - libwebp/sharpyuv (1.5.0)
- libwebp/webp (1.3.2): - libwebp/webp (1.5.0):
- libwebp/sharpyuv - libwebp/sharpyuv
- MMKV (2.0.2): - MMKV (2.2.2):
- MMKVCore (~> 2.0.2) - MMKVCore (~> 2.2.2)
- MMKVCore (2.0.2) - MMKVCore (2.2.2)
- nanopb (3.30910.0): - nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0) - nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0) - nanopb/encode (= 3.30910.0)
...@@ -3861,26 +3861,26 @@ SPEC CHECKSUMS: ...@@ -3861,26 +3861,26 @@ SPEC CHECKSUMS:
fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6
FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45
Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c
FirebaseAppCheckInterop: 2376d3ec5cb4267facad4fe754ab4f301a5a519b FirebaseAppCheckInterop: 73b173e5ec45192e2d522ad43f526a82ad10b852
FirebaseAuth: 2a198b8cdbbbd457f08d74df7040feb0a0e7777a FirebaseAuth: 2a198b8cdbbbd457f08d74df7040feb0a0e7777a
FirebaseAuthInterop: a6973d72aa242ea88ffb6be9c9b06c65455071da FirebaseAuthInterop: b583210c039a60ed3f1e48865e1f3da44a796595
FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da
FirebaseCoreExtension: f1bc67a4702931a7caa097d8e4ac0a1b0d16720e FirebaseCoreExtension: f1bc67a4702931a7caa097d8e4ac0a1b0d16720e
FirebaseCoreInternal: d6c17dafc8dc33614733a8b52df78fcb4394c881 FirebaseCoreInternal: 480d23e21f699cc7bcbc3d8eaa301f15d1e3ffaf
FirebaseFirestore: 62708adbc1dfcd6d165a7c0a202067b441912dc9 FirebaseFirestore: 62708adbc1dfcd6d165a7c0a202067b441912dc9
FirebaseFirestoreInternal: ad9b9ee2d3d430c8f31333a69b3b6737a7206232 FirebaseFirestoreInternal: ad9b9ee2d3d430c8f31333a69b3b6737a7206232
FirebaseSharedSwift: a45efd84d60ebbfdcdbaebc66948af3630459e62 FirebaseSharedSwift: d2475748a2d2a36242ed13baa34b2acda846c925
fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6 fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
"gRPC-C++": 2fa52b3141e7789a28a737f251e0c45b4cb20a87 "gRPC-C++": 2fa52b3141e7789a28a737f251e0c45b4cb20a87
gRPC-Core: a27c294d6149e1c39a7d173527119cfbc3375ce4 gRPC-Core: a27c294d6149e1c39a7d173527119cfbc3375ce4
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11 hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
MMKV: 3eacda84cd1c4fc95cf848d3ecb69d85ed56006c MMKV: b4802ebd5a7c68fc0c4a5ccb4926fbdfb62d68e0
MMKVCore: 508b4d3a8ce031f1b5c8bd235f0517fb3f4c73a9 MMKVCore: a255341a3746955f50da2ad9121b18cb2b346e61
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774 OneSignalXCFramework: 1a3b28dfbff23aabce585796d23c1bef37772774
OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104 OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104
......
...@@ -23,9 +23,31 @@ ...@@ -23,9 +23,31 @@
<string>ja</string> <string>ja</string>
<string>pt</string> <string>pt</string>
<string>vi</string> <string>vi</string>
<string>es-ES</string> <!-- Spanish locales that use `,` as the decimal separator -->
<string>es-US</string>
<string>es-419</string> <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> </array>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string> <string>$(PRODUCT_NAME)</string>
......
b5838bdddb0692a13d0a061696f7ea1af4f7b16152b8824052498e1a494a4988 7a03144e3423ea77bd883f7e79a0e3d6a3c54ea3e98424ac288e5f817f88931c
\ No newline at end of file \ No newline at end of file
#!/bin/bash #!/bin/bash
MAX_SIZE=21.1 MAX_SIZE=23
MAX_BUFFER=0.5 MAX_BUFFER=0.5
# Check OS type and use appropriate stat command # Check OS type and use appropriate stat command
......
...@@ -16,6 +16,7 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' ...@@ -16,6 +16,7 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { ShareableEntity } from 'uniswap/src/types/sharing' import { ShareableEntity } from 'uniswap/src/types/sharing'
import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { buildCurrencyId } from 'uniswap/src/utils/currencyId'
import { closeKeyboardBeforeCallback } from 'utilities/src/device/keyboard/dismissNativeKeyboard'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants' import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { import {
...@@ -120,7 +121,9 @@ function useNavigateToHomepageTab(tab: HomeScreenTabIndex): () => void { ...@@ -120,7 +121,9 @@ function useNavigateToHomepageTab(tab: HomeScreenTabIndex): () => void {
const { navigate } = useAppStackNavigation() const { navigate } = useAppStackNavigation()
return useCallback((): void => { return useCallback((): void => {
navigate(MobileScreens.Home, { tab }) closeKeyboardBeforeCallback(() => {
navigate(MobileScreens.Home, { tab })
})
}, [navigate, tab]) }, [navigate, tab])
} }
...@@ -128,7 +131,9 @@ function useNavigateToReceive(): () => void { ...@@ -128,7 +131,9 @@ function useNavigateToReceive(): () => void {
const dispatch = useDispatch() const dispatch = useDispatch()
return useCallback((): void => { return useCallback((): void => {
dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) closeKeyboardBeforeCallback(() => {
dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }))
})
}, [dispatch]) }, [dispatch])
} }
...@@ -137,8 +142,10 @@ function useNavigateToSend(): (args: NavigateToSendFlowArgs) => void { ...@@ -137,8 +142,10 @@ function useNavigateToSend(): (args: NavigateToSendFlowArgs) => void {
return useCallback( return useCallback(
(args: NavigateToSendFlowArgs) => { (args: NavigateToSendFlowArgs) => {
const initialSendState = getNavigateToSendFlowArgsInitialState(args) closeKeyboardBeforeCallback(() => {
dispatch(openModal({ name: ModalName.Send, initialState: initialSendState })) const initialSendState = getNavigateToSendFlowArgsInitialState(args)
dispatch(openModal({ name: ModalName.Send, initialState: initialSendState }))
})
}, },
[dispatch], [dispatch],
) )
...@@ -151,25 +158,27 @@ function useNavigateToSwapFlow(): (args: NavigateToSwapFlowArgs) => void { ...@@ -151,25 +158,27 @@ function useNavigateToSwapFlow(): (args: NavigateToSwapFlowArgs) => void {
return useCallback( return useCallback(
(args: NavigateToSwapFlowArgs): void => { (args: NavigateToSwapFlowArgs): void => {
const initialState = getNavigateToSwapFlowArgsInitialState(args, defaultChainId) closeKeyboardBeforeCallback(() => {
const initialState = getNavigateToSwapFlowArgsInitialState(args, defaultChainId)
// If no prefilled token, go directly to swap
if (!isNavigateToSwapFlowArgsPartialState(args)) { // If no prefilled token, go directly to swap
dispatch(closeModal({ name: ModalName.Swap })) if (!isNavigateToSwapFlowArgsPartialState(args)) {
dispatch(openModal({ name: ModalName.Swap, initialState })) dispatch(closeModal({ name: ModalName.Swap }))
return dispatch(openModal({ name: ModalName.Swap, initialState }))
} return
}
// Show warning modal for prefilled tokens, which will handle token safety checks
const currencyId = buildCurrencyId(args.currencyChainId, args.currencyAddress) // Show warning modal for prefilled tokens, which will handle token safety checks
navigate(ModalName.TokenWarning, { const currencyId = buildCurrencyId(args.currencyChainId, args.currencyAddress)
initialState: { navigate(ModalName.TokenWarning, {
currencyId, initialState: {
onAcknowledge: () => { currencyId,
dispatch(closeModal({ name: ModalName.Swap })) onAcknowledge: () => {
dispatch(openModal({ name: ModalName.Swap, initialState })) dispatch(closeModal({ name: ModalName.Swap }))
dispatch(openModal({ name: ModalName.Swap, initialState }))
},
}, },
}, })
}) })
}, },
[dispatch, defaultChainId, navigate], [dispatch, defaultChainId, navigate],
...@@ -182,13 +191,15 @@ function useNavigateToTokenDetails(): (currencyId: string) => void { ...@@ -182,13 +191,15 @@ function useNavigateToTokenDetails(): (currencyId: string) => void {
return useCallback( return useCallback(
(currencyId: string): void => { (currencyId: string): void => {
dispatch(closeModal({ name: ModalName.Swap })) closeKeyboardBeforeCallback(() => {
dispatch(closeAllModals()) dispatch(closeModal({ name: ModalName.Swap }))
if (exploreNavigationRef.current && exploreNavigationRef.isFocused()) { dispatch(closeAllModals())
exploreNavigationRef.navigate(MobileScreens.TokenDetails, { currencyId }) if (exploreNavigationRef.current && exploreNavigationRef.isFocused()) {
} else { exploreNavigationRef.navigate(MobileScreens.TokenDetails, { currencyId })
appNavigation.navigate(MobileScreens.TokenDetails, { currencyId }) } else {
} appNavigation.navigate(MobileScreens.TokenDetails, { currencyId })
}
})
}, },
[appNavigation, dispatch], [appNavigation, dispatch],
) )
...@@ -199,12 +210,14 @@ function useNavigateToNftDetails(): (args: NavigateToNftItemArgs) => void { ...@@ -199,12 +210,14 @@ function useNavigateToNftDetails(): (args: NavigateToNftItemArgs) => void {
return useCallback( return useCallback(
({ owner, address, tokenId, isSpam, fallbackData }: NavigateToNftItemArgs): void => { ({ owner, address, tokenId, isSpam, fallbackData }: NavigateToNftItemArgs): void => {
navigation.navigate(MobileScreens.NFTItem, { closeKeyboardBeforeCallback(() => {
owner, navigation.navigate(MobileScreens.NFTItem, {
address, owner,
tokenId, address,
isSpam, tokenId,
fallbackData, isSpam,
fallbackData,
})
}) })
}, },
[navigation], [navigation],
...@@ -216,15 +229,17 @@ function useNavigateToNftCollection(): (args: NavigateToNftCollectionArgs) => vo ...@@ -216,15 +229,17 @@ function useNavigateToNftCollection(): (args: NavigateToNftCollectionArgs) => vo
return useCallback( return useCallback(
({ collectionAddress }: NavigateToNftCollectionArgs): void => { ({ collectionAddress }: NavigateToNftCollectionArgs): void => {
if (exploreNavigationRef.current && exploreNavigationRef.isFocused()) { closeKeyboardBeforeCallback(() => {
exploreNavigationRef.navigate(MobileScreens.NFTCollection, { if (exploreNavigationRef.current && exploreNavigationRef.isFocused()) {
collectionAddress, exploreNavigationRef.navigate(MobileScreens.NFTCollection, {
}) collectionAddress,
} else { })
appNavigation.navigate(MobileScreens.NFTCollection, { } else {
collectionAddress, appNavigation.navigate(MobileScreens.NFTCollection, {
}) collectionAddress,
} })
}
})
}, },
[appNavigation], [appNavigation],
) )
...@@ -242,18 +257,20 @@ function useNavigateToBuyOrReceiveWithEmptyWallet(): () => void { ...@@ -242,18 +257,20 @@ function useNavigateToBuyOrReceiveWithEmptyWallet(): () => void {
) )
return useCallback((): void => { return useCallback((): void => {
dispatch(closeModal({ name: ModalName.Send })) closeKeyboardBeforeCallback(() => {
dispatch(closeModal({ name: ModalName.Send }))
if (forAggregatorEnabled) {
dispatch(openModal({ name: ModalName.FiatOnRampAggregator })) if (forAggregatorEnabled) {
} else { dispatch(openModal({ name: ModalName.FiatOnRampAggregator }))
dispatch( } else {
openModal({ dispatch(
name: ModalName.WalletConnectScan, openModal({
initialState: ScannerModalState.WalletQr, name: ModalName.WalletConnectScan,
}), initialState: ScannerModalState.WalletQr,
) }),
} )
}
})
}, [dispatch, forAggregatorEnabled]) }, [dispatch, forAggregatorEnabled])
} }
...@@ -262,7 +279,9 @@ function useNavigateToFiatOnRamp(): (args: NavigateToFiatOnRampArgs) => void { ...@@ -262,7 +279,9 @@ function useNavigateToFiatOnRamp(): (args: NavigateToFiatOnRampArgs) => void {
return useCallback( return useCallback(
({ prefilledCurrency, isOfframp }: NavigateToFiatOnRampArgs): void => { ({ prefilledCurrency, isOfframp }: NavigateToFiatOnRampArgs): void => {
dispatch(openModal({ name: ModalName.FiatOnRampAggregator, initialState: { prefilledCurrency, isOfframp } })) closeKeyboardBeforeCallback(() => {
dispatch(openModal({ name: ModalName.FiatOnRampAggregator, initialState: { prefilledCurrency, isOfframp } }))
})
}, },
[dispatch], [dispatch],
) )
...@@ -273,11 +292,13 @@ function useNavigateToExternalProfile(): (args: NavigateToExternalProfileArgs) = ...@@ -273,11 +292,13 @@ function useNavigateToExternalProfile(): (args: NavigateToExternalProfileArgs) =
return useCallback( return useCallback(
({ address }: NavigateToExternalProfileArgs): void => { ({ address }: NavigateToExternalProfileArgs): void => {
if (exploreNavigationRef.isFocused()) { closeKeyboardBeforeCallback(() => {
exploreNavigationRef.navigate(MobileScreens.ExternalProfile, { address }) if (exploreNavigationRef.isFocused()) {
} else { exploreNavigationRef.navigate(MobileScreens.ExternalProfile, { address })
appNavigation.navigate(MobileScreens.ExternalProfile, { address }) } else {
} appNavigation.navigate(MobileScreens.ExternalProfile, { address })
}
})
}, },
[appNavigation], [appNavigation],
) )
......
...@@ -150,18 +150,33 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -150,18 +150,33 @@ exports[`AccountSwitcher renders correctly 1`] = `
</View> </View>
</View> </View>
<View <View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={16} hitSlop={16}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -174,6 +189,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -174,6 +189,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
testID="copy" testID="copy"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -187,6 +204,12 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -187,6 +204,12 @@ exports[`AccountSwitcher renders correctly 1`] = `
"borderBottomRightRadius": 999999, "borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999, "borderTopLeftRadius": 999999,
"borderTopRightRadius": 999999, "borderTopRightRadius": 999999,
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 4, "gap": 4,
"justifyContent": "center", "justifyContent": "center",
...@@ -201,6 +224,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -201,6 +224,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -439,25 +464,32 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -439,25 +464,32 @@ exports[`AccountSwitcher renders correctly 1`] = `
</View> </View>
</View> </View>
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"marginTop": 16, "marginTop": 16,
"opacity": 1, "opacity": 1,
...@@ -470,9 +502,17 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -470,9 +502,17 @@ exports[`AccountSwitcher renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 8, "gap": 8,
"marginLeft": 24, "marginLeft": 24,
...@@ -480,6 +520,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -480,6 +520,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -601,6 +643,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` ...@@ -601,6 +643,8 @@ exports[`AccountSwitcher renders correctly 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.2} maxFontSizeMultiplier={1.2}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
......
...@@ -261,7 +261,11 @@ function ExploreTabBarButton({ activeScale = 0.98, onLayout, isNarrow }: Explore ...@@ -261,7 +261,11 @@ function ExploreTabBarButton({ activeScale = 0.98, onLayout, isNarrow }: Explore
const Wrapper = isIOS ? BlurView : Flex const Wrapper = isIOS ? BlurView : Flex
return ( 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}> <TapGestureHandler testID={TestID.SearchTokensAndWallets} onGestureEvent={onGestureEvent}>
<AnimatedFlex <AnimatedFlex
borderRadius="$roundedFull" borderRadius="$roundedFull"
......
...@@ -108,6 +108,7 @@ import { ...@@ -108,6 +108,7 @@ import {
UnitagStackParamList, UnitagStackParamList,
} from 'uniswap/src/types/screens/mobile' } from 'uniswap/src/types/screens/mobile'
import { OnboardingContextProvider } from 'wallet/src/features/onboarding/OnboardingContext' 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 { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks'
import { selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors' import { selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors'
...@@ -147,6 +148,7 @@ function SettingsStackGroup(): JSX.Element { ...@@ -147,6 +148,7 @@ function SettingsStackGroup(): JSX.Element {
name={MobileScreens.SettingsCloudBackupProcessing} name={MobileScreens.SettingsCloudBackupProcessing}
/> />
<SettingsStack.Screen component={SettingsCloudBackupStatus} name={MobileScreens.SettingsCloudBackupStatus} /> <SettingsStack.Screen component={SettingsCloudBackupStatus} name={MobileScreens.SettingsCloudBackupStatus} />
<SettingsStack.Screen component={SmartWalletSettings} name={MobileScreens.SettingsSmartWallet} />
<SettingsStack.Screen component={SettingsPrivacyScreen} name={MobileScreens.SettingsPrivacy} /> <SettingsStack.Screen component={SettingsPrivacyScreen} name={MobileScreens.SettingsPrivacy} />
<SettingsStack.Screen component={SettingsNotificationsScreen} name={MobileScreens.SettingsNotifications} /> <SettingsStack.Screen component={SettingsNotificationsScreen} name={MobileScreens.SettingsNotifications} />
<SettingsStack.Group screenOptions={navNativeStackOptions.presentationBottomSheet}> <SettingsStack.Group screenOptions={navNativeStackOptions.presentationBottomSheet}>
......
...@@ -18,7 +18,6 @@ import { HomeScreenTabIndex } from 'src/screens/HomeScreen/HomeScreenTabIndex' ...@@ -18,7 +18,6 @@ import { HomeScreenTabIndex } from 'src/screens/HomeScreen/HomeScreenTabIndex'
import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState' import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState'
import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types'
import { PasskeyManagementModalState } from 'uniswap/src/features/passkey/PasskeyManagementModal' 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 { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestnetModeModalState } from 'uniswap/src/features/testnets/TestnetModeModal' import { TestnetModeModalState } from 'uniswap/src/features/testnets/TestnetModeModal'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
...@@ -30,6 +29,7 @@ import { ...@@ -30,6 +29,7 @@ import {
UnitagStackParamList, UnitagStackParamList,
} from 'uniswap/src/types/screens/mobile' } from 'uniswap/src/types/screens/mobile'
import { NFTItem } from 'wallet/src/features/nfts/types' import { NFTItem } from 'wallet/src/features/nfts/types'
import { SmartWalletAdvancedSettingsModalState } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
type NFTItemScreenParams = { type NFTItemScreenParams = {
owner?: Address owner?: Address
...@@ -90,6 +90,7 @@ export type SettingsStackParamList = { ...@@ -90,6 +90,7 @@ export type SettingsStackParamList = {
[MobileScreens.SettingsLanguage]: undefined [MobileScreens.SettingsLanguage]: undefined
[MobileScreens.SettingsNotifications]: undefined [MobileScreens.SettingsNotifications]: undefined
[MobileScreens.SettingsPrivacy]: undefined [MobileScreens.SettingsPrivacy]: undefined
[MobileScreens.SettingsSmartWallet]: { Wrapper: React.FC<{ children: React.ReactNode }> }
[MobileScreens.SettingsViewSeedPhrase]: { address: Address; walletNeedsRestore?: boolean } [MobileScreens.SettingsViewSeedPhrase]: { address: Address; walletNeedsRestore?: boolean }
[MobileScreens.SettingsWallet]: { address: Address } [MobileScreens.SettingsWallet]: { address: Address }
[MobileScreens.SettingsWalletEdit]: { address: Address } [MobileScreens.SettingsWalletEdit]: { address: Address }
......
...@@ -5,6 +5,7 @@ import { MOBILE_STATE_VERSION, migrations } from 'src/app/migrations' ...@@ -5,6 +5,7 @@ import { MOBILE_STATE_VERSION, migrations } from 'src/app/migrations'
import { MobileState, mobilePersistedStateList, mobileReducer } from 'src/app/mobileReducer' import { MobileState, mobilePersistedStateList, mobileReducer } from 'src/app/mobileReducer'
import { rootMobileSaga } from 'src/app/saga' import { rootMobileSaga } from 'src/app/saga'
import { fiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api' 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 { isNonJestDev } from 'utilities/src/environment/constants'
import { createDatadogReduxEnhancer } from 'utilities/src/logger/datadog/Datadog' import { createDatadogReduxEnhancer } from 'utilities/src/logger/datadog/Datadog'
import { createStore } from 'wallet/src/state' import { createStore } from 'wallet/src/state'
...@@ -51,7 +52,7 @@ if (isNonJestDev) { ...@@ -51,7 +52,7 @@ if (isNonJestDev) {
enhancers.push(reactotron.createEnhancer()) enhancers.push(reactotron.createEnhancer())
} }
const middlewares: Middleware[] = [fiatOnRampAggregatorApi.middleware] const middlewares: Middleware[] = [fiatOnRampAggregatorApi.middleware, delegationListenerMiddleware.middleware]
const setupStore = ( const setupStore = (
preloadedState?: PreloadedState<MobileState>, preloadedState?: PreloadedState<MobileState>,
......
...@@ -18,10 +18,10 @@ import { Flex, Skeleton, Switch, Text, TouchableArea, useSporeColors } from 'ui/ ...@@ -18,10 +18,10 @@ import { Flex, Skeleton, Switch, Text, TouchableArea, useSporeColors } from 'ui/
import { Arrow } from 'ui/src/components/arrow/Arrow' import { Arrow } from 'ui/src/components/arrow/Arrow'
import { RotatableChevron } from 'ui/src/components/icons' import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme' import { iconSizes } from 'ui/src/theme'
import { SmartWalletAdvancedSettingsModalState } from 'uniswap/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { openUri } from 'uniswap/src/utils/linking' import { openUri } from 'uniswap/src/utils/linking'
import { SmartWalletAdvancedSettingsModalState } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
export const SETTINGS_ROW_HEIGHT = 60 export const SETTINGS_ROW_HEIGHT = 60
......
...@@ -128,6 +128,7 @@ describe('TraceUserProperties', () => { ...@@ -128,6 +128,7 @@ describe('TraceUserProperties', () => {
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletAddress, 'address', undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletAddress, 'address', undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletType, AccountType.SignerMnemonic, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletType, AccountType.SignerMnemonic, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsCloudBackedUp, true, 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.IsPushEnabled, true, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSmallBalancesEnabled, false, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSmallBalancesEnabled, false, undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSpamTokensEnabled, true, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSpamTokensEnabled, true, undefined)
...@@ -148,7 +149,7 @@ describe('TraceUserProperties', () => { ...@@ -148,7 +149,7 @@ describe('TraceUserProperties', () => {
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Language, 'English', undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Language, 'English', undefined)
expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Currency, 'USD', undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Currency, 'USD', undefined)
expect(mocked).toHaveBeenCalledTimes(20) expect(mocked).toHaveBeenCalledTimes(21)
}) })
it('sets user properties without active account', async () => { it('sets user properties without active account', async () => {
......
...@@ -115,11 +115,13 @@ export function TraceUserProperties(): null { ...@@ -115,11 +115,13 @@ export function TraceUserProperties(): null {
if (!activeAccount) { if (!activeAccount) {
return return
} }
if (activeAccount.backups) {
setUserProperty(MobileUserPropertyName.BackupTypes, activeAccount.backups)
}
setUserProperty(MobileUserPropertyName.ActiveWalletAddress, activeAccount.address) setUserProperty(MobileUserPropertyName.ActiveWalletAddress, activeAccount.address)
setUserProperty(MobileUserPropertyName.ActiveWalletType, activeAccount.type) setUserProperty(MobileUserPropertyName.ActiveWalletType, activeAccount.type)
setUserProperty(MobileUserPropertyName.IsCloudBackedUp, hasBackup(BackupType.Cloud, activeAccount)) setUserProperty(MobileUserPropertyName.IsCloudBackedUp, hasBackup(BackupType.Cloud, activeAccount))
setUserProperty(MobileUserPropertyName.IsPushEnabled, Boolean(activeAccount.pushNotificationsEnabled)) setUserProperty(MobileUserPropertyName.IsPushEnabled, Boolean(activeAccount.pushNotificationsEnabled))
setUserProperty(MobileUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances) setUserProperty(MobileUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances)
setUserProperty(MobileUserPropertyName.IsHideSpamTokensEnabled, hideSpamTokens) setUserProperty(MobileUserPropertyName.IsHideSpamTokensEnabled, hideSpamTokens)
}, [allowAnalytics, activeAccount, hideSmallBalances, hideSpamTokens]) }, [allowAnalytics, activeAccount, hideSmallBalances, hideSpamTokens])
......
...@@ -137,6 +137,7 @@ export function AccountHeader(): JSX.Element { ...@@ -137,6 +137,7 @@ export function AccountHeader(): JSX.Element {
flexDirection="row" flexDirection="row"
hitSlop={20} hitSlop={20}
testID={TestID.AccountHeaderAvatar} testID={TestID.AccountHeaderAvatar}
dd-action-name={TestID.AccountHeaderAvatar}
onLongPress={async (): Promise<void> => { onLongPress={async (): Promise<void> => {
if (isDevEnv()) { if (isDevEnv()) {
navigate(ModalName.Experiments) navigate(ModalName.Experiments)
...@@ -180,7 +181,12 @@ export function AccountHeader(): JSX.Element { ...@@ -180,7 +181,12 @@ export function AccountHeader(): JSX.Element {
)} )}
</Flex> </Flex>
<Flex row alignItems="flex-start" gap="$spacing16"> <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" /> <ScanHome color="$neutral2" size="$icon.28" />
</TouchableArea> </TouchableArea>
<RotatingSettingsIcon onPressSettings={onPressSettings} /> <RotatingSettingsIcon onPressSettings={onPressSettings} />
......
...@@ -14,25 +14,32 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -14,25 +14,32 @@ exports[`AccountCardItem renders correctly 1`] = `
onPress={[Function]} onPress={[Function]}
> >
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"paddingBottom": 12, "paddingBottom": 12,
...@@ -48,9 +55,17 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -48,9 +55,17 @@ exports[`AccountCardItem renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "flex-start", "alignItems": "flex-start",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 16, "gap": 16,
} }
...@@ -58,6 +73,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -58,6 +73,8 @@ exports[`AccountCardItem renders correctly 1`] = `
testID="account-item/0x82D56A352367453f74FC0dC7B071b311da373Fa6" testID="account-item/0x82D56A352367453f74FC0dC7B071b311da373Fa6"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flex": 1, "flex": 1,
...@@ -66,6 +83,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -66,6 +83,8 @@ exports[`AccountCardItem renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -77,6 +96,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -77,6 +96,8 @@ exports[`AccountCardItem renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -85,6 +106,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -85,6 +106,8 @@ exports[`AccountCardItem renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -133,6 +156,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -133,6 +156,8 @@ exports[`AccountCardItem renders correctly 1`] = `
testID="account-icon" testID="account-icon"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -144,6 +169,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -144,6 +169,8 @@ exports[`AccountCardItem renders correctly 1`] = `
</View> </View>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -153,6 +180,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -153,6 +180,8 @@ exports[`AccountCardItem renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "row", "flexDirection": "row",
...@@ -161,6 +190,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -161,6 +190,8 @@ exports[`AccountCardItem renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -176,6 +207,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -176,6 +207,8 @@ exports[`AccountCardItem renders correctly 1`] = `
ellipsizeMode="tail" ellipsizeMode="tail"
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -204,6 +237,8 @@ exports[`AccountCardItem renders correctly 1`] = ` ...@@ -204,6 +237,8 @@ exports[`AccountCardItem renders correctly 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
......
...@@ -44,19 +44,35 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -44,19 +44,35 @@ exports[`AccountHeader renders correctly 1`] = `
} }
> >
<View <View
collapsable={false}
dd-action-name="account-header-avatar"
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={20} hitSlop={20}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "row", "flexDirection": "row",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -69,6 +85,8 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -69,6 +85,8 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-header-avatar" testID="account-header-avatar"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -117,6 +135,8 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -117,6 +135,8 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-icon" testID="account-icon"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -140,18 +160,33 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -140,18 +160,33 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-header/display-name" testID="account-header/display-name"
> >
<View <View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={20} hitSlop={20}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"flexGrow": 1, "flexGrow": 1,
"opacity": 1, "opacity": 1,
...@@ -164,6 +199,8 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -164,6 +199,8 @@ exports[`AccountHeader renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -173,6 +210,8 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -173,6 +210,8 @@ exports[`AccountHeader renders correctly 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "row", "flexDirection": "row",
...@@ -185,6 +224,8 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -185,6 +224,8 @@ exports[`AccountHeader renders correctly 1`] = `
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -214,25 +255,32 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -214,25 +255,32 @@ exports[`AccountHeader renders correctly 1`] = `
</Text> </Text>
</View> </View>
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -245,9 +293,17 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -245,9 +293,17 @@ exports[`AccountHeader renders correctly 1`] = `
testID="account-header-copy-address" testID="account-header-copy-address"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 4, "gap": 4,
} }
...@@ -257,6 +313,8 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -257,6 +313,8 @@ exports[`AccountHeader renders correctly 1`] = `
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -356,25 +414,33 @@ exports[`AccountHeader renders correctly 1`] = ` ...@@ -356,25 +414,33 @@ exports[`AccountHeader renders correctly 1`] = `
} }
> >
<View <View
hitSlop={ collapsable={false}
dd-action-name="Scan"
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
......
...@@ -86,25 +86,32 @@ exports[`AccountList renders without error 1`] = ` ...@@ -86,25 +86,32 @@ exports[`AccountList renders without error 1`] = `
onPress={[Function]} onPress={[Function]}
> >
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"paddingBottom": 12, "paddingBottom": 12,
...@@ -120,9 +127,17 @@ exports[`AccountList renders without error 1`] = ` ...@@ -120,9 +127,17 @@ exports[`AccountList renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "flex-start", "alignItems": "flex-start",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 16, "gap": 16,
} }
...@@ -130,6 +145,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -130,6 +145,8 @@ exports[`AccountList renders without error 1`] = `
testID="account-item/0x82D56A352367453f74FC0dC7B071b311da373Fa6" testID="account-item/0x82D56A352367453f74FC0dC7B071b311da373Fa6"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flex": 1, "flex": 1,
...@@ -138,6 +155,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -138,6 +155,8 @@ exports[`AccountList renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -149,6 +168,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -149,6 +168,8 @@ exports[`AccountList renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -157,6 +178,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -157,6 +178,8 @@ exports[`AccountList renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -205,6 +228,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -205,6 +228,8 @@ exports[`AccountList renders without error 1`] = `
testID="account-icon" testID="account-icon"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -216,6 +241,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -216,6 +241,8 @@ exports[`AccountList renders without error 1`] = `
</View> </View>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -225,6 +252,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -225,6 +252,8 @@ exports[`AccountList renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "row", "flexDirection": "row",
...@@ -233,6 +262,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -233,6 +262,8 @@ exports[`AccountList renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -248,6 +279,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -248,6 +279,8 @@ exports[`AccountList renders without error 1`] = `
ellipsizeMode="tail" ellipsizeMode="tail"
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -276,6 +309,8 @@ exports[`AccountList renders without error 1`] = ` ...@@ -276,6 +309,8 @@ exports[`AccountList renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
......
...@@ -20,7 +20,14 @@ export function BackButton({ onPressBack, size, color, showButtonLabel, ...rest ...@@ -20,7 +20,14 @@ export function BackButton({ onPressBack, size, color, showButtonLabel, ...rest
navigation.goBack() navigation.goBack()
} }
return ( 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} /> <BackButtonView color={color} showButtonLabel={showButtonLabel} size={size} />
</TouchableArea> </TouchableArea>
) )
......
...@@ -2,19 +2,35 @@ ...@@ -2,19 +2,35 @@
exports[`BackButton renders without error 1`] = ` exports[`BackButton renders without error 1`] = `
<View <View
collapsable={false}
dd-action-name="back"
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={24} hitSlop={24}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -27,6 +43,8 @@ exports[`BackButton renders without error 1`] = ` ...@@ -27,6 +43,8 @@ exports[`BackButton renders without error 1`] = `
testID="back" testID="back"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -53,6 +71,8 @@ exports[`BackButton renders without error 1`] = ` ...@@ -53,6 +71,8 @@ exports[`BackButton renders without error 1`] = `
}, },
} }
} }
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -87,7 +107,7 @@ exports[`BackButton renders without error 1`] = ` ...@@ -87,7 +107,7 @@ exports[`BackButton renders without error 1`] = `
"borderWidth": 0, "borderWidth": 0,
}, },
{ {
"color": "rgba(19, 19, 19, 0.63)", "color": "#222222",
"height": 24, "height": 24,
"width": 24, "width": 24,
}, },
...@@ -102,7 +122,7 @@ exports[`BackButton renders without error 1`] = ` ...@@ -102,7 +122,7 @@ exports[`BackButton renders without error 1`] = `
vbWidth={24} vbWidth={24}
> >
<RNSVGGroup <RNSVGGroup
color="rgba(19, 19, 19, 0.63)" color="#222222"
fill={null} fill={null}
propList={ propList={
[ [
...@@ -134,6 +154,8 @@ exports[`BackButton renders without error 1`] = ` ...@@ -134,6 +154,8 @@ exports[`BackButton renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
......
...@@ -2,25 +2,32 @@ ...@@ -2,25 +2,32 @@
exports[`CloseButton renders without error 1`] = ` exports[`CloseButton renders without error 1`] = `
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -49,6 +56,7 @@ exports[`CloseButton renders without error 1`] = ` ...@@ -49,6 +56,7 @@ exports[`CloseButton renders without error 1`] = `
"borderWidth": 0, "borderWidth": 0,
}, },
{ {
"color": "#222222",
"height": 20, "height": 20,
"width": 20, "width": 20,
}, },
...@@ -63,6 +71,7 @@ exports[`CloseButton renders without error 1`] = ` ...@@ -63,6 +71,7 @@ exports[`CloseButton renders without error 1`] = `
vbWidth={16} vbWidth={16}
> >
<RNSVGGroup <RNSVGGroup
color="#222222"
fill={null} fill={null}
propList={ propList={
[ [
......
...@@ -14,9 +14,7 @@ export default function RemoveButton({ visible = true, ...rest }: RemoveButtonPr ...@@ -14,9 +14,7 @@ export default function RemoveButton({ visible = true, ...rest }: RemoveButtonPr
disabled={!visible} disabled={!visible}
height={imageSizes.image24} height={imageSizes.image24}
justifyContent="center" justifyContent="center"
style={{ opacity={visible ? 1 : 0}
opacity: visible ? 1 : 0,
}}
testID="explore/remove-button" testID="explore/remove-button"
width={imageSizes.image24} width={imageSizes.image24}
zIndex="$tooltip" zIndex="$tooltip"
......
...@@ -36,18 +36,33 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = ` ...@@ -36,18 +36,33 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = `
Editing Title Editing Title
</Text> </Text>
<View <View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={16} hitSlop={16}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -61,6 +76,8 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = ` ...@@ -61,6 +76,8 @@ exports[`FavoriteHeaderRow when editing renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.2} maxFontSizeMultiplier={1.2}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -120,18 +137,33 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = ` ...@@ -120,18 +137,33 @@ exports[`FavoriteHeaderRow when not editing renders without error 1`] = `
Title Title
</Text> </Text>
<View <View
collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
hitSlop={16} hitSlop={16}
jestAnimatedStyle={
{
"value": {},
}
}
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
......
...@@ -36,31 +36,27 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -36,31 +36,27 @@ exports[`FavoriteTokenCard renders without error 1`] = `
> >
<View <View
collapsable={false} collapsable={false}
hitSlop={ focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": { "backgroundColor": "#FFFFFF",
"dynamic": {
"dark": "#131313",
"light": "#FFFFFF",
},
},
"borderBottomColor": "rgba(34,34,34,0.05)", "borderBottomColor": "rgba(34,34,34,0.05)",
"borderBottomLeftRadius": 16, "borderBottomLeftRadius": 16,
"borderBottomRightRadius": 16, "borderBottomRightRadius": 16,
...@@ -94,9 +90,17 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -94,9 +90,17 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="token-box-undefined" testID="token-box-undefined"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "flex-start", "alignItems": "flex-start",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "column", "flexDirection": "column",
"gap": 8, "gap": 8,
"paddingBottom": 12, "paddingBottom": 12,
...@@ -107,6 +111,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -107,6 +111,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "row", "flexDirection": "row",
...@@ -116,6 +122,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -116,6 +122,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -126,6 +134,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -126,6 +134,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -136,6 +146,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -136,6 +146,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="shimmer-placeholder" testID="shimmer-placeholder"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -143,6 +155,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -143,6 +155,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -166,6 +180,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -166,6 +180,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -184,25 +200,22 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -184,25 +200,22 @@ exports[`FavoriteTokenCard renders without error 1`] = `
/> />
</View> </View>
<View <View
aria-disabled={true}
collapsable={false} collapsable={false}
disabled={true} focusVisibleStyle={{}}
hitSlop={ forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onLayout={[Function]}
pointerEvents="box-none"
role="button"
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": { "backgroundColor": "rgba(19, 19, 19, 0.35)",
"dynamic": {
"dark": "rgba(255, 255, 255, 0.38)",
"light": "rgba(19, 19, 19, 0.35)",
},
},
"borderBottomLeftRadius": 999999, "borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999, "borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999, "borderTopLeftRadius": 999999,
...@@ -220,21 +233,31 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -220,21 +233,31 @@ exports[`FavoriteTokenCard renders without error 1`] = `
"zIndex": 1080, "zIndex": 1080,
} }
} }
tabIndex={-1}
testID="explore/remove-button" testID="explore/remove-button"
userSelect="none"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
"dynamic": { "dynamic": {
"dark": "#131313", "dark": "#1F1F1F",
"light": "#FFFFFF", "light": "#F9F9F9",
}, },
}, },
"borderBottomLeftRadius": 12, "borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12, "borderBottomRightRadius": 12,
"borderTopLeftRadius": 12, "borderTopLeftRadius": 12,
"borderTopRightRadius": 12, "borderTopRightRadius": 12,
"color": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.65)",
"light": "rgba(19, 19, 19, 0.63)",
},
},
"flexDirection": "column", "flexDirection": "column",
"height": 2, "height": 2,
"width": 10, "width": 10,
...@@ -244,6 +267,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -244,6 +267,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
</View> </View>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -252,6 +277,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -252,6 +277,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -262,6 +289,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -262,6 +289,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="shimmer-placeholder" testID="shimmer-placeholder"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -269,6 +298,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -269,6 +298,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -291,6 +322,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -291,6 +322,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
</View> </View>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -301,6 +334,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -301,6 +334,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
testID="shimmer-placeholder" testID="shimmer-placeholder"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -308,6 +343,8 @@ exports[`FavoriteTokenCard renders without error 1`] = ` ...@@ -308,6 +343,8 @@ exports[`FavoriteTokenCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
......
...@@ -23,32 +23,28 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -23,32 +23,28 @@ exports[`FavoriteWalletCard renders without error 1`] = `
} }
> >
<View <View
disabled={false} collapsable={false}
hitSlop={ focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": { "backgroundColor": "#FFFFFF",
"dynamic": {
"dark": "#131313",
"light": "#FFFFFF",
},
},
"borderBottomColor": "rgba(34,34,34,0.05)", "borderBottomColor": "rgba(34,34,34,0.05)",
"borderBottomLeftRadius": 16, "borderBottomLeftRadius": 16,
"borderBottomRightRadius": 16, "borderBottomRightRadius": 16,
...@@ -86,8 +82,16 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -86,8 +82,16 @@ exports[`FavoriteWalletCard renders without error 1`] = `
testID="favorite-wallet-card" testID="favorite-wallet-card"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 4, "gap": 4,
"justifyContent": "space-between", "justifyContent": "space-between",
...@@ -99,6 +103,8 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -99,6 +103,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -109,6 +115,8 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -109,6 +115,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -157,6 +165,8 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -157,6 +165,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
testID="account-icon" testID="account-icon"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -167,6 +177,8 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -167,6 +177,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
/> />
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -179,6 +191,8 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -179,6 +191,8 @@ exports[`FavoriteWalletCard renders without error 1`] = `
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -201,25 +215,22 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -201,25 +215,22 @@ exports[`FavoriteWalletCard renders without error 1`] = `
</View> </View>
</View> </View>
<View <View
aria-disabled={true}
collapsable={false} collapsable={false}
disabled={true} focusVisibleStyle={{}}
hitSlop={ forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onLayout={[Function]}
pointerEvents="box-none"
role="button"
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": { "backgroundColor": "rgba(19, 19, 19, 0.35)",
"dynamic": {
"dark": "rgba(255, 255, 255, 0.38)",
"light": "rgba(19, 19, 19, 0.35)",
},
},
"borderBottomLeftRadius": 999999, "borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999, "borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999, "borderTopLeftRadius": 999999,
...@@ -237,21 +248,31 @@ exports[`FavoriteWalletCard renders without error 1`] = ` ...@@ -237,21 +248,31 @@ exports[`FavoriteWalletCard renders without error 1`] = `
"zIndex": 1080, "zIndex": 1080,
} }
} }
tabIndex={-1}
testID="explore/remove-button" testID="explore/remove-button"
userSelect="none"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
"dynamic": { "dynamic": {
"dark": "#131313", "dark": "#1F1F1F",
"light": "#FFFFFF", "light": "#F9F9F9",
}, },
}, },
"borderBottomLeftRadius": 12, "borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12, "borderBottomRightRadius": 12,
"borderTopLeftRadius": 12, "borderTopLeftRadius": 12,
"borderTopRightRadius": 12, "borderTopRightRadius": 12,
"color": {
"dynamic": {
"dark": "rgba(255, 255, 255, 0.65)",
"light": "rgba(19, 19, 19, 0.63)",
},
},
"flexDirection": "column", "flexDirection": "column",
"height": 2, "height": 2,
"width": 10, "width": 10,
......
...@@ -3,33 +3,28 @@ ...@@ -3,33 +3,28 @@
exports[`RemoveButton renders without error 1`] = ` exports[`RemoveButton renders without error 1`] = `
<View <View
collapsable={false} collapsable={false}
disabled={false} focusVisibleStyle={{}}
hitSlop={ forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"backgroundColor": { "backgroundColor": "rgba(19, 19, 19, 0.35)",
"dynamic": {
"dark": "rgba(255, 255, 255, 0.38)",
"light": "rgba(19, 19, 19, 0.35)",
},
},
"borderBottomLeftRadius": 999999, "borderBottomLeftRadius": 999999,
"borderBottomRightRadius": 999999, "borderBottomRightRadius": 999999,
"borderTopLeftRadius": 999999, "borderTopLeftRadius": 999999,
...@@ -50,6 +45,8 @@ exports[`RemoveButton renders without error 1`] = ` ...@@ -50,6 +45,8 @@ exports[`RemoveButton renders without error 1`] = `
testID="explore/remove-button" testID="explore/remove-button"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": { "backgroundColor": {
...@@ -62,6 +59,12 @@ exports[`RemoveButton renders without error 1`] = ` ...@@ -62,6 +59,12 @@ exports[`RemoveButton renders without error 1`] = `
"borderBottomRightRadius": 12, "borderBottomRightRadius": 12,
"borderTopLeftRadius": 12, "borderTopLeftRadius": 12,
"borderTopRightRadius": 12, "borderTopRightRadius": 12,
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "column", "flexDirection": "column",
"height": 2, "height": 2,
"width": 10, "width": 10,
......
...@@ -25,25 +25,32 @@ exports[`SortButton renders without error 1`] = ` ...@@ -25,25 +25,32 @@ exports[`SortButton renders without error 1`] = `
} }
> >
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -56,9 +63,17 @@ exports[`SortButton renders without error 1`] = ` ...@@ -56,9 +63,17 @@ exports[`SortButton renders without error 1`] = `
> >
<View <View
collapsable={false} collapsable={false}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 8, "gap": 8,
"justifyContent": "center", "justifyContent": "center",
...@@ -73,6 +88,8 @@ exports[`SortButton renders without error 1`] = ` ...@@ -73,6 +88,8 @@ exports[`SortButton renders without error 1`] = `
lineBreakMode="clip" lineBreakMode="clip"
maxFontSizeMultiplier={1.2} maxFontSizeMultiplier={1.2}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -110,6 +127,8 @@ exports[`SortButton renders without error 1`] = ` ...@@ -110,6 +127,8 @@ exports[`SortButton renders without error 1`] = `
}, },
} }
} }
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
......
...@@ -6,25 +6,32 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -6,25 +6,32 @@ exports[`TokenItem renders without error 1`] = `
onPress={[MockFunction]} onPress={[MockFunction]}
> >
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -38,9 +45,17 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -38,9 +45,17 @@ exports[`TokenItem renders without error 1`] = `
> >
<View <View
collapsable={false} collapsable={false}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"flexGrow": 1, "flexGrow": 1,
"gap": 12, "gap": 12,
...@@ -52,6 +67,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -52,6 +67,8 @@ exports[`TokenItem renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -62,6 +79,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -62,6 +79,8 @@ exports[`TokenItem renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -73,6 +92,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -73,6 +92,8 @@ exports[`TokenItem renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -93,6 +114,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -93,6 +114,8 @@ exports[`TokenItem renders without error 1`] = `
</Text> </Text>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
pointerEvents="auto" pointerEvents="auto"
style={ style={
{ {
...@@ -107,6 +130,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -107,6 +130,8 @@ exports[`TokenItem renders without error 1`] = `
testID="token-logo" testID="token-logo"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"backgroundColor": "#FFFFFF", "backgroundColor": "#FFFFFF",
...@@ -148,6 +173,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -148,6 +173,8 @@ exports[`TokenItem renders without error 1`] = `
</View> </View>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flex": 1, "flex": 1,
...@@ -161,6 +188,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -161,6 +188,8 @@ exports[`TokenItem renders without error 1`] = `
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -183,6 +212,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -183,6 +212,8 @@ exports[`TokenItem renders without error 1`] = `
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -202,6 +233,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -202,6 +233,8 @@ exports[`TokenItem renders without error 1`] = `
/> />
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
onLayout={[Function]} onLayout={[Function]}
style={ style={
{ {
...@@ -212,6 +245,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -212,6 +245,8 @@ exports[`TokenItem renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "row", "flexDirection": "row",
...@@ -219,6 +254,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -219,6 +254,8 @@ exports[`TokenItem renders without error 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "flex-end", "alignItems": "flex-end",
...@@ -231,6 +268,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -231,6 +268,8 @@ exports[`TokenItem renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -251,6 +290,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -251,6 +290,8 @@ exports[`TokenItem renders without error 1`] = `
- -
</Text> </Text>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -262,6 +303,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -262,6 +303,8 @@ exports[`TokenItem renders without error 1`] = `
testID="relative-change" testID="relative-change"
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -271,6 +314,8 @@ exports[`TokenItem renders without error 1`] = ` ...@@ -271,6 +314,8 @@ exports[`TokenItem renders without error 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
......
...@@ -38,7 +38,7 @@ function LegacyExploreSearchResultsList({ ...@@ -38,7 +38,7 @@ function LegacyExploreSearchResultsList({
ListHeaderComponent={<SearchEmptySection selectedChain={chainFilter} />} ListHeaderComponent={<SearchEmptySection selectedChain={chainFilter} />}
data={[]} data={[]}
keyExtractor={(): string => 'search-empty-section-container'} keyExtractor={(): string => 'search-empty-section-container'}
keyboardShouldPersistTaps="always" keyboardShouldPersistTaps="always" // TODO(WALL-6724): does not actually work; behaves as default/"never"
renderItem={null} renderItem={null}
scrollEventThrottle={16} scrollEventThrottle={16}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
......
import { AppStackScreenProp } from 'src/app/navigation/types' import { AppStackScreenProp } from 'src/app/navigation/types'
import { ReactNavigationModal } from 'src/components/modals/ReactNavigationModals/ReactNavigationModal' 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 { ModalName } from 'uniswap/src/features/telemetry/constants'
import { SmartWalletAdvancedSettingsModal } from 'wallet/src/features/smartWallet/modals/SmartWalletAdvancedSettingsModal'
export const AdvancedSettingsModal = ( export const AdvancedSettingsModal = (
props: AppStackScreenProp<typeof ModalName.SmartWalletAdvancedSettingsModal>, props: AppStackScreenProp<typeof ModalName.SmartWalletAdvancedSettingsModal>,
......
...@@ -4,10 +4,10 @@ import { useReactNavigationModal } from 'src/components/modals/useReactNavigatio ...@@ -4,10 +4,10 @@ import { useReactNavigationModal } from 'src/components/modals/useReactNavigatio
import type { GetProps } from 'ui/src' import type { GetProps } from 'ui/src'
import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal' import { PasskeyManagementModal } from 'uniswap/src/features/passkey/PasskeyManagementModal'
import { PasskeysHelpModal } from 'uniswap/src/features/passkey/PasskeysHelpModal' 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 { ModalName } from 'uniswap/src/features/telemetry/constants'
import { TestnetModeModal } from 'uniswap/src/features/testnets/TestnetModeModal' import { TestnetModeModal } from 'uniswap/src/features/testnets/TestnetModeModal'
import { HiddenTokenInfoModal } from 'uniswap/src/features/transactions/modals/HiddenTokenInfoModal' 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 // Define names of shared modals we're explicitly supporting on mobile
type ValidModalNames = keyof Pick< type ValidModalNames = keyof Pick<
......
...@@ -46,5 +46,9 @@ export function useLogMissingMnemonic(): void { ...@@ -46,5 +46,9 @@ export function useLogMissingMnemonic(): void {
tags: { file: 'useLogMissingMnemonic.ts', function: 'logMissingMnemonic' }, 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 { navigate } from 'src/app/navigation/rootNavigation'
import { openModal } from 'src/features/modals/modalSlice' import { openModal } from 'src/features/modals/modalSlice'
import { dismissInAppBrowser } from 'src/utils/linking' import { dismissInAppBrowser } from 'src/utils/linking'
...@@ -9,6 +10,7 @@ import { FiatOffRampEventName, ModalName } from 'uniswap/src/features/telemetry/ ...@@ -9,6 +10,7 @@ import { FiatOffRampEventName, ModalName } from 'uniswap/src/features/telemetry/
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { TransactionScreen } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModalContext' import { TransactionScreen } from 'uniswap/src/features/transactions/components/TransactionModal/TransactionModalContext'
import { forceFetchFiatOnRampTransactions } from 'uniswap/src/features/transactions/slice' import { forceFetchFiatOnRampTransactions } from 'uniswap/src/features/transactions/slice'
import i18n from 'uniswap/src/i18n'
import { CurrencyField } from 'uniswap/src/types/currency' import { CurrencyField } from 'uniswap/src/types/currency'
import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { MobileScreens } from 'uniswap/src/types/screens/mobile'
import { createTransactionId } from 'uniswap/src/utils/createTransactionId' import { createTransactionId } from 'uniswap/src/utils/createTransactionId'
...@@ -19,9 +21,7 @@ export function* handleOffRampReturnLink(url: URL) { ...@@ -19,9 +21,7 @@ export function* handleOffRampReturnLink(url: URL) {
try { try {
yield* call(_handleOffRampReturnLink, url) yield* call(_handleOffRampReturnLink, url)
} catch (error) { } catch (error) {
// TODO: handle error in UI Alert.alert(i18n.t('fiatOffRamp.error.populateSend.title'), i18n.t('fiatOffRamp.error.populateSend.description'))
// Alert.alert(i18n.t('walletConnect.error.general.title'), i18n.t('walletConnect.error.general.message'))
// yield* put(openModal({ name: ModalName.Send, initialState: initialSendState }))
} }
} }
...@@ -54,7 +54,13 @@ function* _handleOffRampReturnLink(url: URL) { ...@@ -54,7 +54,13 @@ function* _handleOffRampReturnLink(url: URL) {
throw new Error('Failed to fetch offramp transfer details') 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') 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`] = ` ...@@ -223,31 +223,28 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
} }
> >
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": { "backgroundColor": "rgba(255, 55, 199, 0.08)",
"dynamic": {
"dark": "rgba(255, 55, 199, 0.08)",
"light": "rgba(255, 55, 199, 0.08)",
},
},
"borderBottomLeftRadius": 12, "borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12, "borderBottomRightRadius": 12,
"borderTopLeftRadius": 12, "borderTopLeftRadius": 12,
...@@ -267,9 +264,17 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = ` ...@@ -267,9 +264,17 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 4, "gap": 4,
"justifyContent": "center", "justifyContent": "center",
...@@ -279,6 +284,8 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = ` ...@@ -279,6 +284,8 @@ exports[`GenericImportForm renders a placeholder when there is no value 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.2} maxFontSizeMultiplier={1.2}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
......
...@@ -2,26 +2,32 @@ ...@@ -2,26 +2,32 @@
exports[`renders collection preview card 1`] = ` exports[`renders collection preview card 1`] = `
<View <View
disabled={false} collapsable={false}
hitSlop={ focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"transform": [ "transform": [
...@@ -33,6 +39,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -33,6 +39,8 @@ exports[`renders collection preview card 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -41,6 +49,12 @@ exports[`renders collection preview card 1`] = ` ...@@ -41,6 +49,12 @@ exports[`renders collection preview card 1`] = `
"borderBottomRightRadius": 16, "borderBottomRightRadius": 16,
"borderTopLeftRadius": 16, "borderTopLeftRadius": 16,
"borderTopRightRadius": 16, "borderTopRightRadius": 16,
"color": {
"dynamic": {
"dark": "#FFFFFF",
"light": "#222222",
},
},
"flexDirection": "row", "flexDirection": "row",
"gap": 8, "gap": 8,
"justifyContent": "space-between", "justifyContent": "space-between",
...@@ -52,6 +66,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -52,6 +66,8 @@ exports[`renders collection preview card 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -63,6 +79,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -63,6 +79,8 @@ exports[`renders collection preview card 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"borderBottomLeftRadius": 999999, "borderBottomLeftRadius": 999999,
...@@ -77,6 +95,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -77,6 +95,8 @@ exports[`renders collection preview card 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -101,6 +121,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -101,6 +121,8 @@ exports[`renders collection preview card 1`] = `
<Text <Text
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": { "color": {
...@@ -123,6 +145,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -123,6 +145,8 @@ exports[`renders collection preview card 1`] = `
</View> </View>
</View> </View>
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -131,6 +155,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -131,6 +155,8 @@ exports[`renders collection preview card 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -141,6 +167,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -141,6 +167,8 @@ exports[`renders collection preview card 1`] = `
} }
> >
<View <View
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"flexDirection": "column", "flexDirection": "column",
...@@ -152,6 +180,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -152,6 +180,8 @@ exports[`renders collection preview card 1`] = `
allowFontScaling={true} allowFontScaling={true}
maxFontSizeMultiplier={1.4} maxFontSizeMultiplier={1.4}
numberOfLines={1} numberOfLines={1}
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"color": "#131313", "color": "#131313",
...@@ -297,6 +327,8 @@ exports[`renders collection preview card 1`] = ` ...@@ -297,6 +327,8 @@ exports[`renders collection preview card 1`] = `
}, },
} }
} }
onBlur={[Function]}
onFocus={[Function]}
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
......
...@@ -88,6 +88,7 @@ function SendFormScreenContent({ hideContent }: { hideContent: boolean }): JSX.E ...@@ -88,6 +88,7 @@ function SendFormScreenContent({ hideContent }: { hideContent: boolean }): JSX.E
<TransactionModalInnerContainer fullscreen bottomSheetViewStyles={[bottomSheetViewStyles]}> <TransactionModalInnerContainer fullscreen bottomSheetViewStyles={[bottomSheetViewStyles]}>
{showRecipientSelectBottomSheet && ( {showRecipientSelectBottomSheet && (
<Modal <Modal
extendOnKeyboardVisible
fullScreen fullScreen
backgroundColor={colors.surface1.val} backgroundColor={colors.surface1.val}
name={ModalName.TokenSelector} name={ModalName.TokenSelector}
......
...@@ -42,15 +42,117 @@ export function loadLocaleData(langCode) { ...@@ -42,15 +42,117 @@ export function loadLocaleData(langCode) {
case 'es': case 'es':
require('@formatjs/intl-pluralrules/locale-data/es').default require('@formatjs/intl-pluralrules/locale-data/es').default
switch (langCode) { 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': case 'es-US':
require('@formatjs/intl-numberformat/locale-data/es-US').default require('@formatjs/intl-numberformat/locale-data/es-US').default
require('@formatjs/intl-datetimeformat/locale-data/es-US').default require('@formatjs/intl-datetimeformat/locale-data/es-US').default
require('@formatjs/intl-relativetimeformat/locale-data/es-US').default require('@formatjs/intl-relativetimeformat/locale-data/es-US').default
break break
case 'es-419': // Spanish locales that use `.` as the decimal separator
require('@formatjs/intl-numberformat/locale-data/es-419').default case 'es-AR':
require('@formatjs/intl-datetimeformat/locale-data/es-419').default require('@formatjs/intl-numberformat/locale-data/es-AR').default
require('@formatjs/intl-relativetimeformat/locale-data/es-419').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 break
default: default:
require('@formatjs/intl-numberformat/locale-data/es').default require('@formatjs/intl-numberformat/locale-data/es').default
......
...@@ -93,7 +93,7 @@ export function HomeScreenQuickActions(): JSX.Element { ...@@ -93,7 +93,7 @@ export function HomeScreenQuickActions(): JSX.Element {
<Flex centered row gap="$spacing8" px="$spacing12"> <Flex centered row gap="$spacing8" px="$spacing12">
{actions.map(({ eventName, name, label, Icon, onPress }) => ( {actions.map(({ eventName, name, label, Icon, onPress }) => (
<Trace key={name} logPress element={name} eventOnTrigger={eventName}> <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 <Flex
fill fill
backgroundColor="$accent2" backgroundColor="$accent2"
......
...@@ -25,6 +25,7 @@ import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants ...@@ -25,6 +25,7 @@ import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants
import { TestID } from 'uniswap/src/test/fixtures/testIDs' import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding'
import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile'
import { logger } from 'utilities/src/logger/logger'
const options: ImportMethodOption[] = [seedPhraseImportOption, importFromCloudBackupOption, passKeySignInOption] const options: ImportMethodOption[] = [seedPhraseImportOption, importFromCloudBackupOption, passKeySignInOption]
...@@ -63,7 +64,12 @@ export function ImportMethodScreen({ navigation, route: { params } }: Props): JS ...@@ -63,7 +64,12 @@ export function ImportMethodScreen({ navigation, route: { params } }: Props): JS
if (importType === ImportType.Passkey) { if (importType === ImportType.Passkey) {
setIsLoadingPasskey(true) 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) { if (!credential) {
navigate(ModalName.PasskeysHelp) navigate(ModalName.PasskeysHelp)
......
...@@ -392,25 +392,32 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` ...@@ -392,25 +392,32 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
} }
> >
<View <View
hitSlop={ collapsable={false}
focusVisibleStyle={{}}
forwardedRef={[Function]}
jestAnimatedStyle={
{ {
"bottom": 5, "value": {},
"left": 5,
"right": 5,
"top": 5,
} }
} }
onBlur={[Function]} onBlur={[Function]}
onClick={[Function]} onClick={[Function]}
onFocus={[Function]} onFocus={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]} onResponderGrant={[Function]}
onResponderMove={[Function]} onResponderMove={[Function]}
onResponderRelease={[Function]} onResponderRelease={[Function]}
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
role="button"
style={ style={
{ {
"backgroundColor": "transparent",
"borderBottomLeftRadius": 12,
"borderBottomRightRadius": 12,
"borderTopLeftRadius": 12,
"borderTopRightRadius": 12,
"flexDirection": "column", "flexDirection": "column",
"opacity": 1, "opacity": 1,
"paddingBottom": 4, "paddingBottom": 4,
...@@ -502,6 +509,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` ...@@ -502,6 +509,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
} }
> >
<View <View
aria-disabled={true}
collapsable={false} collapsable={false}
dd-action-name="Continue" dd-action-name="Continue"
focusVisibleStyle={ focusVisibleStyle={
...@@ -526,7 +534,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` ...@@ -526,7 +534,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
onResponderTerminate={[Function]} onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]} onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]} onStartShouldSetResponder={[Function]}
pointerEvents="none" pointerEvents="box-none"
style={ style={
{ {
"alignItems": "center", "alignItems": "center",
...@@ -557,6 +565,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = ` ...@@ -557,6 +565,7 @@ exports[`RestoreCloudBackupPasswordScreen renders correctly 1`] = `
"paddingTop": 16, "paddingTop": 16,
} }
} }
tabIndex={-1}
testID="continue" testID="continue"
userSelect="none" userSelect="none"
> >
......
...@@ -132,7 +132,7 @@ export function LandingScreen({ navigation }: Props): JSX.Element { ...@@ -132,7 +132,7 @@ export function LandingScreen({ navigation }: Props): JSX.Element {
variant="buttonLabel1" variant="buttonLabel1"
> >
{isEmbeddedWalletEnabled {isEmbeddedWalletEnabled
? t('onboarding.intro.button.signInOrImport') ? t('onboarding.intro.button.logInOrImport')
: t('onboarding.landing.button.add')} : t('onboarding.landing.button.add')}
</Text> </Text>
</TouchableArea> </TouchableArea>
......
import { FlashList } from '@shopify/flash-list'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { useReactNavigationModal } from 'src/components/modals/useReactNavigationModal' import { useReactNavigationModal } from 'src/components/modals/useReactNavigationModal'
import { Flex, Text, TouchableArea } from 'ui/src' import { Flex, Text, TouchableArea } from 'ui/src'
import { AnimatedBottomSheetFlashList } from 'ui/src/components/AnimatedFlashList/AnimatedFlashList'
import { Check } from 'ui/src/components/icons' import { Check } from 'ui/src/components/icons'
import { Modal } from 'uniswap/src/components/modals/Modal' import { Modal } from 'uniswap/src/components/modals/Modal'
import { FiatCurrency, ORDERED_CURRENCIES } from 'uniswap/src/features/fiatCurrency/constants' import { FiatCurrency, ORDERED_CURRENCIES } from 'uniswap/src/features/fiatCurrency/constants'
...@@ -29,11 +29,7 @@ export function SettingsFiatCurrencyModal(): JSX.Element { ...@@ -29,11 +29,7 @@ export function SettingsFiatCurrencyModal(): JSX.Element {
<Text pb="$spacing12" textAlign="center" variant="subheading1"> <Text pb="$spacing12" textAlign="center" variant="subheading1">
{t('settings.setting.currency.title')} {t('settings.setting.currency.title')}
</Text> </Text>
<AnimatedBottomSheetFlashList <FlashList data={ORDERED_CURRENCIES} keyExtractor={(item: FiatCurrency) => item} renderItem={renderItem} />
data={ORDERED_CURRENCIES}
keyExtractor={(item: FiatCurrency) => item}
renderItem={renderItem}
/>
</Modal> </Modal>
) )
} }
......
...@@ -27,7 +27,7 @@ import { ...@@ -27,7 +27,7 @@ import {
} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled' } from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled'
import { useWalletRestore } from 'src/features/wallet/useWalletRestore' import { useWalletRestore } from 'src/features/wallet/useWalletRestore'
import { useHapticFeedback } from 'src/utils/haptics/useHapticFeedback' 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 { import {
Bell, Bell,
BookOpen, BookOpen,
...@@ -44,6 +44,7 @@ import { ...@@ -44,6 +44,7 @@ import {
Lock, Lock,
MessageQuestion, MessageQuestion,
Passkey, Passkey,
QuestionInCircleFilled,
Sliders, Sliders,
TouchId, TouchId,
UniswapLogo, UniswapLogo,
...@@ -151,6 +152,27 @@ export function SettingsScreen(): JSX.Element { ...@@ -151,6 +152,27 @@ export function SettingsScreen(): JSX.Element {
[navigation], [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 sections: SettingsSection[] = useMemo((): SettingsSection[] => {
const svgProps: IconProps = { const svgProps: IconProps = {
color: colors.neutral2.get(), color: colors.neutral2.get(),
...@@ -225,7 +247,12 @@ export function SettingsScreen(): JSX.Element { ...@@ -225,7 +247,12 @@ export function SettingsScreen(): JSX.Element {
icon: <Sliders {...iconProps} />, icon: <Sliders {...iconProps} />,
navigationProps: { navigationProps: {
isTestnetEnabled: isTestnetModeEnabled, isTestnetEnabled: isTestnetModeEnabled,
onTestnetModeToggled: () => handleTestnetModeToggle(), onTestnetModeToggled: (): void => handleTestnetModeToggle(),
onPressSmartWallet: (): void => {
navigation.navigate(MobileScreens.SettingsSmartWallet, {
Wrapper: SmartWalletWrapper,
})
},
}, },
}, },
] ]
...@@ -380,6 +407,7 @@ export function SettingsScreen(): JSX.Element { ...@@ -380,6 +407,7 @@ export function SettingsScreen(): JSX.Element {
hasPasskeyBackup, hasPasskeyBackup,
isTestnetModeEnabled, isTestnetModeEnabled,
isSmartWalletEnabled, isSmartWalletEnabled,
SmartWalletWrapper,
handleTestnetModeToggle, handleTestnetModeToggle,
notificationOSPermission, notificationOSPermission,
navigation, navigation,
......
/* eslint-disable max-lines */
/** /**
* Util to format numbers inside reanimated worklets. * Util to format numbers inside reanimated worklets.
* *
...@@ -132,9 +133,29 @@ const transformForLocale = { ...@@ -132,9 +133,29 @@ const transformForLocale = {
en: commaThousDotDec, en: commaThousDotDec,
'en-GB': commaThousDotDec, 'en-GB': commaThousDotDec,
'en-US': commaThousDotDec, 'en-US': commaThousDotDec,
'es-ES': dotThousCommaDec,
'es-US': commaThousDotDec,
'es-419': 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, 'fi-FI': spaceThousCommaDec,
fr: spaceThousCommaDec, fr: spaceThousCommaDec,
'fr-FR': spaceThousCommaDec, 'fr-FR': spaceThousCommaDec,
...@@ -187,9 +208,29 @@ const currencyFormatMap = { ...@@ -187,9 +208,29 @@ const currencyFormatMap = {
en: 'pre', en: 'pre',
'en-GB': 'pre', 'en-GB': 'pre',
'en-US': 'pre', 'en-US': 'pre',
'es-ES': 'post',
'es-US': 'pre',
'es-419': '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', 'fi-FI': 'post',
fr: 'post', fr: 'post',
'fr-FR': 'post', 'fr-FR': 'post',
......
ignores: [ ignores: [
# Dependencies that depcheck thinks are unused but are actually used # Dependencies that depcheck thinks are unused but are actually used
'wrangler',
'@web3-react/eip1193', '@web3-react/eip1193',
'@web3-react/empty', '@web3-react/empty',
'@swc/core', '@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 = { ...@@ -76,11 +76,18 @@ const config: StorybookConfig = {
config.resolve ??= {} config.resolve ??= {}
// Add fallback for Node.js 'os' module
config.resolve.fallback = {
...(config.resolve.fallback || {}),
os: false,
}
config.resolve = { config.resolve = {
...config.resolve, ...config.resolve,
alias: { alias: {
...config?.resolve?.alias, ...config?.resolve?.alias,
'react-native$': 'react-native-web', 'react-native$': 'react-native-web',
'expo-blur': require.resolve('./__mocks__/expo-blur.js'),
}, },
} }
......
import type { Preview } from '@storybook/react' 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 { TamaguiProvider } from '../src/theme/tamaguiProvider'
import '@reach/dialog/styles.css' import '@reach/dialog/styles.css'
import { MemoryRouter } from 'react-router-dom'
import '../src/global.css' import '../src/global.css'
import '../src/polyfills' import '../src/polyfills'
const preview: Preview = { const preview: Preview = {
decorators: [ decorators: [
(Story) => ( (Story) => (
<TamaguiProvider> <MemoryRouter>
{/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */} <ReactRouterUrlProvider>
<Story /> <Provider store={store}>
</TamaguiProvider> <TamaguiProvider>
{/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */}
<Story />
</TamaguiProvider>
</Provider>
</ReactRouterUrlProvider>
</MemoryRouter>
), ),
], ],
parameters: { parameters: {
......
...@@ -175,6 +175,7 @@ module.exports = { ...@@ -175,6 +175,7 @@ module.exports = {
crypto: require.resolve('expo-crypto'), crypto: require.resolve('expo-crypto'),
'react-native-gesture-handler$': require.resolve('react-native-gesture-handler'), 'react-native-gesture-handler$': require.resolve('react-native-gesture-handler'),
'react-native$': 'react-native-web', 'react-native$': 'react-native-web',
'expo-blur': require.resolve('./.storybook/__mocks__/expo-blur.js'),
}, },
plugins: webpackConfig.resolve.plugins, plugins: webpackConfig.resolve.plugins,
// Webpack 5 does not resolve node modules, so we do so for those necessary: // Webpack 5 does not resolve node modules, so we do so for those necessary:
...@@ -310,7 +311,9 @@ module.exports = { ...@@ -310,7 +311,9 @@ module.exports = {
usedExports: true, usedExports: true,
sideEffects: true, sideEffects: true,
// Optimize over all chunks, instead of async chunks (the default), so that initial chunks are also included. // 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 @@ ...@@ -2,7 +2,7 @@
exports[`should inject metadata for valid pools 1`] = ` exports[`should inject metadata for valid pools 1`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
...@@ -131,7 +131,7 @@ exports[`should inject metadata for valid pools 1`] = ` ...@@ -131,7 +131,7 @@ exports[`should inject metadata for valid pools 1`] = `
exports[`should inject metadata for valid pools 2`] = ` exports[`should inject metadata for valid pools 2`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
...@@ -260,7 +260,7 @@ exports[`should inject metadata for valid pools 2`] = ` ...@@ -260,7 +260,7 @@ exports[`should inject metadata for valid pools 2`] = `
exports[`should inject metadata for valid pools 3`] = ` exports[`should inject metadata for valid pools 3`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
exports[`should inject metadata for valid tokens 1`] = ` exports[`should inject metadata for valid tokens 1`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
...@@ -131,7 +131,7 @@ exports[`should inject metadata for valid tokens 1`] = ` ...@@ -131,7 +131,7 @@ exports[`should inject metadata for valid tokens 1`] = `
exports[`should inject metadata for valid tokens 2`] = ` exports[`should inject metadata for valid tokens 2`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
...@@ -260,7 +260,7 @@ exports[`should inject metadata for valid tokens 2`] = ` ...@@ -260,7 +260,7 @@ exports[`should inject metadata for valid tokens 2`] = `
exports[`should inject metadata for valid tokens 3`] = ` exports[`should inject metadata for valid tokens 3`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
...@@ -389,7 +389,7 @@ exports[`should inject metadata for valid tokens 3`] = ` ...@@ -389,7 +389,7 @@ exports[`should inject metadata for valid tokens 3`] = `
exports[`should inject metadata for valid tokens 4`] = ` exports[`should inject metadata for valid tokens 4`] = `
"<!DOCTYPE html> "<!DOCTYPE html>
<html translate="no" style="overflow-x: hidden;"> <html translate="no">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
......
...@@ -11,7 +11,7 @@ ...@@ -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\"", "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", "sitemap:generate": "node scripts/generate-sitemap.js",
"start": "craco start", "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": "NODE_OPTIONS=--max-old-space-size=8192 craco build",
"build:production:analyze": "UNISWAP_ANALYZE_BUNDLE_SIZE=static craco build", "build:production:analyze": "UNISWAP_ANALYZE_BUNDLE_SIZE=static craco build",
"analyze": "source-map-explorer 'build/static/js/*.js' --no-border-checks --gzip", "analyze": "source-map-explorer 'build/static/js/*.js' --no-border-checks --gzip",
...@@ -198,7 +198,9 @@ ...@@ -198,7 +198,9 @@
"@tamagui/core": "1.125.17", "@tamagui/core": "1.125.17",
"@tamagui/portal": "1.125.17", "@tamagui/portal": "1.125.17",
"@tamagui/react-native-svg": "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": "5.51.16",
"@tanstack/react-query-persist-client": "5.75.2",
"@tanstack/react-table": "8.21.2", "@tanstack/react-table": "8.21.2",
"@types/react-scroll-sync": "0.9.0", "@types/react-scroll-sync": "0.9.0",
"@uniswap/analytics": "1.7.0", "@uniswap/analytics": "1.7.0",
......
...@@ -7,7 +7,7 @@ if (process.env.CI !== 'true') { ...@@ -7,7 +7,7 @@ if (process.env.CI !== 'true') {
} }
export default defineConfig({ export default defineConfig({
testDir: './src/pages', testDir: './src',
testMatch: '**/*.e2e.test.ts', testMatch: '**/*.e2e.test.ts',
// TODO: WEB-7311 - Increase number of workers // TODO: WEB-7311 - Increase number of workers
workers: 1, workers: 1,
......
...@@ -9,7 +9,13 @@ ...@@ -9,7 +9,13 @@
], ],
"styleSrc": [ "styleSrc": [
"'self'", "'self'",
"'unsafe-inline'" "'unsafe-inline'",
"https://fonts.googleapis.com"
],
"fontSrc": [
"'self'",
"https://fonts.googleapis.com",
"https://fonts.gstatic.com"
], ],
"imgSrc": [ "imgSrc": [
"*", "*",
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<% } %> <% } %>
<meta <meta
http-equiv="Content-Security-Policy" 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 @@ ...@@ -11170,4 +11170,54 @@
<lastmod>2025-05-03T01:25:23.563Z</lastmod> <lastmod>2025-05-03T01:25:23.563Z</lastmod>
<priority>0.8</priority> <priority>0.8</priority>
</url> </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> </urlset>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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