Render data of blocked users (behind deactivated feature flag).

This commit is contained in:
Benoit Marty
2024-05-28 14:32:54 +02:00
committed by Benoit Marty
parent 053ef9b9ab
commit 687b30bd5f
7 changed files with 84 additions and 18 deletions

View File

@@ -21,20 +21,26 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
class BlockedUsersPresenter @Inject constructor(
private val matrixClient: MatrixClient,
private val featureFlagService: FeatureFlagService,
) : Presenter<BlockedUsersState> {
@Composable
override fun present(): BlockedUsersState {
@@ -47,7 +53,24 @@ class BlockedUsersPresenter @Inject constructor(
mutableStateOf(AsyncAction.Uninitialized)
}
val renderBlockedUsersDetail = featureFlagService
.isFeatureEnabledFlow(FeatureFlags.ShowBlockedUsersDetails)
.collectAsState(initial = false)
val ignoredUserIds by matrixClient.ignoredUsersFlow.collectAsState()
val ignoredMatrixUser by produceState(
initialValue = ignoredUserIds.map { MatrixUser(userId = it) },
key1 = renderBlockedUsersDetail.value,
key2 = ignoredUserIds
) {
value = ignoredUserIds.map {
if (renderBlockedUsersDetail.value) {
matrixClient.getProfile(it).getOrNull()
} else {
null
}
?: MatrixUser(userId = it)
}
}
fun handleEvents(event: BlockedUsersEvents) {
when (event) {
@@ -68,7 +91,7 @@ class BlockedUsersPresenter @Inject constructor(
}
}
return BlockedUsersState(
blockedUsers = ignoredUserIds,
blockedUsers = ignoredMatrixUser.toPersistentList(),
unblockUserAction = unblockUserAction.value,
eventSink = ::handleEvents
)

View File

@@ -17,11 +17,11 @@
package io.element.android.features.preferences.impl.blockedusers
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
data class BlockedUsersState(
val blockedUsers: ImmutableList<UserId>,
val blockedUsers: ImmutableList<MatrixUser>,
val unblockUserAction: AsyncAction<Unit>,
val eventSink: (BlockedUsersEvents) -> Unit,
)

View File

@@ -18,7 +18,7 @@ package io.element.android.features.preferences.impl.blockedusers
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import kotlinx.collections.immutable.toPersistentList
@@ -26,6 +26,7 @@ class BlockedUsersStatePreviewProvider : PreviewParameterProvider<BlockedUsersSt
override val values: Sequence<BlockedUsersState>
get() = sequenceOf(
aBlockedUsersState(),
aBlockedUsersState(blockedUsers = aMatrixUserList().map { it.copy(displayName = null, avatarUrl = null) }),
aBlockedUsersState(blockedUsers = emptyList()),
aBlockedUsersState(unblockUserAction = AsyncAction.Confirming),
// Sadly there's no good way to preview Loading or Failure states since they're presented with an animation
@@ -37,7 +38,7 @@ class BlockedUsersStatePreviewProvider : PreviewParameterProvider<BlockedUsersSt
}
internal fun aBlockedUsersState(
blockedUsers: List<UserId> = aMatrixUserList().map { it.userId },
blockedUsers: List<MatrixUser> = aMatrixUserList(),
unblockUserAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
): BlockedUsersState {
return BlockedUsersState(

View File

@@ -73,9 +73,9 @@ fun BlockedUsersView(
LazyColumn(
modifier = Modifier.padding(padding)
) {
items(state.blockedUsers) { userId ->
items(state.blockedUsers) { matrixUser ->
BlockedUserItem(
userId = userId,
matrixUser = matrixUser,
onClick = { state.eventSink(BlockedUsersEvents.Unblock(it)) }
)
}
@@ -121,12 +121,12 @@ fun BlockedUsersView(
@Composable
private fun BlockedUserItem(
userId: UserId,
matrixUser: MatrixUser,
onClick: (UserId) -> Unit,
) {
MatrixUserRow(
modifier = Modifier.clickable { onClick(userId) },
matrixUser = MatrixUser(userId),
modifier = Modifier.clickable { onClick(matrixUser.userId) },
matrixUser = matrixUser,
)
}

View File

@@ -21,6 +21,11 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction
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.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.FakeMatrixClient
@@ -52,7 +57,7 @@ class BlockedUsersPresenterTests {
presenter.present()
}.test {
with(awaitItem()) {
assertThat(blockedUsers).isEqualTo(persistentListOf(A_USER_ID))
assertThat(blockedUsers).isEqualTo(persistentListOf(MatrixUser(A_USER_ID)))
assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized)
}
}
@@ -68,14 +73,39 @@ class BlockedUsersPresenterTests {
presenter.present()
}.test {
with(awaitItem()) {
assertThat(blockedUsers).containsAtLeastElementsIn(persistentListOf(A_USER_ID))
assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(blockedUsers).isEqualTo(listOf(MatrixUser(A_USER_ID)))
}
matrixClient.ignoredUsersFlow.value = persistentListOf(A_USER_ID, A_USER_ID_2)
skipItems(1)
with(awaitItem()) {
assertThat(blockedUsers).isEqualTo(persistentListOf(A_USER_ID, A_USER_ID_2))
assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(blockedUsers).isEqualTo(listOf(MatrixUser(A_USER_ID), MatrixUser(A_USER_ID_2)))
}
}
}
@Test
fun `present - blocked users list with data`() = runTest {
val alice = MatrixUser(A_USER_ID, displayName = "Alice", avatarUrl = "aliceAvatar")
val matrixClient = FakeMatrixClient().apply {
ignoredUsersFlow.value = persistentListOf(A_USER_ID, A_USER_ID_2)
givenGetProfileResult(A_USER_ID, Result.success(alice))
givenGetProfileResult(A_USER_ID_2, Result.failure(AN_EXCEPTION))
}
val presenter = aBlockedUsersPresenter(
matrixClient = matrixClient,
featureFlagService = FakeFeatureFlagService().apply {
setFeatureEnabled(FeatureFlags.ShowBlockedUsersDetails, true)
}
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
with(awaitItem()) {
assertThat(blockedUsers).isEqualTo(listOf(MatrixUser(A_USER_ID), MatrixUser(A_USER_ID_2)))
}
// Alice is resolved
with(awaitItem()) {
assertThat(blockedUsers).isEqualTo(listOf(alice, MatrixUser(A_USER_ID_2)))
}
}
}
@@ -157,5 +187,9 @@ class BlockedUsersPresenterTests {
private fun aBlockedUsersPresenter(
matrixClient: FakeMatrixClient = FakeMatrixClient(),
) = BlockedUsersPresenter(matrixClient)
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
) = BlockedUsersPresenter(
matrixClient = matrixClient,
featureFlagService = featureFlagService,
)
}

View File

@@ -81,5 +81,12 @@ enum class FeatureFlags(
description = "Allow user to search for public rooms in their homeserver",
defaultValue = false,
isFinished = false,
)
),
ShowBlockedUsersDetails(
key = "feature.showBlockedUsersDetails",
title = "Show blocked users details",
description = "Show the name and avatar of blocked users in the blocked users list",
defaultValue = false,
isFinished = false,
),
}

View File

@@ -41,6 +41,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
FeatureFlags.Mentions -> true
FeatureFlags.MarkAsUnread -> true
FeatureFlags.RoomDirectorySearch -> false
FeatureFlags.ShowBlockedUsersDetails -> false
}
} else {
false