Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/pages/settings/Wallet/ExpensifyCardPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ function ExpensifyCardPage({
const shouldShowReportLostCardButton = currentPhysicalCard?.state === CONST.EXPENSIFY_CARD.STATE.NOT_ACTIVATED || currentPhysicalCard?.state === CONST.EXPENSIFY_CARD.STATE.OPEN;

const currency = getCurrencyKeyByCountryCode(currencyList, cardsToShow?.at(0)?.nameValuePairs?.feedCountry);
const shouldShowPIN = currency !== CONST.CURRENCY.USD;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ PERF-6 (docs)

The shouldShowPIN calculation is executed on every render, even though it only depends on a specific property of the cardsToShow array. This can be optimized by memoizing the calculation.

Consider using useMemo to optimize this:

const shouldShowPIN = useMemo(() => {
    const feedCountry = cardsToShow?.at(0)?.nameValuePairs?.feedCountry;
    const currency = getCurrencyKeyByCountryCode(currencyList, feedCountry);
    return currency !== CONST.CURRENCY.USD;
}, [currencyList, cardsToShow]);

This ensures the calculation only runs when the actual dependencies change, not on every render.

const formattedAvailableSpendAmount = convertToDisplayString(cardsToShow?.at(0)?.availableSpend, currency);
const {limitNameKey, limitTitleKey} = getLimitTypeTranslationKeys(cardsToShow?.at(0)?.nameValuePairs?.limitType);

Expand Down Expand Up @@ -302,12 +303,14 @@ function ExpensifyCardPage({
interactive={false}
titleStyle={styles.walletCardNumber}
/>
<MenuItemWithTopDescription
description={translate('cardPage.physicalCardPin')}
title={maskPin(pin)}
interactive={false}
titleStyle={styles.walletCardNumber}
/>
{shouldShowPIN && (
<MenuItemWithTopDescription
description={translate('cardPage.physicalCardPin')}
title={maskPin(pin)}
interactive={false}
titleStyle={styles.walletCardNumber}
/>
)}
<MenuItem
title={translate('reportCardLostOrDamaged.screenTitle')}
icon={expensifyIcons.Flag}
Expand Down
78 changes: 78 additions & 0 deletions tests/ui/WalletExpensifyCardPageTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ExpensifyCardPage from '@pages/settings/Wallet/ExpensifyCardPage';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SCREENS from '@src/SCREENS';
import currencyList from '../unit/currencyList.json';
import * as TestHelper from '../utils/TestHelper';
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';

Expand Down Expand Up @@ -51,6 +52,9 @@ describe('ExpensifyCardPage', () => {
// Initialize Onyx with required keys before running any test.
Onyx.init({
keys: ONYXKEYS,
initialKeyStates: {
[ONYXKEYS.CURRENCY_LIST]: currencyList,
},
});
});

Expand Down Expand Up @@ -158,4 +162,78 @@ describe('ExpensifyCardPage', () => {
unmount();
await waitForBatchedUpdatesWithAct();
});

it('should not show the PIN option on screen', async () => {
// Sign in as a test user before running the test.
await TestHelper.signInWithTestUser();

// Add a mock card to Onyx storage to simulate a valid card being loaded.
await act(async () => {
await Onyx.merge(ONYXKEYS.CARD_LIST, {
[userCardID]: {
cardID: 1234,
state: CONST.EXPENSIFY_CARD.STATE.OPEN,
domainName: 'xyz',
nameValuePairs: {
isVirtual: false,
cardTitle: 'Test Card',
feedCountry: CONST.COUNTRY.US,
},
availableSpend: 50000,
fraud: null,
},
});
});

// Render the page with the specified card ID.
const {unmount} = renderPage(SCREENS.SETTINGS.WALLET.DOMAIN_CARD, {cardID: '1234'});

await waitForBatchedUpdatesWithAct();

// Verify that the "PIN" option is not displayed on the screen.
await waitFor(() => {
expect(screen.queryByText(translateLocal('cardPage.physicalCardPin'))).not.toBeOnTheScreen();
});

// Unmount the component after assertions to clean up.
unmount();
await waitForBatchedUpdatesWithAct();
});

it('should show the PIN option on screen', async () => {
// Sign in as a test user before running the test.
await TestHelper.signInWithTestUser();

// Add a mock card to Onyx storage to simulate a valid card being loaded.
await act(async () => {
await Onyx.merge(ONYXKEYS.CARD_LIST, {
[userCardID]: {
cardID: 1234,
state: CONST.EXPENSIFY_CARD.STATE.OPEN,
domainName: 'xyz',
nameValuePairs: {
isVirtual: false,
cardTitle: 'Test Card',
feedCountry: CONST.COUNTRY.GB,
},
availableSpend: 50000,
fraud: null,
},
});
});

// Render the page with the specified card ID.
const {unmount} = renderPage(SCREENS.SETTINGS.WALLET.DOMAIN_CARD, {cardID: '1234'});

await waitForBatchedUpdatesWithAct();

// Verify that the "PIN" option is displayed on the screen.
await waitFor(() => {
expect(screen.getByText(translateLocal('cardPage.physicalCardPin'))).toBeOnTheScreen();
});

// Unmount the component after assertions to clean up.
unmount();
await waitForBatchedUpdatesWithAct();
});
});
1 change: 1 addition & 0 deletions tests/unit/currencyList.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
"GBP": {
"symbol": "£",
"name": "British Pound",
"countries": ["GB"],
"ISO4217": "826"
},
"GEL": {
Expand Down
Loading