Hide Element Call entry point if Element Call service is not available. (#4783)

* Hide Element Call entry point if Element Call service is not available.

* No need to preview the case RoomCallState.Unavailable

* Hide start call action from user profile if Element Call is not available.

* Add mising `use` and cover the problem by a test.

* Update screenshots

* Update enterprise submodule ref.

* Ensure `enterpriseService.isElementCallAvailable()` is not called several times.
And fix unit tests on CI

---------

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Benoit Marty 2025-05-27 16:31:05 +02:00 committed by GitHub
parent 730fb684b0
commit 5b9da3c41b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 146 additions and 45 deletions

View file

@ -21,6 +21,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.userprofile.api.UserProfileEvents
import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog
@ -44,6 +45,7 @@ class UserProfilePresenter @AssistedInject constructor(
@Assisted private val userId: UserId,
private val client: MatrixClient,
private val startDMAction: StartDMAction,
private val enterpriseService: EnterpriseService,
) : Presenter<UserProfileState> {
@AssistedFactory
interface Factory {
@ -59,11 +61,21 @@ class UserProfilePresenter @AssistedInject constructor(
@Composable
private fun getCanCall(roomId: RoomId?): State<Boolean> {
return produceState(initialValue = false, roomId) {
value = if (client.isMe(userId)) {
false
} else {
roomId?.let { client.getRoom(it)?.canUserJoinCall(client.sessionId)?.getOrNull() == true }.orFalse()
val isElementCallAvailable by produceState(initialValue = false, roomId) {
value = enterpriseService.isElementCallAvailable()
}
return produceState(initialValue = false, isElementCallAvailable, roomId) {
value = when {
isElementCallAvailable.not() -> false
client.isMe(userId) -> false
else ->
roomId
?.let { client.getRoom(it) }
?.use { room ->
room.canUserJoinCall(client.sessionId).getOrNull()
}
.orFalse()
}
}
}

View file

@ -16,6 +16,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.createroom.api.ConfirmingStartDmWithMatrixUser
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.createroom.test.FakeStartDMAction
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.userprofile.api.UserProfileEvents
import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState
@ -37,7 +38,6 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.awaitLastSequentialItem
import io.element.android.tests.testutils.lambda.any
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
@ -81,6 +81,8 @@ class UserProfilePresenterTest {
fun `present - canCall is true when all the conditions are met`() {
testCanCall(
expectedResult = true,
skipItems = 3,
checkThatRoomIsDestroyed = true,
)
}
@ -116,11 +118,22 @@ class UserProfilePresenterTest {
)
}
@Test
fun `present - canCall is false when call is not available`() {
testCanCall(
isElementCallAvailable = false,
expectedResult = false,
)
}
private fun testCanCall(
isElementCallAvailable: Boolean = true,
canUserJoinCallResult: Result<Boolean> = Result.success(true),
dmRoom: RoomId? = A_ROOM_ID,
canFindRoom: Boolean = true,
expectedResult: Boolean,
skipItems: Int = 1,
checkThatRoomIsDestroyed: Boolean = false,
) = runTest {
val room = FakeBaseRoom(
canUserJoinCallResult = { canUserJoinCallResult },
@ -134,11 +147,15 @@ class UserProfilePresenterTest {
val presenter = createUserProfilePresenter(
userId = A_USER_ID_2,
client = client,
isElementCallAvailable = isElementCallAvailable,
)
presenter.test {
val initialState = awaitLastSequentialItem()
val initialState = awaitFirstItem(skipItems)
assertThat(initialState.canCall).isEqualTo(expectedResult)
}
if (checkThatRoomIsDestroyed) {
room.assertDestroyed()
}
}
@Test
@ -202,7 +219,7 @@ class UserProfilePresenterTest {
)
val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test {
val initialState = awaitFirstItem()
val initialState = awaitFirstItem(count = 2)
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem()
@ -220,7 +237,7 @@ class UserProfilePresenterTest {
)
val presenter = createUserProfilePresenter(client = matrixClient)
presenter.test {
val initialState = awaitFirstItem()
val initialState = awaitFirstItem(count = 2)
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
val errorState = awaitItem()
@ -363,8 +380,8 @@ class UserProfilePresenterTest {
}
}
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
skipItems(1)
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(count: Int = 1): T {
skipItems(count)
return awaitItem()
}
@ -387,12 +404,16 @@ class UserProfilePresenterTest {
private fun createUserProfilePresenter(
client: MatrixClient = createFakeMatrixClient(),
userId: UserId = UserId("@alice:server.org"),
startDMAction: StartDMAction = FakeStartDMAction()
startDMAction: StartDMAction = FakeStartDMAction(),
isElementCallAvailable: Boolean = true,
): UserProfilePresenter {
return UserProfilePresenter(
userId = userId,
client = client,
startDMAction = startDMAction,
enterpriseService = FakeEnterpriseService(
isElementCallAvailableResult = { isElementCallAvailable },
),
)
}
}