-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathReplyNotification.swift
More file actions
135 lines (118 loc) · 4.79 KB
/
Copy pathReplyNotification.swift
File metadata and controls
135 lines (118 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//
// ReplyNotification.swift
// RocketChatRN
//
// Created by Djorkaeff Alexandre Vilela Pereira on 9/17/20.
// Copyright © 2020 Rocket.Chat. All rights reserved.
//
import Foundation
import UserNotifications
// Handles direct reply from iOS notifications.
// Intercepts REPLY_ACTION responses and sends messages natively,
// while forwarding all other notification events to expo-notifications.
@objc(ReplyNotification)
class ReplyNotification: NSObject, UNUserNotificationCenterDelegate {
private static var shared: ReplyNotification?
private weak var originalDelegate: UNUserNotificationCenterDelegate?
@objc
public static func configure() {
let instance = ReplyNotification()
shared = instance
// Store the original delegate (expo-notifications) and set ourselves as the delegate
let center = UNUserNotificationCenter.current()
instance.originalDelegate = center.delegate
center.delegate = instance
}
// MARK: - UNUserNotificationCenterDelegate
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
// Handle REPLY_ACTION natively
if response.actionIdentifier == "REPLY_ACTION" {
handleReplyAction(response: response, completionHandler: completionHandler)
return
}
// Forward to original delegate (expo-notifications)
if let originalDelegate = originalDelegate {
originalDelegate.userNotificationCenter?(center, didReceive: response, withCompletionHandler: completionHandler)
} else {
completionHandler()
}
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
// Forward to original delegate (expo-notifications)
if let originalDelegate = originalDelegate {
originalDelegate.userNotificationCenter?(center, willPresent: notification, withCompletionHandler: completionHandler)
} else {
completionHandler([])
}
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
openSettingsFor notification: UNNotification?
) {
// Forward to original delegate (expo-notifications)
if let originalDelegate = originalDelegate {
if #available(iOS 12.0, *) {
originalDelegate.userNotificationCenter?(center, openSettingsFor: notification)
}
}
}
// MARK: - Reply Handling
private func handleReplyAction(response: UNNotificationResponse, completionHandler: @escaping () -> Void) {
guard let textResponse = response as? UNTextInputNotificationResponse else {
completionHandler()
return
}
let userInfo = response.notification.request.content.userInfo
guard let ejsonString = userInfo["ejson"] as? String,
let ejsonData = ejsonString.data(using: .utf8),
let payload = try? JSONDecoder().decode(Payload.self, from: ejsonData),
let rid = payload.rid else {
// Show failure notification to user
let content = UNMutableNotificationContent()
content.body = "Failed to send reply. Invalid notification data."
let request = UNNotificationRequest(identifier: "replyPayloadFailure", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
completionHandler()
return
}
let message = textResponse.userText
let rocketchat = RocketChat(server: payload.host.removeTrailingSlash())
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
backgroundTask = UIApplication.shared.beginBackgroundTask {
// Expiration handler - called if system needs to reclaim resources
if backgroundTask != .invalid {
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
completionHandler()
}
rocketchat.sendMessage(rid: rid, message: message, threadIdentifier: payload.tmid) { response in
// Ensure we're on the main thread for UI operations
DispatchQueue.main.async {
defer {
if backgroundTask != .invalid {
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
completionHandler()
}
guard let response = response, response.success else {
// Show failure notification
let content = UNMutableNotificationContent()
content.body = "Failed to send reply."
let request = UNNotificationRequest(identifier: "replyFailure", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
return
}
}
}
}
}