Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the Assigned Cards Section of the Wallet Page #38998

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0515ffb
make wallet page cards grouped by domain
SzymczakJ Mar 26, 2024
ecdcf48
Merge branch 'main' into @szymczak/update-cards-section
SzymczakJ Mar 26, 2024
804005a
Merge branch 'main' into @szymczak/update-cards-section
SzymczakJ Mar 28, 2024
71c86a2
fix navigation on lost/fraud/actvate card flows
SzymczakJ Mar 28, 2024
13674cd
fix navigation on get physical card flow
SzymczakJ Mar 28, 2024
e5d8cc5
fix PR comments
SzymczakJ Apr 2, 2024
a26532f
Merge branch 'main' into @szymczak/update-cards-section
SzymczakJ Apr 5, 2024
ecd7c73
Merge branch 'main' into @szymczak/update-cards-section
SzymczakJ Apr 5, 2024
6bf60e9
make card row title depend on cardTitle when card is admin assigned
SzymczakJ Apr 5, 2024
a10788e
adjust expensifyCardPage title
SzymczakJ Apr 5, 2024
bc9ce69
fix PR comments
SzymczakJ Apr 12, 2024
c19eb59
fix PR comments
SzymczakJ Apr 16, 2024
ae6f359
fix types
SzymczakJ Apr 16, 2024
24321a9
Merge branch 'main' into @szymczak/update-cards-section
SzymczakJ Apr 19, 2024
70d02e4
fix nameValuePairs bugs
SzymczakJ Apr 19, 2024
55bed2d
fix PR comments
SzymczakJ Apr 22, 2024
5b9ad8c
Merge branch 'main' into @szymczak/update-cards-section
SzymczakJ Apr 22, 2024
a8454dc
fix button margins
SzymczakJ Apr 22, 2024
4f6ee19
add spanish translations
SzymczakJ Apr 22, 2024
0486463
merge main
SzymczakJ Apr 24, 2024
f989cea
fix PR comments
SzymczakJ Apr 24, 2024
f114894
fix navigation bugs
SzymczakJ Apr 24, 2024
d542600
remove redundant types
SzymczakJ Apr 24, 2024
c7a0f12
fix ExpensifyCardPage bug
SzymczakJ Apr 25, 2024
0458752
fix pr comments
SzymczakJ Apr 26, 2024
f915905
fix lint errors
SzymczakJ Apr 26, 2024
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
16 changes: 8 additions & 8 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ const ROUTES = {
SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links',
SETTINGS_WALLET: 'settings/wallet',
SETTINGS_WALLET_DOMAINCARD: {
route: 'settings/wallet/card/:domain',
getRoute: (domain: string) => `settings/wallet/card/${domain}` as const,
route: 'settings/wallet/card/:domain/:cardId',
getRoute: (domain: string, cardId: string) => `settings/wallet/card/${domain}/${cardId}` as const,
},
SETTINGS_REPORT_FRAUD: {
route: 'settings/wallet/card/:domain/report-virtual-fraud',
getRoute: (domain: string) => `settings/wallet/card/${domain}/report-virtual-fraud` as const,
route: 'settings/wallet/card/:domain/:cardId/report-virtual-fraud',
getRoute: (domain: string, cardId: string) => `settings/wallet/card/${domain}/${cardId}/report-virtual-fraud` as const,
},
SETTINGS_WALLET_CARD_GET_PHYSICAL_NAME: {
route: 'settings/wallet/card/:domain/get-physical/name',
Expand Down Expand Up @@ -117,12 +117,12 @@ const ROUTES = {
SETTINGS_WALLET_TRANSFER_BALANCE: 'settings/wallet/transfer-balance',
SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT: 'settings/wallet/choose-transfer-account',
SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED: {
route: 'settings/wallet/card/:domain/report-card-lost-or-damaged',
getRoute: (domain: string) => `settings/wallet/card/${domain}/report-card-lost-or-damaged` as const,
route: 'settings/wallet/card/:domain/:cardId/report-card-lost-or-damaged',
getRoute: (domain: string, cardId: string) => `settings/wallet/card/${domain}/${cardId}/report-card-lost-or-damaged` as const,
},
SETTINGS_WALLET_CARD_ACTIVATE: {
route: 'settings/wallet/card/:domain/activate',
getRoute: (domain: string) => `settings/wallet/card/${domain}/activate` as const,
route: 'settings/wallet/card/:domain/:cardId/activate',
getRoute: (domain: string, cardId: string) => `settings/wallet/card/${domain}/${cardId}/activate` as const,
},
SETTINGS_LEGAL_NAME: 'settings/profile/legal-name',
SETTINGS_DATE_OF_BIRTH: 'settings/profile/date-of-birth',
Expand Down
12 changes: 12 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,18 @@ export default {
cardPage: {
expensifyCard: 'Expensify Card',
availableSpend: 'Remaining limit',
smartLimit: {
name: 'Smart limit',
title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card, and the limit will reset as your submitted expenses are approved.`,
},
fixedLimit: {
name: 'Fixed limit',
title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card, and then it will deactivate.`,
},
monthlyLimit: {
name: 'Monthly limit',
title: (formattedLimit: string) => `You can spend up to ${formattedLimit} on this card per month. The limit will reset on the 1st day of each calendar month.`,
},
virtualCardNumber: 'Virtual card number',
physicalCardNumber: 'Physical card number',
getPhysicalCard: 'Get physical card',
Expand Down
12 changes: 12 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,18 @@ export default {
cardPage: {
expensifyCard: 'Tarjeta Expensify',
availableSpend: 'Límite restante',
smartLimit: {
name: 'Smart limit',
Copy link
Contributor

Choose a reason for hiding this comment

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

Did we miss translation for the names i.e. Smart limit, Fixed limit, Monthly limit? Or was this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Asked @grgia about it 😄

title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta al mes. El límite se restablecerá el primer día del mes.`,
},
fixedLimit: {
name: 'Fixed limit',
title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta, luego se desactivará.`,
},
monthlyLimit: {
name: 'Monthly limit',
title: (formattedLimit: string) => `Puedes gastar hasta ${formattedLimit} en esta tarjeta y el límite se restablecerá a medida que se aprueben tus gastos.`,
},
virtualCardNumber: 'Número de la tarjeta virtual',
physicalCardNumber: 'Número de la tarjeta física',
getPhysicalCard: 'Obtener tarjeta física',
Expand Down
30 changes: 25 additions & 5 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,26 @@ type SettingsNavigatorParamList = {
};
[SCREENS.SETTINGS.WALLET.ROOT]: undefined;
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: undefined;
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: undefined;
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: undefined;
[SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: undefined;
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: {
/** domain passed via route /settings/wallet/card/:domain/:card */
domain: string;
/** cardId passed via route /settings/wallet/card/:domain/:card */
cardId: string;
};
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: {
/** domain passed via route /settings/wallet/card/:domain/:card */
domain: string;
/** cardId passed via route /settings/wallet/card/:domain/:card */
cardId: string;
};
[SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: {
/** domain passed via route /settings/wallet/card/:domain/:card */
domain: string;
/** cardId passed via route /settings/wallet/card/:domain/:card */
cardId: string;
};
[SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME]: {
/** domain passed via route /settings/wallet/card/:domain */
/** domain passed via route /settings/wallet/card/:domain/:card */
domain: string;
};
[SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.PHONE]: {
Expand Down Expand Up @@ -250,7 +265,12 @@ type SettingsNavigatorParamList = {
backTo: Routes;
};
[SCREENS.SETTINGS.TWO_FACTOR_AUTH]: BackToParams;
[SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: undefined;
[SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: {
/** domain passed via route /settings/wallet/card/:domain/:card */
domain: string;
/** cardId passed via route /settings/wallet/card/:domain/:card */
Copy link
Contributor

Choose a reason for hiding this comment

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

While I understand this style of comment is already used in this file, I still find it kinda redundant.

Do you really think its needed? and if yes then could we write it in some more human-friendly way?
For example there is a comment in line 141
/** Currently selected country */
^ that reads much better

so something like // the domain of the current card etc

cardId: string;
};
[SCREENS.KEYBOARD_SHORTCUTS]: undefined;
[SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined;
[SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: {
Expand Down
28 changes: 13 additions & 15 deletions src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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 type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import * as CardSettings from '@userActions/Card';
import CONST from '@src/CONST';
Expand All @@ -34,15 +34,15 @@ type ActivatePhysicalCardPageOnyxProps = {
cardList: OnyxEntry<Record<string, Card>>;
};

type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps<PublicScreensParamList, typeof SCREENS.TRANSITION_BETWEEN_APPS>;
type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.WALLET.CARD_ACTIVATE>;
grgia marked this conversation as resolved.
Show resolved Hide resolved

const LAST_FOUR_DIGITS_LENGTH = 4;
const MAGIC_INPUT_MIN_HEIGHT = 86;

function ActivatePhysicalCardPage({
cardList,
route: {
params: {domain = ''},
params: {domain = '', cardId = ''},
},
}: ActivatePhysicalCardPageProps) {
const theme = useTheme();
Expand All @@ -56,8 +56,7 @@ function ActivatePhysicalCardPage({
const [lastPressedDigit, setLastPressedDigit] = useState('');

const domainCards = CardUtils.getDomainCards(cardList)[domain] ?? [];
const physicalCard = domainCards.find((card) => !card.isVirtual);
const cardID = physicalCard?.cardID ?? 0;
const physicalCard = domainCards.find((card) => card?.state === CONST.EXPENSIFY_CARD.STATE.NOT_ACTIVATED);
Copy link
Contributor

Choose a reason for hiding this comment

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

Don’t we have to also add the check for isVirtual here as we are trying to find a physicalCard? We do this check here. Also, the name seems to be confusing here. Something like inactivePhysicalCard or inactiveCard seems more correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only physical Card can be in NOT_ACTIVATED state. I was also wondering about, ff it's more readable to add isVirtual check, but decided to skip it since it's redundant. What do you think?

Copy link
Contributor

@rojiphil rojiphil Apr 22, 2024

Choose a reason for hiding this comment

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

Ok. Got it. Since NOT_ACTIVATED state is only for physical card, it is fine not to add a redundant check.

const cardError = ErrorUtils.getLatestErrorMessage(physicalCard ?? {});

const activateCardCodeInputRef = useRef<MagicCodeInputHandle>(null);
Expand All @@ -66,19 +65,18 @@ function ActivatePhysicalCardPage({
* If state of the card is CONST.EXPENSIFY_CARD.STATE.OPEN, navigate to card details screen.
*/
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (physicalCard?.isLoading || cardList?.[cardID]?.state !== CONST.EXPENSIFY_CARD.STATE.OPEN) {
if (physicalCard?.state !== CONST.EXPENSIFY_CARD.STATE.OPEN || physicalCard?.isLoading) {
return;
}

Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain));
}, [cardID, cardList, domain, physicalCard?.isLoading]);
Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain, cardId));
}, [cardId, cardList, domain, physicalCard?.isLoading, physicalCard?.state]);

useEffect(
() => () => {
CardSettings.clearCardListErrors(cardID);
CardSettings.clearCardListErrors(physicalCard?.cardID ?? 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm do we want to default to 0 here? It looks like it would merge 0 into the cardList

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not though? would 0: {} work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could just do if and an early return. Dunno why I didn't think of that. Defaulting to 0 would probably cause bugs 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah let's do an early return instead

},
[cardID],
[physicalCard?.cardID],
);

/**
Expand All @@ -96,7 +94,7 @@ function ActivatePhysicalCardPage({
setFormError('');

if (cardError) {
CardSettings.clearCardListErrors(cardID);
CardSettings.clearCardListErrors(physicalCard?.cardID ?? 0);
}

setLastFourDigits(text);
Expand All @@ -110,8 +108,8 @@ function ActivatePhysicalCardPage({
return;
}

CardSettings.activatePhysicalExpensifyCard(lastFourDigits, cardID);
}, [lastFourDigits, cardID]);
CardSettings.activatePhysicalExpensifyCard(lastFourDigits, physicalCard?.cardID ?? 0);
}, [lastFourDigits, physicalCard?.cardID]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should default to a negative ID

Copy link
Contributor Author

@SzymczakJ SzymczakJ Apr 12, 2024

Choose a reason for hiding this comment

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

We could but isn't 0 also a wrong card ID and would cause CardSettings.activatePhysicalExpensifyCard to fail(which is what we want when physical card doesn't have cardID). Do we have some special negative number for such cases?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah 0 is an incorrect card ID. I think we've historically used -1 but I need to double check. But let me know if you'll try returning early instead


if (isEmptyObject(physicalCard)) {
return <NotFoundPage />;
Expand All @@ -120,7 +118,7 @@ function ActivatePhysicalCardPage({
return (
<IllustratedHeaderPageLayout
title={translate('activateCardPage.activateCard')}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain))}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain, cardId))}
backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.PREFERENCES.ROOT].backgroundColor}
illustration={LottieAnimations.Magician}
scrollViewContainerStyles={[styles.mnh100]}
Expand Down
25 changes: 11 additions & 14 deletions src/pages/settings/Wallet/Card/BaseGetPhysicalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,24 +109,25 @@ function BaseGetPhysicalCard({
const styles = useThemeStyles();
const isRouteSet = useRef(false);

const domainCards = CardUtils.getDomainCards(cardList)[domain] || [];
const physicalCard = domainCards.find((card) => !card?.isVirtual && card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED);
const cardID = physicalCard?.cardID ?? 0;

useEffect(() => {
if (isRouteSet.current || !privatePersonalDetails || !cardList) {
return;
}

const domainCards = CardUtils.getDomainCards(cardList)[domain] || [];
const physicalCard = domainCards.find((card) => !card?.isVirtual);

// When there are no cards for the specified domain, user is redirected to the wallet page
if (domainCards.length === 0) {
if (domainCards.length === 0 || !physicalCard) {
Navigation.goBack(ROUTES.SETTINGS_WALLET);
return;
}

// When there's no physical card or it exists but it doesn't have the required state for this flow,
// redirect user to the espensify card page
if (!physicalCard || physicalCard.state !== CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED) {
Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain));
if (physicalCard.state !== CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED) {
Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain, physicalCard.cardID.toString()));
return;
}

Expand All @@ -141,25 +142,21 @@ function BaseGetPhysicalCard({
// Redirect user to previous steps of the flow if he hasn't finished them yet
GetPhysicalCardUtils.setCurrentRoute(currentRoute, domain, GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues));
isRouteSet.current = true;
}, [cardList, currentRoute, domain, draftValues, loginList, privatePersonalDetails]);
}, [cardList, currentRoute, domain, domainCards.length, draftValues, loginList, physicalCard, privatePersonalDetails]);

const onSubmit = useCallback(() => {
const updatedPrivatePersonalDetails = GetPhysicalCardUtils.getUpdatedPrivatePersonalDetails(draftValues);
// If the current step of the get physical card flow is the confirmation page
if (isConfirmation) {
const domainCards = CardUtils.getDomainCards(cardList)[domain];
const physicalCard = domainCards.find((card) => !card?.isVirtual);
const cardID = physicalCard?.cardID ?? 0;

Wallet.requestPhysicalExpensifyCard(cardID, 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(domain));
Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain, cardID.toString()));
return;
}
GetPhysicalCardUtils.goToNextPhysicalCardRoute(domain, updatedPrivatePersonalDetails);
}, [cardList, domain, draftValues, isConfirmation, session?.authToken]);
}, [cardID, domain, draftValues, isConfirmation, session?.authToken]);
return (
<ScreenWrapper
shouldEnablePickerAvoiding={false}
Expand All @@ -168,7 +165,7 @@ function BaseGetPhysicalCard({
>
<HeaderWithBackButton
title={title}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain))}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain, cardID.toString()))}
/>
<Text style={[styles.textHeadline, styles.mh5, styles.mb5]}>{headline}</Text>
{renderContent({onSubmit, submitButtonText, children, onValidate})}
Expand Down
Loading
Loading