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

Commit

Permalink
Refactor existing error notices from the API (#7728)
Browse files Browse the repository at this point in the history
* Update API type defs

* Move create notice utils

* Replace useCheckoutNotices with new contexts

* processCheckoutResponseHeaders should check headers are defined

* Scroll to error notices only if we're not editing a field

* Error handling utils

* processErrorResponse when pushing changes

* processErrorResponse when processing checkout

* remove formatStoreApiErrorMessage

* Add todo for cart errors

* Remove unused deps

* unused imports

* Fix linting warnings

* Unused dep

* Update assets/js/types/type-defs/api-response.ts

Co-authored-by: Thomas Roberts <[email protected]>

* Add todo

* Use generic

* remove const

* Update array types

* Phone should be in address blocks

Co-authored-by: Thomas Roberts <[email protected]>
  • Loading branch information
mikejolley and opr committed Dec 5, 2022
1 parent ad756b9 commit fc95e40
Show file tree
Hide file tree
Showing 30 changed files with 443 additions and 357 deletions.
1 change: 0 additions & 1 deletion assets/js/base/context/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export * from './use-store-products';
export * from './use-store-add-to-cart';
export * from './use-customer-data';
export * from './use-checkout-address';
export * from './use-checkout-notices';
export * from './use-checkout-submit';
export * from './use-checkout-extension-data';
export * from './use-validation';
55 changes: 0 additions & 55 deletions assets/js/base/context/hooks/use-checkout-notices.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ import {
* Internal dependencies
*/
import { useEventEmitters, reducer as emitReducer } from './event-emit';
import type { emitterCallback } from '../../../event-emit';
import { emitterCallback, noticeContexts } from '../../../event-emit';
import { useStoreEvents } from '../../../hooks/use-store-events';
import { useCheckoutNotices } from '../../../hooks/use-checkout-notices';
import {
getExpressPaymentMethods,
getPaymentMethods,
Expand Down Expand Up @@ -134,11 +133,29 @@ export const CheckoutEventsProvider = ( {
}

const { setValidationErrors } = useDispatch( VALIDATION_STORE_KEY );
const { createErrorNotice } = useDispatch( 'core/notices' );

const { dispatchCheckoutEvent } = useStoreEvents();
const { checkoutNotices, paymentNotices, expressPaymentNotices } =
useCheckoutNotices();
useSelect( ( select ) => {
const { getNotices } = select( 'core/notices' );
const checkoutContexts = Object.values( noticeContexts ).filter(
( context ) =>
context !== noticeContexts.PAYMENTS &&
context !== noticeContexts.EXPRESS_PAYMENTS
);
const allCheckoutNotices = checkoutContexts.reduce(
( acc, context ) => {
return [ ...acc, ...getNotices( context ) ];
},
[]
);
return {
checkoutNotices: allCheckoutNotices,
paymentNotices: getNotices( noticeContexts.PAYMENTS ),
expressPaymentNotices: getNotices(
noticeContexts.EXPRESS_PAYMENTS
),
};
}, [] );

const [ observers, observerDispatch ] = useReducer( emitReducer, {} );
const currentObservers = useRef( observers );
Expand Down Expand Up @@ -186,7 +203,6 @@ export const CheckoutEventsProvider = ( {
}, [
isCheckoutBeforeProcessing,
setValidationErrors,
createErrorNotice,
__internalEmitValidateEvent,
] );

Expand Down Expand Up @@ -224,7 +240,6 @@ export const CheckoutEventsProvider = ( {
isCheckoutBeforeProcessing,
previousStatus,
previousHasError,
createErrorNotice,
checkoutNotices,
expressPaymentNotices,
paymentNotices,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import triggerFetch from '@wordpress/api-fetch';
import {
useEffect,
Expand All @@ -12,19 +12,26 @@ import {
} from '@wordpress/element';
import {
emptyHiddenAddressFields,
formatStoreApiErrorMessage,
removeAllNotices,
} from '@woocommerce/base-utils';
import { useDispatch, useSelect } from '@wordpress/data';
import {
CHECKOUT_STORE_KEY,
PAYMENT_STORE_KEY,
VALIDATION_STORE_KEY,
CART_STORE_KEY,
processErrorResponse,
} from '@woocommerce/block-data';
import {
getPaymentMethods,
getExpressPaymentMethods,
} from '@woocommerce/blocks-registry';
import {
ApiResponse,
CheckoutResponseSuccess,
CheckoutResponseError,
assertResponseIsValid,
} from '@woocommerce/types';

/**
* Internal dependencies
Expand All @@ -41,7 +48,6 @@ import { useStoreCart } from '../../hooks/cart/use-store-cart';
*/
const CheckoutProcessor = () => {
const { onCheckoutValidationBeforeProcessing } = useCheckoutEventsContext();

const {
hasError: checkoutHasError,
redirectUrl,
Expand All @@ -64,10 +70,8 @@ const CheckoutProcessor = () => {
extensionData: store.getExtensionData(),
};
} );

const { __internalSetHasError, __internalProcessCheckoutResponse } =
useDispatch( CHECKOUT_STORE_KEY );

const hasValidationErrors = useSelect(
( select ) => select( VALIDATION_STORE_KEY ).hasValidationErrors
);
Expand All @@ -78,7 +82,6 @@ const CheckoutProcessor = () => {
);

const { cartNeedsPayment, cartNeedsShipping, receiveCart } = useStoreCart();
const { createErrorNotice, removeNotice } = useDispatch( 'core/notices' );

const {
activePaymentMethod,
Expand Down Expand Up @@ -126,8 +129,8 @@ const CheckoutProcessor = () => {
( isPaymentSuccess || ! cartNeedsPayment ) &&
checkoutIsProcessing;

// Determine if checkout has an error.
useEffect( () => {
// Determine if checkout has an error.
if (
checkoutWillHaveError !== checkoutHasError &&
( checkoutIsProcessing || checkoutIsBeforeProcessing ) &&
Expand All @@ -144,8 +147,8 @@ const CheckoutProcessor = () => {
__internalSetHasError,
] );

// Keep the billing, shipping and redirectUrl current
useEffect( () => {
// Keep the billing, shipping and redirectUrl current
currentBillingAddress.current = billingAddress;
currentShippingAddress.current = shippingAddress;
currentRedirectUrl.current = redirectUrl;
Expand Down Expand Up @@ -175,17 +178,20 @@ const CheckoutProcessor = () => {
return true;
}, [ hasValidationErrors, hasPaymentError, shippingErrorStatus.hasError ] );

// Validate the checkout using the CHECKOUT_VALIDATION_BEFORE_PROCESSING event
useEffect( () => {
let unsubscribeProcessing;
// Validate the checkout using the CHECKOUT_VALIDATION_BEFORE_PROCESSING event.
let unsubscribeProcessing: () => void;
if ( ! isExpressPaymentMethodActive ) {
unsubscribeProcessing = onCheckoutValidationBeforeProcessing(
checkValidation,
0
);
}
return () => {
if ( ! isExpressPaymentMethodActive ) {
if (
! isExpressPaymentMethodActive &&
typeof unsubscribeProcessing === 'function'
) {
unsubscribeProcessing();
}
};
Expand All @@ -195,8 +201,8 @@ const CheckoutProcessor = () => {
isExpressPaymentMethodActive,
] );

// Redirect when checkout is complete and there is a redirect url.
useEffect( () => {
// Redirect when checkout is complete and there is a redirect url.
if ( currentRedirectUrl.current ) {
window.location.href = currentRedirectUrl.current;
}
Expand All @@ -207,8 +213,8 @@ const CheckoutProcessor = () => {
if ( isProcessingOrder ) {
return;
}
removeAllNotices();
setIsProcessingOrder( true );
removeNotice( 'checkout' );

const paymentData = cartNeedsPayment
? {
Expand All @@ -221,97 +227,67 @@ const CheckoutProcessor = () => {
}
: {};

const data = {
billing_address: emptyHiddenAddressFields(
currentBillingAddress.current
),
customer_note: orderNotes,
create_account: shouldCreateAccount,
...paymentData,
extensions: { ...extensionData },
};

if ( cartNeedsShipping ) {
data.shipping_address = emptyHiddenAddressFields(
currentShippingAddress.current
);
}

triggerFetch( {
path: '/wc/store/v1/checkout',
method: 'POST',
data,
data: {
shipping_address: cartNeedsShipping
? emptyHiddenAddressFields( currentShippingAddress.current )
: undefined,
billing_address: emptyHiddenAddressFields(
currentBillingAddress.current
),
customer_note: orderNotes,
create_account: shouldCreateAccount,
...paymentData,
extensions: { ...extensionData },
},
cache: 'no-store',
parse: false,
} )
.then( ( response ) => {
.then( ( response: unknown ) => {
assertResponseIsValid< CheckoutResponseSuccess >( response );
processCheckoutResponseHeaders( response.headers );
if ( ! response.ok ) {
throw new Error( response );
throw response;
}
return response.json();
} )
.then( ( responseJson ) => {
.then( ( responseJson: CheckoutResponseSuccess ) => {
__internalProcessCheckoutResponse( responseJson );
setIsProcessingOrder( false );
} )
.catch( ( errorResponse ) => {
.catch( ( errorResponse: ApiResponse< CheckoutResponseError > ) => {
processCheckoutResponseHeaders( errorResponse?.headers );
try {
if ( errorResponse?.headers ) {
processCheckoutResponseHeaders( errorResponse.headers );
}
// This attempts to parse a JSON error response where the status code was 4xx/5xx.
errorResponse.json().then( ( response ) => {
// If updated cart state was returned, update the store.
if ( response.data?.cart ) {
receiveCart( response.data.cart );
}
createErrorNotice(
formatStoreApiErrorMessage( response ),
{
id: 'checkout',
context: 'wc/checkout',
__unstableHTML: true,
errorResponse
.json()
.then(
( response ) => response as CheckoutResponseError
)
.then( ( response: CheckoutResponseError ) => {
if ( response.data?.cart ) {
receiveCart( response.data.cart );
}
);
response?.additional_errors?.forEach?.(
( additionalError ) => {
createErrorNotice( additionalError.message, {
id: additionalError.error_code,
context: 'wc/checkout',
__unstableHTML: true,
} );
}
);
__internalProcessCheckoutResponse( response );
} );
processErrorResponse( response );
__internalProcessCheckoutResponse( response );
} );
} catch {
createErrorNotice(
sprintf(
// Translators: %s Error text.
__(
'%s Please try placing your order again.',
'woo-gutenberg-products-block'
),
errorResponse?.message ??
__(
'Something went wrong. Please contact us for assistance.',
'woo-gutenberg-products-block'
)
processErrorResponse( {
code: 'unknown_error',
message: __(
'Something went wrong. Please try placing your order again.',
'woo-gutenberg-products-block'
),
{
id: 'checkout',
context: 'wc/checkout',
__unstableHTML: true,
}
);
data: null,
} );
}
__internalSetHasError( true );
setIsProcessingOrder( false );
} );
}, [
isProcessingOrder,
removeNotice,
cartNeedsPayment,
paymentMethodId,
paymentMethodData,
Expand All @@ -321,7 +297,6 @@ const CheckoutProcessor = () => {
shouldCreateAccount,
extensionData,
cartNeedsShipping,
createErrorNotice,
receiveCart,
__internalSetHasError,
__internalProcessCheckoutResponse,
Expand Down
Loading

0 comments on commit fc95e40

Please sign in to comment.