Skip to content

Commit 12c3bfc

Browse files
authored
fix: check if distinctId and anonId are a string before using it (#30)
1 parent 6cb5214 commit 12c3bfc

File tree

10 files changed

+250
-181
lines changed

10 files changed

+250
-181
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,5 @@ lib/
8282
# React Native Codegen
8383
ios/generated
8484
android/generated
85+
86+
.cursor/

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## Next
22

3+
## 1.1.1 - 2025-06-24
4+
5+
- fix: check if distinctId and anonId are a string before using it
6+
- chore: pin the iOS SDK to 3.21.x
7+
- chore: pin the Android SDK to 3.19.1
8+
39
## 1.1.0 - 2025-06-06
410

511
- chore: remove maskPhotoLibraryImages from the SDK config

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.PHONY: formatKotlin formatSwift
2+
3+
# brew install ktlint
4+
formatKotlin:
5+
ktlint -F "**/*.kt"
6+
7+
# brew install swift-format
8+
formatSwift:
9+
swiftformat ios --swiftversion 5.3

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,6 @@ dependencies {
9595
//noinspection GradleDynamicVersion
9696
implementation "com.facebook.react:react-native:+"
9797
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
98-
implementation "com.posthog:posthog-android:3.11.3"
98+
implementation "com.posthog:posthog-android:3.19.1"
9999
}
100100

android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayModule.kt

Lines changed: 87 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.posthogreactnativesessionreplay
22

33
import android.util.Log
4+
import com.facebook.react.bridge.Promise
45
import com.facebook.react.bridge.ReactApplicationContext
56
import com.facebook.react.bridge.ReactContextBaseJavaModule
67
import com.facebook.react.bridge.ReactMethod
7-
import com.facebook.react.bridge.Promise
88
import com.facebook.react.bridge.ReadableMap
99
import com.facebook.react.bridge.UiThreadUtil
10-
1110
import com.posthog.PostHog
1211
import com.posthog.PostHogConfig
1312
import com.posthog.android.PostHogAndroid
@@ -18,88 +17,90 @@ import com.posthog.internal.PostHogPreferences.Companion.DISTINCT_ID
1817
import com.posthog.internal.PostHogSessionManager
1918
import java.util.UUID
2019

21-
class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContext) :
22-
ReactContextBaseJavaModule(reactContext) {
23-
24-
override fun getName(): String {
25-
return NAME
26-
}
20+
class PosthogReactNativeSessionReplayModule(
21+
reactContext: ReactApplicationContext,
22+
) : ReactContextBaseJavaModule(reactContext) {
23+
override fun getName(): String = NAME
2724

2825
@ReactMethod
2926
fun start(
3027
sessionId: String,
3128
sdkOptions: ReadableMap,
3229
sdkReplayConfig: ReadableMap,
3330
decideReplayConfig: ReadableMap,
34-
promise: Promise
31+
promise: Promise,
3532
) {
36-
val initRunnable = Runnable {
37-
try {
38-
val uuid = UUID.fromString(sessionId)
39-
PostHogSessionManager.setSessionId(uuid)
40-
41-
val context = this.reactApplicationContext
42-
val apiKey = sdkOptions.getString("apiKey") ?: ""
43-
val host = sdkOptions.getString("host") ?: PostHogConfig.DEFAULT_HOST
44-
val debugValue = sdkOptions.getBoolean("debug")
45-
46-
val maskAllTextInputs = sdkReplayConfig.getBoolean("maskAllTextInputs")
47-
val maskAllImages = sdkReplayConfig.getBoolean("maskAllImages")
48-
val captureLog = sdkReplayConfig.getBoolean("captureLog")
49-
val debouncerDelayMs = sdkReplayConfig.getInt("androidDebouncerDelayMs")
50-
51-
val endpoint = decideReplayConfig.getString("endpoint")
52-
53-
val distinctId = try {
54-
sdkOptions.getString("distinctId") ?: ""
55-
} catch (e: Throwable) {
56-
logError("parse distinctId", e)
57-
""
58-
}
59-
val anonymousId = try {
60-
sdkOptions.getString("anonymousId") ?: ""
61-
} catch (e: Throwable) {
62-
logError("parse anonymousId", e)
63-
""
64-
}
65-
val theSdkVersion = sdkOptions.getString("sdkVersion")
66-
67-
var theFlushAt = 20
68-
if (sdkOptions.hasKey("flushAt")) {
69-
theFlushAt = sdkOptions.getInt("flushAt")
70-
}
71-
72-
val config = PostHogAndroidConfig(apiKey, host).apply {
73-
debug = debugValue
74-
captureDeepLinks = false
75-
captureApplicationLifecycleEvents = false
76-
captureScreenViews = false
77-
flushAt = theFlushAt
78-
sessionReplay = true
79-
sessionReplayConfig.screenshot = true
80-
sessionReplayConfig.captureLogcat = captureLog
81-
sessionReplayConfig.debouncerDelayMs = debouncerDelayMs.toLong()
82-
sessionReplayConfig.maskAllImages = maskAllImages
83-
sessionReplayConfig.maskAllTextInputs = maskAllTextInputs
84-
85-
if (!endpoint.isNullOrEmpty()) {
86-
snapshotEndpoint = endpoint
33+
val initRunnable =
34+
Runnable {
35+
try {
36+
val uuid = UUID.fromString(sessionId)
37+
PostHogSessionManager.setSessionId(uuid)
38+
39+
val context = this.reactApplicationContext
40+
val apiKey = sdkOptions.getString("apiKey") ?: ""
41+
val host = sdkOptions.getString("host") ?: PostHogConfig.DEFAULT_HOST
42+
val debugValue = sdkOptions.getBoolean("debug")
43+
44+
val maskAllTextInputs = sdkReplayConfig.getBoolean("maskAllTextInputs")
45+
val maskAllImages = sdkReplayConfig.getBoolean("maskAllImages")
46+
val captureLog = sdkReplayConfig.getBoolean("captureLog")
47+
val debouncerDelayMs = sdkReplayConfig.getInt("androidDebouncerDelayMs")
48+
49+
val endpoint = decideReplayConfig.getString("endpoint")
50+
51+
val distinctId =
52+
try {
53+
sdkOptions.getString("distinctId") ?: ""
54+
} catch (e: Throwable) {
55+
logError("parse distinctId", e)
56+
""
57+
}
58+
val anonymousId =
59+
try {
60+
sdkOptions.getString("anonymousId") ?: ""
61+
} catch (e: Throwable) {
62+
logError("parse anonymousId", e)
63+
""
64+
}
65+
val theSdkVersion = sdkOptions.getString("sdkVersion")
66+
67+
var theFlushAt = 20
68+
if (sdkOptions.hasKey("flushAt")) {
69+
theFlushAt = sdkOptions.getInt("flushAt")
8770
}
8871

89-
if (!theSdkVersion.isNullOrEmpty()) {
90-
sdkName = "posthog-react-native"
91-
sdkVersion = theSdkVersion
92-
}
72+
val config =
73+
PostHogAndroidConfig(apiKey, host).apply {
74+
debug = debugValue
75+
captureDeepLinks = false
76+
captureApplicationLifecycleEvents = false
77+
captureScreenViews = false
78+
flushAt = theFlushAt
79+
sessionReplay = true
80+
sessionReplayConfig.screenshot = true
81+
sessionReplayConfig.captureLogcat = captureLog
82+
sessionReplayConfig.throttleDelayMs = debouncerDelayMs.toLong()
83+
sessionReplayConfig.maskAllImages = maskAllImages
84+
sessionReplayConfig.maskAllTextInputs = maskAllTextInputs
85+
86+
if (!endpoint.isNullOrEmpty()) {
87+
snapshotEndpoint = endpoint
88+
}
89+
90+
if (!theSdkVersion.isNullOrEmpty()) {
91+
sdkName = "posthog-react-native"
92+
sdkVersion = theSdkVersion
93+
}
94+
}
95+
PostHogAndroid.setup(context, config)
96+
97+
setIdentify(config.cachePreferences, distinctId, anonymousId)
98+
} catch (e: Throwable) {
99+
logError("start", e)
100+
} finally {
101+
promise.resolve(null)
93102
}
94-
PostHogAndroid.setup(context, config)
95-
96-
setIdentify(config.cachePreferences, distinctId, anonymousId)
97-
} catch (e: Throwable) {
98-
logError("start", e)
99-
} finally {
100-
promise.resolve(null)
101103
}
102-
}
103104

104105
// forces the SDK to be initialized on the main thread
105106
if (UiThreadUtil.isOnUiThread()) {
@@ -110,7 +111,10 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex
110111
}
111112

112113
@ReactMethod
113-
fun startSession(sessionId: String, promise: Promise) {
114+
fun startSession(
115+
sessionId: String,
116+
promise: Promise,
117+
) {
114118
try {
115119
val uuid = UUID.fromString(sessionId)
116120
PostHogSessionManager.setSessionId(uuid)
@@ -144,7 +148,11 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex
144148
}
145149

146150
@ReactMethod
147-
fun identify(distinctId: String, anonymousId: String, promise: Promise) {
151+
fun identify(
152+
distinctId: String,
153+
anonymousId: String,
154+
promise: Promise,
155+
) {
148156
try {
149157
setIdentify(PostHog.getConfig<PostHogConfig>()?.cachePreferences, distinctId, anonymousId)
150158
} catch (e: Throwable) {
@@ -157,7 +165,7 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex
157165
private fun setIdentify(
158166
cachePreferences: PostHogPreferences?,
159167
distinctId: String,
160-
anonymousId: String
168+
anonymousId: String,
161169
) {
162170
cachePreferences?.let { preferences ->
163171
if (anonymousId.isNotEmpty()) {
@@ -169,7 +177,10 @@ class PosthogReactNativeSessionReplayModule(reactContext: ReactApplicationContex
169177
}
170178
}
171179

172-
private fun logError(method: String, error: Throwable) {
180+
private fun logError(
181+
method: String,
182+
error: Throwable,
183+
) {
173184
Log.println(Log.ERROR, POSTHOG_TAG, "Method $method, error: $error")
174185
}
175186

android/src/main/java/com/posthogreactnativesessionreplay/PosthogReactNativeSessionReplayPackage.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,9 @@ import com.facebook.react.bridge.NativeModule
55
import com.facebook.react.bridge.ReactApplicationContext
66
import com.facebook.react.uimanager.ViewManager
77

8-
98
class PosthogReactNativeSessionReplayPackage : ReactPackage {
10-
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11-
return listOf(PosthogReactNativeSessionReplayModule(reactContext))
12-
}
9+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
10+
listOf(PosthogReactNativeSessionReplayModule(reactContext))
1311

14-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15-
return emptyList()
16-
}
12+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
1713
}

example/android/app/src/main/java/posthogreactnativesessionreplay/example/MainActivity.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnable
66
import com.facebook.react.defaults.DefaultReactActivityDelegate
77

88
class MainActivity : ReactActivity() {
9-
109
/**
1110
* Returns the name of the main component registered from JavaScript. This is used to schedule
1211
* rendering of the component.
@@ -17,6 +16,5 @@ class MainActivity : ReactActivity() {
1716
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
1817
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
1918
*/
20-
override fun createReactActivityDelegate(): ReactActivityDelegate =
21-
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
19+
override fun createReactActivityDelegate(): ReactActivityDelegate = DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
2220
}

example/android/app/src/main/java/posthogreactnativesessionreplay/example/MainApplication.kt

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,24 @@ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
1111
import com.facebook.react.defaults.DefaultReactNativeHost
1212
import com.facebook.soloader.SoLoader
1313

14-
class MainApplication : Application(), ReactApplication {
15-
14+
class MainApplication :
15+
Application(),
16+
ReactApplication {
1617
override val reactNativeHost: ReactNativeHost =
17-
object : DefaultReactNativeHost(this) {
18-
override fun getPackages(): List<ReactPackage> =
19-
PackageList(this).packages.apply {
20-
// Packages that cannot be autolinked yet can be added manually here, for example:
21-
// add(MyReactNativePackage())
22-
}
18+
object : DefaultReactNativeHost(this) {
19+
override fun getPackages(): List<ReactPackage> =
20+
PackageList(this).packages.apply {
21+
// Packages that cannot be autolinked yet can be added manually here, for example:
22+
// add(MyReactNativePackage())
23+
}
2324

24-
override fun getJSMainModuleName(): String = "index"
25+
override fun getJSMainModuleName(): String = "index"
2526

26-
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
27+
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
2728

28-
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
29-
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
30-
}
29+
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
30+
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
31+
}
3132

3233
override val reactHost: ReactHost
3334
get() = getDefaultReactHost(applicationContext, reactNativeHost)

0 commit comments

Comments
 (0)