zixflow-reactnative 1.1.2 → 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 +2224 -322
- 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 +69 -24
- 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 -3
- 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/logging/NativeZixflowLoggingModule.kt +2 -2
- package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/NativeMessagingInAppModule.kt +149 -10
- 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/ZixflowConfigUtils.swift +5 -9
- 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 +15 -15
- 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 +77 -2
- package/lib/commonjs/utils/param-validation.js.map +1 -1
- package/lib/commonjs/zixflow-cdp.js +101 -11
- 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/commonjs/zixflow.js.map +1 -1
- 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 +15 -15
- 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 +77 -2
- package/lib/module/utils/param-validation.js.map +1 -1
- package/lib/module/zixflow-cdp.js +101 -11
- 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/module/zixflow.js +0 -6
- package/lib/module/zixflow.js.map +1 -1
- 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 +54 -18
- 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/commonjs/zixflow.d.ts +0 -5
- package/lib/typescript/commonjs/zixflow.d.ts.map +1 -1
- 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 +54 -18
- 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/lib/typescript/module/zixflow.d.ts +0 -5
- package/lib/typescript/module/zixflow.d.ts.map +1 -1
- package/package.json +14 -5
- 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 +55 -19
- 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 +104 -1
- package/src/zixflow-cdp.ts +119 -13
- package/src/zixflow-geofence.ts +56 -0
- package/src/zixflow-inapp.ts +125 -2
- package/src/zixflow-liveactivities.ts +86 -0
- package/src/zixflow.ts +0 -6
- package/zixflow-reactnative-richpush.podspec +6 -3
- package/zixflow-reactnative.podspec +28 -2
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
package com.zixflow.reactnative.sdk.liveactivities
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import com.facebook.react.bridge.Promise
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
8
|
+
import com.zixflow.messagingpush.MessagingPushModuleConfig
|
|
9
|
+
import com.zixflow.messagingpush.ModuleMessagingPushFCM
|
|
10
|
+
import com.zixflow.messagingpush.data.communication.ZixflowLiveNotificationsCallback
|
|
11
|
+
import com.zixflow.messagingpush.di.pushModuleConfig
|
|
12
|
+
import com.zixflow.messagingpush.livenotification.LiveNotificationAsset
|
|
13
|
+
import com.zixflow.messagingpush.livenotification.LiveNotificationBranding
|
|
14
|
+
import com.zixflow.messagingpush.livenotification.LiveNotificationData
|
|
15
|
+
import com.zixflow.messagingpush.livenotification.LiveNotificationType
|
|
16
|
+
import com.zixflow.reactnative.sdk.NativeZixflowLiveActivitiesSpec
|
|
17
|
+
import com.zixflow.reactnative.sdk.extension.getTypedValue
|
|
18
|
+
import com.zixflow.sdk.core.di.SDKComponent
|
|
19
|
+
import com.zixflow.sdk.core.util.Logger
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* React Native module implementation for Zixflow Live Activities / Live Notifications
|
|
23
|
+
* using TurboModules with new architecture.
|
|
24
|
+
*
|
|
25
|
+
* Live Notifications live on the FCM push module ([ModuleMessagingPushFCM]); this bridge
|
|
26
|
+
* maps the wrapper's template payloads to [LiveNotificationData] and forwards them.
|
|
27
|
+
*/
|
|
28
|
+
@ReactModule(name = NativeLiveActivitiesModule.NAME)
|
|
29
|
+
class NativeLiveActivitiesModule(
|
|
30
|
+
private val reactContext: ReactApplicationContext,
|
|
31
|
+
) : NativeZixflowLiveActivitiesSpec(reactContext) {
|
|
32
|
+
private val logger: Logger
|
|
33
|
+
get() = SDKComponent.logger
|
|
34
|
+
|
|
35
|
+
// Live Notifications are hosted by the FCM push module. Reach it via the module registry
|
|
36
|
+
// (the wrapper can't reference the SDK-internal MODULE_NAME constant, so use the literal).
|
|
37
|
+
//
|
|
38
|
+
// Deliberately not runCatching: `as?` yields null instead of throwing, so a failure branch keyed
|
|
39
|
+
// on a thrown error would never run and the message below would never be logged.
|
|
40
|
+
private fun getPushModule(): ModuleMessagingPushFCM? {
|
|
41
|
+
val module = SDKComponent.modules[PUSH_FCM_MODULE_NAME] as? ModuleMessagingPushFCM
|
|
42
|
+
if (module == null) {
|
|
43
|
+
logger.error("Live Notifications: push module is not initialized. Ensure the SDK is initialized with live activity templates enabled.")
|
|
44
|
+
}
|
|
45
|
+
return module
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun handleWidgetUrl(url: String?, promise: Promise?) {
|
|
49
|
+
// Zixflow Live Activity widget URLs are an iOS-only contract. Preserve every Android
|
|
50
|
+
// URL so a shared Expo Router redirectSystemPath implementation cannot suppress routing.
|
|
51
|
+
promise?.resolve(url)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override fun start(payload: ReadableMap?, promise: Promise?) {
|
|
55
|
+
val module = getPushModule() ?: return promise.rejectNotAvailable()
|
|
56
|
+
try {
|
|
57
|
+
val map = requireNotNull(payload) { "payload is required" }
|
|
58
|
+
// Custom types have no built-in template, so they take the SDK's untyped map path and are
|
|
59
|
+
// rendered by the app's own callback. Built-ins keep the typed path.
|
|
60
|
+
val isCustom = map.getString("type") == CUSTOM_TYPE_DISCRIMINATOR
|
|
61
|
+
val activityType = if (isCustom) {
|
|
62
|
+
requireCustomActivityType()
|
|
63
|
+
} else {
|
|
64
|
+
requireNotNull(map.getString("type")) { "payload.type is required" }
|
|
65
|
+
}
|
|
66
|
+
if (activityType !in enabledActivityTypes()) {
|
|
67
|
+
return promise.rejectNotRegistered(activityType)
|
|
68
|
+
}
|
|
69
|
+
val id = if (isCustom) {
|
|
70
|
+
module.startLiveNotification(activityType, customData(map))
|
|
71
|
+
} else {
|
|
72
|
+
module.startLiveNotification(parseData(map))
|
|
73
|
+
}
|
|
74
|
+
promise?.resolve(id)
|
|
75
|
+
} catch (ex: Throwable) {
|
|
76
|
+
promise?.reject("live_activity_start_failed", ex.message, ex)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override fun update(activityId: String?, payload: ReadableMap?, promise: Promise?) {
|
|
81
|
+
val module = getPushModule() ?: return promise.rejectNotAvailable()
|
|
82
|
+
try {
|
|
83
|
+
val id = requireNotNull(activityId) { "activityId is required" }
|
|
84
|
+
val map = requireNotNull(payload) { "payload is required" }
|
|
85
|
+
if (map.getString("type") == CUSTOM_TYPE_DISCRIMINATOR) {
|
|
86
|
+
module.updateLiveNotification(id, requireCustomActivityType(), customData(map))
|
|
87
|
+
} else {
|
|
88
|
+
module.updateLiveNotification(id, parseData(map))
|
|
89
|
+
}
|
|
90
|
+
promise?.resolve(null)
|
|
91
|
+
} catch (ex: Throwable) {
|
|
92
|
+
promise?.reject("live_activity_update_failed", ex.message, ex)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Ends a live notification. [payload] is accepted for signature parity with iOS, where a final
|
|
98
|
+
* content-state is what makes ActivityKit render a terminal state; Android renders its own
|
|
99
|
+
* terminal state (the notification simply stops being ongoing), so it is ignored here.
|
|
100
|
+
*/
|
|
101
|
+
override fun end(activityId: String?, payload: ReadableMap?, promise: Promise?) {
|
|
102
|
+
val module = getPushModule() ?: return promise.rejectNotAvailable()
|
|
103
|
+
try {
|
|
104
|
+
module.endLiveNotification(requireNotNull(activityId) { "activityId is required" })
|
|
105
|
+
promise?.resolve(null)
|
|
106
|
+
} catch (ex: Throwable) {
|
|
107
|
+
promise?.reject("live_activity_end_failed", ex.message, ex)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The app's own identifier for the custom template. Absent means the app sent a custom payload
|
|
113
|
+
* without configuring `liveNotifications.customType` — say so, rather than starting a
|
|
114
|
+
* notification the allowlist would silently drop.
|
|
115
|
+
*/
|
|
116
|
+
/**
|
|
117
|
+
* Identifiers the SDK currently has enabled. Read from the live module config rather than cached
|
|
118
|
+
* at wrapper init: the config can also be built by generated native code that never calls
|
|
119
|
+
* [applyLiveActivitiesConfig] — the Expo config plugin does exactly that — and a cached set would
|
|
120
|
+
* be empty there, rejecting every start.
|
|
121
|
+
*/
|
|
122
|
+
private fun enabledActivityTypes(): Set<String> =
|
|
123
|
+
SDKComponent.pushModuleConfig.liveNotificationTypes
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The custom identifier, preferring what the wrapper saw and otherwise deriving it from the live
|
|
127
|
+
* config: exactly one custom type is supported, so it is the single enabled identifier that no
|
|
128
|
+
* built-in [LiveNotificationType] claims.
|
|
129
|
+
*/
|
|
130
|
+
private fun customActivityTypeOrNull(): String? = customActivityType
|
|
131
|
+
?: enabledActivityTypes().firstOrNull { identifier ->
|
|
132
|
+
LiveNotificationType.entries.none { it.identifier == identifier }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private fun requireCustomActivityType(): String = requireNotNull(customActivityTypeOrNull()) {
|
|
136
|
+
"No custom Live Activity type is configured. Set `liveNotifications.customType` in your " +
|
|
137
|
+
"Zixflow SDK config to your own reverse-DNS identifier, and render it from your " +
|
|
138
|
+
"ZixflowLiveNotificationsCallback."
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Flattens the payload's `data` map. Android stringifies every value downstream anyway. */
|
|
142
|
+
private fun customData(payload: ReadableMap): Map<String, Any?> =
|
|
143
|
+
payload.getMap("data")?.toHashMap() ?: emptyMap()
|
|
144
|
+
|
|
145
|
+
private fun parseData(payload: ReadableMap): LiveNotificationData {
|
|
146
|
+
return when (val type = payload.getString("type")) {
|
|
147
|
+
LiveNotificationType.SEGMENTS.identifier -> LiveNotificationData.Segments(
|
|
148
|
+
header = payload.requireString("header"),
|
|
149
|
+
status = payload.requireString("status"),
|
|
150
|
+
substatus = payload.optString("substatus"),
|
|
151
|
+
segmentsTotal = payload.requireDouble("segmentsTotal").toInt(),
|
|
152
|
+
segmentsComplete = payload.requireDouble("segmentsComplete").toInt(),
|
|
153
|
+
trailingText = payload.optString("trailingText"),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
LiveNotificationType.COUNTDOWN_TIMER.identifier -> LiveNotificationData.CountdownTimer(
|
|
157
|
+
header = payload.requireString("header"),
|
|
158
|
+
title = payload.requireString("title"),
|
|
159
|
+
statusMessage = payload.optString("statusMessage"),
|
|
160
|
+
endTime = if (payload.hasKey("endTime") && !payload.isNull("endTime")) {
|
|
161
|
+
payload.getDouble("endTime").toLong()
|
|
162
|
+
} else {
|
|
163
|
+
null
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
// A newer native SDK may know this type even though this wrapper build doesn't.
|
|
168
|
+
// Reject softly (the caller turns this into a rejected promise) rather than crash.
|
|
169
|
+
else -> throw IllegalArgumentException("Unsupported Live Activity template: $type")
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private fun ReadableMap.requireString(key: String): String =
|
|
174
|
+
requireNotNull(if (hasKey(key)) getString(key) else null) { "$key is required" }
|
|
175
|
+
|
|
176
|
+
private fun ReadableMap.optString(key: String): String? =
|
|
177
|
+
if (hasKey(key) && !isNull(key)) getString(key) else null
|
|
178
|
+
|
|
179
|
+
private fun ReadableMap.requireDouble(key: String): Double {
|
|
180
|
+
require(hasKey(key) && !isNull(key)) { "$key is required" }
|
|
181
|
+
return getDouble(key)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private fun Promise?.rejectNotAvailable() {
|
|
185
|
+
this?.reject(
|
|
186
|
+
"live_activity_module_unavailable",
|
|
187
|
+
"Live Notifications are unavailable. Enable live activity templates in the SDK config.",
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Mirrors iOS's `live_activity_type_not_registered` so the same mistake reads the same way. */
|
|
192
|
+
private fun Promise?.rejectNotRegistered(activityType: String?) {
|
|
193
|
+
this?.reject(
|
|
194
|
+
"live_activity_type_not_registered",
|
|
195
|
+
"Live Activity type '$activityType' is not registered. Add it to " +
|
|
196
|
+
"`liveNotifications.types` in your Zixflow SDK config (or set " +
|
|
197
|
+
"`liveNotifications.customType` for a custom type).",
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
companion object {
|
|
202
|
+
const val NAME = "NativeZixflowLiveActivities"
|
|
203
|
+
|
|
204
|
+
// The SDK's ModuleMessagingPushFCM.MODULE_NAME is `internal`, so we mirror its value here.
|
|
205
|
+
private const val PUSH_FCM_MODULE_NAME = "MessagingPushFCM"
|
|
206
|
+
|
|
207
|
+
// Discriminator JavaScript sends for the custom template. Not a wire identifier — the
|
|
208
|
+
// notification is started under the app's own `liveNotifications.customType`.
|
|
209
|
+
private const val CUSTOM_TYPE_DISCRIMINATOR = "custom"
|
|
210
|
+
|
|
211
|
+
// The app's own identifier for the custom template, captured from config at SDK init.
|
|
212
|
+
@Volatile
|
|
213
|
+
private var customActivityType: String? = null
|
|
214
|
+
|
|
215
|
+
// Host-app callback used to render custom (app-defined) live notifications. The native SDK
|
|
216
|
+
// only accepts it at build time, so the app must register it before the SDK initializes
|
|
217
|
+
// (e.g. in Application.onCreate before React Native starts). Stored statically because the
|
|
218
|
+
// SDK config is applied in applyLiveActivitiesConfig, not on this module instance.
|
|
219
|
+
@Volatile
|
|
220
|
+
private var liveNotificationCallback: ZixflowLiveNotificationsCallback? = null
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Register the host app's callback for rendering live notifications itself. Custom
|
|
224
|
+
* (app-defined) activity types have no built-in template, so the app must build the
|
|
225
|
+
* [android.app.Notification] in
|
|
226
|
+
* [ZixflowLiveNotificationsCallback.createLiveNotification]; returning `null` there
|
|
227
|
+
* falls back to the SDK's built-in template.
|
|
228
|
+
* Call this before the Zixflow SDK is initialized.
|
|
229
|
+
*/
|
|
230
|
+
@JvmStatic
|
|
231
|
+
fun setLiveNotificationCallback(callback: ZixflowLiveNotificationsCallback) {
|
|
232
|
+
liveNotificationCallback = callback
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Applies live activity configuration onto the FCM push module's config builder. Live
|
|
237
|
+
* Notifications are hosted by [ModuleMessagingPushFCM], so their config (enabled templates,
|
|
238
|
+
* custom types, branding) is set on the same [MessagingPushModuleConfig].
|
|
239
|
+
*
|
|
240
|
+
* @param builder the push module's config builder.
|
|
241
|
+
* @param config the `liveNotifications` config map from the customer app.
|
|
242
|
+
*/
|
|
243
|
+
internal fun applyLiveActivitiesConfig(
|
|
244
|
+
builder: MessagingPushModuleConfig.Builder,
|
|
245
|
+
config: Map<String, Any>,
|
|
246
|
+
) {
|
|
247
|
+
// Wire the host app's live-notification renderer, if one was registered.
|
|
248
|
+
liveNotificationCallback?.let { builder.setLiveNotificationCallback(it) }
|
|
249
|
+
|
|
250
|
+
// Unrecognized identifiers are ignored: a newer native SDK may ship types this
|
|
251
|
+
// wrapper build doesn't know, and that must never break the ones it does know.
|
|
252
|
+
val templateTypes = config.getTypedValue<List<*>>("types")
|
|
253
|
+
?.mapNotNull { it as? String }
|
|
254
|
+
?.mapNotNull { identifier ->
|
|
255
|
+
LiveNotificationType.entries.firstOrNull { it.identifier == identifier }
|
|
256
|
+
}
|
|
257
|
+
.orEmpty()
|
|
258
|
+
if (templateTypes.isNotEmpty()) {
|
|
259
|
+
builder.enableLiveNotificationTypes(*templateTypes.toTypedArray())
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// The custom template. Allowlisting the identifier is both necessary and sufficient:
|
|
263
|
+
// LiveNotificationHandler drops any push whose activityType isn't in this set, and a type
|
|
264
|
+
// with no built-in template falls through to the host app's render callback.
|
|
265
|
+
//
|
|
266
|
+
// Assigned unconditionally: a re-initialize that drops `customType` must clear it, or
|
|
267
|
+
// `start` would keep minting notifications under an identifier no longer allowlisted —
|
|
268
|
+
// which the handler then discards, leaving the caller with an id and nothing on screen.
|
|
269
|
+
val customType = config.getTypedValue<String>("customType")?.trim()?.takeIf { it.isNotEmpty() }
|
|
270
|
+
customActivityType = customType
|
|
271
|
+
if (customType != null) {
|
|
272
|
+
builder.enableCustomLiveNotificationTypes(customType)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
val branding = config.getTypedValue<Map<String, Any>>("branding")
|
|
276
|
+
if (branding != null) {
|
|
277
|
+
val accentColor = branding.getTypedValue<String>("accentColorHex")
|
|
278
|
+
?.let { runCatching { Color.parseColor(it) }.getOrNull() }
|
|
279
|
+
?: Color.TRANSPARENT
|
|
280
|
+
// A bundled drawable is preferred over a remote URL: it renders without a network
|
|
281
|
+
// round-trip, so the logo is present on the very first frame.
|
|
282
|
+
val logo = branding.getTypedValue<String>("logoResource")
|
|
283
|
+
?.let { name -> drawableResId(name)?.let(LiveNotificationAsset::Drawable) }
|
|
284
|
+
?: branding.getTypedValue<String>("logoUrl")
|
|
285
|
+
?.let(LiveNotificationAsset::RemoteUrl)
|
|
286
|
+
val smallIcon = branding.getTypedValue<String>("smallIconResource")
|
|
287
|
+
?.let { name -> drawableResId(name) }
|
|
288
|
+
builder.setLiveNotificationBranding(
|
|
289
|
+
LiveNotificationBranding(
|
|
290
|
+
companyName = branding.getTypedValue<String>("companyName").orEmpty(),
|
|
291
|
+
accentColor = accentColor,
|
|
292
|
+
smallIcon = smallIcon,
|
|
293
|
+
logo = logo,
|
|
294
|
+
),
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Resolve a bundled drawable by name, or `null` when the host app doesn't ship one under
|
|
301
|
+
* that name — a missing asset must degrade to "no image", never crash rendering.
|
|
302
|
+
*/
|
|
303
|
+
private fun drawableResId(name: String): Int? {
|
|
304
|
+
val context = SDKComponent.android().applicationContext
|
|
305
|
+
return context.resources
|
|
306
|
+
.getIdentifier(name, "drawable", context.packageName)
|
|
307
|
+
.takeIf { it != 0 }
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
package/android/src/main/java/com/zixflow/reactnative/sdk/logging/NativeZixflowLoggingModule.kt
CHANGED
|
@@ -6,7 +6,7 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
6
6
|
import com.facebook.react.bridge.ReadableMap
|
|
7
7
|
import com.zixflow.reactnative.sdk.NativeZixflowLoggingSpec
|
|
8
8
|
import com.zixflow.sdk.core.di.SDKComponent
|
|
9
|
-
import com.zixflow.sdk.core.util.
|
|
9
|
+
import com.zixflow.sdk.core.util.ZixflowLogLevel
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* React Native module implementation for Zixflow Logging Native SDK
|
|
@@ -72,7 +72,7 @@ class NativeZixflowLoggingModule(
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Converts native SDK log events to React Native compatible format and emits them.
|
|
75
|
-
private fun emitLogEvent(level:
|
|
75
|
+
private fun emitLogEvent(level: ZixflowLogLevel, message: String) {
|
|
76
76
|
// Defensive check: only emit if emitter is available
|
|
77
77
|
val emitter = logEventEmitter ?: return
|
|
78
78
|
|
|
@@ -10,6 +10,8 @@ import com.zixflow.messaginginapp.di.inAppMessaging
|
|
|
10
10
|
import com.zixflow.messaginginapp.gist.data.model.InboxMessage
|
|
11
11
|
import com.zixflow.messaginginapp.gist.data.model.response.InboxMessageFactory
|
|
12
12
|
import com.zixflow.messaginginapp.inbox.NotificationInbox
|
|
13
|
+
import com.zixflow.messaginginapp.type.ColorScheme
|
|
14
|
+
import com.zixflow.messaginginapp.type.NotificationInboxAccessibilityLabels
|
|
13
15
|
import com.zixflow.reactnative.sdk.NativeZixflowMessagingInAppSpec
|
|
14
16
|
import com.zixflow.reactnative.sdk.constant.Keys
|
|
15
17
|
import com.zixflow.reactnative.sdk.extension.getTypedValue
|
|
@@ -33,6 +35,7 @@ class NativeMessagingInAppModule(
|
|
|
33
35
|
|
|
34
36
|
private val inAppEventListener = ReactInAppEventListener.instance
|
|
35
37
|
private val inboxChangeListener = ReactNotificationInboxChangeListener.instance
|
|
38
|
+
private val inboxEventListener = ReactInboxEventListener.instance
|
|
36
39
|
|
|
37
40
|
// Dedicated lock for inbox listener setup to avoid blocking other operations
|
|
38
41
|
private val inboxListenerLock = Any()
|
|
@@ -63,6 +66,7 @@ class NativeMessagingInAppModule(
|
|
|
63
66
|
override fun invalidate() {
|
|
64
67
|
inAppEventListener.clearEventEmitter()
|
|
65
68
|
clearInboxChangeListener()
|
|
69
|
+
clearInboxEventListener()
|
|
66
70
|
super.invalidate()
|
|
67
71
|
}
|
|
68
72
|
|
|
@@ -70,10 +74,76 @@ class NativeMessagingInAppModule(
|
|
|
70
74
|
inAppMessagingModule?.dismissMessage()
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
// Nullable like every other argument-taking override here, and for the same reason: Codegen
|
|
78
|
+
// declares the parameter non-null, so a non-null Kotlin type would throw
|
|
79
|
+
// `Intrinsics.checkNotNullParameter` before the body runs when JavaScript passes null — which
|
|
80
|
+
// is what an untyped caller reading a stored theme sends on first launch.
|
|
81
|
+
override fun setColorScheme(colorScheme: String?) {
|
|
82
|
+
val resolved = colorSchemeFromRawValue(colorScheme)
|
|
83
|
+
if (resolved == null) {
|
|
84
|
+
// Leave the current scheme alone rather than resetting it to AUTO, so a bad value
|
|
85
|
+
// cannot quietly undo a scheme the app set correctly earlier.
|
|
86
|
+
logger.error(
|
|
87
|
+
"Unrecognized in-app colorScheme '$colorScheme', expected one of auto, light, " +
|
|
88
|
+
"dark. Leaving the color scheme unchanged."
|
|
89
|
+
)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
val module = inAppMessagingModule
|
|
93
|
+
if (module == null) {
|
|
94
|
+
// Reachable when the host calls this before Zixflow.initialize, or without the
|
|
95
|
+
// in-app module configured. Logged rather than ignored: the scheme is silently not
|
|
96
|
+
// applied, and nothing else surfaces that.
|
|
97
|
+
logger.error(
|
|
98
|
+
"In-app messaging is not available, so the color scheme was not applied. " +
|
|
99
|
+
"Ensure Zixflow SDK is initialized with the inApp configuration."
|
|
100
|
+
)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
module.setColorScheme(resolved)
|
|
104
|
+
}
|
|
105
|
+
|
|
73
106
|
override fun setupInboxListener() {
|
|
74
107
|
setupInboxChangeListener()
|
|
75
108
|
}
|
|
76
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Registers the native inbox event forwarder with the SDK and wires it to the React Native
|
|
112
|
+
* event emitter. Called when a JS inbox event listener is registered.
|
|
113
|
+
*/
|
|
114
|
+
override fun registerInboxEventListener() {
|
|
115
|
+
inboxEventListener.setEventEmitter { data ->
|
|
116
|
+
emitOnInboxEventReceived(data)
|
|
117
|
+
}
|
|
118
|
+
val module = inAppMessagingModule
|
|
119
|
+
if (module == null) {
|
|
120
|
+
// Without this the failure is invisible: the JS subscription reports success while the SDK
|
|
121
|
+
// never forwards anything, so inbox callbacks silently never arrive. Matches the guidance
|
|
122
|
+
// used by getMessages() for the same situation.
|
|
123
|
+
logger.error(
|
|
124
|
+
"Cannot register inbox event listener: in-app messaging is not available. " +
|
|
125
|
+
"Ensure Zixflow SDK is initialized before registering the listener."
|
|
126
|
+
)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
module.setInboxEventListener(inboxEventListener)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Unregisters the native inbox event forwarder by installing a no-op listener (the native API is
|
|
134
|
+
* non-null), restoring the SDK's default action handling. Called when the JS inbox event
|
|
135
|
+
* listener is removed.
|
|
136
|
+
*/
|
|
137
|
+
override fun unregisterInboxEventListener() {
|
|
138
|
+
inAppMessagingModule?.setInboxEventListener(NoOpInboxEventListener)
|
|
139
|
+
inboxEventListener.clearEventEmitter()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private fun clearInboxEventListener() {
|
|
143
|
+
inAppMessagingModule?.setInboxEventListener(NoOpInboxEventListener)
|
|
144
|
+
inboxEventListener.clearEventEmitter()
|
|
145
|
+
}
|
|
146
|
+
|
|
77
147
|
override fun getMessages(topic: String?, promise: Promise?) {
|
|
78
148
|
try {
|
|
79
149
|
val inbox = requireInboxInstance() ?: run {
|
|
@@ -191,25 +261,94 @@ class NativeMessagingInAppModule(
|
|
|
191
261
|
*
|
|
192
262
|
* @param builder ZixflowBuilder instance to add InAppMessaging module
|
|
193
263
|
* @param config Configuration provided by customer app for InAppMessaging module
|
|
194
|
-
* @param region Region to be used for InAppMessaging module
|
|
195
264
|
*/
|
|
196
265
|
internal fun addNativeModuleFromConfig(
|
|
197
266
|
builder: ZixflowBuilder,
|
|
198
|
-
config: Map<String, Any
|
|
199
|
-
region: Region
|
|
267
|
+
config: Map<String, Any>
|
|
200
268
|
) {
|
|
201
|
-
val siteId = config.getTypedValue<String>(Keys.Config.SITE_ID)
|
|
202
|
-
if (siteId.isNullOrBlank()) {
|
|
203
|
-
SDKComponent.logger.error("Site ID is required to initialize InAppMessaging module")
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
269
|
val module = ModuleMessagingInApp(
|
|
208
|
-
MessagingInAppModuleConfig.Builder(
|
|
270
|
+
MessagingInAppModuleConfig.Builder().apply {
|
|
209
271
|
setEventListener(eventListener = ReactInAppEventListener.instance)
|
|
272
|
+
colorSchemeFromConfig(config)?.let { colorScheme ->
|
|
273
|
+
setColorScheme(colorScheme)
|
|
274
|
+
}
|
|
275
|
+
inboxAccessibilityLabelsFromConfig(config)?.let { labels ->
|
|
276
|
+
setNotificationInboxAccessibilityLabels(labels)
|
|
277
|
+
}
|
|
210
278
|
}.build(),
|
|
211
279
|
)
|
|
212
280
|
builder.addZixflowModule(module)
|
|
213
281
|
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Maps the wrapper's `colorScheme` value onto the native [ColorScheme], or null when the
|
|
285
|
+
* value is absent or unrecognized.
|
|
286
|
+
*
|
|
287
|
+
* The accepted values are lowercase because that is the wire contract the JavaScript
|
|
288
|
+
* `ZixflowColorScheme` enum serializes to and the one iOS already matches. Kept free of
|
|
289
|
+
* logging so each caller can report a bad value in its own terms: at initialization an
|
|
290
|
+
* unrecognized value falls back to AUTO, while the runtime setter leaves the scheme the
|
|
291
|
+
* app already chose untouched.
|
|
292
|
+
*/
|
|
293
|
+
internal fun colorSchemeFromRawValue(rawValue: String?): ColorScheme? = when (rawValue) {
|
|
294
|
+
"auto" -> ColorScheme.AUTO
|
|
295
|
+
"light" -> ColorScheme.LIGHT
|
|
296
|
+
"dark" -> ColorScheme.DARK
|
|
297
|
+
else -> null
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private fun colorSchemeFromConfig(config: Map<String, Any>): ColorScheme? {
|
|
301
|
+
// Absent is not a mistake — the SDK's own AUTO default stands — so only a value the
|
|
302
|
+
// host actually provided is worth reporting.
|
|
303
|
+
val rawValue = config.getTypedValue<String>(Keys.Config.COLOR_SCHEME) ?: return null
|
|
304
|
+
|
|
305
|
+
return colorSchemeFromRawValue(rawValue).also { resolved ->
|
|
306
|
+
if (resolved == null) {
|
|
307
|
+
SDKComponent.logger.error(
|
|
308
|
+
"Unrecognized in-app colorScheme '$rawValue', expected one of auto, " +
|
|
309
|
+
"light, dark. Falling back to the device appearance."
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Builds the host's inbox accessibility labels from the wrapper configuration, or null when
|
|
317
|
+
* the app provided none — in which case the SDK keeps its default of emitting no labels at
|
|
318
|
+
* all rather than falling back to English.
|
|
319
|
+
*
|
|
320
|
+
* `bellWithUnreadCount` arrives as a template string because the bridge carries data but not
|
|
321
|
+
* functions; it is converted here into the `(Int) -> String` the native SDK expects. A
|
|
322
|
+
* template without the placeholder is returned verbatim for every count; the JavaScript
|
|
323
|
+
* layer warns about that case, where the developer can actually see it.
|
|
324
|
+
*/
|
|
325
|
+
private fun inboxAccessibilityLabelsFromConfig(
|
|
326
|
+
config: Map<String, Any>
|
|
327
|
+
): NotificationInboxAccessibilityLabels? {
|
|
328
|
+
val labels = config.getTypedValue<Map<String, Any>>(
|
|
329
|
+
Keys.Config.NOTIFICATION_INBOX_ACCESSIBILITY_LABELS
|
|
330
|
+
) ?: return null
|
|
331
|
+
|
|
332
|
+
val unreadCountTemplate = labels.getTypedValue<String>(
|
|
333
|
+
Keys.InboxAccessibilityLabels.BELL_WITH_UNREAD_COUNT
|
|
334
|
+
)
|
|
335
|
+
return NotificationInboxAccessibilityLabels(
|
|
336
|
+
bell = labels.getTypedValue<String>(Keys.InboxAccessibilityLabels.BELL),
|
|
337
|
+
bellWithUnreadCount = unreadCountTemplate?.let { template ->
|
|
338
|
+
{ count: Int ->
|
|
339
|
+
template.replace(
|
|
340
|
+
Keys.InboxAccessibilityLabels.COUNT_PLACEHOLDER,
|
|
341
|
+
count.toString()
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
loadingIndicator = labels.getTypedValue<String>(
|
|
346
|
+
Keys.InboxAccessibilityLabels.LOADING_INDICATOR
|
|
347
|
+
),
|
|
348
|
+
emptyState = labels.getTypedValue<String>(
|
|
349
|
+
Keys.InboxAccessibilityLabels.EMPTY_STATE
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
}
|
|
214
353
|
}
|
|
215
354
|
}
|
package/android/src/main/java/com/zixflow/reactnative/sdk/messaginginapp/ReactInboxEventListener.kt
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
package com.zixflow.reactnative.sdk.messaginginapp
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
|
+
import com.zixflow.messaginginapp.gist.data.model.InboxMessage
|
|
6
|
+
import com.zixflow.messaginginapp.type.InboxEventListener
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* React Native bridge for Zixflow Visual Notification Inbox events.
|
|
10
|
+
*
|
|
11
|
+
* Registered on the SDK (via [com.zixflow.messaginginapp.ModuleMessagingInApp.setInboxEventListener])
|
|
12
|
+
* only while a JS listener is registered. Because the New-Arch event emitter is one-way we cannot
|
|
13
|
+
* round-trip a bool back from JS, so [messageActionTaken] forwards the event fire-and-forget and
|
|
14
|
+
* RETURNS `true` — telling the SDK the host handled the action so the SDK suppresses its default
|
|
15
|
+
* navigation. The JS host owns action handling while a listener is registered. Observational
|
|
16
|
+
* callbacks forward fire-and-forget.
|
|
17
|
+
*/
|
|
18
|
+
class ReactInboxEventListener private constructor() : InboxEventListener {
|
|
19
|
+
// Event emitter function to send events to React Native layer
|
|
20
|
+
private var eventEmitter: ((ReadableMap) -> Unit)? = null
|
|
21
|
+
|
|
22
|
+
// Sets the event emitter function
|
|
23
|
+
internal fun setEventEmitter(emitter: ((ReadableMap) -> Unit)?) {
|
|
24
|
+
this.eventEmitter = emitter
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Clears the event emitter to prevent memory leaks
|
|
28
|
+
internal fun clearEventEmitter() {
|
|
29
|
+
this.eventEmitter = null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Emits an inbox message event to React Native with a serialized message payload
|
|
33
|
+
private fun emitInboxEvent(
|
|
34
|
+
eventType: String,
|
|
35
|
+
message: InboxMessage,
|
|
36
|
+
actionName: String? = null,
|
|
37
|
+
actionValue: String? = null,
|
|
38
|
+
) {
|
|
39
|
+
// Get the emitter, return early if not set
|
|
40
|
+
val emitter = eventEmitter ?: return
|
|
41
|
+
|
|
42
|
+
val data = Arguments.createMap().apply {
|
|
43
|
+
putString("eventType", eventType)
|
|
44
|
+
putMap("message", message.toWritableMap())
|
|
45
|
+
actionName?.let { putString("actionName", it) }
|
|
46
|
+
actionValue?.let { putString("actionValue", it) }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
emitter.invoke(data)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun messageActionTaken(
|
|
53
|
+
message: InboxMessage,
|
|
54
|
+
actionName: String,
|
|
55
|
+
actionValue: String,
|
|
56
|
+
): Boolean {
|
|
57
|
+
emitInboxEvent(
|
|
58
|
+
eventType = "messageActionTaken",
|
|
59
|
+
message = message,
|
|
60
|
+
actionName = actionName,
|
|
61
|
+
actionValue = actionValue,
|
|
62
|
+
)
|
|
63
|
+
// Host (React Native) owns action handling while a listener is registered.
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override fun messageShown(message: InboxMessage) = emitInboxEvent(
|
|
68
|
+
eventType = "messageShown",
|
|
69
|
+
message = message,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
override fun messageOpened(message: InboxMessage) = emitInboxEvent(
|
|
73
|
+
eventType = "messageOpened",
|
|
74
|
+
message = message,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
override fun messageDismissed(message: InboxMessage) = emitInboxEvent(
|
|
78
|
+
eventType = "messageDismissed",
|
|
79
|
+
message = message,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
companion object {
|
|
83
|
+
// Singleton instance with public visibility for direct access by Expo plugin
|
|
84
|
+
val instance: ReactInboxEventListener by lazy { ReactInboxEventListener() }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* No-op inbox listener used to clear the forwarder from the SDK. Returning `false` from
|
|
90
|
+
* [messageActionTaken] restores the SDK's default action handling. The native
|
|
91
|
+
* `setInboxEventListener` API is non-null, so a no-op is installed rather than clearing.
|
|
92
|
+
*/
|
|
93
|
+
internal object NoOpInboxEventListener : InboxEventListener {
|
|
94
|
+
override fun messageActionTaken(
|
|
95
|
+
message: InboxMessage,
|
|
96
|
+
actionName: String,
|
|
97
|
+
actionValue: String,
|
|
98
|
+
): Boolean = false
|
|
99
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.zixflow.reactnative.sdk.messaginginbox
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
4
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
5
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
7
|
+
import com.facebook.react.viewmanagers.NotificationInboxBellNativeManagerDelegate
|
|
8
|
+
import com.facebook.react.viewmanagers.NotificationInboxBellNativeManagerInterface
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* View manager for the Visual Notification Inbox bell (just the bell; host opens its own UI).
|
|
12
|
+
*
|
|
13
|
+
* Registers the `onTap` direct event so taps reach the JS wrapper.
|
|
14
|
+
*/
|
|
15
|
+
@ReactModule(name = NotificationInboxBellViewManager.NAME)
|
|
16
|
+
class NotificationInboxBellViewManager :
|
|
17
|
+
NotificationInboxBellNativeManagerInterface<ReactNotificationInboxBellView>,
|
|
18
|
+
SimpleViewManager<ReactNotificationInboxBellView>() {
|
|
19
|
+
private val delegate = NotificationInboxBellNativeManagerDelegate(this)
|
|
20
|
+
|
|
21
|
+
override fun getName() = NAME
|
|
22
|
+
override fun getDelegate(): ViewManagerDelegate<ReactNotificationInboxBellView> = delegate
|
|
23
|
+
|
|
24
|
+
override fun createViewInstance(
|
|
25
|
+
reactContext: ThemedReactContext
|
|
26
|
+
): ReactNotificationInboxBellView = ReactNotificationInboxBellView(reactContext)
|
|
27
|
+
|
|
28
|
+
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
|
|
29
|
+
val customEvents = super.getExportedCustomDirectEventTypeConstants() ?: mutableMapOf()
|
|
30
|
+
customEvents[ReactNotificationInboxBellView.ON_TAP] =
|
|
31
|
+
mapOf("registrationName" to ReactNotificationInboxBellView.ON_TAP)
|
|
32
|
+
return customEvents
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
internal const val NAME = "NotificationInboxBellNative"
|
|
37
|
+
}
|
|
38
|
+
}
|