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
package/android/build.gradle
CHANGED
|
@@ -10,11 +10,14 @@ buildscript {
|
|
|
10
10
|
dependencies {
|
|
11
11
|
// noinspection DifferentKotlinGradleVersion
|
|
12
12
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
13
|
+
// Compose compiler plugin (Kotlin 2.x) for hosting the Visual Notification Inbox composables.
|
|
14
|
+
classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:$kotlin_version"
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
apply plugin: 'com.android.library'
|
|
17
19
|
apply plugin: 'kotlin-android'
|
|
20
|
+
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
|
|
18
21
|
apply plugin: "com.facebook.react"
|
|
19
22
|
|
|
20
23
|
def getExtOrDefault(name) {
|
|
@@ -26,6 +29,7 @@ def getExtOrIntegerDefault(name) {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
ext.zixflowLocationEnabled = rootProject.findProperty("zixflow_location_enabled")?.toBoolean() ?: false
|
|
32
|
+
ext.zixflowGeofenceEnabled = rootProject.findProperty("zixflow_geofence_enabled")?.toBoolean() ?: false
|
|
29
33
|
|
|
30
34
|
android {
|
|
31
35
|
namespace = 'com.zixflow.reactnative.sdk'
|
|
@@ -33,12 +37,17 @@ android {
|
|
|
33
37
|
defaultConfig {
|
|
34
38
|
minSdkVersion getExtOrIntegerDefault('minSdkVersion')
|
|
35
39
|
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
|
|
36
|
-
|
|
40
|
+
// Geofence implies Location, so the Location flag is on whenever geofence is enabled
|
|
41
|
+
// (mirrors the iOS geofence subspec, which also defines ZIXFLOW_LOCATION_ENABLED).
|
|
42
|
+
buildConfigField "boolean", "ZIXFLOW_LOCATION_ENABLED", "${project.zixflowLocationEnabled || project.zixflowGeofenceEnabled}"
|
|
43
|
+
buildConfigField "boolean", "ZIXFLOW_GEOFENCE_ENABLED", "${project.zixflowGeofenceEnabled}"
|
|
37
44
|
consumerProguardFiles 'consumer-rules.pro'
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
buildFeatures {
|
|
41
48
|
buildConfig true
|
|
49
|
+
// Required to host the Visual Notification Inbox Compose UI components.
|
|
50
|
+
compose true
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
buildTypes {
|
|
@@ -144,4 +153,12 @@ dependencies {
|
|
|
144
153
|
// noinspection GradleDynamicVersion
|
|
145
154
|
api 'com.facebook.react:react-native:+'
|
|
146
155
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
156
|
+
|
|
157
|
+
// Jetpack Compose — needed because the Visual Notification Inbox view managers host
|
|
158
|
+
// the native `@Composable` inbox components inside a ComposeView.
|
|
159
|
+
def composeBom = platform('androidx.compose:compose-bom:2025.10.00')
|
|
160
|
+
implementation composeBom
|
|
161
|
+
implementation 'androidx.compose.ui:ui'
|
|
162
|
+
implementation 'androidx.compose.foundation:foundation'
|
|
163
|
+
implementation 'androidx.compose.runtime:runtime'
|
|
147
164
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
# Location
|
|
2
|
-
# When not enabled, R8 must not fail on missing
|
|
1
|
+
# Location and geofence modules are optional compileOnly dependencies.
|
|
2
|
+
# When not enabled, R8 must not fail on their missing classes.
|
|
3
3
|
-dontwarn io.customer.location.**
|
|
4
|
+
-dontwarn io.customer.geofence.**
|
|
@@ -101,11 +101,14 @@ class NativeZixflowModule(
|
|
|
101
101
|
cdnHost(it)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
// Configure push messaging module based on config provided by customer app
|
|
104
|
+
// Configure push messaging module based on config provided by customer app.
|
|
105
|
+
// Live Activities are hosted by the FCM push module, so the `liveNotifications`
|
|
106
|
+
// config is applied to the same module.
|
|
105
107
|
packageConfig.getTypedValue<Map<String, Any>>(key = "push").let { pushConfig ->
|
|
106
108
|
NativeMessagingPushModule.addNativeModuleFromConfig(
|
|
107
109
|
builder = this,
|
|
108
|
-
config = pushConfig ?: emptyMap()
|
|
110
|
+
config = pushConfig ?: emptyMap(),
|
|
111
|
+
liveNotificationsConfig = packageConfig.getTypedValue<Map<String, Any>>(key = "liveNotifications")
|
|
109
112
|
)
|
|
110
113
|
}
|
|
111
114
|
packageConfig.getTypedValue<Map<String, Any>>(key = "inApp")?.let { inAppConfig ->
|
|
@@ -114,12 +117,28 @@ class NativeZixflowModule(
|
|
|
114
117
|
config = inAppConfig
|
|
115
118
|
)
|
|
116
119
|
}
|
|
117
|
-
// Configure location module
|
|
120
|
+
// Configure location module. Geofence implies location, so register location
|
|
121
|
+
// (with the app's config if given, otherwise defaults) whenever location or
|
|
122
|
+
// geofence is enabled, since geofence relies on the location module's fixes.
|
|
123
|
+
// ZIXFLOW_LOCATION_ENABLED is already true for geofence-enabled builds.
|
|
118
124
|
if (BuildConfig.ZIXFLOW_LOCATION_ENABLED) {
|
|
119
|
-
packageConfig.getTypedValue<Map<String, Any>>(key = "location")
|
|
125
|
+
val locationConfig = packageConfig.getTypedValue<Map<String, Any>>(key = "location")
|
|
126
|
+
val geofenceConfigured = BuildConfig.ZIXFLOW_GEOFENCE_ENABLED &&
|
|
127
|
+
packageConfig.getTypedValue<Map<String, Any>>(key = "geofence") != null
|
|
128
|
+
if (locationConfig != null || geofenceConfigured) {
|
|
120
129
|
NativeLocationModule.addNativeModuleFromConfig(
|
|
121
130
|
builder = this,
|
|
122
|
-
config = locationConfig
|
|
131
|
+
config = locationConfig ?: emptyMap()
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Configure geofence module if enabled via gradle property; it runs
|
|
136
|
+
// automatically once registered and relies on the location module above.
|
|
137
|
+
if (BuildConfig.ZIXFLOW_GEOFENCE_ENABLED) {
|
|
138
|
+
packageConfig.getTypedValue<Map<String, Any>>(key = "geofence")?.let { geofenceConfig ->
|
|
139
|
+
NativeGeofenceModule.addNativeModuleFromConfig(
|
|
140
|
+
builder = this,
|
|
141
|
+
config = geofenceConfig
|
|
123
142
|
)
|
|
124
143
|
}
|
|
125
144
|
}
|
|
@@ -133,6 +152,22 @@ class NativeZixflowModule(
|
|
|
133
152
|
}
|
|
134
153
|
}
|
|
135
154
|
|
|
155
|
+
override fun setDeepLinkRoutingReady() {
|
|
156
|
+
// UIScene and React Native Linking readiness are iOS-only concerns.
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
override fun registerDeepLinkHandler() {
|
|
160
|
+
// UIScene deep-link acknowledgement is an iOS-only concern.
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
override fun unregisterDeepLinkHandler() {
|
|
164
|
+
// UIScene deep-link acknowledgement is an iOS-only concern.
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
override fun acknowledgeDeepLink(id: String?, handled: Boolean) {
|
|
168
|
+
// UIScene deep-link acknowledgement is an iOS-only concern.
|
|
169
|
+
}
|
|
170
|
+
|
|
136
171
|
override fun identify(params: ReadableMap?) {
|
|
137
172
|
val userId = params?.getString("userId")
|
|
138
173
|
val traits = params?.getMap("traits")
|
|
@@ -6,10 +6,14 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
6
6
|
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
7
|
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
8
|
import com.facebook.react.uimanager.ViewManager
|
|
9
|
+
import com.zixflow.reactnative.sdk.geofence.NativeGeofenceModule
|
|
10
|
+
import com.zixflow.reactnative.sdk.liveactivities.NativeLiveActivitiesModule
|
|
9
11
|
import com.zixflow.reactnative.sdk.location.NativeLocationModule
|
|
10
12
|
import com.zixflow.reactnative.sdk.logging.NativeZixflowLoggingModule
|
|
11
13
|
import com.zixflow.reactnative.sdk.messaginginapp.InlineInAppMessageViewManager
|
|
12
14
|
import com.zixflow.reactnative.sdk.messaginginapp.NativeMessagingInAppModule
|
|
15
|
+
import com.zixflow.reactnative.sdk.messaginginbox.NotificationInboxBellViewManager
|
|
16
|
+
import com.zixflow.reactnative.sdk.messaginginbox.NotificationInboxViewManager
|
|
13
17
|
import com.zixflow.reactnative.sdk.messagingpush.NativeMessagingPushModule
|
|
14
18
|
import com.zixflow.reactnative.sdk.util.assertNotNull
|
|
15
19
|
|
|
@@ -22,7 +26,11 @@ class ZixflowReactNativePackage : BaseReactPackage() {
|
|
|
22
26
|
* Creates the list of view managers for the Zixflow React Native SDK.
|
|
23
27
|
*/
|
|
24
28
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
25
|
-
return listOf(
|
|
29
|
+
return listOf(
|
|
30
|
+
InlineInAppMessageViewManager(),
|
|
31
|
+
NotificationInboxBellViewManager(),
|
|
32
|
+
NotificationInboxViewManager()
|
|
33
|
+
)
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
@@ -31,11 +39,23 @@ class ZixflowReactNativePackage : BaseReactPackage() {
|
|
|
31
39
|
// See: https://reactnative.dev/docs/fabric-native-components-introduction?platforms=android#4-write-the-reactwebviewpackage
|
|
32
40
|
return when (name) {
|
|
33
41
|
InlineInAppMessageViewManager.NAME -> InlineInAppMessageViewManager()
|
|
42
|
+
NotificationInboxBellViewManager.NAME -> NotificationInboxBellViewManager()
|
|
43
|
+
NotificationInboxViewManager.NAME -> NotificationInboxViewManager()
|
|
34
44
|
NativeZixflowLoggingModule.NAME -> NativeZixflowLoggingModule(reactContext)
|
|
35
45
|
NativeZixflowModule.NAME -> NativeZixflowModule(reactContext = reactContext)
|
|
46
|
+
// Unconditional on purpose, unlike Location below. Location has its own artifact
|
|
47
|
+
// (com.zixflow.android:location) that the flag switches between `api` and `compileOnly`,
|
|
48
|
+
// so gating it genuinely keeps a dependency off the consumer's classpath. Live
|
|
49
|
+
// Notifications ship inside messaging-push-fcm, already a hard dependency here, so a flag
|
|
50
|
+
// would remove nothing — and the module is inert until `liveNotifications` config enables
|
|
51
|
+
// a type. iOS gates its equivalent only because the subspec pulls in extra pods.
|
|
52
|
+
NativeLiveActivitiesModule.NAME -> NativeLiveActivitiesModule(reactContext)
|
|
36
53
|
NativeLocationModule.NAME -> if (BuildConfig.ZIXFLOW_LOCATION_ENABLED) {
|
|
37
54
|
NativeLocationModule(reactContext)
|
|
38
55
|
} else null
|
|
56
|
+
NativeGeofenceModule.NAME -> if (BuildConfig.ZIXFLOW_GEOFENCE_ENABLED) {
|
|
57
|
+
NativeGeofenceModule(reactContext)
|
|
58
|
+
} else null
|
|
39
59
|
NativeMessagingInAppModule.NAME -> NativeMessagingInAppModule(reactContext)
|
|
40
60
|
NativeMessagingPushModule.NAME -> NativeMessagingPushModule(reactContext)
|
|
41
61
|
else -> assertNotNull<NativeModule>(value = null) { "Unknown module name: $name" }
|
|
@@ -69,9 +89,13 @@ class ZixflowReactNativePackage : BaseReactPackage() {
|
|
|
69
89
|
// doesn't crash at import time. getModule() returns null when disabled.
|
|
70
90
|
val moduleNames: List<String> = listOf(
|
|
71
91
|
InlineInAppMessageViewManager.NAME,
|
|
92
|
+
NotificationInboxBellViewManager.NAME,
|
|
93
|
+
NotificationInboxViewManager.NAME,
|
|
72
94
|
NativeZixflowLoggingModule.NAME,
|
|
73
95
|
NativeZixflowModule.NAME,
|
|
96
|
+
NativeLiveActivitiesModule.NAME,
|
|
74
97
|
NativeLocationModule.NAME,
|
|
98
|
+
NativeGeofenceModule.NAME,
|
|
75
99
|
NativeMessagingInAppModule.NAME,
|
|
76
100
|
NativeMessagingPushModule.NAME,
|
|
77
101
|
)
|
|
@@ -12,6 +12,25 @@ internal object Keys {
|
|
|
12
12
|
const val SCREEN_VIEW_USE = "screenViewUse"
|
|
13
13
|
const val API_HOST = "apiHost"
|
|
14
14
|
const val CDN_HOST = "cdnHost"
|
|
15
|
+
const val NOTIFICATION_INBOX_ACCESSIBILITY_LABELS = "notificationInboxAccessibilityLabels"
|
|
16
|
+
const val COLOR_SCHEME = "colorScheme"
|
|
17
|
+
// Push messaging
|
|
15
18
|
const val PUSH_CLICK_BEHAVIOR = "pushClickBehavior"
|
|
16
19
|
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Keys of the `notificationInboxAccessibilityLabels` sub-map.
|
|
23
|
+
*
|
|
24
|
+
* [BELL_WITH_UNREAD_COUNT] carries a template string containing [COUNT_PLACEHOLDER] rather than
|
|
25
|
+
* a callback, because wrapper configuration crosses a bridge that carries data but not
|
|
26
|
+
* functions. The native SDK takes a `(Int) -> String`, so the template is converted to one when
|
|
27
|
+
* the module is configured.
|
|
28
|
+
*/
|
|
29
|
+
object InboxAccessibilityLabels {
|
|
30
|
+
const val BELL = "bell"
|
|
31
|
+
const val BELL_WITH_UNREAD_COUNT = "bellWithUnreadCount"
|
|
32
|
+
const val LOADING_INDICATOR = "loadingIndicator"
|
|
33
|
+
const val EMPTY_STATE = "emptyState"
|
|
34
|
+
const val COUNT_PLACEHOLDER = "{count}"
|
|
35
|
+
}
|
|
17
36
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
package com.zixflow.reactnative.sdk.geofence
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
5
|
+
import com.zixflow.geofence.GeofenceLocationMode
|
|
6
|
+
import com.zixflow.geofence.GeofenceModuleConfig
|
|
7
|
+
import com.zixflow.geofence.ModuleGeofence
|
|
8
|
+
import com.zixflow.reactnative.sdk.NativeZixflowGeofenceSpec
|
|
9
|
+
import com.zixflow.reactnative.sdk.extension.getTypedValue
|
|
10
|
+
import com.zixflow.sdk.ZixflowBuilder
|
|
11
|
+
import com.zixflow.sdk.core.di.SDKComponent
|
|
12
|
+
import com.zixflow.sdk.core.util.Logger
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* React Native module implementation for Zixflow Geofence Native SDK
|
|
16
|
+
* using TurboModules with new architecture.
|
|
17
|
+
*
|
|
18
|
+
* The reference to [ModuleGeofence] is isolated in this file so the geofence dependency is
|
|
19
|
+
* only loaded when the module is enabled and bundled. Geofence depends on the location
|
|
20
|
+
* module, which the caller registers alongside it.
|
|
21
|
+
*/
|
|
22
|
+
@ReactModule(name = NativeGeofenceModule.NAME)
|
|
23
|
+
class NativeGeofenceModule(
|
|
24
|
+
private val reactContext: ReactApplicationContext,
|
|
25
|
+
) : NativeZixflowGeofenceSpec(reactContext) {
|
|
26
|
+
private val logger: Logger
|
|
27
|
+
get() = SDKComponent.logger
|
|
28
|
+
|
|
29
|
+
private fun getModuleGeofence() = runCatching {
|
|
30
|
+
ModuleGeofence.instance()
|
|
31
|
+
}.onFailure {
|
|
32
|
+
logger.error("Geofence module is not initialized. Ensure geofence config is provided during SDK initialization.")
|
|
33
|
+
}.getOrNull()
|
|
34
|
+
|
|
35
|
+
override fun refreshFromCurrentLocation() {
|
|
36
|
+
getModuleGeofence()?.refreshFromCurrentLocation()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
companion object {
|
|
40
|
+
const val NAME = "NativeZixflowGeofence"
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Adds the geofence module to the native Android SDK based on the configuration
|
|
44
|
+
* provided by the customer app.
|
|
45
|
+
*
|
|
46
|
+
* @param builder instance of ZixflowBuilder to add the geofence module.
|
|
47
|
+
* @param config configuration provided by the customer app for the geofence module.
|
|
48
|
+
*/
|
|
49
|
+
internal fun addNativeModuleFromConfig(
|
|
50
|
+
builder: ZixflowBuilder,
|
|
51
|
+
config: Map<String, Any>
|
|
52
|
+
) {
|
|
53
|
+
val locationModeValue = config.getTypedValue<String>("locationMode")
|
|
54
|
+
// Uppercase before matching so casing can't diverge from iOS (which uppercases too);
|
|
55
|
+
// enumValueOf is case-sensitive. Unknown values fall back to the SDK default.
|
|
56
|
+
val locationMode = locationModeValue?.let { value ->
|
|
57
|
+
runCatching { enumValueOf<GeofenceLocationMode>(value.uppercase()) }.getOrNull()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
val configBuilder = GeofenceModuleConfig.Builder()
|
|
61
|
+
locationMode?.let { configBuilder.setLocationMode(it) }
|
|
62
|
+
builder.addZixflowModule(ModuleGeofence(configBuilder.build()))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -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
|
+
}
|