ios: notification actions for calls and contact requests with NSE (#829)
* ios: notification actions for calls and contact requests with NSE * update contact request if already in the list
This commit is contained in:
committed by
GitHub
parent
e538a9e057
commit
8469f921b7
@@ -36,6 +36,9 @@ final class ChatModel: ObservableObject {
|
||||
@Published var tokenStatus: NtfTknStatus?
|
||||
@Published var notificationMode = NotificationsMode.off
|
||||
@Published var notificationPreview: NotificationPreviewMode? = ntfPreviewModeGroupDefault.get()
|
||||
// pending notification actions
|
||||
@Published var ntfContactRequest: ChatId?
|
||||
@Published var ntfCallInvitationAction: (ChatId, NtfCallAction)?
|
||||
// current WebRTC call
|
||||
@Published var callInvitations: Dictionary<ChatId, RcvCallInvitation> = [:]
|
||||
@Published var activeCall: Call?
|
||||
|
||||
@@ -17,6 +17,11 @@ let ntfActionRejectCall = "NTF_ACT_REJECT_CALL"
|
||||
|
||||
private let ntfTimeInterval: TimeInterval = 1
|
||||
|
||||
enum NtfCallAction {
|
||||
case accept
|
||||
case reject
|
||||
}
|
||||
|
||||
class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
static let shared = NtfManager()
|
||||
|
||||
@@ -32,18 +37,19 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
let content = response.notification.request.content
|
||||
let chatModel = ChatModel.shared
|
||||
let action = response.actionIdentifier
|
||||
logger.debug("NtfManager.userNotificationCenter: didReceive: action \(action), categoryIdentifier \(content.categoryIdentifier)")
|
||||
if content.categoryIdentifier == ntfCategoryContactRequest && action == ntfActionAcceptContact,
|
||||
let chatId = content.userInfo["chatId"] as? String,
|
||||
case let .contactRequest(contactRequest) = chatModel.getChat(chatId)?.chatInfo {
|
||||
Task { await acceptContactRequest(contactRequest) }
|
||||
} else if content.categoryIdentifier == ntfCategoryCallInvitation && (action == ntfActionAcceptCall || action == ntfActionRejectCall),
|
||||
let chatId = content.userInfo["chatId"] as? String,
|
||||
let invitation = chatModel.callInvitations.removeValue(forKey: chatId) {
|
||||
let cc = CallController.shared
|
||||
if action == ntfActionAcceptCall {
|
||||
cc.answerCall(invitation: invitation)
|
||||
let chatId = content.userInfo["chatId"] as? String {
|
||||
if case let .contactRequest(contactRequest) = chatModel.getChat(chatId)?.chatInfo {
|
||||
Task { await acceptContactRequest(contactRequest) }
|
||||
} else {
|
||||
cc.endCall(invitation: invitation)
|
||||
chatModel.ntfContactRequest = chatId
|
||||
}
|
||||
} else if let (chatId, ntfAction) = ntfCallAction(content, action) {
|
||||
if let invitation = chatModel.callInvitations.removeValue(forKey: chatId) {
|
||||
CallController.shared.callAction(invitation: invitation, action: ntfAction)
|
||||
} else {
|
||||
chatModel.ntfCallInvitationAction = (chatId, ntfAction)
|
||||
}
|
||||
} else {
|
||||
chatModel.chatId = content.targetContentIdentifier
|
||||
@@ -51,6 +57,19 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
handler()
|
||||
}
|
||||
|
||||
private func ntfCallAction(_ content: UNNotificationContent, _ action: String) -> (ChatId, NtfCallAction)? {
|
||||
if content.categoryIdentifier == ntfCategoryCallInvitation,
|
||||
let chatId = content.userInfo["chatId"] as? String {
|
||||
if action == ntfActionAcceptCall {
|
||||
return (chatId, .accept)
|
||||
} else if action == ntfActionRejectCall {
|
||||
return (chatId, .reject)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Handle notification when the app is in foreground
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification,
|
||||
@@ -103,7 +122,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
identifier: ntfCategoryContactRequest,
|
||||
actions: [UNNotificationAction(
|
||||
identifier: ntfActionAcceptContact,
|
||||
title: NSLocalizedString("Accept", comment: "accept contact request via notification")
|
||||
title: NSLocalizedString("Accept", comment: "accept contact request via notification"),
|
||||
options: .foreground
|
||||
)],
|
||||
intentIdentifiers: [],
|
||||
hiddenPreviewsBodyPlaceholder: NSLocalizedString("New contact request", comment: "notification")
|
||||
|
||||
@@ -669,11 +669,16 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
m.updateContact(contact)
|
||||
m.removeChat(contact.activeConn.id)
|
||||
case let .receivedContactRequest(contactRequest):
|
||||
m.addChat(Chat(
|
||||
chatInfo: ChatInfo.contactRequest(contactRequest: contactRequest),
|
||||
chatItems: []
|
||||
))
|
||||
NtfManager.shared.notifyContactRequest(contactRequest)
|
||||
let cInfo = ChatInfo.contactRequest(contactRequest: contactRequest)
|
||||
if m.hasChat(contactRequest.id) {
|
||||
m.updateChatInfo(cInfo)
|
||||
} else {
|
||||
m.addChat(Chat(
|
||||
chatInfo: cInfo,
|
||||
chatItems: []
|
||||
))
|
||||
NtfManager.shared.notifyContactRequest(contactRequest)
|
||||
}
|
||||
case let .contactUpdated(toContact):
|
||||
let cInfo = ChatInfo.direct(contact: toContact)
|
||||
if m.hasChat(toContact.id) {
|
||||
@@ -850,8 +855,12 @@ func refreshCallInvitations() throws {
|
||||
let m = ChatModel.shared
|
||||
let callInvitations = try apiGetCallInvitations()
|
||||
m.callInvitations = callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) { result, inv in result[inv.contact.id] = inv }
|
||||
if let inv = callInvitations.last {
|
||||
activateCall(inv)
|
||||
if let (chatId, ntfAction) = m.ntfCallInvitationAction,
|
||||
let invitation = m.callInvitations.removeValue(forKey: chatId) {
|
||||
m.ntfCallInvitationAction = nil
|
||||
CallController.shared.callAction(invitation: invitation, action: ntfAction)
|
||||
} else if let invitation = callInvitations.last {
|
||||
activateCall(invitation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,12 @@ struct SimpleXApp: App {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let chatId = chatModel.ntfContactRequest {
|
||||
chatModel.ntfContactRequest = nil
|
||||
if case let .contactRequest(contactRequest) = chatModel.getChat(chatId)?.chatInfo {
|
||||
Task { await acceptContactRequest(contactRequest) }
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("apiGetChats: cannot update chats \(responseError(error))")
|
||||
}
|
||||
@@ -128,8 +134,7 @@ struct SimpleXApp: App {
|
||||
private func updateCallInvitations() {
|
||||
do {
|
||||
try refreshCallInvitations()
|
||||
}
|
||||
catch let error {
|
||||
} catch let error {
|
||||
logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,13 @@ class CallController: NSObject, ObservableObject {
|
||||
callManager.endCall(call: call, completed: completed)
|
||||
}
|
||||
|
||||
func callAction(invitation: RcvCallInvitation, action: NtfCallAction) {
|
||||
switch action {
|
||||
case .accept: answerCall(invitation: invitation)
|
||||
case .reject: endCall(invitation: invitation)
|
||||
}
|
||||
}
|
||||
|
||||
// private func requestTransaction(with action: CXAction) {
|
||||
// let t = CXTransaction()
|
||||
// t.addAction(action)
|
||||
|
||||
@@ -39,7 +39,7 @@ actor PendingNtfs {
|
||||
for await ntf in s {
|
||||
nse.setBestAttemptNtf(ntf)
|
||||
rcvCount -= 1
|
||||
if rcvCount == 0 { break }
|
||||
if rcvCount == 0 || ntf.categoryIdentifier == ntfCategoryCallInvitation { break }
|
||||
}
|
||||
logger.debug("PendingNtfs.readStream: exiting")
|
||||
}
|
||||
@@ -204,7 +204,9 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, UNMutableNotification
|
||||
cItem = apiReceiveFile(fileId: file.fileId)?.chatItem ?? cItem
|
||||
}
|
||||
}
|
||||
return (aChatItem.chatId, createMessageReceivedNtf(cInfo, cItem))
|
||||
return cItem.isCall() ? nil : (aChatItem.chatId, createMessageReceivedNtf(cInfo, cItem))
|
||||
case let .callInvitation(invitation):
|
||||
return (invitation.contact.id, createCallInvitationNtf(invitation))
|
||||
default:
|
||||
logger.debug("NotificationService processReceivedMsg ignored event: \(res.responseType)")
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user