import { useState } from "react"; import { Alert } from "react-native"; import { AddressForm } from "./address-form"; import { Address, FormattedAddress, getGoogleAddressFromGooglesAddressComponents, getMissingTypeErrors, validateAddress, } from "./utils"; import { AddressSelect } from "./address-select"; import { InvalidAddressConfirmation } from "./invalid-address-confirmation"; import { RegionCode } from "./region-codes"; type Frame = | "ADDRESS_INPUT" | "CHOOSE_VALID_ADDRESS" | "MISSING_INFORMATION" | "PROGRESS_WITH_INVALID_ADDRESS"; export const AddressValidation = ({ address, googlePlacesApiKey, googleAddressValidationApiKey, onChange, streetOneLabel = "Street Address", streetOnePlaceholder = "1600 Amphitheatre Pkwy", streetTwoLabel = "Line 2", streetTwoPlaceholder = "Apt, suite, or unit", localityLabel = "City", localityPlaceholder = "City", localityClarifier, administrativeAreaLabel = "State", administrativeAreaPlaceholder = "State", postalCodeLabel = "Zip Code", postalCodePlaceholder = "123456", regionCodeLabel = "Country", continueLabel = "Continue", onFinish, regionCodes, enableUspsCass = false, ...props }: { address: Address; googlePlacesApiKey: string; googleAddressValidationApiKey: string; onChange: (address: Address) => void; streetOneLabel?: string; streetOnePlaceholder?: string; streetTwoLabel?: string; streetTwoPlaceholder?: string; localityLabel?: string; localityPlaceholder?: string; localityClarifier?: string; administrativeAreaLabel?: string; administrativeAreaPlaceholder?: string; postalCodeLabel?: string; postalCodePlaceholder?: string; regionCodeLabel?: string; continueLabel?: string; onFinish: (address: FormattedAddress) => void; regionCodes?: RegionCode[]; enableUspsCass?: boolean; }) => { const [frame, setFrame] = useState("ADDRESS_INPUT"); const [showFormErrors, setShowFormErrors] = useState(false); const [validationLoading, setValidationLoading] = useState(false); const [googleAddress, setGoogleAddress] = useState( null ); const [missingDataErrors, setMissingDataErrors] = useState & { regionCode: string; } > | null>(null); const onSubmitForm = async () => { setShowFormErrors(true); const addressValidationErrors = validateAddress(address); const addressHasRequiredElements = Object.keys(addressValidationErrors).length === 0; setValidationLoading(true); if (missingDataErrors) { setMissingDataErrors(null); } const response = await fetch( `https://addressvalidation.googleapis.com/v1:validateAddress?key=${googleAddressValidationApiKey}`, { method: "POST", body: JSON.stringify({ address: { addressLines: address.streetTwo ? [address.streetOne, address.streetTwo] : [address.streetOne], locality: address.locality, administrativeArea: address.administrativeArea, postalCode: address.postalCode, regionCode: address.regionCode, }, enableUspsCass: enableUspsCass, }), } ); const data = await response.json(); setValidationLoading(false); // perfect as is if ( addressHasRequiredElements && data?.result?.verdict?.addressComplete === true && data?.result?.verdict?.hasInferredComponents === false && (data?.result?.verdict?.validationGranularity === "SUB_PREMISE" || data?.result?.verdict?.validationGranularity === "PREMISE") ) { onFinish({ streetOne: address.streetOne, streetTwo: address.streetTwo, locality: address.locality, administrativeArea: address.administrativeArea, postalCode: address.postalCode, country: address.regionCode, }); } else if ( // google suggested changes addressHasRequiredElements && data?.result?.verdict?.addressComplete === true && (data?.result?.verdict?.validationGranularity === "SUB_PREMISE" || data?.result?.verdict?.validationGranularity === "PREMISE") && !data?.result?.address?.missingComponentTypes ) { const googleFormattedAddress = getGoogleAddressFromGooglesAddressComponents( data.result.address.addressComponents ); if (googleFormattedAddress.locality.length > 17) { setFrame("PROGRESS_WITH_INVALID_ADDRESS"); } else { setGoogleAddress(googleFormattedAddress); setFrame("CHOOSE_VALID_ADDRESS"); } } else if (data?.result?.address?.missingComponentTypes?.length > 0) { const missingData = getMissingTypeErrors( data.result.address.missingComponentTypes ); setMissingDataErrors(missingData); setFrame("MISSING_INFORMATION"); } else if (addressHasRequiredElements) { // couldn't validate against google but has necessary components, allow progression with confirmation setFrame("PROGRESS_WITH_INVALID_ADDRESS"); } else { // could not validate Alert.alert( "Invalid Address", "The entered address could not be verified, please correct the address and try again." ); } }; if (frame === "ADDRESS_INPUT" || frame === "MISSING_INFORMATION") { return ( ); } if (frame === "CHOOSE_VALID_ADDRESS") { return ( { setFrame("ADDRESS_INPUT"); setGoogleAddress(null); }} onConfirm={(confirmedAddress) => { onFinish(confirmedAddress); }} {...props} /> ); } if (frame === "PROGRESS_WITH_INVALID_ADDRESS") { return ( { setFrame("ADDRESS_INPUT"); }} onConfirm={() => onFinish({ streetOne: address.streetOne, streetTwo: address.streetTwo, locality: address.locality, administrativeArea: address.administrativeArea, postalCode: address.postalCode, country: address.regionCode, }) } {...props} /> ); } return null; };