2020-04-01 09:27:53 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import {
|
|
|
|
createContext,
|
|
|
|
useContext,
|
|
|
|
useReducer,
|
|
|
|
useRef,
|
|
|
|
useMemo,
|
|
|
|
useEffect,
|
2020-05-14 23:55:22 +00:00
|
|
|
useCallback,
|
2020-04-01 09:27:53 +00:00
|
|
|
} from '@wordpress/element';
|
|
|
|
import { __ } from '@wordpress/i18n';
|
2020-10-20 09:50:33 +00:00
|
|
|
import {
|
|
|
|
useCheckoutNotices,
|
|
|
|
useStoreNotices,
|
|
|
|
useEmitResponse,
|
|
|
|
usePrevious,
|
|
|
|
} from '@woocommerce/base-hooks';
|
2020-04-01 09:27:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { actions } from './actions';
|
2020-04-14 16:52:23 +00:00
|
|
|
import { reducer, prepareResponseData } from './reducer';
|
2020-04-01 09:27:53 +00:00
|
|
|
import { DEFAULT_STATE, STATUS } from './constants';
|
|
|
|
import {
|
|
|
|
EMIT_TYPES,
|
|
|
|
emitterSubscribers,
|
|
|
|
emitEvent,
|
2020-04-14 16:52:23 +00:00
|
|
|
emitEventWithAbort,
|
2020-04-01 09:27:53 +00:00
|
|
|
reducer as emitReducer,
|
|
|
|
} from './event-emit';
|
2020-07-30 10:57:22 +00:00
|
|
|
import { useValidationContext } from '../../shared/validation';
|
2020-04-01 09:27:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {import('@woocommerce/type-defs/checkout').CheckoutDispatchActions} CheckoutDispatchActions
|
|
|
|
* @typedef {import('@woocommerce/type-defs/contexts').CheckoutDataContext} CheckoutDataContext
|
|
|
|
*/
|
|
|
|
|
|
|
|
const CheckoutContext = createContext( {
|
|
|
|
isComplete: false,
|
|
|
|
isIdle: false,
|
|
|
|
isCalculating: false,
|
|
|
|
isProcessing: false,
|
2020-04-14 16:52:23 +00:00
|
|
|
isBeforeProcessing: false,
|
|
|
|
isAfterProcessing: false,
|
2020-04-01 09:27:53 +00:00
|
|
|
hasError: false,
|
|
|
|
redirectUrl: '',
|
|
|
|
orderId: 0,
|
2020-07-31 15:17:01 +00:00
|
|
|
orderNotes: '',
|
2020-04-14 16:52:23 +00:00
|
|
|
customerId: 0,
|
2020-04-30 09:52:36 +00:00
|
|
|
onSubmit: () => void null,
|
2020-04-14 16:52:23 +00:00
|
|
|
onCheckoutAfterProcessingWithSuccess: ( callback ) => void callback,
|
|
|
|
onCheckoutAfterProcessingWithError: ( callback ) => void callback,
|
|
|
|
onCheckoutBeforeProcessing: ( callback ) => void callback,
|
2020-04-01 09:27:53 +00:00
|
|
|
dispatchActions: {
|
|
|
|
resetCheckout: () => void null,
|
|
|
|
setRedirectUrl: ( url ) => void url,
|
|
|
|
setHasError: ( hasError ) => void hasError,
|
2020-04-14 16:52:23 +00:00
|
|
|
setAfterProcessing: ( response ) => void response,
|
2020-04-01 09:27:53 +00:00
|
|
|
incrementCalculating: () => void null,
|
|
|
|
decrementCalculating: () => void null,
|
|
|
|
setOrderId: ( id ) => void id,
|
2020-07-31 15:17:01 +00:00
|
|
|
setOrderNotes: ( orderNotes ) => void orderNotes,
|
2020-04-01 09:27:53 +00:00
|
|
|
},
|
|
|
|
hasOrder: false,
|
|
|
|
isCart: false,
|
|
|
|
} );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {CheckoutDataContext} Returns the checkout data context value
|
|
|
|
*/
|
|
|
|
export const useCheckoutContext = () => {
|
|
|
|
return useContext( CheckoutContext );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checkout state provider
|
|
|
|
* This provides provides an api interface exposing checkout state for use with
|
|
|
|
* cart or checkout blocks.
|
|
|
|
*
|
|
|
|
* @param {Object} props Incoming props for the provider.
|
|
|
|
* @param {Object} props.children The children being wrapped.
|
|
|
|
* @param {string} props.redirectUrl Initialize what the checkout will
|
|
|
|
* redirect to after successful
|
|
|
|
* submit.
|
|
|
|
* @param {boolean} props.isCart If context provider is being used
|
|
|
|
* in cart context.
|
|
|
|
*/
|
|
|
|
export const CheckoutStateProvider = ( {
|
|
|
|
children,
|
|
|
|
redirectUrl,
|
|
|
|
isCart = false,
|
|
|
|
} ) => {
|
|
|
|
// 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 );
|
2020-04-02 09:27:54 +00:00
|
|
|
const { setValidationErrors } = useValidationContext();
|
2020-04-06 20:36:19 +00:00
|
|
|
const { addErrorNotice, removeNotices } = useStoreNotices();
|
2020-04-08 15:46:34 +00:00
|
|
|
const isCalculating = checkoutState.calculatingCount > 0;
|
2020-04-14 16:52:23 +00:00
|
|
|
const {
|
|
|
|
isSuccessResponse,
|
|
|
|
isErrorResponse,
|
|
|
|
isFailResponse,
|
2020-10-12 08:19:30 +00:00
|
|
|
shouldRetry,
|
2020-04-14 16:52:23 +00:00
|
|
|
} = useEmitResponse();
|
2020-10-20 09:50:33 +00:00
|
|
|
const {
|
|
|
|
checkoutNotices,
|
|
|
|
paymentNotices,
|
|
|
|
expressPaymentNotices,
|
|
|
|
} = useCheckoutNotices();
|
2020-04-08 15:46:34 +00:00
|
|
|
|
2020-04-01 09:27:53 +00:00
|
|
|
// set observers on ref so it's always current.
|
|
|
|
useEffect( () => {
|
|
|
|
currentObservers.current = observers;
|
|
|
|
}, [ observers ] );
|
2020-04-14 16:52:23 +00:00
|
|
|
const onCheckoutAfterProcessingWithSuccess = useMemo(
|
|
|
|
() =>
|
|
|
|
emitterSubscribers( subscriber )
|
|
|
|
.onCheckoutAfterProcessingWithSuccess,
|
2020-04-01 09:27:53 +00:00
|
|
|
[ subscriber ]
|
|
|
|
);
|
2020-04-14 16:52:23 +00:00
|
|
|
const onCheckoutAfterProcessingWithError = useMemo(
|
|
|
|
() =>
|
|
|
|
emitterSubscribers( subscriber ).onCheckoutAfterProcessingWithError,
|
2020-04-01 09:27:53 +00:00
|
|
|
[ subscriber ]
|
|
|
|
);
|
2020-04-14 16:52:23 +00:00
|
|
|
const onCheckoutBeforeProcessing = useMemo(
|
|
|
|
() => emitterSubscribers( subscriber ).onCheckoutBeforeProcessing,
|
2020-04-01 09:27:53 +00:00
|
|
|
[ subscriber ]
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {CheckoutDispatchActions}
|
|
|
|
*/
|
|
|
|
const dispatchActions = useMemo(
|
|
|
|
() => ( {
|
|
|
|
resetCheckout: () => void dispatch( actions.setPristine() ),
|
|
|
|
setRedirectUrl: ( url ) =>
|
|
|
|
void dispatch( actions.setRedirectUrl( url ) ),
|
|
|
|
setHasError: ( hasError ) =>
|
|
|
|
void dispatch( actions.setHasError( hasError ) ),
|
|
|
|
incrementCalculating: () =>
|
|
|
|
void dispatch( actions.incrementCalculating() ),
|
|
|
|
decrementCalculating: () =>
|
|
|
|
void dispatch( actions.decrementCalculating() ),
|
|
|
|
setOrderId: ( orderId ) =>
|
|
|
|
void dispatch( actions.setOrderId( orderId ) ),
|
2020-07-31 15:17:01 +00:00
|
|
|
setOrderNotes: ( orderNotes ) =>
|
|
|
|
void dispatch( actions.setOrderNotes( orderNotes ) ),
|
2020-04-14 16:52:23 +00:00
|
|
|
setAfterProcessing: ( response ) => {
|
2020-04-16 14:39:46 +00:00
|
|
|
// capture general error message if this is an error response.
|
|
|
|
if (
|
|
|
|
! response.payment_result &&
|
|
|
|
response.message &&
|
|
|
|
response?.data?.status !== 200
|
|
|
|
) {
|
|
|
|
response.payment_result = {
|
|
|
|
...response.payment_result,
|
|
|
|
message: response.message,
|
|
|
|
};
|
|
|
|
}
|
2020-04-14 16:52:23 +00:00
|
|
|
if ( response.payment_result ) {
|
|
|
|
if (
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
response.payment_result?.redirect_url
|
|
|
|
) {
|
|
|
|
dispatch(
|
|
|
|
actions.setRedirectUrl(
|
|
|
|
response.payment_result.redirect_url
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
dispatch(
|
|
|
|
actions.setProcessingResponse(
|
|
|
|
prepareResponseData( response.payment_result )
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
void dispatch( actions.setAfterProcessing() );
|
2020-04-03 11:50:54 +00:00
|
|
|
},
|
2020-04-01 09:27:53 +00:00
|
|
|
} ),
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
// emit events.
|
|
|
|
useEffect( () => {
|
2020-05-10 23:41:10 +00:00
|
|
|
const status = checkoutState.status;
|
2020-04-14 16:52:23 +00:00
|
|
|
if ( status === STATUS.BEFORE_PROCESSING ) {
|
2020-04-06 20:36:19 +00:00
|
|
|
removeNotices( 'error' );
|
|
|
|
emitEvent(
|
2020-04-01 09:27:53 +00:00
|
|
|
currentObservers.current,
|
2020-04-14 16:52:23 +00:00
|
|
|
EMIT_TYPES.CHECKOUT_BEFORE_PROCESSING,
|
2020-04-01 09:27:53 +00:00
|
|
|
{}
|
|
|
|
).then( ( response ) => {
|
2020-04-08 16:36:04 +00:00
|
|
|
if ( response !== true ) {
|
2020-04-06 20:36:19 +00:00
|
|
|
if ( Array.isArray( response ) ) {
|
|
|
|
response.forEach(
|
|
|
|
( { errorMessage, validationErrors } ) => {
|
|
|
|
addErrorNotice( errorMessage );
|
|
|
|
setValidationErrors( validationErrors );
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatch( actions.setIdle() );
|
2020-04-06 20:36:19 +00:00
|
|
|
} else {
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatch( actions.setProcessing() );
|
2020-04-01 09:27:53 +00:00
|
|
|
}
|
|
|
|
} );
|
|
|
|
}
|
2020-05-10 23:41:10 +00:00
|
|
|
}, [
|
|
|
|
checkoutState.status,
|
|
|
|
setValidationErrors,
|
|
|
|
addErrorNotice,
|
|
|
|
removeNotices,
|
|
|
|
dispatch,
|
|
|
|
] );
|
2020-04-08 16:36:04 +00:00
|
|
|
|
2020-10-20 09:50:33 +00:00
|
|
|
const previousStatus = usePrevious( checkoutState.status );
|
|
|
|
const previousHasError = usePrevious( checkoutState.hasError );
|
|
|
|
|
2020-04-08 16:36:04 +00:00
|
|
|
useEffect( () => {
|
2020-10-20 09:50:33 +00:00
|
|
|
if (
|
|
|
|
checkoutState.status === previousStatus &&
|
|
|
|
checkoutState.hasError === previousHasError
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
2020-04-14 16:52:23 +00:00
|
|
|
if ( checkoutState.status === STATUS.AFTER_PROCESSING ) {
|
|
|
|
const data = {
|
|
|
|
redirectUrl: checkoutState.redirectUrl,
|
|
|
|
orderId: checkoutState.orderId,
|
|
|
|
customerId: checkoutState.customerId,
|
|
|
|
customerNote: checkoutState.customerNote,
|
|
|
|
processingResponse: checkoutState.processingResponse,
|
|
|
|
};
|
2020-04-08 16:36:04 +00:00
|
|
|
if ( checkoutState.hasError ) {
|
2020-04-14 16:52:23 +00:00
|
|
|
// allow payment methods or other things to customize the error
|
|
|
|
// with a fallback if nothing customizes it.
|
|
|
|
emitEventWithAbort(
|
2020-04-01 09:27:53 +00:00
|
|
|
currentObservers.current,
|
2020-04-14 16:52:23 +00:00
|
|
|
EMIT_TYPES.CHECKOUT_AFTER_PROCESSING_WITH_ERROR,
|
|
|
|
data
|
|
|
|
).then( ( response ) => {
|
|
|
|
if (
|
|
|
|
isErrorResponse( response ) ||
|
|
|
|
isFailResponse( response )
|
|
|
|
) {
|
|
|
|
if ( response.message ) {
|
|
|
|
const errorOptions = response.messageContext
|
|
|
|
? { context: response.messageContext }
|
|
|
|
: undefined;
|
|
|
|
addErrorNotice( response.message, errorOptions );
|
|
|
|
}
|
|
|
|
// irrecoverable error so set complete
|
2020-10-12 08:19:30 +00:00
|
|
|
if ( ! shouldRetry( response ) ) {
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatch( actions.setComplete( response ) );
|
|
|
|
} else {
|
|
|
|
dispatch( actions.setIdle() );
|
|
|
|
}
|
|
|
|
} else {
|
2020-10-20 09:50:33 +00:00
|
|
|
const hasErrorNotices =
|
|
|
|
checkoutNotices.some(
|
|
|
|
( notice ) => notice.status === 'error'
|
|
|
|
) ||
|
|
|
|
expressPaymentNotices.some(
|
|
|
|
( notice ) => notice.status === 'error'
|
|
|
|
) ||
|
|
|
|
paymentNotices.some(
|
|
|
|
( notice ) => notice.status === 'error'
|
2020-04-14 16:52:23 +00:00
|
|
|
);
|
2020-10-20 09:50:33 +00:00
|
|
|
if ( ! hasErrorNotices ) {
|
|
|
|
// no error handling in place by anything so let's fall
|
|
|
|
// back to default
|
|
|
|
const message =
|
|
|
|
data.processingResponse?.message ||
|
|
|
|
__(
|
|
|
|
'Something went wrong. Please contact us to get assistance.',
|
|
|
|
'woo-gutenberg-products-block'
|
|
|
|
);
|
|
|
|
addErrorNotice( message, {
|
|
|
|
id: 'checkout',
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatch( actions.setIdle() );
|
|
|
|
}
|
|
|
|
} );
|
2020-04-01 09:27:53 +00:00
|
|
|
} else {
|
2020-04-14 16:52:23 +00:00
|
|
|
emitEventWithAbort(
|
2020-04-01 09:27:53 +00:00
|
|
|
currentObservers.current,
|
2020-04-14 16:52:23 +00:00
|
|
|
EMIT_TYPES.CHECKOUT_AFTER_PROCESSING_WITH_SUCCESS,
|
|
|
|
data
|
|
|
|
).then( ( response ) => {
|
|
|
|
if ( isSuccessResponse( response ) ) {
|
|
|
|
dispatch( actions.setComplete( response ) );
|
2020-04-15 15:43:03 +00:00
|
|
|
} else if (
|
2020-04-14 16:52:23 +00:00
|
|
|
isErrorResponse( response ) ||
|
|
|
|
isFailResponse( response )
|
|
|
|
) {
|
|
|
|
if ( response.message ) {
|
|
|
|
const errorOptions = response.messageContext
|
|
|
|
? { context: response.messageContext }
|
|
|
|
: undefined;
|
|
|
|
addErrorNotice( response.message, errorOptions );
|
|
|
|
}
|
2020-10-12 08:19:30 +00:00
|
|
|
if ( ! shouldRetry( response ) ) {
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatch( actions.setComplete( response ) );
|
|
|
|
} else {
|
|
|
|
// this will set an error which will end up
|
2020-10-20 09:50:33 +00:00
|
|
|
// triggering the onCheckoutAfterProcessingWithError emitter.
|
2020-04-14 16:52:23 +00:00
|
|
|
// and then setting checkout to IDLE state.
|
|
|
|
dispatch( actions.setHasError( true ) );
|
|
|
|
}
|
2020-04-15 15:43:03 +00:00
|
|
|
} else {
|
|
|
|
// nothing hooked in had any response type so let's just
|
|
|
|
// consider successful
|
|
|
|
dispatch( actions.setComplete() );
|
2020-04-14 16:52:23 +00:00
|
|
|
}
|
|
|
|
} );
|
2020-04-01 09:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-14 16:52:23 +00:00
|
|
|
}, [
|
|
|
|
checkoutState.status,
|
|
|
|
checkoutState.hasError,
|
|
|
|
checkoutState.redirectUrl,
|
|
|
|
checkoutState.orderId,
|
|
|
|
checkoutState.customerId,
|
|
|
|
checkoutState.customerNote,
|
|
|
|
checkoutState.processingResponse,
|
2020-10-20 09:50:33 +00:00
|
|
|
previousStatus,
|
|
|
|
previousHasError,
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatchActions,
|
2020-05-10 23:41:10 +00:00
|
|
|
addErrorNotice,
|
|
|
|
isErrorResponse,
|
|
|
|
isFailResponse,
|
|
|
|
isSuccessResponse,
|
2020-10-20 09:50:33 +00:00
|
|
|
shouldRetry,
|
|
|
|
checkoutNotices,
|
|
|
|
expressPaymentNotices,
|
|
|
|
paymentNotices,
|
2020-04-14 16:52:23 +00:00
|
|
|
] );
|
2020-04-01 09:27:53 +00:00
|
|
|
|
2020-05-14 23:55:22 +00:00
|
|
|
const onSubmit = useCallback( () => {
|
2020-04-14 16:52:23 +00:00
|
|
|
dispatch( actions.setBeforeProcessing() );
|
2020-05-14 23:55:22 +00:00
|
|
|
}, [] );
|
2020-04-01 09:27:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {CheckoutDataContext}
|
|
|
|
*/
|
|
|
|
const checkoutData = {
|
|
|
|
onSubmit,
|
|
|
|
isComplete: checkoutState.status === STATUS.COMPLETE,
|
|
|
|
isIdle: checkoutState.status === STATUS.IDLE,
|
2020-04-08 15:46:34 +00:00
|
|
|
isCalculating,
|
2020-04-01 09:27:53 +00:00
|
|
|
isProcessing: checkoutState.status === STATUS.PROCESSING,
|
2020-04-14 16:52:23 +00:00
|
|
|
isBeforeProcessing: checkoutState.status === STATUS.BEFORE_PROCESSING,
|
|
|
|
isAfterProcessing: checkoutState.status === STATUS.AFTER_PROCESSING,
|
2020-04-01 09:27:53 +00:00
|
|
|
hasError: checkoutState.hasError,
|
|
|
|
redirectUrl: checkoutState.redirectUrl,
|
2020-04-14 16:52:23 +00:00
|
|
|
onCheckoutAfterProcessingWithSuccess,
|
|
|
|
onCheckoutAfterProcessingWithError,
|
|
|
|
onCheckoutBeforeProcessing,
|
2020-04-01 09:27:53 +00:00
|
|
|
dispatchActions,
|
|
|
|
isCart,
|
|
|
|
orderId: checkoutState.orderId,
|
|
|
|
hasOrder: !! checkoutState.orderId,
|
2020-04-07 14:29:59 +00:00
|
|
|
customerId: checkoutState.customerId,
|
2020-07-31 15:17:01 +00:00
|
|
|
orderNotes: checkoutState.orderNotes,
|
Support "create account" option in checkout block (https://github.com/woocommerce/woocommerce-blocks/pull/2851)
* prototype 'create account' checkbox in checkout block
* expose store config for generating password/username to blocks:
+ use FILTER_VALIDATE_BOOLEAN instead of hard-coded `yes`
* stub out signup form in checkout block
* context / provider to store checkout signup form data
* revert signup form - checkout block will always generate username etc
* persist signup checkbox in checkout state & pass to checkout API
* add `create_account` param to order API, fix name in client POST
* handle creating user account as part of order (first cut)
* ensure the order is associated with the new customer
* only show 'create account' checkbox when appropriate (guest checkout)
* remove unnecessary username/password variables
* refactor account-creation logic into functions:
- clarify inputs and outputs
- use RouteException for error handling
- use woo options directly, avoid dependency on WC_Checkout
* update "email exists" error message to use existing error message text
* handle all known errors from wc_create_new_customer + use core message
* only show "create account" checkbox to shopper when necessary:
- if guest checkout is disabled, user must create account - not optional
* only show "create account" if account creation is optional:
- fixes incorrect logic in previous commit
- add some comments to clarify
* fix create account logic in API when checkout requires account:
- use correct woo setting option name
- reverse logic to match option = allow guest false means registration required
* strip html tags from create account error messages
* temporarily force enable autogenerate user/pass in new account API
* fix rebase errors
* add new allowCreateAccount attribute in checkout block
* show/hide `Create account` checkbox dependent on block attribute:
- previously was dependent on store setting
* new create user API, with set initial password email (first cut):
- use core register_new_user for creating the user
- this triggers core "set new password" email
- generate username using logic lifted from WC core
- rough cut, lots to tidy/polish here
* remove alternative/unused create account function
* set `Customer` role for signups during checkout
* eslint fix - switch case break
* remove comments that mirror code & might go stale
* tidy func comment
* remove unused function
* use store setting `allow signup` for default value of new block option
* refactor order signup logic to service class first cut:
- new CreateAccount service
- hook up via custom action (for now at least)
- paste over existing create account logic (temporary - will be replaced)
* adapt wc_create_new_customer logic in CreateAccount service (WIP)
* set default_password_nag on new account + throw instead of WP_Error
* rename `createAccount` => `shouldCreateAccount` to clarify meaning
* fix checkout block - renamed `shouldCreateAccount` (missed in prev commit)
* prototype sending alternative email template for checkout signup
* add magic link to set password to blocks new account html email
* tidy up new account email templates - set password link, subject/heading
* use same id so merchant setting tweaks apply to our new improved email
* remove logging
* code tidies in CreateAccount service:
- remove unnecessary constructor
- type-hint in should_create_customer_account
- streamline logic in should_create_customer_account - remove
unnecessary `empty` check
- add comments to illuminate different use-cases handled by should_create_customer_account
* don't provide password to new account email templates (no longer used)
* declare dependencies in root namespace
* code tidies on new account email class:
- correct namespace and camelcase name
- declare class in file, don't instantiate; instantiate in client code
(CreateAccount service) when used
- no require/file import, use `use`
* move CustomerNewEmail to folder matching namespace
* use Package->get_path for email template paths:
- CreateAccount service now depends on Package
- CreateAccount passes Package to email class so it can use `get_path`
- note: CustomerNewAccount is not registered with DI container as it
needs to be instantiated after Woo init (for `WC_Email`)
- shift email templates to {plugin}/templates, consistent with WP
convention
* call CreateAccount::from_order_request directly, no custom hook:
- custom hook is not appropriate as we may not want to allow
extensibility in this way - TBD
* add appropriate margin above create account checkbox
* remove unnecessary direct-access protection
* generalise name of error-handling method
* simplify CustomerNewAccount - instantiate directly, when needed
* remove unused new_account_email member - now instantiated on demand
* numerous fixes and updates due to rebase changes
* fix typo in name of CustomerNewAccount php file (missing `n`)
* experiment - link to lost-password form in my-account (prototype branded screen)
* Revert "experiment - link to lost-password form in my-account (prototype branded screen)"
This reverts commit e1dc6dd5e9f0218ede81da92188d813c2d0856d9.
* feature gate CreateAccount service init to dev build only +
+ remove stale comment
* feature gate front end "Create account" checkbox to feature plugin only
* feature gate editor "allow signup" option to dev build only
* feature gate checkout api create account - dev build only
* tweak feature gating PHP logic so it's robust:
- all PHP feature gating is in the service class
- all publicly-available methods return early if feature gate off
- Checkout rest API transparently calls service - no explicit feature
gate at API level
* ensure frontend/editor features are feature gated (isExperimentalBuild is a function)
* feature gate value of checkoutAllowsSignup - can only be true in feature plugin
* fix a / an typo in comment
Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>
* remove commented code
* hello world unit test (doesn't test anything yet)
* add a command for running unit tests when container already up:
- this should probably move to another PR/branch
* basic tests of core logic in CreateAccount service
* import isExperimentalBuild direct:
- import from alias package was causing an issue, likely a dependency cycle
* refactor from_order_request to return new user ID so it's easier to test
* test creating a customer from an order + rest request:
- i.e. a full end-to-end integration test
* delete redundant test and tidy comments
* generalise test to provider format
* refactor create-dup-user err test to use same approach as success test
* add test for when user should not be created
* don't hard-code options in "create" test, remove redundant provider in no-account-requested test
* de-generalise "user already signed up" test
* add test for malformed email
* flesh out & comment successful signup tests
* flesh out "invalid email" tests
* clarify no account requested test comment
* remove phpunit:quick - I don't think it's needed
* add comment explaining this is an integration test
* experiment – disable feature flag, is this why the tests are failing?
* revert test commit - restore feature gate (experimental flag)
* skip all tests if CreateAccount is disabled due to feature flag
* d'oh - expose CreateAccount:is_feature_enabled so can be used in tests
* add jsdoc for checkout-state shouldCreateAccount field
* remove unnecessary comment + fix whitespace/indentation
* simulate logged-out user for createaccount signup tests
* use a single, compound if statement for early return (review nitpick)
* don't hide `checkoutAllowsSignup` store setting behind feature flag:
- the feature flag should be used to enable/disable behaviour
- it's dangerous to adjust store settings/options based on feature flag
* rejig tests so they require woocommerce_blocks_phase==3:
- make feature gate method private to avoid exposing
- remove feature flag check & test skip for other builds
- set blocks phase in travis config
* remove redundant user-logout in test setup - cleaner to just require this
* use WP function bracket style (same line)
Co-authored-by: Darren Ethier <darren@roughsmootheng.in>
Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>
2020-10-01 02:07:16 +00:00
|
|
|
shouldCreateAccount: checkoutState.shouldCreateAccount,
|
|
|
|
setShouldCreateAccount: ( value ) =>
|
|
|
|
dispatch( actions.setShouldCreateAccount( value ) ),
|
2020-04-01 09:27:53 +00:00
|
|
|
};
|
|
|
|
return (
|
|
|
|
<CheckoutContext.Provider value={ checkoutData }>
|
|
|
|
{ children }
|
|
|
|
</CheckoutContext.Provider>
|
|
|
|
);
|
|
|
|
};
|