Skip to content

Commit e2b7b1d

Browse files
authored
chore(bridge): type Bridge navigators with feature param lists (Phase 3) (#33292)
<!-- 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. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> **Phase 3 of the incremental React Navigation typing migration — Bridge feature.** Bridge’s nested navigators (`Bridge` / `BridgeModals`) were loosely typed (`BridgeRouteParams` on the root entry / `undefined` for modals), so nested `navigate(container, { screen, params })` calls could not be checked against real screen param shapes. Call sites already navigate with `{ screen, params }` (e.g. into `BridgeView`); this PR aligns the types with that runtime pattern. This is **types-only** — no intentional runtime navigation behavior change. ### What changed **Feature param lists (`app/components/UI/Bridge/types/navigation.ts`)** - `BridgeScreensStackParamList` — screen stack (`BridgeScreenStack`): BridgeView, token selector, batch sell flows, quote selector, hardware wallet / QR scanner - `BridgeModalsNavigationParamList` — modal stack (`BridgeModalStack`): slippage, block explorer, Blockaid, recipient selector, market closed, network list, price/token warnings, post-trade, batch-sell info sheets, etc. - `BridgeNavigationParamList` — feature union + nested `Bridge` / `BridgeModals` entries **Navigators (`app/components/UI/Bridge/routes.tsx`)** - `createNativeStackNavigator<BridgeScreensStackParamList>()` for `BridgeScreenStack` - `createNativeStackNavigator<BridgeModalsNavigationParamList>()` for `BridgeModalStack` **Root stack (`app/core/NavigationService/types.ts`)** - `Bridge` → `NavigatorScreenParams<BridgeScreensStackParamList>` (was incorrectly `BridgeRouteParams`) - `BridgeModals` → `NavigatorScreenParams<BridgeModalsNavigationParamList>` - Leaf Bridge screen/modal routes now reference the feature param lists ### Why - Improves nested Bridge navigation type-safety ahead of flipping `useNavigation` to strict route checking app-wide - Fixes the root `Bridge` entry type so it matches nested `{ screen, params }` navigation already used throughout the app - Centralizes Bridge route params in one feature file instead of duplicating shapes in `RootStackParamList` - Matches the Phase 3 pattern already landed for other features ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- 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** <!-- mms-check: type=issue-link required=true --> Fixes: https://consensyssoftware.atlassian.net/browse/MCWP-674 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> N/A ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> N/A ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> <!-- 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) - [ ] 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** > Compile-time-only refactor with no navigation logic changes; main risk is incorrect param typings surfacing as TypeScript errors at call sites. > > **Overview** > **Phase 3 React Navigation typing for Bridge** — types only; no intended runtime navigation changes. > > Adds `app/components/UI/Bridge/types/navigation.ts` with **`BridgeScreensStackParamList`**, **`BridgeModalsNavigationParamList`**, and **`BridgeNavigationParamList`**, consolidating screen/modal route params that were previously scattered across `RootStackParamList` imports. > > **`BridgeScreenStack`** and **`BridgeModalStack`** now use `createNativeStackNavigator` with those param lists instead of untyped stacks. > > In **`RootStackParamList`**, **`Bridge`** is typed as `NavigatorScreenParams<BridgeScreensStackParamList>` (replacing incorrect `BridgeRouteParams` on the root entry), **`BridgeModals`** gets `NavigatorScreenParams<BridgeModalsNavigationParamList>`, and flat Bridge routes are aliased from the feature lists (including **`HardwareWalletsSwaps`** on the root list). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f5cf5cd. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 81d497c commit e2b7b1d

3 files changed

Lines changed: 124 additions & 57 deletions

File tree

app/components/UI/Bridge/routes.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@ import { BatchSellFinalReviewModal } from './components/BatchSellFinalReviewModa
3232
import { BatchSellNetworkFeeInfoModal } from './components/BatchSellNetworkFeeInfoModal';
3333
import { BatchSellMinimumReceivedInfoModal } from './components/BatchSellMinimumReceivedInfoModal';
3434
import { BatchSellPriceImpactInfoModal } from './components/BatchSellPriceImpactInfoModal';
35+
import type {
36+
BridgeModalsNavigationParamList,
37+
BridgeScreensStackParamList,
38+
} from './types/navigation';
3539

3640
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3741
type ScreenComponent = React.ComponentType<any>;
3842

39-
const Stack = createNativeStackNavigator();
43+
const Stack = createNativeStackNavigator<BridgeScreensStackParamList>();
4044
export const BridgeScreenStack = () => (
4145
<Stack.Navigator screenOptions={{ headerShown: false }}>
4246
<Stack.Screen name={Routes.BRIDGE.BRIDGE_VIEW} component={BridgeView} />
@@ -71,7 +75,8 @@ export const BridgeScreenStack = () => (
7175
</Stack.Navigator>
7276
);
7377

74-
const ModalStack = createNativeStackNavigator();
78+
const ModalStack =
79+
createNativeStackNavigator<BridgeModalsNavigationParamList>();
7580
export const BridgeModalStack = () => (
7681
<ModalStack.Navigator
7782
screenOptions={{
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import type { NavigatorScreenParams } from '@react-navigation/native';
2+
import type { BridgeRouteParams } from '../hooks/useSwapBridgeNavigation';
3+
import type { BridgeTokenSelectorRouteParams } from '../components/BridgeTokenSelector/BridgeTokenSelector';
4+
import type { BatchSellTokenSelectRouteParams } from '../Views/BatchSellTokenSelect/types';
5+
import type { HardwareWalletsSwapsRouteParams } from '../../HardwareWallet/Swaps/flowStrategy';
6+
import type { HwQrScannerRouteParams } from '../../HardwareWallet/Swaps/HwQrScanner';
7+
import type {
8+
BatchSellSlippageModalParams,
9+
SwapSlippageModalParams,
10+
} from '../components/SlippageModal/types';
11+
import type {
12+
BlockaidModalParams,
13+
TransactionDetailsBlockExplorerParams,
14+
} from '../Bridge.types';
15+
import type { PriceImpactModalRouterParams } from '../components/PriceImpactModal/types';
16+
import type { MissingPriceModalParams } from '../components/MissingPriceModal';
17+
import type { TokenWarningModalParams } from '../components/TokenWarningModal';
18+
import type { HighRateAlertModalParams } from '../components/HighRateAlertModal';
19+
import type { PostTradeBottomSheetParams } from '../components/PostTradeBottomSheet/PostTradeBottomSheet.types';
20+
import type { BatchSellPriceImpactInfoModalParams } from '../components/BatchSellPriceImpactInfoModal/BatchSellPriceImpactInfoModal.types';
21+
import type { BatchSellNetworkFeeInfoModalParams } from '../components/BatchSellNetworkFeeInfoModal/BatchSellNetworkFeeInfoModal.types';
22+
import type { BatchSellMinimumReceivedInfoModalParams } from '../components/BatchSellMinimumReceivedInfoModal/BatchSellMinimumReceivedInfoModal.types';
23+
24+
/**
25+
* Param list for screens inside the Bridge screen stack (`BridgeScreenStack`).
26+
*/
27+
// ParamListBase requires `type`; `interface` cannot satisfy it.
28+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
29+
export type BridgeScreensStackParamList = {
30+
BridgeView: BridgeRouteParams | undefined;
31+
BridgeTokenSelector: BridgeTokenSelectorRouteParams | undefined;
32+
BatchSellTokenSelect: BatchSellTokenSelectRouteParams | undefined;
33+
BatchSellReview: undefined;
34+
QuoteSelectorView: undefined;
35+
HardwareWalletsSwaps: HardwareWalletsSwapsRouteParams | undefined;
36+
HwQrScanner: HwQrScannerRouteParams | undefined;
37+
};
38+
39+
/**
40+
* Param list for screens inside the Bridge modal stack (`BridgeModalStack`).
41+
*/
42+
// ParamListBase requires `type`; `interface` cannot satisfy it.
43+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
44+
export type BridgeModalsNavigationParamList = {
45+
SwapDefaultSlippageModal: SwapSlippageModalParams | undefined;
46+
SwapCustomSlippageModal: SwapSlippageModalParams | undefined;
47+
BatchSellDefaultSlippageModal: BatchSellSlippageModalParams | undefined;
48+
BatchSellCustomSlippageModal: BatchSellSlippageModalParams | undefined;
49+
TransactionDetailsBlockExplorer:
50+
| TransactionDetailsBlockExplorerParams
51+
| undefined;
52+
BlockaidModal: BlockaidModalParams;
53+
RecipientSelectorModal: undefined;
54+
MarketClosedModal: undefined;
55+
NetworkListModal: undefined;
56+
PriceImpactModal: PriceImpactModalRouterParams;
57+
MissingPriceModal: MissingPriceModalParams;
58+
TokenWarningModal: TokenWarningModalParams;
59+
HighRateAlertModal: HighRateAlertModalParams;
60+
PostTradeModal: PostTradeBottomSheetParams;
61+
BatchSellDestinationTokenSelectorModal: undefined;
62+
BatchSellQuoteDetailsModal: undefined;
63+
BatchSellFinalReviewModal: undefined;
64+
BatchSellNetworkFeeInfoModal: BatchSellNetworkFeeInfoModalParams | undefined;
65+
BatchSellMinimumReceivedInfoModal:
66+
| BatchSellMinimumReceivedInfoModalParams
67+
| undefined;
68+
BatchSellPriceImpactInfoModal: BatchSellPriceImpactInfoModalParams;
69+
};
70+
71+
/**
72+
* Feature-level Bridge navigation params for nested `Bridge` / `BridgeModals` entry.
73+
*/
74+
// Intersection (`&`) requires `type`; `interface` cannot express this.
75+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
76+
export type BridgeNavigationParamList = BridgeScreensStackParamList &
77+
BridgeModalsNavigationParamList & {
78+
Bridge: NavigatorScreenParams<BridgeScreensStackParamList> | undefined;
79+
BridgeModals:
80+
| NavigatorScreenParams<BridgeModalsNavigationParamList>
81+
| undefined;
82+
};

app/core/NavigationService/types.ts

Lines changed: 35 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,11 @@ import type { BrowserParams } from '../../components/Views/Browser/Browser.types
2020
import type { ActivityDetailsParams } from '../../components/Views/ActivityDetails/ActivityDetails.types';
2121

2222
// Bridge params
23-
import type { BatchSellTokenSelectRouteParams } from '../../components/UI/Bridge/Views/BatchSellTokenSelect/types';
24-
import type { BridgeRouteParams } from '../../components/UI/Bridge/hooks/useSwapBridgeNavigation';
25-
import type { BridgeTokenSelectorRouteParams } from '../../components/UI/Bridge/components/BridgeTokenSelector/BridgeTokenSelector';
26-
import type { HardwareWalletsSwapsRouteParams } from '../../components/UI/HardwareWallet/Swaps/flowStrategy';
27-
import type { HwQrScannerRouteParams } from '../../components/UI/HardwareWallet/Swaps/HwQrScanner';
28-
import type { PriceImpactModalRouterParams } from '../../components/UI/Bridge/components/PriceImpactModal/types';
29-
import type { MissingPriceModalParams } from '../../components/UI/Bridge/components/MissingPriceModal';
30-
import type { TokenWarningModalParams } from '../../components/UI/Bridge/components/TokenWarningModal';
31-
import type { HighRateAlertModalParams } from '../../components/UI/Bridge/components/HighRateAlertModal';
32-
import type { PostTradeBottomSheetParams } from '../../components/UI/Bridge/components/PostTradeBottomSheet/PostTradeBottomSheet.types';
33-
import type { BatchSellPriceImpactInfoModalParams } from '../../components/UI/Bridge/components/BatchSellPriceImpactInfoModal/BatchSellPriceImpactInfoModal.types';
34-
import type { BatchSellNetworkFeeInfoModalParams } from '../../components/UI/Bridge/components/BatchSellNetworkFeeInfoModal/BatchSellNetworkFeeInfoModal.types';
35-
import type { BatchSellMinimumReceivedInfoModalParams } from '../../components/UI/Bridge/components/BatchSellMinimumReceivedInfoModal/BatchSellMinimumReceivedInfoModal.types';
3623
import type {
37-
BatchSellSlippageModalParams,
38-
SwapSlippageModalParams,
39-
} from '../../components/UI/Bridge/components/SlippageModal/types';
40-
import type {
41-
TransactionDetailsBlockExplorerParams,
42-
BlockaidModalParams,
43-
BridgeTransactionDetailsParams,
44-
} from '../../components/UI/Bridge/Bridge.types';
24+
BridgeModalsNavigationParamList,
25+
BridgeScreensStackParamList,
26+
} from '../../components/UI/Bridge/types/navigation';
27+
import type { BridgeTransactionDetailsParams } from '../../components/UI/Bridge/Bridge.types';
4528
import type { SetPayTokenRequest } from '../../components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken';
4629

4730
// Manual backup params
@@ -727,43 +710,40 @@ export type RootStackParamList = {
727710
EditNetwork: EditNetworkParams | undefined;
728711

729712
// Bridge routes
730-
Bridge: BridgeRouteParams | undefined;
731-
BridgeView: BridgeRouteParams | undefined;
732-
BridgeTokenSelector: BridgeTokenSelectorRouteParams | undefined;
733-
BatchSellTokenSelect: BatchSellTokenSelectRouteParams | undefined;
734-
BatchSellReview: undefined;
735-
QuoteSelectorView: undefined;
736-
HwQrScanner: HwQrScannerRouteParams | undefined;
737-
BridgeModals: undefined;
738-
MarketClosedModal: undefined;
739-
NetworkListModal: undefined;
740-
PriceImpactModal: PriceImpactModalRouterParams;
741-
MissingPriceModal: MissingPriceModalParams;
742-
TokenWarningModal: TokenWarningModalParams;
743-
HighRateAlertModal: HighRateAlertModalParams;
744-
PostTradeModal: PostTradeBottomSheetParams;
745-
BatchSellPriceImpactInfoModal: BatchSellPriceImpactInfoModalParams;
746-
SwapDefaultSlippageModal: SwapSlippageModalParams | undefined;
747-
SwapCustomSlippageModal: SwapSlippageModalParams | undefined;
748-
BatchSellDefaultSlippageModal: BatchSellSlippageModalParams | undefined;
749-
BatchSellCustomSlippageModal: BatchSellSlippageModalParams | undefined;
750-
TransactionDetailsBlockExplorer:
751-
| TransactionDetailsBlockExplorerParams
752-
| undefined;
753-
BlockaidModal: BlockaidModalParams;
754-
RecipientSelectorModal: undefined;
755-
BatchSellDestinationTokenSelectorModal: undefined;
756-
BatchSellQuoteDetailsModal: undefined;
757-
BatchSellFinalReviewModal: undefined;
758-
BatchSellNetworkFeeInfoModal: BatchSellNetworkFeeInfoModalParams | undefined;
759-
BatchSellMinimumReceivedInfoModal:
760-
| BatchSellMinimumReceivedInfoModalParams
713+
Bridge: NavigatorScreenParams<BridgeScreensStackParamList> | undefined;
714+
BridgeView: BridgeScreensStackParamList['BridgeView'];
715+
BridgeTokenSelector: BridgeScreensStackParamList['BridgeTokenSelector'];
716+
BatchSellTokenSelect: BridgeScreensStackParamList['BatchSellTokenSelect'];
717+
BatchSellReview: BridgeScreensStackParamList['BatchSellReview'];
718+
QuoteSelectorView: BridgeScreensStackParamList['QuoteSelectorView'];
719+
HwQrScanner: BridgeScreensStackParamList['HwQrScanner'];
720+
HardwareWalletsSwaps: BridgeScreensStackParamList['HardwareWalletsSwaps'];
721+
BridgeModals:
722+
| NavigatorScreenParams<BridgeModalsNavigationParamList>
761723
| undefined;
724+
MarketClosedModal: BridgeModalsNavigationParamList['MarketClosedModal'];
725+
NetworkListModal: BridgeModalsNavigationParamList['NetworkListModal'];
726+
PriceImpactModal: BridgeModalsNavigationParamList['PriceImpactModal'];
727+
MissingPriceModal: BridgeModalsNavigationParamList['MissingPriceModal'];
728+
TokenWarningModal: BridgeModalsNavigationParamList['TokenWarningModal'];
729+
HighRateAlertModal: BridgeModalsNavigationParamList['HighRateAlertModal'];
730+
PostTradeModal: BridgeModalsNavigationParamList['PostTradeModal'];
731+
BatchSellPriceImpactInfoModal: BridgeModalsNavigationParamList['BatchSellPriceImpactInfoModal'];
732+
SwapDefaultSlippageModal: BridgeModalsNavigationParamList['SwapDefaultSlippageModal'];
733+
SwapCustomSlippageModal: BridgeModalsNavigationParamList['SwapCustomSlippageModal'];
734+
BatchSellDefaultSlippageModal: BridgeModalsNavigationParamList['BatchSellDefaultSlippageModal'];
735+
BatchSellCustomSlippageModal: BridgeModalsNavigationParamList['BatchSellCustomSlippageModal'];
736+
TransactionDetailsBlockExplorer: BridgeModalsNavigationParamList['TransactionDetailsBlockExplorer'];
737+
BlockaidModal: BridgeModalsNavigationParamList['BlockaidModal'];
738+
RecipientSelectorModal: BridgeModalsNavigationParamList['RecipientSelectorModal'];
739+
BatchSellDestinationTokenSelectorModal: BridgeModalsNavigationParamList['BatchSellDestinationTokenSelectorModal'];
740+
BatchSellQuoteDetailsModal: BridgeModalsNavigationParamList['BatchSellQuoteDetailsModal'];
741+
BatchSellFinalReviewModal: BridgeModalsNavigationParamList['BatchSellFinalReviewModal'];
742+
BatchSellNetworkFeeInfoModal: BridgeModalsNavigationParamList['BatchSellNetworkFeeInfoModal'];
743+
BatchSellMinimumReceivedInfoModal: BridgeModalsNavigationParamList['BatchSellMinimumReceivedInfoModal'];
762744
BridgeTransactionDetails:
763745
| BridgeTransactionDetailsParams
764-
| TransactionDetailsBlockExplorerParams
765-
| undefined;
766-
HardwareWalletsSwaps: HardwareWalletsSwapsRouteParams | undefined;
746+
| BridgeModalsNavigationParamList['TransactionDetailsBlockExplorer'];
767747

768748
// Perps routes - use PerpsNavigationParamList for type-safe perps navigation.
769749
// The `Perps` root is a nested stack navigator, so it also accepts the

0 commit comments

Comments
 (0)