2020-02-25 11:36:53 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2020-03-19 11:50:51 +00:00
|
|
|
import { select } from '@wordpress/data-controls';
|
2020-02-25 11:36:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { ACTION_TYPES as types } from './action-types';
|
2020-03-09 02:09:47 +00:00
|
|
|
import { STORE_KEY as CART_STORE_KEY } from './constants';
|
2020-03-19 11:50:51 +00:00
|
|
|
import { apiFetchWithHeaders } from '../shared-controls';
|
2020-02-25 11:36:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an action object used in updating the store with the provided items
|
|
|
|
* retrieved from a request using the given querystring.
|
|
|
|
*
|
|
|
|
* This is a generic response action.
|
|
|
|
*
|
|
|
|
* @param {Object} [response={}] An object containing the response from the
|
|
|
|
* request.
|
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
|
|
|
export function receiveCart( response = {} ) {
|
|
|
|
return {
|
|
|
|
type: types.RECEIVE_CART,
|
|
|
|
response,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an action object used for receiving customer facing errors from the
|
|
|
|
* API.
|
|
|
|
*
|
|
|
|
* @param {Object} [error={}] An error object containing the error message
|
|
|
|
* and response code.
|
|
|
|
* @param {boolean} [replace=true] Should existing errors be replaced, or should
|
|
|
|
* the error be appended.
|
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
|
|
|
export function receiveError( error = {}, replace = true ) {
|
|
|
|
return {
|
|
|
|
type: replace ? types.REPLACE_ERRORS : types.RECEIVE_ERROR,
|
|
|
|
error,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an action object used to track when a coupon is applying.
|
|
|
|
*
|
|
|
|
* @param {string} [couponCode] Coupon being added.
|
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
|
|
|
export function receiveApplyingCoupon( couponCode ) {
|
|
|
|
return {
|
|
|
|
type: types.APPLYING_COUPON,
|
|
|
|
couponCode,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an action object used to track when a coupon is removing.
|
|
|
|
*
|
|
|
|
* @param {string} [couponCode] Coupon being removed.
|
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
|
|
|
export function receiveRemovingCoupon( couponCode ) {
|
|
|
|
return {
|
|
|
|
type: types.REMOVING_COUPON,
|
|
|
|
couponCode,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
/**
|
|
|
|
* Returns an action object for updating a single cart item in the store.
|
|
|
|
*
|
|
|
|
* @param {Object} [response={}] A cart item API response.
|
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
|
|
|
export function receiveCartItem( response = {} ) {
|
|
|
|
return {
|
|
|
|
type: types.RECEIVE_CART_ITEM,
|
|
|
|
cartItem: response,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an action object to indicate if the specified cart item
|
2020-04-07 11:03:22 +00:00
|
|
|
* quantity is being updated.
|
2020-03-19 11:50:51 +00:00
|
|
|
*
|
|
|
|
* @param {string} cartItemKey Cart item being updated.
|
2020-04-07 11:03:22 +00:00
|
|
|
* @param {boolean} isPendingQuantity Flag for update state; true if API request
|
|
|
|
* is pending.
|
2020-03-19 11:50:51 +00:00
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
2020-04-07 11:03:22 +00:00
|
|
|
export function itemIsPendingQuantity( cartItemKey, isPendingQuantity = true ) {
|
2020-03-19 11:50:51 +00:00
|
|
|
return {
|
2020-04-07 11:03:22 +00:00
|
|
|
type: types.ITEM_PENDING_QUANTITY,
|
2020-03-19 11:50:51 +00:00
|
|
|
cartItemKey,
|
2020-04-07 11:03:22 +00:00
|
|
|
isPendingQuantity,
|
2020-03-19 11:50:51 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an action object to remove a cart item from the store.
|
|
|
|
*
|
|
|
|
* @param {string} cartItemKey Cart item to remove.
|
2020-04-07 11:03:22 +00:00
|
|
|
* @param {boolean} isPendingDelete Flag for update state; true if API request
|
|
|
|
* is pending.
|
2020-03-19 11:50:51 +00:00
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
2020-04-07 11:03:22 +00:00
|
|
|
export function itemIsPendingDelete( cartItemKey, isPendingDelete = true ) {
|
2020-03-19 11:50:51 +00:00
|
|
|
return {
|
|
|
|
type: types.RECEIVE_REMOVED_ITEM,
|
|
|
|
cartItemKey,
|
2020-04-07 11:03:22 +00:00
|
|
|
isPendingDelete,
|
2020-03-19 11:50:51 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-11-20 15:13:35 +00:00
|
|
|
* Returns an action object used to track when customer data is being updated (billing and/or shipping).
|
2020-03-19 11:50:51 +00:00
|
|
|
*
|
2020-11-20 15:13:35 +00:00
|
|
|
* @param {boolean} isResolving if we're updating customer data or not.
|
2020-03-19 11:50:51 +00:00
|
|
|
* @return {Object} Object for action.
|
|
|
|
*/
|
2020-11-20 15:13:35 +00:00
|
|
|
export function updatingCustomerData( isResolving ) {
|
2020-03-19 11:50:51 +00:00
|
|
|
return {
|
2020-11-20 15:13:35 +00:00
|
|
|
type: types.UPDATING_CUSTOMER_DATA,
|
2020-03-19 11:50:51 +00:00
|
|
|
isResolving,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-03-31 15:40:27 +00:00
|
|
|
/**
|
|
|
|
* Returns an action object used to track whether the shipping rate is being
|
|
|
|
* selected or not.
|
|
|
|
*
|
|
|
|
* @param {boolean} isResolving True if shipping rate is being selected.
|
|
|
|
*
|
|
|
|
* @return {Object} Action object.
|
|
|
|
*/
|
|
|
|
export function shippingRatesBeingSelected( isResolving ) {
|
|
|
|
return {
|
|
|
|
type: types.UPDATING_SELECTED_SHIPPING_RATE,
|
|
|
|
isResolving,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-02-25 11:36:53 +00:00
|
|
|
/**
|
|
|
|
* Applies a coupon code and either invalidates caches, or receives an error if
|
|
|
|
* the coupon cannot be applied.
|
|
|
|
*
|
2020-03-03 10:26:02 +00:00
|
|
|
* @throws Will throw an error if there is an API problem.
|
2020-02-25 11:36:53 +00:00
|
|
|
* @param {string} couponCode The coupon code to apply to the cart.
|
|
|
|
*/
|
|
|
|
export function* applyCoupon( couponCode ) {
|
|
|
|
yield receiveApplyingCoupon( couponCode );
|
|
|
|
|
|
|
|
try {
|
2020-03-19 11:50:51 +00:00
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
2020-02-26 11:46:58 +00:00
|
|
|
path: '/wc/store/cart/apply-coupon',
|
2020-02-25 11:36:53 +00:00
|
|
|
method: 'POST',
|
2020-02-26 11:46:58 +00:00
|
|
|
data: {
|
|
|
|
code: couponCode,
|
|
|
|
},
|
2020-02-25 11:36:53 +00:00
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
yield receiveCart( response );
|
2020-03-03 10:26:02 +00:00
|
|
|
yield receiveApplyingCoupon( '' );
|
2020-02-25 11:36:53 +00:00
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-04-09 12:52:31 +00:00
|
|
|
yield receiveApplyingCoupon( '' );
|
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
|
|
|
|
2020-03-03 10:26:02 +00:00
|
|
|
// Re-throw the error.
|
|
|
|
throw error;
|
2020-02-25 11:36:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 10:26:02 +00:00
|
|
|
return true;
|
2020-02-25 11:36:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a coupon code and either invalidates caches, or receives an error if
|
|
|
|
* the coupon cannot be removed.
|
|
|
|
*
|
2020-03-03 10:26:02 +00:00
|
|
|
* @throws Will throw an error if there is an API problem.
|
2020-02-25 11:36:53 +00:00
|
|
|
* @param {string} couponCode The coupon code to remove from the cart.
|
|
|
|
*/
|
|
|
|
export function* removeCoupon( couponCode ) {
|
|
|
|
yield receiveRemovingCoupon( couponCode );
|
|
|
|
|
|
|
|
try {
|
2020-03-19 11:50:51 +00:00
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
2020-02-26 11:46:58 +00:00
|
|
|
path: '/wc/store/cart/remove-coupon',
|
2020-02-25 11:36:53 +00:00
|
|
|
method: 'POST',
|
2020-02-26 11:46:58 +00:00
|
|
|
data: {
|
|
|
|
code: couponCode,
|
|
|
|
},
|
2020-02-25 11:36:53 +00:00
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
yield receiveCart( response );
|
2020-03-03 10:26:02 +00:00
|
|
|
yield receiveRemovingCoupon( '' );
|
2020-02-25 11:36:53 +00:00
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-03-03 10:26:02 +00:00
|
|
|
yield receiveRemovingCoupon( '' );
|
2020-04-09 12:52:31 +00:00
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
|
|
|
|
2020-03-03 10:26:02 +00:00
|
|
|
// Re-throw the error.
|
|
|
|
throw error;
|
2020-02-25 11:36:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 10:26:02 +00:00
|
|
|
return true;
|
2020-02-25 11:36:53 +00:00
|
|
|
}
|
2020-02-28 02:05:10 +00:00
|
|
|
|
2020-04-06 10:36:28 +00:00
|
|
|
/**
|
|
|
|
* Adds an item to the cart:
|
|
|
|
* - Calls API to add item.
|
|
|
|
* - If successful, yields action to add item from store.
|
|
|
|
* - If error, yields action to store error.
|
|
|
|
*
|
2020-04-27 16:24:54 +00:00
|
|
|
* @throws Will throw an error if there is an API problem.
|
2020-04-06 10:36:28 +00:00
|
|
|
* @param {number} productId Product ID to add to cart.
|
|
|
|
* @param {number} quantity Number of product ID being added to cart.
|
|
|
|
*/
|
|
|
|
export function* addItemToCart( productId, quantity = 1 ) {
|
|
|
|
try {
|
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
|
|
|
path: `/wc/store/cart/add-item`,
|
|
|
|
method: 'POST',
|
|
|
|
data: {
|
|
|
|
id: productId,
|
|
|
|
quantity,
|
|
|
|
},
|
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
|
|
|
yield receiveCart( response );
|
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-04-09 12:52:31 +00:00
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
2020-04-27 16:24:54 +00:00
|
|
|
|
|
|
|
// Re-throw the error.
|
|
|
|
throw error;
|
2020-04-06 10:36:28 +00:00
|
|
|
}
|
2020-04-27 16:24:54 +00:00
|
|
|
|
|
|
|
return true;
|
2020-04-06 10:36:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-28 02:05:10 +00:00
|
|
|
/**
|
|
|
|
* Removes specified item from the cart:
|
|
|
|
* - Calls API to remove item.
|
|
|
|
* - If successful, yields action to remove item from store.
|
|
|
|
* - If error, yields action to store error.
|
|
|
|
* - Sets cart item as pending while API request is in progress.
|
|
|
|
*
|
|
|
|
* @param {string} cartItemKey Cart item being updated.
|
|
|
|
*/
|
|
|
|
export function* removeItemFromCart( cartItemKey ) {
|
2020-04-07 11:03:22 +00:00
|
|
|
yield itemIsPendingDelete( cartItemKey );
|
2020-02-28 02:05:10 +00:00
|
|
|
|
|
|
|
try {
|
2020-03-19 11:50:51 +00:00
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
2020-03-05 19:11:39 +00:00
|
|
|
path: `/wc/store/cart/remove-item/?key=${ cartItemKey }`,
|
|
|
|
method: 'POST',
|
2020-02-28 02:05:10 +00:00
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
yield receiveCart( response );
|
2020-02-28 02:05:10 +00:00
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-04-09 12:52:31 +00:00
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
2020-02-28 02:05:10 +00:00
|
|
|
}
|
2020-04-07 11:03:22 +00:00
|
|
|
yield itemIsPendingDelete( cartItemKey, false );
|
2020-02-28 02:05:10 +00:00
|
|
|
}
|
2020-03-03 01:08:19 +00:00
|
|
|
|
|
|
|
/**
|
2020-03-09 02:09:47 +00:00
|
|
|
* Persists a quantity change the for specified cart item:
|
2020-03-03 01:08:19 +00:00
|
|
|
* - Calls API to set quantity.
|
|
|
|
* - If successful, yields action to update store.
|
|
|
|
* - If error, yields action to store error.
|
|
|
|
*
|
|
|
|
* @param {string} cartItemKey Cart item being updated.
|
|
|
|
* @param {number} quantity Specified (new) quantity.
|
|
|
|
*/
|
|
|
|
export function* changeCartItemQuantity( cartItemKey, quantity ) {
|
2020-03-09 02:09:47 +00:00
|
|
|
const cartItem = yield select( CART_STORE_KEY, 'getCartItem', cartItemKey );
|
2020-04-07 11:03:22 +00:00
|
|
|
yield itemIsPendingQuantity( cartItemKey );
|
2020-03-03 01:08:19 +00:00
|
|
|
|
2020-03-09 02:09:47 +00:00
|
|
|
if ( cartItem?.quantity === quantity ) {
|
|
|
|
return;
|
|
|
|
}
|
2020-03-03 01:08:19 +00:00
|
|
|
try {
|
2020-03-19 11:50:51 +00:00
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
|
|
|
path: '/wc/store/cart/update-item',
|
2020-03-05 19:11:39 +00:00
|
|
|
method: 'POST',
|
|
|
|
data: {
|
|
|
|
key: cartItemKey,
|
|
|
|
quantity,
|
|
|
|
},
|
2020-03-03 01:08:19 +00:00
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
yield receiveCart( response );
|
2020-03-03 01:08:19 +00:00
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-04-09 12:52:31 +00:00
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
2020-03-03 01:08:19 +00:00
|
|
|
}
|
2020-04-07 11:03:22 +00:00
|
|
|
yield itemIsPendingQuantity( cartItemKey, false );
|
2020-03-03 01:08:19 +00:00
|
|
|
}
|
2020-03-05 19:54:05 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Selects a shipping rate.
|
|
|
|
*
|
|
|
|
* @param {string} rateId the id of the rate being selected.
|
|
|
|
* @param {number} [packageId] the key of the packages that we will select within.
|
|
|
|
*/
|
|
|
|
export function* selectShippingRate( rateId, packageId = 0 ) {
|
|
|
|
try {
|
2020-03-31 15:40:27 +00:00
|
|
|
yield shippingRatesBeingSelected( true );
|
2020-03-19 11:50:51 +00:00
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
2020-03-05 19:54:05 +00:00
|
|
|
path: `/wc/store/cart/select-shipping-rate/${ packageId }`,
|
|
|
|
method: 'POST',
|
|
|
|
data: {
|
|
|
|
rate_id: rateId,
|
|
|
|
},
|
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
yield receiveCart( response );
|
2020-03-05 19:54:05 +00:00
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-03-31 15:40:27 +00:00
|
|
|
yield shippingRatesBeingSelected( false );
|
2020-04-09 12:52:31 +00:00
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
|
|
|
|
2020-03-13 20:05:45 +00:00
|
|
|
// Re-throw the error.
|
|
|
|
throw error;
|
2020-03-05 19:54:05 +00:00
|
|
|
}
|
2020-03-31 15:40:27 +00:00
|
|
|
yield shippingRatesBeingSelected( false );
|
2020-03-13 20:05:45 +00:00
|
|
|
return true;
|
2020-03-05 19:54:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-11-20 15:13:35 +00:00
|
|
|
* Updates the shipping and/or billing address for the customer and returns an updated cart.
|
2020-03-05 19:54:05 +00:00
|
|
|
*
|
2020-11-20 15:13:35 +00:00
|
|
|
* @param {Object} customerData Address data to be updated; can contain both billing_address and shipping_address.
|
2020-03-05 19:54:05 +00:00
|
|
|
*/
|
2020-11-20 15:13:35 +00:00
|
|
|
export function* updateCustomerData( customerData ) {
|
|
|
|
yield updatingCustomerData( true );
|
|
|
|
|
2020-03-05 19:54:05 +00:00
|
|
|
try {
|
2020-03-19 11:50:51 +00:00
|
|
|
const { response } = yield apiFetchWithHeaders( {
|
2020-11-20 15:13:35 +00:00
|
|
|
path: '/wc/store/cart/update-customer',
|
2020-03-05 19:54:05 +00:00
|
|
|
method: 'POST',
|
2020-11-20 15:13:35 +00:00
|
|
|
data: customerData,
|
2020-03-05 19:54:05 +00:00
|
|
|
cache: 'no-store',
|
|
|
|
} );
|
|
|
|
|
2020-03-19 11:50:51 +00:00
|
|
|
yield receiveCart( response );
|
2020-03-05 19:54:05 +00:00
|
|
|
} catch ( error ) {
|
|
|
|
yield receiveError( error );
|
2020-11-20 15:13:35 +00:00
|
|
|
yield updatingCustomerData( false );
|
2020-04-09 12:52:31 +00:00
|
|
|
|
|
|
|
// If updated cart state was returned, also update that.
|
|
|
|
if ( error.data?.cart ) {
|
|
|
|
yield receiveCart( error.data.cart );
|
|
|
|
}
|
|
|
|
|
2020-03-13 20:05:45 +00:00
|
|
|
// rethrow error.
|
|
|
|
throw error;
|
2020-03-05 19:54:05 +00:00
|
|
|
}
|
2020-11-20 15:13:35 +00:00
|
|
|
|
|
|
|
yield updatingCustomerData( false );
|
2020-03-13 20:05:45 +00:00
|
|
|
return true;
|
2020-03-05 19:54:05 +00:00
|
|
|
}
|