Skip to content

Commit

Permalink
Merge pull request #42697 from Expensify/revert-41970-travel/address-…
Browse files Browse the repository at this point in the history
…page-refactor

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

(cherry picked from commit d137ce8)
  • Loading branch information
mountiny authored and OSBotify committed May 28, 2024
1 parent 1955918 commit 42360c6
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 153 deletions.
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);
61 changes: 0 additions & 61 deletions src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx

This file was deleted.

Loading

0 comments on commit 42360c6

Please sign in to comment.