Commit a026c842dc7 for woocommerce
commit a026c842dc7cb67211f5d29d04e943d2503d4c3d
Author: Irfan Ömür <omur.irfan@gmail.com>
Date: Tue Sep 29 22:41:04 2026 +0300
Dispatch new order push notifications before the rest of the batch (#68356)
* Dispatch new order push notifications before the rest of the batch
* Add changelog entry
* Update the since version of the new order sort to 11.3.0
diff --git a/plugins/woocommerce/changelog/fix-push-notification-order-first b/plugins/woocommerce/changelog/fix-push-notification-order-first
new file mode 100644
index 00000000000..a96b3003f65
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-push-notification-order-first
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Send the new order push notification before the stock ones queued by the same order, so devices play the new order sound.
diff --git a/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php b/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php
index aaf0b4521f0..bfd8e95e6d9 100644
--- a/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php
+++ b/plugins/woocommerce/src/Internal/PushNotifications/Services/PendingNotificationStore.php
@@ -8,6 +8,7 @@ defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Internal\PushNotifications\DataStores\PushTokensDataStore;
use Automattic\WooCommerce\Internal\PushNotifications\Dispatchers\InternalNotificationDispatcher;
+use Automattic\WooCommerce\Internal\PushNotifications\Notifications\NewOrderNotification;
use Automattic\WooCommerce\Internal\PushNotifications\Notifications\Notification;
use Automattic\WooCommerce\Internal\PushNotifications\PushNotifications;
use Automattic\WooCommerce\Internal\PushNotifications\Services\NotificationProcessor;
@@ -181,7 +182,7 @@ class PendingNotificationStore {
$this->record_trigger_times();
- $this->dispatcher->dispatch( array_values( $this->pending ) );
+ $this->dispatcher->dispatch( $this->sort_with_new_orders_first( array_values( $this->pending ) ) );
$this->enabled = false;
$this->pending = array();
@@ -237,6 +238,33 @@ class PendingNotificationStore {
}
}
+ /**
+ * Moves new order notifications to the front of the batch.
+ *
+ * Android only plays a sound for the first notification of a burst, and
+ * stock is reduced before the order notification is queued, so the order
+ * must go first for its sound to play.
+ *
+ * @param Notification[] $notifications The notifications about to be dispatched.
+ * @return Notification[]
+ *
+ * @since 11.3.0
+ */
+ private function sort_with_new_orders_first( array $notifications ): array {
+ $new_orders = array();
+ $others = array();
+
+ foreach ( $notifications as $notification ) {
+ if ( NewOrderNotification::TYPE === $notification->get_type() ) {
+ $new_orders[] = $notification;
+ } else {
+ $others[] = $notification;
+ }
+ }
+
+ return array_merge( $new_orders, $others );
+ }
+
/**
* Returns the number of pending notifications.
*
diff --git a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php
index 528288b2c6a..53994eeaa92 100644
--- a/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/PushNotifications/Services/PendingNotificationStoreTest.php
@@ -291,6 +291,48 @@ class PendingNotificationStoreTest extends WC_Unit_Test_Case {
remove_action( 'shutdown', array( $store, 'dispatch_all' ) );
}
+ /**
+ * @testdox Should dispatch new order notifications before the rest of the batch.
+ */
+ public function test_dispatch_all_sends_new_orders_first(): void {
+ $dispatched = array();
+ $dispatcher = $this->createMock( InternalNotificationDispatcher::class );
+ $dispatcher->expects( $this->once() )
+ ->method( 'dispatch' )
+ ->willReturnCallback(
+ function ( array $notifications ) use ( &$dispatched ) {
+ $dispatched = $notifications;
+ }
+ );
+
+ $store = new PendingNotificationStore();
+ $store->init( $dispatcher, $this->create_data_store_with_tokens( true ) );
+ $store->register();
+ $store->add( $this->create_stock_mock( 42, StockNotification::EVENT_LOW_STOCK ) );
+ $store->add( $this->create_stock_mock( 43, StockNotification::EVENT_LOW_STOCK ) );
+ $store->add( $this->create_order_mock( 1 ) );
+
+ $store->dispatch_all();
+
+ remove_action( 'shutdown', array( $store, 'dispatch_all' ) );
+
+ $dispatched_order = array_map(
+ function ( $notification ) {
+ return $notification->get_type() . ':' . $notification->get_resource_id();
+ },
+ $dispatched
+ );
+
+ $this->assertSame(
+ array(
+ NewOrderNotification::TYPE . ':1',
+ StockNotification::TYPE . ':42',
+ StockNotification::TYPE . ':43',
+ ),
+ $dispatched_order
+ );
+ }
+
/**
* @testdox Should not call the dispatcher when there are no pending notifications.
*/