diff --git a/CHANGELOG.md b/CHANGELOG.md index 608067cf..d7cd4932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Next +- fix: allow changing person properties after identify ([#205](https://github.com/PostHog/posthog-android/pull/205)) + ## 3.9.1 - 2024-11-11 - recording: fix observation on multiple threads in layout/draw is not supported for compose ([#204](https://github.com/PostHog/posthog-android/pull/204)) diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 1ecc29ee..a554d65c 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -556,7 +556,8 @@ public class PostHog private constructor( config?.logger?.log("identify called with invalid anonymousId: $anonymousId.") } - if (previousDistinctId != distinctId && !isIdentified) { + val hasDifferentDistinctId = previousDistinctId != distinctId + if (hasDifferentDistinctId && !isIdentified) { // this has to be set before capture since this flag will be read during the event // capture synchronized(identifiedLock) { @@ -583,6 +584,16 @@ public class PostHog private constructor( if (reloadFeatureFlags) { reloadFeatureFlags() } + // we need to make sure the user props update is for the same user + // otherwise they have to reset and identify again + } else if (!hasDifferentDistinctId && (userProperties?.isNotEmpty() == true || userPropertiesSetOnce?.isNotEmpty() == true)) { + capture( + "\$set", + distinctId = distinctId, + userProperties = userProperties, + userPropertiesSetOnce = userPropertiesSetOnce, + ) + // Note we don't reload flags on property changes as these get processed async } else { config?.logger?.log("already identified with id: $distinctId.") } diff --git a/posthog/src/test/java/com/posthog/PostHogTest.kt b/posthog/src/test/java/com/posthog/PostHogTest.kt index 5db8741d..af534383 100644 --- a/posthog/src/test/java/com/posthog/PostHogTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogTest.kt @@ -574,13 +574,88 @@ internal class PostHogTest { sut.identify( "anotherDistinctId", + ) + + queueExecutor.shutdownAndAwaitTermination() + + assertEquals(1, http.requestCount) + + sut.close() + } + + @Test + fun `captures a set event if identified`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = false, flushAt = 2) + + sut.identify( + DISTINCT_ID, + userProperties = userProps, + userPropertiesSetOnce = userPropsOnce, + ) + + val userProps = mapOf("user1" to "theResult") + val userPropsOnce = mapOf("logged" to false) + + sut.identify( + DISTINCT_ID, userProperties = userProps, userPropertiesSetOnce = userPropsOnce, ) queueExecutor.shutdownAndAwaitTermination() - assertEquals(1, http.requestCount) + val request = http.takeRequest() + + val content = request.body.unGzip() + val batch = serializer.deserialize(content.reader()) + + val theEvent = batch.batch.last() + + assertEquals("\$set", theEvent.event) + assertEquals(userProps, theEvent.properties!!["\$set"]) + assertEquals(userPropsOnce, theEvent.properties!!["\$set_once"]) + + sut.close() + } + + @Test + fun `does not capture a set event if different user`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = false) + + sut.identify( + DISTINCT_ID, + userProperties = userProps, + userPropertiesSetOnce = userPropsOnce, + ) + + val userProps = mapOf("user1" to "theResult") + val userPropsOnce = mapOf("logged" to false) + + sut.identify( + "different user", + userProperties = userProps, + userPropertiesSetOnce = userPropsOnce, + ) + + queueExecutor.shutdownAndAwaitTermination() + + queueExecutor.shutdownAndAwaitTermination() + + val request = http.takeRequest() + + val content = request.body.unGzip() + val batch = serializer.deserialize(content.reader()) + + val theEvent = batch.batch.last() + + assertEquals(1, batch.batch.size) + assertEquals("\$identify", theEvent.event) sut.close() }