diff --git a/features/invitelist/impl/build.gradle.kts b/features/invitelist/impl/build.gradle.kts index e54b8ede3b..6ff90d676d 100644 --- a/features/invitelist/impl/build.gradle.kts +++ b/features/invitelist/impl/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) + implementation(projects.services.analytics.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) @@ -50,6 +51,7 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) testImplementation(projects.features.invitelist.test) + testImplementation(projects.features.analytics.test) ksp(libs.showkase.processor) } diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt index d537517d54..1ba2c281ac 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invitelist.api.SeenInvitesStore import io.element.android.features.invitelist.impl.model.InviteListInviteSummary import io.element.android.features.invitelist.impl.model.InviteSender @@ -35,6 +36,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.extensions.toAnalyticsJoinedRoom import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first @@ -44,6 +47,7 @@ import javax.inject.Inject class InviteListPresenter @Inject constructor( private val client: MatrixClient, private val store: SeenInvitesStore, + private val analyticsService: AnalyticsService, ) : Presenter { @Composable @@ -133,6 +137,7 @@ class InviteListPresenter @Inject constructor( suspend { client.getRoom(roomId)?.use { it.acceptInvitation().getOrThrow() + analyticsService.capture(it.toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite)) } roomId }.runCatchingUpdatingState(acceptedAction) diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt index cf4f1058e5..16c561e569 100644 --- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt +++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt @@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth +import io.element.android.features.analytics.test.FakeAnalyticsService import io.element.android.features.invitelist.test.FakeSeenInvitesStore import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -50,6 +51,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), FakeSeenInvitesStore(), + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -74,6 +76,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), FakeSeenInvitesStore(), + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -102,6 +105,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), FakeSeenInvitesStore(), + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -128,6 +132,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), FakeSeenInvitesStore(), + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -152,6 +157,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), FakeSeenInvitesStore(), + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -176,6 +182,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), FakeSeenInvitesStore(), + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -199,7 +206,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ) val room = FakeMatrixRoom() - val presenter = InviteListPresenter(client, FakeSeenInvitesStore()) + val presenter = InviteListPresenter(client, FakeSeenInvitesStore(), FakeAnalyticsService()) client.givenGetRoomResult(A_ROOM_ID, room) moleculeFlow(RecompositionClock.Immediate) { @@ -225,7 +232,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ) val room = FakeMatrixRoom() - val presenter = InviteListPresenter(client, FakeSeenInvitesStore()) + val presenter = InviteListPresenter(client, FakeSeenInvitesStore(), FakeAnalyticsService()) val ex = Throwable("Ruh roh!") room.givenRejectInviteResult(Result.failure(ex)) client.givenGetRoomResult(A_ROOM_ID, room) @@ -256,7 +263,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ) val room = FakeMatrixRoom() - val presenter = InviteListPresenter(client, FakeSeenInvitesStore()) + val presenter = InviteListPresenter(client, FakeSeenInvitesStore(), FakeAnalyticsService()) val ex = Throwable("Ruh roh!") room.givenRejectInviteResult(Result.failure(ex)) client.givenGetRoomResult(A_ROOM_ID, room) @@ -288,7 +295,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ) val room = FakeMatrixRoom() - val presenter = InviteListPresenter(client, FakeSeenInvitesStore()) + val presenter = InviteListPresenter(client, FakeSeenInvitesStore(), FakeAnalyticsService()) client.givenGetRoomResult(A_ROOM_ID, room) moleculeFlow(RecompositionClock.Immediate) { @@ -311,7 +318,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ) val room = FakeMatrixRoom() - val presenter = InviteListPresenter(client, FakeSeenInvitesStore()) + val presenter = InviteListPresenter(client, FakeSeenInvitesStore(), FakeAnalyticsService()) val ex = Throwable("Ruh roh!") room.givenAcceptInviteResult(Result.failure(ex)) client.givenGetRoomResult(A_ROOM_ID, room) @@ -336,7 +343,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ) val room = FakeMatrixRoom() - val presenter = InviteListPresenter(client, FakeSeenInvitesStore()) + val presenter = InviteListPresenter(client, FakeSeenInvitesStore(), FakeAnalyticsService()) val ex = Throwable("Ruh roh!") room.givenAcceptInviteResult(Result.failure(ex)) client.givenGetRoomResult(A_ROOM_ID, room) @@ -365,6 +372,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), store, + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -401,6 +409,7 @@ class InviteListPresenterTests { invitesDataSource = invitesDataSource, ), store, + FakeAnalyticsService(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() diff --git a/services/analytics/api/build.gradle.kts b/services/analytics/api/build.gradle.kts index 0906ee9889..28b871a659 100644 --- a/services/analytics/api/build.gradle.kts +++ b/services/analytics/api/build.gradle.kts @@ -24,4 +24,6 @@ android { dependencies { api(projects.services.analyticsproviders.api) implementation(libs.coroutines.core) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.core) } diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/extensions/JoinedRoomExt.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/extensions/JoinedRoomExt.kt new file mode 100644 index 0000000000..ec67b3acf2 --- /dev/null +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/extensions/JoinedRoomExt.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.services.analytics.api.extensions + +import im.vector.app.features.analytics.plan.JoinedRoom +import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.matrix.api.core.MatrixPatterns +import io.element.android.libraries.matrix.api.room.MatrixRoom + +fun Long?.toAnalyticsRoomSize(): JoinedRoom.RoomSize { + return when (this) { + null, + 2L -> JoinedRoom.RoomSize.Two + in 3..10 -> JoinedRoom.RoomSize.ThreeToTen + in 11..100 -> JoinedRoom.RoomSize.ElevenToOneHundred + in 101..1000 -> JoinedRoom.RoomSize.OneHundredAndOneToAThousand + else -> JoinedRoom.RoomSize.MoreThanAThousand + } +} + +fun MatrixRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom { + return JoinedRoom( + isDM = this.isDirect.orFalse(), + isSpace = MatrixPatterns.isSpaceId(this.roomId.value), + roomSize = this.joinedMemberCount.toAnalyticsRoomSize(), + trigger = trigger + ) +}