Skip to content

Commit

Permalink
Merge pull request #30708 from Expensify/beaman-revertFormRefactorAdd…
Browse files Browse the repository at this point in the history
…DebitCardPge

Revert "[Form Provider Refactor] AddDebitCardPage"
  • Loading branch information
tgolen authored Nov 1, 2023
2 parents 0f21275 + 3488601 commit 1921a93
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 132 deletions.
150 changes: 62 additions & 88 deletions src/components/AddressSearch/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {useEffect, useMemo, useRef, useState} from 'react';
import {ActivityIndicator, Keyboard, LogBox, ScrollView, Text, View} from 'react-native';
import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete';
import _ from 'underscore';
Expand Down Expand Up @@ -140,46 +140,27 @@ const defaultProps = {
resultTypes: 'address',
};

function AddressSearch({
canUseCurrentLocation,
containerStyles,
defaultValue,
errorText,
hint,
innerRef,
inputID,
isLimitedToUSA,
label,
maxInputLength,
network,
onBlur,
onInputChange,
onPress,
predefinedPlaces,
preferredLocale,
renamedInputKeys,
resultTypes,
shouldSaveDraft,
translate,
value,
}) {
// 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) {
const [displayListViewBorder, setDisplayListViewBorder] = useState(false);
const [isTyping, setIsTyping] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const [searchValue, setSearchValue] = useState(value || defaultValue || '');
const [searchValue, setSearchValue] = useState(props.value || props.defaultValue || '');
const [locationErrorCode, setLocationErrorCode] = useState(null);
const [isFetchingCurrentLocation, setIsFetchingCurrentLocation] = useState(false);
const shouldTriggerGeolocationCallbacks = useRef(true);
const containerRef = useRef();
const query = useMemo(
() => ({
language: preferredLocale,
types: resultTypes,
components: isLimitedToUSA ? 'country:us' : undefined,
language: props.preferredLocale,
types: props.resultTypes,
components: props.isLimitedToUSA ? 'country:us' : undefined,
}),
[preferredLocale, resultTypes, isLimitedToUSA],
[props.preferredLocale, props.resultTypes, props.isLimitedToUSA],
);
const shouldShowCurrentLocationButton = canUseCurrentLocation && searchValue.trim().length === 0 && isFocused;
const shouldShowCurrentLocationButton = props.canUseCurrentLocation && searchValue.trim().length === 0 && isFocused;

const saveLocationDetails = (autocompleteData, details) => {
const addressComponents = details.address_components;
Expand All @@ -188,7 +169,7 @@ function AddressSearch({
// 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)) {
onPress({
props.onPress({
address: lodashGet(details, 'description'),
lat: lodashGet(details, 'geometry.location.lat', 0),
lng: lodashGet(details, 'geometry.location.lng', 0),
Expand Down Expand Up @@ -275,7 +256,7 @@ function AddressSearch({

// 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 renamedInputKeys.street2 === 'undefined') {
if (subpremise && typeof props.renamedInputKeys.street2 === 'undefined') {
values.street += `, ${subpremise}`;
}

Expand All @@ -284,19 +265,19 @@ function AddressSearch({
values.country = country;
}

if (inputID) {
_.each(values, (inputValue, key) => {
const inputKey = lodashGet(renamedInputKeys, key, key);
if (props.inputID) {
_.each(values, (value, key) => {
const inputKey = lodashGet(props.renamedInputKeys, key, key);
if (!inputKey) {
return;
}
onInputChange(inputValue, inputKey);
props.onInputChange(value, inputKey);
});
} else {
onInputChange(values);
props.onInputChange(values);
}

onPress(values);
props.onPress(values);
};

/** Gets the user's current location and registers success/error callbacks */
Expand Down Expand Up @@ -326,7 +307,7 @@ function AddressSearch({
lng: successData.coords.longitude,
address: CONST.YOUR_LOCATION_TEXT,
};
onPress(location);
props.onPress(location);
},
(errorData) => {
if (!shouldTriggerGeolocationCallbacks.current) {
Expand All @@ -344,16 +325,16 @@ function AddressSearch({
};

const renderHeaderComponent = () =>
predefinedPlaces.length > 0 && (
props.predefinedPlaces.length > 0 && (
<>
{/* This will show current location button in list if there are some recent destinations */}
{shouldShowCurrentLocationButton && (
<CurrentLocationButton
onPress={getCurrentLocation}
isDisabled={network.isOffline}
isDisabled={props.network.isOffline}
/>
)}
{!value && <Text style={[styles.textLabel, styles.colorMuted, styles.pv2, styles.ph3, styles.overflowAuto]}>{translate('common.recentDestinations')}</Text>}
{!props.value && <Text style={[styles.textLabel, styles.colorMuted, styles.pv2, styles.ph3, styles.overflowAuto]}>{props.translate('common.recentDestinations')}</Text>}
</>
);

Expand All @@ -365,26 +346,6 @@ function AddressSearch({
};
}, []);

const listEmptyComponent = useCallback(
() =>
network.isOffline || !isTyping ? null : (
<Text style={[styles.textLabel, styles.colorMuted, styles.pv4, styles.ph3, styles.overflowAuto]}>{translate('common.noResultsFound')}</Text>
),
[isTyping, translate, network.isOffline],
);

const listLoader = useCallback(
() => (
<View style={[styles.pv4]}>
<ActivityIndicator
color={themeColors.spinner}
size="small"
/>
</View>
),
[],
);

return (
/*
* The GooglePlacesAutocomplete component uses a VirtualizedList internally,
Expand All @@ -411,10 +372,20 @@ function AddressSearch({
fetchDetails
suppressDefaultStyles
enablePoweredByContainer={false}
predefinedPlaces={predefinedPlaces}
listEmptyComponent={listEmptyComponent}
listLoaderComponent={listLoader}
renderHeaderComponent={renderHeaderComponent}
predefinedPlaces={props.predefinedPlaces}
listEmptyComponent={
props.network.isOffline || !isTyping ? null : (
<Text style={[styles.textLabel, styles.colorMuted, styles.pv4, styles.ph3, styles.overflowAuto]}>{props.translate('common.noResultsFound')}</Text>
)
}
listLoaderComponent={
<View style={[styles.pv4]}>
<ActivityIndicator
color={themeColors.spinner}
size="small"
/>
</View>
}
renderRow={(data) => {
const title = data.isPredefinedPlace ? data.name : data.structured_formatting.main_text;
const subtitle = data.isPredefinedPlace ? data.description : data.structured_formatting.secondary_text;
Expand All @@ -425,6 +396,7 @@ function AddressSearch({
</View>
);
}}
renderHeaderComponent={renderHeaderComponent}
onPress={(data, details) => {
saveLocationDetails(data, details);
setIsTyping(false);
Expand All @@ -439,31 +411,34 @@ function AddressSearch({
query={query}
requestUrl={{
useOnPlatform: 'all',
url: network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}),
url: props.network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}),
}}
textInputProps={{
InputComp: TextInput,
ref: (node) => {
if (!innerRef) {
if (!props.innerRef) {
return;
}

if (_.isFunction(innerRef)) {
innerRef(node);
if (_.isFunction(props.innerRef)) {
props.innerRef(node);
return;
}

// eslint-disable-next-line no-param-reassign
innerRef.current = node;
props.innerRef.current = node;
},
label,
containerStyles,
errorText,
hint: displayListViewBorder || (predefinedPlaces.length === 0 && shouldShowCurrentLocationButton) || (canUseCurrentLocation && isTyping) ? undefined : hint,
value,
defaultValue,
inputID,
shouldSaveDraft,
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,
onFocus: () => {
setIsFocused(true);
},
Expand All @@ -473,24 +448,24 @@ function AddressSearch({
setIsFocused(false);
setIsTyping(false);
}
onBlur();
props.onBlur();
},
autoComplete: 'off',
onInputChange: (text) => {
setSearchValue(text);
setIsTyping(true);
if (inputID) {
onInputChange(text);
if (props.inputID) {
props.onInputChange(text);
} else {
onInputChange({street: text});
props.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(predefinedPlaces)) {
if (_.isEmpty(text) && _.isEmpty(props.predefinedPlaces)) {
setDisplayListViewBorder(false);
}
},
maxLength: maxInputLength,
maxLength: props.maxInputLength,
spellCheck: false,
}}
styles={{
Expand All @@ -511,18 +486,17 @@ function AddressSearch({
}}
inbetweenCompo={
// We want to show the current location button even if there are no recent destinations
predefinedPlaces.length === 0 && shouldShowCurrentLocationButton ? (
props.predefinedPlaces.length === 0 && shouldShowCurrentLocationButton ? (
<View style={[StyleUtils.getGoogleListViewStyle(true), styles.overflowAuto, styles.borderLeft, styles.borderRight]}>
<CurrentLocationButton
onPress={getCurrentLocation}
isDisabled={network.isOffline}
isDisabled={props.network.isOffline}
/>
</View>
) : (
<></>
)
}
placeholder=""
/>
<LocationErrorMessage
onClose={() => setLocationErrorCode(null)}
Expand Down
3 changes: 1 addition & 2 deletions src/components/CheckboxWithLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import variables from '@styles/variables';
import Checkbox from './Checkbox';
import FormHelpMessage from './FormHelpMessage';
import PressableWithFeedback from './Pressable/PressableWithFeedback';
import refPropTypes from './refPropTypes';
import Text from './Text';

/**
Expand Down Expand Up @@ -55,7 +54,7 @@ const propTypes = {
defaultValue: PropTypes.bool,

/** React ref being forwarded to the Checkbox input */
forwardedRef: refPropTypes,
forwardedRef: PropTypes.func,

/** The ID used to uniquely identify the input in a Form */
/* eslint-disable-next-line react/no-unused-prop-types */
Expand Down
21 changes: 5 additions & 16 deletions src/components/Form/FormProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ const propTypes = {
shouldValidateOnChange: PropTypes.bool,
};

const VALIDATE_DELAY = 200;

const defaultProps = {
isSubmitButtonVisible: true,
formState: {
Expand Down Expand Up @@ -248,28 +246,19 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
// as this is already happening by the value prop.
defaultValue: undefined,
onTouched: (event) => {
setTimeout(() => {
setTouchedInput(inputID);
}, VALIDATE_DELAY);
setTouchedInput(inputID);
if (_.isFunction(propsToParse.onTouched)) {
propsToParse.onTouched(event);
}
},
onPress: (event) => {
setTimeout(() => {
setTouchedInput(inputID);
}, VALIDATE_DELAY);
setTouchedInput(inputID);
if (_.isFunction(propsToParse.onPress)) {
propsToParse.onPress(event);
}
},
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);
onPressIn: (event) => {
setTouchedInput(inputID);
if (_.isFunction(propsToParse.onPressIn)) {
propsToParse.onPressIn(event);
}
Expand All @@ -285,7 +274,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
if (shouldValidateOnBlur) {
onValidate(inputValues, !hasServerError);
}
}, VALIDATE_DELAY);
}, 200);
}

if (_.isFunction(propsToParse.onBlur)) {
Expand Down
3 changes: 1 addition & 2 deletions src/components/Form/InputWrapper.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import PropTypes from 'prop-types';
import React, {forwardRef, useContext} from 'react';
import refPropTypes from '@components/refPropTypes';
import FormContext from './FormContext';

const propTypes = {
InputComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.elementType]).isRequired,
inputID: PropTypes.string.isRequired,
valueType: PropTypes.string,
forwardedRef: refPropTypes,
forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]),
};

const defaultProps = {
Expand Down
Loading

0 comments on commit 1921a93

Please sign in to comment.