react-native-qpush 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +439 -2
  2. package/android/build.gradle +77 -0
  3. package/android/gradle.properties +5 -0
  4. package/android/proguard-rules.pro +5 -0
  5. package/android/src/main/AndroidManifest.xml +29 -0
  6. package/android/src/main/java/com/qpush/reactnative/QPushDeviceInfo.kt +96 -0
  7. package/android/src/main/java/com/qpush/reactnative/QPushEventBus.kt +153 -0
  8. package/android/src/main/java/com/qpush/reactnative/QPushFcm.kt +108 -0
  9. package/android/src/main/java/com/qpush/reactnative/QPushHttp.kt +92 -0
  10. package/android/src/main/java/com/qpush/reactnative/QPushImages.kt +103 -0
  11. package/android/src/main/java/com/qpush/reactnative/QPushLog.kt +39 -0
  12. package/android/src/main/java/com/qpush/reactnative/QPushMessagingService.kt +77 -0
  13. package/android/src/main/java/com/qpush/reactnative/QPushNotificationOpenActivity.kt +121 -0
  14. package/android/src/main/java/com/qpush/reactnative/QPushNotificationRenderer.kt +256 -0
  15. package/android/src/main/java/com/qpush/reactnative/QPushPayload.kt +114 -0
  16. package/android/src/main/java/com/qpush/reactnative/QPushPushModule.kt +368 -0
  17. package/android/src/main/java/com/qpush/reactnative/QPushPushPackage.kt +15 -0
  18. package/android/src/main/java/com/qpush/reactnative/QPushStore.kt +108 -0
  19. package/android/src/main/res/values/qpush_strings.xml +6 -0
  20. package/app.plugin.js +1 -0
  21. package/ios/Extension/QPushNotificationServiceExtension.swift +261 -0
  22. package/ios/Extension/QPushPushExtension.podspec +34 -0
  23. package/ios/QPushPush.m +62 -0
  24. package/ios/QPushPush.swift +186 -0
  25. package/ios/QPushPushBridge.h +5 -0
  26. package/ios/QPushPushCore.swift +457 -0
  27. package/ios/QPushPushHooks.m +237 -0
  28. package/ios/QPushPushSupport.swift +249 -0
  29. package/ios_nse_template/Info.plist +38 -0
  30. package/ios_nse_template/NotificationService.entitlements +10 -0
  31. package/ios_nse_template/NotificationService.swift +44 -0
  32. package/lib/NativeQPushPush.d.ts +44 -0
  33. package/lib/NativeQPushPush.js +4 -0
  34. package/lib/QPush.d.ts +117 -0
  35. package/lib/QPush.js +206 -0
  36. package/lib/core/QPushCore.d.ts +148 -0
  37. package/lib/core/QPushCore.js +1387 -0
  38. package/lib/core/constants.d.ts +37 -0
  39. package/lib/core/constants.js +40 -0
  40. package/lib/core/events.d.ts +11 -0
  41. package/lib/core/events.js +40 -0
  42. package/lib/core/http.d.ts +41 -0
  43. package/lib/core/http.js +123 -0
  44. package/lib/core/logger.d.ts +18 -0
  45. package/lib/core/logger.js +43 -0
  46. package/lib/core/operationQueue.d.ts +117 -0
  47. package/lib/core/operationQueue.js +264 -0
  48. package/lib/core/operations.d.ts +80 -0
  49. package/lib/core/operations.js +88 -0
  50. package/lib/core/payload.d.ts +20 -0
  51. package/lib/core/payload.js +187 -0
  52. package/lib/core/platform.d.ts +72 -0
  53. package/lib/core/platform.js +7 -0
  54. package/lib/core/state.d.ts +51 -0
  55. package/lib/core/state.js +74 -0
  56. package/lib/core/storage.d.ts +34 -0
  57. package/lib/core/storage.js +68 -0
  58. package/lib/core/subscription.d.ts +67 -0
  59. package/lib/core/subscription.js +98 -0
  60. package/lib/core/tags.d.ts +39 -0
  61. package/lib/core/tags.js +123 -0
  62. package/lib/core/url.d.ts +17 -0
  63. package/lib/core/url.js +64 -0
  64. package/lib/core/uuid.d.ts +2 -0
  65. package/lib/core/uuid.js +18 -0
  66. package/lib/index.d.ts +7 -0
  67. package/lib/index.js +15 -0
  68. package/lib/notification.d.ts +67 -0
  69. package/lib/notification.js +73 -0
  70. package/lib/reactNativePlatform.d.ts +3 -0
  71. package/lib/reactNativePlatform.js +164 -0
  72. package/lib/types.d.ts +157 -0
  73. package/lib/types.js +31 -0
  74. package/package.json +70 -4
  75. package/plugin/withQPush.js +330 -0
  76. package/react-native-qpush.podspec +30 -0
  77. package/react-native.config.js +18 -0
  78. package/src/NativeQPushPush.ts +57 -0
  79. package/src/QPush.ts +292 -0
  80. package/src/core/QPushCore.ts +1515 -0
  81. package/src/core/constants.ts +42 -0
  82. package/src/core/events.ts +34 -0
  83. package/src/core/http.ts +146 -0
  84. package/src/core/logger.ts +42 -0
  85. package/src/core/operationQueue.ts +324 -0
  86. package/src/core/operations.ts +166 -0
  87. package/src/core/payload.ts +175 -0
  88. package/src/core/platform.ts +84 -0
  89. package/src/core/state.ts +127 -0
  90. package/src/core/storage.ts +76 -0
  91. package/src/core/subscription.ts +133 -0
  92. package/src/core/tags.ts +116 -0
  93. package/src/core/url.ts +58 -0
  94. package/src/core/uuid.ts +13 -0
  95. package/src/index.ts +23 -0
  96. package/src/notification.ts +113 -0
  97. package/src/reactNativePlatform.ts +172 -0
  98. package/src/types.ts +174 -0
@@ -0,0 +1,261 @@
1
+ import Foundation
2
+ import UserNotifications
3
+
4
+ /// Helper for the Notification Service Extension (NSE) target.
5
+ ///
6
+ /// Shipped as the `QPushPushExtension` pod (no React Native dependency, app
7
+ /// extension safe). For each QPush notification it:
8
+ /// - downloads `qpush.image` and attaches it,
9
+ /// - registers a category `qpush_<notification id>` with the `buttons`,
10
+ /// - calls `PUT /notifications/{id}/report_received`,
11
+ /// then hands the content back to the system.
12
+ ///
13
+ /// The config (`app_id`, `server_url`, `subscription_id`) is read from the App
14
+ /// Group `group.{main app bundle id}.qpush` written by the main app SDK.
15
+ @objc(QPushNotificationServiceExtension)
16
+ public final class QPushNotificationServiceExtension: NSObject {
17
+ private static let lock = NSLock()
18
+ private static var completedRequests: [String] = []
19
+ private static let downloadTimeout: TimeInterval = 20
20
+ private static let reportTimeout: TimeInterval = 10
21
+ private static let maxCategories = 30
22
+ private static let categoryListKey = "qpush_nse_category_ids"
23
+
24
+ /// Call from `didReceive(_:withContentHandler:)`.
25
+ @objc(didReceiveNotificationExtensionRequest:withMutableNotificationContent:withContentHandler:)
26
+ public static func didReceiveNotificationExtensionRequest(
27
+ _ request: UNNotificationRequest,
28
+ with content: UNMutableNotificationContent,
29
+ withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
30
+ ) {
31
+ let userInfo = request.content.userInfo
32
+ guard let qpush = userInfo["qpush"] as? [String: Any],
33
+ let notificationId = qpush["id"] as? String,
34
+ !notificationId.isEmpty
35
+ else {
36
+ // Not a QPush notification: deliver untouched.
37
+ deliver(request.identifier, content, contentHandler)
38
+ return
39
+ }
40
+
41
+ let group = DispatchGroup()
42
+ let resultLock = NSLock()
43
+ var attachment: UNNotificationAttachment?
44
+ var categoryIdentifier: String?
45
+
46
+ group.enter()
47
+ reportReceived(notificationId: notificationId) { group.leave() }
48
+
49
+ if let image = qpush["image"] as? String, !image.isEmpty, let url = URL(string: image) {
50
+ group.enter()
51
+ downloadAttachment(from: url) { downloaded in
52
+ resultLock.lock()
53
+ attachment = downloaded
54
+ resultLock.unlock()
55
+ group.leave()
56
+ }
57
+ }
58
+
59
+ if let buttons = qpush["buttons"] as? [[String: Any]], !buttons.isEmpty {
60
+ group.enter()
61
+ registerCategory(identifier: "qpush_\(notificationId)", buttons: buttons) { identifier in
62
+ resultLock.lock()
63
+ categoryIdentifier = identifier
64
+ resultLock.unlock()
65
+ group.leave()
66
+ }
67
+ }
68
+
69
+ group.notify(queue: .main) {
70
+ resultLock.lock()
71
+ let finalAttachment = attachment
72
+ let finalCategory = categoryIdentifier
73
+ resultLock.unlock()
74
+ QPushNotificationServiceExtension.deliver(request.identifier, content, contentHandler) {
75
+ if let finalAttachment = finalAttachment { content.attachments = [finalAttachment] }
76
+ if let finalCategory = finalCategory { content.categoryIdentifier = finalCategory }
77
+ }
78
+ }
79
+ }
80
+
81
+ /// Call from `serviceExtensionTimeWillExpire()`. Returns `true` when the
82
+ /// caller must still call the content handler (QPush did not deliver yet).
83
+ @discardableResult
84
+ @objc(serviceExtensionTimeWillExpireRequest:withMutableNotificationContent:)
85
+ public static func serviceExtensionTimeWillExpireRequest(
86
+ _ request: UNNotificationRequest,
87
+ with content: UNMutableNotificationContent?
88
+ ) -> Bool {
89
+ return markCompleted(request.identifier)
90
+ }
91
+
92
+ // MARK: - Delivery bookkeeping (the content handler must be called once)
93
+
94
+ private static func markCompleted(_ identifier: String) -> Bool {
95
+ lock.lock()
96
+ defer { lock.unlock() }
97
+ if completedRequests.contains(identifier) { return false }
98
+ completedRequests.append(identifier)
99
+ if completedRequests.count > 50 { completedRequests.removeFirst(completedRequests.count - 50) }
100
+ return true
101
+ }
102
+
103
+ private static func deliver(
104
+ _ identifier: String,
105
+ _ content: UNMutableNotificationContent,
106
+ _ contentHandler: @escaping (UNNotificationContent) -> Void,
107
+ mutate: (() -> Void)? = nil
108
+ ) {
109
+ guard markCompleted(identifier) else { return }
110
+ mutate?()
111
+ contentHandler(content)
112
+ }
113
+
114
+ // MARK: - App Group config
115
+
116
+ /// `QPushAppGroup` in the extension's Info.plist, else `group.{host app bundle id}.qpush`.
117
+ static func appGroupName() -> String {
118
+ if let custom = Bundle.main.object(forInfoDictionaryKey: "QPushAppGroup") as? String, !custom.isEmpty {
119
+ return custom
120
+ }
121
+ return "group.\(hostAppBundleIdentifier()).qpush"
122
+ }
123
+
124
+ /// Bundle id of the containing app (`App.app/PlugIns/X.appex` -> `App.app`).
125
+ static func hostAppBundleIdentifier() -> String {
126
+ let bundleURL = Bundle.main.bundleURL
127
+ if bundleURL.pathExtension == "appex" {
128
+ let appURL = bundleURL.deletingLastPathComponent().deletingLastPathComponent()
129
+ if let identifier = Bundle(url: appURL)?.bundleIdentifier {
130
+ return identifier
131
+ }
132
+ // Fallback: com.example.app.NotificationExtension -> com.example.app
133
+ if let own = Bundle.main.bundleIdentifier, let dot = own.lastIndex(of: ".") {
134
+ return String(own[..<dot])
135
+ }
136
+ }
137
+ return Bundle.main.bundleIdentifier ?? ""
138
+ }
139
+
140
+ private static func sharedDefaults() -> UserDefaults? {
141
+ return UserDefaults(suiteName: appGroupName())
142
+ }
143
+
144
+ // MARK: - Delivery receipt
145
+
146
+ private static func reportReceived(notificationId: String, completion: @escaping () -> Void) {
147
+ guard let defaults = sharedDefaults(),
148
+ let appId = defaults.string(forKey: "app_id"), !appId.isEmpty,
149
+ let serverUrl = defaults.string(forKey: "server_url"), !serverUrl.isEmpty
150
+ else {
151
+ NSLog("[QPush] NSE: app_id/server_url missing in App Group %@ (open the app once after installing)", appGroupName())
152
+ completion()
153
+ return
154
+ }
155
+ var base = serverUrl
156
+ while base.hasSuffix("/") { base.removeLast() }
157
+ var allowed = CharacterSet.urlPathAllowed
158
+ allowed.remove(charactersIn: "/")
159
+ let encodedId = notificationId.addingPercentEncoding(withAllowedCharacters: allowed) ?? notificationId
160
+ guard let url = URL(string: "\(base)/notifications/\(encodedId)/report_received") else {
161
+ completion()
162
+ return
163
+ }
164
+ var body: [String: Any] = ["app_id": appId, "device_type": 0]
165
+ let subscriptionId = defaults.string(forKey: "subscription_id")
166
+ if let subscriptionId = subscriptionId { body["subscription_id"] = subscriptionId }
167
+
168
+ var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: reportTimeout)
169
+ request.httpMethod = "PUT"
170
+ request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])
171
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
172
+ request.setValue("application/json", forHTTPHeaderField: "Accept")
173
+ request.setValue("qpush-react-native/1.0.0", forHTTPHeaderField: "SDK-Version")
174
+ if let subscriptionId = subscriptionId {
175
+ request.setValue(subscriptionId, forHTTPHeaderField: "QPush-Subscription-Id")
176
+ }
177
+ URLSession.shared.dataTask(with: request) { _, _, _ in
178
+ completion()
179
+ }.resume()
180
+ }
181
+
182
+ // MARK: - Image attachment
183
+
184
+ private static func downloadAttachment(from url: URL, completion: @escaping (UNNotificationAttachment?) -> Void) {
185
+ var request = URLRequest(url: url)
186
+ request.timeoutInterval = downloadTimeout
187
+ URLSession.shared.downloadTask(with: request) { location, response, error in
188
+ guard error == nil, let location = location else {
189
+ completion(nil)
190
+ return
191
+ }
192
+ let fileExtension = QPushNotificationServiceExtension.attachmentExtension(url: url, mimeType: response?.mimeType)
193
+ let destination = FileManager.default.temporaryDirectory
194
+ .appendingPathComponent("qpush-\(UUID().uuidString)")
195
+ .appendingPathExtension(fileExtension)
196
+ do {
197
+ // The downloaded file is deleted when this closure returns: move it now.
198
+ try FileManager.default.moveItem(at: location, to: destination)
199
+ let attachment = try UNNotificationAttachment(identifier: "qpush-image", url: destination, options: nil)
200
+ completion(attachment)
201
+ } catch {
202
+ NSLog("[QPush] NSE: could not attach %@: %@", url.absoluteString, error.localizedDescription)
203
+ completion(nil)
204
+ }
205
+ }.resume()
206
+ }
207
+
208
+ private static func attachmentExtension(url: URL, mimeType: String?) -> String {
209
+ let supported = ["jpg", "jpeg", "png", "gif", "heic", "mp4", "m4v", "mov", "mp3", "m4a", "wav", "aiff"]
210
+ let fromPath = url.pathExtension.lowercased()
211
+ if supported.contains(fromPath) { return fromPath }
212
+ switch mimeType?.lowercased() {
213
+ case "image/png": return "png"
214
+ case "image/gif": return "gif"
215
+ case "image/heic": return "heic"
216
+ case "video/mp4": return "mp4"
217
+ case "audio/mpeg": return "mp3"
218
+ default: return "jpg"
219
+ }
220
+ }
221
+
222
+ // MARK: - Action buttons
223
+
224
+ private static func registerCategory(
225
+ identifier: String,
226
+ buttons: [[String: Any]],
227
+ completion: @escaping (String?) -> Void
228
+ ) {
229
+ let actions: [UNNotificationAction] = buttons.compactMap { button in
230
+ guard let id = button["id"] as? String, !id.isEmpty,
231
+ let text = button["text"] as? String, !text.isEmpty
232
+ else { return nil }
233
+ return UNNotificationAction(identifier: id, title: text, options: [.foreground])
234
+ }
235
+ guard !actions.isEmpty else {
236
+ completion(nil)
237
+ return
238
+ }
239
+ let category = UNNotificationCategory(identifier: identifier, actions: actions, intentIdentifiers: [], options: [])
240
+ let center = UNUserNotificationCenter.current()
241
+
242
+ // Remember recent QPush categories so old ones can be pruned.
243
+ let defaults = sharedDefaults()
244
+ var recent = (defaults?.stringArray(forKey: categoryListKey) ?? []).filter { $0 != identifier }
245
+ recent.append(identifier)
246
+ if recent.count > maxCategories { recent.removeFirst(recent.count - maxCategories) }
247
+ defaults?.set(recent, forKey: categoryListKey)
248
+ let keep = Set(recent)
249
+
250
+ center.getNotificationCategories { existing in
251
+ var categories = existing.filter { !$0.identifier.hasPrefix("qpush_") || keep.contains($0.identifier) }
252
+ categories = categories.filter { $0.identifier != identifier }
253
+ categories.insert(category)
254
+ center.setNotificationCategories(categories)
255
+ // Reading back makes sure the new category is registered before delivery.
256
+ center.getNotificationCategories { _ in
257
+ completion(identifier)
258
+ }
259
+ }
260
+ }
261
+ }
@@ -0,0 +1,34 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
4
+
5
+ # Helper for the Notification Service Extension target. No React Native
6
+ # dependency, app-extension safe. Add it to the NSE target in your Podfile:
7
+ #
8
+ # target 'QPushNotificationServiceExtension' do
9
+ # pod 'QPushPushExtension', :path => '../node_modules/react-native-qpush/ios/Extension'
10
+ # end
11
+ #
12
+ # It lives here, not in the package root, so that autolinking (which picks a
13
+ # podspec from the package root) always links react-native-qpush.podspec
14
+ # into the app target.
15
+ Pod::Spec.new do |s|
16
+ s.name = "QPushPushExtension"
17
+ s.version = package["version"]
18
+ s.summary = "Notification Service Extension helper for react-native-qpush"
19
+ s.homepage = package["homepage"]
20
+ s.license = package["license"]
21
+ s.authors = package["author"]
22
+
23
+ s.platforms = { :ios => "13.0" }
24
+ s.source = { :git => package["repository"]["url"], :tag => "v#{s.version}" }
25
+
26
+ s.source_files = "*.swift"
27
+ s.swift_version = "5.0"
28
+ s.frameworks = "UserNotifications"
29
+
30
+ s.pod_target_xcconfig = {
31
+ "APPLICATION_EXTENSION_API_ONLY" => "YES",
32
+ "DEFINES_MODULE" => "YES",
33
+ }
34
+ end
@@ -0,0 +1,62 @@
1
+ // Objective-C bridge exposing the Swift `QPushPush` module to React Native.
2
+ // Every selector here must match the `@objc(...)` name in QPushPush.swift.
3
+
4
+ #import <React/RCTBridgeModule.h>
5
+ #import <React/RCTEventEmitter.h>
6
+
7
+ @interface RCT_EXTERN_MODULE(QPushPush, RCTEventEmitter)
8
+
9
+ // Storage
10
+ RCT_EXTERN_METHOD(getItem:(NSString *)key
11
+ resolver:(RCTPromiseResolveBlock)resolve
12
+ rejecter:(RCTPromiseRejectBlock)reject)
13
+ RCT_EXTERN_METHOD(setItem:(NSString *)key
14
+ value:(NSString *)value
15
+ resolver:(RCTPromiseResolveBlock)resolve
16
+ rejecter:(RCTPromiseRejectBlock)reject)
17
+ RCT_EXTERN_METHOD(removeItem:(NSString *)key
18
+ resolver:(RCTPromiseResolveBlock)resolve
19
+ rejecter:(RCTPromiseRejectBlock)reject)
20
+
21
+ // Device & registration
22
+ RCT_EXTERN_METHOD(getDeviceInfo:(RCTPromiseResolveBlock)resolve
23
+ rejecter:(RCTPromiseRejectBlock)reject)
24
+ RCT_EXTERN_METHOD(registerForPush:(NSDictionary *)fcmParams
25
+ resolver:(RCTPromiseResolveBlock)resolve
26
+ rejecter:(RCTPromiseRejectBlock)reject)
27
+
28
+ // Permission
29
+ RCT_EXTERN_METHOD(getPermissionStatus:(RCTPromiseResolveBlock)resolve
30
+ rejecter:(RCTPromiseRejectBlock)reject)
31
+ RCT_EXTERN_METHOD(requestPermission:(BOOL)fallbackToSettings
32
+ resolver:(RCTPromiseResolveBlock)resolve
33
+ rejecter:(RCTPromiseRejectBlock)reject)
34
+ RCT_EXTERN_METHOD(registerForProvisionalAuthorization:(RCTPromiseResolveBlock)resolve
35
+ rejecter:(RCTPromiseRejectBlock)reject)
36
+ RCT_EXTERN_METHOD(canRequestPermission:(RCTPromiseResolveBlock)resolve
37
+ rejecter:(RCTPromiseRejectBlock)reject)
38
+
39
+ // Notifications
40
+ RCT_EXTERN_METHOD(clearAll)
41
+ RCT_EXTERN_METHOD(removeNotification:(NSString *)notificationId)
42
+ RCT_EXTERN_METHOD(removeGroupedNotifications:(NSString *)groupKey)
43
+ RCT_EXTERN_METHOD(clearBadge:(RCTPromiseResolveBlock)resolve
44
+ rejecter:(RCTPromiseRejectBlock)reject)
45
+
46
+ // Events
47
+ RCT_EXTERN_METHOD(setJsReady)
48
+ RCT_EXTERN_METHOD(displayForegroundNotification:(NSString *)displayId)
49
+ RCT_EXTERN_METHOD(preventForegroundDisplay:(NSString *)displayId)
50
+
51
+ // Settings
52
+ RCT_EXTERN_METHOD(setAutoOpenUrls:(BOOL)enabled)
53
+ RCT_EXTERN_METHOD(setLogLevel:(double)level)
54
+
55
+ // Background flush
56
+ RCT_EXTERN_METHOD(beginBackgroundTask:(RCTPromiseResolveBlock)resolve
57
+ rejecter:(RCTPromiseRejectBlock)reject)
58
+ RCT_EXTERN_METHOD(endBackgroundTask:(double)taskId)
59
+
60
+ // addListener: / removeListeners: are exported by RCTEventEmitter.
61
+
62
+ @end
@@ -0,0 +1,186 @@
1
+ import Foundation
2
+ import UIKit
3
+ import UserNotifications
4
+
5
+ /// React Native module `QPushPush` (exported by QPushPush.m).
6
+ ///
7
+ /// The user / subscription / HTTP state machine lives in TypeScript; this
8
+ /// module only does what must be native: storage (+ App Group mirroring),
9
+ /// APNs token, permission, notification center and events.
10
+ @objc(QPushPush)
11
+ class QPushPush: RCTEventEmitter {
12
+ private(set) var hasListeners = false
13
+
14
+ override init() {
15
+ super.init()
16
+ QPushPushCore.shared.attach(self)
17
+ }
18
+
19
+ // RCTEventEmitter adopts RCTBridgeModule, so Swift sees this as inherited.
20
+ @objc
21
+ override static func requiresMainQueueSetup() -> Bool {
22
+ // Touches UIApplication / UNUserNotificationCenter at init.
23
+ return true
24
+ }
25
+
26
+ override func supportedEvents() -> [String]! {
27
+ return QPushPushCore.supportedEvents
28
+ }
29
+
30
+ override func startObserving() {
31
+ hasListeners = true
32
+ }
33
+
34
+ override func stopObserving() {
35
+ hasListeners = false
36
+ }
37
+
38
+ // MARK: - Storage
39
+
40
+ @objc(getItem:resolver:rejecter:)
41
+ func getItem(_ key: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
42
+ resolve(QPushPushCore.orNull(QPushStorage.shared.string(key)))
43
+ }
44
+
45
+ @objc(setItem:value:resolver:rejecter:)
46
+ func setItem(
47
+ _ key: String,
48
+ value: String,
49
+ resolver resolve: @escaping RCTPromiseResolveBlock,
50
+ rejecter reject: @escaping RCTPromiseRejectBlock
51
+ ) {
52
+ QPushStorage.shared.set(value, forKey: key)
53
+ resolve(NSNull())
54
+ }
55
+
56
+ @objc(removeItem:resolver:rejecter:)
57
+ func removeItem(_ key: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
58
+ QPushStorage.shared.remove(key)
59
+ resolve(NSNull())
60
+ }
61
+
62
+ // MARK: - Device & registration
63
+
64
+ @objc(getDeviceInfo:rejecter:)
65
+ func getDeviceInfo(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
66
+ DispatchQueue.main.async {
67
+ resolve(QPushDeviceInfo.collect())
68
+ }
69
+ }
70
+
71
+ /// `fcmParams` is Android only and ignored here.
72
+ @objc(registerForPush:resolver:rejecter:)
73
+ func registerForPush(
74
+ _ fcmParams: NSDictionary?,
75
+ resolver resolve: @escaping RCTPromiseResolveBlock,
76
+ rejecter reject: @escaping RCTPromiseRejectBlock
77
+ ) {
78
+ QPushPushCore.shared.registerForPush { result in
79
+ resolve(result)
80
+ }
81
+ }
82
+
83
+ // MARK: - Permission
84
+
85
+ @objc(getPermissionStatus:rejecter:)
86
+ func getPermissionStatus(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
87
+ QPushPushCore.shared.permissionStatus { status in
88
+ resolve(status)
89
+ }
90
+ }
91
+
92
+ @objc(requestPermission:resolver:rejecter:)
93
+ func requestPermission(
94
+ _ fallbackToSettings: Bool,
95
+ resolver resolve: @escaping RCTPromiseResolveBlock,
96
+ rejecter reject: @escaping RCTPromiseRejectBlock
97
+ ) {
98
+ QPushPushCore.shared.requestPermission(fallbackToSettings: fallbackToSettings) { granted in
99
+ resolve(granted)
100
+ }
101
+ }
102
+
103
+ @objc(registerForProvisionalAuthorization:rejecter:)
104
+ func registerForProvisionalAuthorization(
105
+ _ resolve: @escaping RCTPromiseResolveBlock,
106
+ rejecter reject: @escaping RCTPromiseRejectBlock
107
+ ) {
108
+ QPushPushCore.shared.registerForProvisionalAuthorization { granted in
109
+ resolve(granted)
110
+ }
111
+ }
112
+
113
+ @objc(canRequestPermission:rejecter:)
114
+ func canRequestPermission(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
115
+ QPushPushCore.shared.canRequestPermission { canRequest in
116
+ resolve(canRequest)
117
+ }
118
+ }
119
+
120
+ // MARK: - Notifications
121
+
122
+ @objc(clearAll)
123
+ func clearAll() {
124
+ QPushPushCore.shared.clearAll()
125
+ }
126
+
127
+ @objc(removeNotification:)
128
+ func removeNotification(_ notificationId: String) {
129
+ QPushPushCore.shared.removeNotification(notificationId)
130
+ }
131
+
132
+ @objc(removeGroupedNotifications:)
133
+ func removeGroupedNotifications(_ groupKey: String) {
134
+ QPushPushCore.shared.removeGroupedNotifications(groupKey)
135
+ }
136
+
137
+ @objc(clearBadge:rejecter:)
138
+ func clearBadge(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
139
+ QPushPushCore.shared.clearBadge { previous in
140
+ resolve(previous)
141
+ }
142
+ }
143
+
144
+ // MARK: - Events
145
+
146
+ @objc(setJsReady)
147
+ func setJsReady() {
148
+ QPushPushCore.shared.setJsReady()
149
+ }
150
+
151
+ @objc(displayForegroundNotification:)
152
+ func displayForegroundNotification(_ displayId: String) {
153
+ QPushPushCore.shared.displayForeground(displayId)
154
+ }
155
+
156
+ @objc(preventForegroundDisplay:)
157
+ func preventForegroundDisplay(_ displayId: String) {
158
+ QPushPushCore.shared.preventForeground(displayId)
159
+ }
160
+
161
+ // MARK: - Settings
162
+
163
+ @objc(setAutoOpenUrls:)
164
+ func setAutoOpenUrls(_ enabled: Bool) {
165
+ QPushStorage.shared.autoOpenUrls = enabled
166
+ }
167
+
168
+ @objc(setLogLevel:)
169
+ func setLogLevel(_ level: Double) {
170
+ QPushLog.level = Int(level)
171
+ }
172
+
173
+ // MARK: - Background flush
174
+
175
+ @objc(beginBackgroundTask:rejecter:)
176
+ func beginBackgroundTask(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
177
+ QPushPushCore.shared.beginBackgroundTask { taskId in
178
+ resolve(taskId)
179
+ }
180
+ }
181
+
182
+ @objc(endBackgroundTask:)
183
+ func endBackgroundTask(_ taskId: Double) {
184
+ QPushPushCore.shared.endBackgroundTask(Int(taskId))
185
+ }
186
+ }
@@ -0,0 +1,5 @@
1
+ // Makes React Native's Objective-C API visible to the Swift files of this pod
2
+ // (CocoaPods includes public headers in the pod's umbrella header, which the
3
+ // Swift code of the same module can see).
4
+ #import <React/RCTBridgeModule.h>
5
+ #import <React/RCTEventEmitter.h>