@capillarytech/react-native-hydra-lib 1.7.0-beta.6 → 1.7.0-beta.8
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/android/build.gradle +8 -7
- package/android/src/main/java/com/hydralib/delegates/NonHydraNotificationActivity.kt +22 -10
- package/android/src/main/java/com/hydralib/eventemitter/HydraEventEmitter.kt +34 -4
- package/android/src/main/java/com/hydralib/gamification/GamificationBannerViewManager.kt +34 -3
- package/android/src/main/java/com/hydralib/gamification/GamificationEmbedViewManager.kt +25 -4
- package/android/src/main/java/com/hydralib/gamification/GamificationLoyaltyViewManager.kt +41 -6
- package/android/src/main/java/com/hydralib/hydrasdk/HydraCore.kt +43 -6
- package/android/src/main/java/com/hydralib/hydrasdk/HydraInAppMessages.kt +68 -41
- package/android/src/main/java/com/hydralib/hydrasdk/HydraInbox.kt +2 -2
- package/android/src/main/java/com/hydralib/hydrasdk/HydraPushNotification.kt +29 -1
- package/android/src/newarch/HydraLibModule.kt +8 -0
- package/android/src/oldarch/HydraLibModule.kt +10 -0
- package/ios/Delegates/HydraApplicationDelegate.swift +8 -0
- package/ios/GamificationViews/GamificationBannerViewComponentView.mm +39 -2
- package/ios/GamificationViews/GamificationEmbedViewComponentView.mm +33 -2
- package/ios/GamificationViews/GamificationLoyaltyViewComponentView.mm +41 -1
- package/ios/HydraLib.mm +8 -0
- package/ios/HydraSDK/HydraInAppMessages.swift +35 -8
- package/ios/HydraSDK/HydraInbox.swift +36 -9
- package/ios/HydraSDK/HydraInboxUI.swift +5 -1
- package/ios/HydraSDK/HydraPushNotification.swift +2 -2
- package/ios/HydraSDK/HydraSDKCore.swift +156 -82
- package/ios/Utils/HydraExtensions.swift +1 -1
- package/lib/commonjs/NativeHydraLib.ts +2 -0
- package/lib/commonjs/hydra-apis/Hydra.js +7 -0
- package/lib/commonjs/hydra-apis/Hydra.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/ThemeMode.js +14 -0
- package/lib/commonjs/models/ThemeMode.js.map +1 -0
- package/lib/module/NativeHydraLib.ts +2 -0
- package/lib/module/hydra-apis/Hydra.js +7 -0
- package/lib/module/hydra-apis/Hydra.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/ThemeMode.js +10 -0
- package/lib/module/models/ThemeMode.js.map +1 -0
- package/lib/typescript/commonjs/src/NativeHydraLib.d.ts +2 -0
- package/lib/typescript/commonjs/src/NativeHydraLib.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/hydra-apis/Hydra.d.ts +3 -0
- package/lib/typescript/commonjs/src/hydra-apis/Hydra.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/models/ThemeMode.d.ts +7 -0
- package/lib/typescript/commonjs/src/models/ThemeMode.d.ts.map +1 -0
- package/lib/typescript/module/src/NativeHydraLib.d.ts +2 -0
- package/lib/typescript/module/src/NativeHydraLib.d.ts.map +1 -1
- package/lib/typescript/module/src/hydra-apis/Hydra.d.ts +3 -0
- package/lib/typescript/module/src/hydra-apis/Hydra.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/models/ThemeMode.d.ts +7 -0
- package/lib/typescript/module/src/models/ThemeMode.d.ts.map +1 -0
- package/package.json +1 -1
- package/sbom.spdx.json +4 -4
- package/scripts/HydraPods.rb +1 -1
- package/src/NativeHydraLib.ts +2 -0
- package/src/hydra-apis/Hydra.ts +10 -0
- package/src/index.tsx +2 -0
- package/src/models/ThemeMode.ts +6 -0
package/android/build.gradle
CHANGED
|
@@ -164,6 +164,7 @@ android {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
repositories {
|
|
167
|
+
mavenLocal()
|
|
167
168
|
mavenCentral()
|
|
168
169
|
google()
|
|
169
170
|
}
|
|
@@ -178,14 +179,14 @@ dependencies {
|
|
|
178
179
|
//noinspection GradleDependency
|
|
179
180
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
180
181
|
|
|
181
|
-
implementation "com.capillarytech:hydra-core:1.9.22-
|
|
182
|
-
implementation "com.capillarytech:hydra-pushbase:1.9.22-
|
|
183
|
-
implementation "com.capillarytech:hydra-firebase:1.9.22-
|
|
184
|
-
implementation "com.capillarytech:hydra-richnotification:1.9.22-
|
|
182
|
+
implementation "com.capillarytech:hydra-core:1.9.22-beta08"
|
|
183
|
+
implementation "com.capillarytech:hydra-pushbase:1.9.22-beta08"
|
|
184
|
+
implementation "com.capillarytech:hydra-firebase:1.9.22-beta08"
|
|
185
|
+
implementation "com.capillarytech:hydra-richnotification:1.9.22-beta08"
|
|
185
186
|
|
|
186
|
-
implementation "com.capillarytech:hydra-inbox-core:1.9.22-
|
|
187
|
-
implementation "com.capillarytech:hydra-inbox-ui:1.9.22-
|
|
188
|
-
implementation "com.capillarytech:hydra-inapp:1.9.22-
|
|
187
|
+
implementation "com.capillarytech:hydra-inbox-core:1.9.22-beta08"
|
|
188
|
+
implementation "com.capillarytech:hydra-inbox-ui:1.9.22-beta08"
|
|
189
|
+
implementation "com.capillarytech:hydra-inapp:1.9.22-beta08"
|
|
189
190
|
implementation "com.google.firebase:firebase-messaging-ktx:24.1.0"
|
|
190
191
|
|
|
191
192
|
implementation "com.google.code.gson:gson:2.11.0"
|
|
@@ -5,6 +5,7 @@ import androidx.annotation.RestrictTo
|
|
|
5
5
|
import androidx.appcompat.app.AppCompatActivity
|
|
6
6
|
import com.capillary.hydra.android.sdk.utils.Logger
|
|
7
7
|
import com.google.gson.Gson
|
|
8
|
+
import com.google.gson.JsonSyntaxException
|
|
8
9
|
import com.google.gson.reflect.TypeToken
|
|
9
10
|
import com.hydralib.eventemitter.HydraEventEmitter
|
|
10
11
|
import com.hydralib.eventemitter.HydraEventType
|
|
@@ -27,17 +28,28 @@ class NonHydraNotificationActivity : AppCompatActivity() {
|
|
|
27
28
|
val remoteMessage = intent.getStringExtra(EXTRA_REMOTE_MESSAGE)
|
|
28
29
|
|
|
29
30
|
Logger.d(TAG, "Remote Message: $remoteMessage")
|
|
30
|
-
val gson = Gson()
|
|
31
|
-
val type = object : TypeToken<Map<String, Any>>() {}.type
|
|
32
|
-
val remoteMessageMap = gson.fromJson<Map<String, Any>>(remoteMessage, type)
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
if (remoteMessage.isNullOrEmpty()) {
|
|
33
|
+
Logger.e(TAG, "EXTRA_REMOTE_MESSAGE missing from intent, cannot emit click event")
|
|
34
|
+
finish()
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
val gson = Gson()
|
|
40
|
+
val type = object : TypeToken<Map<String, Any>>() {}.type
|
|
41
|
+
val remoteMessageMap = gson.fromJson<Map<String, Any>>(remoteMessage, type)
|
|
42
|
+
|
|
43
|
+
// Emit event to React Native
|
|
44
|
+
HydraEventEmitter.sendEvent(
|
|
45
|
+
HydraEventType.ON_NON_HYDRA_NOTIFICATION_CLICKED,
|
|
46
|
+
remoteMessageMap
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
Logger.d(TAG, "Event emitted to React Native: ON_NON_HYDRA_NOTIFICATION_CLICKED")
|
|
50
|
+
} catch (e: JsonSyntaxException) {
|
|
51
|
+
Logger.e(TAG, "Failed to parse remote message JSON: ${e.message}\n${e.stackTraceToString()}")
|
|
52
|
+
}
|
|
41
53
|
finish()
|
|
42
54
|
}
|
|
43
55
|
}
|
|
@@ -60,6 +60,11 @@ object HydraEventEmitter {
|
|
|
60
60
|
if(callback != null) {
|
|
61
61
|
eventsWithPendingCallback.add(PendingEvent(body, callback))
|
|
62
62
|
}
|
|
63
|
+
} else {
|
|
64
|
+
Logger.w(
|
|
65
|
+
"HydraEventEmitter.sendEvent",
|
|
66
|
+
"No active listener for eventType = ${eventType.event}, dropping event. body = $body"
|
|
67
|
+
)
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
}
|
|
@@ -82,8 +87,23 @@ object HydraEventEmitter {
|
|
|
82
87
|
"HydraEventEmitter.addListener",
|
|
83
88
|
"addListener called for eventName = $eventName"
|
|
84
89
|
)
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
if (eventName == null) {
|
|
91
|
+
Logger.e("HydraEventEmitter.addListener", "eventName is null, ignoring addListener call")
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
val eventType = try {
|
|
95
|
+
// HydraEventType.from is declared as returning HydraEventType?, but it never actually
|
|
96
|
+
// returns null — it throws IllegalArgumentException for an unmapped name — so the null
|
|
97
|
+
// branch below is unreachable in practice; kept only to satisfy the nullable return type.
|
|
98
|
+
HydraEventType.from(eventName) ?: run {
|
|
99
|
+
Logger.e("HydraEventEmitter.addListener", "Unknown eventName = $eventName, ignoring addListener call")
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
} catch (e: IllegalArgumentException) {
|
|
103
|
+
Logger.e("HydraEventEmitter.addListener", "Unknown eventName = $eventName, ignoring addListener call")
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
if (!activeEventTypes.contains(eventType)) {
|
|
87
107
|
Logger.d(
|
|
88
108
|
"HydraEventEmitter.addListener",
|
|
89
109
|
"adding event for eventName = $eventName"
|
|
@@ -107,13 +127,23 @@ object HydraEventEmitter {
|
|
|
107
127
|
"removeListener called for eventName = $eventName"
|
|
108
128
|
)
|
|
109
129
|
|
|
110
|
-
if (
|
|
130
|
+
if (eventName == null) {
|
|
131
|
+
Logger.e("HydraEventEmitter.removeListener", "eventName is null, ignoring removeListener call")
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
val eventType = try {
|
|
135
|
+
HydraEventType.from(eventName)
|
|
136
|
+
} catch (e: IllegalArgumentException) {
|
|
137
|
+
Logger.e("HydraEventEmitter.removeListener", "Unknown eventName = $eventName, ignoring removeListener call")
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
if (eventType != null && activeEventTypes.contains(eventType)) {
|
|
111
141
|
Logger.d(
|
|
112
142
|
"HydraEventEmitter.removeListener",
|
|
113
143
|
"removing event for eventName = $eventName"
|
|
114
144
|
)
|
|
115
145
|
|
|
116
|
-
activeEventTypes.remove(
|
|
146
|
+
activeEventTypes.remove(eventType)
|
|
117
147
|
}
|
|
118
148
|
}
|
|
119
149
|
|
|
@@ -9,6 +9,7 @@ import com.facebook.react.module.annotations.ReactModule
|
|
|
9
9
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
10
10
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
11
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
12
|
+
import com.capillary.hydra.android.sdk.utils.Logger
|
|
12
13
|
import com.hydralib.eventemitter.HydraEventEmitter
|
|
13
14
|
import com.hydralib.eventemitter.HydraEventType
|
|
14
15
|
import com.hydralib.hydrasdk.HydraCore
|
|
@@ -23,11 +24,21 @@ class GamificationBannerViewManager : SimpleViewManager<BannerView>() {
|
|
|
23
24
|
|
|
24
25
|
@ReactProp(name = "bannerId")
|
|
25
26
|
fun setBannerId(view: BannerView, bannerId: String?) {
|
|
26
|
-
if (bannerId.isNullOrEmpty())
|
|
27
|
+
if (bannerId.isNullOrEmpty()) {
|
|
28
|
+
Logger.e("GamificationBannerViewManager.setBannerId", "bannerId prop is null or empty, skipping bind")
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
// Guard: don't re-bind if already bound to this bannerId. Without this, every RN
|
|
32
|
+
// re-render (triggered by the height-changed event) calls bindWithLayout again and
|
|
33
|
+
// re-increments the SDK's persistent show-count for this entry point, exhausting its
|
|
34
|
+
// frequency cap in half the real number of screen visits.
|
|
35
|
+
if (view.tag == bannerId) return
|
|
36
|
+
// findEntryPointByBannerId already logs the reason on failure
|
|
27
37
|
val data = findEntryPointByBannerId(bannerId) ?: return
|
|
28
38
|
|
|
29
39
|
// Defer bind until Yoga has measured and laid out the view
|
|
30
40
|
if (view.width > 0 && view.height > 0) {
|
|
41
|
+
view.tag = bannerId
|
|
31
42
|
bindWithLayout(view, data, bannerId)
|
|
32
43
|
} else {
|
|
33
44
|
view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
|
|
@@ -37,6 +48,7 @@ class GamificationBannerViewManager : SimpleViewManager<BannerView>() {
|
|
|
37
48
|
) {
|
|
38
49
|
if (right - left > 0 && bottom - top > 0) {
|
|
39
50
|
view.removeOnLayoutChangeListener(this)
|
|
51
|
+
view.tag = bannerId
|
|
40
52
|
bindWithLayout(view, data, bannerId)
|
|
41
53
|
}
|
|
42
54
|
}
|
|
@@ -57,6 +69,14 @@ class GamificationBannerViewManager : SimpleViewManager<BannerView>() {
|
|
|
57
69
|
private fun bindWithLayout(view: BannerView, data: EntryPointData, bannerId: String) {
|
|
58
70
|
val w = view.width
|
|
59
71
|
val activity = HydraCore.curReactActivity
|
|
72
|
+
if (activity == null) {
|
|
73
|
+
// Not a fatal bind failure — the banner still renders — but entry point clicks
|
|
74
|
+
// will silently no-op below for the life of this view, so surface it.
|
|
75
|
+
Logger.e(
|
|
76
|
+
"GamificationBannerViewManager.bindWithLayout",
|
|
77
|
+
"curReactActivity is null, entry point clicks will not be handled for bannerId = $bannerId"
|
|
78
|
+
)
|
|
79
|
+
}
|
|
60
80
|
|
|
61
81
|
// Set height to 0 so BannerView.bind() enters SDK-managed path
|
|
62
82
|
// and calculates height from dashboard container.height (dp)
|
|
@@ -110,9 +130,20 @@ class GamificationBannerViewManager : SimpleViewManager<BannerView>() {
|
|
|
110
130
|
}
|
|
111
131
|
|
|
112
132
|
private fun findEntryPointByBannerId(bannerId: String): EntryPointData? {
|
|
113
|
-
val flowManager = HydraCore.getFlowManager()
|
|
133
|
+
val flowManager = HydraCore.getFlowManager()
|
|
134
|
+
if (flowManager == null) {
|
|
135
|
+
Logger.e("GamificationBannerViewManager.findEntryPointByBannerId", "FlowManager is null, cannot resolve bannerId = $bannerId")
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
114
138
|
val entryPoints = flowManager.getFilteredEntryPoints(null)
|
|
115
|
-
|
|
139
|
+
val entryPoint = entryPoints.find { it.mobile?.container?.bannerId == bannerId }
|
|
140
|
+
if (entryPoint == null) {
|
|
141
|
+
Logger.e(
|
|
142
|
+
"GamificationBannerViewManager.findEntryPointByBannerId",
|
|
143
|
+
"No matching entry point for bannerId = $bannerId"
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
return entryPoint
|
|
116
147
|
}
|
|
117
148
|
|
|
118
149
|
companion object {
|
|
@@ -8,6 +8,7 @@ import com.facebook.react.module.annotations.ReactModule
|
|
|
8
8
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
9
9
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
10
10
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
11
|
+
import com.capillary.hydra.android.sdk.utils.Logger
|
|
11
12
|
import com.hydralib.eventemitter.HydraEventEmitter
|
|
12
13
|
import com.hydralib.eventemitter.HydraEventType
|
|
13
14
|
import com.hydralib.hydrasdk.HydraCore
|
|
@@ -29,11 +30,15 @@ class GamificationEmbedViewManager : SimpleViewManager<EmbedWebView>() {
|
|
|
29
30
|
|
|
30
31
|
@ReactProp(name = "embedId")
|
|
31
32
|
fun setEmbedId(view: EmbedWebView, embedId: String?) {
|
|
32
|
-
if (embedId.isNullOrEmpty())
|
|
33
|
+
if (embedId.isNullOrEmpty()) {
|
|
34
|
+
Logger.e("GamificationEmbedViewManager.setEmbedId", "embedId prop is null or empty, skipping bind")
|
|
35
|
+
return
|
|
36
|
+
}
|
|
33
37
|
// Guard: don't re-bind if already bound to this embedId. Without this, every RN
|
|
34
38
|
// re-render (triggered by the height-changed event) calls bindWithLayout again and
|
|
35
39
|
// accumulates an additional OnPreDrawListener, producing an infinite emit loop.
|
|
36
40
|
if (view.tag == embedId) return
|
|
41
|
+
// findEntryPointByEmbedId already logs the reason on failure
|
|
37
42
|
val data = findEntryPointByEmbedId(embedId) ?: return
|
|
38
43
|
|
|
39
44
|
// Defer bind until Yoga has measured and laid out the view
|
|
@@ -63,7 +68,11 @@ class GamificationEmbedViewManager : SimpleViewManager<EmbedWebView>() {
|
|
|
63
68
|
private fun bindWithLayout(view: EmbedWebView, data: EntryPointData, embedId: String) {
|
|
64
69
|
val w = view.width
|
|
65
70
|
val h = view.height
|
|
66
|
-
val activity = HydraCore.curReactActivity
|
|
71
|
+
val activity = HydraCore.curReactActivity
|
|
72
|
+
if (activity == null) {
|
|
73
|
+
Logger.e("GamificationEmbedViewManager.bindWithLayout", "curReactActivity is null, cannot bind embed view")
|
|
74
|
+
return
|
|
75
|
+
}
|
|
67
76
|
|
|
68
77
|
// Sync layoutParams with Yoga's actual pixel dimensions
|
|
69
78
|
// so SDK sees a positive height and takes host-controlled path
|
|
@@ -136,12 +145,24 @@ class GamificationEmbedViewManager : SimpleViewManager<EmbedWebView>() {
|
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
private fun findEntryPointByEmbedId(embedId: String): EntryPointData? {
|
|
139
|
-
val flowManager = HydraCore.getFlowManager()
|
|
148
|
+
val flowManager = HydraCore.getFlowManager()
|
|
149
|
+
if (flowManager == null) {
|
|
150
|
+
Logger.e("GamificationEmbedViewManager.findEntryPointByEmbedId", "FlowManager is null, cannot resolve embedId = $embedId")
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
140
153
|
val entryPoints = flowManager.getFilteredEntryPoints(null)
|
|
141
|
-
|
|
154
|
+
val entryPoint = entryPoints.find { it.mobile?.container?.bannerId == embedId }
|
|
155
|
+
if (entryPoint == null) {
|
|
156
|
+
Logger.e(
|
|
157
|
+
"GamificationEmbedViewManager.findEntryPointByEmbedId",
|
|
158
|
+
"No matching entry point for embedId = $embedId"
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
return entryPoint
|
|
142
162
|
}
|
|
143
163
|
|
|
144
164
|
override fun onDropViewInstance(view: EmbedWebView) {
|
|
165
|
+
Logger.d("GamificationEmbedViewManager.onDropViewInstance", "Destroying embed view instance")
|
|
145
166
|
preDrawListeners.remove(view)?.let { view.viewTreeObserver.removeOnPreDrawListener(it) }
|
|
146
167
|
view.destroy()
|
|
147
168
|
super.onDropViewInstance(view)
|
|
@@ -7,6 +7,7 @@ import com.facebook.react.module.annotations.ReactModule
|
|
|
7
7
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
8
8
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
9
9
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
10
|
+
import com.capillary.hydra.android.sdk.utils.Logger
|
|
10
11
|
import com.hydralib.hydrasdk.HydraCore
|
|
11
12
|
import kotlinx.coroutines.CoroutineScope
|
|
12
13
|
import kotlinx.coroutines.Dispatchers
|
|
@@ -22,11 +23,19 @@ class GamificationLoyaltyViewManager : SimpleViewManager<LoyaltyEmbedView>() {
|
|
|
22
23
|
|
|
23
24
|
@ReactProp(name = "loyaltyId")
|
|
24
25
|
fun setLoyaltyId(view: LoyaltyEmbedView, loyaltyId: String?) {
|
|
25
|
-
if (loyaltyId.isNullOrEmpty())
|
|
26
|
+
if (loyaltyId.isNullOrEmpty()) {
|
|
27
|
+
Logger.e("GamificationLoyaltyViewManager.setLoyaltyId", "loyaltyId prop is null or empty, skipping bind")
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
// Guard: don't re-bind if already bound to this loyaltyId. Without this, every RN
|
|
31
|
+
// re-render calls bindWithLayout again, re-binding the WebView unnecessarily.
|
|
32
|
+
if (view.tag == loyaltyId) return
|
|
33
|
+
// findLoyaltyUrlById already logs the reason on failure (missing FlowManager or unmatched id)
|
|
26
34
|
val loyaltyUrl = findLoyaltyUrlById(loyaltyId) ?: return
|
|
27
35
|
|
|
28
36
|
// Defer bind until Yoga has measured and laid out the view
|
|
29
37
|
if (view.width > 0 && view.height > 0) {
|
|
38
|
+
view.tag = loyaltyId
|
|
30
39
|
bindWithLayout(view, loyaltyUrl)
|
|
31
40
|
} else {
|
|
32
41
|
view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
|
|
@@ -36,6 +45,7 @@ class GamificationLoyaltyViewManager : SimpleViewManager<LoyaltyEmbedView>() {
|
|
|
36
45
|
) {
|
|
37
46
|
if (right - left > 0 && bottom - top > 0) {
|
|
38
47
|
view.removeOnLayoutChangeListener(this)
|
|
48
|
+
view.tag = loyaltyId
|
|
39
49
|
bindWithLayout(view, loyaltyUrl)
|
|
40
50
|
}
|
|
41
51
|
}
|
|
@@ -51,8 +61,16 @@ class GamificationLoyaltyViewManager : SimpleViewManager<LoyaltyEmbedView>() {
|
|
|
51
61
|
private fun bindWithLayout(view: LoyaltyEmbedView, loyaltyUrl: LoyaltyUrl) {
|
|
52
62
|
val w = view.width
|
|
53
63
|
val h = view.height
|
|
54
|
-
val activity = HydraCore.curReactActivity
|
|
55
|
-
|
|
64
|
+
val activity = HydraCore.curReactActivity
|
|
65
|
+
if (activity == null) {
|
|
66
|
+
Logger.e("GamificationLoyaltyViewManager.bindWithLayout", "curReactActivity is null, cannot bind loyalty view")
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
val flowManager = HydraCore.getFlowManager()
|
|
70
|
+
if (flowManager == null) {
|
|
71
|
+
Logger.e("GamificationLoyaltyViewManager.bindWithLayout", "FlowManager is null, cannot bind loyalty view")
|
|
72
|
+
return
|
|
73
|
+
}
|
|
56
74
|
|
|
57
75
|
// Sync layoutParams with Yoga's actual pixel dimensions
|
|
58
76
|
view.layoutParams?.let { lp ->
|
|
@@ -62,13 +80,18 @@ class GamificationLoyaltyViewManager : SimpleViewManager<LoyaltyEmbedView>() {
|
|
|
62
80
|
|
|
63
81
|
// Resync after game played
|
|
64
82
|
view.onGamePlayed = {
|
|
83
|
+
Logger.d("GamificationLoyaltyViewManager.onGamePlayed", "Game played, resyncing flow manager")
|
|
65
84
|
CoroutineScope(Dispatchers.Main).launch {
|
|
66
85
|
HydraCore.getFlowManager()?.resync()
|
|
67
86
|
}
|
|
68
87
|
}
|
|
69
88
|
|
|
70
89
|
val capToken = flowManager.getCapToken()
|
|
71
|
-
view.bind(loyaltyUrl.url, capToken, activity)
|
|
90
|
+
view.bind(loyaltyUrl.embedId, loyaltyUrl.url, capToken, activity)
|
|
91
|
+
Logger.d(
|
|
92
|
+
"GamificationLoyaltyViewManager.bindWithLayout",
|
|
93
|
+
"Bound loyalty view, url = ${loyaltyUrl.url}, size = ${w}x$h"
|
|
94
|
+
)
|
|
72
95
|
|
|
73
96
|
// Force measure/layout of children — RN parent swallows requestLayout()
|
|
74
97
|
// so LoyaltyEmbedView's internal WebView never gets measured otherwise
|
|
@@ -80,11 +103,23 @@ class GamificationLoyaltyViewManager : SimpleViewManager<LoyaltyEmbedView>() {
|
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
private fun findLoyaltyUrlById(loyaltyId: String): LoyaltyUrl? {
|
|
83
|
-
val flowManager = HydraCore.getFlowManager()
|
|
84
|
-
|
|
106
|
+
val flowManager = HydraCore.getFlowManager()
|
|
107
|
+
if (flowManager == null) {
|
|
108
|
+
Logger.e("GamificationLoyaltyViewManager.findLoyaltyUrlById", "FlowManager is null, cannot resolve loyaltyId = $loyaltyId")
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
val loyaltyUrl = flowManager.getLoyaltyUrls().find { it.embedId == loyaltyId }
|
|
112
|
+
if (loyaltyUrl == null) {
|
|
113
|
+
Logger.e(
|
|
114
|
+
"GamificationLoyaltyViewManager.findLoyaltyUrlById",
|
|
115
|
+
"No matching loyalty URL for loyaltyId = $loyaltyId"
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
return loyaltyUrl
|
|
85
119
|
}
|
|
86
120
|
|
|
87
121
|
override fun onDropViewInstance(view: LoyaltyEmbedView) {
|
|
122
|
+
Logger.d("GamificationLoyaltyViewManager.onDropViewInstance", "Destroying loyalty view instance")
|
|
88
123
|
view.destroy()
|
|
89
124
|
super.onDropViewInstance(view)
|
|
90
125
|
}
|
|
@@ -17,6 +17,7 @@ import com.capillary.hydra.android.sdk.utils.Logger
|
|
|
17
17
|
import com.capillary.hydra.gamification.GamificationCallback
|
|
18
18
|
import com.capillary.hydra.gamification.GamificationFlowManager
|
|
19
19
|
import com.capillary.hydra.gamification.GamificationRegion
|
|
20
|
+
import com.capillary.hydra.gamification.ThemeMode
|
|
20
21
|
import com.capillary.hydra.gamification.ui.GamificationUIManager
|
|
21
22
|
import com.facebook.react.ReactActivity
|
|
22
23
|
import com.facebook.react.bridge.Arguments
|
|
@@ -242,12 +243,16 @@ object HydraCore {
|
|
|
242
243
|
val cgWriteKey = hydraRemoteConfig.gamificationKey ?: ""
|
|
243
244
|
val cgRegion = hydraRemoteConfig.gamificationRegion ?: ""
|
|
244
245
|
val cgBaseUrl = hydraRemoteConfig.gamificationBaseUrl ?: ""
|
|
245
|
-
if (cgWriteKey.isNotEmpty()
|
|
246
|
+
if (cgWriteKey.isNotEmpty()) {
|
|
246
247
|
gamificationWriteKey(cgWriteKey)
|
|
247
248
|
try {
|
|
248
249
|
gamificationRegion(GamificationRegion.valueOf(cgRegion.uppercase()))
|
|
249
|
-
} catch (
|
|
250
|
-
|
|
250
|
+
} catch (e: IllegalArgumentException) {
|
|
251
|
+
Log.e(
|
|
252
|
+
"HydraCore.initHydraInstance",
|
|
253
|
+
"Invalid gamification region \"$cgRegion\", does not match any GamificationRegion enum value",
|
|
254
|
+
e
|
|
255
|
+
)
|
|
251
256
|
}
|
|
252
257
|
}
|
|
253
258
|
if (cgBaseUrl.isNotEmpty()) {
|
|
@@ -761,11 +766,13 @@ object HydraCore {
|
|
|
761
766
|
if (hydraAPI == null) {
|
|
762
767
|
Logger.e("HydraCore.reportUserEvent", "Hydra instance not initialized")
|
|
763
768
|
promise?.reject("Error Reporting user event", "Hydra instance not initialized", Throwable())
|
|
769
|
+
return
|
|
764
770
|
}
|
|
765
771
|
|
|
766
772
|
if (eventName == null) {
|
|
767
773
|
Logger.e("HydraCore.reportUserEvent", "Event name cannot be null")
|
|
768
774
|
promise?.reject("Error Reporting user event", "Event name cannot be null", Throwable())
|
|
775
|
+
return
|
|
769
776
|
}
|
|
770
777
|
|
|
771
778
|
hydraAPI!!.pushEvent(eventName!!, attributes?.toMap())
|
|
@@ -798,7 +805,10 @@ object HydraCore {
|
|
|
798
805
|
Logger.d("HydraCore.getCurrentUserState", "Current user state: $stateString")
|
|
799
806
|
promise?.resolve(stateString)
|
|
800
807
|
} catch (e: Exception) {
|
|
801
|
-
Logger.e(
|
|
808
|
+
Logger.e(
|
|
809
|
+
"HydraCore.getCurrentUserState",
|
|
810
|
+
"Error getting current user state: ${e.message}\n${e.stackTraceToString()}"
|
|
811
|
+
)
|
|
802
812
|
promise?.reject("Error", "Failed to get current user state", e)
|
|
803
813
|
}
|
|
804
814
|
}
|
|
@@ -815,7 +825,10 @@ object HydraCore {
|
|
|
815
825
|
promise?.resolve(null)
|
|
816
826
|
}
|
|
817
827
|
} catch (e: Exception) {
|
|
818
|
-
Logger.e(
|
|
828
|
+
Logger.e(
|
|
829
|
+
"HydraCore.getVisitorDetails",
|
|
830
|
+
"Error getting visitor details: ${e.message}\n${e.stackTraceToString()}"
|
|
831
|
+
)
|
|
819
832
|
promise?.reject("Error", "Failed to get visitor details", e)
|
|
820
833
|
}
|
|
821
834
|
}
|
|
@@ -830,7 +843,10 @@ object HydraCore {
|
|
|
830
843
|
Logger.d("HydraCore.isVisitorFlowEnabled", "Visitor flow enabled: $isEnabled")
|
|
831
844
|
promise?.resolve(isEnabled)
|
|
832
845
|
} catch (e: Exception) {
|
|
833
|
-
Logger.e(
|
|
846
|
+
Logger.e(
|
|
847
|
+
"HydraCore.isVisitorFlowEnabled",
|
|
848
|
+
"Error checking visitor flow: ${e.message}\n${e.stackTraceToString()}"
|
|
849
|
+
)
|
|
834
850
|
promise?.reject("Error", "Failed to check visitor flow", e)
|
|
835
851
|
}
|
|
836
852
|
}
|
|
@@ -905,4 +921,25 @@ object HydraCore {
|
|
|
905
921
|
promise?.resolve(hydraAPI!!.isGamificationSuppressed())
|
|
906
922
|
}
|
|
907
923
|
|
|
924
|
+
fun setThemeMode(mode: String?, promise: Promise?) {
|
|
925
|
+
if (mode == null) {
|
|
926
|
+
Logger.e("HydraCore.setThemeMode", "Theme mode is null")
|
|
927
|
+
promise?.reject("Error setting theme mode", "Theme mode is null", Throwable())
|
|
928
|
+
return
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
try {
|
|
932
|
+
getFlowManager()?.themeMode = ThemeMode.valueOf(mode)
|
|
933
|
+
promise?.resolve(null)
|
|
934
|
+
} catch (e: IllegalArgumentException) {
|
|
935
|
+
Logger.e("HydraCore.setThemeMode", "Invalid theme mode: $mode")
|
|
936
|
+
promise?.reject("Error setting theme mode", "Invalid theme mode: $mode", e)
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
fun getThemeMode(promise: Promise?) {
|
|
941
|
+
val mode = getFlowManager()?.themeMode ?: ThemeMode.NONE
|
|
942
|
+
promise?.resolve(mode.name)
|
|
943
|
+
}
|
|
944
|
+
|
|
908
945
|
}
|
|
@@ -66,33 +66,46 @@ object HydraInAppMessages {
|
|
|
66
66
|
message: ReadableMap?,
|
|
67
67
|
promise: Promise?
|
|
68
68
|
) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
if (message == null) {
|
|
70
|
+
Logger.e("HydraInAppMessages.reportMessageClicked", "message is null, cannot report click")
|
|
71
|
+
promise?.reject("Error", "message is null")
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
val messageMap = message.toMap()
|
|
75
|
+
val nativeJsonPayload = messageMap["nativeJsonPayload"]
|
|
76
|
+
if (nativeJsonPayload == null) {
|
|
77
|
+
Logger.e("HydraInAppMessages.reportMessageClicked", "nativeJsonPayload missing in message, cannot report click")
|
|
78
|
+
promise?.reject("Error", "nativeJsonPayload missing in message")
|
|
79
|
+
return
|
|
79
80
|
}
|
|
81
|
+
val jsonString = Gson().toJson(nativeJsonPayload)
|
|
82
|
+
val payloadFromJson = Gson().fromJson(jsonString, CampaignPayload::class.java)
|
|
83
|
+
HydraInAppUIHelper.getInstance()
|
|
84
|
+
.reportInAppMessageIsClicked(HydraCore.appContext!!, payloadFromJson)
|
|
85
|
+
promise?.resolve(true)
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
fun reportMessageDismissed(
|
|
83
89
|
message: ReadableMap?,
|
|
84
90
|
promise: Promise?
|
|
85
91
|
) {
|
|
86
|
-
message
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
if (message == null) {
|
|
93
|
+
Logger.e("HydraInAppMessages.reportMessageDismissed", "message is null, cannot report dismissal")
|
|
94
|
+
promise?.reject("Error", "message is null")
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
val messageMap = message.toMap()
|
|
98
|
+
val nativeJsonPayload = messageMap["nativeJsonPayload"]
|
|
99
|
+
if (nativeJsonPayload == null) {
|
|
100
|
+
Logger.e("HydraInAppMessages.reportMessageDismissed", "nativeJsonPayload missing in message, cannot report dismissal")
|
|
101
|
+
promise?.reject("Error", "nativeJsonPayload missing in message")
|
|
102
|
+
return
|
|
95
103
|
}
|
|
104
|
+
val jsonString = Gson().toJson(nativeJsonPayload)
|
|
105
|
+
val payloadFromJson = Gson().fromJson(jsonString, CampaignPayload::class.java)
|
|
106
|
+
HydraInAppUIHelper.getInstance()
|
|
107
|
+
.reportInAppMessageDismissed(HydraCore.appContext!!, payloadFromJson)
|
|
108
|
+
promise?.resolve(true)
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
fun reportMessageRating(
|
|
@@ -100,16 +113,23 @@ object HydraInAppMessages {
|
|
|
100
113
|
rating: Double,
|
|
101
114
|
promise: Promise?
|
|
102
115
|
) {
|
|
103
|
-
message
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
if (message == null) {
|
|
117
|
+
Logger.e("HydraInAppMessages.reportMessageRating", "message is null, cannot report rating")
|
|
118
|
+
promise?.reject("Error", "message is null")
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
val messageMap = message.toMap()
|
|
122
|
+
val nativeJsonPayload = messageMap["nativeJsonPayload"]
|
|
123
|
+
if (nativeJsonPayload == null) {
|
|
124
|
+
Logger.e("HydraInAppMessages.reportMessageRating", "nativeJsonPayload missing in message, cannot report rating")
|
|
125
|
+
promise?.reject("Error", "nativeJsonPayload missing in message")
|
|
126
|
+
return
|
|
112
127
|
}
|
|
128
|
+
val jsonString = Gson().toJson(nativeJsonPayload)
|
|
129
|
+
val payloadFromJson = Gson().fromJson(jsonString, CampaignPayload::class.java)
|
|
130
|
+
HydraInAppUIHelper.getInstance()
|
|
131
|
+
.reportInAppMessageRating(HydraCore.appContext!!, payloadFromJson, rating.toInt())
|
|
132
|
+
promise?.resolve(true)
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
fun reportMessageFeedback(
|
|
@@ -118,19 +138,26 @@ object HydraInAppMessages {
|
|
|
118
138
|
feedbackOptions: ReadableArray?,
|
|
119
139
|
promise: Promise?
|
|
120
140
|
) {
|
|
121
|
-
message
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
promise?.resolve(true)
|
|
133
|
-
}
|
|
141
|
+
if (message == null) {
|
|
142
|
+
Logger.e("HydraInAppMessages.reportMessageFeedback", "message is null, cannot report feedback")
|
|
143
|
+
promise?.reject("Error", "message is null")
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
val messageMap = message.toMap()
|
|
147
|
+
val nativeJsonPayload = messageMap["nativeJsonPayload"]
|
|
148
|
+
if (nativeJsonPayload == null) {
|
|
149
|
+
Logger.e("HydraInAppMessages.reportMessageFeedback", "nativeJsonPayload missing in message, cannot report feedback")
|
|
150
|
+
promise?.reject("Error", "nativeJsonPayload missing in message")
|
|
151
|
+
return
|
|
134
152
|
}
|
|
153
|
+
val jsonString = Gson().toJson(nativeJsonPayload)
|
|
154
|
+
val payloadFromJson = Gson().fromJson(jsonString, CampaignPayload::class.java)
|
|
155
|
+
val feedbackOptionsList = feedbackOptions?.toList()?.mapNotNull { option -> option as? Int } ?: listOf()
|
|
156
|
+
HydraInAppUIHelper.getInstance().reportInAppMessageFeedback(
|
|
157
|
+
HydraCore.appContext!!,
|
|
158
|
+
payloadFromJson,
|
|
159
|
+
feedbackOptionsList
|
|
160
|
+
)
|
|
161
|
+
promise?.resolve(true)
|
|
135
162
|
}
|
|
136
163
|
}
|
|
@@ -43,7 +43,7 @@ object HydraInbox {
|
|
|
43
43
|
Logger.d("HydraInbox.markInboxMessageAsRead", "Inbox message marked read successfully")
|
|
44
44
|
return
|
|
45
45
|
}
|
|
46
|
-
Logger.
|
|
46
|
+
Logger.e("HydraInbox.markInboxMessageAsRead", "Invalid inbox message details, input message = $inboxMessage")
|
|
47
47
|
promise?.resolve(false)
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -56,7 +56,7 @@ object HydraInbox {
|
|
|
56
56
|
Logger.d("HydraInbox.deleteInboxMessage", "Inbox message deleted successfully")
|
|
57
57
|
return
|
|
58
58
|
}
|
|
59
|
-
Logger.
|
|
59
|
+
Logger.e("HydraInbox.deleteInboxMessage", "Invalid inbox message details, input message = $inboxMessage")
|
|
60
60
|
promise?.resolve(false)
|
|
61
61
|
}
|
|
62
62
|
|