Set a default power level to join calls in room (#1927)

* Set a default power level to join calls.

Also, create new rooms taking this power level into account.

* Modify test to make sure we display the disabled state even when there is an ongoing call

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
Jorge Martin Espinosa 2023-11-30 10:47:48 +01:00 committed by GitHub
parent 7582d2f12e
commit 2e8d5ac86a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 98 additions and 21 deletions

View file

@ -79,6 +79,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
import io.element.android.libraries.matrix.ui.room.canRedactAsState
@ -108,6 +109,7 @@ class MessagesPresenter @AssistedInject constructor(
private val featureFlagsService: FeatureFlagService,
@Assisted private val navigator: MessagesNavigator,
private val buildMeta: BuildMeta,
private val currentSessionIdHolder: CurrentSessionIdHolder,
) : Presenter<MessagesState> {
private val timelinePresenter = timelinePresenterFactory.create(navigator = navigator)
@ -144,6 +146,16 @@ class MessagesPresenter @AssistedInject constructor(
mutableStateOf(false)
}
var canJoinCall by rememberSaveable {
mutableStateOf(false)
}
LaunchedEffect(currentSessionIdHolder.current) {
withContext(dispatchers.io) {
canJoinCall = room.canUserJoinCall(userId = currentSessionIdHolder.current).getOrDefault(false)
}
}
val inviteProgress = remember { mutableStateOf<Async<Unit>>(Async.Uninitialized) }
var showReinvitePrompt by remember { mutableStateOf(false) }
LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) {
@ -162,8 +174,6 @@ class MessagesPresenter @AssistedInject constructor(
val enableTextFormatting by preferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true)
var enableVoiceMessages by remember { mutableStateOf(false) }
// TODO add min power level to use this feature in the future?
val enableInRoomCalls = true
LaunchedEffect(featureFlagsService) {
enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages)
}
@ -193,6 +203,12 @@ class MessagesPresenter @AssistedInject constructor(
}
}
val callState = when {
!canJoinCall -> RoomCallState.DISABLED
roomInfo?.hasRoomCall == true -> RoomCallState.ONGOING
else -> RoomCallState.ENABLED
}
return MessagesState(
roomId = room.roomId,
roomName = roomName,
@ -213,9 +229,8 @@ class MessagesPresenter @AssistedInject constructor(
inviteProgress = inviteProgress.value,
enableTextFormatting = enableTextFormatting,
enableVoiceMessages = enableVoiceMessages,
enableInRoomCalls = enableInRoomCalls,
appName = buildMeta.applicationName,
isCallOngoing = roomInfo?.hasRoomCall ?: false,
callState = callState,
eventSink = { handleEvents(it) }
)
}

View file

@ -51,8 +51,13 @@ data class MessagesState(
val showReinvitePrompt: Boolean,
val enableTextFormatting: Boolean,
val enableVoiceMessages: Boolean,
val enableInRoomCalls: Boolean,
val isCallOngoing: Boolean,
val callState: RoomCallState,
val appName: String,
val eventSink: (MessagesEvents) -> Unit
)
enum class RoomCallState {
ENABLED,
ONGOING,
DISABLED
}

View file

@ -66,7 +66,7 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
),
),
aMessagesState().copy(
isCallOngoing = true,
callState = RoomCallState.ONGOING,
),
aMessagesState().copy(
enableVoiceMessages = true,
@ -75,6 +75,9 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
showSendFailureDialog = true
),
),
aMessagesState().copy(
callState = RoomCallState.DISABLED,
),
)
}
@ -117,8 +120,7 @@ fun aMessagesState() = MessagesState(
showReinvitePrompt = false,
enableTextFormatting = true,
enableVoiceMessages = true,
enableInRoomCalls = true,
isCallOngoing = false,
callState = RoomCallState.ENABLED,
appName = "Element",
eventSink = {}
)

View file

@ -191,10 +191,9 @@ fun MessagesView(
MessagesViewTopBar(
roomName = state.roomName.dataOrNull(),
roomAvatar = state.roomAvatar.dataOrNull(),
inRoomCallsEnabled = state.enableInRoomCalls,
callState = state.callState,
onBackPressed = onBackPressed,
onRoomDetailsClicked = onRoomDetailsClicked,
isCallOngoing = state.isCallOngoing,
onJoinCallClicked = onJoinCallClicked,
)
}
@ -449,8 +448,7 @@ private fun MessagesViewComposerBottomSheetContents(
private fun MessagesViewTopBar(
roomName: String?,
roomAvatar: AvatarData?,
inRoomCallsEnabled: Boolean,
isCallOngoing: Boolean,
callState: RoomCallState,
onRoomDetailsClicked: () -> Unit,
onJoinCallClicked: () -> Unit,
onBackPressed: () -> Unit,
@ -477,13 +475,11 @@ private fun MessagesViewTopBar(
}
},
actions = {
if (inRoomCallsEnabled) {
if (isCallOngoing) {
JoinCallMenuItem(onJoinCallClicked = onJoinCallClicked)
} else {
IconButton(onClick = onJoinCallClicked) {
Icon(CompoundIcons.VideoCall, contentDescription = stringResource(CommonStrings.a11y_start_call))
}
if (callState == RoomCallState.ONGOING) {
JoinCallMenuItem(onJoinCallClicked = onJoinCallClicked)
} else {
IconButton(onClick = onJoinCallClicked, enabled = callState != RoomCallState.DISABLED) {
Icon(CompoundIcons.VideoCall, contentDescription = stringResource(CommonStrings.a11y_start_call))
}
}
Spacer(Modifier.width(8.dp))

View file

@ -99,6 +99,7 @@ import org.junit.Rule
import org.junit.Test
import kotlin.time.Duration.Companion.milliseconds
@Suppress("LargeClass")
class MessagesPresenterTest {
@get:Rule
@ -126,6 +127,21 @@ class MessagesPresenterTest {
}
}
@Test
fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest {
val room = FakeMatrixRoom().apply {
givenCanUserJoinCall(Result.success(false))
givenRoomInfo(aRoomInfo(hasRoomCall = true))
}
val presenter = createMessagesPresenter(matrixRoom = room)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = consumeItemsUntilTimeout().last()
assertThat(initialState.callState).isEqualTo(RoomCallState.DISABLED)
}
}
@Test
fun `present - handle toggling a reaction`() = runTest {
val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
@ -656,6 +672,7 @@ class MessagesPresenterTest {
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
currentSessionIdHolder: CurrentSessionIdHolder = CurrentSessionIdHolder(FakeMatrixClient(A_SESSION_ID)),
): MessagesPresenter {
val mediaSender = MediaSender(FakeMediaPreProcessor(), matrixRoom)
val permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter)
@ -724,6 +741,7 @@ class MessagesPresenterTest {
featureFlagsService = FakeFeatureFlagService(),
buildMeta = aBuildMeta(),
dispatchers = coroutineDispatchers,
currentSessionIdHolder = currentSessionIdHolder,
)
}
}