Skip to content

Commit

Permalink
Merge pull request #3648 from element-hq/feature/bma/identityChangeFe…
Browse files Browse the repository at this point in the history
…atureFlag

Add feature flag IdentityPinningViolationNotifications.
  • Loading branch information
bmarty authored Oct 10, 2024
2 parents 930bbca + aa07ab1 commit 9c7f2b9
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import androidx.compose.runtime.rememberCoroutineScope
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.room.MatrixRoom
Expand All @@ -31,7 +33,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
Expand All @@ -40,6 +41,7 @@ import javax.inject.Inject
class IdentityChangeStatePresenter @Inject constructor(
private val room: MatrixRoom,
private val encryptionService: EncryptionService,
private val featureFlagService: FeatureFlagService,
) : Presenter<IdentityChangeState> {
@Composable
override fun present(): IdentityChangeState {
Expand All @@ -62,14 +64,18 @@ class IdentityChangeStatePresenter @Inject constructor(

@OptIn(ExperimentalCoroutinesApi::class)
private fun ProduceStateScope<PersistentList<RoomMemberIdentityStateChange>>.observeRoomMemberIdentityStateChange() {
room.syncUpdateFlow
featureFlagService.isFeatureEnabledFlow(FeatureFlags.IdentityPinningViolationNotifications)
.filter { it }
.flatMapLatest {
room.syncUpdateFlow
}
.filter {
// Room cannot become unencrypted, so we can just apply a filter here.
room.isEncrypted
}
.distinctUntilChanged()
.flatMapLatest {
combine(room.identityStateChangesFlow, room.membersStateFlow,) { identityStateChanges, membersState ->
combine(room.identityStateChangesFlow, room.membersStateFlow) { identityStateChanges, membersState ->
identityStateChanges.map { identityStateChange ->
val member = membersState.roomMembers()
?.firstOrNull { roomMember -> roomMember.userId == identityStateChange.userId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ package io.element.android.features.messages.impl.crypto.identity

import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
Expand Down Expand Up @@ -65,6 +68,43 @@ class IdentityChangeStatePresenterTest {
}
}

@Test
fun `present - when the room emits identity change, but the feature is disabled, the presenter emits new state`() = runTest {
val room = FakeMatrixRoom(
isEncrypted = true,
)
val featureFlagService = FakeFeatureFlagService(
initialState = mapOf(
FeatureFlags.IdentityPinningViolationNotifications.key to false,
)
)
val presenter = createIdentityChangeStatePresenter(
room = room,
featureFlagService = featureFlagService,
)
presenter.test {
val initialState = awaitItem()
assertThat(initialState.roomMemberIdentityStateChanges).isEmpty()
room.emitIdentityStateChanges(
listOf(
IdentityStateChange(
userId = A_USER_ID_2,
identityState = IdentityState.PinViolation,
),
)
)
// No item emitted.
expectNoEvents()
// Enable the feature
featureFlagService.setFeatureEnabled(FeatureFlags.IdentityPinningViolationNotifications, true)
val finalItem = awaitItem()
assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1)
val value = finalItem.roomMemberIdentityStateChanges.first()
assertThat(value.identityRoomMember.userId).isEqualTo(A_USER_ID_2)
assertThat(value.identityState).isEqualTo(IdentityState.PinViolation)
}
}

@Test
fun `present - when the clear room emits identity change, the presenter does not emits new state`() = runTest {
val room = FakeMatrixRoom(isEncrypted = false)
Expand Down Expand Up @@ -147,10 +187,16 @@ class IdentityChangeStatePresenterTest {
private fun createIdentityChangeStatePresenter(
room: MatrixRoom = FakeMatrixRoom(),
encryptionService: EncryptionService = FakeEncryptionService(),
featureFlagService: FeatureFlagService = FakeFeatureFlagService(
initialState = mapOf(
FeatureFlags.IdentityPinningViolationNotifications.key to true,
)
),
): IdentityChangeStatePresenter {
return IdentityChangeStatePresenter(
room = room,
encryptionService = encryptionService,
featureFlagService = featureFlagService,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,17 @@ enum class FeatureFlags(
defaultValue = { false },
isFinished = false,
),
IdentityPinningViolationNotifications(
key = "feature.identityPinningViolationNotifications",
title = "Identity pinning violation notifications",
description = null,
defaultValue = { buildMeta ->
when (buildMeta.buildType) {
// Do not enable this feature in release builds
BuildType.RELEASE -> false
else -> true
}
},
isFinished = false,
),
}

0 comments on commit 9c7f2b9

Please sign in to comment.