@kindly-ai/react-native 0.1.0
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/KindlyReactNative.podspec +36 -0
- package/LICENSE +20 -0
- package/README.md +127 -0
- package/android/build.gradle +113 -0
- package/android/maven-repo/ai/kindly/sdk/1.0.93/sdk-1.0.93.aar +0 -0
- package/android/maven-repo/ai/kindly/sdk/1.0.93/sdk-1.0.93.pom +125 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/kindlyai/reactnative/KindlyReactNativeModule.kt +445 -0
- package/android/src/main/java/com/kindlyai/reactnative/KindlyReactNativePackage.kt +38 -0
- package/ios/Frameworks/Kindly.xcframework/Info.plist +44 -0
- package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeDirectory +0 -0
- package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeRequirements +0 -0
- package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeRequirements-1 +0 -0
- package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeResources +578 -0
- package/ios/Frameworks/Kindly.xcframework/_CodeSignature/CodeSignature +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Assets.car +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Config.plist +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/ConfirmationWindow.nib +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Headers/Kindly-Swift.h +331 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Headers/Kindly.h +18 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Info.plist +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Kindly +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.abi.json +28769 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.private.swiftinterface +547 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios.swiftinterface +543 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/Modules/module.modulemap +11 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/PrivacyInfo.xcprivacy +83 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64/Kindly.framework/_CodeSignature/CodeResources +223 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Assets.car +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Config.plist +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/ConfirmationWindow.nib +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Headers/Kindly-Swift.h +658 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Headers/Kindly.h +18 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Info.plist +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Kindly +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.abi.json +28769 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +547 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/arm64-apple-ios-simulator.swiftinterface +543 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.abi.json +28769 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +547 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/Kindly.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +543 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/Modules/module.modulemap +11 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/PrivacyInfo.xcprivacy +83 -0
- package/ios/Frameworks/Kindly.xcframework/ios-arm64_x86_64-simulator/Kindly.framework/_CodeSignature/CodeResources +267 -0
- package/ios/KindlyReactNative.h +18 -0
- package/ios/KindlyReactNative.mm +234 -0
- package/ios/KindlyReactNativeImpl.swift +410 -0
- package/lib/module/NativeKindlyReactNative.js +22 -0
- package/lib/module/NativeKindlyReactNative.js.map +1 -0
- package/lib/module/index.js +280 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeKindlyReactNative.d.ts +47 -0
- package/lib/typescript/src/NativeKindlyReactNative.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +150 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +121 -0
- package/src/NativeKindlyReactNative.ts +80 -0
- package/src/index.tsx +353 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
package com.kindlyai.reactnative
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.app.Application
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import com.facebook.react.bridge.Arguments
|
|
7
|
+
import com.facebook.react.bridge.LifecycleEventListener
|
|
8
|
+
import com.facebook.react.bridge.Promise
|
|
9
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
|
+
import com.facebook.react.bridge.ReadableArray
|
|
11
|
+
import com.facebook.react.bridge.ReadableMap
|
|
12
|
+
import com.facebook.react.bridge.WritableMap
|
|
13
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
14
|
+
import com.google.firebase.messaging.RemoteMessage
|
|
15
|
+
import androidx.compose.ui.graphics.Color
|
|
16
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
17
|
+
import kotlinx.coroutines.CoroutineScope
|
|
18
|
+
import kotlinx.coroutines.Dispatchers
|
|
19
|
+
import kotlinx.coroutines.SupervisorJob
|
|
20
|
+
import kotlinx.coroutines.launch
|
|
21
|
+
import kotlinx.coroutines.runBlocking
|
|
22
|
+
import kotlinx.coroutines.withTimeoutOrNull
|
|
23
|
+
import no.kindly.chatsdk.chat.AuthTokenCallback
|
|
24
|
+
import no.kindly.chatsdk.chat.ChatKindlySDK
|
|
25
|
+
import no.kindly.chatsdk.chat.common.CallbackHandover
|
|
26
|
+
import no.kindly.chatsdk.chat.common.KindlySDKInteraction
|
|
27
|
+
import no.kindly.chatsdk.chat.domain.MessageChat
|
|
28
|
+
import no.kindly.chatsdk.chat.domain.entites.ButtonChat
|
|
29
|
+
import no.kindly.chatsdk.chat.domain.entites.ExternalNotification
|
|
30
|
+
import java.util.UUID
|
|
31
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* KindlyReactNativeModule — Kotlin TurboModule that bridges JS calls into
|
|
35
|
+
* the Kindly Android SDK (`ChatKindlySDK`).
|
|
36
|
+
*
|
|
37
|
+
* Symmetric with `flutter-sdk/.../KindlyPlugin.kt`. Major differences from
|
|
38
|
+
* the Flutter version:
|
|
39
|
+
* 1. Extends the codegen-generated `NativeKindlyReactNativeSpec` abstract
|
|
40
|
+
* class (not `MethodCallHandler`) — RN's TurboModule contract.
|
|
41
|
+
* 2. JS→native calls arrive as method invocations on this class; native→JS
|
|
42
|
+
* callbacks fire via `DeviceEventManagerModule.RCTDeviceEventEmitter`.
|
|
43
|
+
* 3. Sync-bool delegate callbacks (`shouldHandleLink`,
|
|
44
|
+
* `shouldHandleNotification`) use a request-id round-trip — native fires
|
|
45
|
+
* an event with a UUID, blocks on a `Map<id, CompletableDeferred>` for
|
|
46
|
+
* up to 5s, JS replies via `respondToShouldHandle(requestId, value)`.
|
|
47
|
+
* Default `true` on timeout — never hang chat UI.
|
|
48
|
+
*
|
|
49
|
+
* The codegen-derived `NativeKindlyReactNativeSpec` class is generated at
|
|
50
|
+
* build time from `src/NativeKindlyReactNative.ts`. Its package is set in
|
|
51
|
+
* `package.json` -> `codegenConfig.android.javaPackageName` to match this
|
|
52
|
+
* file's `package` line.
|
|
53
|
+
*/
|
|
54
|
+
class KindlyReactNativeModule(reactContext: ReactApplicationContext) :
|
|
55
|
+
NativeKindlyReactNativeSpec(reactContext), LifecycleEventListener {
|
|
56
|
+
|
|
57
|
+
private val context = reactContext
|
|
58
|
+
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
59
|
+
|
|
60
|
+
// Pending request maps for the sync round-trips.
|
|
61
|
+
private val authPending = ConcurrentHashMap<String, CompletableDeferred<String?>>()
|
|
62
|
+
private val shouldHandlePending = ConcurrentHashMap<String, CompletableDeferred<Boolean>>()
|
|
63
|
+
|
|
64
|
+
// Delegate bridge — set on ChatKindlySDK.`interface` when at least one
|
|
65
|
+
// delegate flag is on.
|
|
66
|
+
private var delegateBridge: KindlyDelegateBridge? = null
|
|
67
|
+
|
|
68
|
+
init {
|
|
69
|
+
reactContext.addLifecycleEventListener(this)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override fun getName(): String = NAME
|
|
73
|
+
|
|
74
|
+
// Required by the codegen-generated spec for event-emitter accounting.
|
|
75
|
+
override fun addListener(eventName: String) {
|
|
76
|
+
// No-op — RCTDeviceEventEmitter manages itself on Android.
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override fun removeListeners(count: Double) {
|
|
80
|
+
// No-op
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
override fun onHostResume() {}
|
|
84
|
+
override fun onHostPause() {}
|
|
85
|
+
override fun onHostDestroy() {
|
|
86
|
+
// Best-effort — clear any pending deferreds so the JS side doesn't hang.
|
|
87
|
+
authPending.values.forEach { it.complete(null) }
|
|
88
|
+
authPending.clear()
|
|
89
|
+
shouldHandlePending.values.forEach { it.complete(true) }
|
|
90
|
+
shouldHandlePending.clear()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// MARK: - Lifecycle
|
|
94
|
+
|
|
95
|
+
override fun start(botKey: String, languageCode: String?, hasAuthCallback: Boolean) {
|
|
96
|
+
val app = context.applicationContext as? Application
|
|
97
|
+
if (app == null) return
|
|
98
|
+
|
|
99
|
+
val authCallback: AuthTokenCallback? = if (hasAuthCallback) {
|
|
100
|
+
{ _: String ->
|
|
101
|
+
val deferred = CompletableDeferred<String>()
|
|
102
|
+
val requestId = UUID.randomUUID().toString()
|
|
103
|
+
val pending = CompletableDeferred<String?>()
|
|
104
|
+
authPending[requestId] = pending
|
|
105
|
+
|
|
106
|
+
emitEvent("KindlyGetAuthToken", Arguments.createMap().apply {
|
|
107
|
+
putString("requestId", requestId)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// Fan out to the SDK's deferred — the SDK consumes it on its own thread.
|
|
111
|
+
scope.launch {
|
|
112
|
+
val tok = pending.await()
|
|
113
|
+
if (tok != null) deferred.complete(tok)
|
|
114
|
+
else deferred.completeExceptionally(IllegalStateException("Auth token not provided"))
|
|
115
|
+
}
|
|
116
|
+
deferred
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
ChatKindlySDK.start(
|
|
123
|
+
application = app,
|
|
124
|
+
botKey = botKey,
|
|
125
|
+
languageCode = languageCode ?: "en",
|
|
126
|
+
authTokenCallback = authCallback,
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override fun displayChat(languageCode: String?, triggerDialogueId: String?) {
|
|
131
|
+
val act = currentActivity ?: return
|
|
132
|
+
ChatKindlySDK.displayChat(act)
|
|
133
|
+
if (triggerDialogueId != null) {
|
|
134
|
+
ChatKindlySDK.triggerDialogue(triggerDialogueId)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
override fun launchChat(triggerDialogueId: String?) {
|
|
139
|
+
val act = currentActivity ?: return
|
|
140
|
+
ChatKindlySDK.launchChat(context = act, triggerDialogueId = triggerDialogueId)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override fun closeChat() {
|
|
144
|
+
ChatKindlySDK.close()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override fun endChat(promise: Promise) {
|
|
148
|
+
try {
|
|
149
|
+
ChatKindlySDK.endChat()
|
|
150
|
+
promise.resolve(null)
|
|
151
|
+
} catch (e: Throwable) {
|
|
152
|
+
promise.reject("END_CHAT_FAILED", e.message, e)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
override fun kill(promise: Promise) {
|
|
157
|
+
try {
|
|
158
|
+
ChatKindlySDK.kill()
|
|
159
|
+
promise.resolve(null)
|
|
160
|
+
} catch (e: Throwable) {
|
|
161
|
+
promise.reject("KILL_FAILED", e.message, e)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
override fun setLanguage(languageCode: String, promise: Promise) {
|
|
166
|
+
try {
|
|
167
|
+
ChatKindlySDK.setLanguage(languageCode)
|
|
168
|
+
promise.resolve(null)
|
|
169
|
+
} catch (e: Throwable) {
|
|
170
|
+
promise.reject("SET_LANGUAGE_FAILED", e.message, e)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// MARK: - Messaging
|
|
175
|
+
|
|
176
|
+
override fun sendMessage(message: String, newContext: ReadableMap?, promise: Promise) {
|
|
177
|
+
val ctx = newContext?.toStringMap()
|
|
178
|
+
scope.launch {
|
|
179
|
+
try {
|
|
180
|
+
val msg = ChatKindlySDK.sendMessage(message, ctx).await()
|
|
181
|
+
promise.resolve(serializeMessageChat(msg))
|
|
182
|
+
} catch (e: Throwable) {
|
|
183
|
+
promise.reject("SEND_MESSAGE_FAILED", e.message, e)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
override fun setNewContext(context: ReadableMap) {
|
|
189
|
+
ChatKindlySDK.setNewContext(context.toStringMap())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
override fun clearNewContext() {
|
|
193
|
+
ChatKindlySDK.clearNewContext()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
override fun triggerDialogue(dialogueId: String) {
|
|
197
|
+
ChatKindlySDK.triggerDialogue(dialogueId)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
override fun clearTriggerDialogue() {
|
|
201
|
+
ChatKindlySDK.clearTriggerDialog()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
override fun handleUrl(url: String, promise: Promise) {
|
|
205
|
+
val act = currentActivity
|
|
206
|
+
if (act == null) {
|
|
207
|
+
promise.resolve(false)
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
promise.resolve(ChatKindlySDK.handleUrl(act, Uri.parse(url)))
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// MARK: - Theme
|
|
214
|
+
|
|
215
|
+
override fun setCustomTheme(colors: ReadableMap) {
|
|
216
|
+
fun color(key: String): Color? =
|
|
217
|
+
if (colors.hasKey(key) && !colors.isNull(key)) Color(colors.getInt(key)) else null
|
|
218
|
+
|
|
219
|
+
ChatKindlySDK.setCustomTheme(
|
|
220
|
+
background = color("background"),
|
|
221
|
+
botMessageBackground = color("botMessageBackground"),
|
|
222
|
+
botMessageText = color("botMessageText"),
|
|
223
|
+
buttonBackground = color("buttonBackground"),
|
|
224
|
+
buttonOutline = color("buttonOutline"),
|
|
225
|
+
buttonText = color("buttonText"),
|
|
226
|
+
chatLogElements = color("chatLogElements"),
|
|
227
|
+
// Flutter/RN expose navBarBackground / navBarText (iOS naming);
|
|
228
|
+
// Android maps them to headerBackground / headerText.
|
|
229
|
+
headerBackground = color("navBarBackground"),
|
|
230
|
+
headerText = color("navBarText"),
|
|
231
|
+
inputBackground = color("inputBackground"),
|
|
232
|
+
inputText = color("inputText"),
|
|
233
|
+
userMessageBackground = color("userMessageBackground"),
|
|
234
|
+
userMessageText = color("userMessageText"),
|
|
235
|
+
maintenanceHeaderBackground = color("maintenanceHeaderBackground"),
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
override fun clearCustomTheme() {
|
|
240
|
+
ChatKindlySDK.clearCustomTheme()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// MARK: - Push
|
|
244
|
+
|
|
245
|
+
override fun setAPNSDeviceToken(token: String) {
|
|
246
|
+
// iOS-only — accept silently.
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
override fun saveNotificationToken(token: String) {
|
|
250
|
+
ChatKindlySDK.saveNotificationToken(token)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
override fun handleNotification(data: ReadableMap) {
|
|
254
|
+
val rm = mapToRemoteMessage(data) ?: return
|
|
255
|
+
ChatKindlySDK.handleNotification(rm)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override fun isKindlyNotification(data: ReadableMap): Boolean {
|
|
259
|
+
val rm = mapToRemoteMessage(data) ?: return false
|
|
260
|
+
return ChatKindlySDK.isKindlyNotification(rm)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// MARK: - State / settings
|
|
264
|
+
|
|
265
|
+
override fun saveAuthToken(token: String, promise: Promise) {
|
|
266
|
+
// Android handles auth via AuthTokenCallback; accept symmetrically.
|
|
267
|
+
promise.resolve(true)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
override fun isChatDisplayed(promise: Promise) {
|
|
271
|
+
// Not exposed by Android EntryPoint — always false (matches Flutter SDK).
|
|
272
|
+
promise.resolve(false)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
override fun setVerboseLogging(enabled: Boolean) {
|
|
276
|
+
ChatKindlySDK.verboseLogging = enabled
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
override fun setCrashReporting(enabled: Boolean) {
|
|
280
|
+
ChatKindlySDK.enableCrashReporting = enabled
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// MARK: - Round-trip responders (called by JS)
|
|
284
|
+
|
|
285
|
+
override fun respondToAuthToken(requestId: String, token: String?) {
|
|
286
|
+
authPending.remove(requestId)?.complete(token)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
override fun respondToShouldHandle(requestId: String, value: Boolean) {
|
|
290
|
+
shouldHandlePending.remove(requestId)?.complete(value)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// MARK: - Delegate gating
|
|
294
|
+
|
|
295
|
+
override fun setDelegate(
|
|
296
|
+
onButtonPressed: Boolean,
|
|
297
|
+
shouldHandleLink: Boolean,
|
|
298
|
+
shouldHandleNotification: Boolean,
|
|
299
|
+
onHandover: Boolean,
|
|
300
|
+
) {
|
|
301
|
+
val bridge = delegateBridge ?: KindlyDelegateBridge(this)
|
|
302
|
+
bridge.hasButtonPressed = onButtonPressed
|
|
303
|
+
bridge.hasShouldHandleLink = shouldHandleLink
|
|
304
|
+
bridge.hasShouldHandleNotification = shouldHandleNotification
|
|
305
|
+
delegateBridge = bridge
|
|
306
|
+
|
|
307
|
+
val anySet = onButtonPressed || shouldHandleLink || shouldHandleNotification
|
|
308
|
+
ChatKindlySDK.`interface` = if (anySet) bridge else null
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
override fun callHandover() {
|
|
312
|
+
val listener = object : CallbackHandover {
|
|
313
|
+
override fun callHandover() {
|
|
314
|
+
emitEvent("KindlyOnHandover", null)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
ChatKindlySDK.callHandover(listener)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// MARK: - Event emit helper
|
|
321
|
+
|
|
322
|
+
internal fun emitEvent(name: String, body: WritableMap?) {
|
|
323
|
+
context
|
|
324
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
325
|
+
.emit(name, body)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Block the calling thread for up to 5s while JS computes a Bool answer.
|
|
330
|
+
* Default `true` on timeout / no-listener — never hang chat UI.
|
|
331
|
+
*/
|
|
332
|
+
internal fun blockingShouldHandle(eventName: String, payload: WritableMap): Boolean {
|
|
333
|
+
val requestId = UUID.randomUUID().toString()
|
|
334
|
+
val deferred = CompletableDeferred<Boolean>()
|
|
335
|
+
shouldHandlePending[requestId] = deferred
|
|
336
|
+
payload.putString("requestId", requestId)
|
|
337
|
+
emitEvent(eventName, payload)
|
|
338
|
+
return runBlocking {
|
|
339
|
+
withTimeoutOrNull(5_000) { deferred.await() } ?: run {
|
|
340
|
+
shouldHandlePending.remove(requestId)
|
|
341
|
+
true
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
companion object {
|
|
347
|
+
const val NAME = "KindlyReactNative"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// MARK: - Helpers
|
|
352
|
+
|
|
353
|
+
private fun ReadableMap.toStringMap(): Map<String, String> {
|
|
354
|
+
val out = HashMap<String, String>()
|
|
355
|
+
val iter = this.keySetIterator()
|
|
356
|
+
while (iter.hasNextKey()) {
|
|
357
|
+
val k = iter.nextKey()
|
|
358
|
+
val v = this.getString(k) ?: continue
|
|
359
|
+
out[k] = v
|
|
360
|
+
}
|
|
361
|
+
return out
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private fun serializeMessageChat(msg: MessageChat): WritableMap {
|
|
365
|
+
return Arguments.createMap().apply {
|
|
366
|
+
putString("id", msg.id)
|
|
367
|
+
putString("text", msg.message)
|
|
368
|
+
putString("created", msg.created)
|
|
369
|
+
putString("sender", msg.sender)
|
|
370
|
+
putString("chatId", msg.chatId)
|
|
371
|
+
putBoolean("isMine", msg.isMine)
|
|
372
|
+
putString("exchangeId", msg.exchangeId)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private fun serializeButtonChat(button: ButtonChat): WritableMap {
|
|
377
|
+
return Arguments.createMap().apply {
|
|
378
|
+
putString("id", button.id)
|
|
379
|
+
putString("type", button.buttonType)
|
|
380
|
+
putString("label", button.label)
|
|
381
|
+
putString("value", button.value)
|
|
382
|
+
putInt("index", button.index)
|
|
383
|
+
putBoolean("hasBeenSelected", button.clicked)
|
|
384
|
+
putString("languageCode", button.languageCode)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private fun serializeExternalNotification(notification: ExternalNotification): WritableMap {
|
|
389
|
+
return Arguments.createMap().apply {
|
|
390
|
+
putString("id", notification.id)
|
|
391
|
+
putString("title", notification.title)
|
|
392
|
+
putString("body", notification.body)
|
|
393
|
+
val data = Arguments.createMap()
|
|
394
|
+
notification.data.forEach { (k, v) -> data.putString(k.toString(), v.toString()) }
|
|
395
|
+
putMap("data", data)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private fun mapToRemoteMessage(data: ReadableMap): RemoteMessage? {
|
|
400
|
+
val builder = RemoteMessage.Builder("kindly_rn@gcm.googleapis.com")
|
|
401
|
+
val iter = data.keySetIterator()
|
|
402
|
+
while (iter.hasNextKey()) {
|
|
403
|
+
val k = iter.nextKey()
|
|
404
|
+
val v = data.getString(k) ?: continue
|
|
405
|
+
builder.addData(k, v)
|
|
406
|
+
}
|
|
407
|
+
return builder.build()
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Bridges the Android SDK's `KindlySDKInteraction` interface to the JS-side
|
|
412
|
+
* callbacks registered via `KindlySDK.setDelegate(...)`.
|
|
413
|
+
*/
|
|
414
|
+
private class KindlyDelegateBridge(
|
|
415
|
+
private val module: KindlyReactNativeModule,
|
|
416
|
+
) : KindlySDKInteraction {
|
|
417
|
+
var hasButtonPressed: Boolean = false
|
|
418
|
+
var hasShouldHandleLink: Boolean = false
|
|
419
|
+
var hasShouldHandleNotification: Boolean = false
|
|
420
|
+
|
|
421
|
+
override fun didPressButton(chatButton: ButtonChat, chatLog: List<MessageChat>) {
|
|
422
|
+
if (!hasButtonPressed) return
|
|
423
|
+
val payload = Arguments.createMap().apply {
|
|
424
|
+
putMap("button", serializeButtonChat(chatButton))
|
|
425
|
+
val arr = Arguments.createArray()
|
|
426
|
+
chatLog.forEach { arr.pushMap(serializeMessageChat(it)) }
|
|
427
|
+
putArray("chatLog", arr)
|
|
428
|
+
}
|
|
429
|
+
module.emitEvent("KindlyOnButtonPressed", payload)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
override fun shouldHandleLink(url: String): Boolean {
|
|
433
|
+
if (!hasShouldHandleLink) return true
|
|
434
|
+
val payload = Arguments.createMap().apply { putString("url", url) }
|
|
435
|
+
return module.blockingShouldHandle("KindlyShouldHandleLink", payload)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
override fun shouldHandleNotification(notification: ExternalNotification): Boolean {
|
|
439
|
+
if (!hasShouldHandleNotification) return true
|
|
440
|
+
val payload = Arguments.createMap().apply {
|
|
441
|
+
putMap("notification", serializeExternalNotification(notification))
|
|
442
|
+
}
|
|
443
|
+
return module.blockingShouldHandle("KindlyShouldHandleNotification", payload)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.kindlyai.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Auto-linking entry point for the Kindly React Native package.
|
|
11
|
+
*
|
|
12
|
+
* Registered in customer apps automatically via `react-native.config.js`
|
|
13
|
+
* (autolinking) — they don't need to add anything to MainApplication.kt.
|
|
14
|
+
*
|
|
15
|
+
* Returns the single TurboModule we ship: `KindlyReactNativeModule`.
|
|
16
|
+
*/
|
|
17
|
+
class KindlyReactNativePackage : BaseReactPackage() {
|
|
18
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
19
|
+
return if (name == KindlyReactNativeModule.NAME) {
|
|
20
|
+
KindlyReactNativeModule(reactContext)
|
|
21
|
+
} else {
|
|
22
|
+
null
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
27
|
+
mapOf(
|
|
28
|
+
KindlyReactNativeModule.NAME to ReactModuleInfo(
|
|
29
|
+
/* name = */ KindlyReactNativeModule.NAME,
|
|
30
|
+
/* className = */ KindlyReactNativeModule.NAME,
|
|
31
|
+
/* canOverrideExistingModule = */ false,
|
|
32
|
+
/* needsEagerInit = */ false,
|
|
33
|
+
/* isCxxModule = */ false,
|
|
34
|
+
/* isTurboModule = */ true,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>AvailableLibraries</key>
|
|
6
|
+
<array>
|
|
7
|
+
<dict>
|
|
8
|
+
<key>BinaryPath</key>
|
|
9
|
+
<string>Kindly.framework/Kindly</string>
|
|
10
|
+
<key>LibraryIdentifier</key>
|
|
11
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
12
|
+
<key>LibraryPath</key>
|
|
13
|
+
<string>Kindly.framework</string>
|
|
14
|
+
<key>SupportedArchitectures</key>
|
|
15
|
+
<array>
|
|
16
|
+
<string>arm64</string>
|
|
17
|
+
<string>x86_64</string>
|
|
18
|
+
</array>
|
|
19
|
+
<key>SupportedPlatform</key>
|
|
20
|
+
<string>ios</string>
|
|
21
|
+
<key>SupportedPlatformVariant</key>
|
|
22
|
+
<string>simulator</string>
|
|
23
|
+
</dict>
|
|
24
|
+
<dict>
|
|
25
|
+
<key>BinaryPath</key>
|
|
26
|
+
<string>Kindly.framework/Kindly</string>
|
|
27
|
+
<key>LibraryIdentifier</key>
|
|
28
|
+
<string>ios-arm64</string>
|
|
29
|
+
<key>LibraryPath</key>
|
|
30
|
+
<string>Kindly.framework</string>
|
|
31
|
+
<key>SupportedArchitectures</key>
|
|
32
|
+
<array>
|
|
33
|
+
<string>arm64</string>
|
|
34
|
+
</array>
|
|
35
|
+
<key>SupportedPlatform</key>
|
|
36
|
+
<string>ios</string>
|
|
37
|
+
</dict>
|
|
38
|
+
</array>
|
|
39
|
+
<key>CFBundlePackageType</key>
|
|
40
|
+
<string>XFWK</string>
|
|
41
|
+
<key>XCFrameworkFormatVersion</key>
|
|
42
|
+
<string>1.0</string>
|
|
43
|
+
</dict>
|
|
44
|
+
</plist>
|
|
Binary file
|