@trycourier/courier-react-native 1.0.9 → 2.0.0-beta0

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 (89) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +16 -412
  3. package/android/build.gradle +48 -83
  4. package/android/src/main/AndroidManifest.xml +0 -1
  5. package/android/src/main/AndroidManifestNew.xml +2 -0
  6. package/android/src/main/java/com/courierreactnative/CourierReactNativeActivity.kt +10 -10
  7. package/android/src/main/java/com/courierreactnative/CourierReactNativeModule.kt +146 -137
  8. package/android/src/main/java/com/courierreactnative/CourierReactNativePackage.kt +1 -3
  9. package/android/src/main/java/com/courierreactnative/CourierReactNativeViewManager.kt +43 -0
  10. package/courier-react-native.podspec +12 -4
  11. package/ios/CourierReactNative-Bridging-Header.h +0 -1
  12. package/ios/CourierReactNative.xcodeproj/project.pbxproj +4 -4
  13. package/ios/CourierReactNative.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  14. package/ios/CourierReactNativeModule.m +48 -0
  15. package/ios/{CourierReactNative.swift → CourierReactNativeModule.swift} +165 -45
  16. package/ios/CourierReactNativeViewManager.m +15 -0
  17. package/ios/CourierReactNativeViewManager.swift +348 -0
  18. package/lib/commonjs/index.js +68 -213
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/commonjs/models/CourierInboxListener.js +26 -0
  21. package/lib/commonjs/models/CourierInboxListener.js.map +1 -0
  22. package/lib/commonjs/models/CourierInboxTheme.js +2 -0
  23. package/lib/commonjs/models/CourierInboxTheme.js.map +1 -0
  24. package/lib/commonjs/models/InboxAction.js +2 -0
  25. package/lib/commonjs/models/InboxAction.js.map +1 -0
  26. package/lib/commonjs/models/InboxMessage.js +6 -0
  27. package/lib/commonjs/models/InboxMessage.js.map +1 -0
  28. package/lib/commonjs/views/CourierInboxView.js +53 -0
  29. package/lib/commonjs/views/CourierInboxView.js.map +1 -0
  30. package/lib/module/index.js +62 -212
  31. package/lib/module/index.js.map +1 -1
  32. package/lib/module/models/CourierInboxListener.js +18 -0
  33. package/lib/module/models/CourierInboxListener.js.map +1 -0
  34. package/lib/module/models/CourierInboxTheme.js +2 -0
  35. package/lib/module/models/CourierInboxTheme.js.map +1 -0
  36. package/lib/module/models/InboxAction.js +2 -0
  37. package/lib/module/models/InboxAction.js.map +1 -0
  38. package/lib/module/models/InboxMessage.js +2 -0
  39. package/lib/module/models/InboxMessage.js.map +1 -0
  40. package/lib/module/views/CourierInboxView.js +45 -0
  41. package/lib/module/views/CourierInboxView.js.map +1 -0
  42. package/lib/typescript/index.d.ts +34 -92
  43. package/lib/typescript/index.d.ts.map +1 -0
  44. package/lib/typescript/models/CourierInboxListener.d.ts +9 -0
  45. package/lib/typescript/models/CourierInboxListener.d.ts.map +1 -0
  46. package/lib/typescript/models/CourierInboxTheme.d.ts +34 -0
  47. package/lib/typescript/models/CourierInboxTheme.d.ts.map +1 -0
  48. package/lib/typescript/models/InboxAction.d.ts +8 -0
  49. package/lib/typescript/models/InboxAction.d.ts.map +1 -0
  50. package/lib/typescript/models/InboxMessage.d.ts +18 -0
  51. package/lib/typescript/models/InboxMessage.d.ts.map +1 -0
  52. package/lib/typescript/views/CourierInboxView.d.ts +16 -0
  53. package/lib/typescript/views/CourierInboxView.d.ts.map +1 -0
  54. package/package.json +61 -44
  55. package/src/index.tsx +188 -0
  56. package/src/models/CourierInboxListener.tsx +25 -0
  57. package/src/models/CourierInboxTheme.tsx +30 -0
  58. package/src/models/InboxAction.tsx +5 -0
  59. package/src/models/InboxMessage.tsx +16 -0
  60. package/src/views/CourierInboxView.tsx +85 -0
  61. package/android/.gradle/7.1/dependencies-accessors/dependencies-accessors.lock +0 -0
  62. package/android/.gradle/7.1/dependencies-accessors/gc.properties +0 -0
  63. package/android/.gradle/7.1/executionHistory/executionHistory.lock +0 -0
  64. package/android/.gradle/7.1/fileChanges/last-build.bin +0 -0
  65. package/android/.gradle/7.1/fileHashes/fileHashes.bin +0 -0
  66. package/android/.gradle/7.1/fileHashes/fileHashes.lock +0 -0
  67. package/android/.gradle/7.1/gc.properties +0 -0
  68. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  69. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  70. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  71. package/android/.gradle/checksums/checksums.lock +0 -0
  72. package/android/.gradle/checksums/md5-checksums.bin +0 -0
  73. package/android/.gradle/checksums/sha1-checksums.bin +0 -0
  74. package/android/.gradle/vcs-1/gc.properties +0 -0
  75. package/android/.idea/compiler.xml +0 -6
  76. package/android/.idea/gradle.xml +0 -17
  77. package/android/.idea/jarRepositories.xml +0 -35
  78. package/android/.idea/misc.xml +0 -10
  79. package/android/.idea/vcs.xml +0 -6
  80. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  81. package/android/gradle/wrapper/gradle-wrapper.properties +0 -5
  82. package/android/gradlew +0 -185
  83. package/android/gradlew.bat +0 -89
  84. package/android/local.properties +0 -8
  85. package/android/src/main/java/com/courierreactnative/NotificationPermissionStatus.kt +0 -6
  86. package/ios/CourierReactNative.m +0 -52
  87. package/ios/CourierReactNativeDelegate.h +0 -20
  88. package/ios/CourierReactNativeDelegate.m +0 -125
  89. package/src/index.ts +0 -337
@@ -1,13 +1,21 @@
1
1
  import Courier_iOS
2
2
 
3
- @objc(CourierReactNative)
4
- class CourierReactNative: RCTEventEmitter {
3
+ @objc(CourierReactNativeModule)
4
+ class CourierReactNativeModule: RCTEventEmitter {
5
5
 
6
6
  private static let COURIER_ERROR_TAG = "Courier iOS SDK Error"
7
7
  internal static let COURIER_PUSH_NOTIFICATION_CLICKED_EVENT = "pushNotificationClicked"
8
8
  internal static let COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT = "pushNotificationDelivered"
9
9
  private static let COURIER_PUSH_NOTIFICATION_DEBUG_LOG_EVENT = "courierDebugEvent"
10
10
 
11
+ class InboxEvents {
12
+ internal static let INITIAL_LOADING = "inboxInitialLoad"
13
+ internal static let ERROR = "inboxError"
14
+ internal static let MESSAGES_CHANGED = "inboxMessagesChanged"
15
+ }
16
+
17
+ private var inboxListeners = [String: CourierInboxListener]()
18
+
11
19
  private var hasListeners = false
12
20
 
13
21
  private var lastClickedMessage: [AnyHashable: Any]? = nil
@@ -35,7 +43,10 @@ class CourierReactNative: RCTEventEmitter {
35
43
 
36
44
  // setup listeners
37
45
  Courier.shared.logListener = { log in
38
- self.sendEvent(withName: CourierReactNative.COURIER_PUSH_NOTIFICATION_DEBUG_LOG_EVENT, body: log)
46
+ self.sendEvent(
47
+ withName: CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_DEBUG_LOG_EVENT,
48
+ body: log
49
+ )
39
50
  }
40
51
 
41
52
  }
@@ -52,14 +63,14 @@ class CourierReactNative: RCTEventEmitter {
52
63
  notificationCenter.addObserver(
53
64
  self,
54
65
  selector: #selector(pushNotificationClicked),
55
- name: Notification.Name(rawValue: CourierReactNative.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT),
66
+ name: Notification.Name(rawValue: CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT),
56
67
  object: nil
57
68
  )
58
69
 
59
70
  notificationCenter.addObserver(
60
71
  self,
61
72
  selector: #selector(pushNotificationDelivered),
62
- name: Notification.Name(rawValue: CourierReactNative.COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT),
73
+ name: Notification.Name(rawValue: CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT),
63
74
  object: nil
64
75
  )
65
76
 
@@ -86,7 +97,7 @@ class CourierReactNative: RCTEventEmitter {
86
97
 
87
98
  lastClickedMessage = notification.userInfo
88
99
  sendMessage(
89
- name: CourierReactNative.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT,
100
+ name: CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT,
90
101
  message: lastClickedMessage
91
102
  )
92
103
 
@@ -95,7 +106,7 @@ class CourierReactNative: RCTEventEmitter {
95
106
  @objc private func pushNotificationDelivered(notification: Notification) {
96
107
 
97
108
  sendMessage(
98
- name: CourierReactNative.COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT,
109
+ name: CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT,
99
110
  message: notification.userInfo
100
111
  )
101
112
 
@@ -104,28 +115,12 @@ class CourierReactNative: RCTEventEmitter {
104
115
  @objc func registerPushNotificationClickedOnKilledState() {
105
116
 
106
117
  sendMessage(
107
- name: CourierReactNative.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT,
118
+ name: CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT,
108
119
  message: lastClickedMessage
109
120
  )
110
121
 
111
122
  }
112
-
113
- @objc(signIn: accessToken: withResolver: withRejecter:)
114
- func signIn(userId: NSString, accessToken: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
115
-
116
- Courier.shared.signIn(
117
- accessToken: accessToken as String,
118
- userId: userId as String,
119
- onSuccess: {
120
- resolve(nil)
121
- },
122
- onFailure: { error in
123
- reject(String(describing: error), CourierReactNative.COURIER_ERROR_TAG, nil)
124
- }
125
- )
126
-
127
- }
128
-
123
+
129
124
  @objc(getNotificationPermissionStatus: withRejecter:)
130
125
  func getNotificationPermissionStatus(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
131
126
 
@@ -144,6 +139,23 @@ class CourierReactNative: RCTEventEmitter {
144
139
 
145
140
  }
146
141
 
142
+ @objc(signIn: withClientKey: withUserId: withResolver: withRejecter:)
143
+ func signIn(accessToken: NSString, clientKey: NSString?, userId: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
144
+
145
+ Courier.shared.signIn(
146
+ accessToken: accessToken as String,
147
+ clientKey: clientKey as? String,
148
+ userId: userId as String,
149
+ onSuccess: {
150
+ resolve(nil)
151
+ },
152
+ onFailure: { error in
153
+ reject(String(describing: error), CourierReactNativeModule.COURIER_ERROR_TAG, nil)
154
+ }
155
+ )
156
+
157
+ }
158
+
147
159
  @objc(signOut: withRejecter:)
148
160
  func signOut(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
149
161
 
@@ -152,7 +164,7 @@ class CourierReactNative: RCTEventEmitter {
152
164
  resolve(nil)
153
165
  },
154
166
  onFailure: { error in
155
- reject(String(describing: error), CourierReactNative.COURIER_ERROR_TAG, nil)
167
+ reject(String(describing: error), CourierReactNativeModule.COURIER_ERROR_TAG, nil)
156
168
  }
157
169
  )
158
170
 
@@ -183,7 +195,7 @@ class CourierReactNative: RCTEventEmitter {
183
195
  resolve(nil)
184
196
  },
185
197
  onFailure: { error in
186
- reject(String(describing: error), CourierReactNative.COURIER_ERROR_TAG, nil)
198
+ reject(String(describing: error), CourierReactNativeModule.COURIER_ERROR_TAG, nil)
187
199
  }
188
200
  )
189
201
 
@@ -201,23 +213,23 @@ class CourierReactNative: RCTEventEmitter {
201
213
  func sendPush(authKey: NSString, userId: NSString, title: NSString, body: NSString, providers: NSArray, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
202
214
 
203
215
  guard let courierProviders = providers as? [String] else {
204
- reject("No provider supported", CourierReactNative.COURIER_ERROR_TAG, nil)
216
+ reject("No provider supported", CourierReactNativeModule.COURIER_ERROR_TAG, nil)
205
217
  return
206
218
  }
207
219
 
208
- Courier.shared.sendPush(
209
- authKey: authKey as String,
210
- userId: userId as String,
211
- title: title as String,
212
- message: body as String,
213
- providers: courierProviders,
214
- onSuccess: { requestId in
215
- resolve(requestId)
216
- },
217
- onFailure: { error in
218
- reject(String(describing: error), CourierReactNative.COURIER_ERROR_TAG, nil)
219
- }
220
- )
220
+ // Courier.shared.sendPush(
221
+ // authKey: authKey as String,
222
+ // userId: userId as String,
223
+ // title: title as String,
224
+ // message: body as String,
225
+ // providers: courierProviders,
226
+ // onSuccess: { requestId in
227
+ // resolve(requestId)
228
+ // },
229
+ // onFailure: { error in
230
+ // reject(String(describing: error), CourierReactNative.COURIER_ERROR_TAG, nil)
231
+ // }
232
+ // )
221
233
 
222
234
  }
223
235
 
@@ -234,21 +246,129 @@ class CourierReactNative: RCTEventEmitter {
234
246
  }
235
247
 
236
248
  @objc(setDebugMode: withResolver: withRejecter:)
237
- func setDebugMode(isDebugging: Bool,resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
249
+ func setDebugMode(isDebugging: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
238
250
 
239
251
  Courier.shared.isDebugging = isDebugging
240
252
  resolve(Courier.shared.isDebugging)
241
253
 
242
254
  }
255
+
256
+ @objc(readMessage: withResolver: withRejecter:)
257
+ func readMessage(messageId: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
258
+
259
+ Courier.shared.readMessage(
260
+ messageId: messageId as String,
261
+ onSuccess: {
262
+ resolve(nil)
263
+ },
264
+ onFailure: { error in
265
+ reject(String(describing: error), CourierReactNativeModule.COURIER_ERROR_TAG, nil)
266
+ }
267
+ )
268
+
269
+ }
270
+
271
+ @objc(unreadMessage: withResolver: withRejecter:)
272
+ func unreadMessage(messageId: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
273
+
274
+ Courier.shared.unreadMessage(
275
+ messageId: messageId as String,
276
+ onSuccess: {
277
+ resolve(nil)
278
+ },
279
+ onFailure: { error in
280
+ reject(String(describing: error), CourierReactNativeModule.COURIER_ERROR_TAG, nil)
281
+ }
282
+ )
283
+
284
+ }
285
+
286
+ @objc(readAllInboxMessages: withRejecter:)
287
+ func readAllInboxMessages(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
288
+
289
+ Courier.shared.readAllInboxMessages(
290
+ onSuccess: {
291
+ resolve(nil)
292
+ },
293
+ onFailure: { error in
294
+ reject(String(describing: error), CourierReactNativeModule.COURIER_ERROR_TAG, nil)
295
+ }
296
+ )
297
+
298
+ }
299
+
300
+ @objc(addInboxListener:)
301
+ func addInboxListener(listenerId: NSString?) -> String {
302
+
303
+ let listener = Courier.shared.addInboxListener(
304
+ onInitialLoad: { [weak self] in
305
+ self?.sendEvent(
306
+ withName: CourierReactNativeModule.InboxEvents.INITIAL_LOADING,
307
+ body: nil
308
+ )
309
+ },
310
+ onError: { [weak self] error in
311
+ self?.sendEvent(
312
+ withName: CourierReactNativeModule.InboxEvents.ERROR,
313
+ body: String(describing: error)
314
+ )
315
+ },
316
+ onMessagesChanged: { [weak self] messages, unreadMessageCount, totalMessageCount, canPaginate in
317
+
318
+ let json: [String: Any] = [
319
+ "messages": messages.map { $0.toDictionary() },
320
+ "unreadMessageCount": unreadMessageCount,
321
+ "totalMessageCount": totalMessageCount,
322
+ "canPaginate": canPaginate
323
+ ]
324
+
325
+ self?.sendEvent(
326
+ withName: CourierReactNativeModule.InboxEvents.MESSAGES_CHANGED,
327
+ body: json
328
+ )
329
+
330
+ }
331
+ )
332
+
333
+ // Create an id and add the listener to the dictionary
334
+ let id = UUID().uuidString
335
+ inboxListeners[id] = listener
336
+
337
+ return id
338
+
339
+ }
340
+
341
+ @objc(removeInboxListener:)
342
+ func removeInboxListener(listenerId: NSString) -> String {
343
+
344
+ let id = listenerId as String
345
+
346
+ // Remove the listener
347
+ let listener = inboxListeners[id]
348
+ listener?.remove()
349
+
350
+ // Remove from dictionary
351
+ inboxListeners.removeValue(forKey: id)
352
+
353
+ return id
354
+
355
+ }
243
356
 
244
357
  override func supportedEvents() -> [String]! {
245
358
  return [
246
- CourierReactNative.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT,
247
- CourierReactNative.COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT,
248
- CourierReactNative.COURIER_PUSH_NOTIFICATION_DEBUG_LOG_EVENT
359
+ CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_CLICKED_EVENT,
360
+ CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_DELIVERED_EVENT,
361
+ CourierReactNativeModule.COURIER_PUSH_NOTIFICATION_DEBUG_LOG_EVENT,
362
+ CourierReactNativeModule.InboxEvents.INITIAL_LOADING,
363
+ CourierReactNativeModule.InboxEvents.ERROR,
364
+ CourierReactNativeModule.InboxEvents.MESSAGES_CHANGED
249
365
  ]
250
366
  }
251
367
 
368
+ @objc override static func requiresMainQueueSetup() -> Bool {
369
+ return true
370
+ }
371
+
252
372
  }
253
373
 
254
374
  extension [AnyHashable: Any] {
@@ -0,0 +1,15 @@
1
+ #import <React/RCTViewManager.h>
2
+
3
+ @interface RCT_EXTERN_MODULE(CourierReactNativeViewManager, RCTViewManager)
4
+
5
+ RCT_EXPORT_VIEW_PROPERTY(lightTheme, NSDictionary)
6
+
7
+ RCT_EXPORT_VIEW_PROPERTY(darkTheme, NSDictionary)
8
+
9
+ RCT_EXPORT_VIEW_PROPERTY(onClickInboxMessageAtIndex, RCTBubblingEventBlock)
10
+
11
+ RCT_EXPORT_VIEW_PROPERTY(onClickInboxActionForMessageAtIndex, RCTBubblingEventBlock)
12
+
13
+ RCT_EXPORT_VIEW_PROPERTY(onScrollInbox, RCTBubblingEventBlock)
14
+
15
+ @end
@@ -0,0 +1,348 @@
1
+ import Courier_iOS
2
+
3
+ @objc(CourierReactNativeViewManager)
4
+ class CourierReactNativeViewManager: RCTViewManager {
5
+
6
+ override func view() -> (CourierReactNativeView) {
7
+ return CourierReactNativeView()
8
+ }
9
+
10
+ @objc override static func requiresMainQueueSetup() -> Bool {
11
+ return true
12
+ }
13
+
14
+ }
15
+
16
+ class CourierReactNativeView : UIView {
17
+
18
+ @objc var lightTheme: NSDictionary? = [:] {
19
+ didSet {
20
+ refreshInbox()
21
+ }
22
+ }
23
+
24
+ @objc var darkTheme: NSDictionary? = [:] {
25
+ didSet {
26
+ refreshInbox()
27
+ }
28
+ }
29
+
30
+ @objc var onClickInboxMessageAtIndex: RCTBubblingEventBlock? = nil
31
+
32
+ @objc var onClickInboxActionForMessageAtIndex: RCTBubblingEventBlock? = nil
33
+
34
+ @objc var onScrollInbox: RCTBubblingEventBlock? = nil
35
+
36
+ override init(frame: CGRect) {
37
+ super.init(frame: frame)
38
+ refreshInbox()
39
+ }
40
+
41
+ required init?(coder: NSCoder) {
42
+ super.init(coder: coder)
43
+ refreshInbox()
44
+ }
45
+
46
+ private func refreshInbox() {
47
+
48
+ subviews.forEach { $0.removeFromSuperview() }
49
+
50
+ // Create the view
51
+ let courierInbox = CourierInbox(
52
+ lightTheme: dictionaryToTheme(dictionary: lightTheme) ?? .defaultLight,
53
+ darkTheme: dictionaryToTheme(dictionary: darkTheme) ?? .defaultDark,
54
+ didClickInboxMessageAtIndex: { [weak self] message, index in
55
+ self?.onClickInboxMessageAtIndex?([
56
+ "message" : message.toDictionary(),
57
+ "index" : index
58
+ ])
59
+ },
60
+ didClickInboxActionForMessageAtIndex: { [weak self] action, message, index in
61
+ self?.onClickInboxActionForMessageAtIndex?([
62
+ "action" : action.toDictionary(),
63
+ "message" : message.toDictionary(),
64
+ "index" : index
65
+ ])
66
+ },
67
+ didScrollInbox: { [weak self] scrollView in
68
+ self?.onScrollInbox?([
69
+ "contentOffset" : [
70
+ "y": scrollView.contentOffset.y,
71
+ "x": scrollView.contentOffset.x
72
+ ]
73
+ ])
74
+ }
75
+ )
76
+
77
+ // Add the view to your UI
78
+ courierInbox.translatesAutoresizingMaskIntoConstraints = false
79
+ addSubview(courierInbox)
80
+
81
+ // Constrain the view how you'd like
82
+ NSLayoutConstraint.activate([
83
+ courierInbox.topAnchor.constraint(equalTo: topAnchor),
84
+ courierInbox.bottomAnchor.constraint(equalTo: bottomAnchor),
85
+ courierInbox.leadingAnchor.constraint(equalTo: leadingAnchor),
86
+ courierInbox.trailingAnchor.constraint(equalTo: trailingAnchor),
87
+ ])
88
+
89
+ }
90
+
91
+ func dictionaryToTheme(dictionary: NSDictionary?) -> CourierInboxTheme? {
92
+
93
+ guard let dict = dictionary else {
94
+ return nil
95
+ }
96
+
97
+ // iOS Theme
98
+ let iOS = dict["iOS"] as? [String : Any]
99
+ let messageAnimationStyle = iOS?["messageAnimationStyle"] as? String
100
+ let cellStyles = iOS?["cellStyles"] as? [String : Any]
101
+
102
+ // Unread
103
+ let unreadIndicatorBarColor = dict["unreadIndicatorBarColor"] as? String
104
+
105
+ // Loading
106
+ let loadingIndicatorColor = dict["loadingIndicatorColor"] as? String
107
+
108
+ // Title
109
+ let titleFont = dict["titleFont"] as? [String : Any]
110
+
111
+ // Time
112
+ let timeFont = dict["timeFont"] as? [String : Any]
113
+
114
+ // Body
115
+ let bodyFont = dict["bodyFont"] as? [String : Any]
116
+
117
+ // Detail
118
+ let detailTitleFont = dict["detailTitleFont"] as? [String : Any]
119
+
120
+ // Detail
121
+ let buttonStyles = dict["buttonStyles"] as? [String : Any]
122
+
123
+ return CourierInboxTheme(
124
+ messageAnimationStyle: messageAnimationStyle?.toRowAnimation() ?? .left,
125
+ unreadIndicatorBarColor: unreadIndicatorBarColor?.toColor(),
126
+ loadingIndicatorColor: loadingIndicatorColor?.toColor(),
127
+ titleFont: dictionaryToFont(
128
+ dictionary: titleFont,
129
+ defaultFont: UIFont.boldSystemFont(ofSize: UIFont.labelFontSize),
130
+ defaultColor: .label
131
+ ),
132
+ timeFont: dictionaryToFont(
133
+ dictionary: timeFont,
134
+ defaultFont: UIFont.systemFont(ofSize: UIFont.labelFontSize),
135
+ defaultColor: .placeholderText
136
+ ),
137
+ bodyFont: dictionaryToFont(
138
+ dictionary: bodyFont,
139
+ defaultFont: UIFont.systemFont(ofSize: UIFont.labelFontSize),
140
+ defaultColor: .label
141
+ ),
142
+ detailTitleFont: dictionaryToFont(
143
+ dictionary: detailTitleFont,
144
+ defaultFont: UIFont.systemFont(ofSize: UIFont.labelFontSize),
145
+ defaultColor: .label
146
+ ),
147
+ buttonStyles: dictionaryToButtonStyles(
148
+ dictionary: buttonStyles
149
+ ),
150
+ cellStyles: dictionaryToCellStyles(
151
+ dictionary: cellStyles
152
+ )
153
+ )
154
+
155
+ }
156
+
157
+ func dictionaryToFont(dictionary: [String : Any]?, defaultFont: UIFont, defaultColor: UIColor) -> CourierInboxFont {
158
+
159
+ guard let dict = dictionary else {
160
+ return CourierInboxFont(
161
+ font: defaultFont,
162
+ color: defaultColor
163
+ )
164
+ }
165
+
166
+ let family = dict["family"] as? String ?? defaultFont.familyName
167
+ let size = dict["size"] as? CGFloat ?? defaultFont.pointSize
168
+ let color = dict["color"] as? String
169
+
170
+ return CourierInboxFont(
171
+ font: UIFont(name: family, size: size) ?? defaultFont,
172
+ color: color?.toColor() ?? defaultColor
173
+ )
174
+
175
+ }
176
+
177
+ func dictionaryToButtonStyles(dictionary: [String : Any]?) -> CourierInboxButtonStyles {
178
+
179
+ guard let dict = dictionary else {
180
+ return CourierInboxButtonStyles()
181
+ }
182
+
183
+ let font = dict["font"] as? [String : Any]
184
+ let backgroundColor = dict["backgroundColor"] as? String
185
+ let cornerRadius = dict["cornerRadius"] as? CGFloat
186
+
187
+ return CourierInboxButtonStyles(
188
+ font: dictionaryToFont(
189
+ dictionary: font,
190
+ defaultFont: UIFont.systemFont(ofSize: UIFont.labelFontSize),
191
+ defaultColor: .white
192
+ ),
193
+ backgroundColor: backgroundColor?.toColor(),
194
+ cornerRadius: cornerRadius ?? 8
195
+ )
196
+
197
+ }
198
+
199
+ func dictionaryToCellStyles(dictionary: [String : Any]?) -> CourierInboxCellStyles {
200
+
201
+ guard let dict = dictionary else {
202
+ return CourierInboxCellStyles()
203
+ }
204
+
205
+ let separatorStyle = dict["separatorStyle"] as? String
206
+ let separatorColor = dict["separatorColor"] as? String
207
+ let selectionStyle = dict["selectionStyle"] as? String
208
+
209
+ let insets = dict["separatorInsets"] as? [String : Any]
210
+ let top = insets?["top"] as? CGFloat
211
+ let left = insets?["left"] as? CGFloat
212
+ let right = insets?["right"] as? CGFloat
213
+ let bottom = insets?["bottom"] as? CGFloat
214
+ let separatorInsets = UIEdgeInsets(top: top ?? 0, left: left ?? 0, bottom: bottom ?? 0, right: right ?? 0)
215
+
216
+ return CourierInboxCellStyles(
217
+ separatorStyle: separatorStyle?.toSeparatorStyle() ?? .singleLine,
218
+ separatorInsets: separatorInsets,
219
+ separatorColor: separatorColor?.toColor(),
220
+ selectionStyle: selectionStyle?.toSelectionStyle() ?? .default
221
+ )
222
+
223
+ }
224
+
225
+ }
226
+
227
+ internal extension InboxMessage {
228
+
229
+ @objc func toDictionary() -> NSDictionary {
230
+
231
+ let dictionary: [String: Any?] = [
232
+ "messageId": messageId,
233
+ "title": title,
234
+ "body": body,
235
+ "preview": preview,
236
+ "created": created,
237
+ "actions": actions?.map { $0.toDictionary() },
238
+ "data": data,
239
+ "read": isRead,
240
+ "opened": isOpened,
241
+ "archived": isArchived
242
+ ]
243
+
244
+ let mutableDictionary = NSMutableDictionary()
245
+ for (key, value) in dictionary {
246
+ if let unwrappedValue = value {
247
+ mutableDictionary[key] = unwrappedValue
248
+ }
249
+ }
250
+
251
+ return mutableDictionary
252
+
253
+ }
254
+
255
+ }
256
+
257
+ internal extension InboxAction {
258
+
259
+ @objc func toDictionary() -> NSDictionary {
260
+
261
+ let dictionary: [String: Any?] = [
262
+ "content": content,
263
+ "href": href,
264
+ "data": data
265
+ ]
266
+
267
+ let mutableDictionary = NSMutableDictionary()
268
+ for (key, value) in dictionary {
269
+ if let unwrappedValue = value {
270
+ mutableDictionary[key] = unwrappedValue
271
+ }
272
+ }
273
+
274
+ return mutableDictionary
275
+
276
+ }
277
+
278
+ }
279
+
280
+ internal extension String {
281
+
282
+ func toRowAnimation() -> UITableView.RowAnimation {
283
+
284
+ switch self.lowercased() {
285
+ case "fade": return .fade
286
+ case "right": return .right
287
+ case "left": return .left
288
+ case "top": return .top
289
+ case "bottom": return .bottom
290
+ case "none": return .none
291
+ case "middle": return .middle
292
+ case "automatic":
293
+ if #available(iOS 11.0, *) {
294
+ return .automatic
295
+ } else {
296
+ return .fade
297
+ }
298
+ default: return .fade
299
+ }
300
+
301
+ }
302
+
303
+ func toSeparatorStyle() -> UITableViewCell.SeparatorStyle {
304
+
305
+ switch self.lowercased() {
306
+ case "none": return .none
307
+ case "singleLine": return .singleLine
308
+ case "singleLineEtched": return .singleLineEtched
309
+ default: return .singleLine
310
+ }
311
+
312
+ }
313
+
314
+ func toSelectionStyle() -> UITableViewCell.SelectionStyle? {
315
+
316
+ switch self.lowercased() {
317
+ case "none": return .none
318
+ case "blue": return .blue
319
+ case "gray": return .gray
320
+ case "default": return .default
321
+ default: return .default
322
+ }
323
+
324
+ }
325
+
326
+ func toColor() -> UIColor? {
327
+
328
+ var hexSanitized = trimmingCharacters(in: .whitespacesAndNewlines)
329
+ hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
330
+
331
+ var rgb: UInt64 = 0
332
+
333
+ Scanner(string: hexSanitized).scanHexInt64(&rgb)
334
+
335
+ guard hexSanitized.count == 6 else {
336
+ return nil
337
+ }
338
+
339
+ return UIColor(
340
+ red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
341
+ green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
342
+ blue: CGFloat(rgb & 0x0000FF) / 255.0,
343
+ alpha: 1.0
344
+ )
345
+
346
+ }
347
+
348
+ }