SimpleX-Chat/apps/ios/SimpleXChat/Notifications.swift
spaced4ndy 47adbe2813
ui: fix strings, update translations (#5718)
* ios: fix strings

* update translations

* update report ru translations

* remove unnecessary localizations

* update ru translations

* update android translations

* import translations

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-03-07 12:50:44 +00:00

213 lines
9.1 KiB
Swift

//
// Notifications.swift
// SimpleX
//
// Created by Evgeny on 28/04/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import Foundation
import UserNotifications
import SwiftUI
public let ntfCategoryContactRequest = "NTF_CAT_CONTACT_REQUEST"
public let ntfCategoryContactConnected = "NTF_CAT_CONTACT_CONNECTED"
public let ntfCategoryMessageReceived = "NTF_CAT_MESSAGE_RECEIVED"
public let ntfCategoryCallInvitation = "NTF_CAT_CALL_INVITATION"
public let ntfCategoryConnectionEvent = "NTF_CAT_CONNECTION_EVENT"
public let ntfCategoryManyEvents = "NTF_CAT_MANY_EVENTS"
public let ntfCategoryCheckMessage = "NTF_CAT_CHECK_MESSAGE"
public let appNotificationId = "chat.simplex.app.notification"
let contactHidden = NSLocalizedString("Contact hidden:", comment: "notification")
public func createContactRequestNtf(_ user: any UserLike, _ contactRequest: UserContactRequest, _ badgeCount: Int) -> UNMutableNotificationContent {
let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
return createNotification(
categoryIdentifier: ntfCategoryContactRequest,
title: String.localizedStringWithFormat(
NSLocalizedString("%@ wants to connect!", comment: "notification title"),
hideContent ? NSLocalizedString("Somebody", comment: "notification title") : contactRequest.displayName
),
body: String.localizedStringWithFormat(
NSLocalizedString("Accept contact request from %@?", comment: "notification body"),
hideContent ? NSLocalizedString("this contact", comment: "notification title") : contactRequest.chatViewName
),
targetContentIdentifier: nil,
userInfo: ["chatId": contactRequest.id, "contactRequestId": contactRequest.apiId, "userId": user.userId],
badgeCount: badgeCount
)
}
public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact, _ badgeCount: Int) -> UNMutableNotificationContent {
let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
return createNotification(
categoryIdentifier: ntfCategoryContactConnected,
title: String.localizedStringWithFormat(
NSLocalizedString("%@ is connected!", comment: "notification title"),
hideContent ? NSLocalizedString("A new contact", comment: "notification title") : contact.displayName
),
body: String.localizedStringWithFormat(
NSLocalizedString("You can now chat with %@", comment: "notification body"),
hideContent ? NSLocalizedString("this contact", comment: "notification title") : contact.chatViewName
),
targetContentIdentifier: contact.id,
userInfo: ["userId": user.userId],
// userInfo: ["chatId": contact.id, "contactId": contact.apiId]
badgeCount: badgeCount
)
}
public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem, _ badgeCount: Int) -> UNMutableNotificationContent {
let previewMode = ntfPreviewModeGroupDefault.get()
var title: String
if case let .group(groupInfo) = cInfo, case let .groupRcv(groupMember) = cItem.chatDir {
title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: previewMode == .hidden)
} else {
title = previewMode == .hidden ? contactHidden : "\(cInfo.chatViewName):"
}
return createNotification(
categoryIdentifier: ntfCategoryMessageReceived,
title: title,
body: previewMode == .message ? hideSecrets(cItem) : NSLocalizedString("new message", comment: "notification"),
targetContentIdentifier: cInfo.id,
userInfo: ["userId": user.userId],
// userInfo: ["chatId": cInfo.id, "chatItemId": cItem.id]
badgeCount: badgeCount
)
}
public func createCallInvitationNtf(_ invitation: RcvCallInvitation, _ badgeCount: Int) -> UNMutableNotificationContent {
let text = invitation.callType.media == .video
? NSLocalizedString("Incoming video call", comment: "notification")
: NSLocalizedString("Incoming audio call", comment: "notification")
let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
return createNotification(
categoryIdentifier: ntfCategoryCallInvitation,
title: hideContent ? contactHidden : "\(invitation.contact.chatViewName):",
body: text,
targetContentIdentifier: nil,
userInfo: ["chatId": invitation.contact.id, "userId": invitation.user.userId],
badgeCount: badgeCount
)
}
public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntity, _ badgeCount: Int) -> UNMutableNotificationContent {
let hideContent = ntfPreviewModeGroupDefault.get() == .hidden
var title: String
var body: String? = nil
var targetContentIdentifier: String? = nil
switch connEntity {
case let .rcvDirectMsgConnection(_, contact):
if let contact = contact {
title = hideContent ? contactHidden : "\(contact.chatViewName):"
targetContentIdentifier = contact.id
} else {
title = NSLocalizedString("New contact:", comment: "notification")
}
body = NSLocalizedString("message received", comment: "notification")
case let .rcvGroupMsgConnection(_, groupInfo, groupMember):
title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: hideContent)
body = NSLocalizedString("message received", comment: "notification")
targetContentIdentifier = groupInfo.id
case .sndFileConnection:
title = NSLocalizedString("Sent file event", comment: "notification")
case .rcvFileConnection:
title = NSLocalizedString("Received file event", comment: "notification")
case .userContactConnection:
title = NSLocalizedString("New contact request", comment: "notification")
}
return createNotification(
categoryIdentifier: ntfCategoryConnectionEvent,
title: title,
body: body,
targetContentIdentifier: targetContentIdentifier,
userInfo: ["userId": user.userId],
badgeCount: badgeCount
)
}
public func createErrorNtf(_ dbStatus: DBMigrationResult, _ badgeCount: Int) -> UNMutableNotificationContent {
var title: String
switch dbStatus {
case .errorNotADatabase:
title = NSLocalizedString("Encrypted message: no passphrase", comment: "notification")
case .errorMigration:
title = NSLocalizedString("Encrypted message: database migration error", comment: "notification")
case .errorSQL:
title = NSLocalizedString("Encrypted message: database error", comment: "notification")
case .errorKeychain:
title = NSLocalizedString("Encrypted message: keychain error", comment: "notification")
case .unknown:
title = NSLocalizedString("Encrypted message: unexpected error", comment: "notification")
case .invalidConfirmation:
title = NSLocalizedString("Encrypted message or another event", comment: "notification")
case .ok:
title = NSLocalizedString("Encrypted message or another event", comment: "notification")
}
return createNotification(
categoryIdentifier: ntfCategoryConnectionEvent,
title: title,
badgeCount: badgeCount
)
}
public func createAppStoppedNtf(_ badgeCount: Int) -> UNMutableNotificationContent {
return createNotification(
categoryIdentifier: ntfCategoryConnectionEvent,
title: NSLocalizedString("Encrypted message: app is stopped", comment: "notification"),
badgeCount: badgeCount
)
}
private func groupMsgNtfTitle(_ groupInfo: GroupInfo, _ groupMember: GroupMember, hideContent: Bool) -> String {
hideContent
? NSLocalizedString("Group message:", comment: "notification")
: "#\(groupInfo.displayName) \(groupMember.chatViewName):"
}
public func createNotification(
categoryIdentifier: String,
title: String,
subtitle: String? = nil,
body: String? = nil,
targetContentIdentifier: String? = nil,
userInfo: [AnyHashable : Any] = [:],
badgeCount: Int
) -> UNMutableNotificationContent {
let content = UNMutableNotificationContent()
content.categoryIdentifier = categoryIdentifier
content.title = title
if let s = subtitle { content.subtitle = s }
if let s = body { content.body = s }
content.targetContentIdentifier = targetContentIdentifier
content.userInfo = userInfo
// TODO move logic of adding sound here, so it applies to background notifications too
content.sound = .default
content.badge = badgeCount as NSNumber
// content.interruptionLevel = .active
// content.relevanceScore = 0.5 // 0-1
return content
}
func hideSecrets(_ cItem: ChatItem) -> String {
if let md = cItem.formattedText {
var res = ""
for ft in md {
if case .secret = ft.format {
res = res + "..."
} else {
res = res + ft.text
}
}
return res
} else {
let mc = cItem.content.msgContent
if case let .report(text, reason) = mc {
return String.localizedStringWithFormat(NSLocalizedString("Report: %@", comment: "report in notification"), text.isEmpty ? reason.text : text)
} else {
return cItem.text
}
}
}