Уведомления CarPlay, панель вложений с Lottie, фикс reply preview, плавная анимация клавиатуры, стабильность WebSocket
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import UserNotifications
|
||||
import Intents
|
||||
|
||||
/// Notification Service Extension — runs as a separate process even when the main app
|
||||
/// is terminated. Intercepts push notifications with `mutable-content: 1` and:
|
||||
@@ -6,6 +7,7 @@ import UserNotifications
|
||||
/// 2. Increments the app icon badge from shared App Group storage
|
||||
/// 3. Normalizes sender_public_key in userInfo (Android parity: multi-key fallback)
|
||||
/// 4. Filters muted chats
|
||||
/// 5. Creates Communication Notification via INSendMessageIntent (CarPlay + Focus parity)
|
||||
final class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
private static let appGroupID = "group.com.rosetta.dev"
|
||||
@@ -76,7 +78,18 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
content.categoryIdentifier = "message"
|
||||
}
|
||||
|
||||
contentHandler(content)
|
||||
// 6. Create Communication Notification via INSendMessageIntent.
|
||||
// This makes the notification appear on CarPlay and work with Focus filters.
|
||||
// Apple requires INSendMessageIntent for messaging notifications on CarPlay (iOS 15+).
|
||||
let senderName = Self.firstNonBlank(content.userInfo, keys: Self.senderNameKeyNames)
|
||||
?? content.title
|
||||
let finalContent = Self.makeCommunicationNotification(
|
||||
content: content,
|
||||
senderName: senderName,
|
||||
senderKey: senderKey
|
||||
)
|
||||
|
||||
contentHandler(finalContent)
|
||||
}
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
@@ -86,6 +99,56 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Communication Notification (CarPlay + Focus)
|
||||
|
||||
/// Wraps the notification content with an INSendMessageIntent so iOS treats it
|
||||
/// as a Communication Notification. This enables:
|
||||
/// - Display on CarPlay
|
||||
/// - Proper grouping in Focus modes
|
||||
/// - Sender name/avatar in notification UI
|
||||
private static func makeCommunicationNotification(
|
||||
content: UNMutableNotificationContent,
|
||||
senderName: String,
|
||||
senderKey: String
|
||||
) -> UNNotificationContent {
|
||||
let handle = INPersonHandle(value: senderKey, type: .unknown)
|
||||
let sender = INPerson(
|
||||
personHandle: handle,
|
||||
nameComponents: nil,
|
||||
displayName: senderName.isEmpty ? "Rosetta" : senderName,
|
||||
image: nil,
|
||||
contactIdentifier: nil,
|
||||
customIdentifier: senderKey
|
||||
)
|
||||
|
||||
let intent = INSendMessageIntent(
|
||||
recipients: nil,
|
||||
outgoingMessageType: .outgoingMessageText,
|
||||
content: content.body,
|
||||
speakableGroupName: nil,
|
||||
conversationIdentifier: senderKey,
|
||||
serviceName: "Rosetta",
|
||||
sender: sender,
|
||||
attachments: nil
|
||||
)
|
||||
|
||||
// Donate the intent so Siri can learn communication patterns.
|
||||
let interaction = INInteraction(intent: intent, response: nil)
|
||||
interaction.direction = .incoming
|
||||
interaction.donate(completion: nil)
|
||||
|
||||
// Update the notification content with the intent.
|
||||
// This returns a new content object that iOS recognizes as a Communication Notification.
|
||||
do {
|
||||
let updatedContent = try content.updating(from: intent)
|
||||
return updatedContent
|
||||
} catch {
|
||||
// If updating fails, return original content — notification still works,
|
||||
// just without CarPlay / Communication Notification features.
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Android parity: extract sender key from multiple possible key names.
|
||||
|
||||
Reference in New Issue
Block a user