From 6e20c4455fdb96cccda31771f04550a4ba2a1955 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 9 Nov 2022 17:23:35 +0100 Subject: [PATCH] Timeline: start handling multiple contents --- .../MessageTimelineItemStateMapper.kt | 34 +++++++--- .../x/features/messages/MessagesScreen.kt | 62 ++++++++++++++----- .../MessagesTimelineItemEncryptedView.kt | 20 ++++++ .../MessagesTimelineItemInformativeView.kt | 41 ++++++++++++ .../MessagesTimelineItemRedactedView.kt | 20 ++++++ .../MessagesTimelineItemTextView.kt | 17 +++++ .../MessagesTimelineItemUnknownView.kt | 20 ++++++ .../model/MessagesTimelineItemState.kt | 5 +- .../content/MessagesTimelineItemContent.kt | 3 + .../MessagesTimelineItemEmoteContent.kt | 8 +++ .../MessagesTimelineItemEncryptedContent.kt | 7 +++ .../MessagesTimelineItemNoticeContent.kt | 8 +++ .../MessagesTimelineItemRedactedContent.kt | 3 + .../MessagesTimelineItemTextBasedContent.kt | 8 +++ .../MessagesTimelineItemTextContent.kt | 8 +++ .../MessagesTimelineItemUnknownContent.kt | 3 + 16 files changed, 240 insertions(+), 27 deletions(-) create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemEncryptedView.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemInformativeView.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemRedactedView.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemTextView.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemUnknownView.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt create mode 100644 features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt index 3a4185b0fa..581a9b85e3 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt @@ -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 } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt index 5872f1cc00..6f72ced13a 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt @@ -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 diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemEncryptedView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemEncryptedView.kt new file mode 100644 index 0000000000..d2b280657e --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemEncryptedView.kt @@ -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 + ) +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemInformativeView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemInformativeView.kt new file mode 100644 index 0000000000..9da4a6de98 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemInformativeView.kt @@ -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 + ) + } +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemRedactedView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemRedactedView.kt new file mode 100644 index 0000000000..bc1a4d2d10 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemRedactedView.kt @@ -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 + ) +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemTextView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemTextView.kt new file mode 100644 index 0000000000..00b4bc7ff7 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemTextView.kt @@ -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) + } +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemUnknownView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemUnknownView.kt new file mode 100644 index 0000000000..2c9344e02d --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemUnknownView.kt @@ -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 + ) +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt index f50f2e41df..0b57e2a23e 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt @@ -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 diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt new file mode 100644 index 0000000000..c977713876 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt @@ -0,0 +1,3 @@ +package io.element.android.x.features.messages.model.content + +sealed interface MessagesTimelineItemContent \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt new file mode 100644 index 0000000000..b2f44c50e8 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt @@ -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 \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt new file mode 100644 index 0000000000..80324626a2 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt @@ -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 \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt new file mode 100644 index 0000000000..614f6a2616 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt @@ -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 \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt new file mode 100644 index 0000000000..5eb7cad4f9 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt @@ -0,0 +1,3 @@ +package io.element.android.x.features.messages.model.content + +object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt new file mode 100644 index 0000000000..201ccf5d31 --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt @@ -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? +} \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt new file mode 100644 index 0000000000..0ddf0a919b --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt @@ -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 \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt new file mode 100644 index 0000000000..36dd7484ca --- /dev/null +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt @@ -0,0 +1,3 @@ +package io.element.android.x.features.messages.model.content + +object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent \ No newline at end of file