diff --git a/android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java b/android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java index d468c7f91f..22339abd69 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java +++ b/android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java @@ -95,7 +95,7 @@ public void onReceived() { private void handleNotification() { Ejson receivedEjson = safeFromJson(mBundle.getString("ejson", "{}"), Ejson.class); - + if (receivedEjson != null && receivedEjson.notificationType != null && receivedEjson.notificationType.equals("message-id-only")) { Log.d(TAG, "Detected message-id-only notification, will fetch full content from server"); loadNotificationAndProcess(receivedEjson); @@ -202,8 +202,12 @@ private void showNotification(Bundle bundle, Ejson ejson, String notId) { boolean hasSender = ejson != null && ejson.sender != null; String title = bundle.getString("title"); + String displaySenderName = (ejson != null && ejson.senderName != null && !ejson.senderName.isEmpty()) + ? ejson.senderName + : (hasSender ? ejson.sender.username : title); + bundle.putLong("time", new Date().getTime()); - bundle.putString("username", hasSender ? ejson.sender.username : title); + bundle.putString("username", displaySenderName); bundle.putString("senderId", hasSender ? ejson.sender._id : "1"); String avatarUri = ejson != null ? ejson.getAvatarUri() : null; @@ -291,19 +295,6 @@ private Notification.Builder buildNotification(int notificationId) { // Determine the correct title based on notification type String notificationTitle = title; - if (ejson != null && ejson.type != null) { - if ("p".equals(ejson.type) || "c".equals(ejson.type)) { - // For groups/channels, use room name if available, otherwise fall back to title - notificationTitle = (ejson.name != null && !ejson.name.isEmpty()) ? ejson.name : title; - } else if ("d".equals(ejson.type)) { - // For direct messages, use title (sender name from server) - notificationTitle = title; - } else if ("l".equals(ejson.type)) { - // For omnichannel, use sender name if available, otherwise fall back to title - notificationTitle = (ejson.sender != null && ejson.sender.name != null && !ejson.sender.name.isEmpty()) - ? ejson.sender.name : title; - } - } if (ENABLE_VERBOSE_LOGS) { Log.d(TAG, "[buildNotification] notId=" + notId); @@ -465,19 +456,6 @@ private void notificationStyle(Notification.Builder notification, int notId, Bun // Determine the correct conversation title based on notification type Ejson bundleEjson = safeFromJson(bundle.getString("ejson", "{}"), Ejson.class); String conversationTitle = title; - if (bundleEjson != null && bundleEjson.type != null) { - if ("p".equals(bundleEjson.type) || "c".equals(bundleEjson.type)) { - // For groups/channels, use room name if available, otherwise fall back to title - conversationTitle = (bundleEjson.name != null && !bundleEjson.name.isEmpty()) ? bundleEjson.name : title; - } else if ("d".equals(bundleEjson.type)) { - // For direct messages, use title (sender name from server) - conversationTitle = title; - } else if ("l".equals(bundleEjson.type)) { - // For omnichannel, use sender name if available, otherwise fall back to title - conversationTitle = (bundleEjson.sender != null && bundleEjson.sender.name != null && !bundleEjson.sender.name.isEmpty()) - ? bundleEjson.sender.name : title; - } - } messageStyle.setConversationTitle(conversationTitle); if (bundles != null) { @@ -489,15 +467,17 @@ private void notificationStyle(Notification.Builder notification, int notId, Bun Ejson ejson = safeFromJson(data.getString("ejson", "{}"), Ejson.class); String m = extractMessage(message, ejson); + String displaySenderName = (ejson != null && ejson.senderName != null && !ejson.senderName.isEmpty()) + ? ejson.senderName + : (ejson != null && ejson.sender != null ? ejson.sender.username : title); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - String senderName = ejson != null ? ejson.senderName : "Unknown"; - messageStyle.addMessage(m, timestamp, senderName); + messageStyle.addMessage(m, timestamp, displaySenderName); } else { Bitmap avatar = getAvatar(avatarUri); - String senderName = ejson != null ? ejson.senderName : "Unknown"; Person.Builder senderBuilder = new Person.Builder() .setKey(senderId) - .setName(senderName); + .setName(displaySenderName); if (avatar != null) { senderBuilder.setIcon(Icon.createWithBitmap(avatar)); diff --git a/android/app/src/main/java/chat/rocket/reactnative/notification/Ejson.java b/android/app/src/main/java/chat/rocket/reactnative/notification/Ejson.java index c7c35a1d4c..e9998ff823 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/notification/Ejson.java +++ b/android/app/src/main/java/chat/rocket/reactnative/notification/Ejson.java @@ -67,20 +67,18 @@ private String buildAvatarUri(String avatarPath, String errorContext) { String userToken = token(); String uid = userId(); - if (userToken.isEmpty() || uid.isEmpty()) { - Log.w(TAG, "Cannot generate " + errorContext + " avatar URI: missing auth credentials"); - return null; + String finalUri = server + avatarPath + "?format=png&size=100"; + if (!userToken.isEmpty() && !uid.isEmpty()) { + finalUri += "&rc_token=" + userToken + "&rc_uid=" + uid; } - return server + avatarPath + "?format=png&size=100&rc_token=" + userToken + "&rc_uid=" + uid; + return finalUri; } public String getAvatarUri() { String avatarPath; - // For DMs, show sender's avatar; for groups/channels, show room avatar if ("d".equals(type)) { - // Direct message: use sender's avatar if (sender == null || sender.username == null || sender.username.isEmpty()) { Log.w(TAG, "Cannot generate avatar URI: sender or username is null"); return null; @@ -92,7 +90,6 @@ public String getAvatarUri() { return null; } } else { - // Group/Channel/Livechat: use room avatar if (rid == null || rid.isEmpty()) { Log.w(TAG, "Cannot generate avatar URI: rid is null for non-DM"); return null; diff --git a/android/app/src/main/java/chat/rocket/reactnative/notification/VideoConfNotification.kt b/android/app/src/main/java/chat/rocket/reactnative/notification/VideoConfNotification.kt index a2f7bb5a30..92846f46ac 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/notification/VideoConfNotification.kt +++ b/android/app/src/main/java/chat/rocket/reactnative/notification/VideoConfNotification.kt @@ -91,11 +91,11 @@ class VideoConfNotification(private val context: Context) { if (ejson.caller != null) { callerId = ejson.caller._id ?: "" - callerName = ejson.caller.name ?: "Unknown" + callerName = ejson.senderName ?: ejson.caller.name ?: "Unknown" } else if (ejson.sender != null) { // Fallback to sender if caller is not present callerId = ejson.sender._id ?: "" - callerName = ejson.sender.name ?: ejson.senderName ?: "Unknown" + callerName = ejson.senderName ?: ejson.sender.name ?: "Unknown" } else { callerId = "" callerName = "Unknown" diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift index 00d8ac9549..eb3f5b37e7 100644 --- a/ios/NotificationService/NotificationService.swift +++ b/ios/NotificationService/NotificationService.swift @@ -14,22 +14,21 @@ class NotificationService: UNNotificationServiceExtension { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - guard let bestAttemptContent = bestAttemptContent, - let ejsonString = bestAttemptContent.userInfo["ejson"] as? String, - let ejson = ejsonString.data(using: .utf8), - let payload = try? JSONDecoder().decode(Payload.self, from: ejson) else { - contentHandler(request.content) - return - } - - rocketchat = RocketChat(server: payload.host.removeTrailingSlash()) - - if payload.notificationType == .videoconf { - processVideoConf(payload: payload) - } else if payload.notificationType == .messageIdOnly { - fetchMessageContent(payload: payload) + if let bestAttemptContent = bestAttemptContent, + let ejsonString = bestAttemptContent.userInfo["ejson"] as? String, + let ejson = ejsonString.data(using: .utf8), + let payload = try? JSONDecoder().decode(Payload.self, from: ejson) { + rocketchat = RocketChat(server: payload.host.removeTrailingSlash()) + + if payload.notificationType == .videoconf { + processVideoConf(payload: payload) + } else if payload.notificationType == .messageIdOnly { + fetchMessageContent(payload: payload) + } else { + processPayload(payload: payload) + } } else { - processPayload(payload: payload) + contentHandler(request.content) } } @@ -49,7 +48,7 @@ class NotificationService: UNNotificationServiceExtension { } // 1. Setup Basic Content - let callerName = payload.caller?.name ?? "Unknown" + let callerName = payload.senderName ?? payload.caller?.name ?? "Unknown" bestAttemptContent.title = NSLocalizedString("Video Call", comment: "") bestAttemptContent.body = String(format: NSLocalizedString("Incoming call from %@", comment: ""), callerName) bestAttemptContent.categoryIdentifier = "VIDEOCONF" @@ -77,14 +76,15 @@ class NotificationService: UNNotificationServiceExtension { guard let bestAttemptContent = bestAttemptContent else { return } // 1. Setup Basic Content (Title/Body) - let senderName = payload.sender?.name ?? payload.senderName ?? "Unknown" + let senderName = payload.senderName ?? payload.sender?.name ?? "Unknown" let senderUsername = payload.sender?.username ?? payload.senderName ?? "" - bestAttemptContent.title = senderName + if bestAttemptContent.title.isEmpty { + bestAttemptContent.title = senderName + } if let roomType = payload.type { if roomType == .group || roomType == .channel { - bestAttemptContent.title = payload.name ?? senderName // Strip sender prefix if present if let body = bestAttemptContent.body as? String { let prefix = "\(senderUsername): " @@ -98,8 +98,6 @@ class NotificationService: UNNotificationServiceExtension { } } } - } else if roomType == .livechat { - bestAttemptContent.title = payload.sender?.name ?? senderName } } @@ -122,7 +120,7 @@ class NotificationService: UNNotificationServiceExtension { avatarData: avatarData, conversationId: payload.rid ?? "", isGroup: isGroup, - groupName: payload.name + groupName: bestAttemptContent.title ) self.contentHandler?(self.finalContent ?? bestAttemptContent) @@ -214,7 +212,8 @@ class NotificationService: UNNotificationServiceExtension { if let messageId = payload.messageId { self.rocketchat?.getPushWithId(messageId) { notification in if let notification = notification { - // Set body first, processPayload will strip sender prefix for groups/channels + // Set title and body first, processPayload will strip sender prefix for groups/channels + self.bestAttemptContent?.title = notification.title self.bestAttemptContent?.body = notification.text // Update ejson with full payload from server for correct navigation