Commit e214e8a6ee for woocommerce

commit e214e8a6eea24bff0dc2fb5be896bb8575f4e646
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Thu Sep 18 16:25:07 2025 +0200

    Add to Cart Button: disable optimistic updates if product is sold individually and is already in the cart (#60947)

    * Add changelog file

    * Add to Cart Button: disable optimistic updates if product is sold individually and is already in the cart

    * CodeRabbit suggestion

    * Make comment more explanatory

diff --git a/plugins/woocommerce/changelog/fix-sold-individually-optimistic-updates b/plugins/woocommerce/changelog/fix-sold-individually-optimistic-updates
new file mode 100644
index 0000000000..995e51c0b9
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-sold-individually-optimistic-updates
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Add to Cart Button: disable optimistic updates if product is sold individually and is already in the cart
diff --git a/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/button/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/button/frontend.ts
index 502214ad46..af331a0ed0 100644
--- a/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/button/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/atomic/blocks/product-elements/button/frontend.ts
@@ -213,7 +213,15 @@ const productButtonStore = {
 				addToCartWithOptionsState?.isFormValid
 			) {
 				context.hasPressedButton = true;
-				context.animationStatus = AnimationStatus.SLIDE_OUT;
+
+				// Only animate if the quantity number changes and there is no
+				// animation in progress.
+				if (
+					context.tempQuantity !== state.quantity &&
+					context.animationStatus === AnimationStatus.IDLE
+				) {
+					context.animationStatus = AnimationStatus.SLIDE_OUT;
+				}
 			}
 		},
 	},
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
index 1db4c8cc10..edbbc437da 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
@@ -40,7 +40,6 @@ export type OptimisticCartItem = {
 	quantity: number;
 	variation?: CartVariationItem[];
 	type: string;
-	updateOptimistically?: boolean;
 };

 export type ClientCartItem = Omit< OptimisticCartItem, 'variation' > & {
@@ -305,12 +304,7 @@ const { state, actions } = store< Store >(
 			},

 			*addCartItem(
-				{
-					id,
-					quantity,
-					variation,
-					updateOptimistically = true,
-				}: ClientCartItem,
+				{ id, quantity, variation }: ClientCartItem,
 				{ showCartUpdatesNotices = true }: CartUpdateOptions = {}
 			) {
 				let item = state.cart.items.find( ( cartItem ) => {
@@ -339,12 +333,16 @@ const { state, actions } = store< Store >(
 				const previousCart = JSON.stringify( state.cart );
 				const quantityChanges: QuantityChanges = {};

-				// Optimistically updates the number of items in the cart.
+				// Optimistically update the number of items in the cart except
+				// if the product is sold individually and is already in the
+				// cart.
+				let updatedItem = null;
 				if ( item ) {
-					if ( item.key ) {
+					const isSoldIndividually =
+						isCartItem( item ) && item.sold_individually;
+					updatedItem = { ...item, quantity };
+					if ( item.key && ! isSoldIndividually ) {
 						quantityChanges.cartItemsPendingQuantity = [ item.key ];
-					}
-					if ( updateOptimistically ) {
 						item.quantity = quantity;
 					}
 				} else {
@@ -354,9 +352,8 @@ const { state, actions } = store< Store >(
 						variation,
 					} as OptimisticCartItem;
 					quantityChanges.productsPendingAdd = [ id ];
-					if ( updateOptimistically ) {
-						state.cart.items.push( item );
-					}
+					state.cart.items.push( item );
+					updatedItem = item;
 				}

 				// Updates the database.
@@ -369,7 +366,7 @@ const { state, actions } = store< Store >(
 								Nonce: state.nonce,
 								'Content-Type': 'application/json',
 							},
-							body: JSON.stringify( item ),
+							body: JSON.stringify( updatedItem ),
 						}
 					);
 					const json: Cart = yield res.json();