diff --git a/.maestro/tests/roomList/createAndDeleteRoom.yaml b/.maestro/tests/roomList/createAndDeleteRoom.yaml index 60619154935..ae6f5772c65 100644 --- a/.maestro/tests/roomList/createAndDeleteRoom.yaml +++ b/.maestro/tests/roomList/createAndDeleteRoom.yaml @@ -16,7 +16,7 @@ appId: ${MAESTRO_APP_ID} - tapOn: "Create" - takeScreenshot: build/maestro/320-createAndDeleteRoom - tapOn: "aRoomName" -- tapOn: "Invite people" +- tapOn: "Invite" # assert there's 1 member and 1 invitee - tapOn: "Search for someone" - inputText: ${MAESTRO_INVITEE2_MXID} diff --git a/changelog.d/2814.misc b/changelog.d/2814.misc new file mode 100644 index 00000000000..14fa36637fc --- /dev/null +++ b/changelog.d/2814.misc @@ -0,0 +1 @@ +UX cleanup: room details screen, add new CTA buttons for Invite and Call actions. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 5825d5acfbf..e5127ea865e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -86,6 +86,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MessageEventType 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.canCall import io.element.android.libraries.matrix.ui.room.canRedactOtherAsState import io.element.android.libraries.matrix.ui.room.canRedactOwnAsState import io.element.android.libraries.matrix.ui.room.canSendMessageAsState @@ -158,9 +159,7 @@ class MessagesPresenter @AssistedInject constructor( mutableStateOf(false) } - var canJoinCall by rememberSaveable { - mutableStateOf(false) - } + val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value) LaunchedEffect(Unit) { // Remove the unread flag on entering but don't send read receipts @@ -170,12 +169,6 @@ class MessagesPresenter @AssistedInject constructor( } } - LaunchedEffect(syncUpdateFlow.value) { - withContext(dispatchers.io) { - canJoinCall = room.canUserJoinCall(room.sessionId).getOrDefault(false) - } - } - val inviteProgress = remember { mutableStateOf>(AsyncData.Uninitialized) } var showReinvitePrompt by remember { mutableStateOf(false) } LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow.value) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 8ef9d3d2688..3adc7059b0a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -288,7 +288,7 @@ class MessagesPresenterTest { initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent())) val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None) } } @@ -298,10 +298,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(2) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null))) - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(initialState.actionListState.target).isEqualTo(ActionListState.Target.None) // Otherwise we would have some extra items here ensureAllEventsConsumed() } @@ -335,7 +334,7 @@ class MessagesPresenterTest { assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply assertThat(replyMode.attachmentThumbnailInfo).isNotNull() - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None) } } @@ -368,7 +367,7 @@ class MessagesPresenterTest { assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply assertThat(replyMode.attachmentThumbnailInfo).isNotNull() - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None) } } @@ -394,7 +393,7 @@ class MessagesPresenterTest { assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply assertThat(replyMode.attachmentThumbnailInfo).isNotNull() - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None) } } @@ -408,7 +407,7 @@ class MessagesPresenterTest { initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent())) val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Edit::class.java) - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None) } } @@ -732,7 +731,7 @@ class MessagesPresenterTest { assertThat(replyMode.attachmentThumbnailInfo).isNotNull() assertThat(replyMode.attachmentThumbnailInfo?.textContent) .isEqualTo("What type of food should we have at the party?") - assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + assertThat(finalState.actionListState.target).isEqualTo(ActionListState.Target.None) } } diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 624331f62c1..037165e4eec 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -56,8 +56,9 @@ dependencies { api(projects.libraries.usersearch.api) api(projects.services.apperror.api) implementation(libs.coil.compose) - implementation(projects.features.leaveroom.api) + implementation(projects.features.call) implementation(projects.features.createroom.api) + implementation(projects.features.leaveroom.api) implementation(projects.features.userprofile.shared) implementation(projects.services.analytics.api) implementation(projects.features.poll.api) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 239e7dd2996..460d3038acc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -16,6 +16,7 @@ package io.element.android.features.roomdetails.impl +import android.content.Context import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -28,6 +29,8 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.call.CallType +import io.element.android.features.call.ui.ElementCallActivity import io.element.android.features.poll.api.history.PollHistoryEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode @@ -42,10 +45,12 @@ import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.mediaviewer.api.local.MediaInfo import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import kotlinx.parcelize.Parcelize @@ -54,7 +59,9 @@ import kotlinx.parcelize.Parcelize class RoomDetailsFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + @ApplicationContext private val context: Context, private val pollHistoryEntryPoint: PollHistoryEntryPoint, + private val room: MatrixRoom, ) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), @@ -129,6 +136,14 @@ class RoomDetailsFlowNode @AssistedInject constructor( override fun openAdminSettings() { backstack.push(NavTarget.AdminSettings) } + + override fun onJoinCall() { + val inputs = CallType.RoomCall( + sessionId = room.sessionId, + roomId = room.roomId, + ) + ElementCallActivity.start(context, inputs) + } } createNode(buildContext, listOf(roomDetailsCallback)) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index 84658ccc832..80a2d5d5d41 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -58,6 +58,7 @@ class RoomDetailsNode @AssistedInject constructor( fun openAvatarPreview(name: String, url: String) fun openPollHistory() fun openAdminSettings() + fun onJoinCall() } private val callbacks = plugins() @@ -86,6 +87,10 @@ class RoomDetailsNode @AssistedInject constructor( callbacks.forEach { it.openPollHistory() } } + private fun onJoinCall() { + callbacks.forEach { it.onJoinCall() } + } + private fun CoroutineScope.onShareRoom(context: Context) = launch { room.getPermalink() .onSuccess { permalink -> @@ -162,6 +167,7 @@ class RoomDetailsNode @AssistedInject constructor( openAvatarPreview = ::openAvatarPreview, openPollHistory = ::openPollHistory, openAdminSettings = this::openAdminSettings, + onJoinCallClicked = ::onJoinCall, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index c8b0f49346c..5aa7baf0ea4 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -44,6 +44,7 @@ import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canSendState import io.element.android.libraries.matrix.api.room.roomNotificationSettings +import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin import io.element.android.services.analytics.api.AnalyticsService @@ -86,11 +87,14 @@ class RoomDetailsPresenter @Inject constructor( } } + val syncUpdateTimestamp by room.syncUpdateFlow.collectAsState() + val membersState by room.membersStateFlow.collectAsState() val canInvite by getCanInvite(membersState) val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME) val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR) val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC) + val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp) val dmMember by room.getDirectRoomMember(membersState) val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) val roomType by getRoomType(dmMember) @@ -138,6 +142,7 @@ class RoomDetailsPresenter @Inject constructor( canInvite = canInvite, canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room, canShowNotificationSettings = canShowNotificationSettings.value, + canCall = canJoinCall, roomType = roomType, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index 668add6f1c9..51b89b50b56 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -36,6 +36,7 @@ data class RoomDetailsState( val canEdit: Boolean, val canInvite: Boolean, val canShowNotificationSettings: Boolean, + val canCall: Boolean, val leaveRoomState: LeaveRoomState, val roomNotificationSettings: RoomNotificationSettings?, val isFavorite: Boolean, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index c2b262ac71b..5bfac47f7e4 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -46,6 +46,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider // Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true) ), + aRoomDetailsState(canCall = false, canInvite = false), // Add other state here ) } @@ -89,6 +90,7 @@ fun aRoomDetailsState( canInvite: Boolean = false, canEdit: Boolean = false, canShowNotificationSettings: Boolean = true, + canCall: Boolean = true, roomType: RoomDetailsType = RoomDetailsType.Room, roomMemberDetailsState: UserProfileState? = null, leaveRoomState: LeaveRoomState = aLeaveRoomState(), @@ -107,6 +109,7 @@ fun aRoomDetailsState( canInvite = canInvite, canEdit = canEdit, canShowNotificationSettings = canShowNotificationSettings, + canCall = canCall, roomType = roomType, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 5058a38a362..10a9609ff96 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -100,6 +99,7 @@ fun RoomDetailsView( openAvatarPreview: (name: String, url: String) -> Unit, openPollHistory: () -> Unit, openAdminSettings: () -> Unit, + onJoinCallClicked: () -> Unit, modifier: Modifier = Modifier, ) { fun onShareMember() { @@ -137,7 +137,9 @@ fun RoomDetailsView( ) MainActionsSection( state = state, - onShareRoom = onShareRoom + onShareRoom = onShareRoom, + onInvitePeople = invitePeople, + onCall = onJoinCallClicked, ) } @@ -188,20 +190,12 @@ fun RoomDetailsView( } val displayMemberListItem = state.roomType is RoomDetailsType.Room - val displayInviteMembersItem = state.canInvite - if (displayMemberListItem || displayInviteMembersItem) { + if (displayMemberListItem) { PreferenceCategory { - if (displayMemberListItem) { - MembersItem( - memberCount = state.memberCount, - openRoomMemberList = openRoomMemberList, - ) - } - if (displayInviteMembersItem) { - InviteItem( - invitePeople = invitePeople - ) - } + MembersItem( + memberCount = state.memberCount, + openRoomMemberList = openRoomMemberList, + ) } } @@ -267,10 +261,12 @@ private fun RoomDetailsTopBar( private fun MainActionsSection( state: RoomDetailsState, onShareRoom: () -> Unit, + onInvitePeople: () -> Unit, + onCall: () -> Unit, ) { Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.SpaceEvenly, ) { val roomNotificationSettings = state.roomNotificationSettings if (state.canShowNotificationSettings && roomNotificationSettings != null) { @@ -292,9 +288,22 @@ private fun MainActionsSection( ) } } - Spacer(modifier = Modifier.width(20.dp)) + if (state.canCall) { + MainActionButton( + title = stringResource(CommonStrings.action_call), + imageVector = CompoundIcons.VideoCall(), + onClick = onCall, + ) + } + if (state.roomType is RoomDetailsType.Room && state.canInvite) { + MainActionButton( + title = stringResource(CommonStrings.action_invite), + imageVector = CompoundIcons.UserAdd(), + onClick = onInvitePeople, + ) + } MainActionButton( - title = stringResource(R.string.screen_room_details_share_room_title), + title = stringResource(CommonStrings.action_share), imageVector = CompoundIcons.ShareAndroid(), onClick = onShareRoom ) @@ -410,17 +419,6 @@ private fun MembersItem( ) } -@Composable -private fun InviteItem( - invitePeople: () -> Unit, -) { - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_details_invite_people_title)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserAdd())), - onClick = invitePeople, - ) -} - @Composable private fun PollsSection( openPollHistory: () -> Unit, @@ -491,5 +489,6 @@ private fun ContentToPreview(state: RoomDetailsState) { openAvatarPreview = { _, _ -> }, openPollHistory = {}, openAdminSettings = {}, + onJoinCallClicked = {}, ) } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index f4e7ef1d60b..27317156e28 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -62,7 +62,7 @@ class RoomDetailsViewTest { rule.setRoomDetailView( onShareRoom = callback, ) - rule.clickOn(R.string.screen_room_details_share_room_title) + rule.clickOn(CommonStrings.action_share) } } @@ -112,9 +112,8 @@ class RoomDetailsViewTest { } } - @Config(qualifiers = "h1024dp") @Test - fun `click on invite people invokes expected callback`() { + fun `click on invite invokes expected callback`() { ensureCalledOnce { callback -> rule.setRoomDetailView( state = aRoomDetailsState( @@ -123,7 +122,21 @@ class RoomDetailsViewTest { ), invitePeople = callback, ) - rule.clickOn(R.string.screen_room_details_invite_people_title) + rule.clickOn(CommonStrings.action_invite) + } + } + + @Test + fun `click on call invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + canInvite = true, + ), + onJoinCallClicked = callback, + ) + rule.clickOn(CommonStrings.action_call) } } @@ -258,6 +271,7 @@ private fun AndroidComposeTestRule.setRoomD openAvatarPreview: (name: String, url: String) -> Unit = EnsureNeverCalledWithTwoParams(), openPollHistory: () -> Unit = EnsureNeverCalled(), openAdminSettings: () -> Unit = EnsureNeverCalled(), + onJoinCallClicked: () -> Unit = EnsureNeverCalled(), ) { setContent { RoomDetailsView( @@ -272,6 +286,7 @@ private fun AndroidComposeTestRule.setRoomD openAvatarPreview = openAvatarPreview, openPollHistory = openPollHistory, openAdminSettings = openAdminSettings, + onJoinCallClicked = onJoinCallClicked, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt index bc530b8fd32..a1172b47fa1 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/MainActionButton.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -53,12 +54,14 @@ fun MainActionButton( val ripple = rememberRipple(bounded = false) val interactionSource = remember { MutableInteractionSource() } Column( - modifier.clickable( - enabled = enabled, - interactionSource = interactionSource, - onClick = onClick, - indication = ripple - ), + modifier + .clickable( + enabled = enabled, + interactionSource = interactionSource, + onClick = onClick, + indication = ripple + ) + .widthIn(min = 76.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { val tintColor = if (enabled) LocalContentColor.current else MaterialTheme.colorScheme.secondary diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 4a80709d425..aa3121146a8 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -49,6 +49,13 @@ fun MatrixRoom.canRedactOtherAsState(updateKey: Long): State { } } +@Composable +fun MatrixRoom.canCall(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canUserJoinCall(sessionId).getOrElse { false } + } +} + @Composable fun MatrixRoom.isOwnUserAdmin(): Boolean { val roomInfo by roomInfoFlow.collectAsState(initial = null) diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index cede56abc39..4f88ddc05ca 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -34,6 +34,7 @@ "Accept" "Add to timeline" "Back" + "Call" "Cancel" "Choose photo" "Clear" diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png index a9aa4137684..0aa6af6ee6e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe5642aa6804a99d9b3505bb63fe0c328015592f35e095415c910d9a5abfa052 -size 47238 +oid sha256:ba1133eb6569d4821b12fe56c3347a472bd95ad5b269546fed57f249e365d432 +size 47379 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png index 06dcb0c6f84..47ed3e0c551 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7465cc5ca6b9f49ab4ac1a529fc2ee21547afb85a231d74491df6b2a2781bd18 -size 33075 +oid sha256:d0f46ff6cfe976d3d96ecbf323fcf2ed2ed57c326c3bed3c450c30e851442567 +size 33239 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 00000000000..ac8b4f88da8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3a1bc43adac954bc9ab1b9b9d69814e376d2f2a41006b8126acdeb19bb93df7 +size 44471 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png index 13b74220aed..2d3b01d016c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93d4eb6dce460237fe6650e965b901c28b020057907867ac9f73665032a85f88 -size 35427 +oid sha256:84dcef17832aad71a9bf0505346731b27f882795d9bf75b194b647dfad40eca2 +size 35595 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png index 1253286deb2..a913fd1e41e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8a655fb4e64fa300c6169d332fa894b6bb215a9acd09c0fbe876c7430f3ae1d -size 34786 +oid sha256:72b731453adaad784d3c037c06defd7b24939435ab54ee47a693f6aabcc0dce7 +size 34928 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png index ba3922dabfa..315ccc031de 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4329c322042bc8489177886e37f6d3a876cbfee8a8a73e3aae6414a17e92ad67 -size 42330 +oid sha256:c675a4d9422ba2d73e860420e25f10a21c899b33e780a4f61a41f71921f3078a +size 42471 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png index 265ed3a00e7..3e5b34bb681 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8359c8e69df44350b30b5aaf9ad99ace52fe378ba6db2cfd86a4deb35aa3abd1 -size 46347 +oid sha256:78a182e17a2ef55a4d7dbcb4ad952d5f06f12044da4dc3578a5cd3cb7b7d34a1 +size 45723 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png index 6bcd031f28b..e8fc878d377 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8659b897a22fd88915a8df3de3f0ba8d6775057f51b1c5cd1db83b0e58497576 -size 44394 +oid sha256:ccf59aaaba1ebbf35518840ba069f2ea587c5f5590ed1f0be5904922d10321e0 +size 44535 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_9,NEXUS_5,1.0,en].png index 211b17e1453..528460a99a8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a195980dff4ad4343eec99438a35ab655abd124346f5e4d66011c3c1736938e -size 44589 +oid sha256:972414b2900a7b2fddc7c45640f0718bbed3ff799067a0dde4f9e3f2fb70266a +size 44574 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png index b377d61fc33..8674de33bad 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29abf008345cd7c7150a64b17c6bd1c0af06c8d960c5e91a2cf0e4b1c0365954 -size 48791 +oid sha256:f89ab41056584b086cf6fbeabff68cee7ee7dde4d52fcef9849dcdc296c69a1e +size 48983 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png index c359f319de7..3fe4f5ea557 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6228ac8af567a9d4fc402d2b14ff65315f6cfca2d02407664b0db00df5fa68e -size 34363 +oid sha256:d3dead46255af9a750e9afe09f2e5201a4f12adea8f2e1a3d0806062775e3ea0 +size 34587 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_10,NEXUS_5,1.0,en].png new file mode 100644 index 00000000000..5f4f2473e5e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_10,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52ded1bfd8ff7a95041172bdca31be2a869f5e2b81f1b181f0ece14ffdf4c7aa +size 45708 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png index ffce2cd6257..a9bcccc16c9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3987ef26780da018bedbe05b1af43c3a9c3f31963aff1eb17833a284118d29ec -size 36951 +oid sha256:82107faba76f5c6cc992ec47d6e4d765761743094fcae65c5ed948fce3cf1f05 +size 37183 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png index 65597d5fb93..ace749a5730 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c74c580b51aaa480a1ca15f2f33e3e6acb9e2a5581d5501ff67b6c4b56d92099 -size 35582 +oid sha256:73a016fc3d40517042f8d231ab682d71e477843195fbe4e080cd62fc12e8b954 +size 35778 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png index 4e26b62c8bb..d6cd988f6aa 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d81c82c5d5f07bb57911488e18e2cf0867009233ad678557a8ead13e6827ba2b -size 43640 +oid sha256:b9250d7927307d397b969e0803ed9965a7ae301347446fef264004d690d064f6 +size 43826 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png index b708fb25921..fb7e2778dc7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdfe3fb1c1594335fda27a62f2c1b4740815778248ba76923a2184937db02144 -size 47743 +oid sha256:fd42eba5e0daa676298cfb2813d595083e8abd1c5abf8dfb416910fbbc2ea02c +size 47128 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png index ee4cb47b6f9..5c7801f83c8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d23cfed590306aec6eb92664c616e38f18e54274bb381e2e044e22916e8b1129 -size 45723 +oid sha256:58cdd2ba7d10769a29ad59fa2cff7c972767d01352d656a06bfcb922522980b3 +size 45918 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_9,NEXUS_5,1.0,en].png index 3f2e7020243..9945d7f6f08 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33e9d17227fda64a35cd885d029b70bab0088d5748cc5440c32d1cfa0a3bbf52 -size 45908 +oid sha256:43d176f6e953006f72b09ba374f95c28388f10ce600c9d495761c4c11ccc109b +size 45874 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png index 5f0958761f3..503e6218824 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.button_MainActionButton_null_Buttons_MainActionButton_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a4cd869d7be5ebd07dc72947fe36f6d601fa55494bb4f7c75b828d1569d983a -size 15105 +oid sha256:9c5d638b74379f27b9774309848b4cb068514faa856f5882f8f0f8614ecaad12 +size 15712