From b4d942fc7d23c314ec7e6efab99c19073cce560f Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 19 Oct 2023 10:46:56 +0200 Subject: [PATCH 01/21] WIP --- src/pages/settings/Wallet/AddDebitCardPage.js | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index e75c3b2c517e..23532346a990 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -22,6 +22,8 @@ import ROUTES from '../../../ROUTES'; import usePrevious from '../../../hooks/usePrevious'; import NotFoundPage from '../../ErrorPage/NotFoundPage'; import Permissions from '../../../libs/Permissions'; +import FormProvider from "../../../components/Form/FormProvider"; +import InputWrapper from "../../../components/Form/InputWrapper"; const propTypes = { /* Onyx Props */ @@ -114,7 +116,7 @@ function DebitCardPage(props) { title={translate('addDebitCardPage.addADebitCard')} onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET)} /> -
- (nameOnCardRef.current = ref)} spellCheck={false} /> - - - - - - + - ( @@ -195,7 +204,7 @@ function DebitCardPage(props) { )} style={[styles.mt4]} /> - + ); } From a348d1ba55eb224a53538f5afc9cd9c7b215a97b Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 19 Oct 2023 15:26:34 +0200 Subject: [PATCH 02/21] Fix checkbox proptypes --- src/components/CheckboxWithLabel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 63c067c93234..777c46f861aa 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -54,7 +54,7 @@ const propTypes = { defaultValue: PropTypes.bool, /** React ref being forwarded to the Checkbox input */ - forwardedRef: PropTypes.func, + forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]), /** The ID used to uniquely identify the input in a Form */ /* eslint-disable-next-line react/no-unused-prop-types */ From 38d91848c62674d56b4137fdce9c1f482b4e46bd Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 19 Oct 2023 15:27:14 +0200 Subject: [PATCH 03/21] Fix address search prop type issue --- src/components/AddressSearch/index.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 3e676b811c16..3fac925cdb0a 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -1,7 +1,7 @@ import _ from 'underscore'; -import React, {useEffect, useMemo, useRef, useState} from 'react'; +import React, {memo, useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; -import {Keyboard, LogBox, ScrollView, View, Text, ActivityIndicator} from 'react-native'; +import {ActivityIndicator, Keyboard, LogBox, ScrollView, Text, View} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; import lodashGet from 'lodash/get'; import compose from '../../libs/compose'; @@ -137,6 +137,16 @@ const defaultProps = { resultTypes: 'address', }; +function listLoader() { + return memo(() => ( + + )); +} + + // Do not convert to class component! It's been tried before and presents more challenges than it's worth. // Relevant thread: https://expensify.slack.com/archives/C03TQ48KC/p1634088400387400 // Reference: https://github.com/FaridSafi/react-native-google-places-autocomplete/issues/609#issuecomment-886133839 @@ -374,14 +384,7 @@ function AddressSearch(props) { {props.translate('common.noResultsFound')} ) } - listLoaderComponent={ - - - - } + listLoaderComponent={listLoader} renderHeaderComponent={renderHeaderComponent} onPress={(data, details) => { saveLocationDetails(data, details); From 00e12fc524860cc05fa5c34929a3ccec21e5efb5 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 19 Oct 2023 15:27:36 +0200 Subject: [PATCH 04/21] Add checkbox default value --- src/pages/settings/Wallet/AddDebitCardPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index 23532346a990..ad284da16827 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -196,6 +196,7 @@ function DebitCardPage(props) { InputComponent={CheckboxWithLabel} accessibilityLabel={`${translate('common.iAcceptThe')} ${translate('common.expensifyTermsOfService')}`} inputID="acceptTerms" + defaultValue={false} LabelComponent={() => ( {`${translate('common.iAcceptThe')}`} From b05c8bab57672e9a00aeb1b86b1efcdc9d1bf228 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 19 Oct 2023 16:25:41 +0200 Subject: [PATCH 05/21] Prettier --- src/pages/settings/Wallet/AddDebitCardPage.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index ad284da16827..b0196e8e620e 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -22,8 +22,8 @@ import ROUTES from '../../../ROUTES'; import usePrevious from '../../../hooks/usePrevious'; import NotFoundPage from '../../ErrorPage/NotFoundPage'; import Permissions from '../../../libs/Permissions'; -import FormProvider from "../../../components/Form/FormProvider"; -import InputWrapper from "../../../components/Form/InputWrapper"; +import FormProvider from '../../../components/Form/FormProvider'; +import InputWrapper from '../../../components/Form/InputWrapper'; const propTypes = { /* Onyx Props */ @@ -190,7 +190,10 @@ function DebitCardPage(props) { containerStyles={[styles.mt4]} /> - + Date: Thu, 19 Oct 2023 16:25:56 +0200 Subject: [PATCH 06/21] Spread props --- src/components/AddressSearch/index.js | 146 +++++++++++++++----------- 1 file changed, 86 insertions(+), 60 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 3fac925cdb0a..db8d6b6bae42 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {memo, useEffect, useMemo, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import {ActivityIndicator, Keyboard, LogBox, ScrollView, Text, View} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; @@ -137,37 +137,49 @@ const defaultProps = { resultTypes: 'address', }; -function listLoader() { - return memo(() => ( - - )); -} - - // Do not convert to class component! It's been tried before and presents more challenges than it's worth. // Relevant thread: https://expensify.slack.com/archives/C03TQ48KC/p1634088400387400 // Reference: https://github.com/FaridSafi/react-native-google-places-autocomplete/issues/609#issuecomment-886133839 -function AddressSearch(props) { +function AddressSearch({ + canUseCurrentLocation, + containerStyles, + defaultValue, + errorText, + hint, + innerRef, + inputID, + isLimitedToUSA, + label, + maxInputLength, + network, + onBlur, + onInputChange, + onPress, + predefinedPlaces, + preferredLocale, + renamedInputKeys, + resultTypes, + shouldSaveDraft, + translate, + value, +}) { const [displayListViewBorder, setDisplayListViewBorder] = useState(false); const [isTyping, setIsTyping] = useState(false); const [isFocused, setIsFocused] = useState(false); - const [searchValue, setSearchValue] = useState(props.value || props.defaultValue || ''); + const [searchValue, setSearchValue] = useState(value || defaultValue || ''); const [locationErrorCode, setLocationErrorCode] = useState(null); const [isFetchingCurrentLocation, setIsFetchingCurrentLocation] = useState(false); const shouldTriggerGeolocationCallbacks = useRef(true); const containerRef = useRef(); const query = useMemo( () => ({ - language: props.preferredLocale, - types: props.resultTypes, - components: props.isLimitedToUSA ? 'country:us' : undefined, + language: preferredLocale, + types: resultTypes, + components: isLimitedToUSA ? 'country:us' : undefined, }), - [props.preferredLocale, props.resultTypes, props.isLimitedToUSA], + [preferredLocale, resultTypes, isLimitedToUSA], ); - const shouldShowCurrentLocationButton = props.canUseCurrentLocation && searchValue.trim().length === 0 && isFocused; + const shouldShowCurrentLocationButton = canUseCurrentLocation && searchValue.trim().length === 0 && isFocused; const saveLocationDetails = (autocompleteData, details) => { const addressComponents = details.address_components; @@ -176,7 +188,7 @@ function AddressSearch(props) { // to this component which don't match the usual properties coming from auto-complete. In that case, only a limited // amount of data massaging needs to happen for what the parent expects to get from this function. if (_.size(details)) { - props.onPress({ + onPress({ address: lodashGet(details, 'description', ''), lat: lodashGet(details, 'geometry.location.lat', 0), lng: lodashGet(details, 'geometry.location.lng', 0), @@ -262,7 +274,7 @@ function AddressSearch(props) { // Not all pages define the Address Line 2 field, so in that case we append any additional address details // (e.g. Apt #) to Address Line 1 - if (subpremise && typeof props.renamedInputKeys.street2 === 'undefined') { + if (subpremise && typeof renamedInputKeys.street2 === 'undefined') { values.street += `, ${subpremise}`; } @@ -271,19 +283,19 @@ function AddressSearch(props) { values.country = country; } - if (props.inputID) { - _.each(values, (value, key) => { - const inputKey = lodashGet(props.renamedInputKeys, key, key); + if (inputID) { + _.each(values, (inputValue, key) => { + const inputKey = lodashGet(renamedInputKeys, key, key); if (!inputKey) { return; } - props.onInputChange(value, inputKey); + onInputChange(inputValue, inputKey); }); } else { - props.onInputChange(values); + onInputChange(values); } - props.onPress(values); + onPress(values); }; /** Gets the user's current location and registers success/error callbacks */ @@ -313,7 +325,7 @@ function AddressSearch(props) { lng: successData.coords.longitude, address: CONST.YOUR_LOCATION_TEXT, }; - props.onPress(location); + onPress(location); }, (errorData) => { if (!shouldTriggerGeolocationCallbacks.current) { @@ -331,16 +343,16 @@ function AddressSearch(props) { }; const renderHeaderComponent = () => - props.predefinedPlaces.length > 0 && ( + predefinedPlaces.length > 0 && ( <> {/* This will show current location button in list if there are some recent destinations */} {shouldShowCurrentLocationButton && ( )} - {!props.value && {props.translate('common.recentDestinations')}} + {!value && {translate('common.recentDestinations')}} ); @@ -352,6 +364,26 @@ function AddressSearch(props) { }; }, []); + const listEmptyComponent = useCallback( + () => + network.isOffline || !isTyping ? null : ( + {translate('common.noResultsFound')} + ), + [isTyping, translate, network.isOffline], + ); + + const listLoader = useCallback( + () => ( + + + + ), + [], + ); + return ( /* * The GooglePlacesAutocomplete component uses a VirtualizedList internally, @@ -378,12 +410,8 @@ function AddressSearch(props) { fetchDetails suppressDefaultStyles enablePoweredByContainer={false} - predefinedPlaces={props.predefinedPlaces} - listEmptyComponent={ - props.network.isOffline || !isTyping ? null : ( - {props.translate('common.noResultsFound')} - ) - } + predefinedPlaces={predefinedPlaces} + listEmptyComponent={listEmptyComponent} listLoaderComponent={listLoader} renderHeaderComponent={renderHeaderComponent} onPress={(data, details) => { @@ -400,34 +428,31 @@ function AddressSearch(props) { query={query} requestUrl={{ useOnPlatform: 'all', - url: props.network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}), + url: network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}), }} textInputProps={{ InputComp: TextInput, ref: (node) => { - if (!props.innerRef) { + if (!innerRef) { return; } - if (_.isFunction(props.innerRef)) { - props.innerRef(node); + if (_.isFunction(innerRef)) { + innerRef(node); return; } // eslint-disable-next-line no-param-reassign - props.innerRef.current = node; + innerRef.current = node; }, - label: props.label, - containerStyles: props.containerStyles, - errorText: props.errorText, - hint: - displayListViewBorder || (props.predefinedPlaces.length === 0 && shouldShowCurrentLocationButton) || (props.canUseCurrentLocation && isTyping) - ? undefined - : props.hint, - value: props.value, - defaultValue: props.defaultValue, - inputID: props.inputID, - shouldSaveDraft: props.shouldSaveDraft, + label, + containerStyles, + errorText, + hint: displayListViewBorder || (predefinedPlaces.length === 0 && shouldShowCurrentLocationButton) || (canUseCurrentLocation && isTyping) ? undefined : hint, + value, + defaultValue, + inputID, + shouldSaveDraft, onFocus: () => { setIsFocused(true); }, @@ -437,24 +462,24 @@ function AddressSearch(props) { setIsFocused(false); setIsTyping(false); } - props.onBlur(); + onBlur(); }, autoComplete: 'off', onInputChange: (text) => { setSearchValue(text); setIsTyping(true); - if (props.inputID) { - props.onInputChange(text); + if (inputID) { + onInputChange(text); } else { - props.onInputChange({street: text}); + onInputChange({street: text}); } // If the text is empty and we have no predefined places, we set displayListViewBorder to false to prevent UI flickering - if (_.isEmpty(text) && _.isEmpty(props.predefinedPlaces)) { + if (_.isEmpty(text) && _.isEmpty(predefinedPlaces)) { setDisplayListViewBorder(false); } }, - maxLength: props.maxInputLength, + maxLength: maxInputLength, spellCheck: false, }} styles={{ @@ -475,17 +500,18 @@ function AddressSearch(props) { }} inbetweenCompo={ // We want to show the current location button even if there are no recent destinations - props.predefinedPlaces.length === 0 && shouldShowCurrentLocationButton ? ( + predefinedPlaces.length === 0 && shouldShowCurrentLocationButton ? ( ) : ( <> ) } + placeholder="" /> setLocationErrorCode(null)} From 64dbcd48507efa6329be72a437f85fabed6e31a6 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 19 Oct 2023 17:45:01 +0200 Subject: [PATCH 07/21] Lint fix --- src/components/AddressSearch/index.js | 2 +- src/pages/settings/Wallet/AddDebitCardPage.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index db8d6b6bae42..7c754a33e370 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import {ActivityIndicator, Keyboard, LogBox, ScrollView, Text, View} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index b0196e8e620e..b9ea0ac6df3a 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -16,7 +16,6 @@ import TextInput from '../../../components/TextInput'; import CONST from '../../../CONST'; import ONYXKEYS from '../../../ONYXKEYS'; import AddressSearch from '../../../components/AddressSearch'; -import Form from '../../../components/Form'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import usePrevious from '../../../hooks/usePrevious'; From 196689db016c212d18b43e13b8316a17cdbc6193 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Fri, 20 Oct 2023 16:35:47 +0200 Subject: [PATCH 08/21] Fix propType --- src/components/CheckboxWithLabel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 777c46f861aa..70fa972a5d2f 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -8,6 +8,7 @@ import Text from './Text'; import FormHelpMessage from './FormHelpMessage'; import variables from '../styles/variables'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; +import refPropTypes from "./refPropTypes"; /** * Returns an error if the required props are not provided @@ -54,7 +55,7 @@ const propTypes = { defaultValue: PropTypes.bool, /** React ref being forwarded to the Checkbox input */ - forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]), + forwardedRef: refPropTypes, /** The ID used to uniquely identify the input in a Form */ /* eslint-disable-next-line react/no-unused-prop-types */ From b418a4cb7561b597c3423b5602cbeb491b8d63fc Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Mon, 23 Oct 2023 15:35:53 +0200 Subject: [PATCH 09/21] Code review changes --- src/components/AddressSearch/index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 7c754a33e370..a4f712435af3 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -137,9 +137,6 @@ const defaultProps = { resultTypes: 'address', }; -// Do not convert to class component! It's been tried before and presents more challenges than it's worth. -// Relevant thread: https://expensify.slack.com/archives/C03TQ48KC/p1634088400387400 -// Reference: https://github.com/FaridSafi/react-native-google-places-autocomplete/issues/609#issuecomment-886133839 function AddressSearch({ canUseCurrentLocation, containerStyles, @@ -372,7 +369,7 @@ function AddressSearch({ [isTyping, translate, network.isOffline], ); - const listLoader = useCallback( + const listLoader = useMemo( () => ( Date: Tue, 24 Oct 2023 12:11:32 +0200 Subject: [PATCH 10/21] Fix not displaying all errors --- src/components/Form/FormProvider.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js index ada40c24ed89..5047903040f4 100644 --- a/src/components/Form/FormProvider.js +++ b/src/components/Form/FormProvider.js @@ -103,6 +103,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC const inputRefs = useRef(null); const touchedInputs = useRef({}); const [inputValues, setInputValues] = useState({}); + const submitPressed = useRef(false); const [errors, setErrors] = useState({}); const hasServerError = useMemo(() => Boolean(formState) && !_.isEmpty(formState.errors), [formState]); @@ -159,7 +160,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}'); } - const touchedInputErrors = _.pick(validateErrors, (inputValue, inputID) => Boolean(touchedInputs.current[inputID])); + const touchedInputErrors = _.pick(validateErrors, (inputValue, inputID) => Boolean(touchedInputs.current[inputID]) || submitPressed.current); if (!_.isEqual(errors, touchedInputErrors)) { setErrors(touchedInputErrors); @@ -181,6 +182,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC ); const submit = useCallback(() => { + submitPressed.current = true; // Return early if the form is already submitting to avoid duplicate submission if (formState.isLoading) { return; From 677c2a51fa139cc905459aa19ea1fe7c8d9f54e2 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Tue, 24 Oct 2023 12:15:15 +0200 Subject: [PATCH 11/21] Fix prettier --- src/components/CheckboxWithLabel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 70fa972a5d2f..8ce08caad54b 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -8,7 +8,7 @@ import Text from './Text'; import FormHelpMessage from './FormHelpMessage'; import variables from '../styles/variables'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; -import refPropTypes from "./refPropTypes"; +import refPropTypes from './refPropTypes'; /** * Returns an error if the required props are not provided From c2f54d3866962daed3d19fcb4e892f233ecfc1a4 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Tue, 24 Oct 2023 16:18:03 +0200 Subject: [PATCH 12/21] Remove unused ref --- src/components/Form/FormProvider.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js index ecd9f2322cc5..add58dbef18c 100644 --- a/src/components/Form/FormProvider.js +++ b/src/components/Form/FormProvider.js @@ -103,7 +103,6 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC const inputRefs = useRef({}); const touchedInputs = useRef({}); const [inputValues, setInputValues] = useState({}); - const submitPressed = useRef(false); const [errors, setErrors] = useState({}); const hasServerError = useMemo(() => Boolean(formState) && !_.isEmpty(formState.errors), [formState]); @@ -160,7 +159,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}'); } - const touchedInputErrors = _.pick(validateErrors, (inputValue, inputID) => Boolean(touchedInputs.current[inputID]) || submitPressed.current); + const touchedInputErrors = _.pick(validateErrors, (inputValue, inputID) => Boolean(touchedInputs.current[inputID])); if (!_.isEqual(errors, touchedInputErrors)) { setErrors(touchedInputErrors); @@ -182,7 +181,6 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC ); const submit = useCallback(() => { - submitPressed.current = true; // Return early if the form is already submitting to avoid duplicate submission if (formState.isLoading) { return; From 06a6f2745b0c67a07ebd35a6a24fbb0539b37458 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Wed, 25 Oct 2023 12:12:47 +0200 Subject: [PATCH 13/21] Fix ref issue --- src/components/Form/InputWrapper.js | 3 ++- src/pages/settings/Wallet/AddDebitCardPage.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Form/InputWrapper.js b/src/components/Form/InputWrapper.js index 43064b5a6690..ab087b079e1f 100644 --- a/src/components/Form/InputWrapper.js +++ b/src/components/Form/InputWrapper.js @@ -1,12 +1,13 @@ import React, {forwardRef, useContext} from 'react'; import PropTypes from 'prop-types'; import FormContext from './FormContext'; +import refPropTypes from "../refPropTypes"; const propTypes = { InputComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.elementType]).isRequired, inputID: PropTypes.string.isRequired, valueType: PropTypes.string, - forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]), + forwardedRef: refPropTypes, }; const defaultProps = { diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index b9ea0ac6df3a..0ba631148070 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -129,7 +129,7 @@ function DebitCardPage(props) { label={translate('addDebitCardPage.nameOnCard')} accessibilityLabel={translate('addDebitCardPage.nameOnCard')} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} - ref={(ref) => (nameOnCardRef.current = ref)} + ref={nameOnCardRef} spellCheck={false} /> Date: Wed, 25 Oct 2023 12:19:47 +0200 Subject: [PATCH 14/21] Fix listLoader issue --- src/components/AddressSearch/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index a4f712435af3..59dd6c2c658d 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -369,7 +369,7 @@ function AddressSearch({ [isTyping, translate, network.isOffline], ); - const listLoader = useMemo( + const listLoader = useCallback( () => ( Date: Thu, 26 Oct 2023 16:33:22 +0200 Subject: [PATCH 15/21] Merge fixes --- src/components/AddressSearch/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 016826349b6f..3754102337b2 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -415,6 +415,16 @@ function AddressSearch({ listEmptyComponent={listEmptyComponent} listLoaderComponent={listLoader} renderHeaderComponent={renderHeaderComponent} + renderRow={(data) => { + const title = data.isPredefinedPlace ? data.name : data.structured_formatting.main_text; + const subtitle = data.isPredefinedPlace ? data.description : data.structured_formatting.secondary_text; + return ( + + {title && {title}} + {subtitle} + + ); + }} onPress={(data, details) => { saveLocationDetails(data, details); setIsTyping(false); From 211934d5b754aca11b2d7c0f992ba8253f8aff04 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 26 Oct 2023 16:56:00 +0200 Subject: [PATCH 16/21] Fix too early error message --- src/components/Form/FormProvider.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js index add58dbef18c..82a92d9d5974 100644 --- a/src/components/Form/FormProvider.js +++ b/src/components/Form/FormProvider.js @@ -71,6 +71,8 @@ const propTypes = { shouldValidateOnChange: PropTypes.bool, }; +const VALIDATE_DELAY = 200; + const defaultProps = { isSubmitButtonVisible: true, formState: { @@ -240,19 +242,26 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC // as this is already happening by the value prop. defaultValue: undefined, onTouched: (event) => { - setTouchedInput(inputID); - if (_.isFunction(propsToParse.onTouched)) { + setTimeout(() => { + setTouchedInput(inputID); + }, VALIDATE_DELAY); if (_.isFunction(propsToParse.onTouched)) { propsToParse.onTouched(event); } }, onPress: (event) => { - setTouchedInput(inputID); - if (_.isFunction(propsToParse.onPress)) { + setTimeout(() => { + setTouchedInput(inputID); + }, VALIDATE_DELAY); if (_.isFunction(propsToParse.onPress)) { propsToParse.onPress(event); } }, - onPressIn: (event) => { - setTouchedInput(inputID); + onPressOut: (event) => { + // To prevent validating just pressed inputs, we need to set the touched input right after + // onValidate and to do so, we need to delays setTouchedInput of the same amount of time + // as the onValidate is delayed + setTimeout(() => { + setTouchedInput(inputID); + }, VALIDATE_DELAY); if (_.isFunction(propsToParse.onPressIn)) { propsToParse.onPressIn(event); } @@ -268,7 +277,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC if (shouldValidateOnBlur) { onValidate(inputValues, !hasServerError); } - }, 200); + }, VALIDATE_DELAY); } if (_.isFunction(propsToParse.onBlur)) { From 674a0dcba8026f859585a43b724a21243ae7df2a Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 26 Oct 2023 17:48:51 +0200 Subject: [PATCH 17/21] Fix prettier --- src/components/AddressSearch/index.js | 8 ++++---- src/components/Form/FormProvider.js | 8 +++++--- src/components/Form/InputWrapper.js | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 3754102337b2..734839ccc4d2 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -419,10 +419,10 @@ function AddressSearch({ const title = data.isPredefinedPlace ? data.name : data.structured_formatting.main_text; const subtitle = data.isPredefinedPlace ? data.description : data.structured_formatting.secondary_text; return ( - - {title && {title}} - {subtitle} - + + {title && {title}} + {subtitle} + ); }} onPress={(data, details) => { diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js index 82a92d9d5974..52cd5cddf137 100644 --- a/src/components/Form/FormProvider.js +++ b/src/components/Form/FormProvider.js @@ -244,14 +244,16 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC onTouched: (event) => { setTimeout(() => { setTouchedInput(inputID); - }, VALIDATE_DELAY); if (_.isFunction(propsToParse.onTouched)) { + }, VALIDATE_DELAY); + if (_.isFunction(propsToParse.onTouched)) { propsToParse.onTouched(event); } }, onPress: (event) => { setTimeout(() => { setTouchedInput(inputID); - }, VALIDATE_DELAY); if (_.isFunction(propsToParse.onPress)) { + }, VALIDATE_DELAY); + if (_.isFunction(propsToParse.onPress)) { propsToParse.onPress(event); } }, @@ -260,7 +262,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC // onValidate and to do so, we need to delays setTouchedInput of the same amount of time // as the onValidate is delayed setTimeout(() => { - setTouchedInput(inputID); + setTouchedInput(inputID); }, VALIDATE_DELAY); if (_.isFunction(propsToParse.onPressIn)) { propsToParse.onPressIn(event); diff --git a/src/components/Form/InputWrapper.js b/src/components/Form/InputWrapper.js index ab7326673ebc..249221335148 100644 --- a/src/components/Form/InputWrapper.js +++ b/src/components/Form/InputWrapper.js @@ -1,7 +1,7 @@ import React, {forwardRef, useContext} from 'react'; import PropTypes from 'prop-types'; import FormContext from './FormContext'; -import refPropTypes from "../refPropTypes"; +import refPropTypes from '../refPropTypes'; const propTypes = { InputComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.elementType]).isRequired, From 6a4f127c801324c30671465db236c6a2cb73be04 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:16:50 +0100 Subject: [PATCH 18/21] fix lint errors and include new import method --- src/components/AddressSearch/index.js | 5 ++--- src/components/Form/InputWrapper.js | 3 ++- src/pages/settings/Wallet/AddDebitCardPage.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 2d550623a0ad..f8219c028853 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -1,8 +1,7 @@ -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {ActivityIndicator, Keyboard, LogBox, ScrollView, Text, View} from 'react-native'; -import lodashGet from 'lodash/get'; -import React, {useEffect, useMemo, useRef, useState} from 'react'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; import _ from 'underscore'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; diff --git a/src/components/Form/InputWrapper.js b/src/components/Form/InputWrapper.js index 5a97185aa89d..c49d11a5b075 100644 --- a/src/components/Form/InputWrapper.js +++ b/src/components/Form/InputWrapper.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React, {forwardRef, useContext} from 'react'; -import refPropTypes from '../refPropTypes'; +import refPropTypes from '@components/refPropTypes'; import FormContext from './FormContext'; const propTypes = { @@ -14,6 +14,7 @@ const propTypes = { const defaultProps = { forwardedRef: undefined, valueType: 'string', + valueParser: () => {}, }; function InputWrapper(props) { diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index 4dba77158653..8b8938a9ea42 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -1,11 +1,11 @@ +import PropTypes from 'prop-types'; import React, {useEffect, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import PropTypes from 'prop-types'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; import AddressSearch from '@components/AddressSearch'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import StatePicker from '@components/StatePicker'; From 0f6688b2204017be697fbedc8d1a865f7134dc6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tymoteusz=20Ka=C5=82uzie=C5=84ski?= <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:52:47 +0100 Subject: [PATCH 19/21] Update src/components/CheckboxWithLabel.js Co-authored-by: Rajat Parashar --- src/components/CheckboxWithLabel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index f18ec346dfa2..d8f18754c700 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -7,7 +7,7 @@ import variables from '@styles/variables'; import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; -import refPropTypes from './refPropTypes'; +import refPropTypes from '@components/refPropTypes'; import Text from './Text'; /** From b35ebf5e53165a62e27c01eb60db57572c9f761c Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:04:01 +0100 Subject: [PATCH 20/21] revert import to fix prettier and lint --- src/components/CheckboxWithLabel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index d8f18754c700..f18ec346dfa2 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -7,7 +7,7 @@ import variables from '@styles/variables'; import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; -import refPropTypes from '@components/refPropTypes'; +import refPropTypes from './refPropTypes'; import Text from './Text'; /** From 890dd5449d9d29fce8ff0cfec548acfdfd2ab262 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:54:11 +0100 Subject: [PATCH 21/21] remove unused code --- src/components/Form/InputWrapper.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Form/InputWrapper.js b/src/components/Form/InputWrapper.js index c49d11a5b075..b2e6f4477e89 100644 --- a/src/components/Form/InputWrapper.js +++ b/src/components/Form/InputWrapper.js @@ -8,13 +8,11 @@ const propTypes = { inputID: PropTypes.string.isRequired, valueType: PropTypes.string, forwardedRef: refPropTypes, - valueParser: PropTypes.func, }; const defaultProps = { forwardedRef: undefined, valueType: 'string', - valueParser: () => {}, }; function InputWrapper(props) {