diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 78d5f4d54888..5ce59a7be3f0 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -85,6 +85,16 @@ export default {
SETTINGS_PERSONAL_DETAILS_LEGAL_NAME: 'settings/profile/personal-details/legal-name',
SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH: 'settings/profile/personal-details/date-of-birth',
SETTINGS_PERSONAL_DETAILS_ADDRESS: 'settings/profile/personal-details/address',
+ SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY: {
+ route: 'settings/profile/personal-details/address/country',
+ getRoute: (country: string, backTo?: string) => {
+ let route = `settings/profile/personal-details/address/country?country=${country}`;
+ if (backTo) {
+ route += `&backTo=${encodeURIComponent(backTo)}`;
+ }
+ return route;
+ }
+ },
SETTINGS_CONTACT_METHODS: 'settings/profile/contact-methods',
SETTINGS_CONTACT_METHOD_DETAILS: {
route: 'settings/profile/contact-methods/:contactMethod/details',
diff --git a/src/components/CountryPicker/CountrySelectorModal.js b/src/components/CountryPicker/CountrySelectorModal.js
deleted file mode 100644
index 6c6cd19af0c7..000000000000
--- a/src/components/CountryPicker/CountrySelectorModal.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import _ from 'underscore';
-import React, {useMemo, useEffect} from 'react';
-import PropTypes from 'prop-types';
-import CONST from '../../CONST';
-import useLocalize from '../../hooks/useLocalize';
-import HeaderWithBackButton from '../HeaderWithBackButton';
-import SelectionList from '../SelectionList';
-import Modal from '../Modal';
-import ScreenWrapper from '../ScreenWrapper';
-import styles from '../../styles/styles';
-import searchCountryOptions from '../../libs/searchCountryOptions';
-import StringUtils from '../../libs/StringUtils';
-
-const propTypes = {
- /** Whether the modal is visible */
- isVisible: PropTypes.bool.isRequired,
-
- /** Country value selected */
- currentCountry: PropTypes.string,
-
- /** Function to call when the user selects a Country */
- onCountrySelected: PropTypes.func,
-
- /** Function to call when the user closes the Country modal */
- onClose: PropTypes.func,
-
- /** The search value from the selection list */
- searchValue: PropTypes.string.isRequired,
-
- /** Function to call when the user types in the search input */
- setSearchValue: PropTypes.func.isRequired,
-};
-
-const defaultProps = {
- currentCountry: '',
- onClose: () => {},
- onCountrySelected: () => {},
-};
-
-function CountrySelectorModal({currentCountry, isVisible, onClose, onCountrySelected, setSearchValue, searchValue}) {
- const {translate} = useLocalize();
-
- useEffect(() => {
- if (isVisible) {
- return;
- }
- setSearchValue('');
- }, [isVisible, setSearchValue]);
-
- const countries = useMemo(
- () =>
- _.map(_.keys(CONST.ALL_COUNTRIES), (countryISO) => {
- const countryName = translate(`allCountries.${countryISO}`);
- return {
- value: countryISO,
- keyForList: countryISO,
- text: countryName,
- isSelected: currentCountry === countryISO,
- searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`),
- };
- }),
- [translate, currentCountry],
- );
-
- const searchResults = searchCountryOptions(searchValue, countries);
- const headerMessage = searchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : '';
-
- return (
-
-
-
-
-
-
- );
-}
-
-CountrySelectorModal.propTypes = propTypes;
-CountrySelectorModal.defaultProps = defaultProps;
-CountrySelectorModal.displayName = 'CountrySelectorModal';
-
-export default CountrySelectorModal;
diff --git a/src/components/CountryPicker/index.js b/src/components/CountryPicker/index.js
deleted file mode 100644
index 8f5c89b1bce8..000000000000
--- a/src/components/CountryPicker/index.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import React, {useState} from 'react';
-import {View} from 'react-native';
-import PropTypes from 'prop-types';
-import styles from '../../styles/styles';
-import MenuItemWithTopDescription from '../MenuItemWithTopDescription';
-import useLocalize from '../../hooks/useLocalize';
-import CountrySelectorModal from './CountrySelectorModal';
-import FormHelpMessage from '../FormHelpMessage';
-import refPropTypes from '../refPropTypes';
-
-const propTypes = {
- /** Form Error description */
- errorText: PropTypes.string,
-
- /** Country to display */
- value: PropTypes.string,
-
- /** Callback to call when the input changes */
- onInputChange: PropTypes.func,
-
- /** A ref to forward to MenuItemWithTopDescription */
- forwardedRef: refPropTypes,
-};
-
-const defaultProps = {
- value: undefined,
- forwardedRef: undefined,
- errorText: '',
- onInputChange: () => {},
-};
-
-function CountryPicker({value, errorText, onInputChange, forwardedRef}) {
- const {translate} = useLocalize();
- const [isPickerVisible, setIsPickerVisible] = useState(false);
- const [searchValue, setSearchValue] = useState('');
-
- const showPickerModal = () => {
- setIsPickerVisible(true);
- };
-
- const hidePickerModal = () => {
- setIsPickerVisible(false);
- };
-
- const updateCountryInput = (country) => {
- if (country.value !== value) {
- onInputChange(country.value);
- }
- hidePickerModal();
- };
-
- const title = value ? translate(`allCountries.${value}`) : '';
- const descStyle = title.length === 0 ? styles.textNormal : null;
-
- return (
-
-
-
-
-
-
-
- );
-}
-
-CountryPicker.propTypes = propTypes;
-CountryPicker.defaultProps = defaultProps;
-CountryPicker.displayName = 'CountryPicker';
-
-export default React.forwardRef((props, ref) => (
-
-));
diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js
new file mode 100644
index 000000000000..2788f3cea8e3
--- /dev/null
+++ b/src/components/CountrySelector.js
@@ -0,0 +1,77 @@
+import React, {useEffect} from 'react';
+import PropTypes from 'prop-types';
+import {View} from 'react-native';
+import styles from '../styles/styles';
+import Navigation from '../libs/Navigation/Navigation';
+import ROUTES from '../ROUTES';
+import useLocalize from '../hooks/useLocalize';
+import MenuItemWithTopDescription from './MenuItemWithTopDescription';
+import FormHelpMessage from './FormHelpMessage';
+
+const propTypes = {
+ /** Form error text. e.g when no country is selected */
+ errorText: PropTypes.string,
+
+ /** Callback called when the country changes. */
+ onInputChange: PropTypes.func.isRequired,
+
+ /** Current selected country */
+ value: PropTypes.string,
+
+ /** inputID used by the Form component */
+ // eslint-disable-next-line react/no-unused-prop-types
+ inputID: PropTypes.string.isRequired,
+
+ /** React ref being forwarded to the MenuItemWithTopDescription */
+ forwardedRef: PropTypes.func,
+};
+
+const defaultProps = {
+ errorText: '',
+ value: undefined,
+ forwardedRef: () => {},
+};
+
+function CountrySelector({errorText, value: countryCode, onInputChange, forwardedRef}) {
+ const {translate} = useLocalize();
+
+ const title = countryCode ? translate(`allCountries.${countryCode}`) : '';
+ const countryTitleDescStyle = title.length === 0 ? styles.textNormal : null;
+
+ useEffect(() => {
+ // This will cause the form to revalidate and remove any error related to country name
+ onInputChange(countryCode);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [countryCode]);
+
+ return (
+
+ {
+ const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, '');
+ Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.getRoute(countryCode, activeRoute));
+ }}
+ />
+
+
+
+
+ );
+}
+
+CountrySelector.propTypes = propTypes;
+CountrySelector.defaultProps = defaultProps;
+CountrySelector.displayName = 'CountrySelector';
+
+export default React.forwardRef((props, ref) => (
+
+));
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
index fc284f566c80..1dfa4d7b707b 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js
@@ -126,6 +126,7 @@ const SettingsModalStackNavigator = createModalStackNavigator({
Settings_PersonalDetails_LegalName: () => require('../../../pages/settings/Profile/PersonalDetails/LegalNamePage').default,
Settings_PersonalDetails_DateOfBirth: () => require('../../../pages/settings/Profile/PersonalDetails/DateOfBirthPage').default,
Settings_PersonalDetails_Address: () => require('../../../pages/settings/Profile/PersonalDetails/AddressPage').default,
+ Settings_PersonalDetails_Address_Country: () => require('../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default,
Settings_ContactMethods: () => require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default,
Settings_ContactMethodDetails: () => require('../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default,
Settings_NewContactMethod: () => require('../../../pages/settings/Profile/Contacts/NewContactMethodPage').default,
diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index 116e1b9d55a5..99771fd34558 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -151,6 +151,10 @@ export default {
path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS,
exact: true,
},
+ Settings_PersonalDetails_Address_Country: {
+ path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.route,
+ exact: true,
+ },
Settings_TwoFactorAuth: {
path: ROUTES.SETTINGS_2FA,
exact: true,
diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js
index 782756024d8f..7dadbb4608d9 100644
--- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js
+++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js
@@ -1,6 +1,6 @@
import lodashGet from 'lodash/get';
import _ from 'underscore';
-import React, {useState, useCallback} from 'react';
+import React, {useState, useCallback, useEffect} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
@@ -15,13 +15,13 @@ import styles from '../../../../styles/styles';
import * as PersonalDetails from '../../../../libs/actions/PersonalDetails';
import * as ValidationUtils from '../../../../libs/ValidationUtils';
import AddressSearch from '../../../../components/AddressSearch';
-import CountryPicker from '../../../../components/CountryPicker';
import StatePicker from '../../../../components/StatePicker';
import Navigation from '../../../../libs/Navigation/Navigation';
import ROUTES from '../../../../ROUTES';
import useLocalize from '../../../../hooks/useLocalize';
import usePrivatePersonalDetails from '../../../../hooks/usePrivatePersonalDetails';
import FullscreenLoadingIndicator from '../../../../components/FullscreenLoadingIndicator';
+import CountrySelector from '../../../../components/CountrySelector';
const propTypes = {
/* Onyx Props */
@@ -37,6 +37,15 @@ const propTypes = {
country: PropTypes.string,
}),
}),
+
+ /** Route from navigation */
+ route: PropTypes.shape({
+ /** Params from the route */
+ params: PropTypes.shape({
+ /** Currently selected country */
+ country: PropTypes.string,
+ }),
+ }).isRequired,
};
const defaultProps = {
@@ -59,10 +68,11 @@ function updateAddress(values) {
PersonalDetails.updateAddress(values.addressLine1.trim(), values.addressLine2.trim(), values.city.trim(), values.state.trim(), values.zipPostCode.trim().toUpperCase(), values.country);
}
-function AddressPage({privatePersonalDetails}) {
+function AddressPage({privatePersonalDetails, route}) {
usePrivatePersonalDetails();
const {translate} = useLocalize();
- const [currentCountry, setCurrentCountry] = useState(PersonalDetails.getCountryISO(lodashGet(privatePersonalDetails, 'address.country')));
+ const countryFromUrl = lodashGet(route, 'params.country');
+ const [currentCountry, setCurrentCountry] = useState(countryFromUrl || PersonalDetails.getCountryISO(lodashGet(privatePersonalDetails, 'address.country')));
const isUSAForm = currentCountry === CONST.COUNTRY.US;
const zipSampleFormat = lodashGet(CONST.COUNTRY_ZIP_REGEX_DATA, [currentCountry, 'samples'], '');
const zipFormat = translate('common.zipCodeExampleFormat', {zipSampleFormat});
@@ -116,7 +126,7 @@ function AddressPage({privatePersonalDetails}) {
return errors;
}, []);
- const handleAddressChange = (value, key) => {
+ const handleAddressChange = useCallback((value, key) => {
if (key !== 'country' && key !== 'state') {
return;
}
@@ -126,7 +136,14 @@ function AddressPage({privatePersonalDetails}) {
return;
}
setState(value);
- };
+ }, []);
+
+ useEffect(() => {
+ if (!countryFromUrl || countryFromUrl === currentCountry) {
+ return;
+ }
+ handleAddressChange(countryFromUrl, 'country');
+ }, [countryFromUrl, handleAddressChange, currentCountry]);
return (
-
diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js
new file mode 100644
index 000000000000..741974776df1
--- /dev/null
+++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js
@@ -0,0 +1,107 @@
+import React, {useState, useMemo, useCallback} from 'react';
+import PropTypes from 'prop-types';
+import _ from 'underscore';
+import lodashGet from 'lodash/get';
+import Navigation from '../../../../libs/Navigation/Navigation';
+import ScreenWrapper from '../../../../components/ScreenWrapper';
+import HeaderWithBackButton from '../../../../components/HeaderWithBackButton';
+import SelectionList from '../../../../components/SelectionList';
+import searchCountryOptions from '../../../../libs/searchCountryOptions';
+import StringUtils from '../../../../libs/StringUtils';
+import CONST from '../../../../CONST';
+import useLocalize from '../../../../hooks/useLocalize';
+
+const propTypes = {
+ /** Route from navigation */
+ route: PropTypes.shape({
+ /** Params from the route */
+ params: PropTypes.shape({
+ /** Currently selected country */
+ country: PropTypes.string,
+
+ /** 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}) {
+ const [searchValue, setSearchValue] = useState('');
+ const {translate} = useLocalize();
+ const currentCountry = lodashGet(route, 'params.country');
+
+ const countries = useMemo(
+ () =>
+ _.map(_.keys(CONST.ALL_COUNTRIES), (countryISO) => {
+ const countryName = translate(`allCountries.${countryISO}`);
+ return {
+ value: countryISO,
+ keyForList: countryISO,
+ text: countryName,
+ isSelected: currentCountry === countryISO,
+ searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`),
+ };
+ }),
+ [translate, currentCountry],
+ );
+
+ const searchResults = searchCountryOptions(searchValue, countries);
+ const headerMessage = searchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : '';
+
+ const selectCountry = useCallback(
+ (option) => {
+ const backTo = lodashGet(route, 'params.backTo', '');
+
+ // Check the navigation state and "backTo" parameter to decide navigation behavior
+ if (navigation.getState().routes.length === 1 && _.isEmpty(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) {
+ // 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}`);
+ } else {
+ // Otherwise, navigate to the specific route defined in "backTo" with a country parameter
+ Navigation.navigate(`${route.params.backTo}?country=${option.value}`);
+ }
+ },
+ [route, navigation],
+ );
+
+ return (
+
+ {
+ const backTo = lodashGet(route, 'params.backTo', '');
+ const backToRoute = backTo ? `${backTo}?country=${currentCountry}` : '';
+ Navigation.goBack(backToRoute);
+ }}
+ />
+
+
+
+ );
+}
+
+CountrySelectionPage.displayName = 'CountrySelectionPage';
+CountrySelectionPage.propTypes = propTypes;
+
+export default CountrySelectionPage;