Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
prelminary work on the checkout context
Browse files Browse the repository at this point in the history
  • Loading branch information
nerrad committed Feb 27, 2020
1 parent db5cf05 commit 216e261
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 97 deletions.
96 changes: 0 additions & 96 deletions assets/js/base/context/cart-checkout/checkout.js

This file was deleted.

43 changes: 43 additions & 0 deletions assets/js/base/context/cart-checkout/checkout/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Internal dependencies
*/
import { TYPES } from './constants';

const {
SET_PRISTINE,
SET_REDIRECT_URL,
SET_COMPLETE,
SET_INCOMPLETE,
SET_HAS_ERROR,
SET_NO_ERROR,
INCREMENT_CALCULATING,
DECREMENT_CALCULATING,
} = TYPES;

export const actions = {
setPristine: () => ( {
type: SET_PRISTINE,
} ),
setRedirectUrl: ( url ) => ( {
type: SET_REDIRECT_URL,
url,
} ),
setComplete: () => ( {
type: SET_COMPLETE,
} ),
setIncomplete: () => ( {
type: SET_INCOMPLETE,
} ),
setHasError: () => ( {
type: SET_HAS_ERROR,
} ),
clearError: () => ( {
type: SET_NO_ERROR,
} ),
incrementCalculating: () => ( {
type: INCREMENT_CALCULATING,
} ),
decrementCalculating: () => ( {
type: DECREMENT_CALCULATING,
} ),
};
17 changes: 17 additions & 0 deletions assets/js/base/context/cart-checkout/checkout/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const DEFAULT_STATE = {
redirectUrl: '',
isComplete: false,
hasError: false,
calculatingCount: 0,
};

export const TYPES = {
SET_PRISTINE: 'set_pristine',
SET_REDIRECT_URL: 'set_redirect_url',
SET_COMPLETE: 'set_checkout_complete',
SET_INCOMPLETE: 'set_checkout_incomplete',
SET_HAS_ERROR: 'set_checkout_has_error',
SET_NO_ERROR: 'set_checkout_no_error',
INCREMENT_CALCULATING: 'increment_calculating',
DECREMENT_CALCULATING: 'decrement_calculating',
};
41 changes: 41 additions & 0 deletions assets/js/base/context/cart-checkout/checkout/event-emit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Internal dependencies
*/
import { actions, reducer, emitEvent } from '../event_emit';

const EMIT_TYPES = {
CHECKOUT_COMPLETE: 'checkout_complete',
};

/**
* Receives a reducer dispatcher and returns an object with the
* onCheckoutComplete callback registration function for the checkout emit
* events.
*
* Calling the event registration function with the callback will register it
* for the event emitter and will return a dispatcher for removing the
* registered callback (useful for implementation in `useEffect`).
*
* @param {Function} dispatcher The emitter reducer dispatcher.
*
* @return {Object} An object with the `onCheckoutComplete` emmitter registration
*/
const emitterSubscribers = ( dispatcher ) => ( {
onCheckoutComplete: ( callback ) => {
const action = actions.addEventCallback(
EMIT_TYPES.CHECKOUT_COMPLETE,
callback
);
dispatcher( action );
return () => {
dispatcher(
actions.removeEventCallback(
EMIT_TYPES.CHECKOUT_COMPLETE,
action.id
)
);
};
},
} );

export { EMIT_TYPES, emitterSubscribers, reducer, emitEvent };
136 changes: 136 additions & 0 deletions assets/js/base/context/cart-checkout/checkout/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* Internal dependencies
*/
import { PaymentMethodDataProvider } from '../payment-methods';
import { ShippingMethodDataProvider } from '../shipping';
import { actions } from './actions';
import { reducer } from './reducer';
import { TYPES, DEFAULT_STATE } from './constants';
import {
EMIT_TYPES,
emitterSubscribers,
emitEvent,
reducer as emitReducer,
} from './event-emit';

/**
* External dependencies
*/
import {
createContext,
useContext,
useReducer,
useRef,
useMemo,
useEffect,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';

const CheckoutContext = createContext( {} );

export const useCheckoutContext = () => {
return useContext( CheckoutContext );
};

// create context provider for coupons

// create context provider for checkout validation errors.

// checkout and cart provider context will be different context but implement
// the above (in terms of finalization.). For example cart context will redirect
// checkout for finalization. Checkout context handles pinging the server for
// processing checkout, validation of existing fields, and redirecting on
// success.

// checkout context should have events (calculating, success, fail, pristine)
// Also, extensions should be able to register callbacks that will be fired as
// each event is triggered. So there should be a way to register and remove
// callbacks.
// for setCalculating dispatches... let's do this as an incremental thing,
// anything that triggers total changes will increment the calculator by one and when its
// done it triggers a decrement. That way `isCalculating` is actually a report
// on all things currently in processing.

// todo add onClick handler for checkout submit button and this will:
// - run validation (via validation context)
// - if active payment method is present, trigger the payment method processing.
// - when payment payment status is set to success, trigger the server request
// for finalizing the checkout (which should include all the payment data)
// - on receiving the server response, trigger checkout complete.
// - do redirect to redirectUrl if checkoutComplete and paymentMethodStatus is
// is complete (to allow for final payment method processing after checkout
// is complete) (NOTE: maybe we could just handle this by detecting if there are
// any observers left to process?)
// One way I might be able to do all the above is to pass through the onSubmit
// handler that is enhanced through each provider (maybe). Otherwise I'm going
// to have to figure out a way to connect the various providers.

const CheckoutProvider = ( {
children,
activePaymentMethod: initialActivePaymentMethod,
redirectUrl,
submitLabel = __( 'Place Order', 'woo-gutenberg-product-block' ),
} ) => {
// note, this is done intentionally so that the default state now has
// the redirectUrl for when checkout is reset to PRISTINE state.
DEFAULT_STATE.redirectUrl = redirectUrl;
const [ checkoutState, dispatch ] = useReducer( reducer, DEFAULT_STATE );
const [ observers, subscriber ] = useReducer( emitReducer );
const currentObservers = useRef( observers );
// set observers on ref so it's always current
useEffect( () => {
currentObservers.current = observers;
}, [ observers ] );
const onCheckoutComplete = emitterSubscribers( subscriber )
.onCheckoutComplete;
const dispatchActions = useMemo(
() => ( {
resetCheckout: () => void dispatch( actions.setPristine() ),
setRedirectUrl: ( url ) =>
void dispatch( actions.setRedirectUrl( url ) ),
setHasError: () => void dispatch( actions.setHasError() ),
clearError: () => void dispatch( actions.clearError() ),
incrementCalculating: () =>
void dispatch( actions.incrementCalculating() ),
decrementCalculating: () =>
void dispatch( actions.decrementCalculating() ),
} ),
[]
);
const onSubmit = () => {
// @todo this is where we do validation and checkout processing
dispatch( actions.setComplete() );
};
// emit events
useEffect( () => {
if ( checkoutState.isComplete ) {
emitEvent(
currentObservers.current,
EMIT_TYPES.CHECKOUT_COMPLETE,
{}
);
}
}, [ checkoutState.isComplete ] );

// do redirect!
useEffect( () => {
// @todo check for if payment status and checkout status are complete
// and then redirect
window.location = checkoutState.redirectUrl;
}, [ checkoutState.isComplete ] );

//@todo setup the checkout data to pass on the checkout providers value.
return (
<CheckoutContext.Provider value={ contextValue }>
<PaymentMethodDataProvider
activePaymentMethod={ initialActivePaymentMethod }
>
<ShippingMethodDataProvider>
{ children }
</ShippingMethodDataProvider>
</PaymentMethodDataProvider>
</CheckoutContext.Provider>
);
};

export default CheckoutProvider;
Loading

0 comments on commit 216e261

Please sign in to comment.