Commit d0f2dca6371 for woocommerce

commit d0f2dca63712917c3ed0c2502d781f249cb19bad
Author: Marcus Karlos <manager1@onmail.com>
Date:   Thu Mar 5 19:40:20 2026 +0200

    Fix cart: prevent false "Quantity changed to 1" notices and add optimistic removal feedback (#63403)

    * fix(cart): prevent false 'Quantity changed to 1' notices and add optimistic removal feedback

    Fixes #63381

    1. Prevents false "Quantity changed to 1" notices during rapid delete/re-add flows
       by checking `old.quantity > 0` in `getInfoNoticesFromCartUpdates`.
    2. Ensures "Removed" notices appear instantly (optimistic UI) when removing items.

    * fix(cart): trigger immediate notice on item removal and cleanup unused vars

    * Changes in this proposed patch

    * Update fix_false_notices_quantity_changed

    * fix: replace space indentation with tabs in cart.ts

    - Fix indentation in getInfoNoticesFromCartUpdates: replace spaces with
      tabs for destructuring block and autoDeletedToNotify /
      autoUpdatedToNotify variable declarations

    - Fix indentation in removeCartItem: replace spaces with tabs for
      itemToRemove lookup, optimistic notice dispatch, and
      quantityChanges initialization block

    * fix(cart): clear notices when last cart item is removed

    * fix(cart): simplify notice logic per review — remove quantityChanges tracking, revert optimistic removal notice

    Address review feedback from @SantosGuillamot on #63403.

    getInfoNoticesFromCartUpdates:
    - Remove quantityChanges parameter entirely — not needed since we
      compare the optimistic snapshot (oldCart) against the server response
      (newCart). User-initiated changes are already reflected in oldCart,
      so only server-side corrections (stock cap, sold-individually, auto-
      removed items) produce diffs and generate notices.
    - Remove redundant old.key && guard — isCartItem() and the .some()
      comparison already cover this.
    - Simplify autoUpdatedToNotify predicate to:
      return old && item.quantity !== old.quantity

    removeCartItem:
    - Revert optimistic "was removed from your cart." notice — user-
      initiated removals should not generate notices. The server-response
      path in getInfoNoticesFromCartUpdates handles any unexpected server-
      side removals correctly.
    - Remove itemToRemove lookup and isLastItem branch (no longer needed).
    - quantityChanges kept — still required for emitSyncEvent.

    * Back to removeCartItem AsyncAction

diff --git a/plugins/woocommerce/changelog/fix_false_notices_quantity_changed b/plugins/woocommerce/changelog/fix_false_notices_quantity_changed
new file mode 100644
index 00000000000..84f9ea92e15
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix_false_notices_quantity_changed
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix cart: prevent false "Quantity changed to 1" notices and add optimistic removal feedback
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 0ca5a694fd3..fcd160be9fd 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
@@ -165,37 +165,30 @@ const generateInfoNotice = ( message: string ): Notice => ( {

 const getInfoNoticesFromCartUpdates = (
 	oldCart: Store[ 'state' ][ 'cart' ],
-	newCart: Cart,
-	quantityChanges: QuantityChanges
+	newCart: Cart
 ): Notice[] => {
 	const oldItems = oldCart.items;
 	const newItems = newCart.items;

-	const {
-		productsPendingAdd: pendingAdd = [],
-		cartItemsPendingQuantity: pendingQuantity = [],
-		cartItemsPendingDelete: pendingDelete = [],
-	} = quantityChanges;
-
-	const autoDeletedToNotify = oldItems
-		.filter( isCartItem )
-		.filter(
-			( old ) =>
-				! newItems.some( ( item ) => old.key === item.key ) &&
-				! pendingDelete.includes( old.key )
-		);
+	// Items auto-removed by the server (stock change, product deleted, etc.).
+	// We pass the optimistic snapshot as oldCart, so user-initiated removals
+	// are already absent and do not generate spurious notices here.
+	const autoDeletedToNotify = oldItems.filter(
+		( old ) =>
+			isCartItem( old ) &&
+			! newItems.some( ( item ) => old.key === item.key )
+	);

+	// Items whose quantity was adjusted by the server (stock cap, sold-individually).
+	// Comparing optimistic → server means intentional user changes are already
+	// reflected in oldItems and will not trigger this notice.
 	const autoUpdatedToNotify = newItems.filter( ( item ) => {
 		if ( ! isCartItem( item ) ) {
 			return false;
 		}
 		const old = oldItems.find( ( o ) => o.key === item.key );
-		return old
-			? ! pendingQuantity.includes( item.key ) &&
-					item.quantity !== old.quantity
-			: ! pendingAdd.includes( item.id );
+		return old && item.quantity !== old.quantity;
 	} );
-
 	return [
 		...autoDeletedToNotify.map( ( item ) =>
 			// TODO: move the message template to iAPI config.
@@ -320,7 +313,7 @@ const { state, actions } = store< Store >(
 	{
 		actions: {
 			*removeCartItem( key: string ): AsyncAction< void > {
-				// Track what changes we're making for notice comparison.
+				// Track what changes we're making for the sync event.
 				const quantityChanges: QuantityChanges = {
 					cartItemsPendingDelete: [ key ],
 				};
@@ -357,8 +350,7 @@ const { state, actions } = store< Store >(
 					if ( cart && cartAfterOptimistic ) {
 						const infoNotices = getInfoNoticesFromCartUpdates(
 							cartAfterOptimistic,
-							cart,
-							quantityChanges
+							cart
 						);
 						const errorNotices =
 							cart.errors.map( generateErrorNotice );
@@ -505,8 +497,7 @@ const { state, actions } = store< Store >(
 					) {
 						const infoNotices = getInfoNoticesFromCartUpdates(
 							cartAfterOptimistic,
-							cart,
-							quantityChanges
+							cart
 						);
 						const errorNotices =
 							cart.errors.map( generateErrorNotice );
@@ -647,8 +638,7 @@ const { state, actions } = store< Store >(
 						if ( showCartUpdatesNotices ) {
 							const infoNotices = getInfoNoticesFromCartUpdates(
 								cartAfterOptimistic,
-								cart,
-								quantityChanges
+								cart
 							);
 							const errorNotices =
 								cart.errors.map( generateErrorNotice );