From da1f8cd4650739335bd4c863cee6ea785ce3d285 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jun 2024 15:43:32 +0200 Subject: [PATCH] Use DmAvatars in header of DM details. --- .../roomdetails/impl/RoomDetailsPresenter.kt | 16 +++-- .../roomdetails/impl/RoomDetailsState.kt | 5 +- .../impl/RoomDetailsStateProvider.kt | 6 +- .../roomdetails/impl/RoomDetailsView.kt | 58 ++++++++++++++++--- .../roomdetails/RoomDetailsPresenterTest.kt | 8 ++- .../roomdetails/impl/RoomDetailsViewTest.kt | 7 ++- .../components/avatar/DmAvatars.kt | 3 + .../matrix/ui/room/MatrixRoomMembers.kt | 11 ++++ 8 files changed, 97 insertions(+), 17 deletions(-) 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 3d07ea3e63..fdca72fa1d 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 @@ -45,6 +45,7 @@ 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.getCurrentRoomMember 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 @@ -98,8 +99,9 @@ class RoomDetailsPresenter @Inject constructor( val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC) val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp) val dmMember by room.getDirectRoomMember(membersState) + val currentMember by room.getCurrentRoomMember(membersState) val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) - val roomType by getRoomType(dmMember) + val roomType by getRoomType(dmMember, currentMember) val topicState = remember(canEditTopic, roomTopic, roomType) { val topic = roomTopic @@ -165,10 +167,16 @@ class RoomDetailsPresenter @Inject constructor( } @Composable - private fun getRoomType(dmMember: RoomMember?): State = remember(dmMember) { + private fun getRoomType( + dmMember: RoomMember?, + currentMember: RoomMember?, + ): State = remember(dmMember, currentMember) { derivedStateOf { - if (dmMember != null) { - RoomDetailsType.Dm(dmMember) + if (dmMember != null && currentMember != null) { + RoomDetailsType.Dm( + me = currentMember, + roomMember = dmMember, + ) } else { RoomDetailsType.Room } 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 787aa79741..b991130111 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 @@ -52,7 +52,10 @@ data class RoomDetailsState( @Immutable sealed interface RoomDetailsType { data object Room : RoomDetailsType - data class Dm(val roomMember: RoomMember) : RoomDetailsType + data class Dm( + val me: RoomMember, + val roomMember: RoomMember, + ) : RoomDetailsType } @Immutable 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 6c3e9bdac0..3b35375aac 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 @@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.userprofile.shared.UserProfileState import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.matrix.api.core.RoomAlias @@ -141,6 +142,9 @@ fun aDmRoomDetailsState( roomName: String = "Daniel", ) = aRoomDetailsState( roomName = roomName, - roomType = RoomDetailsType.Dm(aDmRoomMember(isIgnored = isDmMemberIgnored)), + roomType = RoomDetailsType.Dm( + aRoomMember(), + aDmRoomMember(isIgnored = isDmMemberIgnored), + ), roomMemberDetailsState = aUserProfileState() ) 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 c66b413d0b..be2b661bf3 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 @@ -40,6 +40,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter @@ -48,7 +49,6 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.roomdetails.impl.components.RoomBadge -import io.element.android.features.userprofile.shared.UserProfileHeaderSection import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage @@ -56,6 +56,7 @@ import io.element.android.libraries.designsystem.components.ClickableLinkText import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.CompositeAvatar +import io.element.android.libraries.designsystem.components.avatar.DmAvatars import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.MainActionButton import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -78,6 +79,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.getBestName import io.element.android.libraries.matrix.api.user.MatrixUser @@ -137,13 +139,12 @@ fun RoomDetailsView( ) } is RoomDetailsType.Dm -> { - val member = state.roomType.roomMember - UserProfileHeaderSection( - avatarUrl = state.roomAvatarUrl ?: member.avatarUrl, - userId = member.userId, - userName = state.roomName, - openAvatarPreview = { avatarUrl -> - openAvatarPreview(member.getBestName(), avatarUrl) + DmHeaderSection( + me = state.roomType.me, + otherMember = state.roomType.roomMember, + roomName = state.roomName, + openAvatarPreview = { name, avatarUrl -> + openAvatarPreview(name, avatarUrl) }, ) } @@ -359,6 +360,47 @@ private fun RoomHeaderSection( } } +@Composable +private fun DmHeaderSection( + me: RoomMember, + otherMember: RoomMember, + roomName: String, + openAvatarPreview: (name: String, url: String) -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + DmAvatars( + userAvatarData = me.getAvatarData(size = AvatarSize.DmCluster), + otherUserAvatarData = otherMember.getAvatarData(size = AvatarSize.DmCluster), + openAvatarPreview = { url -> openAvatarPreview(me.getBestName(), url) }, + openOtherAvatarPreview = { url -> openAvatarPreview(roomName, url) }, + ) + Spacer(modifier = Modifier.height(24.dp)) + Text( + modifier = Modifier.clipToBounds(), + text = roomName, + style = ElementTheme.typography.fontHeadingLgBold, + textAlign = TextAlign.Center, + ) + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = otherMember.userId.value, + style = ElementTheme.typography.fontBodyLgRegular, + color = MaterialTheme.colorScheme.secondary, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + textAlign = TextAlign.Center, + ) + Spacer(Modifier.height(40.dp)) + } +} + @Composable private fun BadgeList( isEncrypted: Boolean, diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt index e3788e4187..351f2ca5e3 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTest.kt @@ -169,8 +169,12 @@ class RoomDetailsPresenterTest { val presenter = createRoomDetailsPresenter(room) presenter.test { val initialState = awaitItem() - assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherRoomMember)) - + assertThat(initialState.roomType).isEqualTo( + RoomDetailsType.Dm( + me = myRoomMember, + roomMember = otherRoomMember, + ) + ) cancelAndIgnoreRemainingEvents() } } 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 dc1c1735cb..b3bbb21cf6 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 @@ -23,6 +23,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.ui.strings.CommonStrings @@ -177,7 +178,11 @@ class RoomDetailsViewTest { fun `click on avatar test on DM`() { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aRoomDetailsState( - roomType = RoomDetailsType.Dm(aDmRoomMember(avatarUrl = "an_avatar_url")), + roomType = RoomDetailsType.Dm( + aRoomMember(), + aDmRoomMember(avatarUrl = "an_avatar_url"), + ), + roomName = "Daniel", eventSink = eventsRecorder, ) val callback = EnsureCalledOnceWithTwoParams("Daniel", "an_avatar_url") diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt index a45fbde6e3..1c2d98eddc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt @@ -34,6 +34,8 @@ import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.text.toPx +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag /** * https://www.figma.com/design/A2pAEvTEpJZBiOPUlcMnKi/Settings-%2B-Room-Details-(new)?node-id=1787-56333 @@ -90,6 +92,7 @@ fun DmAvatars( .clickable(enabled = otherUserAvatarData.url != null) { otherUserAvatarData.url?.let { openOtherAvatarPreview(it) } } + .testTag(TestTags.memberDetailAvatar) ) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt index 668b963bf1..38fed2a60c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt @@ -63,3 +63,14 @@ fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): St } } } + +@Composable +fun MatrixRoom.getCurrentRoomMember(roomMembersState: MatrixRoomMembersState): State { + val roomMembers = roomMembersState.roomMembers() + return remember(roomMembersState) { + derivedStateOf { + roomMembers + ?.find { it.userId == sessionId } + } + } +}