zixflow-reactnative 1.1.3 → 1.1.4
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.
- package/README.md +631 -261
- package/android/build.gradle +18 -1
- package/android/consumer-rules.pro +3 -2
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/zixflow/reactnative/sdk/NativeZixflowModule.kt +40 -5
- package/android/src/main/java/com/zixflow/reactnative/sdk/ZixflowReactNativePackage.kt +25 -1
- package/android/src/main/java/com/zixflow/reactnative/sdk/constant/Keys.kt +19 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/geofence/NativeGeofenceModule.kt +65 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/liveactivities/NativeLiveActivitiesModule.kt +310 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +150 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/ReactInboxEventListener.kt +99 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxBellViewManager.kt +38 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/NotificationInboxViewManager.kt +30 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactComposeHostView.kt +96 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxBellView.kt +48 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginbox/ReactNotificationInboxView.kt +17 -0
- package/android/src/main/java/com/zixflow/reactnative/sdk/messagingpush/NativeMessagingPushModule.kt +11 -1
- package/android/src/main/res/values/zixflow_config.xml +1 -1
- package/android/zixflow-core.gradle +17 -7
- package/docs/cocoapods-deployment-target-normalization.md +139 -0
- package/ios/cocoapods_deployment_target.rb +432 -0
- package/ios/wrappers/NativeZixflow.mm +37 -0
- package/ios/wrappers/NativeZixflow.swift +135 -1
- package/ios/wrappers/ZixflowReactNativeDeepLinkRequestStore.swift +123 -0
- package/ios/wrappers/ZixflowReactNativeDeepLinkRouter.swift +312 -0
- package/ios/wrappers/geofence/NativeGeofence.mm +46 -0
- package/ios/wrappers/geofence/NativeGeofence.swift +27 -0
- package/ios/wrappers/inapp/NativeMessagingInApp.mm +22 -0
- package/ios/wrappers/inapp/NativeMessagingInApp.swift +90 -0
- package/ios/wrappers/inbox/InboxHostContainment.swift +40 -0
- package/ios/wrappers/inbox/RCTNotificationInboxBellNative.h +12 -0
- package/ios/wrappers/inbox/RCTNotificationInboxBellNative.mm +98 -0
- package/ios/wrappers/inbox/RCTNotificationInboxViewNative.h +12 -0
- package/ios/wrappers/inbox/RCTNotificationInboxViewNative.mm +78 -0
- package/ios/wrappers/inbox/ReactInboxEventListener.swift +76 -0
- package/ios/wrappers/inbox/ReactNotificationInboxBellView.h +22 -0
- package/ios/wrappers/inbox/ReactNotificationInboxBellView.swift +109 -0
- package/ios/wrappers/inbox/ReactNotificationInboxView.h +22 -0
- package/ios/wrappers/inbox/ReactNotificationInboxView.swift +69 -0
- package/ios/wrappers/liveactivities/NativeLiveActivities.mm +83 -0
- package/ios/wrappers/liveactivities/NativeLiveActivities.swift +390 -0
- package/ios/wrappers/location/NativeLocation.swift +7 -5
- package/ios/wrappers/utils/ZixflowConstants.swift +4 -0
- package/ios/wrappers/zixflow_reactnative.h +6 -0
- package/lib/commonjs/components/NotificationInboxBellView.js +49 -0
- package/lib/commonjs/components/NotificationInboxBellView.js.map +1 -0
- package/lib/commonjs/components/NotificationInboxView.js +36 -0
- package/lib/commonjs/components/NotificationInboxView.js.map +1 -0
- package/lib/commonjs/components/index.js +14 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/index.js +81 -4
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/notification-inbox.js +1 -0
- package/lib/commonjs/notification-inbox.js.map +1 -1
- package/lib/commonjs/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
- package/lib/commonjs/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
- package/lib/commonjs/specs/modules/NativeZixflow.js.map +1 -1
- package/lib/commonjs/specs/modules/NativeZixflowGeofence.js +15 -0
- package/lib/commonjs/specs/modules/NativeZixflowGeofence.js.map +1 -0
- package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js +23 -0
- package/lib/commonjs/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
- package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js +3 -1
- package/lib/commonjs/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
- package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js +3 -1
- package/lib/commonjs/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
- package/lib/commonjs/types/data-pipelines.js +13 -1
- package/lib/commonjs/types/data-pipelines.js.map +1 -1
- package/lib/commonjs/types/in-app.js +24 -1
- package/lib/commonjs/types/in-app.js.map +1 -1
- package/lib/commonjs/types/inbox.js +79 -0
- package/lib/commonjs/types/inbox.js.map +1 -0
- package/lib/commonjs/types/index.js +22 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/commonjs/types/live-activities.js +85 -0
- package/lib/commonjs/types/live-activities.js.map +1 -0
- package/lib/commonjs/utils/param-validation.js +76 -1
- package/lib/commonjs/utils/param-validation.js.map +1 -1
- package/lib/commonjs/zixflow-cdp.js +99 -1
- package/lib/commonjs/zixflow-cdp.js.map +1 -1
- package/lib/commonjs/zixflow-geofence.js +55 -0
- package/lib/commonjs/zixflow-geofence.js.map +1 -0
- package/lib/commonjs/zixflow-inapp.js +120 -0
- package/lib/commonjs/zixflow-inapp.js.map +1 -1
- package/lib/commonjs/zixflow-liveactivities.js +85 -0
- package/lib/commonjs/zixflow-liveactivities.js.map +1 -0
- package/lib/module/components/NotificationInboxBellView.js +44 -0
- package/lib/module/components/NotificationInboxBellView.js.map +1 -0
- package/lib/module/components/NotificationInboxView.js +31 -0
- package/lib/module/components/NotificationInboxView.js.map +1 -0
- package/lib/module/components/index.js +5 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/index.js +8 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/notification-inbox.js +1 -1
- package/lib/module/notification-inbox.js.map +1 -1
- package/lib/module/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
- package/lib/module/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
- package/lib/module/specs/modules/NativeZixflow.js.map +1 -1
- package/lib/module/specs/modules/NativeZixflowGeofence.js +13 -0
- package/lib/module/specs/modules/NativeZixflowGeofence.js.map +1 -0
- package/lib/module/specs/modules/NativeZixflowLiveActivities.js +21 -0
- package/lib/module/specs/modules/NativeZixflowLiveActivities.js.map +1 -0
- package/lib/module/specs/modules/NativeZixflowMessagingInApp.js +3 -1
- package/lib/module/specs/modules/NativeZixflowMessagingInApp.js.map +1 -1
- package/lib/module/specs/modules/NativeZixflowMessagingPush.js +3 -1
- package/lib/module/specs/modules/NativeZixflowMessagingPush.js.map +1 -1
- package/lib/module/types/data-pipelines.js +13 -0
- package/lib/module/types/data-pipelines.js.map +1 -1
- package/lib/module/types/in-app.js +24 -0
- package/lib/module/types/in-app.js.map +1 -1
- package/lib/module/types/inbox.js +75 -0
- package/lib/module/types/inbox.js.map +1 -0
- package/lib/module/types/index.js +2 -0
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/live-activities.js +88 -0
- package/lib/module/types/live-activities.js.map +1 -0
- package/lib/module/utils/param-validation.js +76 -1
- package/lib/module/utils/param-validation.js.map +1 -1
- package/lib/module/zixflow-cdp.js +99 -1
- package/lib/module/zixflow-cdp.js.map +1 -1
- package/lib/module/zixflow-geofence.js +50 -0
- package/lib/module/zixflow-geofence.js.map +1 -0
- package/lib/module/zixflow-inapp.js +99 -1
- package/lib/module/zixflow-inapp.js.map +1 -1
- package/lib/module/zixflow-liveactivities.js +80 -0
- package/lib/module/zixflow-liveactivities.js.map +1 -0
- package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts +39 -0
- package/lib/typescript/commonjs/components/NotificationInboxBellView.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/NotificationInboxView.d.ts +25 -0
- package/lib/typescript/commonjs/components/NotificationInboxView.d.ts.map +1 -0
- package/lib/typescript/commonjs/components/index.d.ts +4 -0
- package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +8 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/notification-inbox.d.ts +5 -0
- package/lib/typescript/commonjs/notification-inbox.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
- package/lib/typescript/commonjs/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
- package/lib/typescript/commonjs/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts +6 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflow.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts +13 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
- package/lib/typescript/commonjs/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/data-pipelines.d.ts +53 -1
- package/lib/typescript/commonjs/types/data-pipelines.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/in-app.d.ts +22 -0
- package/lib/typescript/commonjs/types/in-app.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/inbox.d.ts +97 -0
- package/lib/typescript/commonjs/types/inbox.d.ts.map +1 -0
- package/lib/typescript/commonjs/types/index.d.ts +2 -0
- package/lib/typescript/commonjs/types/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/types/live-activities.d.ts +161 -0
- package/lib/typescript/commonjs/types/live-activities.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/param-validation.d.ts +7 -0
- package/lib/typescript/commonjs/utils/param-validation.d.ts.map +1 -1
- package/lib/typescript/commonjs/zixflow-cdp.d.ts +38 -0
- package/lib/typescript/commonjs/zixflow-cdp.d.ts.map +1 -1
- package/lib/typescript/commonjs/zixflow-geofence.d.ts +36 -0
- package/lib/typescript/commonjs/zixflow-geofence.d.ts.map +1 -0
- package/lib/typescript/commonjs/zixflow-inapp.d.ts +36 -2
- package/lib/typescript/commonjs/zixflow-inapp.d.ts.map +1 -1
- package/lib/typescript/commonjs/zixflow-liveactivities.d.ts +66 -0
- package/lib/typescript/commonjs/zixflow-liveactivities.d.ts.map +1 -0
- package/lib/typescript/module/components/NotificationInboxBellView.d.ts +39 -0
- package/lib/typescript/module/components/NotificationInboxBellView.d.ts.map +1 -0
- package/lib/typescript/module/components/NotificationInboxView.d.ts +25 -0
- package/lib/typescript/module/components/NotificationInboxView.d.ts.map +1 -0
- package/lib/typescript/module/components/index.d.ts +4 -0
- package/lib/typescript/module/components/index.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +8 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/notification-inbox.d.ts +5 -0
- package/lib/typescript/module/notification-inbox.d.ts.map +1 -1
- package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts +13 -0
- package/lib/typescript/module/specs/components/NotificationInboxBellNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts +12 -0
- package/lib/typescript/module/specs/components/NotificationInboxViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/specs/modules/NativeZixflow.d.ts +6 -1
- package/lib/typescript/module/specs/modules/NativeZixflow.d.ts.map +1 -1
- package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts +13 -0
- package/lib/typescript/module/specs/modules/NativeZixflowGeofence.d.ts.map +1 -0
- package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts +31 -0
- package/lib/typescript/module/specs/modules/NativeZixflowLiveActivities.d.ts.map +1 -0
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts +5 -1
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingInApp.d.ts.map +1 -1
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts +1 -1
- package/lib/typescript/module/specs/modules/NativeZixflowMessagingPush.d.ts.map +1 -1
- package/lib/typescript/module/types/data-pipelines.d.ts +53 -1
- package/lib/typescript/module/types/data-pipelines.d.ts.map +1 -1
- package/lib/typescript/module/types/in-app.d.ts +22 -0
- package/lib/typescript/module/types/in-app.d.ts.map +1 -1
- package/lib/typescript/module/types/inbox.d.ts +97 -0
- package/lib/typescript/module/types/inbox.d.ts.map +1 -0
- package/lib/typescript/module/types/index.d.ts +2 -0
- package/lib/typescript/module/types/index.d.ts.map +1 -1
- package/lib/typescript/module/types/live-activities.d.ts +161 -0
- package/lib/typescript/module/types/live-activities.d.ts.map +1 -0
- package/lib/typescript/module/utils/param-validation.d.ts +7 -0
- package/lib/typescript/module/utils/param-validation.d.ts.map +1 -1
- package/lib/typescript/module/zixflow-cdp.d.ts +38 -0
- package/lib/typescript/module/zixflow-cdp.d.ts.map +1 -1
- package/lib/typescript/module/zixflow-geofence.d.ts +36 -0
- package/lib/typescript/module/zixflow-geofence.d.ts.map +1 -0
- package/lib/typescript/module/zixflow-inapp.d.ts +36 -2
- package/lib/typescript/module/zixflow-inapp.d.ts.map +1 -1
- package/lib/typescript/module/zixflow-liveactivities.d.ts +66 -0
- package/lib/typescript/module/zixflow-liveactivities.d.ts.map +1 -0
- package/package.json +11 -2
- package/src/components/NotificationInboxBellView.tsx +55 -0
- package/src/components/NotificationInboxView.tsx +30 -0
- package/src/components/index.ts +6 -0
- package/src/index.ts +8 -3
- package/src/notification-inbox.ts +1 -1
- package/src/specs/components/NotificationInboxBellNativeComponent.ts +24 -0
- package/src/specs/components/NotificationInboxViewNativeComponent.ts +22 -0
- package/src/specs/modules/NativeZixflow.ts +9 -1
- package/src/specs/modules/NativeZixflowGeofence.ts +14 -0
- package/src/specs/modules/NativeZixflowLiveActivities.ts +39 -0
- package/src/specs/modules/NativeZixflowMessagingInApp.ts +12 -1
- package/src/specs/modules/NativeZixflowMessagingPush.ts +3 -1
- package/src/types/data-pipelines.ts +54 -1
- package/src/types/in-app.ts +23 -0
- package/src/types/inbox.ts +110 -0
- package/src/types/index.ts +2 -0
- package/src/types/live-activities.ts +170 -0
- package/src/utils/param-validation.ts +103 -0
- package/src/zixflow-cdp.ts +117 -1
- package/src/zixflow-geofence.ts +56 -0
- package/src/zixflow-inapp.ts +125 -2
- package/src/zixflow-liveactivities.ts +86 -0
- package/zixflow-reactnative-richpush.podspec +4 -1
- 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
|
}
|
|
@@ -50,6 +50,48 @@ public class NativeMessagingInApp: NSObject {
|
|
|
50
50
|
// Clear in-app event listener to prevent leaks
|
|
51
51
|
clearInAppEventListener()
|
|
52
52
|
clearInboxChangeListener()
|
|
53
|
+
clearInboxEventListener()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// MARK: - Inbox Event Listener
|
|
57
|
+
|
|
58
|
+
/// Registers the native inbox event forwarder with the SDK and wires it to the ObjC event
|
|
59
|
+
/// emitter. Called by React Native when a JS inbox event listener is registered.
|
|
60
|
+
@objc(registerInboxEventListener)
|
|
61
|
+
public func registerInboxEventListener() {
|
|
62
|
+
ReactInboxEventListener.shared.setEventEmitter { [weak self] data in
|
|
63
|
+
guard let self else { return }
|
|
64
|
+
// Filter out nil values to convert [String: Any?] to [String: Any]
|
|
65
|
+
let body = data.compactMapValues { $0 }
|
|
66
|
+
self.sendInboxEvent(body: body)
|
|
67
|
+
}
|
|
68
|
+
MessagingInApp.shared.setInboxEventListener(ReactInboxEventListener.shared)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Unregisters the native inbox event forwarder from the SDK, restoring the SDK's default action
|
|
72
|
+
/// handling. Called by React Native when the JS inbox event listener is removed.
|
|
73
|
+
@objc(unregisterInboxEventListener)
|
|
74
|
+
public func unregisterInboxEventListener() {
|
|
75
|
+
MessagingInApp.shared.setInboxEventListener(nil)
|
|
76
|
+
ReactInboxEventListener.shared.clearEventEmitter()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private func clearInboxEventListener() {
|
|
80
|
+
MessagingInApp.shared.setInboxEventListener(nil)
|
|
81
|
+
ReactInboxEventListener.shared.clearEventEmitter()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Send inbox event to React Native layer using ObjC event emitter
|
|
85
|
+
private func sendInboxEvent(body: [String: Any]) {
|
|
86
|
+
guard let emitter = objcEventEmitter else {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let selector = Selector(("emitOnInboxEventReceived:"))
|
|
91
|
+
guard emitter.responds(to: selector) else {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
_ = emitter.perform(selector, with: body as NSDictionary)
|
|
53
95
|
}
|
|
54
96
|
|
|
55
97
|
/// Dismisses any currently displayed in-app message
|
|
@@ -58,6 +100,54 @@ public class NativeMessagingInApp: NSObject {
|
|
|
58
100
|
MessagingInApp.shared.dismissMessage()
|
|
59
101
|
}
|
|
60
102
|
|
|
103
|
+
/// Overrides the color scheme used to render in-app messages.
|
|
104
|
+
///
|
|
105
|
+
/// Receives the raw value of the JavaScript `ZixflowColorScheme`, since the enum itself cannot
|
|
106
|
+
/// cross Codegen. An unrecognized value leaves the current scheme alone instead of resetting
|
|
107
|
+
/// it to `.auto`, so a typo cannot quietly undo a scheme the app set correctly earlier — the
|
|
108
|
+
/// Android bridge behaves the same way.
|
|
109
|
+
///
|
|
110
|
+
/// The parameter is optional even though Codegen declares it non-null: untyped JavaScript can
|
|
111
|
+
/// still pass `null` or `undefined`, which the bridge forwards as `nil`. Bridging that into a
|
|
112
|
+
/// non-optional `String` would trap before the value could be reported, and Android accepts a
|
|
113
|
+
/// nullable argument for the same reason, so a non-optional type here would also leave the two
|
|
114
|
+
/// platforms behaving differently on the same input.
|
|
115
|
+
@objc(setColorScheme:)
|
|
116
|
+
public func setColorScheme(_ colorScheme: String?) {
|
|
117
|
+
guard let resolved = Self.colorScheme(fromRawValue: colorScheme) else {
|
|
118
|
+
logger.error(
|
|
119
|
+
"Unrecognized in-app colorScheme '\(colorScheme ?? "nil")', expected one of auto, light, dark. Leaving the color scheme unchanged."
|
|
120
|
+
)
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
// Without this the call is dropped in silence: `setColorScheme` forwards through the
|
|
124
|
+
// module's `implementation?`, which is nil until the SDK is initialized, and the only
|
|
125
|
+
// trace is an `.info` line the default `.error` log level discards. Android logs this
|
|
126
|
+
// case at error, so reporting it here is what keeps the two platforms diagnosable in the
|
|
127
|
+
// same way. Logged rather than failed, again to match Android, which completes the call.
|
|
128
|
+
guard MessagingInApp.shared.hasBeenInitialized else {
|
|
129
|
+
logger.error(
|
|
130
|
+
"In-app messaging is not available, so the color scheme was not applied. Ensure Zixflow SDK is initialized with the inApp configuration."
|
|
131
|
+
)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
MessagingInApp.shared.setColorScheme(resolved)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// Maps the wrapper's lowercase wire value onto the native `ColorScheme`.
|
|
138
|
+
///
|
|
139
|
+
/// Matched explicitly rather than handed to `MessagingInAppConfigBuilder`, which resolves
|
|
140
|
+
/// anything unrecognized to `.auto`; here an unrecognized value has to stay distinguishable
|
|
141
|
+
/// so it can be reported.
|
|
142
|
+
private static func colorScheme(fromRawValue rawValue: String?) -> ColorScheme? {
|
|
143
|
+
switch rawValue {
|
|
144
|
+
case "auto": return .auto
|
|
145
|
+
case "light": return .light
|
|
146
|
+
case "dark": return .dark
|
|
147
|
+
default: return nil
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
61
151
|
// MARK: - Inbox Methods
|
|
62
152
|
|
|
63
153
|
@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
|