package ai.devrev.sdk.bridge.reactnative import ai.devrev.sdk.DevRev import ai.devrev.sdk.addSessionProperties import ai.devrev.sdk.clearSessionProperties import ai.devrev.sdk.endTimer import ai.devrev.sdk.markSensitiveViews import ai.devrev.sdk.model.Identity import ai.devrev.sdk.model.FeatureConfiguration import ai.devrev.sdk.pauseRecording import ai.devrev.sdk.pauseUserInteractionTracking import ai.devrev.sdk.resumeAllMonitoring import ai.devrev.sdk.resumeRecording import ai.devrev.sdk.resumeUserInteractionTracking import ai.devrev.sdk.showSupport import ai.devrev.sdk.startRecording import ai.devrev.sdk.startTimer import ai.devrev.sdk.stopAllMonitoring import ai.devrev.sdk.stopRecording import ai.devrev.sdk.trackEvent import ai.devrev.sdk.trackScreenName import ai.devrev.sdk.unmarkSensitiveViews import ai.devrev.sdk.setInScreenTransitioning import ai.devrev.sdk.processAllOnDemandSessions import ai.devrev.sdk.setAppFrameworkInfo import ai.devrev.sdk.captureError import android.app.Activity import android.content.Context import android.util.Log import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.ReadableMapKeySetIterator import com.facebook.react.bridge.ReadableType import com.facebook.react.modules.core.DeviceEventManagerModule class DevRevSDKModuleImpl( private val reactContext: ReactApplicationContext ) : IDevRevSDKModule { private val eventEmitter by lazy { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) } private var inAppLinkHandler: ((String) -> Unit)? = null /* * Configuration */ override fun configure(appId: String, version: String, featureConfiguration: ReadableMap?) { val config = if (featureConfiguration != null) { FeatureConfigurationParser.parseFeatureConfiguration(featureConfiguration) ?: FeatureConfiguration.default } else { FeatureConfiguration.default } DevRev.configure(reactContext, appId, config) DevRev.setAppFrameworkInfo(reactContext, "React Native", version) } override fun updateFeatureConfiguration(featureConfiguration: ReadableMap) { val config = FeatureConfigurationParser.parseFeatureConfiguration(featureConfiguration) ?: FeatureConfiguration.default DevRev.updateFeatureConfiguration(reactContext, config) } /* * Identification */ override fun identifyUnverifiedUser(userId: String, organizationId: String?) { DevRev.identifyUnverifiedUser( Identity(userId = userId, organizationId = organizationId) ) } override fun identifyVerifiedUser(userId: String, sessionToken: String) { DevRev.identifyVerifiedUser(userId, sessionToken) } override fun identifyAnonymousUser(userId: String) { DevRev.identifyAnonymousUser(userId) } override fun updateUser(identity: ReadableMap) { val identityLocal = IdentityParser.parseIdentity(identity) DevRev.updateUser(identityLocal) } override fun logout(deviceId: String) { DevRev.logout(reactContext, deviceId) } /* * Support */ override fun showSupport() { val context = getCurrentActivity() ?: reactContext if (context is Activity) { Log.d("DEVREV SDK", "Context is activity") } if (context == null) { Log.e("DEVREV SDK", "Can't open support, context is null") return } DevRev.showSupport(context) } override fun createSupportConversation(prefillMessage: String?) { val context = getCurrentActivity() ?: reactContext if (context is Activity) { Log.d("DEVREV SDK", "Context is activity") } if (context == null) { Log.e("DEVREV SDK", "Can't open support, context is null") return } DevRev.createSupportConversation(context, prefillMessage) } /* * Analytics */ override fun trackEvent(name: String, properties: ReadableMap?) { val eventProps = HashMap() properties?.keySetIterator()?.let { iterator: ReadableMapKeySetIterator -> while (iterator.hasNextKey()) { val key = iterator.nextKey() val value = properties.getString(key) if (value != null) { eventProps[key] = value } } } DevRev.trackEvent(name, eventProps) } /* * Session recording */ override fun startRecording() { DevRev.startRecording(reactContext) } override fun stopRecording() { DevRev.stopRecording() } override fun pauseRecording() { DevRev.pauseRecording() } override fun resumeRecording() { DevRev.resumeRecording() } override fun pauseUserInteractionTracking() { DevRev.pauseUserInteractionTracking() } override fun resumeUserInteractionTracking() { DevRev.resumeUserInteractionTracking() } override fun processAllOnDemandSessions() { DevRev.processAllOnDemandSessions() } /* * Session properties */ override fun addSessionProperties(properties: ReadableMap?) { val sessionProps = HashMap() properties?.keySetIterator()?.let { iterator -> while (iterator.hasNextKey()) { val key = iterator.nextKey() when (properties.getType(key)) { ReadableType.String -> properties.getString(key)?.let { sessionProps[key] = it } ReadableType.Boolean -> properties.getBoolean(key).let { sessionProps[key] = it } ReadableType.Number -> properties.getDouble(key).let { sessionProps[key] = it } else -> { Log.w("DEVREV SDK", "Unsupported type for key: $key") } } } } DevRev.addSessionProperties(sessionProps) } override fun clearSessionProperties() { DevRev.clearSessionProperties() } /* * Sensitive field masking */ override fun markSensitiveViews(views: List) { DevRev.markSensitiveViews(views) } override fun unmarkSensitiveViews(views: List) { DevRev.unmarkSensitiveViews(views) } /* * Timer control */ override fun startTimer(name: String, properties: ReadableMap?) { val timerProps = HashMap() properties?.keySetIterator()?.let { iterator: ReadableMapKeySetIterator -> while (iterator.hasNextKey()) { val key = iterator.nextKey() val value = properties.getString(key) if (value != null) { timerProps[key] = value } } } DevRev.startTimer(name, timerProps) } override fun endTimer(name: String, properties: ReadableMap?) { val timerProps = HashMap() properties?.keySetIterator()?.let { iterator: ReadableMapKeySetIterator -> while (iterator.hasNextKey()) { val key = iterator.nextKey() val value = properties.getString(key) if (value != null) { timerProps[key] = value } } } DevRev.endTimer(name, timerProps) } /* * Monitoring */ override fun resumeAllMonitoring() { DevRev.resumeAllMonitoring() } override fun stopAllMonitoring() { DevRev.stopAllMonitoring() } /* * Screen tracking */ override fun trackScreenName(name: String) { DevRev.trackScreenName(name) } /* * Push notifications */ override fun registerDeviceToken(deviceToken: String, deviceId: String) { DevRev.registerDeviceToken(reactContext, deviceToken, deviceId) } override fun unregisterDevice(deviceId: String) { DevRev.unregisterDevice(reactContext, deviceId) } override fun processPushNotification(payload: String) { DevRev.processPushNotification(reactContext, payload) } /* * In-app link handling */ override fun setInAppLinkHandler() { inAppLinkHandler = { url -> val params = Arguments.createMap().apply { putString("url", url) } eventEmitter.emit("inAppLinkReceived", params) } inAppLinkHandler?.let { DevRev.setInAppLinkHandler(it) } } override fun setShouldDismissModalsOnOpenLink(value: Boolean) { DevRev.setShouldDismissModalsOnOpenLink(value) } /* * Dynamic theme configuration */ override fun setPrefersSystemTheme(value: Boolean) { DevRev.setShouldPreferSystemTheme(value) } /* * Screen transition management */ override fun setInScreenTransitioning(value: Boolean) { DevRev.setInScreenTransitioning(value) } /* * Constants */ override fun getDevRevConstants(): Map { return mapOf( "EVENT_IN_APP_LINK" to "inAppLinkReceived" ) } /* * Event listener management */ override fun addListener(eventName: String) { // Event listener management } override fun removeListeners(count: Int) { // Event listener cleanup } /* * Error capture */ override fun captureError(error: String, tag: String, stackTrace: String?) { val throwable = if (stackTrace != null && stackTrace.isNotEmpty()) { val exception = RuntimeException(error) val stackTraceElements = stackTrace.split("\n") .filter { it.trim().isNotEmpty() } .map { StackTraceElement("", "", it.trim(), -1) } .toTypedArray() exception.stackTrace = stackTraceElements exception } else { RuntimeException(error) } DevRev.captureError(throwable, tag) } private fun getCurrentActivity(): Activity? { return reactContext.currentActivity } }