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 'SettingsProfileDetails' page to TypeScript #35307

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dcd834b
ref: started working on migration of Profile pages
kubabutkiewicz Jan 26, 2024
7d88436
ref: migrate AddressPage
kubabutkiewicz Jan 26, 2024
2f7f0be
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Jan 29, 2024
75fb85f
ref: migrate DateOfBirthPage to TS
kubabutkiewicz Jan 29, 2024
5d83e8e
ref: move PersonalDetailInitialPage to TS, fix Errors type
kubabutkiewicz Jan 29, 2024
5f26b89
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Jan 30, 2024
71889c6
fix: typecheck
kubabutkiewicz Jan 30, 2024
9e87935
fix: typecheck
kubabutkiewicz Jan 30, 2024
ff249fd
fix: remove comment
kubabutkiewicz Jan 30, 2024
7cffd1c
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 2, 2024
07aeee9
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 5, 2024
d7fa1f4
fix: resolve comments
kubabutkiewicz Feb 5, 2024
9165006
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 7, 2024
3b37ccc
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 8, 2024
7d667af
fix: resolved comments
kubabutkiewicz Feb 8, 2024
d8c3281
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 9, 2024
20c98fd
fix: typecheck
kubabutkiewicz Feb 9, 2024
9a85683
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 12, 2024
e09dcae
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 15, 2024
c627a1a
fix: typecheck
kubabutkiewicz Feb 15, 2024
296c724
fix: resolve comments
kubabutkiewicz Feb 15, 2024
87dc6e3
Merge branch 'main' into ts-migration/SettingsProfileDetails
kubabutkiewicz Feb 20, 2024
f0c8f25
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 22, 2024
60f0c39
fix: typecheck
kubabutkiewicz Feb 22, 2024
9e89e34
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 23, 2024
6d9b146
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Feb 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
2 changes: 1 addition & 1 deletion src/components/Form/FormWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function FormWrapper({
buttonText={submitButtonText}
isAlertVisible={((!isEmptyObject(errors) || !isEmptyObject(formState?.errorFields)) && !shouldHideFixErrorsAlert) || !!errorMessage}
isLoading={!!formState?.isLoading}
message={isEmptyObject(formState?.errorFields) ? errorMessage : undefined}
message={typeof errorMessage === 'string' && isEmptyObject(formState?.errorFields) ? errorMessage : undefined}
onSubmit={onSubmit}
footerContent={footerContent}
onFixTheErrorsLinkPressed={onFixTheErrorsLinkPressed}
Expand Down
9 changes: 7 additions & 2 deletions src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import type AmountForm from '@components/AmountForm';
import type AmountTextInput from '@components/AmountTextInput';
import type CheckboxWithLabel from '@components/CheckboxWithLabel';
import type CountrySelector from '@components/CountrySelector';
import type DatePicker from '@components/DatePicker';
import type Picker from '@components/Picker';
import type RadioButtons from '@components/RadioButtons';
import type SingleChoiceQuestion from '@components/SingleChoiceQuestion';
import type StatePicker from '@components/StatePicker';
import type TextInput from '@components/TextInput';
import type ValuePicker from '@components/ValuePicker';
import type {MaybePhraseKey} from '@libs/Localize';
import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker';
import type {TranslationPaths} from '@src/languages/types';
import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS';
import type {BaseForm} from '@src/types/form/Form';

Expand All @@ -35,6 +36,7 @@ type ValidInputs =
| typeof AmountForm
| typeof BusinessTypePicker
| typeof StatePicker
| typeof DatePicker
| typeof ValuePicker
| typeof RadioButtons;

Expand Down Expand Up @@ -64,6 +66,9 @@ type InputComponentBaseProps<TValue extends ValueTypeKey = ValueTypeKey> = Input
isFocused?: boolean;
measureLayout?: (ref: unknown, callback: MeasureLayoutOnSuccessCallback) => void;
focus?: () => void;
label?: string;
minDate?: Date;
maxDate?: Date;
onTouched?: (event: GestureResponderEvent) => void;
onBlur?: (event: FocusEvent | NativeSyntheticEvent<TextInputFocusEventData>) => void;
onPressOut?: (event: GestureResponderEvent) => void;
Expand Down Expand Up @@ -121,6 +126,6 @@ type FormProps<TFormID extends OnyxFormKey = OnyxFormKey> = {

type InputRefs = Record<string, MutableRefObject<InputComponentBaseProps>>;

type FormInputErrors<TFormID extends OnyxFormKey = OnyxFormKey> = Partial<Record<FormOnyxKeys<TFormID>, TranslationPaths>>;
type FormInputErrors<TFormID extends OnyxFormKey = OnyxFormKey> = Partial<Record<FormOnyxKeys<TFormID>, MaybePhraseKey>>;

export type {FormProps, ValidInputs, InputComponentValueProps, FormValue, ValueTypeKey, FormOnyxValues, FormOnyxKeys, FormInputErrors, InputRefs, InputComponentBaseProps, ValueTypeMap};
10 changes: 9 additions & 1 deletion src/libs/GetPhysicalCardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,15 @@ function getUpdatedPrivatePersonalDetails(draftValues: OnyxEntry<GetPhysicalCard
legalFirstName,
legalLastName,
phoneNumber,
address: {street: PersonalDetailsUtils.getFormattedStreet(addressLine1, addressLine2), city, country, state, zip: zipPostCode},
address: {
street: PersonalDetailsUtils.getFormattedStreet(addressLine1, addressLine2),
city,
country,
state,
zip: zipPostCode,
addressLine1: addressLine1 ?? '',
zipPostCode: zipPostCode ?? '',
},
};
}

Expand Down
9 changes: 7 additions & 2 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,13 @@ type SettingsNavigatorParamList = {
[SCREENS.SETTINGS.PROFILE.TIMEZONE_SELECT]: undefined;
[SCREENS.SETTINGS.PROFILE.LEGAL_NAME]: undefined;
[SCREENS.SETTINGS.PROFILE.DATE_OF_BIRTH]: undefined;
[SCREENS.SETTINGS.PROFILE.ADDRESS]: undefined;
[SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY]: undefined;
[SCREENS.SETTINGS.PROFILE.ADDRESS]: {
country?: string;
};
[SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY]: {
backTo?: Routes;
country: string;
};
[SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: {
backTo: Routes;
};
Expand Down
4 changes: 2 additions & 2 deletions src/libs/PersonalDetailsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ function getStreetLines(street = '') {
* @param privatePersonalDetails - details object
* @returns - formatted address
*/
function getFormattedAddress(privatePersonalDetails: PrivatePersonalDetails): string {
const {address} = privatePersonalDetails;
kubabutkiewicz marked this conversation as resolved.
Show resolved Hide resolved
function getFormattedAddress(privatePersonalDetails: OnyxEntry<PrivatePersonalDetails>): string {
const {address} = privatePersonalDetails ?? {};
const [street1, street2] = getStreetLines(address?.street);
const formattedAddress =
formatPiece(street1) + formatPiece(street2) + formatPiece(address?.city) + formatPiece(address?.state) + formatPiece(address?.zip) + formatPiece(address?.country);
Expand Down
5 changes: 3 additions & 2 deletions src/libs/ValidationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {OnyxFormKey} from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';
import * as CardUtils from './CardUtils';
import DateUtils from './DateUtils';
import type {MaybePhraseKey} from './Localize';
import * as LoginUtils from './LoginUtils';
import {parsePhoneNumber} from './PhoneNumber';
import StringUtils from './StringUtils';
Expand Down Expand Up @@ -190,7 +191,7 @@ function meetsMaximumAgeRequirement(date: string): boolean {
/**
* Validate that given date is in a specified range of years before now.
*/
function getAgeRequirementError(date: string, minimumAge: number, maximumAge: number): string | Array<string | Record<string, string>> {
function getAgeRequirementError(date: string, minimumAge: number, maximumAge: number): MaybePhraseKey {
const currentDate = startOfDay(new Date());
const testDate = parse(date, CONST.DATE.FNS_FORMAT_STRING, currentDate);

Expand Down Expand Up @@ -360,7 +361,7 @@ function isValidPersonName(value: string) {
/**
* Checks if the provided string includes any of the provided reserved words
*/
function doesContainReservedWord(value: string, reservedWords: string[]): boolean {
function doesContainReservedWord(value: string, reservedWords: typeof CONST.DISPLAY_NAME.RESERVED_NAMES): boolean {
const valueToCheck = value.trim().toLowerCase();
return reservedWords.some((reservedWord) => valueToCheck.includes(reservedWord.toLowerCase()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import AddressForm from '@components/AddressForm';
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
Expand All @@ -10,66 +10,47 @@ import useLocalize from '@hooks/useLocalize';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import * as PersonalDetails from '@userActions/PersonalDetails';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {PrivatePersonalDetails} from '@src/types/onyx';
import type {Address} from '@src/types/onyx/PrivatePersonalDetails';

const propTypes = {
/* Onyx Props */

type AddressPageOnyxProps = {
/** User's private personal details */
privatePersonalDetails: PropTypes.shape({
/** User's home address */
address: PropTypes.shape({
street: PropTypes.string,
city: PropTypes.string,
state: PropTypes.string,
zip: PropTypes.string,
country: PropTypes.string,
}),
}),

/** Route from navigation */
route: PropTypes.shape({
/** Params from the route */
params: PropTypes.shape({
/** Currently selected country */
country: PropTypes.string,
}),
}).isRequired,
privatePersonalDetails: OnyxEntry<PrivatePersonalDetails>;
};

const defaultProps = {
privatePersonalDetails: {
address: {
street: '',
city: '',
state: '',
zip: '',
country: '',
},
},
};
type AddressPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.PROFILE.ADDRESS> & AddressPageOnyxProps;

/**
* Submit form to update user's first and last legal name
* @param {Object} values - form input values
* @param values - form input values
*/
function updateAddress(values) {
PersonalDetails.updateAddress(values.addressLine1.trim(), values.addressLine2.trim(), values.city.trim(), values.state.trim(), values.zipPostCode.trim().toUpperCase(), values.country);
function updateAddress(values: Address) {
PersonalDetails.updateAddress(
values.addressLine1.trim(),
values.addressLine2?.trim() ?? '',
values.city.trim(),
values.state.trim(),
values?.zipPostCode.trim().toUpperCase(),
values.country,
);
}

function AddressPage({privatePersonalDetails, route}) {
function AddressPage({privatePersonalDetails, route}: AddressPageProps) {
const styles = useThemeStyles();
usePrivatePersonalDetails();
const {translate} = useLocalize();
const address = useMemo(() => lodashGet(privatePersonalDetails, 'address') || {}, [privatePersonalDetails]);
const countryFromUrl = lodashGet(route, 'params.country');
const [currentCountry, setCurrentCountry] = useState(address.country);
const isLoadingPersonalDetails = lodashGet(privatePersonalDetails, 'isLoading', true);
const [street1, street2] = (address.street || '').split('\n');
const [state, setState] = useState(address.state);
const [city, setCity] = useState(address.city);
const [zipcode, setZipcode] = useState(address.zip);
const address = useMemo(() => privatePersonalDetails?.address, [privatePersonalDetails]);
const countryFromUrl = route.params?.country;
const [currentCountry, setCurrentCountry] = useState(address?.country);
const isLoadingPersonalDetails = privatePersonalDetails?.isLoading ?? true;
const [street1, street2] = (address?.street ?? '').split('\n');
const [state, setState] = useState(address?.state);
const [city, setCity] = useState(address?.city);
const [zipcode, setZipcode] = useState(address?.zip);

useEffect(() => {
if (!address) {
Expand All @@ -81,7 +62,7 @@ function AddressPage({privatePersonalDetails, route}) {
setZipcode(address.zip);
}, [address]);

const handleAddressChange = useCallback((value, key) => {
const handleAddressChange = useCallback((value: string, key: keyof Address) => {
if (key !== 'country' && key !== 'state' && key !== 'city' && key !== 'zipPostCode') {
return;
}
Expand Down Expand Up @@ -143,11 +124,9 @@ function AddressPage({privatePersonalDetails, route}) {
);
}

AddressPage.propTypes = propTypes;
AddressPage.defaultProps = defaultProps;
AddressPage.displayName = 'AddressPage';

export default withOnyx({
export default withOnyx<AddressPageProps, AddressPageOnyxProps>({
privatePersonalDetails: {
key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,31 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useMemo, useState} from 'react';
import _ from 'underscore';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import useLocalize from '@hooks/useLocalize';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import type {CountryData} from '@libs/searchCountryOptions';
import searchCountryOptions from '@libs/searchCountryOptions';
import StringUtils from '@libs/StringUtils';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type {Route} from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';

const propTypes = {
/** Route from navigation */
route: PropTypes.shape({
/** Params from the route */
params: PropTypes.shape({
/** Currently selected country */
country: PropTypes.string,
type CountrySelectionPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY>;

/** Route to navigate back after selecting a currency */
backTo: PropTypes.string,
}),
}).isRequired,

/** Navigation from react-navigation */
navigation: PropTypes.shape({
/** getState function retrieves the current navigation state from react-navigation's navigation property */
getState: PropTypes.func.isRequired,
}).isRequired,
};

function CountrySelectionPage({route, navigation}) {
function CountrySelectionPage({route, navigation}: CountrySelectionPageProps) {
const [searchValue, setSearchValue] = useState('');
const {translate} = useLocalize();
const currentCountry = lodashGet(route, 'params.country');
const currentCountry = route.params.country;

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually I think params are always there
image
So this should be fine to left it as it is

const countries = useMemo(
() =>
_.map(_.keys(CONST.ALL_COUNTRIES), (countryISO) => {
const countryName = translate(`allCountries.${countryISO}`);
Object.keys(CONST.ALL_COUNTRIES).map((countryISO) => {
const countryName = translate(`allCountries.${countryISO}` as TranslationPaths);
return {
value: countryISO,
keyForList: countryISO,
Expand All @@ -56,19 +41,18 @@ function CountrySelectionPage({route, navigation}) {
const headerMessage = searchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : '';

const selectCountry = useCallback(
(option) => {
const backTo = lodashGet(route, 'params.backTo', '');

(option: CountryData) => {
const backTo = route.params.backTo ?? '';
// Check the navigation state and "backTo" parameter to decide navigation behavior
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here. route.params always exists?

if (navigation.getState().routes.length === 1 && _.isEmpty(backTo)) {
if (navigation.getState().routes.length === 1 && !backTo) {
// If there is only one route and "backTo" is empty, go back in navigation
Navigation.goBack();
} else if (!_.isEmpty(backTo) && navigation.getState().routes.length === 1) {
} else if (!!backTo && navigation.getState().routes.length === 1) {
// If "backTo" is not empty and there is only one route, go back to the specific route defined in "backTo" with a country parameter
Navigation.goBack(`${route.params.backTo}?country=${option.value}`);
Navigation.goBack(`${route.params.backTo}?country=${option.value}` as Route);
} else {
// Otherwise, navigate to the specific route defined in "backTo" with a country parameter
Navigation.navigate(`${route.params.backTo}?country=${option.value}`);
Navigation.navigate(`${route.params.backTo}?country=${option.value}` as Route);
}
},
[route, navigation],
Expand All @@ -83,9 +67,9 @@ function CountrySelectionPage({route, navigation}) {
title={translate('common.country')}
shouldShowBackButton
onBackButtonPress={() => {
const backTo = lodashGet(route, 'params.backTo', '');
const backToRoute = backTo ? `${backTo}?country=${currentCountry}` : '';
Navigation.goBack(backToRoute);
const backTo = route.params.backTo ?? '';
const backToRoute = backTo ? (`${backTo}?country=${currentCountry}` as const) : '';
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here ^

Navigation.goBack(backToRoute as Route);
}}
/>

Expand All @@ -105,6 +89,5 @@ function CountrySelectionPage({route, navigation}) {
}

CountrySelectionPage.displayName = 'CountrySelectionPage';
CountrySelectionPage.propTypes = propTypes;

export default CountrySelectionPage;
Loading
Loading