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

[CP Staging] Revert "[Travel] [Refactor] Create a new shared component for AddressPage" #42697

Merged
merged 1 commit into from
May 28, 2024
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
1 change: 0 additions & 1 deletion src/components/AddressForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ function AddressForm({
InputComponent={CountrySelector}
inputID={INPUT_IDS.COUNTRY}
value={country}
onValueChange={onAddressChanged}
shouldSaveDraft={shouldSaveDraft}
/>
</View>
Expand Down
28 changes: 4 additions & 24 deletions src/components/CountrySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {useIsFocused} from '@react-navigation/native';
import React, {forwardRef, useEffect, useRef} from 'react';
import type {ForwardedRef} from 'react';
import type {View} from 'react-native';
import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import type {MaybePhraseKey} from '@libs/Localize';
Expand Down Expand Up @@ -33,38 +32,19 @@ type CountrySelectorProps = {
function CountrySelector({errorText = '', value: countryCode, onInputChange = () => {}, onBlur}: CountrySelectorProps, ref: ForwardedRef<View>) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {country: countryFromUrl} = useGeographicalStateAndCountryFromRoute();

const title = countryCode ? translate(`allCountries.${countryCode}`) : '';
const countryTitleDescStyle = title.length === 0 ? styles.textNormal : null;

const didOpenContrySelector = useRef(false);
const isFocused = useIsFocused();
useEffect(() => {
// Check if the country selector was opened and no value was selected, triggering onBlur to display an error
if (isFocused && didOpenContrySelector.current) {
didOpenContrySelector.current = false;
if (!countryFromUrl) {
onBlur?.();
}
}

// If no country is selected from the URL, exit the effect early to avoid further processing.
if (!countryFromUrl) {
if (!isFocused || !didOpenContrySelector.current) {
return;
}

// If a country is selected, invoke `onInputChange` to update the form and clear any validation errors related to the country selection.
if (onInputChange) {
onInputChange(countryFromUrl);
}

// Clears the `country` parameter from the URL to ensure the component country is driven by the parent component rather than URL parameters.
// This helps prevent issues where the component might not update correctly if the country is controlled by both the parent and the URL.
Navigation.setParams({country: undefined});

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [countryFromUrl, isFocused, onBlur]);
didOpenContrySelector.current = false;
onBlur?.();
}, [isFocused, onBlur]);

useEffect(() => {
// This will cause the form to revalidate and remove any error related to country name
Expand Down
4 changes: 2 additions & 2 deletions src/components/StateSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
import React, {useEffect, useRef} from 'react';
import type {ForwardedRef} from 'react';
import type {View} from 'react-native';
import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute';
import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import type {MaybePhraseKey} from '@libs/Localize';
Expand Down Expand Up @@ -44,7 +44,7 @@ function StateSelector(
) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {state: stateFromUrl} = useGeographicalStateAndCountryFromRoute();
const stateFromUrl = useGeographicalStateFromRoute();

const didOpenStateSelector = useRef(false);
const isFocused = useIsFocused();
Expand Down
27 changes: 0 additions & 27 deletions src/hooks/useGeographicalStateAndCountryFromRoute.ts

This file was deleted.

23 changes: 23 additions & 0 deletions src/hooks/useGeographicalStateFromRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {useRoute} from '@react-navigation/native';
import type {ParamListBase, RouteProp} from '@react-navigation/native';
import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';

type CustomParamList = ParamListBase & Record<string, Record<string, string>>;
type State = keyof typeof COMMON_CONST.STATES;

/**
* Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`.
* Example 1: Url: https://new.expensify.com/settings/profile/address?state=MO Returns: MO
* Example 2: Url: https://new.expensify.com/settings/profile/address?state=ASDF Returns: undefined
* Example 3: Url: https://new.expensify.com/settings/profile/address Returns: undefined
* Example 4: Url: https://new.expensify.com/settings/profile/address?state=MO-hash-a12341 Returns: MO
*/
export default function useGeographicalStateFromRoute(stateParamName = 'state'): State | undefined {
const route = useRoute<RouteProp<CustomParamList, string>>();
const stateFromUrlTemp = route.params?.[stateParamName] as string | undefined;

if (!stateFromUrlTemp) {
return;
}
return COMMON_CONST.STATES[stateFromUrlTemp as State]?.stateISO;
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.PROFILE.TIMEZONE_SELECT]: () => require('../../../../pages/settings/Profile/TimezoneSelectPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.LEGAL_NAME]: () => require('../../../../pages/settings/Profile/PersonalDetails/LegalNamePage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.DATE_OF_BIRTH]: () => require('../../../../pages/settings/Profile/PersonalDetails/DateOfBirthPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY]: () => require('../../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.ADDRESS_STATE]: () => require('../../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: () => require('../../../../pages/settings/Profile/Contacts/ContactMethodsPage').default as React.ComponentType,
Expand All @@ -193,7 +193,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: () => require('../../../../pages/settings/AppDownloadLinks').default as React.ComponentType,
[SCREENS.SETTINGS.CONSOLE]: () => require('../../../../pages/settings/AboutPage/ConsolePage').default as React.ComponentType,
[SCREENS.SETTINGS.SHARE_LOG]: () => require('../../../../pages/settings/AboutPage/ShareLogPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: () => require('../../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: () => require('../../../../pages/settings/Wallet/ActivatePhysicalCardPage').default as React.ComponentType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
import React, {useCallback, useEffect, useState} from 'react';
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';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute';
import useLocalize from '@hooks/useLocalize';
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 type {FormOnyxValues} from '@src/components/Form/types';
import CONST from '@src/CONST';
import type {Country} from '@src/CONST';
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';

type AddressPageProps = {
type AddressPageOnyxProps = {
/** User's private personal details */
address?: Address;
privatePersonalDetails: OnyxEntry<PrivatePersonalDetails>;
/** Whether app is loading */
isLoadingApp: OnyxEntry<boolean>;
/** Function to call when address form is submitted */
updateAddress: (values: FormOnyxValues<typeof ONYXKEYS.FORMS.HOME_ADDRESS_FORM>) => void;
/** Title of address page */
title: string;
};

function AddressPage({title, address, updateAddress, isLoadingApp = true}: AddressPageProps) {
type AddressPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.PROFILE.ADDRESS> & AddressPageOnyxProps;

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

function AddressPage({privatePersonalDetails, route, isLoadingApp = true}: AddressPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const address = useMemo(() => privatePersonalDetails?.address, [privatePersonalDetails]);
const countryFromUrlTemp = route?.params?.country;

// Check if country is valid
const {street, street2} = address ?? {};
const countryFromUrl = CONST.ALL_COUNTRIES[countryFromUrlTemp as keyof typeof CONST.ALL_COUNTRIES] ? countryFromUrlTemp : '';
const stateFromUrl = useGeographicalStateFromRoute();
const [currentCountry, setCurrentCountry] = useState(address?.country);
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);
Expand Down Expand Up @@ -72,13 +97,27 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true}: Addre
setZipcode(addressPart);
}, []);

useEffect(() => {
if (!countryFromUrl) {
return;
}
handleAddressChange(countryFromUrl, 'country');
}, [countryFromUrl, handleAddressChange]);

useEffect(() => {
if (!stateFromUrl) {
return;
}
handleAddressChange(stateFromUrl, 'state');
}, [handleAddressChange, stateFromUrl]);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
testID={AddressPage.displayName}
>
<HeaderWithBackButton
title={title}
title={translate('privatePersonalDetails.address')}
shouldShowBackButton
onBackButtonPress={() => Navigation.goBack()}
/>
Expand All @@ -93,7 +132,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true}: Addre
country={currentCountry}
onAddressChanged={handleAddressChange}
state={state}
street1={street}
street1={street1}
street2={street2}
zip={zipcode}
/>
Expand All @@ -104,4 +143,11 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true}: Addre

AddressPage.displayName = 'AddressPage';

export default AddressPage;
export default withOnyx<AddressPageProps, AddressPageOnyxProps>({
privatePersonalDetails: {
key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS,
},
isLoadingApp: {
key: ONYXKEYS.IS_LOADING_APP,
},
})(AddressPage);

This file was deleted.

Loading
Loading