diff --git a/src/App.tsx b/src/App.tsx
index abd15d47c8fe..683f6772a9bb 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -47,7 +47,6 @@ import HybridAppHandler from './HybridAppHandler';
import OnyxUpdateManager from './libs/actions/OnyxUpdateManager';
import './libs/HybridApp';
import {AttachmentModalContextProvider} from './pages/media/AttachmentModalScreen/AttachmentModalContext';
-import ExpensifyCardContextProvider from './pages/settings/Wallet/ExpensifyCardPage/ExpensifyCardContextProvider';
import './setup/backgroundTask';
import './setup/fraudProtection';
import './setup/hybridApp';
@@ -122,7 +121,6 @@ function App() {
FullScreenBlockingViewContextProvider,
FullScreenLoaderContextProvider,
SidePanelContextProvider,
- ExpensifyCardContextProvider,
]}
>
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index dd66a515578f..b889a6bb3055 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -259,10 +259,6 @@ const ROUTES = {
route: 'settings/wallet/card/:cardID?',
getRoute: (cardID: string) => `settings/wallet/card/${cardID}` as const,
},
- SETTINGS_WALLET_DOMAIN_CARD_CONFIRM_MAGIC_CODE: {
- route: 'settings/wallet/card/:cardID/confirm-magic-code',
- getRoute: (cardID: string) => `settings/wallet/card/${cardID}/confirm-magic-code` as const,
- },
SETTINGS_DOMAIN_CARD_DETAIL: {
route: 'settings/card/:cardID?',
getRoute: (cardID: string) => `settings/card/${cardID}` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 090fae7a7487..3ee6dbca3183 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -140,7 +140,6 @@ const SCREENS = {
ROOT: 'Settings_Wallet',
VERIFY_ACCOUNT: 'Settings_Wallet_VerifyAccount',
DOMAIN_CARD: 'Settings_Wallet_DomainCard',
- DOMAIN_CARD_CONFIRM_MAGIC_CODE: 'Settings_Wallet_DomainCard_ConfirmMagicCode',
TRANSFER_BALANCE: 'Settings_Wallet_Transfer_Balance',
CHOOSE_TRANSFER_ACCOUNT: 'Settings_Wallet_Choose_Transfer_Account',
ENABLE_PAYMENTS: 'Settings_Wallet_EnablePayments',
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 16e26c0437ad..296c6bd4b976 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -282,7 +282,7 @@ const ExpensifyCardModalStackNavigator = createModalStackNavigator({
});
const DomainCardModalStackNavigator = createModalStackNavigator({
- [SCREENS.DOMAIN_CARD.DOMAIN_CARD_DETAIL]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage/index').default,
+ [SCREENS.DOMAIN_CARD.DOMAIN_CARD_DETAIL]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage').default,
[SCREENS.DOMAIN_CARD.DOMAIN_CARD_REPORT_FRAUD]: () => require('../../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default,
});
@@ -352,9 +352,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/AboutPage/ShareLogPage').default,
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default,
[SCREENS.SETTINGS.WALLET.VERIFY_ACCOUNT]: () => require('../../../../pages/settings/Wallet/VerifyAccountPage').default,
- [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage/index').default,
- [SCREENS.SETTINGS.WALLET.DOMAIN_CARD_CONFIRM_MAGIC_CODE]: () =>
- require('../../../../pages/settings/Wallet/ExpensifyCardPage/ExpensifyCardVerifyAccountPage').default,
+ [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage').default,
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: () => require('../../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default,
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD_CONFIRM_MAGIC_CODE]: () =>
require('../../../../pages/settings/Wallet/ReportVirtualCardFraudVerifyAccountPage').default,
diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts
index 9a841547c3ac..8c8a4930b017 100755
--- a/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts
+++ b/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts
@@ -31,7 +31,6 @@ const SETTINGS_TO_RHP: Partial['config'] = {
path: ROUTES.SETTINGS_WALLET_DOMAIN_CARD.route,
exact: true,
},
- [SCREENS.SETTINGS.WALLET.DOMAIN_CARD_CONFIRM_MAGIC_CODE]: {
- path: ROUTES.SETTINGS_WALLET_DOMAIN_CARD_CONFIRM_MAGIC_CODE.route,
- exact: true,
- },
[SCREENS.SETTINGS.WALLET.VERIFY_ACCOUNT]: {
path: ROUTES.SETTINGS_WALLET_VERIFY_ACCOUNT,
exact: true,
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index e005058e3e7d..478930755635 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -146,10 +146,6 @@ type SettingsNavigatorParamList = {
/** cardID of selected card */
cardID: string;
};
- [SCREENS.SETTINGS.WALLET.DOMAIN_CARD_CONFIRM_MAGIC_CODE]: {
- /** cardID of selected card */
- cardID: string;
- };
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: {
/** cardID of selected card */
cardID: string;
diff --git a/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx b/src/pages/settings/Wallet/ExpensifyCardPage.tsx
similarity index 82%
rename from src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
rename to src/pages/settings/Wallet/ExpensifyCardPage.tsx
index af29c821243a..cfdf3695610d 100644
--- a/src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
+++ b/src/pages/settings/Wallet/ExpensifyCardPage.tsx
@@ -12,23 +12,23 @@ import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
+import ValidateCodeActionModal from '@components/ValidateCodeActionModal';
import useBeforeRemove from '@hooks/useBeforeRemove';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
-import {resetValidateActionCodeSent} from '@libs/actions/User';
+import {requestValidateCodeAction, resetValidateActionCodeSent} from '@libs/actions/User';
import {formatCardExpiration, getDomainCards, maskCard, maskPin} from '@libs/CardUtils';
import {convertToDisplayString, getCurrencyKeyByCountryCode} from '@libs/CurrencyUtils';
+import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import {buildCannedSearchQuery} from '@libs/SearchQueryUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
-import RedDotCardSection from '@pages/settings/Wallet/RedDotCardSection';
-import CardDetails from '@pages/settings/Wallet/WalletPage/CardDetails';
-import {clearActivatedCardPin} from '@userActions/Card';
+import {clearActivatedCardPin, revealVirtualCardDetails} from '@userActions/Card';
import {openOldDotLink} from '@userActions/Link';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
@@ -36,8 +36,11 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {CurrencyList} from '@src/types/onyx';
+import type {ExpensifyCardDetails} from '@src/types/onyx/Card';
+import type {Errors} from '@src/types/onyx/OnyxCommon';
import {getEmptyObject} from '@src/types/utils/EmptyObject';
-import useExpensifyCardContext from './useExpensifyCardContext';
+import RedDotCardSection from './RedDotCardSection';
+import CardDetails from './WalletPage/CardDetails';
type ExpensifyCardPageProps = PlatformStackScreenProps;
@@ -73,6 +76,8 @@ function ExpensifyCardPage({
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const {translate} = useLocalize();
+ const [isValidateCodeActionModalVisible, setIsValidateCodeActionModalVisible] = useState(false);
+ const [currentCardID, setCurrentCardID] = useState(-1);
const isTravelCard = cardList?.[cardID]?.nameValuePairs?.isTravelCard;
const shouldDisplayCardDomain = !isTravelCard && (!cardList?.[cardID]?.nameValuePairs?.issuedBy || !cardList?.[cardID]?.nameValuePairs?.isVirtual);
const domain = cardList?.[cardID]?.domainName ?? '';
@@ -88,6 +93,8 @@ function ExpensifyCardPage({
return [cardList?.[cardID]];
}, [shouldDisplayCardDomain, cardList, cardID, domain]);
+ useBeforeRemove(() => setIsValidateCodeActionModalVisible(false));
+
useEffect(() => {
return () => {
if (!pin) {
@@ -111,15 +118,52 @@ function ExpensifyCardPage({
const cardToAdd = useMemo(() => {
return virtualCards?.at(0);
}, [virtualCards]);
+ const [cardsDetails, setCardsDetails] = useState>({});
+ const [isCardDetailsLoading, setIsCardDetailsLoading] = useState>({});
+ const [cardsDetailsErrors, setCardsDetailsErrors] = useState>({});
+ const [validateError, setValidateError] = useState({});
+ const {isAccountLocked, showLockedAccountModal} = useContext(LockedAccountContext);
- const {cardsDetails, setCardsDetails, isCardDetailsLoading, cardsDetailsErrors} = useExpensifyCardContext();
+ const openValidateCodeModal = (revealedCardID: number) => {
+ setCurrentCardID(revealedCardID);
+ setIsValidateCodeActionModalVisible(true);
+ };
- // This resets card details when we exit the page.
- useBeforeRemove(() => {
- setCardsDetails((oldCardDetails) => ({...oldCardDetails, [cardID]: null}));
- });
-
- const {isAccountLocked, showLockedAccountModal} = useContext(LockedAccountContext);
+ const handleRevealDetails = (validateCode: string) => {
+ setIsCardDetailsLoading((prevState: Record) => ({
+ ...prevState,
+ [currentCardID]: true,
+ }));
+ // We can't store the response in Onyx for security reasons.
+ // That is why this action is handled manually and the response is stored in a local state
+ // Hence eslint disable here.
+ // eslint-disable-next-line rulesdir/no-thenable-actions-in-views
+ revealVirtualCardDetails(currentCardID, validateCode)
+ .then((value) => {
+ setCardsDetails((prevState: Record) => ({...prevState, [currentCardID]: value}));
+ setCardsDetailsErrors((prevState) => ({
+ ...prevState,
+ [currentCardID]: '',
+ }));
+ setIsValidateCodeActionModalVisible(false);
+ })
+ .catch((error: string) => {
+ // Displaying magic code errors is handled in the modal, no need to set it on the card
+ // TODO: remove setValidateError once backend deploys https://github.com/Expensify/Web-Expensify/pull/46007
+ if (error === 'validateCodeForm.error.incorrectMagicCode') {
+ setValidateError(() => getMicroSecondOnyxErrorWithTranslationKey('validateCodeForm.error.incorrectMagicCode'));
+ return;
+ }
+ setCardsDetailsErrors((prevState) => ({
+ ...prevState,
+ [currentCardID]: error,
+ }));
+ setIsValidateCodeActionModalVisible(false);
+ })
+ .finally(() => {
+ setIsCardDetailsLoading((prevState: Record) => ({...prevState, [currentCardID]: false}));
+ });
+ };
const hasDetectedDomainFraud = cardsToShow?.some((card) => card?.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN);
const hasDetectedIndividualFraud = cardsToShow?.some((card) => card?.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL);
@@ -132,6 +176,7 @@ function ExpensifyCardPage({
const formattedAvailableSpendAmount = convertToDisplayString(cardsToShow?.at(0)?.availableSpend, currency);
const {limitNameKey, limitTitleKey} = getLimitTypeTranslationKeys(cardsToShow?.at(0)?.nameValuePairs?.limitType);
+ const primaryLogin = account?.primaryLogin ?? '';
const isSignedInAsDelegate = !!account?.delegatedAccess?.delegate || false;
if (isNotFound) {
@@ -216,7 +261,7 @@ function ExpensifyCardPage({
showLockedAccountModal();
return;
}
- Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAIN_CARD_CONFIRM_MAGIC_CODE.getRoute(cardID));
+ openValidateCodeModal(card.cardID);
}}
isDisabled={isCardDetailsLoading[card.cardID] || isOffline}
isLoading={isCardDetailsLoading[card.cardID]}
@@ -268,7 +313,7 @@ function ExpensifyCardPage({
!isSignedInAsDelegate ? (