Skip to content

Commit

Permalink
Merge pull request #55278 from callstack-internal/fix/53996-wallet-it…
Browse files Browse the repository at this point in the history
…s-not-here-error

Add report fraud confirmation page when getting new virtual card
  • Loading branch information
mountiny authored Jan 29, 2025
2 parents 3fb0633 + 2cecae4 commit 79e0ecf
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 8 deletions.
156 changes: 156 additions & 0 deletions assets/images/magnifying-glass-spy-mouth-closed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ const ROUTES = {
route: 'settings/wallet/card/:cardID/report-virtual-fraud',
getRoute: (cardID: string) => `settings/wallet/card/${cardID}/report-virtual-fraud` as const,
},
SETTINGS_REPORT_FRAUD_CONFIRMATION: {
route: 'settings/wallet/card/:cardID/report-virtual-fraud-confirm',
getRoute: (cardID: string) => `settings/wallet/card/${cardID}/report-virtual-fraud-confirm` as const,
},
SETTINGS_DOMAINCARD_REPORT_FRAUD: {
route: 'settings/card/:cardID/report-virtual-fraud',
getRoute: (cardID: string) => `settings/card/${cardID}/report-virtual-fraud` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ const SCREENS = {
ENABLE_PAYMENTS: 'Settings_Wallet_EnablePayments',
CARD_ACTIVATE: 'Settings_Wallet_Card_Activate',
REPORT_VIRTUAL_CARD_FRAUD: 'Settings_Wallet_ReportVirtualCardFraud',
REPORT_VIRTUAL_CARD_FRAUD_CONFIRMATION: 'Settings_Wallet_ReportVirtualCardFraudConfirmation',
CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS: 'Settings_Wallet_Cards_Digital_Details_Update_Address',
VERIFY_ACCOUNT: 'Settings_Wallet_Verify_Account',
},
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ import Link from '@assets/images/link.svg';
import Location from '@assets/images/location.svg';
import Lock from '@assets/images/lock.svg';
import Luggage from '@assets/images/luggage.svg';
import MagnifyingGlassSpyMouthClosed from '@assets/images/magnifying-glass-spy-mouth-closed.svg';
import MagnifyingGlass from '@assets/images/magnifying-glass.svg';
import Mail from '@assets/images/mail.svg';
import MakeAdmin from '@assets/images/make-admin.svg';
Expand Down Expand Up @@ -422,4 +423,5 @@ export {
GalleryNotFound,
Train,
boltSlash,
MagnifyingGlassSpyMouthClosed,
};
5 changes: 5 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,11 @@ const translations = {
deactivateCard: 'Deactivate card',
reportVirtualCardFraud: 'Report virtual card fraud',
},
reportFraudConfirmationPage: {
title: 'Card fraud reported',
description: 'We’ve permanently deactivated your existing card. When you go back to view your card details, you’ll have a new virtual card available.',
buttonText: 'Got it, thanks!',
},
activateCardPage: {
activateCard: 'Activate card',
pleaseEnterLastFour: 'Please enter the last four digits of your card.',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,11 @@ const translations = {
deactivateCard: 'Desactivar tarjeta',
reportVirtualCardFraud: 'Reportar fraude con la tarjeta virtual',
},
reportFraudConfirmationPage: {
title: 'Fraude con tarjeta reportado',
description: 'Hemos desactivado permanentemente tu tarjeta existente. Cuando vuelvas a ver los detalles de tu tarjeta, tendrás una nueva tarjeta virtual disponible.',
buttonText: 'Entendido, ¡gracias!',
},
activateCardPage: {
activateCard: 'Activar tarjeta',
pleaseEnterLastFour: 'Introduce los cuatro últimos dígitos de la tarjeta.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require<ReactComponentModule>('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default,
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/ExpensifyCardPage').default,
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default,
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD_CONFIRMATION]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/ReportVirtualCardFraudConfirmationPage').default,
[SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/ActivatePhysicalCardPage').default,
[SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/Card/GetPhysicalCardName').default,
[SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.PHONE]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/Card/GetPhysicalCardPhone').default,
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.SETTINGS_REPORT_FRAUD.route,
exact: true,
},
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD_CONFIRMATION]: {
path: ROUTES.SETTINGS_REPORT_FRAUD_CONFIRMATION.route,
exact: true,
},
[SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME]: {
path: ROUTES.SETTINGS_WALLET_CARD_GET_PHYSICAL_NAME.route,
exact: true,
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ type SettingsNavigatorParamList = {
/** cardID of selected card */
cardID: string;
};
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD_CONFIRMATION]: {
/** cardID of selected card */
cardID: string;
};
[SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: {
/** cardID of selected card */
cardID: string;
Expand Down
6 changes: 6 additions & 0 deletions src/libs/actions/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function reportVirtualExpensifyCardFraud(card: Card, validateCode: string) {
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
cardID,
isLoading: true,
errors: null,
},
Expand Down Expand Up @@ -225,6 +226,10 @@ function clearCardListErrors(cardID: number) {
Onyx.merge(ONYXKEYS.CARD_LIST, {[cardID]: {errors: null, isLoading: false}});
}

function clearReportVirtualCardFraudForm() {
Onyx.merge(ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD, {cardID: null, isLoading: false, errors: null});
}

/**
* Makes an API call to get virtual card details (pan, cvv, expiration date, address)
* This function purposefully uses `makeRequestWithSideEffects` method. For security reason
Expand Down Expand Up @@ -896,6 +901,7 @@ export {
requestReplacementExpensifyCard,
activatePhysicalExpensifyCard,
clearCardListErrors,
clearReportVirtualCardFraudForm,
clearIssueNewCardError,
reportVirtualExpensifyCardFraud,
revealVirtualCardDetails,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/settings/Wallet/ExpensifyCardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ function ExpensifyCardPage({
<ScreenWrapper testID={ExpensifyCardPage.displayName}>
<HeaderWithBackButton
title={pageTitle}
onBackButtonPress={() => Navigation.goBack()}
onBackButtonPress={() => Navigation.navigate(ROUTES.SETTINGS_WALLET)}
/>
<ScrollView>
<View style={[styles.flex1, styles.mb9, styles.mt9]}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, {useCallback} from 'react';
import {View} from 'react-native';
import Button from '@components/Button';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import ImageSVG from '@components/ImageSVG';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@navigation/Navigation';
import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@navigation/types';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';

type ReportVirtualCardFraudConfirmationPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD_CONFIRMATION>;

function ReportVirtualCardFraudConfirmationPage({
route: {
params: {cardID = ''},
},
}: ReportVirtualCardFraudConfirmationPageProps) {
const themeStyles = useThemeStyles();
const {translate} = useLocalize();

const close = useCallback(() => {
Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID));
}, [cardID]);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom
includePaddingTop
shouldEnableMaxHeight
testID={ReportVirtualCardFraudConfirmationPage.displayName}
offlineIndicatorStyle={themeStyles.mtAuto}
>
<HeaderWithBackButton
title={translate('reportFraudConfirmationPage.title')}
onBackButtonPress={close}
/>

<View style={[themeStyles.ph5, themeStyles.mt3, themeStyles.mb5, themeStyles.flex1]}>
<View style={[themeStyles.justifyContentCenter, themeStyles.flex1]}>
<ImageSVG
contentFit="contain"
src={Expensicons.MagnifyingGlassSpyMouthClosed}
style={themeStyles.alignSelfCenter}
width={184}
height={290}
/>

<Text style={[themeStyles.textHeadlineH1, themeStyles.alignSelfCenter, themeStyles.mt5]}>{translate('reportFraudConfirmationPage.title')}</Text>
<Text style={[themeStyles.textSupporting, themeStyles.alignSelfCenter, themeStyles.mt2, themeStyles.textAlignCenter]}>
{translate('reportFraudConfirmationPage.description')}
</Text>
</View>

<Button
text={translate('reportFraudConfirmationPage.buttonText')}
onPress={close}
style={themeStyles.justifyContentEnd}
success
large
/>
</View>
</ScreenWrapper>
);
}

ReportVirtualCardFraudConfirmationPage.displayName = 'ReportVirtualCardFraudConfirmationPage';

export default ReportVirtualCardFraudConfirmationPage;
18 changes: 11 additions & 7 deletions src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import {clearCardListErrors, reportVirtualExpensifyCardFraud} from '@userActions/Card';
import {clearCardListErrors, clearReportVirtualCardFraudForm, reportVirtualExpensifyCardFraud} from '@userActions/Card';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
Expand All @@ -42,14 +42,17 @@ function ReportVirtualCardFraudPage({
const latestIssuedVirtualCardID = Object.keys(cardList ?? {})?.pop();
const virtualCardError = getLatestErrorMessage(virtualCard);
const validateError = getLatestErrorMessageField(virtualCard);
const prevVirtualCard = usePrevious(virtualCard);

const [isValidateCodeActionModalVisible, setIsValidateCodeActionModalVisible] = useState(false);

const prevIsLoading = usePrevious(formData?.isLoading);

useBeforeRemove(() => setIsValidateCodeActionModalVisible(false));

useEffect(() => {
clearReportVirtualCardFraudForm();
}, []);

useEffect(() => {
if (!prevIsLoading || formData?.isLoading) {
return;
Expand All @@ -59,17 +62,18 @@ function ReportVirtualCardFraudPage({
}

if (latestIssuedVirtualCardID) {
Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(latestIssuedVirtualCardID));
Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD_CONFIRMATION.getRoute(latestIssuedVirtualCardID));
setIsValidateCodeActionModalVisible(false);
}
}, [cardID, formData?.isLoading, prevIsLoading, virtualCard?.errors, latestIssuedVirtualCardID]);
}, [formData?.isLoading, latestIssuedVirtualCardID, prevIsLoading, virtualCard?.errors]);

const handleValidateCodeEntered = useCallback(
(validateCode: string) => {
if (!virtualCard) {
return;
}

reportVirtualExpensifyCardFraud(virtualCard, validateCode);
setIsValidateCodeActionModalVisible(false);
},
[virtualCard],
);
Expand All @@ -86,7 +90,7 @@ function ReportVirtualCardFraudPage({
setIsValidateCodeActionModalVisible(true);
}, [setIsValidateCodeActionModalVisible]);

if (isEmptyObject(virtualCard) && isEmptyObject(prevVirtualCard)) {
if (isEmptyObject(virtualCard) && !formData?.cardID) {
return <NotFoundPage />;
}

Expand All @@ -102,7 +106,6 @@ function ReportVirtualCardFraudPage({
isAlertVisible={!!virtualCardError}
onSubmit={handleSubmit}
message={virtualCardError}
isLoading={formData?.isLoading}
buttonText={translate('reportFraudPage.deactivateCard')}
containerStyles={[styles.m5]}
/>
Expand All @@ -121,6 +124,7 @@ function ReportVirtualCardFraudPage({
title={translate('cardPage.validateCardTitle')}
descriptionPrimary={translate('cardPage.enterMagicCode', {contactMethod: primaryLogin})}
hasMagicCodeBeenSent={!!loginData?.validateCodeSent}
isLoading={formData?.isLoading}
/>
</View>
</ScreenWrapper>
Expand Down

0 comments on commit 79e0ecf

Please sign in to comment.