Rework Message bubbles, to avoid using zIndex and offset.
This commit is contained in:
parent
89e3302f12
commit
965e622032
2 changed files with 56 additions and 52 deletions
|
|
@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
|
@ -49,7 +48,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.zIndex
|
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
|
@ -98,64 +96,71 @@ fun TimelineItemEventRow(
|
||||||
inReplyToClick(inReplyToEventId)
|
inReplyToClick(inReplyToEventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (parentAlignment, contentAlignment) = if (event.isMine) {
|
// To avoid using negative offset, we display in this Box a column with:
|
||||||
Pair(Alignment.CenterEnd, Alignment.End)
|
// - Spacer to give room to the Sender information if they must be displayed;
|
||||||
} else {
|
// - The message bubble;
|
||||||
Pair(Alignment.CenterStart, Alignment.Start)
|
// - Spacer for the reactions if there are some.
|
||||||
}
|
// Then the Sender information and the reactions are displayed on top of it.
|
||||||
|
// This fixes some clickable issue and some unexpected margin on top and bottom of each message row
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
contentAlignment = parentAlignment
|
contentAlignment = if (event.isMine) Alignment.CenterEnd else Alignment.CenterStart
|
||||||
) {
|
) {
|
||||||
Row {
|
Column {
|
||||||
Column(horizontalAlignment = contentAlignment) {
|
if (event.showSenderInformation) {
|
||||||
if (event.showSenderInformation) {
|
Spacer(modifier = Modifier.height(event.senderAvatar.size.dp - 8.dp))
|
||||||
MessageSenderInformation(
|
}
|
||||||
event.safeSenderName,
|
val bubbleState = BubbleState(
|
||||||
event.senderAvatar,
|
groupPosition = event.groupPosition,
|
||||||
Modifier
|
isMine = event.isMine,
|
||||||
.zIndex(1f)
|
isHighlighted = isHighlighted,
|
||||||
.offset(y = 8.dp)
|
)
|
||||||
.clickable(onClick = ::onUserDataClicked)
|
MessageEventBubble(
|
||||||
)
|
state = bubbleState,
|
||||||
}
|
interactionSource = interactionSource,
|
||||||
val bubbleState = BubbleState(
|
onClick = onClick,
|
||||||
groupPosition = event.groupPosition,
|
onLongClick = onLongClick,
|
||||||
isMine = event.isMine,
|
modifier = Modifier
|
||||||
isHighlighted = isHighlighted,
|
.widthIn(max = 320.dp)
|
||||||
)
|
) {
|
||||||
MessageEventBubble(
|
MessageEventBubbleContent(
|
||||||
state = bubbleState,
|
event = event,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
onClick = onClick,
|
onMessageClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onMessageLongClick = onLongClick,
|
||||||
modifier = Modifier
|
inReplyToClick = ::inReplyToClicked,
|
||||||
.zIndex(-1f)
|
onTimestampClicked = {
|
||||||
.widthIn(max = 320.dp)
|
onTimestampClicked(event)
|
||||||
) {
|
}
|
||||||
MessageEventBubbleContent(
|
|
||||||
event = event,
|
|
||||||
interactionSource = interactionSource,
|
|
||||||
onMessageClick = onClick,
|
|
||||||
onMessageLongClick = onLongClick,
|
|
||||||
inReplyToClick = ::inReplyToClicked,
|
|
||||||
onTimestampClicked = {
|
|
||||||
onTimestampClicked(event)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
TimelineItemReactionsView(
|
|
||||||
reactionsState = event.reactionsState,
|
|
||||||
modifier = Modifier
|
|
||||||
.zIndex(1f)
|
|
||||||
.offset(x = if (event.isMine) 0.dp else 20.dp, y = -(4.dp))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (event.reactionsState.reactions.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(28.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Align to the top of the box
|
||||||
|
if (event.showSenderInformation) {
|
||||||
|
MessageSenderInformation(
|
||||||
|
event.safeSenderName,
|
||||||
|
event.senderAvatar,
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.TopStart)
|
||||||
|
.clickable(onClick = ::onUserDataClicked)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Align to the bottom of the box
|
||||||
|
if (event.reactionsState.reactions.isNotEmpty()) {
|
||||||
|
TimelineItemReactionsView(
|
||||||
|
reactionsState = event.reactionsState,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(if (event.isMine) Alignment.BottomEnd else Alignment.BottomStart)
|
||||||
|
.padding(start = if (event.isMine) 0.dp else 20.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This is assuming that we are in a ColumnScope, but this is OK, for both Preview and real usage.
|
||||||
if (event.groupPosition.isNew()) {
|
if (event.groupPosition.isNew()) {
|
||||||
Spacer(modifier = modifier.height(8.dp))
|
Spacer(modifier = modifier.height(8.dp))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -173,7 +178,7 @@ private fun MessageSenderInformation(
|
||||||
val avatarStrokeColor = MaterialTheme.colorScheme.background
|
val avatarStrokeColor = MaterialTheme.colorScheme.background
|
||||||
val avatarSize = senderAvatar.size.dp
|
val avatarSize = senderAvatar.size.dp
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.offset(y = avatarStrokeSize)
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
// Background of Avatar, to erase the corner of the message content
|
// Background of Avatar, to erase the corner of the message content
|
||||||
Canvas(
|
Canvas(
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ fun TimelineItemReactionsView(
|
||||||
reactionsState: TimelineItemReactions,
|
reactionsState: TimelineItemReactions,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (reactionsState.reactions.isEmpty()) return
|
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
mainAxisSpacing = 2.dp,
|
mainAxisSpacing = 2.dp,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue