Skip to content

Commit 5ee2de6

Browse files
committed
Fix group on iOS notifications
1 parent 85c4d37 commit 5ee2de6

File tree

2 files changed

+50
-18
lines changed

2 files changed

+50
-18
lines changed

ios/NotificationService/Info.plist

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,12 @@
3737
<array>
3838
<string>INSendMessageIntent</string>
3939
</array>
40+
<key>NSExtensionAttributes</key>
41+
<dict>
42+
<key>IntentsSupported</key>
43+
<array>
44+
<string>INSendMessageIntent</string>
45+
</array>
46+
</dict>
4047
</dict>
4148
</plist>

ios/NotificationService/NotificationService.swift

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class NotificationService: UNNotificationServiceExtension {
7070
guard let bestAttemptContent = bestAttemptContent else { return }
7171

7272
let senderName = payload.sender?.name ?? payload.senderName ?? "Unknown"
73-
let senderId = payload.sender?._id ?? ""
7473
let senderUsername = payload.sender?.username ?? ""
7574

7675
// Create avatar image for the sender
@@ -86,31 +85,43 @@ class NotificationService: UNNotificationServiceExtension {
8685
displayName: senderName,
8786
image: senderImage,
8887
contactIdentifier: nil,
89-
customIdentifier: senderId
88+
customIdentifier: nil
9089
)
9190

92-
// Determine conversation name (room name for groups, sender name for DMs)
93-
let conversationName: String
94-
if payload.type == .group, let roomName = payload.name {
95-
conversationName = roomName
96-
} else {
97-
conversationName = senderName
98-
}
91+
// Determine if this is a group or channel conversation
92+
let roomName = payload.name
93+
let isGroupOrChannel = (payload.type == .group || payload.type == .channel) && roomName != nil && !roomName!.isEmpty
94+
95+
// Create speakable group name for group/channel conversations
96+
let speakableGroupName: INSpeakableString? = isGroupOrChannel && roomName != nil
97+
? INSpeakableString(spokenPhrase: roomName!)
98+
: nil
99+
100+
// Create a dummy recipient to ensure iOS treats this as a group conversation
101+
// This is necessary for iOS to use speakableGroupName instead of sender name
102+
let dummyRecipient = INPerson(
103+
personHandle: INPersonHandle(value: "placeholder", type: .unknown),
104+
nameComponents: nil,
105+
displayName: nil,
106+
image: nil,
107+
contactIdentifier: nil,
108+
customIdentifier: "recipient_\(payload.rid ?? "group")"
109+
)
99110

100111
// Create the messaging intent
101112
let intent = INSendMessageIntent(
102-
recipients: nil,
113+
recipients: isGroupOrChannel ? [dummyRecipient] : nil,
103114
outgoingMessageType: .outgoingMessageText,
104115
content: bestAttemptContent.body,
105-
speakableGroupName: INSpeakableString(spokenPhrase: conversationName),
116+
speakableGroupName: speakableGroupName,
106117
conversationIdentifier: payload.rid ?? "",
107118
serviceName: nil,
108119
sender: sender,
109120
attachments: nil
110121
)
111122

112-
// If it's a group chat, set the group avatar
113-
if payload.type == .group {
123+
// Set group avatar for group/channel conversations
124+
if isGroupOrChannel {
114125
intent.setImage(senderImage, forParameterNamed: \.speakableGroupName)
115126
}
116127

@@ -122,10 +133,9 @@ class NotificationService: UNNotificationServiceExtension {
122133
// Update the notification content with the intent
123134
do {
124135
let updatedContent = try bestAttemptContent.updating(from: intent)
125-
// Store the updated content directly - don't use mutableCopy() as it strips the intent association
126136
self.finalContent = updatedContent
127137
} catch {
128-
// Keep bestAttemptContent as fallback
138+
// Fallback to bestAttemptContent if intent update fails
129139
self.finalContent = bestAttemptContent
130140
}
131141
}
@@ -170,6 +180,7 @@ class NotificationService: UNNotificationServiceExtension {
170180
if let messageId = data.messageId {
171181
self.rocketchat?.getPushWithId(messageId) { notification in
172182
if let notification = notification {
183+
// Set body first, processPayload will strip sender prefix for groups/channels
173184
self.bestAttemptContent?.body = notification.text
174185

175186
// Update ejson with full payload from server for correct navigation
@@ -227,11 +238,28 @@ class NotificationService: UNNotificationServiceExtension {
227238
func processPayload(payload: Payload) {
228239
// Set notification title based on payload type
229240
let senderName = payload.sender?.name ?? payload.senderName ?? "Unknown"
241+
let senderUsername = payload.sender?.username ?? payload.senderName ?? ""
242+
230243
if let roomType = payload.type {
231244
switch roomType {
232245
case .group, .channel:
233246
// For groups/channels, use room name if available, otherwise fall back to sender name
234247
bestAttemptContent?.title = payload.name ?? senderName
248+
249+
// Remove sender name prefix from body for groups/channels
250+
// Server sends body as "senderName: message", but we only want "message"
251+
if let body = bestAttemptContent?.body {
252+
let senderPrefix = "\(senderUsername): "
253+
if body.hasPrefix(senderPrefix) {
254+
bestAttemptContent?.body = String(body.dropFirst(senderPrefix.count))
255+
} else {
256+
// Try with sender name (display name) as fallback
257+
let senderNamePrefix = "\(senderName): "
258+
if body.hasPrefix(senderNamePrefix) {
259+
bestAttemptContent?.body = String(body.dropFirst(senderNamePrefix.count))
260+
}
261+
}
262+
}
235263
case .direct:
236264
// For direct messages, use sender name
237265
bestAttemptContent?.title = senderName
@@ -260,9 +288,6 @@ class NotificationService: UNNotificationServiceExtension {
260288

261289
if let decryptedMessage = decryptedMessage {
262290
bestAttemptContent?.body = decryptedMessage
263-
if let roomType = payload.type, roomType == .group, let sender = payload.senderName {
264-
bestAttemptContent?.body = "\(sender): \(decryptedMessage)"
265-
}
266291
}
267292
}
268293
}

0 commit comments

Comments
 (0)