import React, { useEffect, useState, useRef } from 'react'; import { StyleSheet, View, TouchableOpacity, Keyboard, ScrollView, } from 'react-native'; import { AddressForm as AddressFormController, useLanguage, useConfig, useSession, useOrder, useToast, ToastType } from 'ordering-components/native'; import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete'; import Spinner from 'react-native-loading-spinner-overlay'; import { useForm, Controller } from 'react-hook-form'; import Geocoder from 'react-native-geocoding'; import { OInput, OButton, OText, OModal, OIcon } from '../shared'; import { AddressFormParams } from '../../types'; import { getTraduction } from '../../utils'; import { useTheme } from 'styled-components/native'; import { GoogleMap } from '../GoogleMap'; import NavBar from '../NavBar'; import { AddressFormContainer, AutocompleteInput, IconsContainer, GoogleMapContainer, FormInput, } from './styles'; import { GPSButton } from '../GPSButton'; const inputNames = [ { name: 'address', code: 'Address' }, { name: 'internal_number', code: 'Internal number' }, { name: 'zipcode', code: 'Zipcode' }, { name: 'address_notes', code: 'Address notes' }, ]; const AddressFormUI = (props: AddressFormParams) => { const { navigation, updateChanges, address, formState, isEditing, handleChangeInput, addressState, addressesList, saveAddress, userCustomerSetup, isGuestUser, isRequiredField, isFromProductsList, hasAddressDefault, afterSignup, isSingleBusiness, singleBusiness } = props; const theme = useTheme(); const tagsName = [ { icon: theme.images.general.tag_home, value: 'home' }, { icon: theme.images.general.tag_building, value: 'office' }, { icon: theme.images.general.tag_heart, value: 'favorite' }, { icon: theme.images.general.tag_plus, value: 'other' }, ]; const styles = StyleSheet.create({ iconContainer: { display: 'flex', justifyContent: 'center', alignItems: 'center', paddingHorizontal: 18, paddingVertical: 5, backgroundColor: theme.colors.clear }, inputsStyle: { borderColor: theme.colors.border, borderRadius: 10, marginBottom: 20, height: 50, maxHeight: 50, minHeight: 50, }, textAreaStyles: { borderColor: theme.colors.border, borderRadius: 10, marginBottom: 20, height: 104, maxHeight: 104, textAlignVertical: 'top', alignItems: 'flex-start', }, pinIcon: { position: 'absolute', end: 0, top: 12, zIndex: 1002, }, }); const [, t] = useLanguage(); const [{ auth }] = useSession(); const [, { showToast }] = useToast(); const [configState] = useConfig(); const [orderState] = useOrder(); const { handleSubmit, errors, control, setValue } = useForm(); const [toggleMap, setToggleMap] = useState(false); const [alertState, setAlertState] = useState<{ open: boolean; content: Array; key?: string | null; }>({ open: false, content: [], key: null }); const [addressTag, setAddressTag] = useState(addressState?.address?.tag); const [firstLocationNoEdit, setFirstLocationNoEdit] = useState({ value: { lat: null, lng: null }, address: null, }); const [isFirstTime, setIsFirstTime] = useState(true); const [locationChange, setLocationChange] = useState( isEditing ? addressState?.address?.location : formState.changes?.location ?? null, ); const [saveMapLocation, setSaveMapLocation] = useState(false); const [isKeyboardShow, setIsKeyboardShow] = useState(false); const [isSignUpEffect, setIsSignUpEffect] = useState(false); const [hasEditing, setAddressEditing] = useState(false); const googleInput: any = useRef(null); const internalNumberRef: any = useRef(null); const zipCodeRef: any = useRef(null); const addressNotesRef: any = useRef(null); const googleMapsApiKey = configState?.configs?.google_maps_api_key?.value; const isLocationRequired = configState.configs?.google_autocomplete_selection_required?.value === '1' || configState.configs?.google_autocomplete_selection_required?.value === 'true'; const maxLimitLocation = configState?.configs?.meters_to_change_address?.value; const continueAsGuest = () => { if (isSingleBusiness) { navigation.navigate('Business', {store: singleBusiness}); } else { navigation.navigate('BusinessList'); } }; const goToBack = () => navigation?.canGoBack() && navigation.goBack(); const getAddressFormatted = (address: any) => { const data: any = { address: null, error: null }; Geocoder.init(googleMapsApiKey); Geocoder.from(address) .then((json) => { if (json.results && json.results?.length > 0) { let postalCode = null; for (const component of json.results?.[0].address_components) { const addressType = component.types?.[0]; if (addressType === 'postal_code') { postalCode = component.short_name; break; } } data.address = { address, location: json.results[0].geometry.location, map_data: { library: 'google', place_id: json.results?.[0].place_id, }, }; if (postalCode) { data.address.zipcode = postalCode; } const arrayList = isEditing ? addressesList?.addresses?.filter( (address: any) => address.id !== addressState?.address?.id, ) || [] : addressesList || []; const addressToCompare = isEditing ? { ...addressState.address, ...data.address, ...formState.changes } : { ...data.address, ...formState?.changes }; const isAddressAlreadyExist = arrayList .map((address: any) => checkAddress(address, addressToCompare)) .some((value: any) => value) ?? false; if (!isAddressAlreadyExist) { saveAddress(data.address); if (isGuestUser) { continueAsGuest(); } if (!isGuestUser && !auth) { !isFromProductsList ? navigation.navigate('Business') : navigation?.canGoBack() && navigation.goBack(); } return; } setAlertState({ open: true, content: [t('ADDRESS_ALREADY_EXIST', 'The address already exists')], }); } else { setAlertState({ open: true, content: [t('ERROR_NOT_FOUND_ADDRESS', 'Error, address not found')], }); } }) .catch((error) => { setAlertState({ open: true, content: [error?.message || error?.toString()], }); }); }; const onSubmit = () => { if ( !auth && formState?.changes?.address === '' && addressState?.address?.address ) { setAlertState({ open: true, content: [ t( 'VALIDATION_ERROR_ADDRESS_REQUIRED', 'The field Address is required', ), ], }); return; } if (formState?.changes?.address && !formState?.changes?.location) { if (isLocationRequired) { setAlertState({ open: true, content: [ t( 'ADDRESS_REQUIRE_LOCATION', 'The address needs a location, please select one of the suggested', ), ], }); return; } getAddressFormatted(formState?.changes?.address); return; } const arrayList = isEditing ? addressesList?.addresses?.filter( (address: any) => address.id !== addressState?.address?.id, ) || [] : addressesList || []; const addressToCompare = isEditing ? { ...addressState.address, ...formState.changes } : formState?.changes; const isAddressAlreadyExist = arrayList .map((address: any) => checkAddress(address, addressToCompare)) .some((value: any) => value) ?? false; if (!isAddressAlreadyExist) { saveAddress(); if (isGuestUser) { continueAsGuest(); } if (!isGuestUser && !auth && !afterSignup) { !isFromProductsList ? navigation.navigate('Business') : navigation?.canGoBack() && navigation.goBack(); } return; } setAlertState({ open: true, content: [t('ADDRESS_ALREADY_EXIST', 'The address already exists')], }); }; /** * Returns true when the user made no changes * @param {object} address */ const checkAddress = (address: any, addressToCompare: any) => { const props = [ 'address', 'address_notes', 'zipcode', 'location', 'internal_number', ]; const values: any = []; props.forEach((prop) => { if (addressToCompare[prop]) { if (prop === 'location') { values.push( address[prop]?.lat === addressToCompare[prop]?.lat && address[prop]?.lng === addressToCompare[prop]?.lng, ); } else { values.push(address[prop] === addressToCompare[prop]); } } else { values.push(!address[prop]); } }); return values.every((value: any) => value); }; const handleChangeAddress = (data: any, details: any) => { const addressSelected = { address: data?.description || data?.address, location: details?.geometry?.location, utc_offset: details?.utc_offset || null, map_data: { library: 'google', place_id: data.place_id }, zip_code: data?.zip_code || null, }; updateChanges(addressSelected); }; const handleAddressTag = (tag: string) => { setAddressTag(tag); handleChangeInput({ target: { name: 'tag', value: tag, }, }); }; const handleToggleMap = () => { setToggleMap(!toggleMap); }; useEffect(() => { if ( orderState.loading && !addressesList && orderState.options.address && auth && !afterSignup ) { !isFromProductsList ? navigation.navigate('BottomTab') : navigation.navigate('Business'); } }, [orderState.options.address]); useEffect(() => { if (alertState.open && alertState?.key !== 'ERROR_MAX_LIMIT_LOCATION') { alertState.content && showToast(ToastType.Error, alertState.content); } }, [alertState.content]); useEffect(() => { if (!auth) { inputNames.forEach((field) => setValue( field.name, formState?.changes[field.name] || (orderState?.options?.address && orderState?.options?.address[field.name]) || '', ), ); return; } if (!formState.loading && formState.result?.error) { setAlertState({ open: true, content: formState.result?.result || [t('ERROR', 'Error')], }); } setValue( 'address', formState?.changes?.address ?? addressState.address?.address ?? '', ); if (!isEditing) { formState?.changes?.address && setLocationChange(formState?.changes?.location); if ( formState?.changes?.address && formState?.changes?.address !== firstLocationNoEdit?.address && formState?.changes?.location && formState?.changes?.location?.lat !== firstLocationNoEdit.value?.lat && formState?.changes?.location?.lng !== firstLocationNoEdit.value?.lng ) { setFirstLocationNoEdit({ value: formState?.changes?.location, address: formState?.changes?.address, }); } } if (isEditing) { if (formState?.changes?.location) { const prevLocation = { lat: Math.trunc(locationChange.lat), lng: Math.trunc(locationChange.lng), }; const newLocation = { lat: Math.trunc(formState?.changes?.location?.lat), lng: Math.trunc(formState?.changes?.location?.lng), }; if ( prevLocation.lat !== newLocation.lat && prevLocation.lng !== newLocation.lng ) { setLocationChange(formState?.changes?.location); } } } }, [formState]); useEffect(() => { if (formState?.result?.result && !formState?.loading) { if (formState.result?.error) { showToast(ToastType.Error, formState.result.result); } else { showToast( ToastType.Success, t('UPDATE_SUCCESSFULLY', 'Update successfully'), ); } } }, [formState.result]); useEffect(() => { if (Object.keys(errors).length > 0) { // Convert all errors in one string to show in toast provider const list = Object.values(errors); let stringError = ''; list.map((item: any, i: number) => { stringError += i + 1 === list.length ? `- ${item.message}` : `- ${item.message}\n`; }); showToast(ToastType.Error, stringError); } }, [errors]); useEffect(() => { if (googleInput?.current) { googleInput?.current?.setAddressText( address?.address || formState.changes?.address || addressState.address.address || '', ); } }, []); useEffect(() => { const keyboardDidShowListener = Keyboard.addListener( 'keyboardDidShow', () => { setIsKeyboardShow(true); }, ); const keyboardDidHideListener = Keyboard.addListener( 'keyboardDidHide', () => { setIsKeyboardShow(false); }, ); return () => { keyboardDidShowListener.remove(); keyboardDidHideListener.remove(); }; }, []); useEffect(() => { if (!orderState.loading && auth && !hasAddressDefault && isSignUpEffect) { navigation.navigate('BottomTab'); } setIsSignUpEffect(true); }, [orderState.loading]); return ( <> ( { handleChangeAddress(data, details); }} query={{ key: googleMapsApiKey }} fetchDetails ref={googleInput} textInputProps={{ onChangeText: (text) => { if (!isFirstTime) { handleChangeInput({ target: { name: 'address', value: text }, }); setValue('address', text); } setIsFirstTime(false); setAddressEditing(text.length == 0); }, onSubmitEditing: () => internalNumberRef?.current?.focus?.(), autoCorrect: false, blurOnSubmit: false, returnKeyType: 'next', }} onFail={(error) => setAlertState({ open: true, content: getTraduction(error), }) } styles={{ listView: { position: 'relative', borderBottomStartRadius: 15, borderBottomEndRadius: 15, elevation: 2, borderWidth: 1, borderColor: '#ddd', bottom: 20, }, container: { zIndex: 100, }, textInput: { borderWidth: 1, borderRadius: 7.6, borderColor: theme.colors.border, flexGrow: 1, fontSize: 15, paddingHorizontal: 16, minHeight: 50, fontFamily: 'Poppins-Regular', marginBottom: 24, }, }} /> )} /> {(hasEditing && !formState?.changes?.address) ? ( { handleChangeAddress(data, detail); setValue('address', data.address); if (googleInput?.current) { googleInput?.current?.setAddressText( data.address ); } }} IconButton={} /> ) : null} {(locationChange || formState.changes?.location) && ( )} ( { handleChangeInput(text); setValue('internal_number', text); }} value={ address?.internal_number || formState.changes?.internal_number || addressState?.address?.internal_number || '' } style={{ ...styles.inputsStyle, marginRight: 24 }} forwardRef={internalNumberRef} returnKeyType="next" onSubmitEditing={() => zipCodeRef?.current?.focus?.()} blurOnSubmit={false} /> )} /> ( { handleChangeInput(text); setValue('zipcode', text); }} value={ address?.zipcode || formState.changes?.zipcode || addressState.address.zipcode || '' } style={styles.inputsStyle} forwardRef={zipCodeRef} returnKeyType="next" onSubmitEditing={() => addressNotesRef?.current?.focus?.()} blurOnSubmit={false} /> )} /> ( { handleChangeInput(text); setValue('address_notes', text); }} value={ address?.address_notes || formState.changes?.address_notes || addressState.address.address_notes || '' } multiline style={styles.textAreaStyles} returnKeyType="done" forwardRef={addressNotesRef} blurOnSubmit /> )} /> {!isKeyboardShow && ( {tagsName.map((tag) => ( handleAddressTag(tag.value)}> ))} )} {Object.keys(formState?.changes).length > 0 ? ( ) : ( navigation?.canGoBack() && navigation.goBack()} /> )} handleToggleMap()} entireModal customClose> {(locationChange || formState.changes?.location) && ( )} setSaveMapLocation(true)} /> ); }; export const AddressForm = (props: AddressFormParams) => { const addressFormProps = { ...props, UIComponent: AddressFormUI, }; return ; };