Skip to content

Commit 65f13bd

Browse files
authored
Merge pull request Expensify#68192 from cosmicvulpes/fix/wallet-physical-card-not-displayed
fix: add getAssignedCardSortKey to prioritize physical Expensify cards
2 parents a17ac5b + 8ef200b commit 65f13bd

3 files changed

Lines changed: 46 additions & 2 deletions

File tree

src/libs/CardUtils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ function getMonthFromExpirationDateString(expirationDateString: string) {
3636
return expirationDateString.substring(0, 2);
3737
}
3838

39+
/**
40+
* Sorting logic for assigned cards.
41+
*
42+
* Ensure to sort physical Expensify cards first, no matter what their cardIDs are.
43+
* This way ensures the Expensify Combo Card detail is rendered correctly,
44+
* because we will always use the cardID of the physical card from the combo card duo.
45+
*
46+
* @param card - card to get the sort key for
47+
* @returns number
48+
*/
49+
function getAssignedCardSortKey(card: Card): number {
50+
if (!isExpensifyCard(card)) {
51+
return 2;
52+
}
53+
return card?.nameValuePairs?.isVirtual ? 1 : 0;
54+
}
55+
3956
/**
4057
* @param card
4158
* @returns boolean
@@ -663,6 +680,7 @@ function getFundIdFromSettingsKey(key: string) {
663680
}
664681

665682
export {
683+
getAssignedCardSortKey,
666684
isExpensifyCard,
667685
getDomainCards,
668686
formatCardExpiration,

src/pages/settings/Wallet/PaymentMethodList.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import useStyleUtils from '@hooks/useStyleUtils';
2121
import useThemeIllustrations from '@hooks/useThemeIllustrations';
2222
import useThemeStyles from '@hooks/useThemeStyles';
2323
import {clearAddPaymentMethodError, clearDeletePaymentMethodError} from '@libs/actions/PaymentMethods';
24-
import {getCardFeedIcon, getPlaidInstitutionIconUrl, isExpensifyCard, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils';
24+
import {getAssignedCardSortKey, getCardFeedIcon, getPlaidInstitutionIconUrl, isExpensifyCard, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils';
2525
import Log from '@libs/Log';
2626
import Navigation from '@libs/Navigation/Navigation';
2727
import {formatPaymentMethods} from '@libs/PaymentUtils';
@@ -219,7 +219,8 @@ function PaymentMethodList({
219219
const assignedCards = Object.values(isLoadingCardList ? {} : (cardList ?? {}))
220220
// Filter by active cards associated with a domain
221221
.filter((card) => !!card.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.includes(card.state ?? 0));
222-
const assignedCardsSorted = lodashSortBy(assignedCards, (card) => !isExpensifyCard(card));
222+
223+
const assignedCardsSorted = lodashSortBy(assignedCards, getAssignedCardSortKey);
223224

224225
const assignedCardsGrouped: PaymentMethodItem[] = [];
225226
assignedCardsSorted.forEach((card) => {

tests/unit/CardUtilsTest.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import lodashSortBy from 'lodash/sortBy';
12
import type {OnyxCollection} from 'react-native-onyx';
23
import type IllustrationsType from '@styles/theme/illustrations/types';
34
import type * as Illustrations from '@src/components/Icon/Illustrations';
@@ -8,6 +9,7 @@ import {
89
filterInactiveCards,
910
flatAllCardsList,
1011
formatCardExpiration,
12+
getAssignedCardSortKey,
1113
getBankCardDetailsImage,
1214
getBankName,
1315
getCardDescription,
@@ -1138,4 +1140,27 @@ describe('CardUtils', () => {
11381140
expect(description).toBe('Test');
11391141
});
11401142
});
1143+
1144+
describe('Expensify card sort comparator', () => {
1145+
it('should not change the order of non-Expensify cards', () => {
1146+
const cardList = {
1147+
10: {cardID: 10, bank: 'chase'}, // non-Expensify
1148+
11: {cardID: 11, bank: 'chase'}, // non-Expensify
1149+
} as unknown as CardList;
1150+
1151+
const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey);
1152+
expect(sorted.map((r: Card) => r.cardID)).toEqual([10, 11]);
1153+
});
1154+
1155+
it('places physical Expensify card before its virtual sibling', () => {
1156+
const cardList = {
1157+
10: {cardID: 10, bank: CONST.EXPENSIFY_CARD.BANK, nameValuePairs: {isVirtual: true}}, // Expensify virtual
1158+
11: {cardID: 11, bank: CONST.EXPENSIFY_CARD.BANK}, // Expensify physical
1159+
99: {cardID: 99, bank: 'chase'}, // non-Expensify
1160+
} as unknown as CardList;
1161+
1162+
const sorted = lodashSortBy(Object.values(cardList), getAssignedCardSortKey);
1163+
expect(sorted.map((r: Card) => r.cardID)).toEqual([11, 10, 99]);
1164+
});
1165+
});
11411166
});

0 commit comments

Comments
 (0)