From 6aa92816654f3873c65f33915daa12832f2df021 Mon Sep 17 00:00:00 2001 From: Hans Date: Sun, 20 Oct 2024 16:46:47 +0700 Subject: [PATCH 01/23] Add validatecode modal when issuing Physical card --- src/libs/actions/Wallet.ts | 3 +- .../Wallet/Card/BaseGetPhysicalCard.tsx | 59 +++++++++++++++---- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index b1f97421eea0..1a345be14100 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -257,7 +257,7 @@ function answerQuestionsForWallet(answers: WalletQuestionAnswer[], idNumber: str }); } -function requestPhysicalExpensifyCard(cardID: number, authToken: string, privatePersonalDetails: PrivatePersonalDetails) { +function requestPhysicalExpensifyCard(cardID: number, authToken: string, privatePersonalDetails: PrivatePersonalDetails, validateCode: string) { const {legalFirstName = '', legalLastName = '', phoneNumber = ''} = privatePersonalDetails; const {city = '', country = '', state = '', street = '', zip = ''} = PersonalDetailsUtils.getCurrentAddress(privatePersonalDetails) ?? {}; @@ -271,6 +271,7 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private addressState: state, addressStreet: street, addressZip: zip, + validateCode, }; const optimisticData: OnyxUpdate[] = [ diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index ae003c4afbe2..aed73018d582 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -1,13 +1,16 @@ -import React, {useCallback, useEffect, useRef} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import type {ReactNode} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; +import ValidateCodeActionModal from '@components/ValidateCodeActionModal'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as FormActions from '@libs/actions/FormActions'; +import * as User from '@libs/actions/User'; import * as Wallet from '@libs/actions/Wallet'; import * as CardUtils from '@libs/CardUtils'; import * as GetPhysicalCardUtils from '@libs/GetPhysicalCardUtils'; @@ -108,7 +111,9 @@ function BaseGetPhysicalCard({ }: BaseGetPhysicalCardProps) { const styles = useThemeStyles(); const isRouteSet = useRef(false); - + const {translate} = useLocalize(); + const [account] = useOnyx(ONYXKEYS.ACCOUNT); + const [isActionCodeModalVisible, setActionCodeModalVisible] = useState(false); const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; @@ -145,18 +150,37 @@ function BaseGetPhysicalCard({ }, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, cardToBeIssued, privatePersonalDetails]); const onSubmit = useCallback(() => { - const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); - // If the current step of the get physical card flow is the confirmation page - if (isConfirmation) { - Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails); - // Form draft data needs to be erased when the flow is complete, - // so that no stale data is left on Onyx - FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); + setActionCodeModalVisible(true); + }, []); + + const handleIssuePhysicalCard = useCallback( + (validateCode: string) => { + const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); + // If the current step of the get physical card flow is the confirmation page + if (isConfirmation) { + Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); + // Form draft data needs to be erased when the flow is complete, + // so that no stale data is left on Onyx + FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); + return; + } + GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, updatedPrivatePersonalDetails); + }, + [cardID, cardToBeIssued?.cardID, domain, draftValues, isConfirmation, session?.authToken, privatePersonalDetails], + ); + + const sendValidateCode = useCallback(() => { + const primaryLogin = account?.primaryLogin ?? ''; + const loginData = loginList?.[primaryLogin]; + + if (loginData?.validateCodeSent) { return; } - GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, updatedPrivatePersonalDetails); - }, [cardID, cardToBeIssued?.cardID, domain, draftValues, isConfirmation, session?.authToken, privatePersonalDetails]); + + User.requestValidateCodeAction(); + }, [account]); + return ( {headline} {renderContent({onSubmit, submitButtonText, children, onValidate})} + {}} + handleSubmitForm={handleIssuePhysicalCard} + title={translate('cardPage.validateCardTitle')} + onClose={() => setActionCodeModalVisible(false)} + description={translate('cardPage.enterMagicCode', {contactMethod: account?.primaryLogin ?? ''})} + /> ); } From b1e52cc0a62feac8924e809fb4055e604ad7b396 Mon Sep 17 00:00:00 2001 From: Hans Date: Sun, 20 Oct 2024 17:19:18 +0700 Subject: [PATCH 02/23] adjust logic --- .../RequestPhysicalExpensifyCardParams.ts | 1 + .../Wallet/Card/BaseGetPhysicalCard.tsx | 25 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libs/API/parameters/RequestPhysicalExpensifyCardParams.ts b/src/libs/API/parameters/RequestPhysicalExpensifyCardParams.ts index 91995b6e37aa..94e45a29b728 100644 --- a/src/libs/API/parameters/RequestPhysicalExpensifyCardParams.ts +++ b/src/libs/API/parameters/RequestPhysicalExpensifyCardParams.ts @@ -8,6 +8,7 @@ type RequestPhysicalExpensifyCardParams = { addressState: string; addressStreet: string; addressZip: string; + validateCode: string; }; export default RequestPhysicalExpensifyCardParams; diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index aed73018d582..ac579a0942c4 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -150,22 +150,23 @@ function BaseGetPhysicalCard({ }, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, cardToBeIssued, privatePersonalDetails]); const onSubmit = useCallback(() => { - setActionCodeModalVisible(true); - }, []); + const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); + if (isConfirmation) { + setActionCodeModalVisible(true); + return; + } + GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, updatedPrivatePersonalDetails); + }, [isConfirmation, domain]); const handleIssuePhysicalCard = useCallback( (validateCode: string) => { const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); // If the current step of the get physical card flow is the confirmation page - if (isConfirmation) { - Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); - // Form draft data needs to be erased when the flow is complete, - // so that no stale data is left on Onyx - FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); - return; - } - GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, updatedPrivatePersonalDetails); + Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); + // Form draft data needs to be erased when the flow is complete, + // so that no stale data is left on Onyx + FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); }, [cardID, cardToBeIssued?.cardID, domain, draftValues, isConfirmation, session?.authToken, privatePersonalDetails], ); @@ -179,7 +180,7 @@ function BaseGetPhysicalCard({ } User.requestValidateCodeAction(); - }, [account]); + }, [account, loginList]); return ( Date: Sun, 20 Oct 2024 17:29:38 +0700 Subject: [PATCH 03/23] address linting --- ios/.ruby-version | 1 + src/libs/Permissions.ts | 2 +- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 ios/.ruby-version diff --git a/ios/.ruby-version b/ios/.ruby-version new file mode 100644 index 000000000000..fa7adc7ac72a --- /dev/null +++ b/ios/.ruby-version @@ -0,0 +1 @@ +3.3.5 diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 24de2e612208..1d6fd98d8ccc 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -4,7 +4,7 @@ import type {IOUType} from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.ALL); + return true//!!betas?.includes(CONST.BETAS.ALL); } function canUseDefaultRooms(betas: OnyxEntry): boolean { diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index ac579a0942c4..9638fc6aba60 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -156,7 +156,7 @@ function BaseGetPhysicalCard({ return; } GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, updatedPrivatePersonalDetails); - }, [isConfirmation, domain]); + }, [isConfirmation, domain, draftValues, privatePersonalDetails]); const handleIssuePhysicalCard = useCallback( (validateCode: string) => { @@ -168,7 +168,7 @@ function BaseGetPhysicalCard({ FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); }, - [cardID, cardToBeIssued?.cardID, domain, draftValues, isConfirmation, session?.authToken, privatePersonalDetails], + [cardID, cardToBeIssued?.cardID, draftValues, session?.authToken, privatePersonalDetails], ); const sendValidateCode = useCallback(() => { From 91bc9b1e06176f0135291fd45dd8442c3a50c9cc Mon Sep 17 00:00:00 2001 From: Hans Date: Sun, 20 Oct 2024 17:30:33 +0700 Subject: [PATCH 04/23] revert .ruby-version --- ios/.ruby-version | 1 - src/libs/Permissions.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 ios/.ruby-version diff --git a/ios/.ruby-version b/ios/.ruby-version deleted file mode 100644 index fa7adc7ac72a..000000000000 --- a/ios/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -3.3.5 diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 1d6fd98d8ccc..24de2e612208 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -4,7 +4,7 @@ import type {IOUType} from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { - return true//!!betas?.includes(CONST.BETAS.ALL); + return !!betas?.includes(CONST.BETAS.ALL); } function canUseDefaultRooms(betas: OnyxEntry): boolean { From 98904943d30d963af7c62aacb8deb25c7e6eb349 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 23 Oct 2024 14:07:42 +0700 Subject: [PATCH 05/23] Onyx migration --- .../Wallet/Card/BaseGetPhysicalCard.tsx | 50 +++---------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 9638fc6aba60..f318416e5642 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -1,6 +1,6 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import type {ReactNode} from 'react'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -19,7 +19,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {GetPhysicalCardForm} from '@src/types/form'; -import type {CardList, LoginList, PrivatePersonalDetails, Session} from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; @@ -31,24 +30,7 @@ type RenderContentProps = ChildrenProps & { onValidate: OnValidate; }; -type BaseGetPhysicalCardOnyxProps = { - /** List of available assigned cards */ - cardList: OnyxEntry; - - /** User's private personal details */ - privatePersonalDetails: OnyxEntry; - - /** Draft values used by the get physical card form */ - draftValues: OnyxEntry; - - /** Session info for the currently logged in user. */ - session: OnyxEntry; - - /** List of available login methods */ - loginList: OnyxEntry; -}; - -type BaseGetPhysicalCardProps = BaseGetPhysicalCardOnyxProps & { +type BaseGetPhysicalCardProps = { /** Text displayed below page title */ headline: string; @@ -94,17 +76,12 @@ function DefaultRenderContent({onSubmit, submitButtonText, children, onValidate} } function BaseGetPhysicalCard({ - cardList, children, currentRoute, domain, - draftValues, - privatePersonalDetails, headline, isConfirmation = false, - loginList, renderContent = DefaultRenderContent, - session, submitButtonText, title, onValidate = () => ({}), @@ -112,6 +89,11 @@ function BaseGetPhysicalCard({ const styles = useThemeStyles(); const isRouteSet = useRef(false); const {translate} = useLocalize(); + const [cardList] = useOnyx(ONYXKEYS.CARD_LIST); + const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); + const [draftValues] = useOnyx(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [isActionCodeModalVisible, setActionCodeModalVisible] = useState(false); const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; @@ -209,22 +191,6 @@ function BaseGetPhysicalCard({ BaseGetPhysicalCard.displayName = 'BaseGetPhysicalCard'; -export default withOnyx({ - cardList: { - key: ONYXKEYS.CARD_LIST, - }, - loginList: { - key: ONYXKEYS.LOGIN_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - privatePersonalDetails: { - key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, - }, - draftValues: { - key: ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT, - }, -})(BaseGetPhysicalCard); +export default BaseGetPhysicalCard; export type {RenderContentProps}; From 55187f468b175a13fea360057d204ce20f4324ed Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 30 Oct 2024 15:21:46 +0700 Subject: [PATCH 06/23] handle generic error --- src/libs/actions/Wallet.ts | 35 ++++++++++++++++++- .../Wallet/Card/BaseGetPhysicalCard.tsx | 17 ++++++--- src/types/onyx/Card.ts | 3 ++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index 1a345be14100..c395c4191cb2 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -10,6 +10,7 @@ import type { VerifyIdentityParams, } from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import * as ErrorUtils from '@libs/ErrorUtils'; import type {PrivatePersonalDetails} from '@libs/GetPhysicalCardUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import type CONST from '@src/CONST'; @@ -281,6 +282,9 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private value: { [cardID]: { state: 4, // NOT_ACTIVATED + isLoading: true, + errors: null, + isSuccessfull: null, }, }, }, @@ -291,7 +295,36 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private }, ]; - API.write(WRITE_COMMANDS.REQUEST_PHYSICAL_EXPENSIFY_CARD, requestParams, {optimisticData}); + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CARD_LIST, + value: { + [cardID]: { + state: 4, // NOT_ACTIVATED + isLoading: false, + errors: null, + isSuccessfull: true, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CARD_LIST, + value: { + [cardID]: { + state: 4, // NOT_ACTIVATED + isLoading: false, + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + ]; + + API.write(WRITE_COMMANDS.REQUEST_PHYSICAL_EXPENSIFY_CARD, requestParams, {optimisticData, failureData, successData}); } function resetWalletAdditionalDetailsDraft() { diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index f318416e5642..4549e33795cc 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -99,6 +99,7 @@ function BaseGetPhysicalCard({ const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; + const isSuccessful = cardToBeIssued?.isSuccessfull; useEffect(() => { if (isRouteSet.current || !privatePersonalDetails || !cardList) { @@ -131,6 +132,16 @@ function BaseGetPhysicalCard({ isRouteSet.current = true; }, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, cardToBeIssued, privatePersonalDetails]); + useEffect(() => { + if (!isSuccessful) { + return; + } + // Form draft data needs to be erased when the flow is complete, + // so that no stale data is left on Onyx + FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); + }, [isSuccessful, cardID]); + const onSubmit = useCallback(() => { const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); if (isConfirmation) { @@ -145,12 +156,8 @@ function BaseGetPhysicalCard({ const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); // If the current step of the get physical card flow is the confirmation page Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); - // Form draft data needs to be erased when the flow is complete, - // so that no stale data is left on Onyx - FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); }, - [cardID, cardToBeIssued?.cardID, draftValues, session?.authToken, privatePersonalDetails], + [cardToBeIssued?.cardID, draftValues, session?.authToken, privatePersonalDetails], ); const sendValidateCode = useCallback(() => { diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index c1d0e09d9312..dfe0695a0ece 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -115,6 +115,9 @@ type Card = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Collection of form field errors */ errorFields?: OnyxCommon.ErrorFields; }>; + + /** Card status */ + isSuccessfull?: boolean; }>; /** Model of Expensify card details */ From 340d3b177a3d378d7c250dc0e105bba6beb5f031 Mon Sep 17 00:00:00 2001 From: Hans Date: Fri, 1 Nov 2024 10:08:45 +0700 Subject: [PATCH 07/23] update the error field --- src/libs/actions/Wallet.ts | 17 +++++++++++++-- .../Wallet/Card/BaseGetPhysicalCard.tsx | 21 +++++++------------ src/types/onyx/Card.ts | 2 +- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index c395c4191cb2..6849223974c9 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -284,7 +284,7 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private state: 4, // NOT_ACTIVATED isLoading: true, errors: null, - isSuccessfull: null, + isSuccessful: null, }, }, }, @@ -304,7 +304,7 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private state: 4, // NOT_ACTIVATED isLoading: false, errors: null, - isSuccessfull: true, + isSuccessful: true, }, }, }, @@ -331,6 +331,18 @@ function resetWalletAdditionalDetailsDraft() { Onyx.set(ONYXKEYS.FORMS.WALLET_ADDITIONAL_DETAILS_DRAFT, null); } +/** + * Clear the error of specific card + * @param cardId The card id of the card that you want to clear the errors. + */ +function clearPhysicalCardError(cardId: string) { + Onyx.merge(ONYXKEYS.CARD_LIST, { + [cardId]: { + errors: null, + }, + }); +} + export { openOnfidoFlow, openInitialSettingsPage, @@ -345,4 +357,5 @@ export { setKYCWallSource, requestPhysicalExpensifyCard, resetWalletAdditionalDetailsDraft, + clearPhysicalCardError, }; diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 4549e33795cc..57eb35b295ec 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -13,6 +13,7 @@ import * as FormActions from '@libs/actions/FormActions'; import * as User from '@libs/actions/User'; import * as Wallet from '@libs/actions/Wallet'; import * as CardUtils from '@libs/CardUtils'; +import * as ErrorUtils from '@libs/ErrorUtils'; import * as GetPhysicalCardUtils from '@libs/GetPhysicalCardUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; @@ -99,7 +100,8 @@ function BaseGetPhysicalCard({ const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; - const isSuccessful = cardToBeIssued?.isSuccessfull; + const isSuccessful = cardToBeIssued?.isSuccessful; + const errorMessage = ErrorUtils.getLatestErrorMessageField(cardToBeIssued); useEffect(() => { if (isRouteSet.current || !privatePersonalDetails || !cardList) { @@ -139,6 +141,7 @@ function BaseGetPhysicalCard({ // Form draft data needs to be erased when the flow is complete, // so that no stale data is left on Onyx FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); + Wallet.clearPhysicalCardError(cardID); Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); }, [isSuccessful, cardID]); @@ -160,17 +163,6 @@ function BaseGetPhysicalCard({ [cardToBeIssued?.cardID, draftValues, session?.authToken, privatePersonalDetails], ); - const sendValidateCode = useCallback(() => { - const primaryLogin = account?.primaryLogin ?? ''; - const loginData = loginList?.[primaryLogin]; - - if (loginData?.validateCodeSent) { - return; - } - - User.requestValidateCodeAction(); - }, [account, loginList]); - return ( {}} + sendValidateCode={() => User.requestValidateCodeAction()} + clearError={() => Wallet.clearPhysicalCardError(cardID)} + validateError={errorMessage} handleSubmitForm={handleIssuePhysicalCard} title={translate('cardPage.validateCardTitle')} onClose={() => setActionCodeModalVisible(false)} diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index dfe0695a0ece..a7debf5df925 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -117,7 +117,7 @@ type Card = OnyxCommon.OnyxValueWithOfflineFeedback<{ }>; /** Card status */ - isSuccessfull?: boolean; + isSuccessful?: boolean; }>; /** Model of Expensify card details */ From 23c5163536340c45f71f651cef1246d081d64862 Mon Sep 17 00:00:00 2001 From: Hans Date: Fri, 1 Nov 2024 13:50:23 +0700 Subject: [PATCH 08/23] add loading field and other stuffs --- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 6 ++++- .../ValidateCodeActionModal/index.tsx | 2 ++ .../ValidateCodeActionModal/type.ts | 3 +++ src/libs/actions/Wallet.ts | 27 ++++++++++++++++++- .../Wallet/Card/BaseGetPhysicalCard.tsx | 5 +++- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx index cc2a7314f570..2c318996c235 100644 --- a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -65,6 +65,9 @@ type ValidateCodeFormProps = { /** Function is called when validate code modal is mounted and on magic code resend */ sendValidateCode: () => void; + + /** Wheather the form is loading or not */ + isLoading?: boolean; }; function BaseValidateCodeForm({ @@ -78,6 +81,7 @@ function BaseValidateCodeForm({ clearError, sendValidateCode, buttonStyles, + isLoading, }: ValidateCodeFormProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -266,7 +270,7 @@ function BaseValidateCodeForm({ style={[styles.mt4]} success large - isLoading={account?.isLoading} + isLoading={account?.isLoading || isLoading} /> diff --git a/src/components/ValidateCodeActionModal/index.tsx b/src/components/ValidateCodeActionModal/index.tsx index 8c09d8caad62..21298aa06787 100644 --- a/src/components/ValidateCodeActionModal/index.tsx +++ b/src/components/ValidateCodeActionModal/index.tsx @@ -25,6 +25,7 @@ function ValidateCodeActionModal({ footer, sendValidateCode, hasMagicCodeBeenSent, + isLoading, }: ValidateCodeActionModalProps) { const themeStyles = useThemeStyles(); const firstRenderRef = useRef(true); @@ -70,6 +71,7 @@ function ValidateCodeActionModal({ {description} ) => Errors; @@ -97,6 +98,7 @@ function BaseGetPhysicalCard({ const [draftValues] = useOnyx(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [isActionCodeModalVisible, setActionCodeModalVisible] = useState(false); + const [formData] = useOnyx(ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM); const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; @@ -176,10 +178,11 @@ function BaseGetPhysicalCard({ {headline} {renderContent({onSubmit, submitButtonText, children, onValidate})} User.requestValidateCodeAction()} clearError={() => Wallet.clearPhysicalCardError(cardID)} - validateError={errorMessage} + validateError={!isEmptyObject(formData?.errors) ? formData?.errors : errorMessage} handleSubmitForm={handleIssuePhysicalCard} title={translate('cardPage.validateCardTitle')} onClose={() => setActionCodeModalVisible(false)} From 6ce22aa89dc5cd06e86b7954f866a8b5310a359d Mon Sep 17 00:00:00 2001 From: Hans Date: Fri, 1 Nov 2024 14:22:41 +0700 Subject: [PATCH 09/23] remove cardState optimistic --- src/libs/actions/Wallet.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index cfb4b6082ff8..cf4cc2e0be59 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -282,7 +282,6 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private key: ONYXKEYS.CARD_LIST, value: { [cardID]: { - state: 4, // NOT_ACTIVATED isLoading: true, errors: null, isSuccessful: null, From 2224d58b60a34e2314c9d71f0df5623d83df9e08 Mon Sep 17 00:00:00 2001 From: Hans Date: Fri, 1 Nov 2024 14:36:49 +0700 Subject: [PATCH 10/23] prettier --- src/libs/actions/Wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index cf4cc2e0be59..da88957b1cbd 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -14,10 +14,10 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import type {PrivatePersonalDetails} from '@libs/GetPhysicalCardUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import type CONST from '@src/CONST'; -import * as FormActions from './FormActions'; import ONYXKEYS from '@src/ONYXKEYS'; import type {WalletAdditionalQuestionDetails} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import * as FormActions from './FormActions'; type WalletQuestionAnswer = { question: string; From 60d0fdc000357704fc2896c06409bbc7b6e6063d Mon Sep 17 00:00:00 2001 From: Hans Date: Fri, 1 Nov 2024 14:41:05 +0700 Subject: [PATCH 11/23] fix lint --- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx index 2c318996c235..3e4182399318 100644 --- a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -270,6 +270,7 @@ function BaseValidateCodeForm({ style={[styles.mt4]} success large + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isLoading={account?.isLoading || isLoading} /> From cf938e08e0cfa47456147b78e957bd16d755336d Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 6 Nov 2024 15:21:25 +0700 Subject: [PATCH 12/23] update successful condition --- src/libs/actions/Wallet.ts | 4 ---- .../settings/Wallet/Card/BaseGetPhysicalCard.tsx | 12 +++++++----- src/types/onyx/Card.ts | 3 --- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index da88957b1cbd..d5b3862b62b5 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -282,9 +282,7 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private key: ONYXKEYS.CARD_LIST, value: { [cardID]: { - isLoading: true, errors: null, - isSuccessful: null, }, }, }, @@ -310,9 +308,7 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private value: { [cardID]: { state: 4, // NOT_ACTIVATED - isLoading: false, errors: null, - isSuccessful: true, }, }, }, diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 26b70a6e529f..2b92058caedc 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -102,7 +102,7 @@ function BaseGetPhysicalCard({ const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; - const isSuccessful = cardToBeIssued?.isSuccessful; + const [currentCardId, setCurrentCardId] = useState(cardID); const errorMessage = ErrorUtils.getLatestErrorMessageField(cardToBeIssued); useEffect(() => { @@ -137,15 +137,16 @@ function BaseGetPhysicalCard({ }, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, cardToBeIssued, privatePersonalDetails]); useEffect(() => { - if (!isSuccessful) { + if (!isConfirmation || !!cardToBeIssued || !currentCardId) { return; } // Form draft data needs to be erased when the flow is complete, // so that no stale data is left on Onyx FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); - Wallet.clearPhysicalCardError(cardID); - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID.toString())); - }, [isSuccessful, cardID]); + Wallet.clearPhysicalCardError(currentCardId); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(currentCardId.toString())); + setCurrentCardId(undefined); + }, [currentCardId, isConfirmation, cardToBeIssued]); const onSubmit = useCallback(() => { const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); @@ -158,6 +159,7 @@ function BaseGetPhysicalCard({ const handleIssuePhysicalCard = useCallback( (validateCode: string) => { + setCurrentCardId(cardToBeIssued?.cardID.toString()); const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); // If the current step of the get physical card flow is the confirmation page Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index a7debf5df925..c1d0e09d9312 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -115,9 +115,6 @@ type Card = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Collection of form field errors */ errorFields?: OnyxCommon.ErrorFields; }>; - - /** Card status */ - isSuccessful?: boolean; }>; /** Model of Expensify card details */ From 33c65fc54563e395ac0f67a6ef3b994626f80cfb Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 11 Nov 2024 18:21:58 +0700 Subject: [PATCH 13/23] addressing comment --- .../settings/Wallet/Card/BaseGetPhysicalCard.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 2b92058caedc..ee1875b14276 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -102,7 +102,7 @@ function BaseGetPhysicalCard({ const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; - const [currentCardId, setCurrentCardId] = useState(cardID); + const [currentCardID, setCurrentCardID] = useState(cardID); const errorMessage = ErrorUtils.getLatestErrorMessageField(cardToBeIssued); useEffect(() => { @@ -137,16 +137,18 @@ function BaseGetPhysicalCard({ }, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, cardToBeIssued, privatePersonalDetails]); useEffect(() => { - if (!isConfirmation || !!cardToBeIssued || !currentCardId) { + // If that's not the confirmation route, or if there's a value for cardToBeIssued, + // It means the current card is not issued and we still need to stay on this screen. + if (!isConfirmation || !!cardToBeIssued || !currentCardID) { return; } // Form draft data needs to be erased when the flow is complete, // so that no stale data is left on Onyx FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); - Wallet.clearPhysicalCardError(currentCardId); - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(currentCardId.toString())); - setCurrentCardId(undefined); - }, [currentCardId, isConfirmation, cardToBeIssued]); + Wallet.clearPhysicalCardError(currentCardID); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(currentCardID.toString())); + setCurrentCardID(undefined); + }, [currentCardID, isConfirmation, cardToBeIssued]); const onSubmit = useCallback(() => { const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); @@ -159,7 +161,7 @@ function BaseGetPhysicalCard({ const handleIssuePhysicalCard = useCallback( (validateCode: string) => { - setCurrentCardId(cardToBeIssued?.cardID.toString()); + setCurrentCardID(cardToBeIssued?.cardID.toString()); const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); // If the current step of the get physical card flow is the confirmation page Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); From 14652ea57b6477b87e02ffae72fd0e90c084a399 Mon Sep 17 00:00:00 2001 From: Hans Date: Tue, 12 Nov 2024 09:45:56 +0700 Subject: [PATCH 14/23] add code request status --- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index ee1875b14276..fa1593cf41fb 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -95,6 +95,7 @@ function BaseGetPhysicalCard({ const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [session] = useOnyx(ONYXKEYS.SESSION); const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); + const [validateCodeAction] = useOnyx(ONYXKEYS.VALIDATE_ACTION_CODE); const [draftValues] = useOnyx(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [isActionCodeModalVisible, setActionCodeModalVisible] = useState(false); @@ -183,6 +184,7 @@ function BaseGetPhysicalCard({ {renderContent({onSubmit, submitButtonText, children, onValidate})} User.requestValidateCodeAction()} clearError={() => Wallet.clearPhysicalCardError(cardID)} From 982abf03c4f30b43ba29bfde446ce85f26f9a8de Mon Sep 17 00:00:00 2001 From: Hans Date: Tue, 12 Nov 2024 10:11:50 +0700 Subject: [PATCH 15/23] Update src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx Co-authored-by: Dominic <165644294+dominictb@users.noreply.github.com> --- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index fa1593cf41fb..16b0afcb0c6d 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -164,7 +164,6 @@ function BaseGetPhysicalCard({ (validateCode: string) => { setCurrentCardID(cardToBeIssued?.cardID.toString()); const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues, privatePersonalDetails); - // If the current step of the get physical card flow is the confirmation page Wallet.requestPhysicalExpensifyCard(cardToBeIssued?.cardID ?? -1, session?.authToken ?? '', updatedPrivatePersonalDetails, validateCode); }, [cardToBeIssued?.cardID, draftValues, session?.authToken, privatePersonalDetails], From 55c4a0c7ab8e7b92e3d8c70ecb99b6fdf87ac115 Mon Sep 17 00:00:00 2001 From: Hans Date: Tue, 12 Nov 2024 10:12:01 +0700 Subject: [PATCH 16/23] Update src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx Co-authored-by: Dominic <165644294+dominictb@users.noreply.github.com> --- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 16b0afcb0c6d..bce0a8a928b6 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -138,8 +138,8 @@ function BaseGetPhysicalCard({ }, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, cardToBeIssued, privatePersonalDetails]); useEffect(() => { - // If that's not the confirmation route, or if there's a value for cardToBeIssued, - // It means the current card is not issued and we still need to stay on this screen. + // Current step of the get physical card flow should be the confirmation page; and + // Card has NOT_ACTIVATED state when successfully being issued so cardToBeIssued should be undefined if (!isConfirmation || !!cardToBeIssued || !currentCardID) { return; } From cbf6a2b940b256319fbdb5e84a5fb004af3f18a4 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 13 Nov 2024 11:12:53 +0700 Subject: [PATCH 17/23] address comment --- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 2 +- src/components/ValidateCodeActionModal/type.ts | 2 +- src/libs/actions/Wallet.ts | 6 +++--- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx index 5c918128e662..5418f7e5b3c1 100644 --- a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -66,7 +66,7 @@ type ValidateCodeFormProps = { /** Function is called when validate code modal is mounted and on magic code resend */ sendValidateCode: () => void; - /** Wheather the form is loading or not */ + /** Whether the form is loading or not */ isLoading?: boolean; }; diff --git a/src/components/ValidateCodeActionModal/type.ts b/src/components/ValidateCodeActionModal/type.ts index 1fda266ec47d..fbee1a3e0bf6 100644 --- a/src/components/ValidateCodeActionModal/type.ts +++ b/src/components/ValidateCodeActionModal/type.ts @@ -38,7 +38,7 @@ type ValidateCodeActionModalProps = { /** If the magic code has been resent previously */ hasMagicCodeBeenSent?: boolean; - /** Wheather the form is loading or not */ + /** Whether the form is loading or not */ isLoading?: boolean; }; diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index d5b3862b62b5..2ea60d1a453f 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -352,12 +352,12 @@ function resetWalletAdditionalDetailsDraft() { /** * Clear the error of specific card - * @param cardId The card id of the card that you want to clear the errors. + * @param cardID The card id of the card that you want to clear the errors. */ -function clearPhysicalCardError(cardId: string) { +function clearPhysicalCardError(cardID: string) { FormActions.clearErrors(ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM); Onyx.merge(ONYXKEYS.CARD_LIST, { - [cardId]: { + [cardID]: { errors: null, }, }); diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index bce0a8a928b6..6c347c1d9293 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -143,6 +143,7 @@ function BaseGetPhysicalCard({ if (!isConfirmation || !!cardToBeIssued || !currentCardID) { return; } + // Form draft data needs to be erased when the flow is complete, // so that no stale data is left on Onyx FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); From 0855d78a912e970388a48fa234f55d7172a8164e Mon Sep 17 00:00:00 2001 From: Hans Date: Thu, 14 Nov 2024 17:43:24 +0700 Subject: [PATCH 18/23] remove dupe props --- src/components/ValidateCodeActionModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ValidateCodeActionModal/index.tsx b/src/components/ValidateCodeActionModal/index.tsx index 6e90f3460925..81e4f3092125 100644 --- a/src/components/ValidateCodeActionModal/index.tsx +++ b/src/components/ValidateCodeActionModal/index.tsx @@ -85,7 +85,6 @@ function ValidateCodeActionModal({ buttonStyles={[themeStyles.justifyContentEnd, themeStyles.flex1, safePaddingBottomStyle]} ref={validateCodeFormRef} hasMagicCodeBeenSent={hasMagicCodeBeenSent} - isLoading={isLoading} /> {footer?.()} From 5c75531b6cf352f2b15209d3030e0b9b515001e8 Mon Sep 17 00:00:00 2001 From: Hans Date: Thu, 14 Nov 2024 17:51:13 +0700 Subject: [PATCH 19/23] deprecate description props --- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 6c347c1d9293..8fe75c7c7c3c 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -192,7 +192,7 @@ function BaseGetPhysicalCard({ handleSubmitForm={handleIssuePhysicalCard} title={translate('cardPage.validateCardTitle')} onClose={() => setActionCodeModalVisible(false)} - description={translate('cardPage.enterMagicCode', {contactMethod: account?.primaryLogin ?? ''})} + descriptionPrimary={translate('cardPage.enterMagicCode', {contactMethod: account?.primaryLogin ?? ''})} /> ); From fb7ee30ad820d3903071e27ae561a73ff798da9d Mon Sep 17 00:00:00 2001 From: Hans Date: Thu, 14 Nov 2024 18:12:15 +0700 Subject: [PATCH 20/23] address comment --- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 8fe75c7c7c3c..a8aafa38503f 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -140,7 +140,8 @@ function BaseGetPhysicalCard({ useEffect(() => { // Current step of the get physical card flow should be the confirmation page; and // Card has NOT_ACTIVATED state when successfully being issued so cardToBeIssued should be undefined - if (!isConfirmation || !!cardToBeIssued || !currentCardID) { + // -1 is not a valid cardID, we don't need to clean up the form value in that case. + if (!isConfirmation || !!cardToBeIssued || !currentCardID || currentCardID === '-1') { return; } From ceca20c00a7a8794d6dfcfc6b5f80f891765117d Mon Sep 17 00:00:00 2001 From: Hans Date: Fri, 15 Nov 2024 16:01:35 +0700 Subject: [PATCH 21/23] fix -1 value appears inside cardList --- src/libs/actions/Wallet.ts | 20 ++++++++++++++++++- .../Wallet/Card/BaseGetPhysicalCard.tsx | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index 2ea60d1a453f..ea7e86ef49b7 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -299,6 +299,13 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private errors: null, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.VALIDATE_ACTION_CODE, + value: { + validateCodeSent: false, + }, + }, ]; const successData: OnyxUpdate[] = [ @@ -320,6 +327,13 @@ function requestPhysicalExpensifyCard(cardID: number, authToken: string, private errors: null, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.VALIDATE_ACTION_CODE, + value: { + validateCodeSent: false, + }, + }, ]; const failureData: OnyxUpdate[] = [ @@ -354,7 +368,11 @@ function resetWalletAdditionalDetailsDraft() { * Clear the error of specific card * @param cardID The card id of the card that you want to clear the errors. */ -function clearPhysicalCardError(cardID: string) { +function clearPhysicalCardError(cardID?: string) { + if (!cardID) { + return; + } + FormActions.clearErrors(ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM); Onyx.merge(ONYXKEYS.CARD_LIST, { [cardID]: { diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index a8aafa38503f..f7c8ff60be36 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -188,7 +188,7 @@ function BaseGetPhysicalCard({ hasMagicCodeBeenSent={validateCodeAction?.validateCodeSent} isVisible={isActionCodeModalVisible} sendValidateCode={() => User.requestValidateCodeAction()} - clearError={() => Wallet.clearPhysicalCardError(cardID)} + clearError={() => Wallet.clearPhysicalCardError(currentCardID)} validateError={!isEmptyObject(formData?.errors) ? formData?.errors : errorMessage} handleSubmitForm={handleIssuePhysicalCard} title={translate('cardPage.validateCardTitle')} From 7b876d3ed171a7bfc25d5739f4f59ad13aa7ed18 Mon Sep 17 00:00:00 2001 From: Hans Date: Tue, 19 Nov 2024 10:19:57 +0700 Subject: [PATCH 22/23] address comment --- .../settings/Wallet/Card/BaseGetPhysicalCard.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index f7c8ff60be36..269f0b1c52d7 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -102,8 +102,7 @@ function BaseGetPhysicalCard({ const [formData] = useOnyx(ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM); const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); - const cardID = cardToBeIssued?.cardID.toString() ?? '-1'; - const [currentCardID, setCurrentCardID] = useState(cardID); + const [currentCardID, setCurrentCardID] = useState(cardToBeIssued?.cardID.toString() ?? '-1'); const errorMessage = ErrorUtils.getLatestErrorMessageField(cardToBeIssued); useEffect(() => { @@ -140,8 +139,7 @@ function BaseGetPhysicalCard({ useEffect(() => { // Current step of the get physical card flow should be the confirmation page; and // Card has NOT_ACTIVATED state when successfully being issued so cardToBeIssued should be undefined - // -1 is not a valid cardID, we don't need to clean up the form value in that case. - if (!isConfirmation || !!cardToBeIssued || !currentCardID || currentCardID === '-1') { + if (!isConfirmation || !!cardToBeIssued || !currentCardID) { return; } @@ -149,7 +147,7 @@ function BaseGetPhysicalCard({ // so that no stale data is left on Onyx FormActions.clearDraftValues(ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM); Wallet.clearPhysicalCardError(currentCardID); - Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(currentCardID.toString())); + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(currentCardID)); setCurrentCardID(undefined); }, [currentCardID, isConfirmation, cardToBeIssued]); @@ -179,7 +177,12 @@ function BaseGetPhysicalCard({ > Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID))} + onBackButtonPress={() => { + if (currentCardID) { + Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(currentCardID)); + } + Navigation.goBack(); + }} /> {headline} {renderContent({onSubmit, submitButtonText, children, onValidate})} From 4d963845af22163a841c6fc271154bc11491f77b Mon Sep 17 00:00:00 2001 From: Hans Date: Tue, 19 Nov 2024 10:20:59 +0700 Subject: [PATCH 23/23] remove -1 --- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx index 269f0b1c52d7..eef5024180e7 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx @@ -102,7 +102,7 @@ function BaseGetPhysicalCard({ const [formData] = useOnyx(ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM); const domainCards = CardUtils.getDomainCards(cardList)[domain] || []; const cardToBeIssued = domainCards.find((card) => !card?.nameValuePairs?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED); - const [currentCardID, setCurrentCardID] = useState(cardToBeIssued?.cardID.toString() ?? '-1'); + const [currentCardID, setCurrentCardID] = useState(cardToBeIssued?.cardID.toString()); const errorMessage = ErrorUtils.getLatestErrorMessageField(cardToBeIssued); useEffect(() => {