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 ? (