From f73d60100b4fc965ba780113338b9eed28517a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 6 Oct 2023 10:55:32 +0200 Subject: [PATCH 01/19] first commit --- src/pages/settings/InitialSettingsPage.js | 27 ++++++++-------- .../settings/Wallet/ExpensifyCardPage.js | 31 +++++++++++++------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index d81c9d057174..61b2b5f838eb 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -1,11 +1,11 @@ import lodashGet from 'lodash/get'; -import React, {useState, useEffect, useRef, useMemo, useCallback} from 'react'; -import {View} from 'react-native'; +import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; +import { View } from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; +import { withOnyx } from 'react-native-onyx'; import CurrentUserPersonalDetailsSkeletonView from '../../components/CurrentUserPersonalDetailsSkeletonView'; -import {withNetwork} from '../../components/OnyxProvider'; +import { withNetwork } from '../../components/OnyxProvider'; import styles from '../../styles/styles'; import Text from '../../components/Text'; import * as Session from '../../libs/actions/Session'; @@ -18,11 +18,11 @@ import MenuItem from '../../components/MenuItem'; import themeColors from '../../styles/themes/default'; import SCREENS from '../../SCREENS'; import ROUTES from '../../ROUTES'; -import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import withLocalize, { withLocalizePropTypes } from '../../components/withLocalize'; import compose from '../../libs/compose'; import CONST from '../../CONST'; import Permissions from '../../libs/Permissions'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../../components/withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails, { withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes } from '../../components/withCurrentUserPersonalDetails'; import * as PaymentMethods from '../../libs/actions/PaymentMethods'; import bankAccountPropTypes from '../../components/bankAccountPropTypes'; import cardPropTypes from '../../components/cardPropTypes'; @@ -37,7 +37,7 @@ import * as ReimbursementAccountProps from '../ReimbursementAccount/reimbursemen import * as UserUtils from '../../libs/UserUtils'; import policyMemberPropType from '../policyMemberPropType'; import * as ReportActionContextMenu from '../home/report/ContextMenu/ReportActionContextMenu'; -import {CONTEXT_MENU_TYPES} from '../home/report/ContextMenu/ContextMenuActions'; +import { CONTEXT_MENU_TYPES } from '../home/report/ContextMenu/ContextMenuActions'; import * as CurrencyUtils from '../../libs/CurrencyUtils'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import useLocalize from '../../hooks/useLocalize'; @@ -128,12 +128,13 @@ const defaultProps = { allPolicyMembers: {}, ...withCurrentUserPersonalDetailsDefaultProps, }; +window._navigate = () => Navigation.navigate('/settings/wallet/card/Expensify'); function InitialSettingsPage(props) { - const {isExecuting, singleExecution} = useSingleExecution(); + const { isExecuting, singleExecution } = useSingleExecution(); const waitForNavigate = useWaitForNavigation(); const popoverAnchor = useRef(null); - const {translate} = useLocalize(); + const { translate } = useLocalize(); const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); @@ -179,10 +180,10 @@ function InitialSettingsPage(props) { const policyBrickRoadIndicator = !_.isEmpty(props.reimbursementAccount.errors) || - _.chain(props.policies) - .filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) - .some((policy) => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, props.allPolicyMembers)) - .value() + _.chain(props.policies) + .filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) + .some((policy) => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, props.allPolicyMembers)) + .value() ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null; const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 026e8147d79f..54088b40c0c9 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; -import React, {useState} from 'react'; -import {ScrollView, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import React, { useState } from 'react'; +import { ScrollView, View } from 'react-native'; +import { withOnyx } from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../../ONYXKEYS'; import ROUTES from '../../../ROUTES'; @@ -18,6 +18,8 @@ import styles from '../../../styles/styles'; import * as CardUtils from '../../../libs/CardUtils'; import Button from '../../../components/Button'; import CardDetails from './WalletPage/CardDetails'; +// eslint-disable-next-line rulesdir/no-api-in-views +import * as API from '../../../libs/API'; const propTypes = { /* Onyx Props */ @@ -39,15 +41,17 @@ const defaultProps = { function ExpensifyCardPage({ cardList, route: { - params: {domain}, + params: { domain }, }, }) { - const {translate} = useLocalize(); + const { translate } = useLocalize(); const domainCards = CardUtils.getDomainCards(cardList)[domain]; const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {}; const physicalCard = _.find(domainCards, (card) => !card.isVirtual) || {}; const [shouldShowCardDetails, setShouldShowCardDetails] = useState(false); + const [details, setDetails] = useState({}); + const [loading, setLoading] = useState(false); if (_.isEmpty(virtualCard) && _.isEmpty(physicalCard)) { return ; @@ -57,6 +61,14 @@ function ExpensifyCardPage({ const handleRevealDetails = () => { setShouldShowCardDetails(true); + setLoading(true); + // eslint-disable-next-line + API.makeRequestWithSideEffects('RevealVirtualCardDetails') + .then((val) => { + setDetails(val); + setLoading(false); + }) + .catch(console.log); }; return ( @@ -64,7 +76,7 @@ function ExpensifyCardPage({ includeSafeAreaPaddingBottom={false} testID={ExpensifyCardPage.displayName} > - {({safeAreaPaddingBottomStyle}) => ( + {({ safeAreaPaddingBottomStyle }) => ( <> ) : ( Date: Fri, 6 Oct 2023 17:27:21 +0200 Subject: [PATCH 02/19] move to reducer and error handling --- .../settings/Wallet/ExpensifyCardPage.js | 42 ++++++++------ .../settings/Wallet/revealCardDetailsUtils.ts | 57 +++++++++++++++++++ 2 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 src/pages/settings/Wallet/revealCardDetailsUtils.ts diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 54088b40c0c9..8d42ce149e16 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; -import React, { useState } from 'react'; -import { ScrollView, View } from 'react-native'; -import { withOnyx } from 'react-native-onyx'; +import React, {useReducer} from 'react'; +import {ScrollView, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../../ONYXKEYS'; import ROUTES from '../../../ROUTES'; @@ -20,6 +20,8 @@ import Button from '../../../components/Button'; import CardDetails from './WalletPage/CardDetails'; // eslint-disable-next-line rulesdir/no-api-in-views import * as API from '../../../libs/API'; +import CONST from '../../../CONST'; +import * as revealCardDetailsUtils from './revealCardDetailsUtils'; const propTypes = { /* Onyx Props */ @@ -41,17 +43,15 @@ const defaultProps = { function ExpensifyCardPage({ cardList, route: { - params: { domain }, + params: {domain}, }, }) { - const { translate } = useLocalize(); + const {translate} = useLocalize(); const domainCards = CardUtils.getDomainCards(cardList)[domain]; const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {}; const physicalCard = _.find(domainCards, (card) => !card.isVirtual) || {}; - const [shouldShowCardDetails, setShouldShowCardDetails] = useState(false); - const [details, setDetails] = useState({}); - const [loading, setLoading] = useState(false); + const [{loading, details, error}, dispatch] = useReducer(revealCardDetailsUtils.reducer, revealCardDetailsUtils.initialState); if (_.isEmpty(virtualCard) && _.isEmpty(physicalCard)) { return ; @@ -60,15 +60,19 @@ function ExpensifyCardPage({ const formattedAvailableSpendAmount = CurrencyUtils.convertToDisplayString(physicalCard.availableSpend || virtualCard.availableSpend || 0); const handleRevealDetails = () => { - setShouldShowCardDetails(true); - setLoading(true); + dispatch({type: 'START'}); // eslint-disable-next-line API.makeRequestWithSideEffects('RevealVirtualCardDetails') - .then((val) => { - setDetails(val); - setLoading(false); + .then((response) => { + if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { + dispatch({type: 'FAIL', payload: response.message}); + return; + } + dispatch({type: 'SUCCESS', payload: response}); }) - .catch(console.log); + .catch((err) => { + dispatch({type: 'FAIL', payload: err.message}); + }); }; return ( @@ -76,7 +80,7 @@ function ExpensifyCardPage({ includeSafeAreaPaddingBottom={false} testID={ExpensifyCardPage.displayName} > - {({ safeAreaPaddingBottomStyle }) => ( + {({safeAreaPaddingBottomStyle}) => ( <> {!_.isEmpty(virtualCard) && ( <> - {shouldShowCardDetails ? ( + {details.pan ? ( ) : ( } /> diff --git a/src/pages/settings/Wallet/revealCardDetailsUtils.ts b/src/pages/settings/Wallet/revealCardDetailsUtils.ts new file mode 100644 index 000000000000..86300e1058b6 --- /dev/null +++ b/src/pages/settings/Wallet/revealCardDetailsUtils.ts @@ -0,0 +1,57 @@ +type State = { + details: { + pan: string; + expiration: string; + cvv: string; + privatePersonalDetails: { + address: { + street: string; + street2: string; + city: string; + state: string; + zip: string; + country: string; + }; + }; + }; + loading: boolean; + error: string; +}; + +type Action = {type: 'START'} | {type: 'SUCCESS'; payload: State['details']} | {type: 'FAIL'; payload: string}; + +const initialState: State = { + details: { + pan: '', + expiration: '', + cvv: '', + privatePersonalDetails: { + address: { + street: '', + street2: '', + city: '', + state: '', + zip: '', + country: '', + }, + }, + }, + loading: false, + error: '', +}; + +const reducer = (state: State, action: Action) => { + switch (action.type) { + case 'START': + return {...state, loading: true}; + case 'SUCCESS': + return {details: action.payload, loading: false, error: ''}; + case 'FAIL': { + return {...state, error: action.payload, loading: false}; + } + default: + return state; + } +}; + +export {initialState, reducer}; From df10ff714fa27a6348a53d2a842fff69d5ad7d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 9 Oct 2023 10:46:19 +0200 Subject: [PATCH 03/19] removed unnecessary helper function --- src/pages/settings/InitialSettingsPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 61b2b5f838eb..fc7997a3d0b6 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -128,7 +128,6 @@ const defaultProps = { allPolicyMembers: {}, ...withCurrentUserPersonalDetailsDefaultProps, }; -window._navigate = () => Navigation.navigate('/settings/wallet/card/Expensify'); function InitialSettingsPage(props) { const { isExecuting, singleExecution } = useSingleExecution(); From 36fb11a5c21d9390ba13b3df51fbd60c316204b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 9 Oct 2023 10:46:51 +0200 Subject: [PATCH 04/19] formatting --- src/pages/settings/InitialSettingsPage.js | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index fc7997a3d0b6..d81c9d057174 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -1,11 +1,11 @@ import lodashGet from 'lodash/get'; -import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; -import { View } from 'react-native'; +import React, {useState, useEffect, useRef, useMemo, useCallback} from 'react'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import { withOnyx } from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import CurrentUserPersonalDetailsSkeletonView from '../../components/CurrentUserPersonalDetailsSkeletonView'; -import { withNetwork } from '../../components/OnyxProvider'; +import {withNetwork} from '../../components/OnyxProvider'; import styles from '../../styles/styles'; import Text from '../../components/Text'; import * as Session from '../../libs/actions/Session'; @@ -18,11 +18,11 @@ import MenuItem from '../../components/MenuItem'; import themeColors from '../../styles/themes/default'; import SCREENS from '../../SCREENS'; import ROUTES from '../../ROUTES'; -import withLocalize, { withLocalizePropTypes } from '../../components/withLocalize'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import CONST from '../../CONST'; import Permissions from '../../libs/Permissions'; -import withCurrentUserPersonalDetails, { withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes } from '../../components/withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../../components/withCurrentUserPersonalDetails'; import * as PaymentMethods from '../../libs/actions/PaymentMethods'; import bankAccountPropTypes from '../../components/bankAccountPropTypes'; import cardPropTypes from '../../components/cardPropTypes'; @@ -37,7 +37,7 @@ import * as ReimbursementAccountProps from '../ReimbursementAccount/reimbursemen import * as UserUtils from '../../libs/UserUtils'; import policyMemberPropType from '../policyMemberPropType'; import * as ReportActionContextMenu from '../home/report/ContextMenu/ReportActionContextMenu'; -import { CONTEXT_MENU_TYPES } from '../home/report/ContextMenu/ContextMenuActions'; +import {CONTEXT_MENU_TYPES} from '../home/report/ContextMenu/ContextMenuActions'; import * as CurrencyUtils from '../../libs/CurrencyUtils'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import useLocalize from '../../hooks/useLocalize'; @@ -130,10 +130,10 @@ const defaultProps = { }; function InitialSettingsPage(props) { - const { isExecuting, singleExecution } = useSingleExecution(); + const {isExecuting, singleExecution} = useSingleExecution(); const waitForNavigate = useWaitForNavigation(); const popoverAnchor = useRef(null); - const { translate } = useLocalize(); + const {translate} = useLocalize(); const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); @@ -179,10 +179,10 @@ function InitialSettingsPage(props) { const policyBrickRoadIndicator = !_.isEmpty(props.reimbursementAccount.errors) || - _.chain(props.policies) - .filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) - .some((policy) => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, props.allPolicyMembers)) - .value() + _.chain(props.policies) + .filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) + .some((policy) => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, props.allPolicyMembers)) + .value() ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null; const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); From bfdb546f3d1c5ef7a5fed8f1b4fafd785f46b4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 9 Oct 2023 10:52:05 +0200 Subject: [PATCH 05/19] var name change to isLoading --- src/pages/settings/Wallet/ExpensifyCardPage.js | 6 +++--- src/pages/settings/Wallet/revealCardDetailsUtils.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 8d42ce149e16..7a7a3a742845 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -51,7 +51,7 @@ function ExpensifyCardPage({ const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {}; const physicalCard = _.find(domainCards, (card) => !card.isVirtual) || {}; - const [{loading, details, error}, dispatch] = useReducer(revealCardDetailsUtils.reducer, revealCardDetailsUtils.initialState); + const [{isLoading, details, error}, dispatch] = useReducer(revealCardDetailsUtils.reducer, revealCardDetailsUtils.initialState); if (_.isEmpty(virtualCard) && _.isEmpty(physicalCard)) { return ; @@ -119,8 +119,8 @@ function ExpensifyCardPage({ medium text={translate('cardPage.cardDetails.revealDetails')} onPress={handleRevealDetails} - isDisabled={loading} - isLoading={loading} + isDisabled={isLoading} + isLoading={isLoading} /> } /> diff --git a/src/pages/settings/Wallet/revealCardDetailsUtils.ts b/src/pages/settings/Wallet/revealCardDetailsUtils.ts index 86300e1058b6..5e2eb355acea 100644 --- a/src/pages/settings/Wallet/revealCardDetailsUtils.ts +++ b/src/pages/settings/Wallet/revealCardDetailsUtils.ts @@ -14,7 +14,7 @@ type State = { }; }; }; - loading: boolean; + isLoading: boolean; error: string; }; @@ -36,18 +36,18 @@ const initialState: State = { }, }, }, - loading: false, + isLoading: false, error: '', }; -const reducer = (state: State, action: Action) => { +const reducer = (state: State, action: Action): State => { switch (action.type) { case 'START': - return {...state, loading: true}; + return {...state, isLoading: true}; case 'SUCCESS': - return {details: action.payload, loading: false, error: ''}; + return {details: action.payload, isLoading: false, error: ''}; case 'FAIL': { - return {...state, error: action.payload, loading: false}; + return {...state, error: action.payload, isLoading: false}; } default: return state; From 9a8962c1210888f8c91515ec24dae0d39fb27a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 9 Oct 2023 11:12:47 +0200 Subject: [PATCH 06/19] better linting --- src/pages/settings/Wallet/ExpensifyCardPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 7a7a3a742845..09b0bd53bb7e 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -61,7 +61,7 @@ function ExpensifyCardPage({ const handleRevealDetails = () => { dispatch({type: 'START'}); - // eslint-disable-next-line + // eslint-disable-next-line rulesdir/no-api-in-views,rulesdir/no-api-side-effects-method API.makeRequestWithSideEffects('RevealVirtualCardDetails') .then((response) => { if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { From c7dd8fa76a6757efa2580a6540524a6f35dc8963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 9 Oct 2023 12:43:06 +0200 Subject: [PATCH 07/19] moved action types to const --- src/pages/settings/Wallet/ExpensifyCardPage.js | 8 ++++---- .../settings/Wallet/revealCardDetailsUtils.ts | 16 +++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 09b0bd53bb7e..3e2d8632228a 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -60,18 +60,18 @@ function ExpensifyCardPage({ const formattedAvailableSpendAmount = CurrencyUtils.convertToDisplayString(physicalCard.availableSpend || virtualCard.availableSpend || 0); const handleRevealDetails = () => { - dispatch({type: 'START'}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.start}); // eslint-disable-next-line rulesdir/no-api-in-views,rulesdir/no-api-side-effects-method API.makeRequestWithSideEffects('RevealVirtualCardDetails') .then((response) => { if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { - dispatch({type: 'FAIL', payload: response.message}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.fail, payload: response.message}); return; } - dispatch({type: 'SUCCESS', payload: response}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.success, payload: response}); }) .catch((err) => { - dispatch({type: 'FAIL', payload: err.message}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.fail, payload: err.message}); }); }; diff --git a/src/pages/settings/Wallet/revealCardDetailsUtils.ts b/src/pages/settings/Wallet/revealCardDetailsUtils.ts index 5e2eb355acea..57e70fa41462 100644 --- a/src/pages/settings/Wallet/revealCardDetailsUtils.ts +++ b/src/pages/settings/Wallet/revealCardDetailsUtils.ts @@ -1,3 +1,9 @@ +const ACTION_TYPES = { + START: 'START', + SUCCESS: 'SUCCESS', + FAIL: 'FAIL', +} as const; + type State = { details: { pan: string; @@ -18,7 +24,7 @@ type State = { error: string; }; -type Action = {type: 'START'} | {type: 'SUCCESS'; payload: State['details']} | {type: 'FAIL'; payload: string}; +type Action = {type: typeof ACTION_TYPES.START} | {type: typeof ACTION_TYPES.SUCCESS; payload: State['details']} | {type: typeof ACTION_TYPES.FAIL; payload: string}; const initialState: State = { details: { @@ -42,11 +48,11 @@ const initialState: State = { const reducer = (state: State, action: Action): State => { switch (action.type) { - case 'START': + case ACTION_TYPES.START: return {...state, isLoading: true}; - case 'SUCCESS': + case ACTION_TYPES.SUCCESS: return {details: action.payload, isLoading: false, error: ''}; - case 'FAIL': { + case ACTION_TYPES.FAIL: { return {...state, error: action.payload, isLoading: false}; } default: @@ -54,4 +60,4 @@ const reducer = (state: State, action: Action): State => { } }; -export {initialState, reducer}; +export {initialState, reducer, ACTION_TYPES}; From ccf8f4e213c15aa88f1fc584538e4a63263b11d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Mon, 9 Oct 2023 16:12:50 +0200 Subject: [PATCH 08/19] action types fix and cardID added to api call --- src/pages/settings/Wallet/ExpensifyCardPage.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 3e2d8632228a..fe16b9014e19 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -62,16 +62,16 @@ function ExpensifyCardPage({ const handleRevealDetails = () => { dispatch({type: revealCardDetailsUtils.ACTION_TYPES.start}); // eslint-disable-next-line rulesdir/no-api-in-views,rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects('RevealVirtualCardDetails') + API.makeRequestWithSideEffects('RevealVirtualCardDetails', {cardID: virtualCard.cardID}) .then((response) => { if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.fail, payload: response.message}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.FAIL, payload: response.message}); return; } - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.success, payload: response}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.SUCCESS, payload: response}); }) .catch((err) => { - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.fail, payload: err.message}); + dispatch({type: revealCardDetailsUtils.ACTION_TYPES.FAIL, payload: err.message}); }); }; From ef623a4c603bd4b4909f5f740e0d310d047cee24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 10 Oct 2023 13:10:23 +0200 Subject: [PATCH 09/19] address refactoring --- .../settings/Wallet/ExpensifyCardPage.js | 2 +- .../settings/Wallet/revealCardDetailsUtils.ts | 32 ++++++++----------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index fe16b9014e19..892aea0495da 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -104,7 +104,7 @@ function ExpensifyCardPage({ pan={details.pan} expiration={details.expiration} cvv={details.cvv} - privatePersonalDetails={details.privatePersonalDetails} + privatePersonalDetails={{address: details.address}} /> ) : ( Date: Wed, 11 Oct 2023 16:22:19 +0200 Subject: [PATCH 10/19] refactoring --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/actions/Card.ts | 23 ++++++++ .../settings/Wallet/ExpensifyCardPage.js | 36 +++++------ .../settings/Wallet/revealCardDetailsUtils.ts | 59 ------------------- src/types/onyx/Card.ts | 15 +++++ 6 files changed, 56 insertions(+), 79 deletions(-) create mode 100644 src/libs/actions/Card.ts delete mode 100644 src/pages/settings/Wallet/revealCardDetailsUtils.ts diff --git a/src/languages/en.ts b/src/languages/en.ts index 9d9d4e0a4d70..2090ee4b76f2 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -838,6 +838,7 @@ export default { revealDetails: 'Reveal details', copyCardNumber: 'Copy card number', }, + cardDetailsLoadingFailure: 'An error occurred loading card details, please try again.', }, transferAmountPage: { transfer: ({amount}: TransferParams) => `Transfer${amount ? ` ${amount}` : ''}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index 2ad75685a1a2..ed44aeedd6d9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -834,6 +834,7 @@ export default { revealDetails: 'Revelar detalles', copyCardNumber: 'Copiar número de la tarjeta', }, + cardDetailsLoadingFailure: 'Ocurrió un error al cargar los detalles de la tarjeta. Por favor, inténtalo de nuevo.', }, transferAmountPage: { transfer: ({amount}: TransferParams) => `Transferir${amount ? ` ${amount}` : ''}`, diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts new file mode 100644 index 000000000000..f75c77c928d9 --- /dev/null +++ b/src/libs/actions/Card.ts @@ -0,0 +1,23 @@ +import * as API from '../API'; +import CONST from '../../CONST'; +import {TCardDetails} from '../../types/onyx/Card'; +import * as Localize from '../Localize'; + +function revealVirtualCardDetails(cardID: string): Promise { + return new Promise((resolve, reject) => { + // eslint-disable-next-line rulesdir/no-api-side-effects-method + API.makeRequestWithSideEffects('RevealVirtualCardDetails', {cardID}) + .then((response) => { + if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { + reject(response.message || Localize.translateLocal('cardPage.cardDetailsLoadingFailure')); + return; + } + resolve(response); + }) + .catch((err) => { + reject(err.message); + }); + }); +} + +export default {revealVirtualCardDetails}; diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js index 892aea0495da..4e94a1ecfe95 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.js +++ b/src/pages/settings/Wallet/ExpensifyCardPage.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, {useReducer} from 'react'; +import React, {useState} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -18,10 +18,7 @@ import styles from '../../../styles/styles'; import * as CardUtils from '../../../libs/CardUtils'; import Button from '../../../components/Button'; import CardDetails from './WalletPage/CardDetails'; -// eslint-disable-next-line rulesdir/no-api-in-views -import * as API from '../../../libs/API'; -import CONST from '../../../CONST'; -import * as revealCardDetailsUtils from './revealCardDetailsUtils'; +import Card from '../../../libs/actions/Card'; const propTypes = { /* Onyx Props */ @@ -51,7 +48,10 @@ function ExpensifyCardPage({ const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {}; const physicalCard = _.find(domainCards, (card) => !card.isVirtual) || {}; - const [{isLoading, details, error}, dispatch] = useReducer(revealCardDetailsUtils.reducer, revealCardDetailsUtils.initialState); + // card details state + const [isLoading, setIsLoading] = useState(false); + const [details, setDetails] = useState({}); + const [errorMessage, setErrorMessage] = useState(''); if (_.isEmpty(virtualCard) && _.isEmpty(physicalCard)) { return ; @@ -60,19 +60,15 @@ function ExpensifyCardPage({ const formattedAvailableSpendAmount = CurrencyUtils.convertToDisplayString(physicalCard.availableSpend || virtualCard.availableSpend || 0); const handleRevealDetails = () => { - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.start}); - // eslint-disable-next-line rulesdir/no-api-in-views,rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects('RevealVirtualCardDetails', {cardID: virtualCard.cardID}) - .then((response) => { - if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.FAIL, payload: response.message}); - return; - } - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.SUCCESS, payload: response}); - }) - .catch((err) => { - dispatch({type: revealCardDetailsUtils.ACTION_TYPES.FAIL, payload: err.message}); - }); + setIsLoading(true); + // We can't store the response in Onyx for security reasons. + // That is this action is handled manually and the response is stored in a local state + // Hence the eslint disable here. + // eslint-disable-next-line rulesdir/no-thenable-actions-in-views + Card.revealVirtualCardDetails(virtualCard.cardID) + .then(setDetails) + .catch(setErrorMessage) + .finally(() => setIsLoading(false)); }; return ( @@ -113,7 +109,7 @@ function ExpensifyCardPage({ interactive={false} titleStyle={styles.walletCardNumber} shouldShowRightComponent - error={error} + error={errorMessage} rightComponent={