Try to improve timestamp rendering for media
This commit is contained in:
parent
462f8c138a
commit
fc464a35f9
2 changed files with 101 additions and 59 deletions
|
|
@ -17,6 +17,7 @@
|
|||
package io.element.android.features.messages.impl.timeline
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -33,11 +34,13 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
import androidx.compose.material.icons.filled.Error
|
||||
|
|
@ -51,6 +54,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.LastBaseline
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -70,8 +74,11 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
|||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel
|
||||
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingModel
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
|
|
@ -234,6 +241,7 @@ fun TimelineItemEventRow(
|
|||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
val (parentAlignment, contentAlignment) = if (event.isMine) {
|
||||
Pair(Alignment.CenterEnd, Alignment.End)
|
||||
} else {
|
||||
|
|
@ -271,25 +279,7 @@ fun TimelineItemEventRow(
|
|||
.zIndex(-1f)
|
||||
.widthIn(max = 320.dp)
|
||||
) {
|
||||
Column {
|
||||
TimelineItemEventContentView(
|
||||
content = event.content,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
)
|
||||
TimestampView(
|
||||
formattedTime = event.sentTime,
|
||||
hasMessageSendingFailed = event.sendState is EventSendState.SendingFailed,
|
||||
isMessageEdited = (event.content as? TimelineItemTextBasedContent)?.isEdited.orFalse(),
|
||||
onClick = {
|
||||
// TODO trigger either resending the message or opening the message edition history. This will be implemented later
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.align(Alignment.End),
|
||||
)
|
||||
}
|
||||
MessageEventLayout(event = event, onMessageClick = onClick, onMessageLongClick = onLongClick)
|
||||
}
|
||||
TimelineItemReactionsView(
|
||||
reactionsState = event.reactionsState,
|
||||
|
|
@ -310,6 +300,92 @@ fun TimelineItemEventRow(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageEventLayout(
|
||||
event: TimelineItem.Event,
|
||||
onMessageClick: () -> Unit,
|
||||
onMessageLongClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val isTextBasedMessage = event.content is TimelineItemTextBasedContent
|
||||
val isMediaMessage = event.content is TimelineItemImageContent
|
||||
|| event.content is TimelineItemVideoContent
|
||||
|| event.content is TimelineItemFileContent
|
||||
val isStateMessage = event.content is TimelineItemStateContent
|
||||
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
@Composable
|
||||
fun ContentView(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
TimelineItemEventContentView(
|
||||
content = event.content,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onMessageClick,
|
||||
onLongClick = onMessageLongClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TimestampView(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val formattedTime = event.sentTime
|
||||
val hasMessageSendingFailed = event.sendState is EventSendState.SendingFailed
|
||||
val isMessageEdited = (event.content as? TimelineItemTextBasedContent)?.isEdited.orFalse()
|
||||
val tint = if (hasMessageSendingFailed) ElementTheme.colors.textActionCritical else null
|
||||
Row(modifier = modifier.clickable(onClick = onMessageClick)) {
|
||||
if (isMessageEdited) {
|
||||
Text(
|
||||
stringResource(StringR.string.common_edited_suffix),
|
||||
style = ElementTextStyles.Regular.caption2,
|
||||
color = tint ?: MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
Text(
|
||||
formattedTime,
|
||||
style = ElementTextStyles.Regular.caption2,
|
||||
color = tint ?: MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
if (hasMessageSendingFailed && tint != null) {
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Icon(imageVector = Icons.Default.Error, contentDescription = "Error sending message", tint = tint, modifier = Modifier.size(15.dp, 18.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
isMediaMessage -> {
|
||||
Box(modifier.wrapContentSize()) {
|
||||
ContentView()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentSize()
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp)
|
||||
.background(Color(0xFFF0F2F5), RoundedCornerShape(10.0.dp)) // TODO: add the color with its dark variant
|
||||
.align(Alignment.BottomEnd)
|
||||
) {
|
||||
TimestampView(Modifier.padding(horizontal = 8.dp, vertical = 4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
isTextBasedMessage -> {
|
||||
Column {
|
||||
ContentView(modifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 6.dp))
|
||||
TimestampView(modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp))
|
||||
}
|
||||
}
|
||||
isStateMessage -> {
|
||||
ContentView(modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TimelineItemStateEventRow(
|
||||
event: TimelineItem.Event,
|
||||
|
|
@ -344,36 +420,6 @@ fun TimelineItemStateEventRow(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TimestampView(
|
||||
formattedTime: String,
|
||||
isMessageEdited: Boolean,
|
||||
hasMessageSendingFailed: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val tint = if (hasMessageSendingFailed) ElementTheme.colors.textActionCritical else null
|
||||
Row(modifier = modifier.clickable(onClick = onClick)) {
|
||||
if (isMessageEdited) {
|
||||
Text(
|
||||
stringResource(StringR.string.common_edited_suffix),
|
||||
style = ElementTextStyles.Regular.caption2,
|
||||
color = tint ?: MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
Text(
|
||||
formattedTime,
|
||||
style = ElementTextStyles.Regular.caption1,
|
||||
color = tint ?: MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
if (hasMessageSendingFailed && tint != null) {
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Icon(imageVector = Icons.Default.Error, contentDescription = "Error sending message", tint = tint, modifier = Modifier.size(15.dp, 18.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MessageSenderInformation(
|
||||
sender: String,
|
||||
|
|
|
|||
|
|
@ -31,10 +31,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
|||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||
|
||||
private fun Modifier.defaultContentPadding(): Modifier = padding(
|
||||
horizontal = 12.dp, vertical = 6.dp
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun TimelineItemEventContentView(
|
||||
content: TimelineItemEventContent,
|
||||
|
|
@ -46,22 +42,22 @@ fun TimelineItemEventContentView(
|
|||
when (content) {
|
||||
is TimelineItemEncryptedContent -> TimelineItemEncryptedView(
|
||||
content = content,
|
||||
modifier = modifier.defaultContentPadding()
|
||||
modifier = modifier
|
||||
)
|
||||
is TimelineItemRedactedContent -> TimelineItemRedactedView(
|
||||
content = content,
|
||||
modifier = modifier.defaultContentPadding()
|
||||
modifier = modifier
|
||||
)
|
||||
is TimelineItemTextBasedContent -> TimelineItemTextView(
|
||||
content = content,
|
||||
interactionSource = interactionSource,
|
||||
modifier = modifier.defaultContentPadding(),
|
||||
modifier = modifier,
|
||||
onTextClicked = onClick,
|
||||
onTextLongClicked = onLongClick
|
||||
)
|
||||
is TimelineItemUnknownContent -> TimelineItemUnknownView(
|
||||
content = content,
|
||||
modifier = modifier.defaultContentPadding()
|
||||
modifier = modifier
|
||||
)
|
||||
is TimelineItemImageContent -> TimelineItemImageView(
|
||||
content = content,
|
||||
|
|
@ -73,11 +69,11 @@ fun TimelineItemEventContentView(
|
|||
)
|
||||
is TimelineItemFileContent -> TimelineItemFileView(
|
||||
content = content,
|
||||
modifier = modifier.defaultContentPadding()
|
||||
modifier = modifier
|
||||
)
|
||||
is TimelineItemStateContent -> TimelineItemStateView(
|
||||
content = content,
|
||||
modifier = modifier.defaultContentPadding()
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue