From 0479a7eced2c7bb86a2ca330fb5604f6e8dbdd56 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 5 Sep 2023 08:30:29 +0530 Subject: [PATCH 01/27] Implement dedicated route for CountryPicker in AddressPage --- src/ROUTES.js | 9 ++ .../AppNavigator/ModalStackNavigators.js | 7 ++ src/libs/Navigation/linkingConfig.js | 4 + .../Profile/PersonalDetails/AddressPage.js | 68 ++++++++++-- .../PersonalDetails/CountrySelection.js | 102 ++++++++++++++++++ 5 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 src/pages/settings/Profile/PersonalDetails/CountrySelection.js diff --git a/src/ROUTES.js b/src/ROUTES.js index b38ce25f590f..d8a451c6416f 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -54,6 +54,15 @@ export default { SETTINGS_PERSONAL_DETAILS_LEGAL_NAME: `${SETTINGS_PERSONAL_DETAILS}/legal-name`, SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH: `${SETTINGS_PERSONAL_DETAILS}/date-of-birth`, SETTINGS_PERSONAL_DETAILS_ADDRESS: `${SETTINGS_PERSONAL_DETAILS}/address`, + getPersonalDetailsAddressRoute: (country) => `${SETTINGS_PERSONAL_DETAILS}/address?country=${country}`, + SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY: `${SETTINGS_PERSONAL_DETAILS}/address/country`, + getCountryRoute: (country, backTo) => { + let route = `${SETTINGS_PERSONAL_DETAILS}/address/country?country=${country}`; + if (backTo) { + route += `&backTo=${backTo}`; + } + return route; + }, SETTINGS_CONTACT_METHODS, SETTINGS_CONTACT_METHOD_DETAILS: `${SETTINGS_CONTACT_METHODS}/:contactMethod/details`, getEditContactMethodRoute: (contactMethod) => `${SETTINGS_CONTACT_METHODS}/${encodeURIComponent(contactMethod)}/details`, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 2adaf0397a2c..fa4b5f9587f9 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -440,6 +440,13 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, name: 'Settings_PersonalDetails_Address', }, + { + getComponent: () => { + const CountrySelectionPage = require('../../../pages/settings/Profile/PersonalDetails/CountrySelection').default; + return CountrySelectionPage; + }, + name: 'Settings_PersonalDetails_Address_Country', + }, { getComponent: () => { const SettingsContactMethodsPage = require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default; diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 8390aa7d700b..edfe29043cb2 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -153,6 +153,10 @@ export default { path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS, exact: true, }, + Settings_PersonalDetails_Address_Country: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY, + 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 b26c833aee11..360cdcdc5672 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, forwardRef} 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,14 @@ 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 MenuItemWithTopDescription from '../../../../components/MenuItemWithTopDescription'; +import FormHelpMessage from '../../../../components/FormHelpMessage'; const propTypes = { /* Onyx Props */ @@ -37,6 +38,14 @@ const propTypes = { country: PropTypes.string, }), }), + /** Route from navigation */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** Currently selected currency */ + 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}); @@ -115,7 +125,7 @@ function AddressPage({privatePersonalDetails}) { return errors; }, []); - const handleAddressChange = (value, key) => { + const handleAddressChange = useCallback((value, key) => { if (key !== 'country' && key !== 'state') { return; } @@ -125,7 +135,12 @@ function AddressPage({privatePersonalDetails}) { return; } setState(value); - }; + }, []); + + useEffect(() => { + if (!countryFromUrl) return; + handleAddressChange(countryFromUrl, 'country'); + }, [countryFromUrl, handleAddressChange]); if (lodashGet(privatePersonalDetails, 'isLoading', true)) { return ; @@ -177,8 +192,7 @@ function AddressPage({privatePersonalDetails}) { @@ -236,3 +250,41 @@ export default withOnyx({ key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, }, })(AddressPage); + +const CountryPicker = React.memo( + forwardRef(({errorText, value}, ref) => { + const {translate} = useLocalize(); + const getCountryFromCountryCode = (code) => translate('allCountries')[code]; + + const countryTitleDescStyle = value && value.length === 0 ? styles.textNormal : null; + + return ( + + { + const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); + Navigation.navigate(ROUTES.getCountryRoute(value, activeRoute)); + }} + /> + + + + + ); + }), +); + +CountryPicker.propTypes = { + errorText: PropTypes.string, + value: PropTypes.string, +}; + +CountryPicker.defaultProps = { + errorText: '', + value: '', +}; diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js new file mode 100644 index 000000000000..69d7f347b08c --- /dev/null +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -0,0 +1,102 @@ +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 compose from '../../../../libs/compose'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import {withNetwork} from '../../../../components/OnyxProvider'; +import SelectionList from '../../../../components/SelectionList'; +import searchCountryOptions from '../../../../libs/searchCountryOptions'; +import StringUtils from '../../../../libs/StringUtils'; + +import useLocalize from '../../../../hooks/useLocalize'; + +/** + * IOU Currency selection for selecting currency + */ +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, + + ...withLocalizePropTypes, +}; + +const defaultProps = {}; + +function CountrySelection(props) { + const [searchValue, setSearchValue] = useState(''); + const {translate} = useLocalize(); + const currentCountry = lodashGet(props, 'route.params.country'); + + const countries = useMemo( + () => + _.map(translate('allCountries'), (countryName, countryISO) => ({ + 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 currentCountryInner = option.value; + const backTo = lodashGet(props.route, 'params.backTo', ''); + const backToRoute = backTo ? `${backTo}?country=${currentCountryInner}` : ''; + Navigation.goBack(backToRoute, true); + }, + [props.route], + ); + + return ( + + { + const backTo = lodashGet(props.route, 'params.backTo', ''); + const backToRoute = backTo ? `${backTo}?country=${currentCountry}` : ''; + Navigation.goBack(backToRoute); + }} + /> + + + + ); +} + +CountrySelection.displayName = 'CountrySelection'; +CountrySelection.propTypes = propTypes; +CountrySelection.defaultProps = defaultProps; + +export default compose( + withLocalize, + + withNetwork(), +)(CountrySelection); From b5a0ebc4050493536844b4823d8d48252514377a Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Wed, 6 Sep 2023 05:22:13 +0530 Subject: [PATCH 02/27] Comment correction Co-authored-by: Rajat Parashar --- src/pages/settings/Profile/PersonalDetails/CountrySelection.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index 69d7f347b08c..8723440436d9 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -14,9 +14,6 @@ import StringUtils from '../../../../libs/StringUtils'; import useLocalize from '../../../../hooks/useLocalize'; -/** - * IOU Currency selection for selecting currency - */ const propTypes = { /** Route from navigation */ route: PropTypes.shape({ From 3cbd8d2aeac626300a01b88defa270ba6decd844 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 05:33:01 +0530 Subject: [PATCH 03/27] Remove unused dependencies in CountrySelection --- .../Profile/PersonalDetails/CountrySelection.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index 8723440436d9..7b1f2d11960e 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -5,9 +5,6 @@ import lodashGet from 'lodash/get'; import Navigation from '../../../../libs/Navigation/Navigation'; import ScreenWrapper from '../../../../components/ScreenWrapper'; import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; -import compose from '../../../../libs/compose'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import {withNetwork} from '../../../../components/OnyxProvider'; import SelectionList from '../../../../components/SelectionList'; import searchCountryOptions from '../../../../libs/searchCountryOptions'; import StringUtils from '../../../../libs/StringUtils'; @@ -26,8 +23,6 @@ const propTypes = { backTo: PropTypes.string, }), }).isRequired, - - ...withLocalizePropTypes, }; const defaultProps = {}; @@ -92,8 +87,4 @@ CountrySelection.displayName = 'CountrySelection'; CountrySelection.propTypes = propTypes; CountrySelection.defaultProps = defaultProps; -export default compose( - withLocalize, - - withNetwork(), -)(CountrySelection); +export default CountrySelection; From 53286fdb7c4b787ab962784fd450e492869e24eb Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 05:34:56 +0530 Subject: [PATCH 04/27] CountrySelection refactoring --- .../Profile/PersonalDetails/CountrySelection.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index 7b1f2d11960e..870f4b9f5253 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -27,10 +27,10 @@ const propTypes = { const defaultProps = {}; -function CountrySelection(props) { +function CountrySelection({route}) { const [searchValue, setSearchValue] = useState(''); const {translate} = useLocalize(); - const currentCountry = lodashGet(props, 'route.params.country'); + const currentCountry = lodashGet(route, 'params.country'); const countries = useMemo( () => @@ -50,11 +50,11 @@ function CountrySelection(props) { const selectCountry = useCallback( (option) => { const currentCountryInner = option.value; - const backTo = lodashGet(props.route, 'params.backTo', ''); + const backTo = lodashGet(route, 'params.backTo', ''); const backToRoute = backTo ? `${backTo}?country=${currentCountryInner}` : ''; Navigation.goBack(backToRoute, true); }, - [props.route], + [route], ); return ( @@ -63,7 +63,7 @@ function CountrySelection(props) { title={translate('common.country')} shouldShowBackButton onBackButtonPress={() => { - const backTo = lodashGet(props.route, 'params.backTo', ''); + const backTo = lodashGet(route, 'params.backTo', ''); const backToRoute = backTo ? `${backTo}?country=${currentCountry}` : ''; Navigation.goBack(backToRoute); }} From d88cc417995580a750b30e295da0dbefed748d16 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 05:35:46 +0530 Subject: [PATCH 05/27] CountrySelection remove defaultProps --- src/pages/settings/Profile/PersonalDetails/CountrySelection.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index 870f4b9f5253..b72ef41943f1 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -25,8 +25,6 @@ const propTypes = { }).isRequired, }; -const defaultProps = {}; - function CountrySelection({route}) { const [searchValue, setSearchValue] = useState(''); const {translate} = useLocalize(); @@ -85,6 +83,5 @@ function CountrySelection({route}) { CountrySelection.displayName = 'CountrySelection'; CountrySelection.propTypes = propTypes; -CountrySelection.defaultProps = defaultProps; export default CountrySelection; From b8cacd76ed1acfbdd45aaf51694d79c12b39874f Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 05:57:15 +0530 Subject: [PATCH 06/27] Make CountryPickerMenuItem a new component on its own page --- src/components/CountryPickerMenuItem.js | 50 +++++++++++++++++++ .../Profile/PersonalDetails/AddressPage.js | 45 ++--------------- 2 files changed, 53 insertions(+), 42 deletions(-) create mode 100644 src/components/CountryPickerMenuItem.js diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js new file mode 100644 index 000000000000..3ecfa0345a6a --- /dev/null +++ b/src/components/CountryPickerMenuItem.js @@ -0,0 +1,50 @@ +import React 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 = { + errorText: PropTypes.string, + value: PropTypes.string, +}; + +const defaultProps = { + errorText: '', + value: '', +}; + +function CountryPickerMenuItem({errorText, value}, ref) { + const {translate} = useLocalize(); + const getCountryFromCountryCode = (code) => translate('allCountries')[code]; + + const countryTitleDescStyle = value && value.length === 0 ? styles.textNormal : null; + + return ( + + { + const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); + Navigation.navigate(ROUTES.getCountryRoute(value, activeRoute)); + }} + /> + + + + + ); +} + +CountryPickerMenuItem.propTypes = propTypes; +CountryPickerMenuItem.defaultProps = defaultProps; + +export default React.memo(React.forwardRef(CountryPickerMenuItem)); diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 360cdcdc5672..89241ba4b4bb 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, useEffect, forwardRef} 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'; @@ -21,8 +21,7 @@ import ROUTES from '../../../../ROUTES'; import useLocalize from '../../../../hooks/useLocalize'; import usePrivatePersonalDetails from '../../../../hooks/usePrivatePersonalDetails'; import FullscreenLoadingIndicator from '../../../../components/FullscreenLoadingIndicator'; -import MenuItemWithTopDescription from '../../../../components/MenuItemWithTopDescription'; -import FormHelpMessage from '../../../../components/FormHelpMessage'; +import CountryPickerMenuItem from '../../../../components/CountryPickerMenuItem'; const propTypes = { /* Onyx Props */ @@ -190,7 +189,7 @@ function AddressPage({privatePersonalDetails, route}) { /> - @@ -250,41 +249,3 @@ export default withOnyx({ key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, }, })(AddressPage); - -const CountryPicker = React.memo( - forwardRef(({errorText, value}, ref) => { - const {translate} = useLocalize(); - const getCountryFromCountryCode = (code) => translate('allCountries')[code]; - - const countryTitleDescStyle = value && value.length === 0 ? styles.textNormal : null; - - return ( - - { - const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); - Navigation.navigate(ROUTES.getCountryRoute(value, activeRoute)); - }} - /> - - - - - ); - }), -); - -CountryPicker.propTypes = { - errorText: PropTypes.string, - value: PropTypes.string, -}; - -CountryPicker.defaultProps = { - errorText: '', - value: '', -}; From f4ae5b223a0ff72ba3e969facf91238204b14c92 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 08:56:27 +0530 Subject: [PATCH 07/27] CountrySelection Navigation bugfix after selecting country --- .../PersonalDetails/CountrySelection.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index b72ef41943f1..4ef61ea4c784 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -23,9 +23,12 @@ const propTypes = { backTo: PropTypes.string, }), }).isRequired, + navigation: PropTypes.shape({ + getState: PropTypes.func.isRequired, + }).isRequired, }; -function CountrySelection({route}) { +function CountrySelection({route, navigation}) { const [searchValue, setSearchValue] = useState(''); const {translate} = useLocalize(); const currentCountry = lodashGet(route, 'params.country'); @@ -47,12 +50,17 @@ function CountrySelection({route}) { const selectCountry = useCallback( (option) => { - const currentCountryInner = option.value; const backTo = lodashGet(route, 'params.backTo', ''); - const backToRoute = backTo ? `${backTo}?country=${currentCountryInner}` : ''; - Navigation.goBack(backToRoute, true); + + if (navigation.getState().routes.length === 1 && _.isEmpty(backTo)) { + Navigation.goBack(); + } else if (!_.isEmpty(backTo) && navigation.getState().routes.length === 1) { + Navigation.goBack(`${route.params.backTo}?country=${option.value}`); + } else { + Navigation.navigate(`${route.params.backTo}?country=${option.value}`); + } }, - [route], + [route, navigation], ); return ( From ae6ebcf4b2be5f7c662e9973a7eb13bbc43032c5 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 19:28:01 +0530 Subject: [PATCH 08/27] Delete Old CountryPicker Modal --- .../CountryPicker/CountrySelectorModal.js | 102 ------------------ src/components/CountryPicker/index.js | 89 --------------- 2 files changed, 191 deletions(-) delete mode 100644 src/components/CountryPicker/CountrySelectorModal.js delete mode 100644 src/components/CountryPicker/index.js diff --git a/src/components/CountryPicker/CountrySelectorModal.js b/src/components/CountryPicker/CountrySelectorModal.js deleted file mode 100644 index 126cea7e842e..000000000000 --- a/src/components/CountryPicker/CountrySelectorModal.js +++ /dev/null @@ -1,102 +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(translate('allCountries'), (countryName, countryISO) => ({ - 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 c6bbae0dd645..000000000000 --- a/src/components/CountryPicker/index.js +++ /dev/null @@ -1,89 +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 allCountries = translate('allCountries'); - const [isPickerVisible, setIsPickerVisible] = useState(false); - const [searchValue, setSearchValue] = useState(''); - - const showPickerModal = () => { - setIsPickerVisible(true); - }; - - const hidePickerModal = () => { - setIsPickerVisible(false); - }; - - const updateCountryInput = (country) => { - onInputChange(country.value); - hidePickerModal(); - }; - - const title = 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) => ( - -)); From 9cfcc96826c0a63cc566dada948f24d6084bbab3 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 6 Sep 2023 19:29:29 +0530 Subject: [PATCH 09/27] Add display name to CountryPickerMenuitem --- src/components/CountryPickerMenuItem.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js index 3ecfa0345a6a..abb0c485d594 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountryPickerMenuItem.js @@ -46,5 +46,6 @@ function CountryPickerMenuItem({errorText, value}, ref) { CountryPickerMenuItem.propTypes = propTypes; CountryPickerMenuItem.defaultProps = defaultProps; +CountryPickerMenuItem.displayName = 'CountryPickerMenuItem'; export default React.memo(React.forwardRef(CountryPickerMenuItem)); From ff87483a8517cdf5f244d59f8b66022c8e5825a6 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Thu, 7 Sep 2023 01:01:07 +0530 Subject: [PATCH 10/27] Remove memoing CountryPickerMenuItem Co-authored-by: Rajat Parashar --- src/components/CountryPickerMenuItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js index 3ecfa0345a6a..8c9e60d0abb1 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountryPickerMenuItem.js @@ -47,4 +47,4 @@ function CountryPickerMenuItem({errorText, value}, ref) { CountryPickerMenuItem.propTypes = propTypes; CountryPickerMenuItem.defaultProps = defaultProps; -export default React.memo(React.forwardRef(CountryPickerMenuItem)); +export default React.forwardRef(CountryPickerMenuItem); From f161188505c1cf5aad2643cb46f4ef600301cc5b Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Thu, 7 Sep 2023 01:08:14 +0530 Subject: [PATCH 11/27] Comment modifications to CountrySelection and related files --- src/components/CountryPickerMenuItem.js | 2 ++ src/pages/settings/Profile/PersonalDetails/AddressPage.js | 2 +- src/pages/settings/Profile/PersonalDetails/CountrySelection.js | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js index abb0c485d594..8aec6a21b35d 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountryPickerMenuItem.js @@ -9,7 +9,9 @@ import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import FormHelpMessage from './FormHelpMessage'; const propTypes = { + /** Error text from form, e.g when no country is selected */ errorText: PropTypes.string, + /** Current selected country */ value: PropTypes.string, }; diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 89241ba4b4bb..cd25d1579264 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -41,7 +41,7 @@ const propTypes = { route: PropTypes.shape({ /** Params from the route */ params: PropTypes.shape({ - /** Currently selected currency */ + /** Currently selected country */ country: PropTypes.string, }), }).isRequired, diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index 4ef61ea4c784..6c9cbbb677db 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -23,6 +23,7 @@ const propTypes = { backTo: PropTypes.string, }), }).isRequired, + /** Navigation from react-navigation */ navigation: PropTypes.shape({ getState: PropTypes.func.isRequired, }).isRequired, From ba351d1b1cd544e315fae33e49c95c7a6d45183c Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Thu, 7 Sep 2023 20:49:24 +0530 Subject: [PATCH 12/27] CountySelectionPage & related files refactoring and comments --- src/ROUTES.js | 3 +-- src/components/CountryPickerMenuItem.js | 11 ++++++----- .../Profile/PersonalDetails/CountrySelection.js | 5 +++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index d8a451c6416f..7c39991c7048 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -54,12 +54,11 @@ export default { SETTINGS_PERSONAL_DETAILS_LEGAL_NAME: `${SETTINGS_PERSONAL_DETAILS}/legal-name`, SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH: `${SETTINGS_PERSONAL_DETAILS}/date-of-birth`, SETTINGS_PERSONAL_DETAILS_ADDRESS: `${SETTINGS_PERSONAL_DETAILS}/address`, - getPersonalDetailsAddressRoute: (country) => `${SETTINGS_PERSONAL_DETAILS}/address?country=${country}`, SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY: `${SETTINGS_PERSONAL_DETAILS}/address/country`, getCountryRoute: (country, backTo) => { let route = `${SETTINGS_PERSONAL_DETAILS}/address/country?country=${country}`; if (backTo) { - route += `&backTo=${backTo}`; + route += `&backTo=${encodeURIComponent(backTo)}`; } return route; }, diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js index 3c3f6f82aa4f..82b1ed0a449a 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountryPickerMenuItem.js @@ -20,23 +20,24 @@ const defaultProps = { value: '', }; -function CountryPickerMenuItem({errorText, value}, ref) { +function CountryPickerMenuItem({errorText, value: countryCode}, ref) { const {translate} = useLocalize(); - const getCountryFromCountryCode = (code) => translate('allCountries')[code]; - const countryTitleDescStyle = value && value.length === 0 ? styles.textNormal : null; + const countries = translate('allCountries'); + const country = countries[countryCode] || ''; + const countryTitleDescStyle = country.length === 0 ? styles.textNormal : null; return ( { const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); - Navigation.navigate(ROUTES.getCountryRoute(value, activeRoute)); + Navigation.navigate(ROUTES.getCountryRoute(countryCode, activeRoute)); }} /> diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js index 6c9cbbb677db..02b1ba9066d6 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js @@ -25,6 +25,7 @@ const propTypes = { }).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, }; @@ -53,11 +54,15 @@ function CountrySelection({route, navigation}) { (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}`); } }, From 55253e795f7c92edf08447500c56931a1dd6e7cf Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Thu, 7 Sep 2023 21:01:04 +0530 Subject: [PATCH 13/27] Rename CountrySelection to CountrySelectionPage --- .../PersonalDetails/CountrySelectionPage.js | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js new file mode 100644 index 000000000000..31b48619c575 --- /dev/null +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js @@ -0,0 +1,101 @@ +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 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(translate('allCountries'), (countryName, countryISO) => ({ + 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; From 4252fba4f71998ff46020edac06adb5f1162fd2c Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Thu, 7 Sep 2023 21:03:27 +0530 Subject: [PATCH 14/27] Rename CountrySelection to CountrySelectionPage --- .../AppNavigator/ModalStackNavigators.js | 2 +- .../PersonalDetails/CountrySelection.js | 101 ------------------ 2 files changed, 1 insertion(+), 102 deletions(-) delete mode 100644 src/pages/settings/Profile/PersonalDetails/CountrySelection.js diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index fa4b5f9587f9..9e3a1dc53ee7 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -442,7 +442,7 @@ const SettingsModalStackNavigator = createModalStackNavigator([ }, { getComponent: () => { - const CountrySelectionPage = require('../../../pages/settings/Profile/PersonalDetails/CountrySelection').default; + const CountrySelectionPage = require('../../../pages/settings/Profile/PersonalDetails/CountrySelectionPage').default; return CountrySelectionPage; }, name: 'Settings_PersonalDetails_Address_Country', diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js b/src/pages/settings/Profile/PersonalDetails/CountrySelection.js deleted file mode 100644 index 02b1ba9066d6..000000000000 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelection.js +++ /dev/null @@ -1,101 +0,0 @@ -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 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 CountrySelection({route, navigation}) { - const [searchValue, setSearchValue] = useState(''); - const {translate} = useLocalize(); - const currentCountry = lodashGet(route, 'params.country'); - - const countries = useMemo( - () => - _.map(translate('allCountries'), (countryName, countryISO) => ({ - 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); - }} - /> - - - - ); -} - -CountrySelection.displayName = 'CountrySelection'; -CountrySelection.propTypes = propTypes; - -export default CountrySelection; From 178d1004272cde14a461f68810509900ff2ad4cb Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Thu, 7 Sep 2023 21:41:12 +0530 Subject: [PATCH 15/27] AddressPage: revalidate input forms when country name changes --- src/components/CountryPickerMenuItem.js | 17 +++++++++++++++-- .../Profile/PersonalDetails/AddressPage.js | 4 ++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js index 82b1ed0a449a..c2b9bf784959 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountryPickerMenuItem.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import styles from '../styles/styles'; @@ -11,6 +11,12 @@ import FormHelpMessage from './FormHelpMessage'; const propTypes = { /** Error text from form, e.g when no country is selected */ errorText: PropTypes.string, + /** function from form to call when the country changes, important for revalidation */ + onInputChange: PropTypes.func.isRequired, + /** Prop which states when country value changed */ + didCountryChange: PropTypes.bool.isRequired, + /** Function for setting didCountryChange to false */ + setDidCountryChange: PropTypes.func.isRequired, /** Current selected country */ value: PropTypes.string, }; @@ -20,13 +26,20 @@ const defaultProps = { value: '', }; -function CountryPickerMenuItem({errorText, value: countryCode}, ref) { +function CountryPickerMenuItem({errorText, value: countryCode, onInputChange, didCountryChange, setDidCountryChange}, ref) { const {translate} = useLocalize(); const countries = translate('allCountries'); const country = countries[countryCode] || ''; const countryTitleDescStyle = country.length === 0 ? styles.textNormal : null; + useEffect(() => { + // This will cause the form to revalidate and remove any error related to country name + if (!didCountryChange) return; + onInputChange(countryCode); + setDidCountryChange(false); + }, [didCountryChange, onInputChange, setDidCountryChange, countryCode]); + return ( { if (!countryFromUrl) return; handleAddressChange(countryFromUrl, 'country'); + setDidCountryChange(true); }, [countryFromUrl, handleAddressChange]); if (lodashGet(privatePersonalDetails, 'isLoading', true)) { @@ -190,6 +192,8 @@ function AddressPage({privatePersonalDetails, route}) { From c7dfa90aead64973ff7dcf798676d0221338587d Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 19 Sep 2023 20:23:08 +0530 Subject: [PATCH 16/27] CountyPicker refactoring --- src/components/CountryPickerMenuItem.js | 13 +++++-------- .../settings/Profile/PersonalDetails/AddressPage.js | 4 ---- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountryPickerMenuItem.js index d5725b4409f7..5567957990c3 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountryPickerMenuItem.js @@ -13,12 +13,11 @@ const propTypes = { errorText: PropTypes.string, /** function from form to call when the country changes, important for revalidation */ onInputChange: PropTypes.func.isRequired, - /** Prop which states when country value changed */ - didCountryChange: PropTypes.bool.isRequired, - /** Function for setting didCountryChange to false */ - setDidCountryChange: 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, }; const defaultProps = { @@ -26,7 +25,7 @@ const defaultProps = { value: '', }; -function CountryPickerMenuItem({errorText, value: countryCode, onInputChange, didCountryChange, setDidCountryChange}, ref) { +function CountryPickerMenuItem({errorText, value: countryCode, onInputChange}, ref) { const {translate} = useLocalize(); const title = countryCode ? translate(`allCountries.${countryCode}`) : ''; @@ -34,10 +33,8 @@ function CountryPickerMenuItem({errorText, value: countryCode, onInputChange, di useEffect(() => { // This will cause the form to revalidate and remove any error related to country name - if (!didCountryChange) return; onInputChange(countryCode); - setDidCountryChange(false); - }, [didCountryChange, onInputChange, setDidCountryChange, countryCode]); + }, [countryCode]); return ( diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index adceb3b01cb4..9903902c233b 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -72,7 +72,6 @@ function AddressPage({privatePersonalDetails, route}) { const {translate} = useLocalize(); const countryFromUrl = lodashGet(route, 'params.country'); const [currentCountry, setCurrentCountry] = useState(countryFromUrl || PersonalDetails.getCountryISO(lodashGet(privatePersonalDetails, 'address.country'))); - const [didCountryChange, setDidCountryChange] = useState(false); const isUSAForm = currentCountry === CONST.COUNTRY.US; const zipSampleFormat = lodashGet(CONST.COUNTRY_ZIP_REGEX_DATA, [currentCountry, 'samples'], ''); const zipFormat = translate('common.zipCodeExampleFormat', {zipSampleFormat}); @@ -141,7 +140,6 @@ function AddressPage({privatePersonalDetails, route}) { useEffect(() => { if (!countryFromUrl) return; handleAddressChange(countryFromUrl, 'country'); - setDidCountryChange(true); }, [countryFromUrl, handleAddressChange]); return ( @@ -192,8 +190,6 @@ function AddressPage({privatePersonalDetails, route}) { From fc551b820e5c2ac27c47a5d6d93e2ee39058b5a1 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 19 Sep 2023 20:25:12 +0530 Subject: [PATCH 17/27] Rename CountryPickerMenuItem to CountrySelector --- .../{CountryPickerMenuItem.js => CountrySelector.js} | 10 +++++----- .../settings/Profile/PersonalDetails/AddressPage.js | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) rename src/components/{CountryPickerMenuItem.js => CountrySelector.js} (86%) diff --git a/src/components/CountryPickerMenuItem.js b/src/components/CountrySelector.js similarity index 86% rename from src/components/CountryPickerMenuItem.js rename to src/components/CountrySelector.js index 5567957990c3..4bee322bf99f 100644 --- a/src/components/CountryPickerMenuItem.js +++ b/src/components/CountrySelector.js @@ -25,7 +25,7 @@ const defaultProps = { value: '', }; -function CountryPickerMenuItem({errorText, value: countryCode, onInputChange}, ref) { +function CountrySelector({errorText, value: countryCode, onInputChange}, ref) { const {translate} = useLocalize(); const title = countryCode ? translate(`allCountries.${countryCode}`) : ''; @@ -56,8 +56,8 @@ function CountryPickerMenuItem({errorText, value: countryCode, onInputChange}, r ); } -CountryPickerMenuItem.propTypes = propTypes; -CountryPickerMenuItem.defaultProps = defaultProps; -CountryPickerMenuItem.displayName = 'CountryPickerMenuItem'; +CountrySelector.propTypes = propTypes; +CountrySelector.defaultProps = defaultProps; +CountrySelector.displayName = 'CountrySelector'; -export default React.forwardRef(CountryPickerMenuItem); +export default React.forwardRef(CountrySelector); diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 9903902c233b..8cad30422f09 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -21,7 +21,7 @@ import ROUTES from '../../../../ROUTES'; import useLocalize from '../../../../hooks/useLocalize'; import usePrivatePersonalDetails from '../../../../hooks/usePrivatePersonalDetails'; import FullscreenLoadingIndicator from '../../../../components/FullscreenLoadingIndicator'; -import CountryPickerMenuItem from '../../../../components/CountryPickerMenuItem'; +import CountrySelector from '../../../../components/CountrySelector'; const propTypes = { /* Onyx Props */ @@ -189,7 +189,7 @@ function AddressPage({privatePersonalDetails, route}) { /> - From 137790cb4a7208afeba5481e1ffd68ade5ebd20f Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:35:13 +0530 Subject: [PATCH 18/27] Refactoring src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js Co-authored-by: Rajat Parashar --- .../settings/Profile/PersonalDetails/CountrySelectionPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js index d14d03dd87d3..e45060b35879 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js @@ -23,6 +23,7 @@ const propTypes = { backTo: PropTypes.string, }), }).isRequired, + /** Navigation from react-navigation */ navigation: PropTypes.shape({ /** getState function retrieves the current navigation state from react-navigation's navigation property */ From a49e1aac57821d2bf8230c22499b95c081e62887 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:35:40 +0530 Subject: [PATCH 19/27] Refactor src/pages/settings/Profile/PersonalDetails/AddressPage.js Co-authored-by: Rajat Parashar --- src/pages/settings/Profile/PersonalDetails/AddressPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 8cad30422f09..949a0820228c 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -37,6 +37,7 @@ const propTypes = { country: PropTypes.string, }), }), + /** Route from navigation */ route: PropTypes.shape({ /** Params from the route */ From 19a1e7e866aaae6f606682bba6ff6862d6b91980 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt <65986357+ygshbht@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:36:07 +0530 Subject: [PATCH 20/27] Refactor src/components/CountrySelector.js Co-authored-by: Rajat Parashar --- src/components/CountrySelector.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index 4bee322bf99f..a16ff089363f 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -9,12 +9,15 @@ import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import FormHelpMessage from './FormHelpMessage'; const propTypes = { - /** Error text from form, e.g when no country is selected */ + /** Form error text. e.g when no country is selected */ errorText: PropTypes.string, - /** function from form to call when the country changes, important for revalidation */ + + /** 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, From 2cca5c0cb494c86e32c8385f8724d9cbbdaffed2 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 19 Sep 2023 20:38:36 +0530 Subject: [PATCH 21/27] Pull from remote --- src/components/CountrySelector.js | 6 +++--- .../Profile/PersonalDetails/CountrySelectionPage.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index a16ff089363f..03c176570287 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -11,13 +11,13 @@ 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, diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js index e45060b35879..79bc86f5498f 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js @@ -23,7 +23,7 @@ const propTypes = { backTo: PropTypes.string, }), }).isRequired, - + /** Navigation from react-navigation */ navigation: PropTypes.shape({ /** getState function retrieves the current navigation state from react-navigation's navigation property */ From 91045391c537b4e19c090fc282b421844890d68a Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 19 Sep 2023 20:39:08 +0530 Subject: [PATCH 22/27] Disable eslint warning CountrySelector --- src/components/CountrySelector.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index 03c176570287..e508bc160f82 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -37,6 +37,7 @@ function CountrySelector({errorText, value: countryCode, onInputChange}, ref) { 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 ( From e81fe77b1f53612109da34523b336774d4321d20 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Wed, 20 Sep 2023 09:53:44 +0530 Subject: [PATCH 23/27] CountrySelector refactoring --- src/components/CountrySelector.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index e508bc160f82..3a944bab5840 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -21,14 +21,18 @@ const propTypes = { /** 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: '', + forwardedRef: () => {}, }; -function CountrySelector({errorText, value: countryCode, onInputChange}, ref) { +function CountrySelector({errorText, value: countryCode, onInputChange, forwardedRef}) { const {translate} = useLocalize(); const title = countryCode ? translate(`allCountries.${countryCode}`) : ''; @@ -45,7 +49,7 @@ function CountrySelector({errorText, value: countryCode, onInputChange}, ref) { { @@ -64,4 +68,10 @@ CountrySelector.propTypes = propTypes; CountrySelector.defaultProps = defaultProps; CountrySelector.displayName = 'CountrySelector'; -export default React.forwardRef(CountrySelector); +export default React.forwardRef((props, ref) => ( + +)); From a96a73904c910626d6e4ee8dac5c7fda14d18371 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Sat, 23 Sep 2023 16:13:34 +0530 Subject: [PATCH 24/27] Format code --- src/pages/settings/Profile/PersonalDetails/AddressPage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index 8ebd900455a2..9ce06841f6a6 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -139,7 +139,9 @@ function AddressPage({privatePersonalDetails, route}) { }, []); useEffect(() => { - if (!countryFromUrl) return; + if (!countryFromUrl) { + return; + } handleAddressChange(countryFromUrl, 'country'); }, [countryFromUrl, handleAddressChange]); From 9d19973cb85e04a9e65bdcf81261ccae205d273d Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Mon, 25 Sep 2023 21:12:05 +0530 Subject: [PATCH 25/27] Formatting --- src/libs/Navigation/AppNavigator/ModalStackNavigators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 612e65efe2b3..29cae97a219f 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -126,7 +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_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, From 3af7931030512f6a458ca0fc2c73bb9cd29cd2f1 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Mon, 25 Sep 2023 21:21:13 +0530 Subject: [PATCH 26/27] Formatting --- src/libs/Navigation/AppNavigator/ModalStackNavigators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 29cae97a219f..da3b2b5e618b 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -222,4 +222,4 @@ export { PrivateNotesModalStackNavigator, NewTeachersUniteNavigator, SignInModalStackNavigator, -}; \ No newline at end of file +}; From 945056d94d0db5960987adf964cea4b8e21c9b92 Mon Sep 17 00:00:00 2001 From: Yogesh Bhatt Date: Tue, 26 Sep 2023 14:38:49 +0530 Subject: [PATCH 27/27] set default prop value of 'value' of CountrySelector to undefined --- src/components/CountrySelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index 74a8f3c126f9..2788f3cea8e3 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -28,7 +28,7 @@ const propTypes = { const defaultProps = { errorText: '', - value: '', + value: undefined, forwardedRef: () => {}, };