[network/kaidan] src/qml: UI: Fix duplicated passive notifications for (un)blocking

Melvin Keskin <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit d28d40510e97a2fceb3abc76c7f08801807c2a68 by Melvin Keskin, on behalf of Linus Jahn.
Committed on 06/08/2026 at 12:44.
Pushed by melvo into branch 'master'.

UI: Fix duplicated passive notifications for (un)blocking

The notification to block an unblocked JID again and the (un)blocking failure
notifications were emitted by two separate connections (in ChatPage and
GlobalDrawer) bound to the same BlockingController. When an account's chat was
open while the same account was modified from its details, both were triggered.
Thus, the notification appeared twice. Triggering both undo actions blocked the
JID twice and listed it twice.

This handles those notifications once per account in a single global
instantiator instead. That reacts to (un)blocking regardless of where it is
triggered, covers all accounts, and keeps the undo action alive after the
originating dialog is destroyed.

M  +0    -25   src/qml/ChatPage.qml
M  +0    -25   src/qml/GlobalDrawer.qml
M  +35   -0    src/qml/main.qml

https://invent.kde.org/network/kaidan/-/commit/d28d40510e97a2fceb3abc76c7f08801807c2a68

diff --git a/src/qml/ChatPage.qml b/src/qml/ChatPage.qml
index d22016dad..1acd25580 100644
--- a/src/qml/ChatPage.qml
+++ b/src/qml/ChatPage.qml
@@ -901,31 +901,6 @@ SearchBarPage {
 		}
 	}
 
-	// Needs to be outside of the DetailsDialog to not be destroyed with it.
-	// Otherwise, the undo action of "showPassiveNotification()" would point to a destroyed object.
-	Connections {
-		target: root.chatController.account ? root.chatController.account.blockingController : null
-
-		function onUnblocked(jid) {
-			// Show a passive notification when a JID that is not in the roster is unblocked and
-			// provide an option to undo that.
-			// JIDs in the roster can be blocked again via their details.
-			if (!RosterModel.hasItem(root.chatController.account.settings.jid, jid)) {
-				showPassiveNotification(qsTr("Unblocked %1", "%1 is a JID").arg(jid), "long", qsTr("Undo"), () => {
-					root.chatController.account.blockingController.block(jid)
-				})
-			}
-		}
-
-		function onBlockingFailed(jid, errorText) {
-			showPassiveNotification(qsTr("Could not block %1: %2", "%1 is a JID, %2 an error message").arg(jid).arg(errorText))
-		}
-
-		function onUnblockingFailed(jid, errorText) {
-			showPassiveNotification(qsTr("Could not unblock %1: %2", "%1 is a JID, %2 an error message").arg(jid).arg(errorText))
-		}
-	}
-
 	/**
 	 * Searches for a message containing the entered text in the search field starting from the current index of the message list view.
 	 *
diff --git a/src/qml/GlobalDrawer.qml b/src/qml/GlobalDrawer.qml
index 288f50269..05be0d56c 100644
--- a/src/qml/GlobalDrawer.qml
+++ b/src/qml/GlobalDrawer.qml
@@ -469,31 +469,6 @@ Kirigami.GlobalDrawer {
 		}
 	}
 
-	// Needs to be outside of the DetailsDialog to not be destroyed with it.
-	// Otherwise, the undo action of "showPassiveNotification()" would point to a destroyed object.
-	Connections {
-		target: root.selectedAccount ? root.selectedAccount.blockingController : null
-
-		function onUnblocked(jid) {
-			// Show a passive notification when a JID that is not in the roster is unblocked and
-			// provide an option to undo that.
-			// JIDs in the roster can be blocked again via their details.
-			if (!RosterModel.hasItem(root.selectedAccount.settings.jid, jid)) {
-				showPassiveNotification(qsTr("Unblocked %1", "%1 is a JID").arg(jid), "long", qsTr("Undo"), () => {
-					root.selectedAccount.blockingController.block(jid)
-				})
-			}
-		}
-
-		function onBlockingFailed(jid, errorText) {
-			showPassiveNotification(qsTr("Could not block %1: %2", "%1 is a JID, %2 an error message").arg(jid).arg(errorText))
-		}
-
-		function onUnblockingFailed(jid, errorText) {
-			showPassiveNotification(qsTr("Could not unblock %1: %2", "%1 is a JID, %2 an error message").arg(jid).arg(errorText))
-		}
-	}
-
 	function openStartPage(accountAvailable = false) {
 		if (accountAvailable) {
 			openPage(startPage)
diff --git a/src/qml/main.qml b/src/qml/main.qml
index 284b9bb61..c7d7f260f 100644
--- a/src/qml/main.qml
+++ b/src/qml/main.qml
@@ -13,6 +13,7 @@
 // SPDX-License-Identifier: GPL-3.0-or-later
 
 import QtQuick
+import QtQml.Models
 import QtQuick.Controls.Material as Material
 import org.kde.kirigami as Kirigami
 
@@ -160,6 +161,40 @@ Kirigami.ApplicationWindow {
 		}
 	}
 
+	// Handle blocking-related passive notifications globally, exactly once per account.
+	// This must not live inside a page or dialog: it has to react to (un)blocking regardless of
+	// where it is triggered from (e.g., contact details or account details) without showing the
+	// notification multiple times, and the undo action must survive the destruction of the dialog
+	// from which (un)blocking was triggered.
+	Instantiator {
+		model: AccountController.accounts
+
+		delegate: Connections {
+			required property var modelData
+
+			target: modelData.blockingController
+
+			function onUnblocked(jid) {
+				// Show a passive notification when a JID that is not in the roster is unblocked and
+				// provide an option to undo that.
+				// JIDs in the roster can be blocked again via their details.
+				if (!RosterModel.hasItem(modelData.settings.jid, jid)) {
+					showPassiveNotification(qsTr("Unblocked %1", "%1 is a JID").arg(jid), "long", qsTr("Undo"), () => {
+						modelData.blockingController.block(jid)
+					})
+				}
+			}
+
+			function onBlockingFailed(jid, errorText) {
+				showPassiveNotification(qsTr("Could not block %1: %2", "%1 is a JID, %2 an error message").arg(jid).arg(errorText))
+			}
+
+			function onUnblockingFailed(jid, errorText) {
+				showPassiveNotification(qsTr("Could not unblock %1: %2", "%1 is a JID, %2 an error message").arg(jid).arg(errorText))
+			}
+		}
+	}
+
 	Connections {
 		target: pageStack
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.