From 79be098ecdff2721251b2ace82d5e64718ebb68d Mon Sep 17 00:00:00 2001 From: Wei Sun Date: Tue, 14 Jul 2026 12:22:34 -0700 Subject: [PATCH] chore(card): type Card navigators with feature param lists (Phase 3) --- .../AddFundsBottomSheet.tsx | 2 +- .../AssetSelectionBottomSheet.tsx | 2 +- .../components/Onboarding/ConfirmModal.tsx | 2 +- .../Onboarding/RegionSelectorModal.tsx | 2 +- .../PasswordBottomSheet.tsx | 2 +- app/components/UI/Card/routes/index.tsx | 52 +++++---- app/components/UI/Card/types/navigation.ts | 104 ++++++++++++++++++ app/core/NavigationService/types.ts | 75 ++++++------- 8 files changed, 173 insertions(+), 68 deletions(-) create mode 100644 app/components/UI/Card/types/navigation.ts diff --git a/app/components/UI/Card/components/AddFundsBottomSheet/AddFundsBottomSheet.tsx b/app/components/UI/Card/components/AddFundsBottomSheet/AddFundsBottomSheet.tsx index aedb075d34d..3b1ef11757e 100644 --- a/app/components/UI/Card/components/AddFundsBottomSheet/AddFundsBottomSheet.tsx +++ b/app/components/UI/Card/components/AddFundsBottomSheet/AddFundsBottomSheet.tsx @@ -42,7 +42,7 @@ import { import Routes from '../../../../../constants/navigation/Routes'; import { mapCaipChainIdToChainName } from '../../util/mapCaipChainIdToChainName'; -interface AddFundsModalNavigationDetails { +export interface AddFundsModalNavigationDetails { priorityToken?: CardFundingToken; } diff --git a/app/components/UI/Card/components/AssetSelectionBottomSheet/AssetSelectionBottomSheet.tsx b/app/components/UI/Card/components/AssetSelectionBottomSheet/AssetSelectionBottomSheet.tsx index f848a3689b2..0151d884d3a 100644 --- a/app/components/UI/Card/components/AssetSelectionBottomSheet/AssetSelectionBottomSheet.tsx +++ b/app/components/UI/Card/components/AssetSelectionBottomSheet/AssetSelectionBottomSheet.tsx @@ -48,7 +48,7 @@ import { import { useCardHomeData } from '../../hooks/useCardHomeData'; import MoneyBalanceIcon from '../../../../../images/money-balance.svg'; -interface AssetSelectionModalNavigationDetails { +export interface AssetSelectionModalNavigationDetails { navigateToCardHomeOnPriorityToken?: boolean; selectionOnly?: boolean; onTokenSelect?: (token: CardFundingToken) => void; diff --git a/app/components/UI/Card/components/Onboarding/ConfirmModal.tsx b/app/components/UI/Card/components/Onboarding/ConfirmModal.tsx index abd6e74e33f..dcfebcb461b 100644 --- a/app/components/UI/Card/components/Onboarding/ConfirmModal.tsx +++ b/app/components/UI/Card/components/Onboarding/ConfirmModal.tsx @@ -22,7 +22,7 @@ import { ModalAction } from '../../../Rewards/components/RewardsBottomSheetModal import { strings } from '../../../../../../locales/i18n'; import { useParams } from '../../../../../util/navigation/navUtils'; -interface ConfirmModalParams { +export interface ConfirmModalParams { title: string; description: string; icon: IconName; diff --git a/app/components/UI/Card/components/Onboarding/RegionSelectorModal.tsx b/app/components/UI/Card/components/Onboarding/RegionSelectorModal.tsx index 0a803bd6744..9e3632b5422 100644 --- a/app/components/UI/Card/components/Onboarding/RegionSelectorModal.tsx +++ b/app/components/UI/Card/components/Onboarding/RegionSelectorModal.tsx @@ -47,7 +47,7 @@ export const clearOnValueChange = () => { onValueChangeCallback = null; }; -interface RegionSelectorModalParams { +export interface RegionSelectorModalParams { regions: Region[]; renderAreaCode?: boolean; selectedRegionKey?: string | null; diff --git a/app/components/UI/Card/components/PasswordBottomSheet/PasswordBottomSheet.tsx b/app/components/UI/Card/components/PasswordBottomSheet/PasswordBottomSheet.tsx index db44f4423a5..24d61b9695f 100644 --- a/app/components/UI/Card/components/PasswordBottomSheet/PasswordBottomSheet.tsx +++ b/app/components/UI/Card/components/PasswordBottomSheet/PasswordBottomSheet.tsx @@ -26,7 +26,7 @@ import useAuthentication from '../../../../../core/Authentication/hooks/useAuthe import { useTailwind } from '@metamask/design-system-twrnc-preset'; import { CardHomeSelectors } from '../../Views/CardHome/CardHome.testIds'; -interface PasswordBottomSheetParams { +export interface PasswordBottomSheetParams { onSuccess: () => void; description?: string; } diff --git a/app/components/UI/Card/routes/index.tsx b/app/components/UI/Card/routes/index.tsx index a3b1571bfa3..f5be934c565 100644 --- a/app/components/UI/Card/routes/index.tsx +++ b/app/components/UI/Card/routes/index.tsx @@ -38,9 +38,15 @@ import { clearNativeStackNavigatorOptions, transparentModalScreenOptions, } from '../../../../constants/navigation/clearStackNavigatorOptions'; +import type { + CardModalsNavigationParamList, + CardRootParamList, + CardScreensStackParamList, +} from '../types/navigation'; -const Stack = createNativeStackNavigator(); -const ModalsStack = createNativeStackNavigator(); +const ScreensStack = createNativeStackNavigator(); +const RootStack = createNativeStackNavigator(); +const ModalsStack = createNativeStackNavigator(); // All Card main screens render their own header via HeaderStandard, so hide // the navigator chrome by default. @@ -51,7 +57,9 @@ const mainScreenOptions: NativeStackNavigationOptions = { headerShown: false }; const spendingLimitScreenOptions = ({ route, }: { - route: { params?: { flow?: 'manage' | 'enable' | 'onboarding' } }; + route: { + params?: CardScreensStackParamList['CardSpendingLimit']; + }; }): NativeStackNavigationOptions => ({ headerShown: false, gestureEnabled: route.params?.flow !== 'onboarding', @@ -68,37 +76,43 @@ const MainRoutes = () => { ); return ( - - - - + + - - + - - - + + - - - + ); }; @@ -169,12 +183,12 @@ const CardModalsRoutes = () => ( ); const CardRoutes = () => ( - - - + ( ...transparentModalScreenOptions, }} /> - + ); export default withCardSDK(CardRoutes); diff --git a/app/components/UI/Card/types/navigation.ts b/app/components/UI/Card/types/navigation.ts new file mode 100644 index 00000000000..0967384126f --- /dev/null +++ b/app/components/UI/Card/types/navigation.ts @@ -0,0 +1,104 @@ +import type { NavigatorScreenParams } from '@react-navigation/native'; +import type { AddFundsModalNavigationDetails } from '../components/AddFundsBottomSheet/AddFundsBottomSheet'; +import type { AssetSelectionModalNavigationDetails } from '../components/AssetSelectionBottomSheet/AssetSelectionBottomSheet'; +import type { RegionSelectorModalParams } from '../components/Onboarding/RegionSelectorModal'; +import type { ConfirmModalParams } from '../components/Onboarding/ConfirmModal'; +import type { PasswordBottomSheetParams } from '../components/PasswordBottomSheet/PasswordBottomSheet'; +import type { DaimoPayModalParams } from '../components/DaimoPayModal/DaimoPayModal'; +import type { CreditBalanceTooltipParams } from '../components/CreditBalanceTooltipSheet/CreditBalanceTooltipSheet'; +import type { MoneyUnlinkCardSheetRouteParams } from '../components/MoneyUnlinkCardSheet/MoneyUnlinkCardSheet'; +import type { ChooseYourCardParams } from '../Views/ChooseYourCard/ChooseYourCard'; +import type { ReviewOrderParams } from '../Views/ReviewOrder/ReviewOrder'; +import type { OrderCompletedParams } from '../Views/OrderCompleted/OrderCompleted'; +import type { CardFundingToken, CardUserPhase } from '../types'; + +/** + * Nested-navigation params for Card container screens navigated via + * `navigate(container, { screen, params })`. + * + * Kept local to avoid a circular import with NavigationService/types. + */ +interface CardNestedNavigationParams { + screen?: string; + params?: object; +} + +/** + * Param list for screens inside the Card main stack (`MainRoutes`). + */ +// ParamListBase requires `type`; `interface` cannot satisfy it. +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type CardScreensStackParamList = { + CardHome: undefined; + CardWelcome: undefined; + ChooseYourCard: ChooseYourCardParams | undefined; + ReviewOrder: ReviewOrderParams | undefined; + OrderCompleted: OrderCompletedParams | undefined; + CardCashback: undefined; + CardCreditRedeem: undefined; + CardAuthentication: + | { + showAuthPrompt?: boolean; + postAuthRedirect?: { screen: string; params?: object }; + } + | undefined; + CardSpendingLimit: + | { + flow?: 'manage' | 'enable' | 'onboarding' | 'enable_card'; + selectedToken?: CardFundingToken; + } + | undefined; + CardOnboarding: + | (CardNestedNavigationParams & { cardUserPhase?: CardUserPhase }) + | undefined; +}; + +/** + * Param list for screens inside the Card modal stack (`CardModalsRoutes`). + */ +// ParamListBase requires `type`; `interface` cannot satisfy it. +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type CardModalsNavigationParamList = { + CardAddFundsModal: AddFundsModalNavigationDetails | undefined; + CardAssetSelectionModal: AssetSelectionModalNavigationDetails | undefined; + CardRegionSelectionModal: RegionSelectorModalParams; + CardConfirmModal: ConfirmModalParams; + CardPasswordModal: PasswordBottomSheetParams; + CardRecurringFeeModal: undefined; + CardDaimoPayModal: DaimoPayModalParams; + CardViewPinModal: { imageUrl: string }; + CardSpendingLimitOptionsModal: { + currentLimitType: 'full' | 'restricted'; + currentCustomLimit: string; + callerRoute: string; + callerParams?: Record; + }; + CardWaitlistFormModal: { url: string }; + CardForgotPasswordModal: { location?: 'us' | 'international' } | undefined; + CardCreditBalanceTooltipModal: CreditBalanceTooltipParams | undefined; + CardCreditRefundTooltipModal: { isMoneyAccount?: boolean } | undefined; + CardUnlinkMoneyAccountSheet: MoneyUnlinkCardSheetRouteParams | undefined; +}; + +/** + * Param list for the outer Card root stack (`CardRoutes`): main screens + modals. + */ +// ParamListBase requires `type`; `interface` cannot satisfy it. +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type CardRootParamList = { + CardHome: NavigatorScreenParams | undefined; + CardModals: NavigatorScreenParams | undefined; +}; + +/** + * Feature-level Card navigation params for nested `CardScreens` / `CardModals` entry. + */ +// Intersection (`&`) requires `type`; `interface` cannot express this. +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type CardNavigationParamList = CardScreensStackParamList & + CardModalsNavigationParamList & { + CardScreens: NavigatorScreenParams | undefined; + CardModals: + | NavigatorScreenParams + | undefined; + }; diff --git a/app/core/NavigationService/types.ts b/app/core/NavigationService/types.ts index 926cffc8f2e..c4724e36170 100644 --- a/app/core/NavigationService/types.ts +++ b/app/core/NavigationService/types.ts @@ -74,6 +74,11 @@ import type { PerpsStackParamList, } from '../../components/UI/Perps/types/navigation'; import type { MoneyNavigationParamList } from '../../components/UI/Money/types/navigation'; +import type { + CardModalsNavigationParamList, + CardRootParamList, + CardScreensStackParamList, +} from '../../components/UI/Card/types/navigation'; import type { TrendingTokensFullViewParams } from '../../components/UI/Trending/Views/TrendingTokensFullView/TrendingTokensFullView'; import type { MarketInsightsRouteParams } from '../../components/UI/MarketInsights/Views/MarketInsightsView/MarketInsightsView'; import type { MoreTokenActionsMenuParams } from '../../components/UI/TokenDetails/components/MoreTokenActionsMenu'; @@ -81,8 +86,6 @@ import type { SecurityBadgeBottomSheetParams } from '../../components/UI/TokenDe import type { MAPickerSheetParams } from '../../components/UI/Charts/AdvancedChart/MAPickerSheet'; import type { AgenticCliApprovalParams } from '../../components/Views/AgenticCliApproval/types'; import type { AgenticCliDashboardWebviewParams } from '../../components/Views/AgenticCliDashboardWebview/types'; -import type { CreditBalanceTooltipParams } from '../../components/UI/Card/components/CreditBalanceTooltipSheet/CreditBalanceTooltipSheet'; -import type { MoneyUnlinkCardSheetRouteParams } from '../../components/UI/Card/components/MoneyUnlinkCardSheet/MoneyUnlinkCardSheet'; import type { MoneyDeeplinkModalParams } from '../../components/UI/Money/components/MoneyDeeplinkModal/MoneyDeeplinkModal'; import type { TradingSignalsSetupParams } from '../../components/Views/SocialLeaderboard/components/TradingSignalsSetupBottomSheet/TradingSignalsSetupBottomSheet'; import type { ExploreFeedRouteParams } from '../../components/Views/TrendingView/TrendingView'; @@ -198,9 +201,6 @@ import type { } from '../../components/Views/RevealPrivateCredential/RevealPrivateCredential.types'; // Card params -import type { CardConfirmModalParams } from '../../components/UI/Card/Card.types'; -import type { ShippingAddress } from '../../components/UI/Card/util/buildUserAddress'; - // Account actions params import type { AccountActionsParams, @@ -927,25 +927,17 @@ export type RootStackParamList = { ///: END:ONLY_INCLUDE_IF // Card routes - CardScreens: undefined; - CardHome: undefined; - CardWelcome: undefined; - CardAuthentication: { showAuthPrompt?: boolean } | undefined; - CardSpendingLimit: { flow: string } | undefined; - ChooseYourCard: - | { flow: string; shippingAddress?: ShippingAddress } - | undefined; - CardCashback: undefined; - CardCreditRedeem: undefined; - ReviewOrder: undefined; - OrderCompleted: - | { - paymentMethod?: string; - transactionHash?: string; - fromUpgrade?: boolean; - } - | undefined; - CardOnboarding: undefined; + CardScreens: NavigatorScreenParams | undefined; + CardHome: CardScreensStackParamList['CardHome']; + CardWelcome: CardScreensStackParamList['CardWelcome']; + CardAuthentication: CardScreensStackParamList['CardAuthentication']; + CardSpendingLimit: CardScreensStackParamList['CardSpendingLimit']; + ChooseYourCard: CardScreensStackParamList['ChooseYourCard']; + CardCashback: CardScreensStackParamList['CardCashback']; + CardCreditRedeem: CardScreensStackParamList['CardCreditRedeem']; + ReviewOrder: CardScreensStackParamList['ReviewOrder']; + OrderCompleted: CardScreensStackParamList['OrderCompleted']; + CardOnboarding: CardScreensStackParamList['CardOnboarding']; CardOnboardingSignUp: undefined; CardOnboardingConfirmEmail: undefined; CardOnboardingSetPhoneNumber: undefined; @@ -957,26 +949,21 @@ export type RootStackParamList = { CardOnboardingComplete: undefined; CardOnboardingKYCFailed: undefined; CardOnboardingKYCPending: undefined; - CardModals: NestedNavigationParams | undefined; - CardAddFundsModal: undefined; - CardAssetSelectionModal: undefined; - CardRegionSelectionModal: undefined; - CardConfirmModal: CardConfirmModalParams | undefined; - CardPasswordModal: undefined; - CardRecurringFeeModal: undefined; - CardDaimoPayModal: undefined; - CardViewPinModal: { imageUrl: string }; - CardSpendingLimitOptionsModal: { - currentLimitType: 'full' | 'restricted'; - currentCustomLimit: string; - callerRoute: string; - callerParams?: Record; - }; - CardWaitlistFormModal: { url: string }; - CardForgotPasswordModal: { location?: 'us' | 'international' } | undefined; - CardCreditBalanceTooltipModal: CreditBalanceTooltipParams | undefined; - CardCreditRefundTooltipModal: { isMoneyAccount?: boolean } | undefined; - CardUnlinkMoneyAccountSheet: MoneyUnlinkCardSheetRouteParams | undefined; + CardModals: NavigatorScreenParams | undefined; + CardAddFundsModal: CardModalsNavigationParamList['CardAddFundsModal']; + CardAssetSelectionModal: CardModalsNavigationParamList['CardAssetSelectionModal']; + CardRegionSelectionModal: CardModalsNavigationParamList['CardRegionSelectionModal']; + CardConfirmModal: CardModalsNavigationParamList['CardConfirmModal']; + CardPasswordModal: CardModalsNavigationParamList['CardPasswordModal']; + CardRecurringFeeModal: CardModalsNavigationParamList['CardRecurringFeeModal']; + CardDaimoPayModal: CardModalsNavigationParamList['CardDaimoPayModal']; + CardViewPinModal: CardModalsNavigationParamList['CardViewPinModal']; + CardSpendingLimitOptionsModal: CardModalsNavigationParamList['CardSpendingLimitOptionsModal']; + CardWaitlistFormModal: CardModalsNavigationParamList['CardWaitlistFormModal']; + CardForgotPasswordModal: CardModalsNavigationParamList['CardForgotPasswordModal']; + CardCreditBalanceTooltipModal: CardModalsNavigationParamList['CardCreditBalanceTooltipModal']; + CardCreditRefundTooltipModal: CardModalsNavigationParamList['CardCreditRefundTooltipModal']; + CardUnlinkMoneyAccountSheet: CardModalsNavigationParamList['CardUnlinkMoneyAccountSheet']; // Send routes Recipient: SendRecipientParams | undefined;