Skip to content

Commit 75716cf

Browse files
authored
refactor(money): use native stack navigators for Money routes (#30008)
<!-- 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** **Notes for reviewer**: There should be no functional difference after this PR. Please refer to TDD on why we are making this switch: https://docs.google.com/document/d/1_oDELkGRLUgaAeMs2NSzG9w1UyjsD5VSKQlW7yW5oZU/edit?tab=t.0#heading=h.gba8s4wq9tzu Money’s **screen** stack and **modal** stack were implemented with `@react-navigation/stack` (JS-driven stack). This change migrates both to **`createNativeStackNavigator`** from `@react-navigation/native-stack`, matching patterns already used elsewhere (e.g. Bridge, Perps main stack). While migrating, surfaced and fixed a latent issue in the shared confirmations helper `getEmptyNavHeader` that prevented the “no header shadow” intent from being honored under native stack. This is why the PR also touches `app/components/Views/confirmations/` and needs **@MetaMask/confirmations** approval. **What changed — Money** - **`MoneyScreenStack`**: `createStackNavigator` → `createNativeStackNavigator`. Replaced `cardStyle: { backgroundColor }` with **`contentStyle: { backgroundColor }`** so the theme background still fills the scene container and avoids flashes during inner navigation. - **`MoneyModalStack`**: same migration. Navigator `screenOptions` now use **`clearNativeStackNavigatorOptions`** and **`transparentModalScreenOptions`** (same preset as `BridgeModalStack`) so modal routes keep a **transparent** scene background — important because Money sheets use `BottomSheet` over the underlying Money home; an opaque default `contentStyle` would hide content behind the sheet. - **`RedesignedConfirmations` (Confirm)**: still uses `useEmptyNavHeaderForConfirmations()`. The previous `as NativeStackNavigationOptions` cast at the call site has been removed — see the confirmations change below for why it is no longer needed. **What changed — Confirmations** - **`getEmptyNavHeader` (`app/components/Views/confirmations/components/UI/navbar/navbar.tsx`)**: now sets **`headerShadowVisible: false`** and narrows `headerStyle` to **`{ backgroundColor }`** only. Previously it inherited `headerStyle.shadowColor: 'transparent'` and `headerStyle.elevation: 0` from `getNavbar`. Those keys are valid on `@react-navigation/stack` but **not on `@react-navigation/native-stack`** (whose `headerStyle` only supports `backgroundColor` / `blurEffect`), so under native stack the header shadow/hairline was not being suppressed. `headerShadowVisible` is the official cross-stack API for this and is supported by both JS stack and native stack, so the visible outcome for the JS-stack callers (`Earn`, `Stake`, `Send`) is unchanged. - **Net effect**: the helper is now **safe to use as-is** on either stack without a type cast, which is what allowed the cast at the Money call site to be removed. **Why** - Aligns Money with other flows on **native** stack transitions and behavior. - Reduces JS-thread animation work for stack transitions where native stack is sufficient. - Makes `getEmptyNavHeader` correct for both navigation stacks, removing a silently-discarded style on native stack. **Tests** - `app/components/UI/Money/routes/index.test.tsx` — now mocks `@react-navigation/native-stack` and asserts `contentStyle` background + registered screens (including modal routes). - `app/components/Views/confirmations/components/UI/navbar/navbar.test.tsx` — new `getEmptyNavHeader` test asserting `headerShadowVisible: false`, `headerShown: true`, and the narrowed `headerStyle`. - `app/components/Views/confirmations/hooks/ui/useEmptyNavHeaderForConfirmations.test.tsx` — mock updated to match the new shape (`headerShadowVisible: false`, `headerStyle` without `shadowColor` / `elevation`). ## **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="382" height="768" alt="iOS before" src="https://github.com/user-attachments/assets/fb101490-597e-480d-ac80-b582d1dc4c76" /> ### **After** <img width="382" height="768" alt="iOS after" src="https://github.com/user-attachments/assets/5fabf030-b4f9-453b-92cd-16a09e1fb6ce" /> ## **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 - [ ] 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] > **Medium Risk** > Changes the navigation implementation for Money screens/modals to `@react-navigation/native-stack`, which can subtly affect transitions, header/background rendering, and modal presentation across platforms. > > **Overview** > Migrates Money’s screen and modal navigators from `@react-navigation/stack` to `@react-navigation/native-stack`. > > Updates the screen stack to use `contentStyle` (instead of `cardStyle`) for applying the theme background, and updates the modal stack to use `clearNativeStackNavigatorOptions` + `transparentModalScreenOptions` for transparent modal presentation. > > Adjusts the Money routes tests to mock `@react-navigation/native-stack` and assert the new `contentStyle` background behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a9d4ae5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 1228eda commit 75716cf

2 files changed

Lines changed: 17 additions & 14 deletions

File tree

app/components/UI/Money/routes/index.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ const themeWithCustomBackground = {
2828
},
2929
};
3030

31-
jest.mock('@react-navigation/stack', () => ({
32-
createStackNavigator: () => ({
31+
jest.mock('@react-navigation/native-stack', () => ({
32+
createNativeStackNavigator: () => ({
3333
Navigator: ({
3434
children,
3535
screenOptions,
3636
}: {
3737
children: React.ReactNode;
3838
screenOptions?: {
3939
headerShown?: boolean;
40-
cardStyle?: { backgroundColor?: string };
40+
contentStyle?: { backgroundColor?: string };
4141
};
4242
}) => (
4343
<MockView testID="money-stack-navigator">
44-
<MockText testID="money-card-background-color">
45-
{screenOptions?.cardStyle?.backgroundColor ?? 'none'}
44+
<MockText testID="money-content-background-color">
45+
{screenOptions?.contentStyle?.backgroundColor ?? 'none'}
4646
</MockText>
4747
{screenOptions?.headerShown === false && (
4848
<MockText testID="money-header-hidden">header-hidden</MockText>
@@ -80,12 +80,12 @@ describe('MoneyScreenStack', () => {
8080
expect(getByTestId('money-screen-MoneyActivity')).toBeOnTheScreen();
8181
});
8282

83-
it('sets stack card background from theme to avoid flash during inner navigation', () => {
83+
it('sets stack content background from theme to avoid flash during inner navigation', () => {
8484
const { getByTestId } = renderWithProvider(<MoneyScreenStack />, {
8585
theme: themeWithCustomBackground,
8686
});
8787

88-
expect(getByTestId('money-card-background-color')).toHaveTextContent(
88+
expect(getByTestId('money-content-background-color')).toHaveTextContent(
8989
EXPECTED_CARD_BACKGROUND,
9090
);
9191
});

app/components/UI/Money/routes/index.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import React, { useEffect } from 'react';
2-
import { createStackNavigator } from '@react-navigation/stack';
2+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
33
import Routes from '../../../../constants/navigation/Routes';
4-
import { clearStackNavigatorOptions } from '../../../../constants/navigation/clearStackNavigatorOptions';
4+
import {
5+
clearNativeStackNavigatorOptions,
6+
transparentModalScreenOptions,
7+
} from '../../../../constants/navigation/clearStackNavigatorOptions';
58
import { useTheme } from '../../../../util/theme';
69
import useThunkDispatch from '../../../hooks/useThunkDispatch';
710
import { upgradeMoneyAccount } from '../../../../actions/money';
@@ -19,8 +22,8 @@ import MoneyEarnCryptoInfoSheet from '../components/MoneyEarnCryptoInfoSheet';
1922
import { Confirm } from '../../../Views/confirmations/components/confirm';
2023
import { useEmptyNavHeaderForConfirmations } from '../../../Views/confirmations/hooks/ui/useEmptyNavHeaderForConfirmations';
2124

22-
const Stack = createStackNavigator();
23-
const ModalStack = createStackNavigator();
25+
const Stack = createNativeStackNavigator();
26+
const ModalStack = createNativeStackNavigator();
2427

2528
const MoneyScreenStack = () => {
2629
const { colors } = useTheme();
@@ -31,7 +34,7 @@ const MoneyScreenStack = () => {
3134
initialRouteName={Routes.MONEY.HOME}
3235
screenOptions={{
3336
headerShown: false,
34-
cardStyle: { backgroundColor: colors.background.default },
37+
contentStyle: { backgroundColor: colors.background.default },
3538
}}
3639
>
3740
<Stack.Screen name={Routes.MONEY.HOME} component={MoneyHomeView} />
@@ -59,8 +62,8 @@ const MoneyScreenStack = () => {
5962
const MoneyModalStack = () => (
6063
<ModalStack.Navigator
6164
screenOptions={{
62-
...clearStackNavigatorOptions,
63-
presentation: 'transparentModal',
65+
...clearNativeStackNavigatorOptions,
66+
...transparentModalScreenOptions,
6467
}}
6568
>
6669
<ModalStack.Screen

0 commit comments

Comments
 (0)