From 0edf53de5f26288b4c0c3d6bd7302e8640ae59fe Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 10:43:00 +0200 Subject: [PATCH 1/7] Convert `object` to `data object` --- .../matrix/api/timeline/item/event/EventContent.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index a3edf80bee..85b7d4b66a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -63,7 +63,7 @@ sealed interface InReplyTo { data object Error : InReplyTo } -object RedactedContent : EventContent +data object RedactedContent : EventContent data class StickerContent( val body: String, @@ -124,11 +124,11 @@ data class FailedToParseStateContent( val error: String ) : EventContent -object UnknownContent : EventContent +data object UnknownContent : EventContent sealed interface MessageType -object UnknownMessageType : MessageType +data object UnknownMessageType : MessageType enum class MessageFormat { HTML, UNKNOWN From 303b174009dc6905ef0b22fc9f562087e37d6b31 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 10:47:00 +0200 Subject: [PATCH 2/7] Emotes starts with `*` --- .../eventformatter/impl/DefaultRoomLastMessageFormatter.kt | 2 +- .../impl/DefaultRoomLastMessageFormatterTests.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 7e8a93ffe7..70ec625fe9 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -111,7 +111,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( val internalMessage = when (messageType) { // Doesn't need a prefix is EmoteMessageType -> { - return "- $senderDisplayName ${messageType.body}" + return "* $senderDisplayName ${messageType.body}" } is TextMessageType -> { messageType.body diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt index 679f9ce891..b66b044a66 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt @@ -201,7 +201,7 @@ class DefaultRoomLastMessageFormatterTests { is ImageMessageType -> "Image" is FileMessageType -> "File" is LocationMessageType -> "Shared location" - is EmoteMessageType -> "- $senderName ${type.body}" + is EmoteMessageType -> "* $senderName ${type.body}" is TextMessageType, is NoticeMessageType -> body UnknownMessageType -> "Unsupported event" } @@ -217,7 +217,7 @@ class DefaultRoomLastMessageFormatterTests { is ImageMessageType -> "$senderName: Image" is FileMessageType -> "$senderName: File" is LocationMessageType -> "$senderName: Shared location" - is EmoteMessageType -> "- $senderName ${type.body}" + is EmoteMessageType -> "* $senderName ${type.body}" is TextMessageType, is NoticeMessageType -> "$senderName: $body" UnknownMessageType -> "$senderName: Unsupported event" } From 46d5417dd3424155f30bcde96b4768d2aa01bbb8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 11:15:26 +0200 Subject: [PATCH 3/7] avoid `else` in when statement. --- .../factories/event/TimelineItemContentMessageFactory.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 3d0dc4d80c..7c60ba29c4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -39,6 +39,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import javax.inject.Inject @@ -48,7 +49,7 @@ class TimelineItemContentMessageFactory @Inject constructor( ) { fun create(content: MessageContent): TimelineItemEventContent { - return when (val messageType = content.type) { + return when (val messageType = content.type ?: UnknownMessageType) { is EmoteMessageType -> TimelineItemEmoteContent( body = messageType.body, htmlDocument = messageType.formatted?.toHtmlDocument(), @@ -130,7 +131,7 @@ class TimelineItemContentMessageFactory @Inject constructor( htmlDocument = messageType.formatted?.toHtmlDocument(), isEdited = content.isEdited, ) - else -> TimelineItemUnknownContent + UnknownMessageType -> TimelineItemUnknownContent } } From 8f660facbfa8cc15887f692c67867b89d1afa476 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 11:39:05 +0200 Subject: [PATCH 4/7] Render emote in the timeline: prefix with sender display name --- .../factories/event/TimelineItemContentFactory.kt | 6 +++++- .../factories/event/TimelineItemContentMessageFactory.kt | 6 +++--- .../messages/impl/timeline/util/toHtmlDocument.kt | 8 ++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index 6a74fd11a5..55e889f496 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent @@ -49,7 +50,10 @@ class TimelineItemContentFactory @Inject constructor( return when (val itemContent = eventTimelineItem.content) { is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent) is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent) - is MessageContent -> messageFactory.create(itemContent) + is MessageContent -> { + val senderDisplayName = (eventTimelineItem.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: eventTimelineItem.sender.value + messageFactory.create(itemContent, senderDisplayName) + } is ProfileChangeContent -> profileChangeFactory.create(eventTimelineItem) is RedactedContent -> redactedMessageFactory.create(itemContent) is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 7c60ba29c4..98853f01cd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -48,11 +48,11 @@ class TimelineItemContentMessageFactory @Inject constructor( private val fileExtensionExtractor: FileExtensionExtractor, ) { - fun create(content: MessageContent): TimelineItemEventContent { + fun create(content: MessageContent, senderDisplayName: String): TimelineItemEventContent { return when (val messageType = content.type ?: UnknownMessageType) { is EmoteMessageType -> TimelineItemEmoteContent( - body = messageType.body, - htmlDocument = messageType.formatted?.toHtmlDocument(), + body = "* $senderDisplayName ${messageType.body}", + htmlDocument = messageType.formatted?.toHtmlDocument(senderDisplayName), isEdited = content.isEdited, ) is ImageMessageType -> { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt index 8031e77a4d..02a7af4a5a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt @@ -21,8 +21,12 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat import org.jsoup.Jsoup import org.jsoup.nodes.Document -fun FormattedBody.toHtmlDocument(): Document? { +fun FormattedBody.toHtmlDocument(senderDisplayNamePrefix: String? = null): Document? { return takeIf { it.format == MessageFormat.HTML }?.body?.let { formattedBody -> - Jsoup.parse(formattedBody) + if (senderDisplayNamePrefix != null) { + Jsoup.parse("* $senderDisplayNamePrefix $formattedBody") + } else { + Jsoup.parse(formattedBody) + } } } From 73f8aa0887ce2afa4709053023cfedff1352ebdb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 11:43:50 +0200 Subject: [PATCH 5/7] Changelog --- changelog.d/1497.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1497.feature diff --git a/changelog.d/1497.feature b/changelog.d/1497.feature new file mode 100644 index 0000000000..04c256dc18 --- /dev/null +++ b/changelog.d/1497.feature @@ -0,0 +1 @@ +Improve rendering of m.emote. From 35b7b661f2f3f8415bc2b049b321c9972ba7fca9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 15:28:38 +0200 Subject: [PATCH 6/7] Code review: improve API. --- .../factories/event/TimelineItemContentMessageFactory.kt | 2 +- .../features/messages/impl/timeline/util/toHtmlDocument.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 98853f01cd..8e6561543c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -52,7 +52,7 @@ class TimelineItemContentMessageFactory @Inject constructor( return when (val messageType = content.type ?: UnknownMessageType) { is EmoteMessageType -> TimelineItemEmoteContent( body = "* $senderDisplayName ${messageType.body}", - htmlDocument = messageType.formatted?.toHtmlDocument(senderDisplayName), + htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* senderDisplayName"), isEdited = content.isEdited, ) is ImageMessageType -> { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt index 02a7af4a5a..a38b631eb2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/util/toHtmlDocument.kt @@ -21,10 +21,10 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat import org.jsoup.Jsoup import org.jsoup.nodes.Document -fun FormattedBody.toHtmlDocument(senderDisplayNamePrefix: String? = null): Document? { +fun FormattedBody.toHtmlDocument(prefix: String? = null): Document? { return takeIf { it.format == MessageFormat.HTML }?.body?.let { formattedBody -> - if (senderDisplayNamePrefix != null) { - Jsoup.parse("* $senderDisplayNamePrefix $formattedBody") + if (prefix != null) { + Jsoup.parse("$prefix $formattedBody") } else { Jsoup.parse(formattedBody) } From 2a7a66fb1865dfe0a7e49d3dbca864e7d2b88cbf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 5 Oct 2023 15:31:10 +0200 Subject: [PATCH 7/7] Emote: format notification --- .../push/impl/notifications/NotifiableEventResolver.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index a04707be2b..113a323611 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -87,7 +87,7 @@ class NotifiableEventResolver @Inject constructor( noisy = isNoisy, timestamp = this.timestamp, senderName = senderDisplayName, - body = descriptionFromMessageContent(content), + body = descriptionFromMessageContent(content, senderDisplayName ?: content.senderId.value), imageUriString = this.contentUrl, roomName = roomDisplayName, roomIsDirect = isDirect, @@ -133,7 +133,7 @@ class NotifiableEventResolver @Inject constructor( NotificationContent.MessageLike.KeyVerificationStart -> null.also { Timber.tag(loggerTag.value).d("Ignoring notification for verification ${content.javaClass.simpleName}") } - is NotificationContent.MessageLike.Poll -> { + is NotificationContent.MessageLike.Poll -> { buildNotifiableMessageEvent( sessionId = userId, senderId = content.senderId, @@ -205,10 +205,11 @@ class NotifiableEventResolver @Inject constructor( private fun descriptionFromMessageContent( content: NotificationContent.MessageLike.RoomMessage, + senderDisplayName: String, ): String { return when (val messageType = content.messageType) { is AudioMessageType -> messageType.body - is EmoteMessageType -> messageType.body + is EmoteMessageType -> "* $senderDisplayName ${messageType.body}" is FileMessageType -> messageType.body is ImageMessageType -> messageType.body is NoticeMessageType -> messageType.body