Фикс: имя файла в пересланных сообщениях, потеря фоток/файлов при пересылке forwarded-сообщений, Фоллбэк при unwrap forwarded-сообщения, защита БД от перезаписи синком
This commit is contained in:
@@ -4,11 +4,22 @@ import UserNotifications
|
||||
/// is terminated. Intercepts push notifications with `mutable-content: 1` and:
|
||||
/// 1. Adds `.default` sound for vibration (server payload has no sound)
|
||||
/// 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
|
||||
final class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
private static let appGroupID = "group.com.rosetta.dev"
|
||||
private static let badgeKey = "app_badge_count"
|
||||
|
||||
/// Android parity: multiple key names for sender public key extraction.
|
||||
private static let senderKeyNames = [
|
||||
"sender_public_key", "from_public_key", "fromPublicKey",
|
||||
"public_key", "publicKey"
|
||||
]
|
||||
private static let senderNameKeyNames = [
|
||||
"sender_name", "from_title", "sender", "title", "name"
|
||||
]
|
||||
|
||||
private var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
private var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
@@ -33,9 +44,34 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
let newBadge = current + 1
|
||||
shared.set(newBadge, forKey: Self.badgeKey)
|
||||
content.badge = NSNumber(value: newBadge)
|
||||
|
||||
// 4. Filter muted chats.
|
||||
let senderKey = Self.extractSenderKey(from: content.userInfo)
|
||||
let mutedKeys = shared.stringArray(forKey: "muted_chats_keys") ?? []
|
||||
if !senderKey.isEmpty, mutedKeys.contains(senderKey) {
|
||||
// Muted: deliver silently (no sound, no alert)
|
||||
content.sound = nil
|
||||
content.title = ""
|
||||
content.body = ""
|
||||
contentHandler(content)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Ensure notification category for CarPlay parity.
|
||||
// 3. Normalize sender_public_key in userInfo for tap navigation.
|
||||
// Server may send under different key names — normalize to "sender_public_key".
|
||||
let senderKey = Self.extractSenderKey(from: content.userInfo)
|
||||
if !senderKey.isEmpty {
|
||||
var updatedInfo = content.userInfo
|
||||
updatedInfo["sender_public_key"] = senderKey
|
||||
// Also normalize sender_name
|
||||
if let name = Self.firstNonBlank(content.userInfo, keys: Self.senderNameKeyNames) {
|
||||
updatedInfo["sender_name"] = name
|
||||
}
|
||||
content.userInfo = updatedInfo
|
||||
}
|
||||
|
||||
// 5. Ensure notification category for CarPlay parity.
|
||||
if content.categoryIdentifier.isEmpty {
|
||||
content.categoryIdentifier = "message"
|
||||
}
|
||||
@@ -43,12 +79,26 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
contentHandler(content)
|
||||
}
|
||||
|
||||
/// Called if the extension takes too long (30s limit).
|
||||
/// Deliver the best attempt content with at least the sound set.
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
if let handler = contentHandler, let content = bestAttemptContent {
|
||||
content.sound = .default
|
||||
handler(content)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Android parity: extract sender key from multiple possible key names.
|
||||
private static func extractSenderKey(from userInfo: [AnyHashable: Any]) -> String {
|
||||
firstNonBlank(userInfo, keys: senderKeyNames) ?? ""
|
||||
}
|
||||
|
||||
private static func firstNonBlank(_ dict: [AnyHashable: Any], keys: [String]) -> String? {
|
||||
for key in keys {
|
||||
if let value = dict[key] as? String, !value.trimmingCharacters(in: .whitespaces).isEmpty {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user