diff --git a/CHANGELOG.md b/CHANGELOG.md index 795e52b..f28d2e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Next +## 1.0.2 - 2025-02-24 + +- fix: distinctId and anonymousId should degrade gracefully if they are strings that can't be parsed + ## 1.0.1 - 2025-02-21 - chore: pin the iOS SDK to 3.x.x diff --git a/android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayModule.kt b/android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayModule.kt index 5e4ae2a..25ab024 100644 --- a/android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayModule.kt +++ b/android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayModule.kt @@ -1,5 +1,6 @@ package com.posthogreactnativesessionreplay +import android.util.Log import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod @@ -25,59 +26,79 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex } @ReactMethod - fun start(sessionId: String, sdkOptions: ReadableMap, sdkReplayConfig: ReadableMap, decideReplayConfig: ReadableMap, promise: Promise) { + fun start( + sessionId: String, + sdkOptions: ReadableMap, + sdkReplayConfig: ReadableMap, + decideReplayConfig: ReadableMap, + promise: Promise + ) { val initRunnable = Runnable { - val uuid = UUID.fromString(sessionId) - PostHogSessionManager.setSessionId(uuid) - - val context = this.reactApplicationContext - val apiKey = sdkOptions.getString("apiKey") ?: "" - val host = sdkOptions.getString("host") ?: PostHogConfig.DEFAULT_HOST - val debugValue = sdkOptions.getBoolean("debug") - - val maskAllTextInputs = sdkReplayConfig.getBoolean("maskAllTextInputs") - val maskAllImages = sdkReplayConfig.getBoolean("maskAllImages") - val captureLog = sdkReplayConfig.getBoolean("captureLog") - val debouncerDelayMs = sdkReplayConfig.getInt("androidDebouncerDelayMs") - - val endpoint = decideReplayConfig.getString("endpoint") - - val distinctId = sdkOptions.getString("distinctId") ?: "" - val anonymousId = sdkOptions.getString("anonymousId") ?: "" - val theSdkVersion = sdkOptions.getString("sdkVersion") - - var theFlushAt = 20 - if (sdkOptions.hasKey("flushAt")) { - theFlushAt = sdkOptions.getInt("flushAt") - } - - val config = PostHogAndroidConfig(apiKey, host).apply { - debug = debugValue - captureDeepLinks = false - captureApplicationLifecycleEvents = false - captureScreenViews = false - flushAt = theFlushAt - sessionReplay = true - sessionReplayConfig.screenshot = true - sessionReplayConfig.captureLogcat = captureLog - sessionReplayConfig.debouncerDelayMs = debouncerDelayMs.toLong() - sessionReplayConfig.maskAllImages = maskAllImages - sessionReplayConfig.maskAllTextInputs = maskAllTextInputs - - if (!endpoint.isNullOrEmpty()) { - snapshotEndpoint = endpoint + try { + val uuid = UUID.fromString(sessionId) + PostHogSessionManager.setSessionId(uuid) + + val context = this.reactApplicationContext + val apiKey = sdkOptions.getString("apiKey") ?: "" + val host = sdkOptions.getString("host") ?: PostHogConfig.DEFAULT_HOST + val debugValue = sdkOptions.getBoolean("debug") + + val maskAllTextInputs = sdkReplayConfig.getBoolean("maskAllTextInputs") + val maskAllImages = sdkReplayConfig.getBoolean("maskAllImages") + val captureLog = sdkReplayConfig.getBoolean("captureLog") + val debouncerDelayMs = sdkReplayConfig.getInt("androidDebouncerDelayMs") + + val endpoint = decideReplayConfig.getString("endpoint") + + val distinctId = try { + sdkOptions.getString("distinctId") ?: "" + } catch (e: Throwable) { + logError("parse distinctId", e) + "" + } + val anonymousId = try { + sdkOptions.getString("anonymousId") ?: "" + } catch (e: Throwable) { + logError("parse anonymousId", e) + "" } + val theSdkVersion = sdkOptions.getString("sdkVersion") - if (!theSdkVersion.isNullOrEmpty()) { - sdkName = "posthog-react-native" - sdkVersion = theSdkVersion + var theFlushAt = 20 + if (sdkOptions.hasKey("flushAt")) { + theFlushAt = sdkOptions.getInt("flushAt") } - } - PostHogAndroid.setup(context, config) - setIdentify(config.cachePreferences, distinctId, anonymousId) + val config = PostHogAndroidConfig(apiKey, host).apply { + debug = debugValue + captureDeepLinks = false + captureApplicationLifecycleEvents = false + captureScreenViews = false + flushAt = theFlushAt + sessionReplay = true + sessionReplayConfig.screenshot = true + sessionReplayConfig.captureLogcat = captureLog + sessionReplayConfig.debouncerDelayMs = debouncerDelayMs.toLong() + sessionReplayConfig.maskAllImages = maskAllImages + sessionReplayConfig.maskAllTextInputs = maskAllTextInputs + + if (!endpoint.isNullOrEmpty()) { + snapshotEndpoint = endpoint + } + + if (!theSdkVersion.isNullOrEmpty()) { + sdkName = "posthog-react-native" + sdkVersion = theSdkVersion + } + } + PostHogAndroid.setup(context, config) - promise.resolve(null) + setIdentify(config.cachePreferences, distinctId, anonymousId) + } catch (e: Throwable) { + logError("start", e) + } finally { + promise.resolve(null) + } } // forces the SDK to be initialized on the main thread @@ -90,31 +111,54 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex @ReactMethod fun startSession(sessionId: String, promise: Promise) { - val uuid = UUID.fromString(sessionId) - PostHogSessionManager.setSessionId(uuid) - PostHog.startSession() - promise.resolve(null) + try { + val uuid = UUID.fromString(sessionId) + PostHogSessionManager.setSessionId(uuid) + PostHog.startSession() + } catch (e: Throwable) { + logError("startSession", e) + } finally { + promise.resolve(null) + } } @ReactMethod fun isEnabled(promise: Promise) { - promise.resolve(PostHog.isSessionReplayActive()) + try { + promise.resolve(PostHog.isSessionReplayActive()) + } catch (e: Throwable) { + logError("isEnabled", e) + promise.resolve(false) + } } @ReactMethod fun endSession(promise: Promise) { - PostHog.endSession() - promise.resolve(null) + try { + PostHog.endSession() + } catch (e: Throwable) { + logError("endSession", e) + } finally { + promise.resolve(null) + } } @ReactMethod fun identify(distinctId: String, anonymousId: String, promise: Promise) { - setIdentify(PostHog.getConfig()?.cachePreferences, distinctId, anonymousId) - - promise.resolve(null) + try { + setIdentify(PostHog.getConfig()?.cachePreferences, distinctId, anonymousId) + } catch (e: Throwable) { + logError("identify", e) + } finally { + promise.resolve(null) + } } - private fun setIdentify(cachePreferences: PostHogPreferences?, distinctId: String, anonymousId: String) { + private fun setIdentify( + cachePreferences: PostHogPreferences?, + distinctId: String, + anonymousId: String + ) { cachePreferences?.let { preferences -> if (anonymousId.isNotEmpty()) { preferences.setValue(ANONYMOUS_ID, anonymousId) @@ -125,7 +169,12 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex } } + private fun logError(method: String, error: Throwable) { + Log.println(Log.ERROR, POSTHOG_TAG, "Method $method, error: $error") + } + companion object { const val NAME = "PosthogReactNativeSessionReplay" + const val POSTHOG_TAG = "PostHog" } }