zixflow-reactnative 1.1.3 → 1.1.5

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 (239) hide show
  1. package/README.md +631 -261
  2. package/android/build.gradle +18 -1
  3. package/android/consumer-rules.pro +3 -2
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/java/com/zixflow/reactnative/sdk/NativeZixflowModule.kt +41 -5
  6. package/android/src/main/java/com/zixflow/reactnative/sdk/ZixflowReactNativePackage.kt +25 -1
  7. package/android/src/main/java/com/zixflow/reactnative/sdk/constant/Keys.kt +19 -0
  8. package/android/src/main/java/com/zixflow/reactnative/sdk/geofence/NativeGeofenceModule.kt +65 -0
  9. package/android/src/main/java/com/zixflow/reactnative/sdk/liveactivities/NativeLiveActivitiesModule.kt +310 -0
  10. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +150 -0
  11. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/ReactInboxEventListener.kt +99 -0
  12. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxBellViewManager.kt +38 -0
  13. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxViewManager.kt +30 -0
  14. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactComposeHostView.kt +96 -0
  15. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxBellView.kt +48 -0
  16. package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxView.kt +17 -0
  17. package/android/src/main/java/com/zixflow/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +11 -1
  18. package/android/src/main/res/values/zixflow_config.xml +1 -1
  19. package/android/zixflow-core.gradle +15 -2
  20. package/docs/cocoapods-deployment-target-normalization.md +139 -0
  21. package/ios/cocoapods_deployment_target.rb +432 -0
  22. package/ios/wrappers/NativeZixflow.mm +37 -0
  23. package/ios/wrappers/NativeZixflow.swift +135 -1
  24. package/ios/wrappers/ZixflowReactNativeDeepLinkRequestStore.swift +123 -0
  25. package/ios/wrappers/ZixflowReactNativeDeepLinkRouter.swift +312 -0
  26. package/ios/wrappers/geofence/NativeGeofence.mm +46 -0
  27. package/ios/wrappers/geofence/NativeGeofence.swift +27 -0
  28. package/ios/wrappers/inapp/NativeMessagingInApp.mm +22 -0
  29. package/ios/wrappers/inapp/NativeMessagingInApp.swift +91 -0
  30. package/ios/wrappers/inbox/InboxHostContainment.swift +40 -0
  31. package/ios/wrappers/inbox/RCTNotificationInboxBellNative.h +12 -0
  32. package/ios/wrappers/inbox/RCTNotificationInboxBellNative.mm +98 -0
  33. package/ios/wrappers/inbox/RCTNotificationInboxViewNative.h +12 -0
  34. package/ios/wrappers/inbox/RCTNotificationInboxViewNative.mm +78 -0
  35. package/ios/wrappers/inbox/ReactInboxEventListener.swift +76 -0
  36. package/ios/wrappers/inbox/ReactNotificationInboxBellView.h +22 -0
  37. package/ios/wrappers/inbox/ReactNotificationInboxBellView.swift +109 -0
  38. package/ios/wrappers/inbox/ReactNotificationInboxView.h +22 -0
  39. package/ios/wrappers/inbox/ReactNotificationInboxView.swift +69 -0
  40. package/ios/wrappers/liveactivities/NativeLiveActivities.mm +83 -0
  41. package/ios/wrappers/liveactivities/NativeLiveActivities.swift +390 -0
  42. package/ios/wrappers/location/NativeLocation.swift +7 -5
  43. package/ios/wrappers/utils/ZixflowConstants.swift +4 -0
  44. package/ios/wrappers/zixflow_reactnative.h +6 -0
  45. package/lib/commonjs/components/NotificationInboxBellView.js +49 -0
  46. package/lib/commonjs/components/NotificationInboxBellView.js.map +1 -0
  47. package/lib/commonjs/components/NotificationInboxView.js +36 -0
  48. package/lib/commonjs/components/NotificationInboxView.js.map +1 -0
  49. package/lib/commonjs/components/index.js +14 -0
  50. package/lib/commonjs/components/index.js.map +1 -1
  51. package/lib/commonjs/index.js +81 -4
  52. package/lib/commonjs/index.js.map +1 -1
  53. package/lib/commonjs/notification-inbox.js +1 -0
  54. package/lib/commonjs/notification-inbox.js.map +1 -1
  55. package/lib/commonjs/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  56. package/lib/commonjs/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  57. package/lib/commonjs/specs/modules/NativeZixflow.js.map +1 -1
  58. package/lib/commonjs/specs/modules/NativeZixflowGeofence.js +15 -0
  59. package/lib/commonjs/specs/modules/NativeZixflowGeofence.js.map +1 -0
  60. package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js +23 -0
  61. package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
  62. package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js +3 -1
  63. package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
  64. package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js +3 -1
  65. package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
  66. package/lib/commonjs/types/data-pipelines.js +13 -1
  67. package/lib/commonjs/types/data-pipelines.js.map +1 -1
  68. package/lib/commonjs/types/in-app.js +24 -1
  69. package/lib/commonjs/types/in-app.js.map +1 -1
  70. package/lib/commonjs/types/inbox.js +79 -0
  71. package/lib/commonjs/types/inbox.js.map +1 -0
  72. package/lib/commonjs/types/index.js +22 -0
  73. package/lib/commonjs/types/index.js.map +1 -1
  74. package/lib/commonjs/types/live-activities.js +85 -0
  75. package/lib/commonjs/types/live-activities.js.map +1 -0
  76. package/lib/commonjs/utils/param-validation.js +76 -1
  77. package/lib/commonjs/utils/param-validation.js.map +1 -1
  78. package/lib/commonjs/zixflow-cdp.js +99 -1
  79. package/lib/commonjs/zixflow-cdp.js.map +1 -1
  80. package/lib/commonjs/zixflow-geofence.js +55 -0
  81. package/lib/commonjs/zixflow-geofence.js.map +1 -0
  82. package/lib/commonjs/zixflow-inapp.js +120 -0
  83. package/lib/commonjs/zixflow-inapp.js.map +1 -1
  84. package/lib/commonjs/zixflow-liveactivities.js +85 -0
  85. package/lib/commonjs/zixflow-liveactivities.js.map +1 -0
  86. package/lib/module/components/NotificationInboxBellView.js +44 -0
  87. package/lib/module/components/NotificationInboxBellView.js.map +1 -0
  88. package/lib/module/components/NotificationInboxView.js +31 -0
  89. package/lib/module/components/NotificationInboxView.js.map +1 -0
  90. package/lib/module/components/index.js +5 -0
  91. package/lib/module/components/index.js.map +1 -1
  92. package/lib/module/index.js +8 -3
  93. package/lib/module/index.js.map +1 -1
  94. package/lib/module/notification-inbox.js +1 -1
  95. package/lib/module/notification-inbox.js.map +1 -1
  96. package/lib/module/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  97. package/lib/module/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  98. package/lib/module/specs/modules/NativeZixflow.js.map +1 -1
  99. package/lib/module/specs/modules/NativeZixflowGeofence.js +13 -0
  100. package/lib/module/specs/modules/NativeZixflowGeofence.js.map +1 -0
  101. package/lib/module/specs/modules/NativeZixflowLiveActivities.js +21 -0
  102. package/lib/module/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
  103. package/lib/module/specs/modules/NativeZixflowMessagingInApp.js +3 -1
  104. package/lib/module/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
  105. package/lib/module/specs/modules/NativeZixflowMessagingPush.js +3 -1
  106. package/lib/module/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
  107. package/lib/module/types/data-pipelines.js +13 -0
  108. package/lib/module/types/data-pipelines.js.map +1 -1
  109. package/lib/module/types/in-app.js +24 -0
  110. package/lib/module/types/in-app.js.map +1 -1
  111. package/lib/module/types/inbox.js +75 -0
  112. package/lib/module/types/inbox.js.map +1 -0
  113. package/lib/module/types/index.js +2 -0
  114. package/lib/module/types/index.js.map +1 -1
  115. package/lib/module/types/live-activities.js +88 -0
  116. package/lib/module/types/live-activities.js.map +1 -0
  117. package/lib/module/utils/param-validation.js +76 -1
  118. package/lib/module/utils/param-validation.js.map +1 -1
  119. package/lib/module/zixflow-cdp.js +99 -1
  120. package/lib/module/zixflow-cdp.js.map +1 -1
  121. package/lib/module/zixflow-geofence.js +50 -0
  122. package/lib/module/zixflow-geofence.js.map +1 -0
  123. package/lib/module/zixflow-inapp.js +99 -1
  124. package/lib/module/zixflow-inapp.js.map +1 -1
  125. package/lib/module/zixflow-liveactivities.js +80 -0
  126. package/lib/module/zixflow-liveactivities.js.map +1 -0
  127. package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts +39 -0
  128. package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/components/NotificationInboxView.d.ts +25 -0
  130. package/lib/typescript/commonjs/components/NotificationInboxView.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/components/index.d.ts +4 -0
  132. package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/index.d.ts +8 -1
  134. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/notification-inbox.d.ts +5 -0
  136. package/lib/typescript/commonjs/notification-inbox.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
  138. package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
  140. package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts +6 -1
  142. package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts +13 -0
  144. package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
  145. package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
  146. package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
  148. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
  150. package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
  151. package/lib/typescript/commonjs/types/data-pipelines.d.ts +53 -1
  152. package/lib/typescript/commonjs/types/data-pipelines.d.ts.map +1 -1
  153. package/lib/typescript/commonjs/types/in-app.d.ts +22 -0
  154. package/lib/typescript/commonjs/types/in-app.d.ts.map +1 -1
  155. package/lib/typescript/commonjs/types/inbox.d.ts +97 -0
  156. package/lib/typescript/commonjs/types/inbox.d.ts.map +1 -0
  157. package/lib/typescript/commonjs/types/index.d.ts +2 -0
  158. package/lib/typescript/commonjs/types/index.d.ts.map +1 -1
  159. package/lib/typescript/commonjs/types/live-activities.d.ts +161 -0
  160. package/lib/typescript/commonjs/types/live-activities.d.ts.map +1 -0
  161. package/lib/typescript/commonjs/utils/param-validation.d.ts +7 -0
  162. package/lib/typescript/commonjs/utils/param-validation.d.ts.map +1 -1
  163. package/lib/typescript/commonjs/zixflow-cdp.d.ts +38 -0
  164. package/lib/typescript/commonjs/zixflow-cdp.d.ts.map +1 -1
  165. package/lib/typescript/commonjs/zixflow-geofence.d.ts +36 -0
  166. package/lib/typescript/commonjs/zixflow-geofence.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/zixflow-inapp.d.ts +36 -2
  168. package/lib/typescript/commonjs/zixflow-inapp.d.ts.map +1 -1
  169. package/lib/typescript/commonjs/zixflow-liveactivities.d.ts +66 -0
  170. package/lib/typescript/commonjs/zixflow-liveactivities.d.ts.map +1 -0
  171. package/lib/typescript/module/components/NotificationInboxBellView.d.ts +39 -0
  172. package/lib/typescript/module/components/NotificationInboxBellView.d.ts.map +1 -0
  173. package/lib/typescript/module/components/NotificationInboxView.d.ts +25 -0
  174. package/lib/typescript/module/components/NotificationInboxView.d.ts.map +1 -0
  175. package/lib/typescript/module/components/index.d.ts +4 -0
  176. package/lib/typescript/module/components/index.d.ts.map +1 -1
  177. package/lib/typescript/module/index.d.ts +8 -1
  178. package/lib/typescript/module/index.d.ts.map +1 -1
  179. package/lib/typescript/module/notification-inbox.d.ts +5 -0
  180. package/lib/typescript/module/notification-inbox.d.ts.map +1 -1
  181. package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
  182. package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
  183. package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
  184. package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
  185. package/lib/typescript/module/specs/modules/NativeZixflow.d.ts +6 -1
  186. package/lib/typescript/module/specs/modules/NativeZixflow.d.ts.map +1 -1
  187. package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts +13 -0
  188. package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
  189. package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
  190. package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
  191. package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
  192. package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
  193. package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
  194. package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
  195. package/lib/typescript/module/types/data-pipelines.d.ts +53 -1
  196. package/lib/typescript/module/types/data-pipelines.d.ts.map +1 -1
  197. package/lib/typescript/module/types/in-app.d.ts +22 -0
  198. package/lib/typescript/module/types/in-app.d.ts.map +1 -1
  199. package/lib/typescript/module/types/inbox.d.ts +97 -0
  200. package/lib/typescript/module/types/inbox.d.ts.map +1 -0
  201. package/lib/typescript/module/types/index.d.ts +2 -0
  202. package/lib/typescript/module/types/index.d.ts.map +1 -1
  203. package/lib/typescript/module/types/live-activities.d.ts +161 -0
  204. package/lib/typescript/module/types/live-activities.d.ts.map +1 -0
  205. package/lib/typescript/module/utils/param-validation.d.ts +7 -0
  206. package/lib/typescript/module/utils/param-validation.d.ts.map +1 -1
  207. package/lib/typescript/module/zixflow-cdp.d.ts +38 -0
  208. package/lib/typescript/module/zixflow-cdp.d.ts.map +1 -1
  209. package/lib/typescript/module/zixflow-geofence.d.ts +36 -0
  210. package/lib/typescript/module/zixflow-geofence.d.ts.map +1 -0
  211. package/lib/typescript/module/zixflow-inapp.d.ts +36 -2
  212. package/lib/typescript/module/zixflow-inapp.d.ts.map +1 -1
  213. package/lib/typescript/module/zixflow-liveactivities.d.ts +66 -0
  214. package/lib/typescript/module/zixflow-liveactivities.d.ts.map +1 -0
  215. package/package.json +11 -2
  216. package/src/components/NotificationInboxBellView.tsx +55 -0
  217. package/src/components/NotificationInboxView.tsx +30 -0
  218. package/src/components/index.ts +6 -0
  219. package/src/index.ts +8 -3
  220. package/src/notification-inbox.ts +1 -1
  221. package/src/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
  222. package/src/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
  223. package/src/specs/modules/NativeZixflow.ts +9 -1
  224. package/src/specs/modules/NativeZixflowGeofence.ts +14 -0
  225. package/src/specs/modules/NativeZixflowLiveActivities.ts +39 -0
  226. package/src/specs/modules/NativeZixflowMessagingInApp.ts +12 -1
  227. package/src/specs/modules/NativeZixflowMessagingPush.ts +3 -1
  228. package/src/types/data-pipelines.ts +54 -1
  229. package/src/types/in-app.ts +23 -0
  230. package/src/types/inbox.ts +110 -0
  231. package/src/types/index.ts +2 -0
  232. package/src/types/live-activities.ts +170 -0
  233. package/src/utils/param-validation.ts +103 -0
  234. package/src/zixflow-cdp.ts +117 -1
  235. package/src/zixflow-geofence.ts +56 -0
  236. package/src/zixflow-inapp.ts +125 -2
  237. package/src/zixflow-liveactivities.ts +86 -0
  238. package/zixflow-reactnative-richpush.podspec +4 -1
  239. package/zixflow-reactnative.podspec +26 -0
@@ -66,11 +66,29 @@ RCT_EXPORT_MODULE(NativeZixflowMessagingInApp)
66
66
  [_swiftBridge dismissMessage];
67
67
  }
68
68
 
69
+ // Signature matches the Codegen protocol, which declares the argument nonnull. ObjC does not
70
+ // enforce that at runtime, so a JavaScript null still arrives here as nil and is forwarded; the
71
+ // Swift side takes an optional and reports it rather than trapping.
72
+ - (void)setColorScheme:(NSString *)colorScheme {
73
+ [self assertBridgeAvailable:@"during setColorScheme"];
74
+ [_swiftBridge setColorScheme:colorScheme];
75
+ }
76
+
69
77
  - (void)setupInboxListener {
70
78
  [self assertBridgeAvailable:@"during setupInboxListener"];
71
79
  [_swiftBridge setupInboxListener];
72
80
  }
73
81
 
82
+ - (void)registerInboxEventListener {
83
+ [self assertBridgeAvailable:@"during registerInboxEventListener"];
84
+ [_swiftBridge registerInboxEventListener];
85
+ }
86
+
87
+ - (void)unregisterInboxEventListener {
88
+ [self assertBridgeAvailable:@"during unregisterInboxEventListener"];
89
+ [_swiftBridge unregisterInboxEventListener];
90
+ }
91
+
74
92
  - (void)getMessages:(NSString *)topic
75
93
  resolve:(RCTPromiseResolveBlock)resolve
76
94
  reject:(RCTPromiseRejectBlock)reject {
@@ -110,6 +128,10 @@ RCT_EXPORT_MODULE(NativeZixflowMessagingInApp)
110
128
  [super emitOnInAppEventReceived:value];
111
129
  }
112
130
 
131
+ - (void)emitOnInboxEventReceived:(NSDictionary *)value {
132
+ [super emitOnInboxEventReceived:value];
133
+ }
134
+
113
135
  - (void)emitSubscribeToMessagesChanged:(NSDictionary *)value {
114
136
  [super emitSubscribeToMessagesChanged:value];
115
137
  }
@@ -2,6 +2,7 @@ import ZixflowInternalCommon
2
2
  import ZixflowMessagingInApp
3
3
  import Foundation
4
4
  import React
5
+ import SwiftUI
5
6
 
6
7
  // In-app messaging module for new React Native architecture (TurboModule)
7
8
  @objc(NativeZixflowMessagingInApp)
@@ -50,6 +51,48 @@ public class NativeMessagingInApp: NSObject {
50
51
  // Clear in-app event listener to prevent leaks
51
52
  clearInAppEventListener()
52
53
  clearInboxChangeListener()
54
+ clearInboxEventListener()
55
+ }
56
+
57
+ // MARK: - Inbox Event Listener
58
+
59
+ /// Registers the native inbox event forwarder with the SDK and wires it to the ObjC event
60
+ /// emitter. Called by React Native when a JS inbox event listener is registered.
61
+ @objc(registerInboxEventListener)
62
+ public func registerInboxEventListener() {
63
+ ReactInboxEventListener.shared.setEventEmitter { [weak self] data in
64
+ guard let self else { return }
65
+ // Filter out nil values to convert [String: Any?] to [String: Any]
66
+ let body = data.compactMapValues { $0 }
67
+ self.sendInboxEvent(body: body)
68
+ }
69
+ MessagingInApp.shared.setInboxEventListener(ReactInboxEventListener.shared)
70
+ }
71
+
72
+ /// Unregisters the native inbox event forwarder from the SDK, restoring the SDK's default action
73
+ /// handling. Called by React Native when the JS inbox event listener is removed.
74
+ @objc(unregisterInboxEventListener)
75
+ public func unregisterInboxEventListener() {
76
+ MessagingInApp.shared.setInboxEventListener(nil)
77
+ ReactInboxEventListener.shared.clearEventEmitter()
78
+ }
79
+
80
+ private func clearInboxEventListener() {
81
+ MessagingInApp.shared.setInboxEventListener(nil)
82
+ ReactInboxEventListener.shared.clearEventEmitter()
83
+ }
84
+
85
+ // Send inbox event to React Native layer using ObjC event emitter
86
+ private func sendInboxEvent(body: [String: Any]) {
87
+ guard let emitter = objcEventEmitter else {
88
+ return
89
+ }
90
+
91
+ let selector = Selector(("emitOnInboxEventReceived:"))
92
+ guard emitter.responds(to: selector) else {
93
+ return
94
+ }
95
+ _ = emitter.perform(selector, with: body as NSDictionary)
53
96
  }
54
97
 
55
98
  /// Dismisses any currently displayed in-app message
@@ -58,6 +101,54 @@ public class NativeMessagingInApp: NSObject {
58
101
  MessagingInApp.shared.dismissMessage()
59
102
  }
60
103
 
104
+ /// Overrides the color scheme used to render in-app messages.
105
+ ///
106
+ /// Receives the raw value of the JavaScript `ZixflowColorScheme`, since the enum itself cannot
107
+ /// cross Codegen. An unrecognized value leaves the current scheme alone instead of resetting
108
+ /// it to `.auto`, so a typo cannot quietly undo a scheme the app set correctly earlier — the
109
+ /// Android bridge behaves the same way.
110
+ ///
111
+ /// The parameter is optional even though Codegen declares it non-null: untyped JavaScript can
112
+ /// still pass `null` or `undefined`, which the bridge forwards as `nil`. Bridging that into a
113
+ /// non-optional `String` would trap before the value could be reported, and Android accepts a
114
+ /// nullable argument for the same reason, so a non-optional type here would also leave the two
115
+ /// platforms behaving differently on the same input.
116
+ @objc(setColorScheme:)
117
+ public func setColorScheme(_ colorScheme: String?) {
118
+ guard let resolved = Self.colorScheme(fromRawValue: colorScheme) else {
119
+ logger.error(
120
+ "Unrecognized in-app colorScheme '\(colorScheme ?? "nil")', expected one of auto, light, dark. Leaving the color scheme unchanged."
121
+ )
122
+ return
123
+ }
124
+ // Without this the call is dropped in silence: `setColorScheme` forwards through the
125
+ // module's `implementation?`, which is nil until the SDK is initialized, and the only
126
+ // trace is an `.info` line the default `.error` log level discards. Android logs this
127
+ // case at error, so reporting it here is what keeps the two platforms diagnosable in the
128
+ // same way. Logged rather than failed, again to match Android, which completes the call.
129
+ guard MessagingInApp.shared.hasBeenInitialized else {
130
+ logger.error(
131
+ "In-app messaging is not available, so the color scheme was not applied. Ensure Zixflow SDK is initialized with the inApp configuration."
132
+ )
133
+ return
134
+ }
135
+ MessagingInApp.shared.setColorScheme(resolved)
136
+ }
137
+
138
+ /// Maps the wrapper's lowercase wire value onto the native `ColorScheme`.
139
+ ///
140
+ /// Matched explicitly rather than handed to `MessagingInAppConfigBuilder`, which resolves
141
+ /// anything unrecognized to `.auto`; here an unrecognized value has to stay distinguishable
142
+ /// so it can be reported.
143
+ private static func colorScheme(fromRawValue rawValue: String?) -> ColorScheme? {
144
+ switch rawValue {
145
+ case "auto": return .auto
146
+ case "light": return .light
147
+ case "dark": return .dark
148
+ default: return nil
149
+ }
150
+ }
151
+
61
152
  // MARK: - Inbox Methods
62
153
 
63
154
  @objc(setupInboxListener)
@@ -0,0 +1,40 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Shared view-controller containment for the inbox `UIHostingController` hosts.
5
+ ///
6
+ /// Adopting only a hosting controller's `view` leaves the controller outside the view-controller
7
+ /// hierarchy. UIKit then has no presentation context or trait/safe-area chain for it, which showed up
8
+ /// as a phantom top margin in the overlay's sheet (the sheet content resolved insets against the
9
+ /// wrong container) and means no lifecycle callbacks ever reach the hosted SwiftUI. Native usage does
10
+ /// not hit this because there the hosting controller *is* a real pushed view controller.
11
+ ///
12
+ /// Containment has to be deferred until the Fabric view is actually in a window: React Native creates
13
+ /// component views before mounting them, so at `init` there is no parent controller to attach to.
14
+ enum InboxHostContainment {
15
+ /// Nearest view controller up the responder chain, or nil while the view is unmounted.
16
+ static func nearestViewController(from view: UIView?) -> UIViewController? {
17
+ var responder: UIResponder? = view
18
+ while let current = responder {
19
+ if let viewController = current as? UIViewController {
20
+ return viewController
21
+ }
22
+ responder = current.next
23
+ }
24
+ return nil
25
+ }
26
+
27
+ /// Attaches `child` to the controller owning `view`. No-op when already attached or unmounted.
28
+ static func attach(_ child: UIViewController, hostedIn view: UIView?) {
29
+ guard child.parent == nil, let parent = nearestViewController(from: view) else { return }
30
+ parent.addChild(child)
31
+ child.didMove(toParent: parent)
32
+ }
33
+
34
+ /// Detaches `child` from its parent. No-op when it was never attached.
35
+ static func detach(_ child: UIViewController) {
36
+ guard child.parent != nil else { return }
37
+ child.willMove(toParent: nil)
38
+ child.removeFromParent()
39
+ }
40
+ }
@@ -0,0 +1,12 @@
1
+ // See RCTInlineMessageNative.h for why this RCTViewComponentView import is guarded by __cplusplus.
2
+ #ifdef __cplusplus
3
+ #import <React/RCTViewComponentView.h>
4
+ #import <UIKit/UIKit.h>
5
+
6
+ NS_ASSUME_NONNULL_BEGIN
7
+
8
+ @interface RCTNotificationInboxBellNative : RCTViewComponentView
9
+ @end
10
+
11
+ NS_ASSUME_NONNULL_END
12
+ #endif
@@ -0,0 +1,98 @@
1
+ #import "RCTNotificationInboxBellNative.h"
2
+ #import "ReactNotificationInboxBellView.h"
3
+
4
+ #import <React/RCTConversions.h>
5
+ #import <React/RCTFabricComponentsPlugins.h>
6
+ #import <react/renderer/components/RNZixflowSpec/ComponentDescriptors.h>
7
+ #import <react/renderer/components/RNZixflowSpec/EventEmitters.h>
8
+ #import <react/renderer/components/RNZixflowSpec/Props.h>
9
+ #import <react/renderer/components/RNZixflowSpec/RCTComponentViewHelpers.h>
10
+ #import <react/renderer/components/RNZixflowSpec/ShadowNodes.h>
11
+
12
+ using namespace facebook::react;
13
+
14
+ /// New-architecture React Native view hosting the SwiftUI NotificationInboxBell.
15
+ @interface RCTNotificationInboxBellNative () <RCTNotificationInboxBellNativeViewProtocol>
16
+ @property(nonatomic, strong) id bridge;
17
+ @end
18
+
19
+ @implementation RCTNotificationInboxBellNative
20
+
21
+ - (void)assertBridgeAvailable:(NSString *)context {
22
+ NSAssert(self.bridge != nil, @"Bridge is nil when %@", context);
23
+ }
24
+
25
+ - (instancetype)init {
26
+ return [self initWithFrame:CGRectZero];
27
+ }
28
+
29
+ - (instancetype)initWithFrame:(CGRect)frame {
30
+ if (self = [super initWithFrame:frame]) {
31
+ _props = NotificationInboxBellNativeShadowNode::defaultSharedProps();
32
+
33
+ // The Swift bridge is @available(iOS 16.0, *) because the bell opens the SDK's inbox sheet, which
34
+ // uses system detents. Below iOS 16 the class exists but must not be instantiated, so the view
35
+ // stays empty rather than crashing — every method below therefore tolerates a nil bridge.
36
+ if (@available(iOS 16.0, *)) {
37
+ Class bridgeClass = NSClassFromString(@"ReactNotificationInboxBellView");
38
+ if (bridgeClass) {
39
+ self.bridge = [[bridgeClass alloc] initWithContainerView:self];
40
+ [self assertBridgeAvailable:@"creating ReactNotificationInboxBellView bridge instance"];
41
+ [self.bridge setEventEmitter:self];
42
+ }
43
+ }
44
+ }
45
+ return self;
46
+ }
47
+
48
+ - (void)layoutSubviews {
49
+ [super layoutSubviews];
50
+ if (self.bridge == nil) {
51
+ return;
52
+ }
53
+ [self.bridge updateLayout:[NSValue valueWithCGRect:self.bounds]];
54
+ }
55
+
56
+ // Containment can only happen once the view is in a window: React Native creates component views
57
+ // before mounting them, so at init there is no parent view controller to attach the host to.
58
+ - (void)didMoveToWindow {
59
+ [super didMoveToWindow];
60
+ if (self.bridge == nil) {
61
+ return;
62
+ }
63
+ if (self.window != nil) {
64
+ [self.bridge attachToParentViewController];
65
+ } else {
66
+ [self.bridge detachFromParentViewController];
67
+ }
68
+ }
69
+
70
+ - (void)prepareForRecycle {
71
+ [super prepareForRecycle];
72
+ if (self.bridge == nil) {
73
+ return;
74
+ }
75
+ [self.bridge prepareForRecycle];
76
+ }
77
+
78
+ - (void)emitOnTapEvent:(NSDictionary *)event {
79
+ if (_eventEmitter) {
80
+ NotificationInboxBellNativeEventEmitter::OnTap result =
81
+ NotificationInboxBellNativeEventEmitter::OnTap{};
82
+ self.eventEmitter.onTap(result);
83
+ }
84
+ }
85
+
86
+ - (const NotificationInboxBellNativeEventEmitter &)eventEmitter {
87
+ return static_cast<const NotificationInboxBellNativeEventEmitter &>(*_eventEmitter);
88
+ }
89
+
90
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
91
+ return concreteComponentDescriptorProvider<NotificationInboxBellNativeComponentDescriptor>();
92
+ }
93
+
94
+ @end
95
+
96
+ Class<RCTComponentViewProtocol> NotificationInboxBellNativeCls(void) {
97
+ return RCTNotificationInboxBellNative.class;
98
+ }
@@ -0,0 +1,12 @@
1
+ // See RCTInlineMessageNative.h for why this RCTViewComponentView import is guarded by __cplusplus.
2
+ #ifdef __cplusplus
3
+ #import <React/RCTViewComponentView.h>
4
+ #import <UIKit/UIKit.h>
5
+
6
+ NS_ASSUME_NONNULL_BEGIN
7
+
8
+ @interface RCTNotificationInboxViewNative : RCTViewComponentView
9
+ @end
10
+
11
+ NS_ASSUME_NONNULL_END
12
+ #endif
@@ -0,0 +1,78 @@
1
+ #import "RCTNotificationInboxViewNative.h"
2
+ #import "ReactNotificationInboxView.h"
3
+
4
+ #import <React/RCTConversions.h>
5
+ #import <React/RCTFabricComponentsPlugins.h>
6
+ #import <react/renderer/components/RNZixflowSpec/ComponentDescriptors.h>
7
+ #import <react/renderer/components/RNZixflowSpec/EventEmitters.h>
8
+ #import <react/renderer/components/RNZixflowSpec/Props.h>
9
+ #import <react/renderer/components/RNZixflowSpec/RCTComponentViewHelpers.h>
10
+ #import <react/renderer/components/RNZixflowSpec/ShadowNodes.h>
11
+
12
+ using namespace facebook::react;
13
+
14
+ /// New-architecture React Native view hosting the SwiftUI NotificationInboxView (message list).
15
+ /// This view emits no events; message actions flow through the existing global InboxEventListener.
16
+ @interface RCTNotificationInboxViewNative () <RCTNotificationInboxViewNativeViewProtocol>
17
+ @property(nonatomic, strong) id bridge;
18
+ @end
19
+
20
+ @implementation RCTNotificationInboxViewNative
21
+
22
+ - (void)assertBridgeAvailable:(NSString *)context {
23
+ NSAssert(self.bridge != nil, @"Bridge is nil when %@", context);
24
+ }
25
+
26
+ - (instancetype)init {
27
+ return [self initWithFrame:CGRectZero];
28
+ }
29
+
30
+ - (instancetype)initWithFrame:(CGRect)frame {
31
+ if (self = [super initWithFrame:frame]) {
32
+ _props = NotificationInboxViewNativeShadowNode::defaultSharedProps();
33
+
34
+ Class bridgeClass = NSClassFromString(@"ReactNotificationInboxView");
35
+ if (bridgeClass) {
36
+ self.bridge = [[bridgeClass alloc] initWithContainerView:self];
37
+ [self assertBridgeAvailable:@"creating ReactNotificationInboxView bridge instance"];
38
+ [self.bridge setEventEmitter:self];
39
+ }
40
+ }
41
+ return self;
42
+ }
43
+
44
+ - (void)layoutSubviews {
45
+ [super layoutSubviews];
46
+ [self assertBridgeAvailable:@"during layoutSubviews"];
47
+ [self.bridge updateLayout:[NSValue valueWithCGRect:self.bounds]];
48
+ }
49
+
50
+ // Containment can only happen once the view is in a window: React Native creates component views
51
+ // before mounting them, so at init there is no parent view controller to attach the host to. The list
52
+ // presents nothing, but containment is what gives the hosted SwiftUI a correct trait/safe-area chain
53
+ // and lifecycle callbacks.
54
+ - (void)didMoveToWindow {
55
+ [super didMoveToWindow];
56
+ [self assertBridgeAvailable:@"during didMoveToWindow"];
57
+ if (self.window != nil) {
58
+ [self.bridge attachToParentViewController];
59
+ } else {
60
+ [self.bridge detachFromParentViewController];
61
+ }
62
+ }
63
+
64
+ - (void)prepareForRecycle {
65
+ [super prepareForRecycle];
66
+ [self assertBridgeAvailable:@"during prepareForRecycle"];
67
+ [self.bridge prepareForRecycle];
68
+ }
69
+
70
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
71
+ return concreteComponentDescriptorProvider<NotificationInboxViewNativeComponentDescriptor>();
72
+ }
73
+
74
+ @end
75
+
76
+ Class<RCTComponentViewProtocol> NotificationInboxViewNativeCls(void) {
77
+ return RCTNotificationInboxViewNative.class;
78
+ }
@@ -0,0 +1,76 @@
1
+ import ZixflowInternalCommon
2
+ import ZixflowMessagingInApp
3
+
4
+ /**
5
+ * React Native bridge for Zixflow Visual Notification Inbox events.
6
+ *
7
+ * Registered on the SDK (via `MessagingInApp.shared.setInboxEventListener`) only while a JS listener
8
+ * is registered. Because the New-Arch event emitter is one-way we cannot round-trip a bool back from
9
+ * JS, so `messageActionTaken` forwards the event fire-and-forget and RETURNS `true` — telling the SDK
10
+ * the host handled the action so the SDK suppresses its default navigation. The JS host owns action
11
+ * handling while a listener is registered. Observational callbacks forward fire-and-forget.
12
+ */
13
+ public class ReactInboxEventListener: InboxEventListener {
14
+ // Shared instance for global access
15
+ public static let shared = ReactInboxEventListener()
16
+
17
+ private init() {}
18
+
19
+ // Event emitter function to send events to React Native layer
20
+ private var eventEmitter: (([String: Any?]) -> Void)?
21
+
22
+ // Sets the event emitter function
23
+ func setEventEmitter(_ emitter: (([String: Any?]) -> Void)?) {
24
+ eventEmitter = emitter
25
+ }
26
+
27
+ // Clears the event emitter to prevent memory leaks
28
+ func clearEventEmitter() {
29
+ eventEmitter = nil
30
+ }
31
+
32
+ // Emits an inbox message event to React Native with a serialized message payload
33
+ private func emitInboxEvent(
34
+ eventType: String,
35
+ message: InboxMessage,
36
+ actionName: String? = nil,
37
+ actionValue: String? = nil
38
+ ) {
39
+ var data: [String: Any?] = [
40
+ ZixflowConstants.eventType: eventType,
41
+ ZixflowConstants.inboxMessage: message.toDictionary()
42
+ ]
43
+
44
+ if let actionName = actionName {
45
+ data[ZixflowConstants.actionName] = actionName
46
+ }
47
+ if let actionValue = actionValue {
48
+ data[ZixflowConstants.actionValue] = actionValue
49
+ }
50
+
51
+ eventEmitter?(data)
52
+ }
53
+
54
+ public func messageActionTaken(message: InboxMessage, actionName: String, actionValue: String) -> Bool {
55
+ emitInboxEvent(
56
+ eventType: ZixflowConstants.messageActionTaken,
57
+ message: message,
58
+ actionName: actionName,
59
+ actionValue: actionValue
60
+ )
61
+ // Host (React Native) owns action handling while a listener is registered.
62
+ return true
63
+ }
64
+
65
+ public func messageShown(message: InboxMessage) {
66
+ emitInboxEvent(eventType: ZixflowConstants.messageShown, message: message)
67
+ }
68
+
69
+ public func messageOpened(message: InboxMessage) {
70
+ emitInboxEvent(eventType: ZixflowConstants.messageOpened, message: message)
71
+ }
72
+
73
+ public func messageDismissed(message: InboxMessage) {
74
+ emitInboxEvent(eventType: ZixflowConstants.messageDismissed, message: message)
75
+ }
76
+ }
@@ -0,0 +1,22 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @class UIView;
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /**
8
+ * Objective-C forward declaration for the Swift `ReactNotificationInboxBellView` class.
9
+ * See `ReactNotificationInboxOverlayView.h` for the rationale behind runtime resolution.
10
+ */
11
+ @interface ReactNotificationInboxBellView : NSObject
12
+
13
+ - (instancetype)initWithContainerView:(UIView *)containerView;
14
+ - (void)setEventEmitter:(id)eventEmitter;
15
+ - (void)updateLayout:(NSValue *)boundsValue;
16
+ - (void)attachToParentViewController;
17
+ - (void)detachFromParentViewController;
18
+ - (void)prepareForRecycle;
19
+
20
+ @end
21
+
22
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,109 @@
1
+ import ZixflowMessagingInbox
2
+ import Foundation
3
+ import SwiftUI
4
+ import UIKit
5
+
6
+ /// React Native wrapper hosting the SwiftUI `NotificationInboxOverlay` inside a `UIHostingController`.
7
+ ///
8
+ /// Despite the name, the hosted composition is the overlay rather than the bare `NotificationInboxBell`:
9
+ /// only the overlay ties the bell to the SDK's own inbox sheet, and the wrapper deliberately does not
10
+ /// reimplement panel presentation. Sized to the frame React Native assigns, that composition *is* a bell
11
+ /// that opens the inbox — the component wrappers expose.
12
+ ///
13
+ /// Remote branding still styles the bell (colors, icon). Branding's bell *position* has no effect here:
14
+ /// alignment resolves inside this view's frame, so the host owns placement via `style`.
15
+ ///
16
+ /// iOS 16+ because the panel is a sheet with system detents.
17
+ @available(iOS 16.0, *)
18
+ @objc(ReactNotificationInboxBellView)
19
+ class ReactNotificationInboxBellView: NSObject {
20
+ private weak var eventEmitter: AnyObject?
21
+ private weak var containerView: UIView?
22
+ private let hostingController: UIHostingController<NotificationInboxOverlay>
23
+
24
+ @objc
25
+ init(containerView: UIView) {
26
+ self.containerView = containerView
27
+ self.hostingController = UIHostingController(rootView: NotificationInboxOverlay())
28
+ super.init()
29
+
30
+ let hostedView = hostingController.view!
31
+ hostedView.backgroundColor = .clear
32
+ hostedView.translatesAutoresizingMaskIntoConstraints = false
33
+ containerView.addSubview(hostedView)
34
+ NSLayoutConstraint.activate([
35
+ hostedView.topAnchor.constraint(equalTo: containerView.topAnchor),
36
+ hostedView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
37
+ hostedView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
38
+ hostedView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)
39
+ ])
40
+
41
+ // Observational tap reporting. The SDK owns presentation, so this recognizer must not consume
42
+ // the touch: `cancelsTouchesInView = false` lets SwiftUI still receive it and open the sheet.
43
+ // It reports any tap inside this view's frame, which is the bell when the view is sized to it.
44
+ let tapObserver = UITapGestureRecognizer(target: self, action: #selector(handleObservedTap))
45
+ tapObserver.cancelsTouchesInView = false
46
+ tapObserver.delaysTouchesEnded = false
47
+ containerView.addGestureRecognizer(tapObserver)
48
+ }
49
+
50
+ @objc
51
+ func setEventEmitter(_ eventEmitter: AnyObject?) {
52
+ self.eventEmitter = eventEmitter
53
+ }
54
+
55
+ /// Adds the hosting controller to the view-controller hierarchy once the view is in a window.
56
+ ///
57
+ /// Without this the controller is orphaned, and the sheet it presents resolves its safe-area insets
58
+ /// against the wrong container — which showed up as a phantom top margin when dragging the sheet.
59
+ @objc
60
+ func attachToParentViewController() {
61
+ InboxHostContainment.attach(hostingController, hostedIn: containerView)
62
+ }
63
+
64
+ @objc
65
+ func detachFromParentViewController() {
66
+ InboxHostContainment.detach(hostingController)
67
+ }
68
+
69
+ @objc
70
+ func updateLayout(_ boundsValue: NSValue) {
71
+ hostingController.view.frame = boundsValue.cgRectValue
72
+ hostingController.view.setNeedsLayout()
73
+ hostingController.view.layoutIfNeeded()
74
+ }
75
+
76
+ @objc
77
+ func prepareForRecycle() {
78
+ // Deliberately does NOT remove the hosted view from its container.
79
+ //
80
+ // Fabric recycles the component view, and detaching the SwiftUI view here with nothing to
81
+ // re-attach it made a reused view come back blank. `ReactInlineMessageView` does not touch the
82
+ // hierarchy either. Containment is released here because a recycled view is leaving its parent.
83
+ detachFromParentViewController()
84
+ }
85
+
86
+ // MARK: - Event Emission
87
+
88
+ @objc
89
+ private func handleObservedTap() {
90
+ emitEvent("emitOnTapEvent:", payload: [:])
91
+ }
92
+
93
+ /// Mirrors `ReactInlineMessageView.emitEvent`: asserts rather than returning silently, so a
94
+ /// mis-wired emitter surfaces in debug instead of the tap simply never reaching JS.
95
+ private func emitEvent(_ selectorName: String, payload: [String: Any?]) {
96
+ guard let emitter = eventEmitter else {
97
+ assertionFailure("Event emitter is nil when trying to emit \(selectorName)")
98
+ return
99
+ }
100
+
101
+ let selector = Selector((selectorName))
102
+ guard emitter.responds(to: selector) else {
103
+ assertionFailure("Event emitter does not respond to selector: \(selectorName)")
104
+ return
105
+ }
106
+
107
+ _ = emitter.perform(selector, with: payload as NSDictionary)
108
+ }
109
+ }
@@ -0,0 +1,22 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @class UIView;
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /**
8
+ * Objective-C forward declaration for the Swift `ReactNotificationInboxView` class.
9
+ * See `ReactNotificationInboxOverlayView.h` for the rationale behind runtime resolution.
10
+ */
11
+ @interface ReactNotificationInboxView : NSObject
12
+
13
+ - (instancetype)initWithContainerView:(UIView *)containerView;
14
+ - (void)setEventEmitter:(id)eventEmitter;
15
+ - (void)updateLayout:(NSValue *)boundsValue;
16
+ - (void)attachToParentViewController;
17
+ - (void)detachFromParentViewController;
18
+ - (void)prepareForRecycle;
19
+
20
+ @end
21
+
22
+ NS_ASSUME_NONNULL_END