2020-05-06 10:21:30 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import defaultAddressFields from '@woocommerce/base-components/cart-checkout/address-form/default-address-fields';
|
2020-05-14 00:05:09 +00:00
|
|
|
import { useState, useEffect, useCallback } from '@wordpress/element';
|
2020-05-06 10:21:30 +00:00
|
|
|
import {
|
|
|
|
useShippingDataContext,
|
|
|
|
useBillingDataContext,
|
|
|
|
useCheckoutContext,
|
|
|
|
} from '@woocommerce/base-context';
|
|
|
|
import { isEqual } from 'lodash';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare two addresses and see if they are the same.
|
|
|
|
*
|
|
|
|
* @param {Object} address1 First address.
|
|
|
|
* @param {Object} address2 Second address.
|
|
|
|
*/
|
|
|
|
const isSameAddress = ( address1, address2 ) => {
|
|
|
|
return Object.keys( defaultAddressFields ).every(
|
|
|
|
( field ) => address1[ field ] === address2[ field ]
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Custom hook for tracking address field state on checkout and persisting it to
|
|
|
|
* context globally on change.
|
|
|
|
*/
|
|
|
|
export const useCheckoutAddress = () => {
|
|
|
|
const { customerId } = useCheckoutContext();
|
|
|
|
const {
|
|
|
|
shippingAddress,
|
|
|
|
setShippingAddress,
|
|
|
|
needsShipping,
|
|
|
|
} = useShippingDataContext();
|
|
|
|
const { billingData, setBillingData } = useBillingDataContext();
|
|
|
|
const [ billingFields, updateBillingFields ] = useState( billingData );
|
|
|
|
|
|
|
|
// This tracks the state of the "shipping as billing" address checkbox. It's
|
|
|
|
// initial value is true (if shipping is needed), however, if the user is
|
|
|
|
// logged in and they have a different billing address, we can toggle this off.
|
|
|
|
const [ shippingAsBilling, setShippingAsBilling ] = useState(
|
|
|
|
() =>
|
|
|
|
needsShipping &&
|
|
|
|
( ! customerId || isSameAddress( shippingAddress, billingData ) )
|
|
|
|
);
|
|
|
|
|
|
|
|
// Pushes to global state when changes are made locally.
|
|
|
|
useEffect( () => {
|
2020-05-14 00:05:09 +00:00
|
|
|
// Uses shipping address or billing fields depending on shippingAsBilling checkbox, but ensures
|
2020-05-13 14:39:26 +00:00
|
|
|
// billing only fields are also included.
|
|
|
|
const newBillingData = {
|
2020-05-14 00:05:09 +00:00
|
|
|
...( shippingAsBilling ? shippingAddress : billingFields ),
|
|
|
|
email: billingFields.email || billingData.email,
|
|
|
|
phone: billingFields.phone || billingData.phone,
|
2020-05-13 14:39:26 +00:00
|
|
|
};
|
2020-05-06 10:21:30 +00:00
|
|
|
|
|
|
|
if ( ! isEqual( newBillingData, billingData ) ) {
|
|
|
|
setBillingData( newBillingData );
|
|
|
|
}
|
2020-05-13 14:39:26 +00:00
|
|
|
}, [
|
|
|
|
billingFields,
|
|
|
|
shippingAsBilling,
|
|
|
|
billingData,
|
|
|
|
shippingAddress,
|
|
|
|
setBillingData,
|
|
|
|
setShippingAddress,
|
|
|
|
] );
|
2020-05-06 10:21:30 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper for updateBillingFields (from useState) which handles merging.
|
|
|
|
*
|
|
|
|
* @param {Object} newValues New values to store to state.
|
|
|
|
*/
|
2020-05-14 00:05:09 +00:00
|
|
|
const setBillingFields = useCallback(
|
|
|
|
( newValues ) =>
|
|
|
|
void updateBillingFields( ( prevState ) => ( {
|
|
|
|
...prevState,
|
|
|
|
...newValues,
|
|
|
|
} ) ),
|
|
|
|
[]
|
|
|
|
);
|
2020-05-06 10:21:30 +00:00
|
|
|
|
|
|
|
const setEmail = ( value ) => void setBillingFields( { email: value } );
|
|
|
|
const setPhone = ( value ) => void setBillingFields( { phone: value } );
|
|
|
|
|
|
|
|
return {
|
|
|
|
defaultAddressFields,
|
2020-05-14 00:05:09 +00:00
|
|
|
shippingFields: shippingAddress,
|
|
|
|
setShippingFields: setShippingAddress,
|
2020-05-06 10:21:30 +00:00
|
|
|
billingFields,
|
|
|
|
setBillingFields,
|
|
|
|
setEmail,
|
|
|
|
setPhone,
|
|
|
|
shippingAsBilling,
|
|
|
|
setShippingAsBilling,
|
|
|
|
showBillingFields: ! needsShipping || ! shippingAsBilling,
|
|
|
|
};
|
|
|
|
};
|