@finos_sdk/sdk-ekyc 1.4.9 → 1.5.1
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/src/main/java/finos/sdk/ekyc/EKYCModule.kt +196 -71
- package/dist/package.json +1 -1
- package/dist/src/components/ExitConfirmSheet.js +6 -3
- package/dist/src/modules/FinosEKYCModule.js +6 -1
- package/package.json +1 -1
- package/src/components/ExitConfirmSheet.tsx +5 -3
- package/src/modules/FinosEKYCModule.ts +6 -1
|
@@ -66,7 +66,10 @@ import java.io.FileInputStream
|
|
|
66
66
|
import java.io.InputStream
|
|
67
67
|
import java.util.Date
|
|
68
68
|
import android.app.Activity
|
|
69
|
+
import android.app.Application
|
|
70
|
+
import android.os.Bundle
|
|
69
71
|
import android.view.ViewGroup
|
|
72
|
+
import java.lang.ref.WeakReference
|
|
70
73
|
|
|
71
74
|
@ReactModule(name = EKYCModule.NAME)
|
|
72
75
|
class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
@@ -74,10 +77,71 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
74
77
|
companion object {
|
|
75
78
|
const val NAME = "EKYCModule"
|
|
76
79
|
private const val TAG = "EKYCModule"
|
|
80
|
+
|
|
81
|
+
// Full class name của SDK activity duy nhất
|
|
82
|
+
private const val SDK_EKYC_ACTIVITY = "finos.sdk.ekyc.sdkui.SDKeKYCActivity"
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
override fun getName(): String = NAME
|
|
80
86
|
|
|
87
|
+
/**
|
|
88
|
+
* WeakReference tới SDK activity đang foreground.
|
|
89
|
+
* Được set/clear bởi ActivityLifecycleCallbacks khi SDK activity resume/pause.
|
|
90
|
+
* @Volatile: viết từ UI thread (lifecycle callback), đọc từ JS bridge thread (showRNExitSheet).
|
|
91
|
+
* WeakReference để tránh memory leak nếu activity bị destroy mà chưa clear.
|
|
92
|
+
*/
|
|
93
|
+
@Volatile
|
|
94
|
+
private var activeSdkActivity: WeakReference<Activity>? = null
|
|
95
|
+
|
|
96
|
+
private val sdkActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
|
|
97
|
+
override fun onActivityResumed(activity: Activity) {
|
|
98
|
+
if (isSDKActivity(activity)) {
|
|
99
|
+
activeSdkActivity = WeakReference(activity)
|
|
100
|
+
Log.d(TAG, "📌 SDK Activity resumed: ${activity.javaClass.simpleName}")
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
override fun onActivityPaused(activity: Activity) {
|
|
104
|
+
if (activeSdkActivity?.get() == activity) {
|
|
105
|
+
activeSdkActivity = null
|
|
106
|
+
Log.d(TAG, "📌 SDK Activity paused: ${activity.javaClass.simpleName}")
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
override fun onActivityDestroyed(activity: Activity) {
|
|
110
|
+
if (activeSdkActivity?.get() == activity) {
|
|
111
|
+
activeSdkActivity = null
|
|
112
|
+
Log.d(TAG, "📌 SDK Activity destroyed: ${activity.javaClass.simpleName}")
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
|
|
116
|
+
override fun onActivityStarted(activity: Activity) {}
|
|
117
|
+
override fun onActivityStopped(activity: Activity) {}
|
|
118
|
+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private fun isSDKActivity(activity: Activity): Boolean {
|
|
122
|
+
return activity.javaClass.name == SDK_EKYC_ACTIVITY
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
init {
|
|
126
|
+
// Đăng ký lifecycle callback để track SDK activity tự động
|
|
127
|
+
val app = reactContext.applicationContext as? Application
|
|
128
|
+
app?.registerActivityLifecycleCallbacks(sdkActivityLifecycleCallbacks)
|
|
129
|
+
?: Log.w(TAG, "⚠️ Could not register ActivityLifecycleCallbacks")
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Lấy SDK activity đang active, fallback về getTrueCurrentActivity() nếu không có.
|
|
134
|
+
*/
|
|
135
|
+
private fun getSDKActivity(): Activity? {
|
|
136
|
+
val sdk = activeSdkActivity?.get()
|
|
137
|
+
if (sdk != null && !sdk.isFinishing && !sdk.isDestroyed) {
|
|
138
|
+
Log.d(TAG, "✅ Using tracked SDK Activity: ${sdk.javaClass.simpleName}")
|
|
139
|
+
return sdk
|
|
140
|
+
}
|
|
141
|
+
Log.d(TAG, "⚠️ No active SDK activity tracked, falling back to getTrueCurrentActivity")
|
|
142
|
+
return getTrueCurrentActivity()
|
|
143
|
+
}
|
|
144
|
+
|
|
81
145
|
/**
|
|
82
146
|
* Reference tới BottomSheetDialog hiện đang hiển thị.
|
|
83
147
|
* @Volatile: resolveExit() chạy trên JS bridge thread, đọc field này được viết bởi UI thread.
|
|
@@ -92,97 +156,153 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
92
156
|
private var exitSheetResolvedViaButton = false
|
|
93
157
|
|
|
94
158
|
private fun getTrueCurrentActivity(): Activity? {
|
|
95
|
-
// Try React Native's currentActivity first
|
|
159
|
+
// Try React Native's currentActivity first as a baseline
|
|
96
160
|
val rnActivity = currentActivity
|
|
97
|
-
|
|
161
|
+
|
|
162
|
+
Log.d(TAG, "🔍 getTrueCurrentActivity check: rnActivity=${rnActivity?.javaClass?.simpleName}")
|
|
163
|
+
|
|
164
|
+
// If rnActivity is already an SDK activity, we can likely trust it
|
|
165
|
+
if (rnActivity != null && rnActivity.javaClass.name.startsWith("finos.sdk.")) {
|
|
166
|
+
Log.d(TAG, "✅ Current activity is already an SDK activity: ${rnActivity.javaClass.simpleName}")
|
|
98
167
|
return rnActivity
|
|
99
168
|
}
|
|
100
169
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
170
|
+
// Use reflection to find the absolute top resumed activity.
|
|
171
|
+
// This is crucial when the SDK is running in a native Activity on top of the RN Activity,
|
|
172
|
+
// and currentActivity might still point to the background Activity.
|
|
173
|
+
try {
|
|
174
|
+
val topActivity = findTopActivityViaReflection()
|
|
175
|
+
if (topActivity != null) {
|
|
176
|
+
Log.d(TAG, "✅ Found top activity via reflection: ${topActivity.javaClass.simpleName}")
|
|
177
|
+
return topActivity
|
|
178
|
+
}
|
|
179
|
+
} catch (e: Exception) {
|
|
180
|
+
Log.e(TAG, "Failed to find top activity via reflection", e)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Log.d(TAG, "⚠️ Fallback to rnActivity: ${rnActivity?.javaClass?.simpleName}")
|
|
184
|
+
return rnActivity
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Finds the top-most resumed activity using ActivityThread's internal records.
|
|
189
|
+
*/
|
|
190
|
+
private fun findTopActivityViaReflection(): Activity? {
|
|
103
191
|
try {
|
|
104
192
|
val activityThreadClass = Class.forName("android.app.ActivityThread")
|
|
105
|
-
val activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null)
|
|
193
|
+
val activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null) ?: return null
|
|
106
194
|
val activitiesField = activityThreadClass.getDeclaredField("mActivities")
|
|
107
195
|
activitiesField.isAccessible = true
|
|
108
196
|
|
|
109
|
-
val activities = activitiesField.get(activityThread) as Map
|
|
110
|
-
Log.d(TAG, "🔍 Scanning ${activities.size} activities...")
|
|
111
|
-
|
|
112
|
-
var topActivity: Activity? = null
|
|
197
|
+
val activities = activitiesField.get(activityThread) as? Map<*, *> ?: return null
|
|
198
|
+
Log.d(TAG, "🔍 Scanning ${activities.size} activities via reflection...")
|
|
113
199
|
|
|
200
|
+
var bestCandidate: Activity? = null
|
|
201
|
+
var highestPriority = -1
|
|
202
|
+
|
|
114
203
|
for (activityRecord in activities.values) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
204
|
+
if (activityRecord == null) continue
|
|
205
|
+
try {
|
|
206
|
+
val activityRecordClass = activityRecord.javaClass
|
|
207
|
+
val activityField = activityRecordClass.getDeclaredField("activity")
|
|
208
|
+
activityField.isAccessible = true
|
|
209
|
+
val activity = activityField.get(activityRecord) as? Activity ?: continue
|
|
210
|
+
|
|
211
|
+
if (activity.isFinishing) continue
|
|
212
|
+
|
|
213
|
+
val pausedField = activityRecordClass.getDeclaredField("paused")
|
|
214
|
+
pausedField.isAccessible = true
|
|
215
|
+
val isPaused = pausedField.get(activityRecord) as Boolean
|
|
216
|
+
|
|
217
|
+
val stoppedField = activityRecordClass.getDeclaredField("stopped")
|
|
218
|
+
stoppedField.isAccessible = true
|
|
219
|
+
val isStopped = stoppedField.get(activityRecord) as Boolean
|
|
220
|
+
|
|
221
|
+
if (isStopped) continue
|
|
222
|
+
|
|
223
|
+
// Identity check: prioritize known SDK activity patterns
|
|
224
|
+
val className = activity.javaClass.name
|
|
225
|
+
val isSDK = className.startsWith("finos.sdk.") ||
|
|
226
|
+
className.contains("EKYC", ignoreCase = true) ||
|
|
227
|
+
className.contains("Liveness", ignoreCase = true) ||
|
|
228
|
+
className.contains("OCR", ignoreCase = true) ||
|
|
229
|
+
className.contains("NFC", ignoreCase = true)
|
|
230
|
+
|
|
231
|
+
// Priority scoring:
|
|
232
|
+
// 3: Resumed SDK Activity
|
|
233
|
+
// 2: Resumed App Activity
|
|
234
|
+
// 1: Paused SDK Activity
|
|
235
|
+
// 0: Paused App Activity
|
|
236
|
+
val currentPriority = (if (isSDK) 1 else 0) + (if (!isPaused) 2 else 0)
|
|
237
|
+
|
|
238
|
+
Log.v(TAG, " [Audit] ${activity.javaClass.simpleName} -> Prio: $currentPriority (SDK=$isSDK, Paused=$isPaused)")
|
|
239
|
+
|
|
240
|
+
if (currentPriority > highestPriority) {
|
|
241
|
+
highestPriority = currentPriority
|
|
242
|
+
bestCandidate = activity
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Optimization: Found the absolute best target
|
|
246
|
+
if (highestPriority == 3) break
|
|
247
|
+
|
|
248
|
+
} catch (e: Exception) {
|
|
249
|
+
// Silently continue for individual activity record failures
|
|
134
250
|
}
|
|
135
251
|
}
|
|
136
|
-
|
|
252
|
+
|
|
253
|
+
if (bestCandidate != null) {
|
|
254
|
+
Log.d(TAG, "✅ Best candidate found: ${bestCandidate.javaClass.simpleName} (Prio: $highestPriority)")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return bestCandidate
|
|
137
258
|
} catch (e: Exception) {
|
|
138
|
-
Log.e(TAG, "
|
|
259
|
+
Log.e(TAG, "❌ Critical error in findTopActivityViaReflection", e)
|
|
260
|
+
return null
|
|
139
261
|
}
|
|
140
|
-
|
|
141
|
-
return rnActivity
|
|
142
262
|
}
|
|
143
263
|
|
|
144
264
|
@ReactMethod
|
|
145
265
|
fun showRNExitSheet(bundleName: String, initialProps: ReadableMap, promise: Promise) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (activity.isFinishing || activity.isDestroyed) {
|
|
153
|
-
Log.e(TAG, "❌ showRNExitSheet failed: Activity is finishing/destroyed")
|
|
154
|
-
promise.reject("ACTIVITY_INVALID", "Activity is finishing or destroyed")
|
|
155
|
-
return
|
|
156
|
-
}
|
|
266
|
+
Handler(Looper.getMainLooper()).post {
|
|
267
|
+
val activity = getSDKActivity() ?: run {
|
|
268
|
+
Log.e(TAG, "❌ showRNExitSheet failed: No visible activity found")
|
|
269
|
+
promise.reject("NO_ACTIVITY", "No visible activity found")
|
|
270
|
+
return@post
|
|
271
|
+
}
|
|
157
272
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
fun safeReject(code: String, msg: String, e: Throwable? = null) {
|
|
164
|
-
if (promiseSettled.compareAndSet(false, true)) {
|
|
165
|
-
if (e != null) promise.reject(code, msg, e) else promise.reject(code, msg)
|
|
273
|
+
if (activity.isFinishing || (android.os.Build.VERSION.SDK_INT >= 17 && activity.isDestroyed)) {
|
|
274
|
+
Log.e(TAG, "❌ showRNExitSheet failed: Activity is finishing or destroyed")
|
|
275
|
+
promise.reject("ACTIVITY_INVALID", "Activity is finishing or destroyed")
|
|
276
|
+
return@post
|
|
166
277
|
}
|
|
167
|
-
}
|
|
168
278
|
|
|
169
|
-
|
|
279
|
+
// Guard chống double-resolve/reject promise
|
|
280
|
+
val promiseSettled = java.util.concurrent.atomic.AtomicBoolean(false)
|
|
281
|
+
fun safeResolve(value: Any?) {
|
|
282
|
+
if (promiseSettled.compareAndSet(false, true)) promise.resolve(value)
|
|
283
|
+
}
|
|
284
|
+
fun safeReject(code: String, msg: String, e: Throwable? = null) {
|
|
285
|
+
if (promiseSettled.compareAndSet(false, true)) {
|
|
286
|
+
if (e != null) promise.reject(code, msg, e) else promise.reject(code, msg)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
170
289
|
try {
|
|
171
290
|
val activityName = activity.javaClass.simpleName
|
|
172
291
|
Log.d(TAG, "▶️ showRNExitSheet: Top Activity=$activityName, bundle=$bundleName")
|
|
173
292
|
|
|
174
293
|
val reactApplication = activity.application as? ReactApplication
|
|
175
294
|
if (reactApplication == null) {
|
|
176
|
-
Log.e(TAG, "❌ Application does not implement ReactApplication")
|
|
295
|
+
Log.e(TAG, "❌ showRNExitSheet failed: Application class (${activity.application.javaClass.name}) does not implement ReactApplication. Please ensure your Application class implements ReactApplication or provide a way for the SDK to access the ReactInstanceManager.")
|
|
177
296
|
safeReject("NO_REACT_APP", "Application does not implement ReactApplication")
|
|
178
|
-
return@
|
|
297
|
+
return@post
|
|
179
298
|
}
|
|
180
299
|
val reactInstanceManager = reactApplication.reactNativeHost.reactInstanceManager
|
|
181
300
|
|
|
182
301
|
if (reactInstanceManager.currentReactContext != null) {
|
|
302
|
+
Log.d(TAG, "✅ ReactContext is ready, presenting sheet...")
|
|
183
303
|
presentRNBottomSheet(activity, reactInstanceManager, bundleName, initialProps,
|
|
184
304
|
::safeResolve, ::safeReject)
|
|
185
|
-
return@
|
|
305
|
+
return@post
|
|
186
306
|
}
|
|
187
307
|
|
|
188
308
|
// Context chưa sẵn sàng: chờ listener
|
|
@@ -212,7 +332,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
212
332
|
Log.d(TAG, "✅ ReactContext init xong giữa lúc add listener — present luôn")
|
|
213
333
|
presentRNBottomSheet(activity, reactInstanceManager, bundleName, initialProps,
|
|
214
334
|
::safeResolve, ::safeReject)
|
|
215
|
-
return@
|
|
335
|
+
return@post
|
|
216
336
|
}
|
|
217
337
|
|
|
218
338
|
// Chỉ trigger background create nếu chưa start (tránh IllegalStateException)
|
|
@@ -250,12 +370,17 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
250
370
|
safeReject: (String, String, Throwable?) -> Unit
|
|
251
371
|
) {
|
|
252
372
|
try {
|
|
373
|
+
val minHeightPx = (400 * activity.resources.displayMetrics.density).toInt()
|
|
253
374
|
val container = LinearLayout(activity).apply {
|
|
254
375
|
orientation = LinearLayout.VERTICAL
|
|
255
376
|
setBackgroundColor(Color.TRANSPARENT)
|
|
377
|
+
minimumHeight = minHeightPx
|
|
256
378
|
}
|
|
257
379
|
|
|
258
380
|
val rootView = ReactRootView(activity)
|
|
381
|
+
// Hardening: Set a minimum height to ensure the sheet is visible even before RN finishes layout
|
|
382
|
+
rootView.minimumHeight = minHeightPx
|
|
383
|
+
|
|
259
384
|
rootView.layoutParams = LinearLayout.LayoutParams(
|
|
260
385
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
261
386
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
@@ -328,7 +453,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
328
453
|
|
|
329
454
|
bottomSheetDialog.show()
|
|
330
455
|
|
|
331
|
-
Log.d(TAG, "✅ showRNExitSheet: BottomSheetDialog shown
|
|
456
|
+
Log.d(TAG, "✅ showRNExitSheet: BottomSheetDialog shown on ${activity.javaClass.simpleName}, bundle=$bundleName")
|
|
332
457
|
safeResolve(true)
|
|
333
458
|
} catch (e: Exception) {
|
|
334
459
|
Log.e(TAG, "❌ Exception in presentRNBottomSheet", e)
|
|
@@ -903,12 +1028,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
903
1028
|
isActiveLiveness = isActiveLiveness ?: false,
|
|
904
1029
|
isActiveLivenessColor = isActiveLivenessColor ?: false,
|
|
905
1030
|
isShowCameraFont = isShowCameraFont ?: true,
|
|
906
|
-
// Custom actions từ checkboxes (nếu có chọn)
|
|
907
|
-
// Nếu có actions được chọn → sử dụng customActions
|
|
908
|
-
// Nếu không có actions nào được chọn → sử dụng random actions với activeActionCount
|
|
909
1031
|
customActions = customActions,
|
|
910
|
-
// Number of random actions (1-10), only used when customActions = null
|
|
911
|
-
// activeActionCount = 2 → 2 random actions + STRAIGHT
|
|
912
1032
|
activeActionCount = activeActionCount ?: 2,
|
|
913
1033
|
forceCaptureTimeout = (forceCaptureTimeout ?: 0.0).toLong(),
|
|
914
1034
|
selfieImage = imageFile,
|
|
@@ -2645,15 +2765,16 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
2645
2765
|
|
|
2646
2766
|
@ReactMethod
|
|
2647
2767
|
fun setExitSheetHeight(heightDp: Float) {
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2768
|
+
Handler(Looper.getMainLooper()).post {
|
|
2769
|
+
val activity = getTrueCurrentActivity() ?: return@post
|
|
2770
|
+
val heightPx = (heightDp * activity.resources.displayMetrics.density).toInt()
|
|
2771
|
+
Log.d(TAG, "▶️ setExitSheetHeight: ${heightDp}dp → ${heightPx}px")
|
|
2772
|
+
|
|
2773
|
+
val sheet = currentExitSheet ?: return@post
|
|
2774
|
+
if (!sheet.isShowing) return@post
|
|
2654
2775
|
val bs = sheet.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
|
2655
|
-
?: return@
|
|
2656
|
-
val lp = bs.layoutParams ?: return@
|
|
2776
|
+
?: return@post
|
|
2777
|
+
val lp = bs.layoutParams ?: return@post
|
|
2657
2778
|
lp.height = heightPx
|
|
2658
2779
|
bs.layoutParams = lp
|
|
2659
2780
|
val behavior = com.google.android.material.bottomsheet.BottomSheetBehavior.from(bs)
|
|
@@ -2675,6 +2796,10 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
|
|
2675
2796
|
override fun onCatalystInstanceDestroy() {
|
|
2676
2797
|
super.onCatalystInstanceDestroy()
|
|
2677
2798
|
SDKeKYCExitHandlerManager.uiListener = null
|
|
2799
|
+
// Unregister lifecycle callback để tránh memory leak
|
|
2800
|
+
val app = reactApplicationContext.applicationContext as? Application
|
|
2801
|
+
app?.unregisterActivityLifecycleCallbacks(sdkActivityLifecycleCallbacks)
|
|
2802
|
+
activeSdkActivity = null
|
|
2678
2803
|
// Dismiss sheet nếu vẫn đang hiển thị khi RN catalyst instance destroy
|
|
2679
2804
|
val sheet = currentExitSheet
|
|
2680
2805
|
currentExitSheet = null
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finos_sdk/sdk-ekyc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -5,7 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_native_1 = require("react-native");
|
|
8
|
-
|
|
8
|
+
// Dùng NativeModules trực tiếp để tránh circular dependency với FinosEKYCModule
|
|
9
|
+
// (ExitConfirmSheet được load qua ExitSheetWrapper được import bởi FinosEKYCModule)
|
|
9
10
|
const BellIcon = () => (<react_native_1.View style={styles.bellWrapper}>
|
|
10
11
|
<react_native_1.View style={styles.bellMount}/>
|
|
11
12
|
<react_native_1.View style={styles.bellBody}/>
|
|
@@ -17,10 +18,12 @@ const ExitConfirmSheet = (props) => {
|
|
|
17
18
|
const confirmText = props.confirmText || 'Đồng ý';
|
|
18
19
|
const cancelText = props.cancelText || 'Ở lại';
|
|
19
20
|
const handleConfirm = async () => {
|
|
20
|
-
|
|
21
|
+
var _a, _b;
|
|
22
|
+
await ((_b = (_a = react_native_1.NativeModules.EKYCModule) === null || _a === void 0 ? void 0 : _a.resolveExit) === null || _b === void 0 ? void 0 : _b.call(_a, 'CONFIRM'));
|
|
21
23
|
};
|
|
22
24
|
const handleCancel = async () => {
|
|
23
|
-
|
|
25
|
+
var _a, _b;
|
|
26
|
+
await ((_b = (_a = react_native_1.NativeModules.EKYCModule) === null || _a === void 0 ? void 0 : _a.resolveExit) === null || _b === void 0 ? void 0 : _b.call(_a, 'CANCEL'));
|
|
24
27
|
};
|
|
25
28
|
return (<react_native_1.View style={styles.container} onLayout={(e) => {
|
|
26
29
|
var _a, _b;
|
|
@@ -826,7 +826,11 @@ const isMethod = (prop) => {
|
|
|
826
826
|
prop === 'checkC06' ||
|
|
827
827
|
prop === 'startOcr' ||
|
|
828
828
|
prop === 'startLiveness' ||
|
|
829
|
-
prop === 'startFaceCompare'
|
|
829
|
+
prop === 'startFaceCompare' ||
|
|
830
|
+
prop === 'registerExitHandler' ||
|
|
831
|
+
prop === 'resolveExit' ||
|
|
832
|
+
prop === 'showRNExitSheet' ||
|
|
833
|
+
prop === 'setExitSheetComponent';
|
|
830
834
|
};
|
|
831
835
|
// Create a comprehensive stub object with all methods to prevent undefined errors
|
|
832
836
|
const createFinosEKYCStub = () => {
|
|
@@ -856,6 +860,7 @@ const createFinosEKYCStub = () => {
|
|
|
856
860
|
'startEkycUI', 'sendOtp', 'verifyOtp', 'resendOtp', 'initializeESign', 'openSessionId',
|
|
857
861
|
'registerDevice', 'listCerts', 'verifyCert', 'listSignRequest', 'confirmSign',
|
|
858
862
|
'registerRemoteSigning', 'signPdf', 'sendConfirmationDocument',
|
|
863
|
+
'registerExitHandler', 'resolveExit', 'showRNExitSheet', 'setExitSheetComponent',
|
|
859
864
|
'onResume', 'onPause', 'isSDKReady', 'getSDKInfo'
|
|
860
865
|
];
|
|
861
866
|
otherMethods.forEach(method => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finos_sdk/sdk-ekyc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
TouchableOpacity,
|
|
7
7
|
NativeModules,
|
|
8
8
|
} from 'react-native';
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// Dùng NativeModules trực tiếp để tránh circular dependency với FinosEKYCModule
|
|
11
|
+
// (ExitConfirmSheet được load qua ExitSheetWrapper được import bởi FinosEKYCModule)
|
|
10
12
|
|
|
11
13
|
const BellIcon = () => (
|
|
12
14
|
<View style={styles.bellWrapper}>
|
|
@@ -23,11 +25,11 @@ const ExitConfirmSheet = (props: any) => {
|
|
|
23
25
|
const cancelText = props.cancelText || 'Ở lại';
|
|
24
26
|
|
|
25
27
|
const handleConfirm = async () => {
|
|
26
|
-
await
|
|
28
|
+
await NativeModules.EKYCModule?.resolveExit?.('CONFIRM');
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const handleCancel = async () => {
|
|
30
|
-
await
|
|
32
|
+
await NativeModules.EKYCModule?.resolveExit?.('CANCEL');
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
return (
|
|
@@ -1001,7 +1001,11 @@ const isMethod = (prop: string | symbol): boolean => {
|
|
|
1001
1001
|
prop === 'checkC06' ||
|
|
1002
1002
|
prop === 'startOcr' ||
|
|
1003
1003
|
prop === 'startLiveness' ||
|
|
1004
|
-
prop === 'startFaceCompare'
|
|
1004
|
+
prop === 'startFaceCompare' ||
|
|
1005
|
+
prop === 'registerExitHandler' ||
|
|
1006
|
+
prop === 'resolveExit' ||
|
|
1007
|
+
prop === 'showRNExitSheet' ||
|
|
1008
|
+
prop === 'setExitSheetComponent';
|
|
1005
1009
|
};
|
|
1006
1010
|
|
|
1007
1011
|
// Create a comprehensive stub object with all methods to prevent undefined errors
|
|
@@ -1035,6 +1039,7 @@ const createFinosEKYCStub = (): FinosEKYCModule => {
|
|
|
1035
1039
|
'startEkycUI', 'sendOtp', 'verifyOtp', 'resendOtp', 'initializeESign', 'openSessionId',
|
|
1036
1040
|
'registerDevice', 'listCerts', 'verifyCert', 'listSignRequest', 'confirmSign',
|
|
1037
1041
|
'registerRemoteSigning', 'signPdf', 'sendConfirmationDocument',
|
|
1042
|
+
'registerExitHandler', 'resolveExit', 'showRNExitSheet', 'setExitSheetComponent',
|
|
1038
1043
|
'onResume', 'onPause', 'isSDKReady', 'getSDKInfo'
|
|
1039
1044
|
];
|
|
1040
1045
|
|