Skip to content

Commit d137ce8

Browse files
authored
Merge pull request #42697 from Expensify/revert-41970-travel/address-page-refactor
[CP Staging] Revert "[Travel] [Refactor] Create a new shared component for AddressPage"
2 parents 96425fa + aed0318 commit d137ce8

File tree

9 files changed

+182
-153
lines changed

9 files changed

+182
-153
lines changed

src/components/AddressForm.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ function AddressForm({
180180
InputComponent={CountrySelector}
181181
inputID={INPUT_IDS.COUNTRY}
182182
value={country}
183-
onValueChange={onAddressChanged}
184183
shouldSaveDraft={shouldSaveDraft}
185184
/>
186185
</View>

src/components/CountrySelector.tsx

+4-24
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {useIsFocused} from '@react-navigation/native';
22
import React, {forwardRef, useEffect, useRef} from 'react';
33
import type {ForwardedRef} from 'react';
44
import type {View} from 'react-native';
5-
import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute';
65
import useLocalize from '@hooks/useLocalize';
76
import useThemeStyles from '@hooks/useThemeStyles';
87
import type {MaybePhraseKey} from '@libs/Localize';
@@ -33,38 +32,19 @@ type CountrySelectorProps = {
3332
function CountrySelector({errorText = '', value: countryCode, onInputChange = () => {}, onBlur}: CountrySelectorProps, ref: ForwardedRef<View>) {
3433
const styles = useThemeStyles();
3534
const {translate} = useLocalize();
36-
const {country: countryFromUrl} = useGeographicalStateAndCountryFromRoute();
3735

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

4139
const didOpenContrySelector = useRef(false);
4240
const isFocused = useIsFocused();
4341
useEffect(() => {
44-
// Check if the country selector was opened and no value was selected, triggering onBlur to display an error
45-
if (isFocused && didOpenContrySelector.current) {
46-
didOpenContrySelector.current = false;
47-
if (!countryFromUrl) {
48-
onBlur?.();
49-
}
50-
}
51-
52-
// If no country is selected from the URL, exit the effect early to avoid further processing.
53-
if (!countryFromUrl) {
42+
if (!isFocused || !didOpenContrySelector.current) {
5443
return;
5544
}
56-
57-
// If a country is selected, invoke `onInputChange` to update the form and clear any validation errors related to the country selection.
58-
if (onInputChange) {
59-
onInputChange(countryFromUrl);
60-
}
61-
62-
// Clears the `country` parameter from the URL to ensure the component country is driven by the parent component rather than URL parameters.
63-
// This helps prevent issues where the component might not update correctly if the country is controlled by both the parent and the URL.
64-
Navigation.setParams({country: undefined});
65-
66-
// eslint-disable-next-line react-hooks/exhaustive-deps
67-
}, [countryFromUrl, isFocused, onBlur]);
45+
didOpenContrySelector.current = false;
46+
onBlur?.();
47+
}, [isFocused, onBlur]);
6848

6949
useEffect(() => {
7050
// This will cause the form to revalidate and remove any error related to country name

src/components/StateSelector.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
33
import React, {useEffect, useRef} from 'react';
44
import type {ForwardedRef} from 'react';
55
import type {View} from 'react-native';
6-
import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute';
6+
import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute';
77
import useLocalize from '@hooks/useLocalize';
88
import useThemeStyles from '@hooks/useThemeStyles';
99
import type {MaybePhraseKey} from '@libs/Localize';
@@ -44,7 +44,7 @@ function StateSelector(
4444
) {
4545
const styles = useThemeStyles();
4646
const {translate} = useLocalize();
47-
const {state: stateFromUrl} = useGeographicalStateAndCountryFromRoute();
47+
const stateFromUrl = useGeographicalStateFromRoute();
4848

4949
const didOpenStateSelector = useRef(false);
5050
const isFocused = useIsFocused();

src/hooks/useGeographicalStateAndCountryFromRoute.ts

-27
This file was deleted.
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {useRoute} from '@react-navigation/native';
2+
import type {ParamListBase, RouteProp} from '@react-navigation/native';
3+
import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
4+
5+
type CustomParamList = ParamListBase & Record<string, Record<string, string>>;
6+
type State = keyof typeof COMMON_CONST.STATES;
7+
8+
/**
9+
* Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`.
10+
* Example 1: Url: https://new.expensify.com/settings/profile/address?state=MO Returns: MO
11+
* Example 2: Url: https://new.expensify.com/settings/profile/address?state=ASDF Returns: undefined
12+
* Example 3: Url: https://new.expensify.com/settings/profile/address Returns: undefined
13+
* Example 4: Url: https://new.expensify.com/settings/profile/address?state=MO-hash-a12341 Returns: MO
14+
*/
15+
export default function useGeographicalStateFromRoute(stateParamName = 'state'): State | undefined {
16+
const route = useRoute<RouteProp<CustomParamList, string>>();
17+
const stateFromUrlTemp = route.params?.[stateParamName] as string | undefined;
18+
19+
if (!stateFromUrlTemp) {
20+
return;
21+
}
22+
return COMMON_CONST.STATES[stateFromUrlTemp as State]?.stateISO;
23+
}

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
179179
[SCREENS.SETTINGS.PROFILE.TIMEZONE_SELECT]: () => require('../../../../pages/settings/Profile/TimezoneSelectPage').default as React.ComponentType,
180180
[SCREENS.SETTINGS.PROFILE.LEGAL_NAME]: () => require('../../../../pages/settings/Profile/PersonalDetails/LegalNamePage').default as React.ComponentType,
181181
[SCREENS.SETTINGS.PROFILE.DATE_OF_BIRTH]: () => require('../../../../pages/settings/Profile/PersonalDetails/DateOfBirthPage').default as React.ComponentType,
182-
[SCREENS.SETTINGS.PROFILE.ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default as React.ComponentType,
182+
[SCREENS.SETTINGS.PROFILE.ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType,
183183
[SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY]: () => require('../../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default as React.ComponentType,
184184
[SCREENS.SETTINGS.PROFILE.ADDRESS_STATE]: () => require('../../../../pages/settings/Profile/PersonalDetails/StateSelectionPage').default as React.ComponentType,
185185
[SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: () => require('../../../../pages/settings/Profile/Contacts/ContactMethodsPage').default as React.ComponentType,
@@ -193,7 +193,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
193193
[SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: () => require('../../../../pages/settings/AppDownloadLinks').default as React.ComponentType,
194194
[SCREENS.SETTINGS.CONSOLE]: () => require('../../../../pages/settings/AboutPage/ConsolePage').default as React.ComponentType,
195195
[SCREENS.SETTINGS.SHARE_LOG]: () => require('../../../../pages/settings/AboutPage/ShareLogPage').default as React.ComponentType,
196-
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/PersonalAddressPage').default as React.ComponentType,
196+
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType,
197197
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../../pages/settings/Wallet/ExpensifyCardPage').default as React.ComponentType,
198198
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: () => require('../../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default as React.ComponentType,
199199
[SCREENS.SETTINGS.WALLET.CARD_ACTIVATE]: () => require('../../../../pages/settings/Wallet/ActivatePhysicalCardPage').default as React.ComponentType,

src/pages/AddressPage.tsx src/pages/settings/Profile/PersonalDetails/AddressPage.tsx

+58-12
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,60 @@
1-
import React, {useCallback, useEffect, useState} from 'react';
1+
import type {StackScreenProps} from '@react-navigation/stack';
2+
import React, {useCallback, useEffect, useMemo, useState} from 'react';
23
import type {OnyxEntry} from 'react-native-onyx';
4+
import {withOnyx} from 'react-native-onyx';
35
import AddressForm from '@components/AddressForm';
46
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
57
import HeaderWithBackButton from '@components/HeaderWithBackButton';
68
import ScreenWrapper from '@components/ScreenWrapper';
9+
import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute';
710
import useLocalize from '@hooks/useLocalize';
811
import useThemeStyles from '@hooks/useThemeStyles';
912
import Navigation from '@libs/Navigation/Navigation';
13+
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
14+
import * as PersonalDetails from '@userActions/PersonalDetails';
1015
import type {FormOnyxValues} from '@src/components/Form/types';
16+
import CONST from '@src/CONST';
1117
import type {Country} from '@src/CONST';
1218
import ONYXKEYS from '@src/ONYXKEYS';
19+
import type SCREENS from '@src/SCREENS';
20+
import type {PrivatePersonalDetails} from '@src/types/onyx';
1321
import type {Address} from '@src/types/onyx/PrivatePersonalDetails';
1422

15-
type AddressPageProps = {
23+
type AddressPageOnyxProps = {
1624
/** User's private personal details */
17-
address?: Address;
25+
privatePersonalDetails: OnyxEntry<PrivatePersonalDetails>;
1826
/** Whether app is loading */
1927
isLoadingApp: OnyxEntry<boolean>;
20-
/** Function to call when address form is submitted */
21-
updateAddress: (values: FormOnyxValues<typeof ONYXKEYS.FORMS.HOME_ADDRESS_FORM>) => void;
22-
/** Title of address page */
23-
title: string;
2428
};
2529

26-
function AddressPage({title, address, updateAddress, isLoadingApp = true}: AddressPageProps) {
30+
type AddressPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.PROFILE.ADDRESS> & AddressPageOnyxProps;
31+
32+
/**
33+
* Submit form to update user's first and last legal name
34+
* @param values - form input values
35+
*/
36+
function updateAddress(values: FormOnyxValues<typeof ONYXKEYS.FORMS.HOME_ADDRESS_FORM>) {
37+
PersonalDetails.updateAddress(
38+
values.addressLine1?.trim() ?? '',
39+
values.addressLine2?.trim() ?? '',
40+
values.city.trim(),
41+
values.state.trim(),
42+
values?.zipPostCode?.trim().toUpperCase() ?? '',
43+
values.country,
44+
);
45+
}
46+
47+
function AddressPage({privatePersonalDetails, route, isLoadingApp = true}: AddressPageProps) {
2748
const styles = useThemeStyles();
2849
const {translate} = useLocalize();
50+
const address = useMemo(() => privatePersonalDetails?.address, [privatePersonalDetails]);
51+
const countryFromUrlTemp = route?.params?.country;
2952

3053
// Check if country is valid
31-
const {street, street2} = address ?? {};
54+
const countryFromUrl = CONST.ALL_COUNTRIES[countryFromUrlTemp as keyof typeof CONST.ALL_COUNTRIES] ? countryFromUrlTemp : '';
55+
const stateFromUrl = useGeographicalStateFromRoute();
3256
const [currentCountry, setCurrentCountry] = useState(address?.country);
57+
const [street1, street2] = (address?.street ?? '').split('\n');
3358
const [state, setState] = useState(address?.state);
3459
const [city, setCity] = useState(address?.city);
3560
const [zipcode, setZipcode] = useState(address?.zip);
@@ -72,13 +97,27 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true}: Addre
7297
setZipcode(addressPart);
7398
}, []);
7499

100+
useEffect(() => {
101+
if (!countryFromUrl) {
102+
return;
103+
}
104+
handleAddressChange(countryFromUrl, 'country');
105+
}, [countryFromUrl, handleAddressChange]);
106+
107+
useEffect(() => {
108+
if (!stateFromUrl) {
109+
return;
110+
}
111+
handleAddressChange(stateFromUrl, 'state');
112+
}, [handleAddressChange, stateFromUrl]);
113+
75114
return (
76115
<ScreenWrapper
77116
includeSafeAreaPaddingBottom={false}
78117
testID={AddressPage.displayName}
79118
>
80119
<HeaderWithBackButton
81-
title={title}
120+
title={translate('privatePersonalDetails.address')}
82121
shouldShowBackButton
83122
onBackButtonPress={() => Navigation.goBack()}
84123
/>
@@ -93,7 +132,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true}: Addre
93132
country={currentCountry}
94133
onAddressChanged={handleAddressChange}
95134
state={state}
96-
street1={street}
135+
street1={street1}
97136
street2={street2}
98137
zip={zipcode}
99138
/>
@@ -104,4 +143,11 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true}: Addre
104143

105144
AddressPage.displayName = 'AddressPage';
106145

107-
export default AddressPage;
146+
export default withOnyx<AddressPageProps, AddressPageOnyxProps>({
147+
privatePersonalDetails: {
148+
key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS,
149+
},
150+
isLoadingApp: {
151+
key: ONYXKEYS.IS_LOADING_APP,
152+
},
153+
})(AddressPage);

src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx

-61
This file was deleted.

0 commit comments

Comments
 (0)