From a7fbd9777e99eed5b7c32262fa72dde8f9964c7c Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 18 Jan 2024 16:47:56 +0700 Subject: [PATCH 01/26] TS migration SettingsWallet --- src/components/BigNumberPad.tsx | 2 +- ...tPage.js => ChooseTransferAccountPage.tsx} | 63 ++++++++---------- ...otCardSection.js => RedDotCardSection.tsx} | 12 ++-- ...Page.js => ReportVirtualCardFraudPage.tsx} | 65 +++++++++---------- .../Wallet/walletTransferPropTypes.js | 16 ----- src/types/onyx/Fund.ts | 2 +- 6 files changed, 63 insertions(+), 97 deletions(-) rename src/pages/settings/Wallet/{ChooseTransferAccountPage.js => ChooseTransferAccountPage.tsx} (51%) rename src/pages/settings/Wallet/{RedDotCardSection.js => RedDotCardSection.tsx} (76%) rename src/pages/settings/Wallet/{ReportVirtualCardFraudPage.js => ReportVirtualCardFraudPage.tsx} (60%) delete mode 100644 src/pages/settings/Wallet/walletTransferPropTypes.js diff --git a/src/components/BigNumberPad.tsx b/src/components/BigNumberPad.tsx index 8b840f9d1b57..adefb78a66c7 100644 --- a/src/components/BigNumberPad.tsx +++ b/src/components/BigNumberPad.tsx @@ -17,7 +17,7 @@ type BigNumberPadProps = { id?: string; /** Whether long press is disabled */ - isLongPressDisabled: boolean; + isLongPressDisabled?: boolean; }; const padNumbers = [ diff --git a/src/pages/settings/Wallet/ChooseTransferAccountPage.js b/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx similarity index 51% rename from src/pages/settings/Wallet/ChooseTransferAccountPage.js rename to src/pages/settings/Wallet/ChooseTransferAccountPage.tsx index 815d2e883cf0..d938ca78117c 100644 --- a/src/pages/settings/Wallet/ChooseTransferAccountPage.js +++ b/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx @@ -1,43 +1,39 @@ import React from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {GestureResponderEvent, View} from 'react-native'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as BankAccounts from '@userActions/BankAccounts'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {AccountData as BankAccountData, WalletTransfer} from '@src/types/onyx'; +import type {AccountData as FundAccountData} from '@src/types/onyx/Fund'; import PaymentMethodList from './PaymentMethodList'; -import walletTransferPropTypes from './walletTransferPropTypes'; -const propTypes = { - /** Wallet transfer propTypes */ - walletTransfer: walletTransferPropTypes, - - ...withLocalizePropTypes, +type ChooseTransferAccountPageOnyxProps = { + walletTransfer: OnyxEntry; }; -const defaultProps = { - walletTransfer: {}, -}; +type ChooseTransferAccountPageProps = ChooseTransferAccountPageOnyxProps & {}; -function ChooseTransferAccountPage(props) { +function ChooseTransferAccountPage({walletTransfer = {}}: ChooseTransferAccountPageProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); /** * Go back to transfer balance screen with the selected bank account set - * @param {Object} event Click event object - * @param {String} accountType of the selected account type - * @param {Object} account of the selected account data */ - const selectAccountAndNavigateBack = (event, accountType, account) => { - PaymentMethods.saveWalletTransferAccountTypeAndID(accountType, accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? account.bankAccountID : account.fundID); + const selectAccountAndNavigateBack = (event: GestureResponderEvent | KeyboardEvent, accountType: string, account: BankAccountData | FundAccountData) => { + PaymentMethods.saveWalletTransferAccountTypeAndID( + accountType, + (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? (account as BankAccountData)?.bankAccountID?.toString() : (account as FundAccountData)?.fundID?.toString()) ?? '', + ); Navigation.goBack(ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE); }; @@ -45,7 +41,7 @@ function ChooseTransferAccountPage(props) { * @param {String} paymentType */ const navigateToAddPaymentMethodPage = () => { - if (props.walletTransfer.filterPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + if (walletTransfer?.filterPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); return; } @@ -55,24 +51,24 @@ function ChooseTransferAccountPage(props) { return ( Navigation.goBack(ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE)} /> @@ -80,15 +76,10 @@ function ChooseTransferAccountPage(props) { ); } -ChooseTransferAccountPage.propTypes = propTypes; -ChooseTransferAccountPage.defaultProps = defaultProps; ChooseTransferAccountPage.displayName = 'ChooseTransferAccountPage'; -export default compose( - withLocalize, - withOnyx({ - walletTransfer: { - key: ONYXKEYS.WALLET_TRANSFER, - }, - }), -)(ChooseTransferAccountPage); +export default withOnyx({ + walletTransfer: { + key: ONYXKEYS.WALLET_TRANSFER, + }, +})(ChooseTransferAccountPage); diff --git a/src/pages/settings/Wallet/RedDotCardSection.js b/src/pages/settings/Wallet/RedDotCardSection.tsx similarity index 76% rename from src/pages/settings/Wallet/RedDotCardSection.js rename to src/pages/settings/Wallet/RedDotCardSection.tsx index 7025cf7bf1a2..29acf7489fc7 100644 --- a/src/pages/settings/Wallet/RedDotCardSection.js +++ b/src/pages/settings/Wallet/RedDotCardSection.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import Icon from '@components/Icon'; @@ -7,12 +6,12 @@ import Text from '@components/Text'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -const propTypes = { - title: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, +type RedDotCardSectionProps = { + title: string; + description: string; }; -function RedDotCardSection({title, description}) { +function RedDotCardSection({title, description}: RedDotCardSectionProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -27,14 +26,13 @@ function RedDotCardSection({title, description}) { {title} - {description} + {description} ); } -RedDotCardSection.propTypes = propTypes; RedDotCardSection.displayName = 'RedDotCardSection'; export default RedDotCardSection; diff --git a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.js b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx similarity index 60% rename from src/pages/settings/Wallet/ReportVirtualCardFraudPage.js rename to src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx index fa88e01d8b41..6570fe804927 100644 --- a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.js +++ b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx @@ -1,7 +1,8 @@ -import PropTypes from 'prop-types'; +import type {RouteProp} from '@react-navigation/native'; +import isEmpty from 'lodash/isEmpty'; import React, {useEffect} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -17,57 +18,51 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Card from '@userActions/Card'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import assignedCardPropTypes from './assignedCardPropTypes'; +import type {Route} from '@src/ROUTES'; +import type {Form, Card as OnyxCard} from '@src/types/onyx'; -const propTypes = { - /* Onyx Props */ - formData: PropTypes.shape({ - isLoading: PropTypes.bool, - }), - cardList: PropTypes.objectOf(assignedCardPropTypes), - /** The parameters needed to authenticate with a short-lived token are in the URL */ - route: PropTypes.shape({ - /** Each parameter passed via the URL */ - params: PropTypes.shape({ - /** Domain string */ - domain: PropTypes.string, - }), - }).isRequired, +type ReportVirtualCardFraudPageOnyxProps = { + formData: OnyxEntry
; + cardList: OnyxEntry>; }; -const defaultProps = { - cardList: {}, - formData: {}, +type ReportVirtualCardFraudPageProps = ReportVirtualCardFraudPageOnyxProps & { + /** + * The parameters needed to authenticate with a short-lived token are in the URL + * Each parameter passed via the URL + * Domain string + */ + route: RouteProp<{params: {domain: Route}}>; }; function ReportVirtualCardFraudPage({ route: { params: {domain}, }, - cardList, - formData, -}) { + cardList = {}, + formData = {}, +}: ReportVirtualCardFraudPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const domainCards = CardUtils.getDomainCards(cardList)[domain]; - const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {}; - const virtualCardError = ErrorUtils.getLatestErrorMessage(virtualCard) || ''; + const domainCards = CardUtils.getDomainCards(cardList ?? {})[domain]; + const virtualCard = domainCards.find((card) => card.isVirtual); + const virtualCardError = ErrorUtils.getLatestErrorMessage(virtualCard?.errors ?? {}) || ''; - const prevIsLoading = usePrevious(formData.isLoading); + const prevIsLoading = usePrevious(formData?.isLoading); useEffect(() => { - if (!prevIsLoading || formData.isLoading) { + if (!prevIsLoading || formData?.isLoading) { return; } - if (!_.isEmpty(virtualCard.errors)) { + if (!isEmpty(virtualCard?.errors)) { return; } Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain)); - }, [domain, formData.isLoading, prevIsLoading, virtualCard.errors]); + }, [domain, formData?.isLoading, prevIsLoading, virtualCard?.errors]); - if (_.isEmpty(virtualCard)) { + if (isEmpty(virtualCard)) { return ; } @@ -78,12 +73,12 @@ function ReportVirtualCardFraudPage({ onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain))} /> - {translate('reportFraudPage.description')} + {translate('reportFraudPage.description')} Card.reportVirtualExpensifyCardFraud(virtualCard.cardID)} message={virtualCardError} - isLoading={formData.isLoading} + isLoading={formData?.isLoading} buttonText={translate('reportFraudPage.deactivateCard')} /> @@ -91,11 +86,9 @@ function ReportVirtualCardFraudPage({ ); } -ReportVirtualCardFraudPage.propTypes = propTypes; -ReportVirtualCardFraudPage.defaultProps = defaultProps; ReportVirtualCardFraudPage.displayName = 'ReportVirtualCardFraudPage'; -export default withOnyx({ +export default withOnyx({ cardList: { key: ONYXKEYS.CARD_LIST, }, diff --git a/src/pages/settings/Wallet/walletTransferPropTypes.js b/src/pages/settings/Wallet/walletTransferPropTypes.js deleted file mode 100644 index 9e25213382e9..000000000000 --- a/src/pages/settings/Wallet/walletTransferPropTypes.js +++ /dev/null @@ -1,16 +0,0 @@ -import PropTypes from 'prop-types'; -import CONST from '@src/CONST'; - -/** Wallet balance transfer props */ -const walletTransferPropTypes = PropTypes.shape({ - /** Selected accountID for transfer */ - selectedAccountID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - - /** Type to filter the payment Method list */ - filterPaymentMethodType: PropTypes.oneOf([CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, '']), - - /** Whether the success screen is shown to user. */ - shouldShowSuccess: PropTypes.bool, -}); - -export default walletTransferPropTypes; diff --git a/src/types/onyx/Fund.ts b/src/types/onyx/Fund.ts index 842a882ff23f..3073172a5eec 100644 --- a/src/types/onyx/Fund.ts +++ b/src/types/onyx/Fund.ts @@ -40,4 +40,4 @@ type Fund = { type FundList = Record; export default Fund; -export type {FundList}; +export type {AccountData, FundList}; From 47ea3acf32da429e1aad1d193effffe614fd4ace Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 18 Jan 2024 17:59:08 +0700 Subject: [PATCH 02/26] use StackScreenProps type --- src/libs/Navigation/types.ts | 1 + .../Wallet/ReportVirtualCardFraudPage.tsx | 25 ++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8d227fa6f697..a3c963729adf 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -384,6 +384,7 @@ type PublicScreensParamList = { shortLivedAuthToken?: string; shortLivedToken?: string; exitTo?: Routes; + domain?: Routes; }; [SCREENS.VALIDATE_LOGIN]: { accountID: string; diff --git a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx index 6570fe804927..d72f6c5ac35e 100644 --- a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx +++ b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx @@ -1,5 +1,4 @@ -import type {RouteProp} from '@react-navigation/native'; -import isEmpty from 'lodash/isEmpty'; +import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; import {View} from 'react-native'; import {OnyxEntry, withOnyx} from 'react-native-onyx'; @@ -14,30 +13,28 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; +import type {PublicScreensParamList} from '@libs/Navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Card from '@userActions/Card'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Route} from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import type {Form, Card as OnyxCard} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type ReportVirtualCardFraudPageOnyxProps = { + /** Form data propTypes */ formData: OnyxEntry; + + /** Card list propTypes */ cardList: OnyxEntry>; }; -type ReportVirtualCardFraudPageProps = ReportVirtualCardFraudPageOnyxProps & { - /** - * The parameters needed to authenticate with a short-lived token are in the URL - * Each parameter passed via the URL - * Domain string - */ - route: RouteProp<{params: {domain: Route}}>; -}; +type ReportVirtualCardFraudPageProps = ReportVirtualCardFraudPageOnyxProps & StackScreenProps; function ReportVirtualCardFraudPage({ route: { - params: {domain}, + params: {domain = ''}, }, cardList = {}, formData = {}, @@ -55,14 +52,14 @@ function ReportVirtualCardFraudPage({ if (!prevIsLoading || formData?.isLoading) { return; } - if (!isEmpty(virtualCard?.errors)) { + if (!isEmptyObject(virtualCard?.errors)) { return; } Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain)); }, [domain, formData?.isLoading, prevIsLoading, virtualCard?.errors]); - if (isEmpty(virtualCard)) { + if (isEmptyObject(virtualCard)) { return ; } From 9506d82a6a9e9a74db2b59ff20a851c7241171fe Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 18 Jan 2024 17:59:50 +0700 Subject: [PATCH 03/26] add commen --- src/pages/settings/Wallet/ChooseTransferAccountPage.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx b/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx index d938ca78117c..fd06bf13302d 100644 --- a/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx +++ b/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx @@ -18,10 +18,11 @@ import type {AccountData as FundAccountData} from '@src/types/onyx/Fund'; import PaymentMethodList from './PaymentMethodList'; type ChooseTransferAccountPageOnyxProps = { + /** Wallet transfer propTypes */ walletTransfer: OnyxEntry; }; -type ChooseTransferAccountPageProps = ChooseTransferAccountPageOnyxProps & {}; +type ChooseTransferAccountPageProps = ChooseTransferAccountPageOnyxProps; function ChooseTransferAccountPage({walletTransfer = {}}: ChooseTransferAccountPageProps) { const styles = useThemeStyles(); @@ -37,9 +38,6 @@ function ChooseTransferAccountPage({walletTransfer = {}}: ChooseTransferAccountP Navigation.goBack(ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE); }; - /** - * @param {String} paymentType - */ const navigateToAddPaymentMethodPage = () => { if (walletTransfer?.filterPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); From a15c596e8d2eb2895c2c08e1855e5354a64b38e5 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 18 Jan 2024 18:16:04 +0700 Subject: [PATCH 04/26] fix lint --- src/pages/settings/Wallet/ChooseTransferAccountPage.tsx | 6 ++++-- src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx b/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx index fd06bf13302d..ff45ca4be548 100644 --- a/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx +++ b/src/pages/settings/Wallet/ChooseTransferAccountPage.tsx @@ -1,6 +1,8 @@ import React from 'react'; -import {GestureResponderEvent, View} from 'react-native'; -import {OnyxEntry, withOnyx} from 'react-native-onyx'; +import type {GestureResponderEvent} from 'react-native'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; diff --git a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx index d72f6c5ac35e..9b18dfacf8c2 100644 --- a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx +++ b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.tsx @@ -1,8 +1,8 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; import {View} from 'react-native'; -import {OnyxEntry, withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; From b8485107464dd76efa0ab34a0d5f7c59b105e422 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 18 Jan 2024 18:17:43 +0700 Subject: [PATCH 05/26] TS migrate ActivatePhysicalCardPage --- .../Wallet/ActivatePhysicalCardPage.tsx | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx new file mode 100644 index 000000000000..85111b606d55 --- /dev/null +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -0,0 +1,166 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; +import {View} from 'react-native'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; +import BigNumberPad from '@components/BigNumberPad'; +import Button from '@components/Button'; +import IllustratedHeaderPageLayout from '@components/IllustratedHeaderPageLayout'; +import LottieAnimations from '@components/LottieAnimations'; +import MagicCodeInput from '@components/MagicCodeInput'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as CardUtils from '@libs/CardUtils'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import type {PublicScreensParamList} from '@libs/Navigation/types'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import * as CardSettings from '@userActions/Card'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; +import type {Card} from '@src/types/onyx'; +import { isEmptyObject } from '@src/types/utils/EmptyObject'; + +type ActivatePhysicalCardPageOnyxProps = { + /** Card list propTypes */ + cardList: OnyxEntry>; +}; + +type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps; + +const LAST_FOUR_DIGITS_LENGTH = 4; +const MAGIC_INPUT_MIN_HEIGHT = 86; + +function ActivatePhysicalCardPage({ + cardList = {}, + route: { + params: {domain = ''}, + }, +}: ActivatePhysicalCardPageProps) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {isExtraSmallScreenHeight} = useWindowDimensions(); + const {translate} = useLocalize(); + const {isOffline} = useNetwork(); + + const [formError, setFormError] = useState(''); + const [lastFourDigits, setLastFourDigits] = useState(''); + const [lastPressedDigit, setLastPressedDigit] = useState(''); + + const domainCards = CardUtils.getDomainCards(cardList ?? {})[domain]; + const physicalCard = domainCards.find((card) => card.isVirtual); + const cardID = physicalCard?.cardID ?? 0; + const cardError = ErrorUtils.getLatestErrorMessage(physicalCard ?? {}); + + const activateCardCodeInputRef = useRef(null); + + /** + * If state of the card is CONST.EXPENSIFY_CARD.STATE.OPEN, navigate to card details screen. + */ + useEffect(() => { + if (physicalCard?.isLoading || (cardList && cardList[cardID]?.state !== CONST.EXPENSIFY_CARD.STATE.OPEN)) { + return; + } + + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain)); + }, [cardID, cardList, domain, physicalCard?.isLoading]); + + useEffect( + () => () => { + CardSettings.clearCardListErrors(cardID); + }, + [cardID], + ); + + /** + * Update lastPressedDigit with value that was pressed on BigNumberPad. + * + * NOTE: If the same digit is pressed twice in a row, append it to the end of the string + * so that useEffect inside MagicCodeInput will be triggered by artificial change of the value. + */ + const updateLastPressedDigit = useCallback((key: string) => setLastPressedDigit(lastPressedDigit === key ? lastPressedDigit + key : key), [lastPressedDigit]); + + /** + * Handle card activation code input + */ + const onCodeInput = (text: string) => { + setFormError(''); + + if (cardError) { + CardSettings.clearCardListErrors(cardID); + } + + setLastFourDigits(text); + }; + + const submitAndNavigateToNextPage = useCallback(() => { + // @ts-expect-error TODO: Remove this once MagicCodeInput (https://github.com/Expensify/App/issues/25078) is migrated to TypeScript. + activateCardCodeInputRef.current?.blur(); + + if (lastFourDigits.replace(CONST.MAGIC_CODE_EMPTY_CHAR, '').length !== LAST_FOUR_DIGITS_LENGTH) { + setFormError(translate('activateCardPage.error.thatDidntMatch')); + return; + } + + CardSettings.activatePhysicalExpensifyCard(lastFourDigits, cardID); + }, [lastFourDigits, cardID, translate]); + + if (isEmptyObject(physicalCard)) { + return ; + } + + return ( + Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain))} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.PREFERENCES.ROOT].backgroundColor} + illustration={LottieAnimations.Magician} + scrollViewContainerStyles={[styles.mnh100]} + childrenContainerStyles={[styles.flex1]} + > + {translate('activateCardPage.pleaseEnterLastFour')} + + + + + {DeviceCapabilities.canUseTouchScreen() && } + +