Timeline: start handling multiple contents

This commit is contained in:
ganfra
2022-11-09 17:23:35 +01:00
parent dc39d061b8
commit 6e20c4455f
16 changed files with 240 additions and 27 deletions

View File

@@ -4,6 +4,7 @@ import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
import io.element.android.x.features.messages.model.MessagesTimelineItemState
import io.element.android.x.features.messages.model.content.*
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.room.MatrixRoom
@@ -61,15 +62,30 @@ class MessageTimelineItemStateMapper(
)
}
private fun MatrixTimelineItem.Event.computeContent(): String? {
val messageType =
event.content().asMessage()?.msgtype()
return when (messageType) {
is MessageType.Emote -> messageType.content.body
is MessageType.Image -> messageType.content.body
is MessageType.Notice -> messageType.content.body
is MessageType.Text -> messageType.content.body
null -> null
private fun MatrixTimelineItem.Event.computeContent(): MessagesTimelineItemContent {
val content = event.content()
content.asUnableToDecrypt()?.let { encryptedMessage ->
return MessagesTimelineItemEncryptedContent(encryptedMessage)
}
if (content.isRedactedMessage()) {
return MessagesTimelineItemRedactedContent
}
val contentAsMessage = content.asMessage()
return when (val messageType = contentAsMessage?.msgtype()) {
is MessageType.Emote -> MessagesTimelineItemEmoteContent(
body = messageType.content.body,
formattedBody = messageType.content.formatted
)
is MessageType.Image -> MessagesTimelineItemUnknownContent
is MessageType.Notice -> MessagesTimelineItemNoticeContent(
body = messageType.content.body,
formattedBody = messageType.content.formatted
)
is MessageType.Text -> MessagesTimelineItemTextContent(
body = messageType.content.body,
formattedBody = messageType.content.formatted
)
null -> MessagesTimelineItemUnknownContent
}
}

View File

@@ -36,9 +36,17 @@ import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.core.data.LogCompositions
import io.element.android.x.core.data.StableCharSequence
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.messages.components.MessagesTimelineItemEncryptedView
import io.element.android.x.features.messages.components.MessagesTimelineItemRedactedView
import io.element.android.x.features.messages.components.MessagesTimelineItemTextView
import io.element.android.x.features.messages.components.MessagesTimelineItemUnknownView
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
import io.element.android.x.features.messages.model.MessagesTimelineItemState
import io.element.android.x.features.messages.model.MessagesViewState
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel
import io.element.android.x.features.messages.textcomposer.MessageComposerViewState
import io.element.android.x.textcomposer.TextComposer
@@ -231,7 +239,32 @@ fun MessageEventRow(
Modifier.zIndex(1f)
)
}
MessageEventBubble(messageEvent, Modifier.zIndex(-1f))
MessageEventBubble(
groupPosition = messageEvent.groupPosition,
isMine = messageEvent.isMine,
modifier = Modifier
.zIndex(-1f)
) {
val contentModifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
when (messageEvent.content) {
is MessagesTimelineItemEncryptedContent -> MessagesTimelineItemEncryptedView(
content = messageEvent.content,
modifier = contentModifier
)
is MessagesTimelineItemRedactedContent -> MessagesTimelineItemRedactedView(
content = messageEvent.content,
modifier = contentModifier
)
is MessagesTimelineItemTextBasedContent -> MessagesTimelineItemTextView(
content = messageEvent.content,
modifier = contentModifier
)
is MessagesTimelineItemUnknownContent -> MessagesTimelineItemUnknownView(
content = messageEvent.content,
modifier = contentModifier
)
}
}
}
if (messageEvent.isMine) {
Spacer(modifier = Modifier.width(16.dp))
@@ -267,10 +300,12 @@ private fun MessageSenderInformation(
@Composable
fun MessageEventBubble(
messageEvent: MessagesTimelineItemState.MessageEvent,
groupPosition: MessagesItemGroupPosition,
isMine: Boolean,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
fun MessagesTimelineItemState.MessageEvent.bubbleShape(): Shape {
fun bubbleShape(): Shape {
return when (groupPosition) {
MessagesItemGroupPosition.First -> if (isMine) {
RoundedCornerShape(BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp, BUBBLE_RADIUS)
@@ -297,15 +332,15 @@ fun MessageEventBubble(
}
}
fun Modifier.offsetForItem(messageEvent: MessagesTimelineItemState.MessageEvent): Modifier {
return if (messageEvent.isMine) {
fun Modifier.offsetForItem(): Modifier {
return if (isMine) {
offset(y = -(12.dp))
} else {
offset(x = 20.dp, y = -(12.dp))
}
}
val (backgroundBubbleColor, border) = if (messageEvent.isMine) {
val (backgroundBubbleColor, border) = if (isMine) {
Pair(MaterialTheme.colorScheme.surfaceVariant, null)
} else {
Pair(
@@ -313,12 +348,11 @@ fun MessageEventBubble(
BorderStroke(1.dp, MaterialTheme.colorScheme.surfaceVariant)
)
}
val bubbleShape = messageEvent.bubbleShape()
val bubbleShape = bubbleShape()
Surface(
modifier = modifier
.widthIn(min = 80.dp)
.offsetForItem(messageEvent)
.offsetForItem()
.clip(bubbleShape)
.clickable(
onClick = { },
@@ -327,13 +361,9 @@ fun MessageEventBubble(
),
color = backgroundBubbleColor,
shape = bubbleShape,
border = border
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
text = messageEvent.content ?: "",
)
}
border = border,
content = content
)
}
@Composable

View File

@@ -0,0 +1,20 @@
package io.element.android.x.features.messages.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent
@Composable
fun MessagesTimelineItemEncryptedView(
content: MessagesTimelineItemEncryptedContent,
modifier: Modifier = Modifier
) {
MessagesTimelineItemInformativeView(
text = "Decryption error",
iconDescription = "Warning",
icon = Icons.Default.Warning,
modifier = modifier
)
}

View File

@@ -0,0 +1,41 @@
package io.element.android.x.features.messages.components
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun MessagesTimelineItemInformativeView(
text: String,
iconDescription: String,
icon: ImageVector,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
tint = MaterialTheme.colorScheme.secondary,
contentDescription = iconDescription
)
Spacer(modifier = Modifier.width(4.dp))
Text(
fontStyle = FontStyle.Italic,
color = MaterialTheme.colorScheme.secondary,
fontSize = 12.sp,
text = text
)
}
}

View File

@@ -0,0 +1,20 @@
package io.element.android.x.features.messages.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
@Composable
fun MessagesTimelineItemRedactedView(
content: MessagesTimelineItemRedactedContent,
modifier: Modifier = Modifier
) {
MessagesTimelineItemInformativeView(
text = "This message has been deleted",
iconDescription = "Delete",
icon = Icons.Default.Delete,
modifier = modifier
)
}

View File

@@ -0,0 +1,17 @@
package io.element.android.x.features.messages.components
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
@Composable
fun MessagesTimelineItemTextView(
content: MessagesTimelineItemTextBasedContent,
modifier: Modifier = Modifier
) {
Box(modifier) {
Text(text = content.body)
}
}

View File

@@ -0,0 +1,20 @@
package io.element.android.x.features.messages.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
@Composable
fun MessagesTimelineItemUnknownView(
content: MessagesTimelineItemUnknownContent,
modifier: Modifier = Modifier
) {
MessagesTimelineItemInformativeView(
text = "Event not handled by EAX",
iconDescription = "Info",
icon = Icons.Default.Info,
modifier = modifier
)
}

View File

@@ -1,6 +1,7 @@
package io.element.android.x.features.messages.model
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContent
sealed interface MessagesTimelineItemState {
data class Virtual(
@@ -12,10 +13,10 @@ sealed interface MessagesTimelineItemState {
val senderId: String,
val senderDisplayName: String?,
val senderAvatar: AvatarData,
val content: String? = null,
val content: MessagesTimelineItemContent,
val sentTime: String = "",
val isMine: Boolean = false,
val groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.None
val groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.None,
) : MessagesTimelineItemState {
val showSenderInformation = groupPosition.isNew() && !isMine

View File

@@ -0,0 +1,3 @@
package io.element.android.x.features.messages.model.content
sealed interface MessagesTimelineItemContent

View File

@@ -0,0 +1,8 @@
package io.element.android.x.features.messages.model.content
import org.matrix.rustcomponents.sdk.FormattedBody
data class MessagesTimelineItemEmoteContent(
override val body: String,
override val formattedBody: FormattedBody?,
) : MessagesTimelineItemTextBasedContent

View File

@@ -0,0 +1,7 @@
package io.element.android.x.features.messages.model.content
import org.matrix.rustcomponents.sdk.EncryptedMessage
data class MessagesTimelineItemEncryptedContent(
val encryptedMessage: EncryptedMessage
) : MessagesTimelineItemContent

View File

@@ -0,0 +1,8 @@
package io.element.android.x.features.messages.model.content
import org.matrix.rustcomponents.sdk.FormattedBody
data class MessagesTimelineItemNoticeContent(
override val body: String,
override val formattedBody: FormattedBody?,
) : MessagesTimelineItemTextBasedContent

View File

@@ -0,0 +1,3 @@
package io.element.android.x.features.messages.model.content
object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent

View File

@@ -0,0 +1,8 @@
package io.element.android.x.features.messages.model.content
import org.matrix.rustcomponents.sdk.FormattedBody
sealed interface MessagesTimelineItemTextBasedContent : MessagesTimelineItemContent {
val body: String
val formattedBody: FormattedBody?
}

View File

@@ -0,0 +1,8 @@
package io.element.android.x.features.messages.model.content
import org.matrix.rustcomponents.sdk.FormattedBody
data class MessagesTimelineItemTextContent(
override val body: String,
override val formattedBody: FormattedBody?,
) : MessagesTimelineItemTextBasedContent

View File

@@ -0,0 +1,3 @@
package io.element.android.x.features.messages.model.content
object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent