From 42d993a78fb07efbb30b939478e6a1e36f307eab Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 22 May 2026 17:51:43 +0300 Subject: [PATCH] [Chat] Fix iOS group chat notifications rendering as direct messages (#10590) Co-authored-by: Claude Opus 4.7 (1M context) --- modules/BlueskyNSE/NotificationService.swift | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/BlueskyNSE/NotificationService.swift b/modules/BlueskyNSE/NotificationService.swift index 18b63b1606..3f40999cfd 100644 --- a/modules/BlueskyNSE/NotificationService.swift +++ b/modules/BlueskyNSE/NotificationService.swift @@ -104,7 +104,7 @@ class NotificationService: UNNotificationServiceExtension { var speakableGroupName: INSpeakableString? = nil if userInfo["convoKind"] as? String == "group", - let groupName = userInfo["convoGroupName"] as? String, + let groupName = userInfo["groupName"] as? String, !groupName.isEmpty { speakableGroupName = INSpeakableString(spokenPhrase: groupName) } @@ -120,6 +120,19 @@ class NotificationService: UNNotificationServiceExtension { attachments: nil ) + // The push payload omits the full recipient list for group convos. Without + // any signal of multiple recipients, iOS classifies the notification as + // `MessagingDirect` and ignores `speakableGroupName`. Setting + // `recipientCount` on the donation metadata is Apple's documented escape + // hatch for this case, and overrides the (zero-length) recipients array. + if userInfo["convoKind"] as? String == "group", + let groupMemberCount = userInfo["groupMemberCount"] as? Int, + groupMemberCount > 0 { + let metadata = INSendMessageIntentDonationMetadata() + metadata.recipientCount = groupMemberCount + intent.donationMetadata = metadata + } + // For group convos, attach the composite group avatar (rendered by the // ogcard service) to the `speakableGroupName` parameter so iOS shows it // alongside the sender on the Communication Notification. @@ -196,7 +209,7 @@ class NotificationService: UNNotificationServiceExtension { userInfo: [AnyHashable: Any] ) { guard userInfo["convoKind"] as? String == "group", - let groupName = userInfo["convoGroupName"] as? String, + let groupName = userInfo["groupName"] as? String, !groupName.isEmpty else { return }