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

[TS migration] Migrate SettingsWallet page to TypeScript #34716

Merged
merged 42 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a7fbd97
TS migration SettingsWallet
tienifr Jan 18, 2024
47ea3ac
use StackScreenProps type
tienifr Jan 18, 2024
9506d82
add commen
tienifr Jan 18, 2024
a15c596
fix lint
tienifr Jan 18, 2024
b848510
TS migrate ActivatePhysicalCardPage
tienifr Jan 18, 2024
a532fc8
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Jan 18, 2024
14951ed
TS migrate AddDebitCardPage
tienifr Jan 18, 2024
44853e7
fix lint
tienifr Jan 18, 2024
40b2601
TS migrate ReportCardLostPage
tienifr Jan 18, 2024
73095a2
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Jan 18, 2024
ff51244
TS migrate TransferBalancePage
tienifr Jan 18, 2024
137ff59
fix lint
tienifr Jan 18, 2024
48d470a
TS migrate ExpensifyCardPage
tienifr Jan 18, 2024
65446ab
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Jan 19, 2024
7b7906c
Fix Form TS error
tienifr Jan 19, 2024
3b5d9ee
Merge branch 'main' into ts-migration/32003
tienifr Jan 19, 2024
9cfedfe
Fix lint and type
tienifr Jan 19, 2024
021be80
remove redundant check
tienifr Jan 19, 2024
47d7f81
Merge branch 'main' into ts-migration/32003
tienifr Jan 23, 2024
9365b3d
add comment and fix minor errors
tienifr Jan 23, 2024
67001c6
Merge branch 'main' into ts-migration/32003
tienifr Jan 25, 2024
d3a9997
Merge branch 'main' into ts-migration/32003
tienifr Jan 26, 2024
5ff1569
Merge branch 'main' into ts-migration/32003
tienifr Feb 16, 2024
6368b04
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Feb 16, 2024
3c59cfe
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Feb 17, 2024
7acd0af
Resolve conflicts
tienifr Feb 17, 2024
6439290
fix type error
tienifr Feb 17, 2024
23bc723
fix lint
tienifr Feb 17, 2024
48d5b09
remove unnecessary changes
tienifr Feb 17, 2024
6e01c60
fix type error
tienifr Feb 17, 2024
c15a756
fix lint
tienifr Feb 17, 2024
923e1a0
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Feb 19, 2024
779a231
use or instead of nullish coalescing
tienifr Feb 19, 2024
aced7a7
Merge branch 'main' into ts-migration/32003
tienifr Feb 20, 2024
8ffbbe1
Fix typecheck
tienifr Feb 20, 2024
79b29cc
Merge branch 'main' into ts-migration/32003
tienifr Feb 22, 2024
d6c3570
fix crash in activate physical card page
tienifr Feb 22, 2024
9d27a52
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Feb 22, 2024
b606e21
Merge branch 'main' into ts-migration/32003
tienifr Feb 23, 2024
1ddf781
Merge branch 'main' of https://github.com/tienifr/App into ts-migrati…
tienifr Feb 23, 2024
befbda6
fix typecheck
tienifr Feb 23, 2024
b8caf36
fix: selected icon not show for selected account
tienifr Feb 25, 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
2 changes: 1 addition & 1 deletion src/components/AddressSearch/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ function AddressSearch(
if (inputID) {
onInputChange?.(text);
} else {
onInputChange({street: text});
onInputChange?.({street: text});
}
// If the text is empty and we have no predefined places, we set displayListViewBorder to false to prevent UI flickering
if (!text && !predefinedPlaces.length) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/AddressSearch/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type AddressSearchProps = {
defaultValue?: string;

/** A callback function when the value of this field has changed */
onInputChange: (value: string | number | RenamedInputKeysProps | StreetValue, key?: string) => void;
onInputChange?: (value: string | number | RenamedInputKeysProps | StreetValue, key?: string) => void;

/** A callback function when an address has been auto-selected */
onPress?: (props: OnPressProps) => void;
Expand All @@ -77,7 +77,7 @@ type AddressSearchProps = {
predefinedPlaces?: Place[];

/** A map of inputID key names */
renamedInputKeys: RenamedInputKeysProps;
renamedInputKeys?: RenamedInputKeysProps;

/** Maximum number of characters allowed in search input */
maxInputLength?: number;
Expand Down
2 changes: 1 addition & 1 deletion src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ type EnterMagicCodeParams = {contactMethod: string};

type TransferParams = {amount: string};

type InstantSummaryParams = {rate: number; minAmount: number};
type InstantSummaryParams = {rate: string; minAmount: string};

type NotYouParams = {user: string};

Expand Down
18 changes: 7 additions & 11 deletions src/libs/GetPhysicalCardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,20 @@ function setCurrentRoute(currentRoute: string, domain: string, privatePersonalDe
* @param privatePersonalDetails
* @returns
*/
function getUpdatedDraftValues(
draftValues: OnyxEntry<GetPhysicalCardForm>,
privatePersonalDetails: OnyxEntry<PrivatePersonalDetails>,
loginList: OnyxEntry<LoginList>,
): Partial<GetPhysicalCardForm> {
function getUpdatedDraftValues(draftValues: OnyxEntry<GetPhysicalCardForm>, privatePersonalDetails: OnyxEntry<PrivatePersonalDetails>, loginList: OnyxEntry<LoginList>): GetPhysicalCardForm {
const {address, legalFirstName, legalLastName, phoneNumber} = privatePersonalDetails ?? {};

return {
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
// we do not need to use nullish coalescing here because we want to allow empty strings
legalFirstName: draftValues?.legalFirstName || legalFirstName,
legalLastName: draftValues?.legalLastName || legalLastName,
addressLine1: draftValues?.addressLine1 || address?.street.split('\n')[0],
legalFirstName: draftValues?.legalFirstName || legalFirstName || '',
legalLastName: draftValues?.legalLastName || legalLastName || '',
addressLine1: draftValues?.addressLine1 || address?.street.split('\n')[0] || '',
addressLine2: draftValues?.addressLine2 || address?.street.split('\n')[1] || '',
city: draftValues?.city || address?.city,
country: draftValues?.country || address?.country,
city: draftValues?.city || address?.city || '',
country: draftValues?.country || address?.country || '',
phoneNumber: draftValues?.phoneNumber || phoneNumber || UserUtils.getSecondaryPhoneLogin(loginList) || '',
state: draftValues?.state || address?.state,
state: draftValues?.state || address?.state || '',
zipPostCode: draftValues?.zipPostCode || address?.zip || '',
/* eslint-enable @typescript-eslint/prefer-nullish-coalescing */
};
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ type PublicScreensParamList = {
shortLivedAuthToken?: string;
shortLivedToken?: string;
exitTo?: Routes | HybridAppRoute;
domain?: Routes;
};
[SCREENS.VALIDATE_LOGIN]: {
accountID: string;
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,4 @@ function revealVirtualCardDetails(cardID: number): Promise<Response> {
}

export {requestReplacementExpensifyCard, activatePhysicalExpensifyCard, clearCardListErrors, reportVirtualExpensifyCardFraud, revealVirtualCardDetails};
export type {ReplacementReason};
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
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 type {MagicCodeInputHandle} from '@components/MagicCodeInput';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
Expand All @@ -19,42 +19,32 @@ 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 assignedCardPropTypes from './assignedCardPropTypes';
import type {Card} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

const propTypes = {
/* Onyx Props */

/** The details about the Expensify cards */
cardList: PropTypes.objectOf(assignedCardPropTypes),

/** Navigation route context info provided by react navigation */
route: PropTypes.shape({
params: PropTypes.shape({
/** domain passed via route /settings/wallet/card/:domain */
domain: PropTypes.string,
}),
}).isRequired,
type ActivatePhysicalCardPageOnyxProps = {
/** Card list propTypes */
cardList: OnyxEntry<Record<string, Card>>;
};

const defaultProps = {
cardList: {},
};
type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps<PublicScreensParamList, typeof SCREENS.TRANSITION_BETWEEN_APPS>;

const LAST_FOUR_DIGITS_LENGTH = 4;
const MAGIC_INPUT_MIN_HEIGHT = 86;

function ActivatePhysicalCardPage({
cardList,
route: {
params: {domain},
params: {domain = ''},
},
}) {
}: ActivatePhysicalCardPageProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {isExtraSmallScreenHeight} = useWindowDimensions();
Expand All @@ -65,23 +55,24 @@ function ActivatePhysicalCardPage({
const [lastFourDigits, setLastFourDigits] = useState('');
const [lastPressedDigit, setLastPressedDigit] = useState('');

const domainCards = CardUtils.getDomainCards(cardList)[domain];
const physicalCard = _.find(domainCards, (card) => !card.isVirtual) || {};
const cardID = lodashGet(physicalCard, 'cardID', 0);
const cardError = ErrorUtils.getLatestErrorMessage(physicalCard);
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);
const activateCardCodeInputRef = useRef<MagicCodeInputHandle>(null);

/**
* If state of the card is CONST.EXPENSIFY_CARD.STATE.OPEN, navigate to card details screen.
*/
useEffect(() => {
if (physicalCard.isLoading || lodashGet(cardList, `${cardID}.state`, 0) !== CONST.EXPENSIFY_CARD.STATE.OPEN) {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (physicalCard?.isLoading || cardList?.[cardID]?.state !== CONST.EXPENSIFY_CARD.STATE.OPEN) {
return;
}

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

useEffect(
() => () => {
Expand All @@ -95,17 +86,13 @@ function ActivatePhysicalCardPage({
*
* 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.
*
* @param {String} key
*/
const updateLastPressedDigit = useCallback((key) => setLastPressedDigit(lastPressedDigit === key ? lastPressedDigit + key : key), [lastPressedDigit]);
const updateLastPressedDigit = useCallback((key: string) => setLastPressedDigit(lastPressedDigit === key ? lastPressedDigit + key : key), [lastPressedDigit]);

/**
* Handle card activation code input
*
* @param {String} text
*/
const onCodeInput = (text) => {
const onCodeInput = (text: string) => {
setFormError('');

if (cardError) {
Expand All @@ -116,7 +103,7 @@ function ActivatePhysicalCardPage({
};

const submitAndNavigateToNextPage = useCallback(() => {
activateCardCodeInputRef.current.blur();
activateCardCodeInputRef.current?.blur();

if (lastFourDigits.replace(CONST.MAGIC_CODE_EMPTY_CHAR, '').length !== LAST_FOUR_DIGITS_LENGTH) {
setFormError('activateCardPage.error.thatDidntMatch');
Expand All @@ -126,7 +113,7 @@ function ActivatePhysicalCardPage({
CardSettings.activatePhysicalExpensifyCard(lastFourDigits, cardID);
}, [lastFourDigits, cardID]);

if (_.isEmpty(physicalCard)) {
if (isEmptyObject(physicalCard)) {
return <NotFoundPage />;
}

Expand Down Expand Up @@ -161,7 +148,7 @@ function ActivatePhysicalCardPage({
<Button
success
isDisabled={isOffline}
isLoading={physicalCard.isLoading}
isLoading={physicalCard?.isLoading}
medium={isExtraSmallScreenHeight}
style={[styles.w100, styles.p5, styles.mtAuto]}
onPress={submitAndNavigateToNextPage}
Expand All @@ -172,11 +159,9 @@ function ActivatePhysicalCardPage({
);
}

ActivatePhysicalCardPage.propTypes = propTypes;
ActivatePhysicalCardPage.defaultProps = defaultProps;
ActivatePhysicalCardPage.displayName = 'ActivatePhysicalCardPage';

export default withOnyx({
export default withOnyx<ActivatePhysicalCardPageProps, ActivatePhysicalCardPageOnyxProps>({
cardList: {
key: ONYXKEYS.CARD_LIST,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import PropTypes from 'prop-types';
import React, {useEffect, useRef} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import AddressSearch from '@components/AddressSearch';
import CheckboxWithLabel from '@components/CheckboxWithLabel';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import ScreenWrapper from '@components/ScreenWrapper';
import StatePicker from '@components/StatePicker';
import Text from '@components/Text';
Expand All @@ -20,26 +22,31 @@ import * as ValidationUtils from '@libs/ValidationUtils';
import * as PaymentMethods from '@userActions/PaymentMethods';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {AddDebitCardForm} from '@src/types/form';
import INPUT_IDS from '@src/types/form/AddDebitCardForm';

const propTypes = {
/* Onyx Props */
formData: PropTypes.shape({
setupComplete: PropTypes.bool,
}),
type DebitCardPageOnyxProps = {
/** Form data propTypes */
formData: OnyxEntry<AddDebitCardForm>;
};

const defaultProps = {
formData: {
setupComplete: false,
},
};
type DebitCardPageProps = DebitCardPageOnyxProps;

const REQUIRED_FIELDS = [
INPUT_IDS.NAME_ON_CARD,
INPUT_IDS.CARD_NUMBER,
INPUT_IDS.EXPIRATION_DATE,
INPUT_IDS.SECURITY_CODE,
INPUT_IDS.ADDRESS_STREET,
INPUT_IDS.ADDRESS_ZIP_CODE,
INPUT_IDS.ADDRESS_STATE,
];

function DebitCardPage(props) {
function DebitCardPage({formData}: DebitCardPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const prevFormDataSetupComplete = usePrevious(props.formData.setupComplete);
const nameOnCardRef = useRef(null);
const prevFormDataSetupComplete = usePrevious(!!formData?.setupComplete);
const nameOnCardRef = useRef<AnimatedTextInputRef>(null);

/**
* Reset the form values on the mount and unmount so that old errors don't show when this form is displayed again.
Expand All @@ -53,20 +60,18 @@ function DebitCardPage(props) {
}, []);

useEffect(() => {
if (prevFormDataSetupComplete || !props.formData.setupComplete) {
if (prevFormDataSetupComplete || !formData?.setupComplete) {
return;
}

PaymentMethods.continueSetup();
}, [prevFormDataSetupComplete, props.formData.setupComplete]);
}, [prevFormDataSetupComplete, formData?.setupComplete]);

/**
* @param {Object} values - form input values passed by the Form component
* @returns {Boolean}
* @param values - form input values passed by the Form component
*/
const validate = (values) => {
const requiredFields = ['nameOnCard', 'cardNumber', 'expirationDate', 'securityCode', 'addressStreet', 'addressZipCode', 'addressState'];
const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);
const validate = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM> => {
const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS);

if (values.nameOnCard && !ValidationUtils.isValidLegalName(values.nameOnCard)) {
errors.nameOnCard = 'addDebitCardPage.error.invalidName';
Expand Down Expand Up @@ -101,7 +106,7 @@ function DebitCardPage(props) {

return (
<ScreenWrapper
onEntryTransitionEnd={() => nameOnCardRef.current && nameOnCardRef.current.focus()}
onEntryTransitionEnd={() => nameOnCardRef.current?.focus()}
includeSafeAreaPaddingBottom={false}
testID={DebitCardPage.displayName}
>
Expand Down Expand Up @@ -206,11 +211,9 @@ function DebitCardPage(props) {
);
}

DebitCardPage.propTypes = propTypes;
DebitCardPage.defaultProps = defaultProps;
DebitCardPage.displayName = 'DebitCardPage';

export default withOnyx({
export default withOnyx<DebitCardPageProps, DebitCardPageOnyxProps>({
formData: {
key: ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM,
},
Expand Down
Loading
Loading