Skip to content

Commit f166abb

Browse files
authored
refactor(aggregator): migrate aggregator routes to native stack (MetaMask#30611)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Migrates the legacy aggregator routes (`Aggregator/routes/index.tsx`) from `@react-navigation/stack` (JS stack) to `@react-navigation/native-stack`, aligning with the rest of the Ramp routing tree (Unified V2 `Ramp/routes.tsx` already uses native stack). Native stack uses platform-native navigation primitives (`UINavigationController` on iOS, `FragmentTransaction` on Android), which yields better performance and a more native feel — especially noticeable on the modal/overlay transitions used by `Quotes` and `Checkout`. The migration is purely a wiring/config change. No screen behaviour, route names, or component imports change. Translations applied: - Navigator: `createStackNavigator` → `createNativeStackNavigator` - Per-screen overlay options: replaced inline `headerShown / cardStyle / animationEnabled / detachPreviousScreen` blocks for `Quotes` and `Checkout` with the shared `clearNativeStackNavigatorOptions` + `transparentModalScreenOptions` presets from `app/constants/navigation/clearStackNavigatorOptions.ts`. - Property renames: `cardStyle` → `contentStyle`, `animationEnabled: false` → `animation: 'none'`. - The `detachPreviousScreen: false` workaround used to keep the underlying screen mounted under transparent modals is no longer needed — native stack keeps the presenting screen mounted automatically when `presentation: 'transparentModal'` is used. - Removed the now-unused `colors` import (it was only used to set `cardStyle.backgroundColor: colors.transparent`). Test mock in `routes/index.test.tsx` updated to mock `@react-navigation/native-stack` (`createNativeStackNavigator`) instead of `@react-navigation/stack`. ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry:null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <img width="364" height="752" alt="Aggregator Before" src="https://github.com/user-attachments/assets/423ec7f4-66b2-48dd-bfc2-ab2769eaf260" /> ### **After** <img width="364" height="752" alt="Aggregator After" src="https://github.com/user-attachments/assets/e545af4e-9b7d-4907-a343-6475c44ba0be" /> Android https://github.com/user-attachments/assets/9df9f89d-3c4e-4e49-ac32-ba3896d0384f ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Navigation-only wiring in Ramp Aggregator routes; no business logic, auth, or data handling changes. > > **Overview** > **Aggregator Ramp routing** now uses `@react-navigation/native-stack` instead of the JS stack, matching the newer Ramp navigation setup while leaving screens and route names unchanged. > > **Quotes**, **Checkout**, and the nested **modals** stack pick up shared `clearNativeStackNavigatorOptions` and `transparentModalScreenOptions` instead of inline `cardStyle` / `animationEnabled` / `detachPreviousScreen` workarounds; `BUILD_QUOTE_HAS_STARTED` uses `animation: 'none'`. The route test mock was updated to `createNativeStackNavigator`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 371f725. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent d95e80a commit f166abb

2 files changed

Lines changed: 24 additions & 25 deletions

File tree

app/components/UI/Ramp/Aggregator/routes/index.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { RampType } from '../types';
88
import Routes from '../../../../../constants/navigation/Routes';
99
import { backgroundState } from '../../../../../util/test/initial-root-state';
1010

11-
jest.mock('@react-navigation/stack', () => ({
12-
createStackNavigator: jest.fn().mockReturnValue({
11+
jest.mock('@react-navigation/native-stack', () => ({
12+
createNativeStackNavigator: jest.fn().mockReturnValue({
1313
Navigator: ({ children }: { children: React.ReactNode }) => children,
1414
Screen: ({
1515
name,

app/components/UI/Ramp/Aggregator/routes/index.tsx

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { createStackNavigator } from '@react-navigation/stack';
2+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
33
import Quotes from '../Views/Quotes';
44
import CheckoutWebView from '../Views/Checkout';
55
import BuildQuote from '../Views/BuildQuote';
@@ -10,52 +10,51 @@ import FiatSelectorModal from '../components/FiatSelectorModal';
1010
import { RampType } from '../types';
1111
import { RampSDKProvider } from '../sdk';
1212
import Routes from '../../../../../constants/navigation/Routes';
13-
import { colors } from '../../../../../styles/common';
1413
import IncompatibleAccountTokenModal from '../components/IncompatibleAccountTokenModal';
1514
import RegionSelectorModal from '../components/RegionSelectorModal';
1615
import UnsupportedRegionModal from '../components/UnsupportedRegionModal';
1716
import SettingsModal from '../Views/Modals/Settings';
18-
import { clearStackNavigatorOptions } from '../../../../../constants/navigation/clearStackNavigatorOptions';
17+
import {
18+
clearNativeStackNavigatorOptions,
19+
transparentModalScreenOptions,
20+
} from '../../../../../constants/navigation/clearStackNavigatorOptions';
1921

20-
const Stack = createStackNavigator();
21-
const ModalsStack = createStackNavigator();
22+
const Stack = createNativeStackNavigator();
23+
const ModalsStack = createNativeStackNavigator();
24+
25+
const overlayScreenOptions = {
26+
...clearNativeStackNavigatorOptions,
27+
...transparentModalScreenOptions,
28+
gestureEnabled: false,
29+
};
2230

2331
const MainRoutes = () => (
2432
<Stack.Navigator initialRouteName={Routes.RAMP.BUILD_QUOTE}>
2533
<Stack.Screen name={Routes.RAMP.BUILD_QUOTE} component={BuildQuote} />
2634
<Stack.Screen
2735
name={Routes.RAMP.BUILD_QUOTE_HAS_STARTED}
2836
component={BuildQuote}
29-
options={{ animationEnabled: false }}
37+
options={{ animation: 'none' }}
3038
/>
3139
<Stack.Screen
3240
name={Routes.RAMP.QUOTES}
3341
component={Quotes}
34-
options={{
35-
headerShown: false,
36-
cardStyle: { backgroundColor: colors.transparent },
37-
animationEnabled: false,
38-
gestureEnabled: false,
39-
detachPreviousScreen: false,
40-
}}
42+
options={overlayScreenOptions}
4143
/>
4244
<Stack.Screen
4345
name={Routes.RAMP.CHECKOUT}
4446
component={CheckoutWebView}
45-
options={{
46-
headerShown: false,
47-
cardStyle: { backgroundColor: colors.transparent },
48-
animationEnabled: false,
49-
gestureEnabled: false,
50-
detachPreviousScreen: false,
51-
}}
47+
options={overlayScreenOptions}
5248
/>
5349
</Stack.Navigator>
5450
);
5551

5652
const RampModalsRoutes = () => (
5753
<ModalsStack.Navigator
58-
screenOptions={{ ...clearStackNavigatorOptions, presentation: 'modal' }}
54+
screenOptions={{
55+
...clearNativeStackNavigatorOptions,
56+
presentation: 'modal',
57+
}}
5958
>
6059
<ModalsStack.Screen
6160
name={Routes.RAMP.MODALS.TOKEN_SELECTOR}
@@ -100,8 +99,8 @@ const RampRoutes = ({ rampType }: { rampType: RampType }) => (
10099
name={Routes.RAMP.MODALS.ID}
101100
component={RampModalsRoutes}
102101
options={{
103-
...clearStackNavigatorOptions,
104-
detachPreviousScreen: false,
102+
...clearNativeStackNavigatorOptions,
103+
...transparentModalScreenOptions,
105104
}}
106105
/>
107106
</Stack.Navigator>

0 commit comments

Comments
 (0)