2020-03-26 13:31:09 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import {
|
|
|
|
createContext,
|
|
|
|
useContext,
|
|
|
|
useReducer,
|
|
|
|
useEffect,
|
|
|
|
useRef,
|
|
|
|
} from '@wordpress/element';
|
2020-03-27 11:14:32 +00:00
|
|
|
import {
|
|
|
|
useShippingAddress,
|
|
|
|
useShippingRates,
|
|
|
|
useStoreCart,
|
2020-03-31 15:40:27 +00:00
|
|
|
useSelectShippingRate,
|
2020-03-27 11:14:32 +00:00
|
|
|
} from '@woocommerce/base-hooks';
|
2020-03-26 13:31:09 +00:00
|
|
|
import { useCheckoutContext } from '@woocommerce/base-context';
|
|
|
|
|
2020-03-27 11:14:32 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { ERROR_TYPES, DEFAULT_SHIPPING_CONTEXT_DATA } from './constants';
|
|
|
|
import {
|
|
|
|
EMIT_TYPES,
|
|
|
|
emitterSubscribers,
|
|
|
|
reducer as emitReducer,
|
|
|
|
emitEvent,
|
|
|
|
} from './event-emit';
|
|
|
|
|
2020-03-26 13:31:09 +00:00
|
|
|
/**
|
|
|
|
* @typedef {import('@woocommerce/type-defs/contexts').ShippingDataContext} ShippingDataContext
|
|
|
|
*/
|
|
|
|
|
|
|
|
const { NONE, INVALID_ADDRESS, UNKNOWN } = ERROR_TYPES;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reducer for shipping status state
|
|
|
|
*
|
|
|
|
* @param {string} state The current status.
|
|
|
|
* @param {Object} action The incoming action.
|
|
|
|
*/
|
|
|
|
const errorStatusReducer = ( state, { type } ) => {
|
|
|
|
if ( Object.keys( ERROR_TYPES ).includes( type ) ) {
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
return type;
|
|
|
|
};
|
|
|
|
|
|
|
|
const ShippingDataContext = createContext( DEFAULT_SHIPPING_CONTEXT_DATA );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {ShippingDataContext} Returns data and functions related to
|
2020-03-31 15:40:27 +00:00
|
|
|
* shipping methods.
|
2020-03-26 13:31:09 +00:00
|
|
|
*/
|
|
|
|
export const useShippingDataContext = () => {
|
|
|
|
return useContext( ShippingDataContext );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2020-03-31 15:40:27 +00:00
|
|
|
* The shipping data provider exposes the interface for shipping in the
|
|
|
|
* checkout/cart.
|
2020-03-26 13:31:09 +00:00
|
|
|
*
|
2020-03-31 15:40:27 +00:00
|
|
|
* @param {Object} props Incoming props for provider
|
2020-03-26 13:31:09 +00:00
|
|
|
*/
|
|
|
|
export const ShippingDataProvider = ( { children } ) => {
|
|
|
|
const { dispatchActions } = useCheckoutContext();
|
|
|
|
const { cartNeedsShipping: needsShipping } = useStoreCart();
|
|
|
|
const [ shippingErrorStatus, dispatchErrorStatus ] = useReducer(
|
|
|
|
errorStatusReducer,
|
|
|
|
NONE
|
|
|
|
);
|
|
|
|
const [ observers, subscriber ] = useReducer( emitReducer, {} );
|
2020-03-27 11:14:32 +00:00
|
|
|
const { shippingAddress, setShippingAddress } = useShippingAddress();
|
2020-03-26 13:31:09 +00:00
|
|
|
const currentObservers = useRef( observers );
|
2020-03-31 15:40:27 +00:00
|
|
|
const { shippingRates, shippingRatesLoading } = useShippingRates();
|
|
|
|
const {
|
|
|
|
selectShippingRate: setSelectedRates,
|
|
|
|
selectedShippingRates: selectedRates,
|
|
|
|
isSelectingRate,
|
|
|
|
} = useSelectShippingRate( shippingRates );
|
2020-03-26 13:31:09 +00:00
|
|
|
const onShippingRateSuccess = emitterSubscribers( subscriber ).onSuccess;
|
|
|
|
const onShippingRateFail = emitterSubscribers( subscriber ).onFail;
|
|
|
|
const onShippingRateSelectSuccess = emitterSubscribers( subscriber )
|
|
|
|
.onSelectSuccess;
|
|
|
|
const onShippingRateSelectFail = emitterSubscribers( subscriber )
|
|
|
|
.onShippingRateSelectFail;
|
|
|
|
|
2020-03-31 15:40:27 +00:00
|
|
|
// set observers on ref so it's always current.
|
2020-03-26 13:31:09 +00:00
|
|
|
useEffect( () => {
|
|
|
|
currentObservers.current = observers;
|
|
|
|
}, [ observers ] );
|
|
|
|
|
2020-03-31 15:40:27 +00:00
|
|
|
// increment/decrement checkout calculating counts when shipping is loading.
|
2020-03-26 13:31:09 +00:00
|
|
|
useEffect( () => {
|
2020-03-31 15:40:27 +00:00
|
|
|
if ( shippingRatesLoading ) {
|
2020-03-26 13:31:09 +00:00
|
|
|
dispatchActions.incrementCalculating();
|
|
|
|
} else {
|
|
|
|
dispatchActions.decrementCalculating();
|
|
|
|
}
|
2020-03-31 15:40:27 +00:00
|
|
|
}, [ shippingRatesLoading ] );
|
2020-03-26 13:31:09 +00:00
|
|
|
|
2020-03-31 15:40:27 +00:00
|
|
|
// increment/decrement checkout calculating counts when shipping rates are
|
|
|
|
// being selected.
|
|
|
|
useEffect( () => {
|
|
|
|
if ( isSelectingRate ) {
|
|
|
|
dispatchActions.incrementCalculating();
|
|
|
|
} else {
|
|
|
|
dispatchActions.decrementCalculating();
|
2020-03-26 13:31:09 +00:00
|
|
|
}
|
2020-03-31 15:40:27 +00:00
|
|
|
}, [ isSelectingRate ] );
|
2020-03-26 13:31:09 +00:00
|
|
|
|
|
|
|
const currentErrorStatus = {
|
|
|
|
isPristine: shippingErrorStatus === NONE,
|
|
|
|
isValid: shippingErrorStatus === NONE,
|
|
|
|
hasInvalidAddress: shippingErrorStatus === INVALID_ADDRESS,
|
|
|
|
hasError:
|
|
|
|
shippingErrorStatus === UNKNOWN ||
|
|
|
|
shippingErrorStatus === INVALID_ADDRESS,
|
|
|
|
};
|
|
|
|
|
2020-03-31 15:40:27 +00:00
|
|
|
// emit events.
|
2020-03-26 13:31:09 +00:00
|
|
|
// @todo add emitters for shipping rate selection.
|
|
|
|
useEffect( () => {
|
2020-03-31 15:40:27 +00:00
|
|
|
if ( ! shippingRatesLoading && currentErrorStatus.hasError ) {
|
2020-03-26 13:31:09 +00:00
|
|
|
emitEvent(
|
|
|
|
currentObservers.current,
|
2020-03-31 15:40:27 +00:00
|
|
|
EMIT_TYPES.SHIPPING_RATES_FAIL,
|
2020-03-26 13:31:09 +00:00
|
|
|
shippingErrorStatus
|
|
|
|
);
|
2020-03-31 15:40:27 +00:00
|
|
|
} else if ( ! shippingRatesLoading && shippingRates ) {
|
2020-03-26 13:31:09 +00:00
|
|
|
emitEvent(
|
|
|
|
currentObservers.current,
|
|
|
|
EMIT_TYPES.SHIPPING_RATES_SUCCESS,
|
2020-03-31 15:40:27 +00:00
|
|
|
shippingRates
|
2020-03-26 13:31:09 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}, [
|
2020-03-31 15:40:27 +00:00
|
|
|
shippingRates,
|
|
|
|
shippingRatesLoading,
|
2020-03-26 13:31:09 +00:00
|
|
|
currentErrorStatus,
|
|
|
|
shippingErrorStatus,
|
|
|
|
] );
|
|
|
|
|
2020-03-31 15:40:27 +00:00
|
|
|
// emit shipping rate selection events.
|
|
|
|
useEffect( () => {
|
|
|
|
if ( ! isSelectingRate && currentErrorStatus.hasError ) {
|
|
|
|
emitEvent(
|
|
|
|
currentObservers.current,
|
|
|
|
EMIT_TYPES.SHIPPING_RATE_SELECT_FAIL,
|
|
|
|
shippingErrorStatus
|
|
|
|
);
|
|
|
|
} else if ( ! isSelectingRate && selectedRates ) {
|
|
|
|
emitEvent(
|
|
|
|
currentObservers.current,
|
|
|
|
EMIT_TYPES.SHIPPING_RATE_SELECT_SUCCESS,
|
|
|
|
selectedRates
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}, [
|
|
|
|
selectedRates,
|
|
|
|
isSelectingRate,
|
|
|
|
currentErrorStatus,
|
|
|
|
shippingErrorStatus,
|
|
|
|
] );
|
|
|
|
|
|
|
|
// dispatch checkout error if there's a shipping error.
|
|
|
|
useEffect( () => {
|
2020-03-31 19:28:36 +00:00
|
|
|
dispatchActions.setHasError( currentErrorStatus.hasError );
|
|
|
|
}, [ currentErrorStatus, dispatchActions.setHasError ] );
|
2020-03-31 15:40:27 +00:00
|
|
|
|
2020-03-26 13:31:09 +00:00
|
|
|
/**
|
|
|
|
* @type {ShippingDataContext}
|
|
|
|
*/
|
|
|
|
const ShippingData = {
|
|
|
|
shippingErrorStatus,
|
|
|
|
dispatchErrorStatus,
|
|
|
|
shippingErrorTypes: ERROR_TYPES,
|
2020-03-31 15:40:27 +00:00
|
|
|
shippingRates,
|
|
|
|
setShippingRates: setSelectedRates,
|
|
|
|
shippingRatesLoading,
|
2020-03-26 13:31:09 +00:00
|
|
|
selectedRates,
|
|
|
|
setSelectedRates,
|
2020-03-27 11:14:32 +00:00
|
|
|
shippingAddress,
|
2020-03-26 13:31:09 +00:00
|
|
|
setShippingAddress,
|
|
|
|
onShippingRateSuccess,
|
|
|
|
onShippingRateFail,
|
|
|
|
onShippingRateSelectSuccess,
|
|
|
|
onShippingRateSelectFail,
|
|
|
|
needsShipping,
|
|
|
|
};
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<ShippingDataContext.Provider value={ ShippingData }>
|
|
|
|
{ children }
|
|
|
|
</ShippingDataContext.Provider>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|