Use DmAvatars in header of DM details.
This commit is contained in:
parent
a2c8dc42cb
commit
e8fe8f51c8
8 changed files with 97 additions and 17 deletions
|
|
@ -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.powerlevels.canSendState
|
||||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
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.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.getDirectRoomMember
|
||||||
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
|
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
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 canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC)
|
||||||
val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp)
|
val canJoinCall by room.canCall(updateKey = syncUpdateTimestamp)
|
||||||
val dmMember by room.getDirectRoomMember(membersState)
|
val dmMember by room.getDirectRoomMember(membersState)
|
||||||
|
val currentMember by room.getCurrentRoomMember(membersState)
|
||||||
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
||||||
val roomType by getRoomType(dmMember)
|
val roomType by getRoomType(dmMember, currentMember)
|
||||||
|
|
||||||
val topicState = remember(canEditTopic, roomTopic, roomType) {
|
val topicState = remember(canEditTopic, roomTopic, roomType) {
|
||||||
val topic = roomTopic
|
val topic = roomTopic
|
||||||
|
|
@ -165,10 +167,16 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getRoomType(dmMember: RoomMember?): State<RoomDetailsType> = remember(dmMember) {
|
private fun getRoomType(
|
||||||
|
dmMember: RoomMember?,
|
||||||
|
currentMember: RoomMember?,
|
||||||
|
): State<RoomDetailsType> = remember(dmMember, currentMember) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
if (dmMember != null) {
|
if (dmMember != null && currentMember != null) {
|
||||||
RoomDetailsType.Dm(dmMember)
|
RoomDetailsType.Dm(
|
||||||
|
me = currentMember,
|
||||||
|
roomMember = dmMember,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
RoomDetailsType.Room
|
RoomDetailsType.Room
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,10 @@ data class RoomDetailsState(
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed interface RoomDetailsType {
|
sealed interface RoomDetailsType {
|
||||||
data object Room : RoomDetailsType
|
data object Room : RoomDetailsType
|
||||||
data class Dm(val roomMember: RoomMember) : RoomDetailsType
|
data class Dm(
|
||||||
|
val me: RoomMember,
|
||||||
|
val roomMember: RoomMember,
|
||||||
|
) : RoomDetailsType
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.impl
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||||
import io.element.android.features.leaveroom.api.aLeaveRoomState
|
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.UserProfileState
|
||||||
import io.element.android.features.userprofile.shared.aUserProfileState
|
import io.element.android.features.userprofile.shared.aUserProfileState
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
|
|
@ -141,6 +142,9 @@ fun aDmRoomDetailsState(
|
||||||
roomName: String = "Daniel",
|
roomName: String = "Daniel",
|
||||||
) = aRoomDetailsState(
|
) = aRoomDetailsState(
|
||||||
roomName = roomName,
|
roomName = roomName,
|
||||||
roomType = RoomDetailsType.Dm(aDmRoomMember(isIgnored = isDmMemberIgnored)),
|
roomType = RoomDetailsType.Dm(
|
||||||
|
aRoomMember(),
|
||||||
|
aDmRoomMember(isIgnored = isDmMemberIgnored),
|
||||||
|
),
|
||||||
roomMemberDetailsState = aUserProfileState()
|
roomMemberDetailsState = aUserProfileState()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
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.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomView
|
import io.element.android.features.leaveroom.api.LeaveRoomView
|
||||||
import io.element.android.features.roomdetails.impl.components.RoomBadge
|
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.BlockUserDialogs
|
||||||
import io.element.android.features.userprofile.shared.blockuser.BlockUserSection
|
import io.element.android.features.userprofile.shared.blockuser.BlockUserSection
|
||||||
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
|
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.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
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.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.BackButton
|
||||||
import io.element.android.libraries.designsystem.components.button.MainActionButton
|
import io.element.android.libraries.designsystem.components.button.MainActionButton
|
||||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
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.designsystem.utils.CommonDrawables
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
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.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.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.room.getBestName
|
import io.element.android.libraries.matrix.api.room.getBestName
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
@ -137,13 +139,12 @@ fun RoomDetailsView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is RoomDetailsType.Dm -> {
|
is RoomDetailsType.Dm -> {
|
||||||
val member = state.roomType.roomMember
|
DmHeaderSection(
|
||||||
UserProfileHeaderSection(
|
me = state.roomType.me,
|
||||||
avatarUrl = state.roomAvatarUrl ?: member.avatarUrl,
|
otherMember = state.roomType.roomMember,
|
||||||
userId = member.userId,
|
roomName = state.roomName,
|
||||||
userName = state.roomName,
|
openAvatarPreview = { name, avatarUrl ->
|
||||||
openAvatarPreview = { avatarUrl ->
|
openAvatarPreview(name, avatarUrl)
|
||||||
openAvatarPreview(member.getBestName(), 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
|
@Composable
|
||||||
private fun BadgeList(
|
private fun BadgeList(
|
||||||
isEncrypted: Boolean,
|
isEncrypted: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -169,8 +169,12 @@ class RoomDetailsPresenterTest {
|
||||||
val presenter = createRoomDetailsPresenter(room)
|
val presenter = createRoomDetailsPresenter(room)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherRoomMember))
|
assertThat(initialState.roomType).isEqualTo(
|
||||||
|
RoomDetailsType.Dm(
|
||||||
|
me = myRoomMember,
|
||||||
|
roomMember = otherRoomMember,
|
||||||
|
)
|
||||||
|
)
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
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.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.testtags.TestTags
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
@ -177,7 +178,11 @@ class RoomDetailsViewTest {
|
||||||
fun `click on avatar test on DM`() {
|
fun `click on avatar test on DM`() {
|
||||||
val eventsRecorder = EventsRecorder<RoomDetailsEvent>(expectEvents = false)
|
val eventsRecorder = EventsRecorder<RoomDetailsEvent>(expectEvents = false)
|
||||||
val state = aRoomDetailsState(
|
val state = aRoomDetailsState(
|
||||||
roomType = RoomDetailsType.Dm(aDmRoomMember(avatarUrl = "an_avatar_url")),
|
roomType = RoomDetailsType.Dm(
|
||||||
|
aRoomMember(),
|
||||||
|
aDmRoomMember(avatarUrl = "an_avatar_url"),
|
||||||
|
),
|
||||||
|
roomName = "Daniel",
|
||||||
eventSink = eventsRecorder,
|
eventSink = eventsRecorder,
|
||||||
)
|
)
|
||||||
val callback = EnsureCalledOnceWithTwoParams("Daniel", "an_avatar_url")
|
val callback = EnsureCalledOnceWithTwoParams("Daniel", "an_avatar_url")
|
||||||
|
|
|
||||||
|
|
@ -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.ElementThemedPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||||
import io.element.android.libraries.designsystem.text.toPx
|
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
|
* 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) {
|
.clickable(enabled = otherUserAvatarData.url != null) {
|
||||||
otherUserAvatarData.url?.let { openOtherAvatarPreview(it) }
|
otherUserAvatarData.url?.let { openOtherAvatarPreview(it) }
|
||||||
}
|
}
|
||||||
|
.testTag(TestTags.memberDetailAvatar)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,3 +63,14 @@ fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): St
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MatrixRoom.getCurrentRoomMember(roomMembersState: MatrixRoomMembersState): State<RoomMember?> {
|
||||||
|
val roomMembers = roomMembersState.roomMembers()
|
||||||
|
return remember(roomMembersState) {
|
||||||
|
derivedStateOf {
|
||||||
|
roomMembers
|
||||||
|
?.find { it.userId == sessionId }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue