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:
parent
37120c5ee5
commit
c86154d120
17 changed files with 146 additions and 45 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit d3dffc97bf8b39386ace2db7d857bbff05c73c18
|
Subproject commit 4a07c862a23a9fd1418eabf132cf9d6b25ea4927
|
||||||
|
|
@ -16,6 +16,8 @@ interface EnterpriseService {
|
||||||
fun defaultHomeserverList(): List<String>
|
fun defaultHomeserverList(): List<String>
|
||||||
suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean
|
suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean
|
||||||
|
|
||||||
|
suspend fun isElementCallAvailable(): Boolean
|
||||||
|
|
||||||
fun semanticColorsLight(): SemanticColors
|
fun semanticColorsLight(): SemanticColors
|
||||||
fun semanticColorsDark(): SemanticColors
|
fun semanticColorsDark(): SemanticColors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService {
|
||||||
override fun defaultHomeserverList(): List<String> = emptyList()
|
override fun defaultHomeserverList(): List<String> = emptyList()
|
||||||
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
|
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
|
||||||
|
|
||||||
|
override suspend fun isElementCallAvailable(): Boolean = true
|
||||||
|
|
||||||
override fun semanticColorsLight(): SemanticColors = compoundColorsLight
|
override fun semanticColorsLight(): SemanticColors = compoundColorsLight
|
||||||
|
|
||||||
override fun semanticColorsDark(): SemanticColors = compoundColorsDark
|
override fun semanticColorsDark(): SemanticColors = compoundColorsDark
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ class FakeEnterpriseService(
|
||||||
private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() },
|
private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() },
|
||||||
private val defaultHomeserverListResult: () -> List<String> = { emptyList() },
|
private val defaultHomeserverListResult: () -> List<String> = { emptyList() },
|
||||||
private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() },
|
private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() },
|
||||||
|
private val isElementCallAvailableResult: () -> Boolean = { lambdaError() },
|
||||||
private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() },
|
private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() },
|
||||||
private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() },
|
private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() },
|
||||||
private val firebasePushGatewayResult: () -> String? = { lambdaError() },
|
private val firebasePushGatewayResult: () -> String? = { lambdaError() },
|
||||||
|
|
@ -35,6 +36,10 @@ class FakeEnterpriseService(
|
||||||
isAllowedToConnectToHomeserverResult(homeserverUrl)
|
isAllowedToConnectToHomeserverResult(homeserverUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun isElementCallAvailable(): Boolean = simulateLongTask {
|
||||||
|
isElementCallAvailableResult()
|
||||||
|
}
|
||||||
|
|
||||||
override fun semanticColorsLight(): SemanticColors {
|
override fun semanticColorsLight(): SemanticColors {
|
||||||
return semanticColorsLightResult()
|
return semanticColorsLightResult()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ internal fun CallMenuItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
when (roomCallState) {
|
when (roomCallState) {
|
||||||
|
RoomCallState.Unavailable -> {
|
||||||
|
Box(modifier)
|
||||||
|
}
|
||||||
is RoomCallState.StandBy -> {
|
is RoomCallState.StandBy -> {
|
||||||
StandByCallMenuItem(
|
StandByCallMenuItem(
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
|
|
|
||||||
|
|
@ -103,10 +103,12 @@ internal fun TimelineItemCallNotifyView(
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TimelineItemCallNotifyViewPreview() {
|
internal fun TimelineItemCallNotifyViewPreview() = ElementPreview {
|
||||||
ElementPreview {
|
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||||
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
RoomCallStateProvider()
|
||||||
RoomCallStateProvider().values.forEach { roomCallState ->
|
.values
|
||||||
|
.filter { it !is RoomCallState.Unavailable }
|
||||||
|
.forEach { roomCallState ->
|
||||||
TimelineItemCallNotifyView(
|
TimelineItemCallNotifyView(
|
||||||
event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()),
|
event = aTimelineItemEvent(content = TimelineItemCallNotifyContent()),
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
|
|
@ -114,6 +116,5 @@ internal fun TimelineItemCallNotifyViewPreview() {
|
||||||
onJoinCallClick = {},
|
onJoinCallClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import io.element.android.features.roomcall.api.RoomCallState.StandBy
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed interface RoomCallState {
|
sealed interface RoomCallState {
|
||||||
|
data object Unavailable : RoomCallState
|
||||||
|
|
||||||
data class StandBy(
|
data class StandBy(
|
||||||
val canStartCall: Boolean,
|
val canStartCall: Boolean,
|
||||||
) : RoomCallState
|
) : RoomCallState
|
||||||
|
|
@ -25,6 +27,7 @@ sealed interface RoomCallState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RoomCallState.hasPermissionToJoin() = when (this) {
|
fun RoomCallState.hasPermissionToJoin() = when (this) {
|
||||||
|
RoomCallState.Unavailable -> false
|
||||||
is StandBy -> canStartCall
|
is StandBy -> canStartCall
|
||||||
is OnGoing -> canJoinCall
|
is OnGoing -> canJoinCall
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ open class RoomCallStateProvider : PreviewParameterProvider<RoomCallState> {
|
||||||
anOngoingCallState(),
|
anOngoingCallState(),
|
||||||
anOngoingCallState(canJoinCall = false),
|
anOngoingCallState(canJoinCall = false),
|
||||||
anOngoingCallState(canJoinCall = true, isUserInTheCall = true),
|
anOngoingCallState(canJoinCall = true, isUserInTheCall = true),
|
||||||
|
RoomCallState.Unavailable,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ dependencies {
|
||||||
api(projects.features.roomcall.api)
|
api(projects.features.roomcall.api)
|
||||||
implementation(libs.kotlinx.collections.immutable)
|
implementation(libs.kotlinx.collections.immutable)
|
||||||
implementation(projects.features.call.api)
|
implementation(projects.features.call.api)
|
||||||
|
implementation(projects.features.enterprise.api)
|
||||||
implementation(projects.libraries.architecture)
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.matrixui)
|
implementation(projects.libraries.matrixui)
|
||||||
|
|
@ -32,6 +33,7 @@ dependencies {
|
||||||
testImplementation(libs.test.turbine)
|
testImplementation(libs.test.turbine)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.features.call.test)
|
testImplementation(projects.features.call.test)
|
||||||
|
testImplementation(projects.features.enterprise.test)
|
||||||
testImplementation(projects.tests.testutils)
|
testImplementation(projects.tests.testutils)
|
||||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||||
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,11 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import io.element.android.features.call.api.CurrentCall
|
import io.element.android.features.call.api.CurrentCall
|
||||||
import io.element.android.features.call.api.CurrentCallService
|
import io.element.android.features.call.api.CurrentCallService
|
||||||
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
import io.element.android.features.roomcall.api.RoomCallState
|
import io.element.android.features.roomcall.api.RoomCallState
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
|
@ -23,9 +25,13 @@ import javax.inject.Inject
|
||||||
class RoomCallStatePresenter @Inject constructor(
|
class RoomCallStatePresenter @Inject constructor(
|
||||||
private val room: JoinedRoom,
|
private val room: JoinedRoom,
|
||||||
private val currentCallService: CurrentCallService,
|
private val currentCallService: CurrentCallService,
|
||||||
|
private val enterpriseService: EnterpriseService,
|
||||||
) : Presenter<RoomCallState> {
|
) : Presenter<RoomCallState> {
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomCallState {
|
override fun present(): RoomCallState {
|
||||||
|
val isAvailable by produceState(false) {
|
||||||
|
value = enterpriseService.isElementCallAvailable()
|
||||||
|
}
|
||||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
|
val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value)
|
||||||
|
|
@ -41,6 +47,7 @@ class RoomCallStatePresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val callState = when {
|
val callState = when {
|
||||||
|
isAvailable.not() -> RoomCallState.Unavailable
|
||||||
roomInfo.hasRoomCall -> RoomCallState.OnGoing(
|
roomInfo.hasRoomCall -> RoomCallState.OnGoing(
|
||||||
canJoinCall = canJoinCall,
|
canJoinCall = canJoinCall,
|
||||||
isUserInTheCall = isUserInTheCall,
|
isUserInTheCall = isUserInTheCall,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.call.api.CurrentCall
|
import io.element.android.features.call.api.CurrentCall
|
||||||
import io.element.android.features.call.api.CurrentCallService
|
import io.element.android.features.call.api.CurrentCallService
|
||||||
import io.element.android.features.call.test.FakeCurrentCallService
|
import io.element.android.features.call.test.FakeCurrentCallService
|
||||||
|
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||||
import io.element.android.features.roomcall.api.RoomCallState
|
import io.element.android.features.roomcall.api.RoomCallState
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||||
|
|
@ -25,12 +26,13 @@ class RoomCallStatePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canUserJoinCallResult = { Result.success(false) },
|
canUserJoinCallResult = { Result.success(false) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState).isEqualTo(
|
assertThat(initialState).isEqualTo(
|
||||||
RoomCallState.StandBy(
|
RoomCallState.StandBy(
|
||||||
|
|
@ -40,10 +42,29 @@ class RoomCallStatePresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - element call not available`() = runTest {
|
||||||
|
val room = FakeJoinedRoom(
|
||||||
|
baseRoom = FakeBaseRoom(
|
||||||
|
canUserJoinCallResult = { Result.success(false) },
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val presenter = createRoomCallStatePresenter(
|
||||||
|
joinedRoom = room,
|
||||||
|
isElementCallAvailable = false,
|
||||||
|
)
|
||||||
|
presenter.test {
|
||||||
|
val initialState = awaitItem()
|
||||||
|
assertThat(initialState).isEqualTo(
|
||||||
|
RoomCallState.Unavailable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state - user can join call`() = runTest {
|
fun `present - initial state - user can join call`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -69,6 +90,7 @@ class RoomCallStatePresenterTest {
|
||||||
)
|
)
|
||||||
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
skipItems(1)
|
||||||
assertThat(awaitItem()).isEqualTo(
|
assertThat(awaitItem()).isEqualTo(
|
||||||
RoomCallState.OnGoing(
|
RoomCallState.OnGoing(
|
||||||
canJoinCall = false,
|
canJoinCall = false,
|
||||||
|
|
@ -83,15 +105,15 @@ class RoomCallStatePresenterTest {
|
||||||
fun `present - user has joined the call on another session`() = runTest {
|
fun `present - user has joined the call on another session`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(
|
givenRoomInfo(
|
||||||
aRoomInfo(
|
aRoomInfo(
|
||||||
hasRoomCall = true,
|
hasRoomCall = true,
|
||||||
activeRoomCallParticipants = listOf(sessionId),
|
activeRoomCallParticipants = listOf(sessionId),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
|
@ -110,15 +132,15 @@ class RoomCallStatePresenterTest {
|
||||||
fun `present - user has joined the call locally`() = runTest {
|
fun `present - user has joined the call locally`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(
|
givenRoomInfo(
|
||||||
aRoomInfo(
|
aRoomInfo(
|
||||||
hasRoomCall = true,
|
hasRoomCall = true,
|
||||||
activeRoomCallParticipants = listOf(sessionId),
|
activeRoomCallParticipants = listOf(sessionId),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
val presenter = createRoomCallStatePresenter(
|
val presenter = createRoomCallStatePresenter(
|
||||||
joinedRoom = room,
|
joinedRoom = room,
|
||||||
|
|
@ -140,15 +162,15 @@ class RoomCallStatePresenterTest {
|
||||||
fun `present - user leaves the call`() = runTest {
|
fun `present - user leaves the call`() = runTest {
|
||||||
val room = FakeJoinedRoom(
|
val room = FakeJoinedRoom(
|
||||||
baseRoom = FakeBaseRoom(
|
baseRoom = FakeBaseRoom(
|
||||||
canUserJoinCallResult = { Result.success(true) },
|
canUserJoinCallResult = { Result.success(true) },
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomInfo(
|
givenRoomInfo(
|
||||||
aRoomInfo(
|
aRoomInfo(
|
||||||
hasRoomCall = true,
|
hasRoomCall = true,
|
||||||
activeRoomCallParticipants = listOf(sessionId),
|
activeRoomCallParticipants = listOf(sessionId),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
val currentCall = MutableStateFlow<CurrentCall>(CurrentCall.RoomCall(room.roomId))
|
val currentCall = MutableStateFlow<CurrentCall>(CurrentCall.RoomCall(room.roomId))
|
||||||
val currentCallService = FakeCurrentCallService(currentCall = currentCall)
|
val currentCallService = FakeCurrentCallService(currentCall = currentCall)
|
||||||
|
|
@ -203,10 +225,14 @@ class RoomCallStatePresenterTest {
|
||||||
private fun createRoomCallStatePresenter(
|
private fun createRoomCallStatePresenter(
|
||||||
joinedRoom: JoinedRoom,
|
joinedRoom: JoinedRoom,
|
||||||
currentCallService: CurrentCallService = FakeCurrentCallService(),
|
currentCallService: CurrentCallService = FakeCurrentCallService(),
|
||||||
|
isElementCallAvailable: Boolean = true,
|
||||||
): RoomCallStatePresenter {
|
): RoomCallStatePresenter {
|
||||||
return RoomCallStatePresenter(
|
return RoomCallStatePresenter(
|
||||||
room = joinedRoom,
|
room = joinedRoom,
|
||||||
currentCallService = currentCallService,
|
currentCallService = currentCallService,
|
||||||
|
enterpriseService = FakeEnterpriseService(
|
||||||
|
isElementCallAvailableResult = { isElementCallAvailable },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ dependencies {
|
||||||
implementation(projects.libraries.androidutils)
|
implementation(projects.libraries.androidutils)
|
||||||
implementation(projects.libraries.mediaviewer.api)
|
implementation(projects.libraries.mediaviewer.api)
|
||||||
implementation(projects.features.call.api)
|
implementation(projects.features.call.api)
|
||||||
|
implementation(projects.features.enterprise.api)
|
||||||
implementation(projects.features.verifysession.api)
|
implementation(projects.features.verifysession.api)
|
||||||
api(projects.features.userprofile.api)
|
api(projects.features.userprofile.api)
|
||||||
api(projects.features.userprofile.shared)
|
api(projects.features.userprofile.shared)
|
||||||
|
|
@ -49,6 +50,7 @@ dependencies {
|
||||||
testImplementation(libs.test.robolectric)
|
testImplementation(libs.test.robolectric)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.features.createroom.test)
|
testImplementation(projects.features.createroom.test)
|
||||||
|
testImplementation(projects.features.enterprise.test)
|
||||||
testImplementation(projects.tests.testutils)
|
testImplementation(projects.tests.testutils)
|
||||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||||
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.createroom.api.StartDMAction
|
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.UserProfileEvents
|
||||||
import io.element.android.features.userprofile.api.UserProfileState
|
import io.element.android.features.userprofile.api.UserProfileState
|
||||||
import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog
|
import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog
|
||||||
|
|
@ -44,6 +45,7 @@ class UserProfilePresenter @AssistedInject constructor(
|
||||||
@Assisted private val userId: UserId,
|
@Assisted private val userId: UserId,
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val startDMAction: StartDMAction,
|
private val startDMAction: StartDMAction,
|
||||||
|
private val enterpriseService: EnterpriseService,
|
||||||
) : Presenter<UserProfileState> {
|
) : Presenter<UserProfileState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
@ -59,11 +61,21 @@ class UserProfilePresenter @AssistedInject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getCanCall(roomId: RoomId?): State<Boolean> {
|
private fun getCanCall(roomId: RoomId?): State<Boolean> {
|
||||||
return produceState(initialValue = false, roomId) {
|
val isElementCallAvailable by produceState(initialValue = false, roomId) {
|
||||||
value = if (client.isMe(userId)) {
|
value = enterpriseService.isElementCallAvailable()
|
||||||
false
|
}
|
||||||
} else {
|
|
||||||
roomId?.let { client.getRoom(it)?.canUserJoinCall(client.sessionId)?.getOrNull() == true }.orFalse()
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.ConfirmingStartDmWithMatrixUser
|
||||||
import io.element.android.features.createroom.api.StartDMAction
|
import io.element.android.features.createroom.api.StartDMAction
|
||||||
import io.element.android.features.createroom.test.FakeStartDMAction
|
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.UserProfileEvents
|
||||||
import io.element.android.features.userprofile.api.UserProfileState
|
import io.element.android.features.userprofile.api.UserProfileState
|
||||||
import io.element.android.features.userprofile.api.UserProfileVerificationState
|
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.test.room.FakeBaseRoom
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
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.any
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.lambda.value
|
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`() {
|
fun `present - canCall is true when all the conditions are met`() {
|
||||||
testCanCall(
|
testCanCall(
|
||||||
expectedResult = true,
|
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(
|
private fun testCanCall(
|
||||||
|
isElementCallAvailable: Boolean = true,
|
||||||
canUserJoinCallResult: Result<Boolean> = Result.success(true),
|
canUserJoinCallResult: Result<Boolean> = Result.success(true),
|
||||||
dmRoom: RoomId? = A_ROOM_ID,
|
dmRoom: RoomId? = A_ROOM_ID,
|
||||||
canFindRoom: Boolean = true,
|
canFindRoom: Boolean = true,
|
||||||
expectedResult: Boolean,
|
expectedResult: Boolean,
|
||||||
|
skipItems: Int = 1,
|
||||||
|
checkThatRoomIsDestroyed: Boolean = false,
|
||||||
) = runTest {
|
) = runTest {
|
||||||
val room = FakeBaseRoom(
|
val room = FakeBaseRoom(
|
||||||
canUserJoinCallResult = { canUserJoinCallResult },
|
canUserJoinCallResult = { canUserJoinCallResult },
|
||||||
|
|
@ -134,11 +147,15 @@ class UserProfilePresenterTest {
|
||||||
val presenter = createUserProfilePresenter(
|
val presenter = createUserProfilePresenter(
|
||||||
userId = A_USER_ID_2,
|
userId = A_USER_ID_2,
|
||||||
client = client,
|
client = client,
|
||||||
|
isElementCallAvailable = isElementCallAvailable,
|
||||||
)
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitLastSequentialItem()
|
val initialState = awaitFirstItem(skipItems)
|
||||||
assertThat(initialState.canCall).isEqualTo(expectedResult)
|
assertThat(initialState.canCall).isEqualTo(expectedResult)
|
||||||
}
|
}
|
||||||
|
if (checkThatRoomIsDestroyed) {
|
||||||
|
room.assertDestroyed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -202,7 +219,7 @@ class UserProfilePresenterTest {
|
||||||
)
|
)
|
||||||
val presenter = createUserProfilePresenter(client = matrixClient)
|
val presenter = createUserProfilePresenter(client = matrixClient)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitFirstItem()
|
val initialState = awaitFirstItem(count = 2)
|
||||||
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
|
initialState.eventSink(UserProfileEvents.BlockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
|
|
@ -220,7 +237,7 @@ class UserProfilePresenterTest {
|
||||||
)
|
)
|
||||||
val presenter = createUserProfilePresenter(client = matrixClient)
|
val presenter = createUserProfilePresenter(client = matrixClient)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitFirstItem()
|
val initialState = awaitFirstItem(count = 2)
|
||||||
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
|
initialState.eventSink(UserProfileEvents.UnblockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
|
|
@ -363,8 +380,8 @@ class UserProfilePresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(count: Int = 1): T {
|
||||||
skipItems(1)
|
skipItems(count)
|
||||||
return awaitItem()
|
return awaitItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,12 +404,16 @@ class UserProfilePresenterTest {
|
||||||
private fun createUserProfilePresenter(
|
private fun createUserProfilePresenter(
|
||||||
client: MatrixClient = createFakeMatrixClient(),
|
client: MatrixClient = createFakeMatrixClient(),
|
||||||
userId: UserId = UserId("@alice:server.org"),
|
userId: UserId = UserId("@alice:server.org"),
|
||||||
startDMAction: StartDMAction = FakeStartDMAction()
|
startDMAction: StartDMAction = FakeStartDMAction(),
|
||||||
|
isElementCallAvailable: Boolean = true,
|
||||||
): UserProfilePresenter {
|
): UserProfilePresenter {
|
||||||
return UserProfilePresenter(
|
return UserProfilePresenter(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
client = client,
|
client = client,
|
||||||
startDMAction = startDMAction,
|
startDMAction = startDMAction,
|
||||||
|
enterpriseService = FakeEnterpriseService(
|
||||||
|
isElementCallAvailableResult = { isElementCallAvailable },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,15 @@ class FakeBaseRoom(
|
||||||
return powerLevelsResult()
|
return powerLevelsResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy() = Unit
|
private var isDestroyed = false
|
||||||
|
|
||||||
|
override fun destroy() {
|
||||||
|
isDestroyed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertDestroyed() {
|
||||||
|
check(isDestroyed) { "Room should be destroyed" }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun userDisplayName(userId: UserId): Result<String?> = simulateLongTask {
|
override suspend fun userDisplayName(userId: UserId): Result<String?> = simulateLongTask {
|
||||||
userDisplayNameResult(userId)
|
userDisplayNameResult(userId)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:96a867cb12498cbdc97957bee07855dfaa13602baddaf933aff2b666ef4c7650
|
||||||
|
size 3642
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:5bb36ccd718f3fec5b04f1bc812dc7718b5ea7fa4619c8b031466297a8d016fd
|
||||||
|
size 3659
|
||||||
Loading…
Add table
Add a link
Reference in a new issue