From c509e70b140f58b9775757cdad269a7c0907402f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 14 Feb 2023 15:42:28 +0000 Subject: [PATCH 1/5] Support partial push without country field --- src/StoreApi/Schemas/V1/AbstractAddressSchema.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/StoreApi/Schemas/V1/AbstractAddressSchema.php b/src/StoreApi/Schemas/V1/AbstractAddressSchema.php index 0f759762d6d..cae085878d1 100644 --- a/src/StoreApi/Schemas/V1/AbstractAddressSchema.php +++ b/src/StoreApi/Schemas/V1/AbstractAddressSchema.php @@ -167,15 +167,7 @@ public function validate_callback( $address, $request, $param ) { $errors = new \WP_Error(); $address = $this->sanitize_callback( $address, $request, $param ); - if ( empty( $address['country'] ) ) { - $errors->add( - 'missing_country', - __( 'Country is required', 'woo-gutenberg-products-block' ) - ); - return $errors; - } - - if ( ! in_array( $address['country'], array_keys( wc()->countries->get_countries() ), true ) ) { + if ( ! empty( $address['country'] ) && ! in_array( $address['country'], array_keys( wc()->countries->get_countries() ), true ) ) { $errors->add( 'invalid_country', sprintf( From 9d7d25915513d1358a503b8e41a10818386dfad4 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 14 Feb 2023 15:43:30 +0000 Subject: [PATCH 2/5] Increase timeout to prevent excessive pushes --- assets/js/data/cart/push-changes.ts | 46 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/assets/js/data/cart/push-changes.ts b/assets/js/data/cart/push-changes.ts index 06228265a8e..d15809087bc 100644 --- a/assets/js/data/cart/push-changes.ts +++ b/assets/js/data/cart/push-changes.ts @@ -34,25 +34,28 @@ const isBillingAddress = ( return 'email' in address; }; -export const trimAddress = ( address: BillingOrShippingAddress ) => { - const trimmedAddress = { - ...address, - }; - Object.keys( address ).forEach( ( key ) => { - trimmedAddress[ key as keyof BillingOrShippingAddress ] = - address[ key as keyof BillingOrShippingAddress ].trim(); - } ); - - trimmedAddress.postcode = trimmedAddress.postcode - ? trimmedAddress.postcode.replace( ' ', '' ).toUpperCase() - : ''; - +/** + * Trims and normalizes address data for comparison. + */ +export const normalizeAddress = ( address: BillingOrShippingAddress ) => { + const trimmedAddress = Object.entries( address ).reduce( + ( acc, [ key, value ] ) => { + if ( key === 'postcode' ) { + acc[ key as keyof BillingOrShippingAddress ] = value + .replace( ' ', '' ) + .toUpperCase(); + } else { + acc[ key as keyof BillingOrShippingAddress ] = value.trim(); + } + return acc; + }, + {} as BillingOrShippingAddress + ); return trimmedAddress; }; /** - * Does a shallow compare of important address data to determine if the cart needs updating on the server. This takes - * the current and previous address into account, as well as the billing email field. + * Does a shallow compare of all address data to determine if the cart needs updating on the server. */ const isAddressDirty = < T extends CartBillingAddress | CartShippingAddress >( // An object containing all previous address information @@ -68,13 +71,12 @@ const isAddressDirty = < T extends CartBillingAddress | CartShippingAddress >( return true; } - return ( - !! address.country && - ! isShallowEqual( - trimAddress( previousAddress ), - trimAddress( address ) - ) + const addressMatches = isShallowEqual( + normalizeAddress( previousAddress ), + normalizeAddress( address ) ); + + return ! addressMatches; }; type BaseAddressKey = keyof CartBillingAddress | keyof CartShippingAddress; @@ -174,7 +176,7 @@ const updateCustomerData = debounce( (): void => { processErrorResponse( response ); } ); } -}, 1000 ); +}, 3000 ); /** * After cart has fully initialized, pushes changes to the server when data in the store is changed. Updates to the From 21da552d50fcba51c9e8cc9df2920bb7153359b7 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 15 Feb 2023 10:14:52 +0000 Subject: [PATCH 3/5] Push on focusout --- assets/js/data/cart/index.ts | 7 ++++++- assets/js/data/cart/push-changes.ts | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/js/data/cart/index.ts b/assets/js/data/cart/index.ts index 5ddd7de7793..f1de44345c9 100644 --- a/assets/js/data/cart/index.ts +++ b/assets/js/data/cart/index.ts @@ -13,7 +13,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducers'; import type { SelectFromMap, DispatchFromMap } from '../mapped-types'; -import { pushChanges } from './push-changes'; +import { pushChanges, flushChanges } from './push-changes'; import { updatePaymentMethods, debouncedUpdatePaymentMethods, @@ -34,6 +34,11 @@ const registeredStore = registerStore< State >( STORE_KEY, { registeredStore.subscribe( pushChanges ); +// This will skip the debounce and immediately push changes to the server when a field is blurred. +document.body.addEventListener( 'focusout', () => { + flushChanges(); +} ); + // First we will run the updatePaymentMethods function without any debounce to ensure payment methods are ready as soon // as the cart is loaded. After that, we will unsubscribe this function and instead run the // debouncedUpdatePaymentMethods function on subsequent cart updates. diff --git a/assets/js/data/cart/push-changes.ts b/assets/js/data/cart/push-changes.ts index d15809087bc..c9fa4889bd8 100644 --- a/assets/js/data/cart/push-changes.ts +++ b/assets/js/data/cart/push-changes.ts @@ -176,7 +176,7 @@ const updateCustomerData = debounce( (): void => { processErrorResponse( response ); } ); } -}, 3000 ); +}, 5000 ); /** * After cart has fully initialized, pushes changes to the server when data in the store is changed. Updates to the @@ -241,3 +241,7 @@ export const pushChanges = (): void => { updateCustomerData(); } }; + +export const flushChanges = (): void => { + updateCustomerData.flush(); +}; From d046c7046b3c07742e14f2a5a904bf9aa6677d7e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 15 Feb 2023 10:25:56 +0000 Subject: [PATCH 4/5] Only flush inputs --- assets/js/data/cart/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/assets/js/data/cart/index.ts b/assets/js/data/cart/index.ts index f1de44345c9..c072ea09578 100644 --- a/assets/js/data/cart/index.ts +++ b/assets/js/data/cart/index.ts @@ -35,8 +35,14 @@ const registeredStore = registerStore< State >( STORE_KEY, { registeredStore.subscribe( pushChanges ); // This will skip the debounce and immediately push changes to the server when a field is blurred. -document.body.addEventListener( 'focusout', () => { - flushChanges(); +document.body.addEventListener( 'focusout', ( event: FocusEvent ) => { + if ( + event.target && + event.target instanceof Element && + event.target.tagName.toLowerCase() === 'input' + ) { + flushChanges(); + } } ); // First we will run the updatePaymentMethods function without any debounce to ensure payment methods are ready as soon From 0fbde555648f2494458c897d5b9fe932fb59e046 Mon Sep 17 00:00:00 2001 From: Nadir Seghir Date: Wed, 15 Feb 2023 12:08:53 +0100 Subject: [PATCH 5/5] change debounce timer to 1s --- assets/js/data/cart/push-changes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/data/cart/push-changes.ts b/assets/js/data/cart/push-changes.ts index c9fa4889bd8..85373cf9e88 100644 --- a/assets/js/data/cart/push-changes.ts +++ b/assets/js/data/cart/push-changes.ts @@ -176,7 +176,7 @@ const updateCustomerData = debounce( (): void => { processErrorResponse( response ); } ); } -}, 5000 ); +}, 1000 ); /** * After cart has fully initialized, pushes changes to the server when data in the store is changed. Updates to the