From 2b3de840f127adbd97a70be2dddbc42bd35c8989 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 22 Nov 2021 17:02:12 +0000 Subject: [PATCH 01/11] Force markdown parse on replies and update quote to use markdown parser. --- .../sdk/api/session/room/send/SendService.kt | 10 ++++ .../session/room/send/DefaultSendService.kt | 6 +++ .../room/send/LocalEchoEventFactory.kt | 54 ++++++++++++++++--- .../session/room/send/MarkdownParser.kt | 10 +++- .../detail/composer/TextComposerViewModel.kt | 18 +------ 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index a2b38b6606..9c41f0d344 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -56,6 +56,16 @@ interface SendService { */ fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable + /** + * Method to quote an events content. + * @param quotedEvent The event to which we will quote it's content. + * @param text the text message to send + * @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE + * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present + * @return a [Cancelable] + */ + fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean = false): Cancelable + /** * Method to send a media asynchronously. * @param attachment the media to send diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index 77aadef6bd..47cc1fc29a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -97,6 +97,12 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } + override fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean): Cancelable { + return localEchoEventFactory.createQuotedTextEvent(roomId, quotedEvent, text, autoMarkdown) + .also { createLocalEcho(it) } + .let { sendEvent(it) } + } + override fun sendPoll(question: String, options: List): Cancelable { return localEchoEventFactory.createPollEvent(roomId, question, options) .also { createLocalEcho(it) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 5cb9687518..8c7f96d1fe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -20,6 +20,7 @@ import android.content.Context import android.graphics.Bitmap import android.media.MediaMetadataRetriever import androidx.exifinterface.media.ExifInterface +import okhttp3.internal.format import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event @@ -88,9 +89,9 @@ internal class LocalEchoEventFactory @Inject constructor( return createMessageEvent(roomId, content) } - private fun createTextContent(text: CharSequence, autoMarkdown: Boolean): TextContent { + private fun createTextContent(text: CharSequence, autoMarkdown: Boolean, forceMarkdownParse: Boolean = false): TextContent { if (autoMarkdown) { - return markdownParser.parse(text) + return markdownParser.parse(text, force = forceMarkdownParse) } else { // Try to detect pills textPillsUtils.processSpecialSpansToHtml(text)?.let { @@ -175,13 +176,17 @@ internal class LocalEchoEventFactory @Inject constructor( val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: "" val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) + // As we always supply formatted body for replies we should force the MarkdownParser to produce html. + val newBodyFormatted = createTextContent(newBodyText, newBodyAutoMarkdown, forceMarkdownParse = true).takeFormatted() + // Body of the original message may not have formatted version, so may also have to convert to html. + val bodyFormatted = body.formattedText ?: createTextContent(body.text, newBodyAutoMarkdown, forceMarkdownParse = true).takeFormatted() val replyFormatted = REPLY_PATTERN.format( permalink, userLink, originalEvent.senderInfo.disambiguatedDisplayName, // Remove inner mx_reply tags if any - body.takeFormatted().replace(MX_REPLY_REGEX, ""), - createTextContent(newBodyText, newBodyAutoMarkdown).takeFormatted() + bodyFormatted.replace(MX_REPLY_REGEX, ""), + newBodyFormatted ) // // > <@alice:example.org> This is the original body @@ -361,13 +366,18 @@ internal class LocalEchoEventFactory @Inject constructor( val userLink = permalinkFactory.createPermalink(userId, false) ?: return null val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) + + // As we always supply formatted body for replies we should force the MarkdownParser to produce html. + val replyTextFormatted = createTextContent(replyText, autoMarkdown, forceMarkdownParse = true).takeFormatted() + // Body of the original message may not have formatted version, so may also have to convert to html. + val bodyFormatted = body.formattedText ?: createTextContent(body.text, autoMarkdown, forceMarkdownParse = true).takeFormatted() val replyFormatted = REPLY_PATTERN.format( permalink, userLink, userId, // Remove inner mx_reply tags if any - body.takeFormatted().replace(MX_REPLY_REGEX, ""), - createTextContent(replyText, autoMarkdown).takeFormatted() + bodyFormatted.replace(MX_REPLY_REGEX, ""), + replyTextFormatted ) // // > <@alice:example.org> This is the original body @@ -467,6 +477,38 @@ internal class LocalEchoEventFactory @Inject constructor( localEchoRepository.createLocalEcho(event) } + fun createQuotedTextEvent( + roomId: String, + quotedEvent: TimelineEvent, + text: String, + autoMarkdown: Boolean + ): Event { + val messageContent = quotedEvent.getLastMessageContent() + val textMsg = messageContent?.body + val quoteText = legacyRiotQuoteText(textMsg, text) + return createFormattedTextEvent(roomId, createTextContent(quoteText, autoMarkdown), MessageType.MSGTYPE_TEXT) + } + + private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { + val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() + return buildString { + if (messageParagraphs != null) { + for (i in messageParagraphs.indices) { + if (messageParagraphs[i].isNotBlank()) { + append("> ") + append(messageParagraphs[i]) + } + + if (i != messageParagraphs.lastIndex) { + append("\n\n") + } + } + } + append("\n\n") + append(myText) + } + } + companion object { // //
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt index c99d482300..2df668bf40 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt @@ -34,11 +34,17 @@ internal class MarkdownParser @Inject constructor( private val mdSpecialChars = "[`_\\-*>.\\[\\]#~]".toRegex() - fun parse(text: CharSequence): TextContent { + /** + * Parses some input text and produces html. + * @param text An input CharSequence to be parsed. + * @param force Skips the check for detecting if the input contains markdown and always converts to html. + * @return TextContent containing the plain text and the formatted html if generated. + */ + fun parse(text: CharSequence, force: Boolean = false): TextContent { val source = textPillsUtils.processSpecialSpansToMarkdown(text) ?: text.toString() // If no special char are detected, just return plain text - if (source.contains(mdSpecialChars).not()) { + if (!force && source.contains(mdSpecialChars).not()) { return TextContent(source) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt index 66d49f9819..55a7eba0de 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt @@ -395,23 +395,7 @@ class TextComposerViewModel @AssistedInject constructor( popDraft() } is SendMode.QUOTE -> { - val messageContent = state.sendMode.timelineEvent.getLastMessageContent() - val textMsg = messageContent?.body - - val finalText = legacyRiotQuoteText(textMsg, action.text.toString()) - - // TODO check for pills? - - // TODO Refactor this, just temporary for quotes - val parser = Parser.builder().build() - val document = parser.parse(finalText) - val renderer = HtmlRenderer.builder().build() - val htmlText = renderer.render(document) - if (finalText == htmlText) { - room.sendTextMessage(finalText) - } else { - room.sendFormattedTextMessage(finalText, htmlText) - } + room.sendQuotedTextMessage(state.sendMode.timelineEvent, action.text.toString(), action.autoMarkdown) _viewEvents.post(TextComposerViewEvents.MessageSent) popDraft() } From 0efe4ef16a7507e7e60928bb6e544c78c2fff0b1 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 22 Nov 2021 17:03:47 +0000 Subject: [PATCH 02/11] Create 4540.bugfix --- changelog.d/4540.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/4540.bugfix diff --git a/changelog.d/4540.bugfix b/changelog.d/4540.bugfix new file mode 100644 index 0000000000..9c962f91f7 --- /dev/null +++ b/changelog.d/4540.bugfix @@ -0,0 +1 @@ +Fix message replies/quotes to respect newlines. \ No newline at end of file From c4cf7fa069167716a95d48248c9599a3d4c2f893 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 22 Nov 2021 17:15:15 +0000 Subject: [PATCH 03/11] lint --- .../features/home/room/detail/composer/TextComposerViewModel.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt index 55a7eba0de..a56fe72b24 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt @@ -36,8 +36,6 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import org.commonmark.parser.Parser -import org.commonmark.renderer.html.HtmlRenderer import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType From 96062b7daa3fa520a8e38b4100e134002b43347f Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 14 Dec 2021 17:10:54 +0000 Subject: [PATCH 04/11] Enable on replies and quotes even if preference is disabled to fix newline issues. --- .../room/model/relation/RelationService.kt | 4 +--- .../sdk/api/session/room/send/SendService.kt | 2 +- .../room/relation/DefaultRelationService.kt | 4 ++-- .../internal/session/room/relation/EventEditor.kt | 3 +-- .../session/room/send/DefaultSendService.kt | 4 ++-- .../session/room/send/LocalEchoEventFactory.kt | 15 ++++++--------- .../detail/composer/MessageComposerViewModel.kt | 4 ++-- 7 files changed, 15 insertions(+), 21 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt index 59d84ef40f..0bc3120119 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt @@ -104,11 +104,9 @@ interface RelationService { * by the sdk into pills. * @param eventReplied the event referenced by the reply * @param replyText the reply text - * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present */ fun replyToMessage(eventReplied: TimelineEvent, - replyText: CharSequence, - autoMarkdown: Boolean = false): Cancelable? + replyText: CharSequence): Cancelable? /** * Get the current EventAnnotationsSummary diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 255b4d5809..6496ddc2ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -64,7 +64,7 @@ interface SendService { * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present * @return a [Cancelable] */ - fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean = false): Cancelable + fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String): Cancelable /** * Method to send a media asynchronously. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index 07927b1412..c496633515 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -131,8 +131,8 @@ internal class DefaultRelationService @AssistedInject constructor( return fetchEditHistoryTask.execute(FetchEditHistoryTask.Params(roomId, eventId)) } - override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Cancelable? { - val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText, autoMarkdown) + override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence): Cancelable? { + val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText) ?.also { saveLocalEcho(it) } ?: return null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt index a666d40fc3..076eb06447 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt @@ -67,7 +67,7 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor: val roomId = replyToEdit.roomId if (replyToEdit.root.sendState.hasFailed()) { // We create a new in memory event for the EventSenderProcessor but we keep the eventId of the failed event. - val editedEvent = eventFactory.createReplyTextEvent(roomId, originalTimelineEvent, newBodyText, false)?.copy( + val editedEvent = eventFactory.createReplyTextEvent(roomId, originalTimelineEvent, newBodyText)?.copy( eventId = replyToEdit.eventId ) ?: return NoOpCancellable updateFailedEchoWithEvent(roomId, replyToEdit.eventId, editedEvent) @@ -78,7 +78,6 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor: replyToEdit, originalTimelineEvent, newBodyText, - true, MessageType.MSGTYPE_TEXT, compatibilityBodyText ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index fb2fb3950a..f7f29f9b34 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -97,8 +97,8 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } - override fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean): Cancelable { - return localEchoEventFactory.createQuotedTextEvent(roomId, quotedEvent, text, autoMarkdown) + override fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String): Cancelable { + return localEchoEventFactory.createQuotedTextEvent(roomId, quotedEvent, text) .also { createLocalEcho(it) } .let { sendEvent(it) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 486af8f327..52cdf94dbb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -199,7 +199,6 @@ internal class LocalEchoEventFactory @Inject constructor( eventReplaced: TimelineEvent, originalEvent: TimelineEvent, newBodyText: String, - newBodyAutoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false) @@ -207,9 +206,9 @@ internal class LocalEchoEventFactory @Inject constructor( val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) // As we always supply formatted body for replies we should force the MarkdownParser to produce html. - val newBodyFormatted = createTextContent(newBodyText, newBodyAutoMarkdown, forceMarkdownParse = true).takeFormatted() + val newBodyFormatted = createTextContent(newBodyText, true, forceMarkdownParse = true).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. - val bodyFormatted = body.formattedText ?: createTextContent(body.text, newBodyAutoMarkdown, forceMarkdownParse = true).takeFormatted() + val bodyFormatted = body.formattedText ?: createTextContent(body.text, true, forceMarkdownParse = true).takeFormatted() val replyFormatted = REPLY_PATTERN.format( permalink, userLink, @@ -387,8 +386,7 @@ internal class LocalEchoEventFactory @Inject constructor( fun createReplyTextEvent(roomId: String, eventReplied: TimelineEvent, - replyText: CharSequence, - autoMarkdown: Boolean): Event? { + replyText: CharSequence): Event? { // Fallbacks and event representation // TODO Add error/warning logs when any of this is null val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null @@ -398,9 +396,9 @@ internal class LocalEchoEventFactory @Inject constructor( val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) // As we always supply formatted body for replies we should force the MarkdownParser to produce html. - val replyTextFormatted = createTextContent(replyText, autoMarkdown, forceMarkdownParse = true).takeFormatted() + val replyTextFormatted = createTextContent(replyText, true, forceMarkdownParse = true).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. - val bodyFormatted = body.formattedText ?: createTextContent(body.text, autoMarkdown, forceMarkdownParse = true).takeFormatted() + val bodyFormatted = body.formattedText ?: createTextContent(body.text, true, forceMarkdownParse = true).takeFormatted() val replyFormatted = REPLY_PATTERN.format( permalink, userLink, @@ -512,12 +510,11 @@ internal class LocalEchoEventFactory @Inject constructor( roomId: String, quotedEvent: TimelineEvent, text: String, - autoMarkdown: Boolean ): Event { val messageContent = quotedEvent.getLastMessageContent() val textMsg = messageContent?.body val quoteText = legacyRiotQuoteText(textMsg, text) - return createFormattedTextEvent(roomId, createTextContent(quoteText, autoMarkdown), MessageType.MSGTYPE_TEXT) + return createFormattedTextEvent(roomId, createTextContent(quoteText, autoMarkdown=true, forceMarkdownParse = true), MessageType.MSGTYPE_TEXT) } private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index a63a06928a..8495cc3970 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -406,13 +406,13 @@ class MessageComposerViewModel @AssistedInject constructor( popDraft() } is SendMode.Quote -> { - room.sendQuotedTextMessage(state.sendMode.timelineEvent, action.text.toString(), action.autoMarkdown) + room.sendQuotedTextMessage(state.sendMode.timelineEvent, action.text.toString()) _viewEvents.post(MessageComposerViewEvents.MessageSent) popDraft() } is SendMode.Reply -> { state.sendMode.timelineEvent.let { - room.replyToMessage(it, action.text.toString(), action.autoMarkdown) + room.replyToMessage(it, action.text.toString()) _viewEvents.post(MessageComposerViewEvents.MessageSent) popDraft() } From bef238f8510d493c9c2ad42889df63ee587b51ff Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 14 Dec 2021 20:40:44 +0000 Subject: [PATCH 05/11] Add simple parser for use with just quotes/replies --- .../sdk/internal/session/room/RoomModule.kt | 20 +++++++++++++++++++ .../room/send/LocalEchoEventFactory.kt | 14 ++++++------- .../session/room/send/MarkdownParser.kt | 8 +++++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index dbd0ae6f06..6497c997ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room import dagger.Binds import dagger.Module import dagger.Provides +import org.commonmark.node.BlockQuote import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import org.matrix.android.sdk.api.session.file.FileService @@ -98,6 +99,14 @@ import org.matrix.android.sdk.internal.session.room.version.DefaultRoomVersionUp import org.matrix.android.sdk.internal.session.room.version.RoomVersionUpgradeTask import org.matrix.android.sdk.internal.session.space.DefaultSpaceService import retrofit2.Retrofit +import javax.inject.Qualifier + +/** + * Used to inject the simple commonmark Parser + */ +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +internal annotation class SimpleCommonmarkParser @Module internal abstract class RoomModule { @@ -124,6 +133,17 @@ internal abstract class RoomModule { return Parser.builder().build() } + @Provides + @SimpleCommonmarkParser + @JvmStatic + fun providesSimpleParser(): Parser { + // The simple parser disables all blocks but quotes. + // Inline parsing(bold, italic, etc) is also enabled and is not easy to disable in commonmark currently. + return Parser.builder() + .enabledBlockTypes(setOf(BlockQuote::class.java)) + .build() + } + @Provides @JvmStatic fun providesHtmlRenderer(): HtmlRenderer { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 52cdf94dbb..9e279dfa44 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -91,9 +91,9 @@ internal class LocalEchoEventFactory @Inject constructor( return createMessageEvent(roomId, content) } - private fun createTextContent(text: CharSequence, autoMarkdown: Boolean, forceMarkdownParse: Boolean = false): TextContent { + private fun createTextContent(text: CharSequence, autoMarkdown: Boolean): TextContent { if (autoMarkdown) { - return markdownParser.parse(text, force = forceMarkdownParse) + return markdownParser.parse(text) } else { // Try to detect pills textPillsUtils.processSpecialSpansToHtml(text)?.let { @@ -206,9 +206,9 @@ internal class LocalEchoEventFactory @Inject constructor( val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) // As we always supply formatted body for replies we should force the MarkdownParser to produce html. - val newBodyFormatted = createTextContent(newBodyText, true, forceMarkdownParse = true).takeFormatted() + val newBodyFormatted = markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. - val bodyFormatted = body.formattedText ?: createTextContent(body.text, true, forceMarkdownParse = true).takeFormatted() + val bodyFormatted = body.formattedText ?: markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted() val replyFormatted = REPLY_PATTERN.format( permalink, userLink, @@ -396,9 +396,9 @@ internal class LocalEchoEventFactory @Inject constructor( val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) // As we always supply formatted body for replies we should force the MarkdownParser to produce html. - val replyTextFormatted = createTextContent(replyText, true, forceMarkdownParse = true).takeFormatted() + val replyTextFormatted = markdownParser.parse(replyText, force = true, advanced = false).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. - val bodyFormatted = body.formattedText ?: createTextContent(body.text, true, forceMarkdownParse = true).takeFormatted() + val bodyFormatted = body.formattedText ?: markdownParser.parse(replyText, force = true, advanced = false).takeFormatted() val replyFormatted = REPLY_PATTERN.format( permalink, userLink, @@ -514,7 +514,7 @@ internal class LocalEchoEventFactory @Inject constructor( val messageContent = quotedEvent.getLastMessageContent() val textMsg = messageContent?.body val quoteText = legacyRiotQuoteText(textMsg, text) - return createFormattedTextEvent(roomId, createTextContent(quoteText, autoMarkdown=true, forceMarkdownParse = true), MessageType.MSGTYPE_TEXT) + return createFormattedTextEvent(roomId, markdownParser.parse(quoteText, force = true, advanced = false), MessageType.MSGTYPE_TEXT) } private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt index 2df668bf40..8f963ba16d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.send import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer +import org.matrix.android.sdk.internal.session.room.SimpleCommonmarkParser import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils import javax.inject.Inject @@ -27,7 +28,8 @@ import javax.inject.Inject * If any change is required, please add a test covering the problem and make sure all the tests are still passing. */ internal class MarkdownParser @Inject constructor( - private val parser: Parser, + private val advancedParser: Parser, + @SimpleCommonmarkParser private val simpleParser: Parser, private val htmlRenderer: HtmlRenderer, private val textPillsUtils: TextPillsUtils ) { @@ -40,7 +42,7 @@ internal class MarkdownParser @Inject constructor( * @param force Skips the check for detecting if the input contains markdown and always converts to html. * @return TextContent containing the plain text and the formatted html if generated. */ - fun parse(text: CharSequence, force: Boolean = false): TextContent { + fun parse(text: CharSequence, force: Boolean = false, advanced: Boolean = true): TextContent { val source = textPillsUtils.processSpecialSpansToMarkdown(text) ?: text.toString() // If no special char are detected, just return plain text @@ -48,7 +50,7 @@ internal class MarkdownParser @Inject constructor( return TextContent(source) } - val document = parser.parse(source) + val document = if (advanced) advancedParser.parse(source) else simpleParser.parse(source) val htmlText = htmlRenderer.render(document) // Cleanup extra paragraph From 13dc2d5b7642430fd6293bb966985f913b72587a Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 15 Dec 2021 10:03:14 +0000 Subject: [PATCH 06/11] Fix documentation. --- .../org/matrix/android/sdk/api/session/room/send/SendService.kt | 2 -- .../sdk/internal/session/room/send/LocalEchoEventFactory.kt | 1 - .../android/sdk/internal/session/room/send/MarkdownParser.kt | 1 + 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 6496ddc2ca..de48a3011f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -60,8 +60,6 @@ interface SendService { * Method to quote an events content. * @param quotedEvent The event to which we will quote it's content. * @param text the text message to send - * @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE - * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present * @return a [Cancelable] */ fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String): Cancelable diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 9e279dfa44..4367ad524f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -20,7 +20,6 @@ import android.content.Context import android.graphics.Bitmap import android.media.MediaMetadataRetriever import androidx.exifinterface.media.ExifInterface -import okhttp3.internal.format import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt index 8f963ba16d..c7ea842a4f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt @@ -40,6 +40,7 @@ internal class MarkdownParser @Inject constructor( * Parses some input text and produces html. * @param text An input CharSequence to be parsed. * @param force Skips the check for detecting if the input contains markdown and always converts to html. + * @param advanced Whether to use the full markdown support or the simple version. * @return TextContent containing the plain text and the formatted html if generated. */ fun parse(text: CharSequence, force: Boolean = false, advanced: Boolean = true): TextContent { From a3a8a5b0b57e31371c3e475ac720d8334b635980 Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 16 Dec 2021 12:47:06 +0000 Subject: [PATCH 07/11] Extract reply formatting --- .../room/send/LocalEchoEventFactory.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 4367ad524f..c6b6b9352a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -208,12 +208,11 @@ internal class LocalEchoEventFactory @Inject constructor( val newBodyFormatted = markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. val bodyFormatted = body.formattedText ?: markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted() - val replyFormatted = REPLY_PATTERN.format( + val replyFormatted = buildFormattedReply( permalink, userLink, originalEvent.senderInfo.disambiguatedDisplayName, - // Remove inner mx_reply tags if any - bodyFormatted.replace(MX_REPLY_REGEX, ""), + bodyFormatted, newBodyFormatted ) // @@ -398,12 +397,11 @@ internal class LocalEchoEventFactory @Inject constructor( val replyTextFormatted = markdownParser.parse(replyText, force = true, advanced = false).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. val bodyFormatted = body.formattedText ?: markdownParser.parse(replyText, force = true, advanced = false).takeFormatted() - val replyFormatted = REPLY_PATTERN.format( + val replyFormatted = buildFormattedReply( permalink, userLink, userId, - // Remove inner mx_reply tags if any - bodyFormatted.replace(MX_REPLY_REGEX, ""), + bodyFormatted, replyTextFormatted ) // @@ -422,6 +420,16 @@ internal class LocalEchoEventFactory @Inject constructor( return createMessageEvent(roomId, content) } + private fun buildFormattedReply(permalink: String, userLink: String, userId: String, bodyFormatted: String, newBodyFormatted: String): String { + return REPLY_PATTERN.format( + permalink, + userLink, + userId, + // Remove inner mx_reply tags if any + bodyFormatted.replace(MX_REPLY_REGEX, ""), + newBodyFormatted + ) + } private fun buildReplyFallback(body: TextContent, originalSenderId: String?, newBodyText: String): String { return buildString { append("> <") From bc6ca2449e85eb8ca89c067e151465ee9931453d Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 16 Dec 2021 13:13:40 +0000 Subject: [PATCH 08/11] Add advanced parser annotation --- .../android/sdk/internal/session/room/RoomModule.kt | 10 +++++++++- .../sdk/internal/session/room/send/MarkdownParser.kt | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 6497c997ed..0af05d8df2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -108,6 +108,13 @@ import javax.inject.Qualifier @Retention(AnnotationRetention.RUNTIME) internal annotation class SimpleCommonmarkParser +/** + * Used to inject the advanced commonmark Parser + */ +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +internal annotation class AdvancedCommonmarkParser + @Module internal abstract class RoomModule { @@ -128,8 +135,9 @@ internal abstract class RoomModule { } @Provides + @AdvancedCommonmarkParser @JvmStatic - fun providesParser(): Parser { + fun providesAdvancedParser(): Parser { return Parser.builder().build() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt index c7ea842a4f..e442d051f6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParser.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.send import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer +import org.matrix.android.sdk.internal.session.room.AdvancedCommonmarkParser import org.matrix.android.sdk.internal.session.room.SimpleCommonmarkParser import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils import javax.inject.Inject @@ -28,7 +29,7 @@ import javax.inject.Inject * If any change is required, please add a test covering the problem and make sure all the tests are still passing. */ internal class MarkdownParser @Inject constructor( - private val advancedParser: Parser, + @AdvancedCommonmarkParser private val advancedParser: Parser, @SimpleCommonmarkParser private val simpleParser: Parser, private val htmlRenderer: HtmlRenderer, private val textPillsUtils: TextPillsUtils From e7bb030d528131199eebc0a2e6160334bb54eecb Mon Sep 17 00:00:00 2001 From: David Langley Date: Thu, 16 Dec 2021 14:10:49 +0000 Subject: [PATCH 09/11] full markdown should work in replies, was hardcoded to simple irrespective of prefrence. --- .../room/model/relation/RelationService.kt | 4 +++- .../sdk/api/session/room/send/SendService.kt | 3 ++- .../room/relation/DefaultRelationService.kt | 4 ++-- .../internal/session/room/relation/EventEditor.kt | 3 ++- .../session/room/send/DefaultSendService.kt | 4 ++-- .../session/room/send/LocalEchoEventFactory.kt | 15 +++++++++------ .../detail/composer/MessageComposerViewModel.kt | 4 ++-- 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt index 0bc3120119..59d84ef40f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt @@ -104,9 +104,11 @@ interface RelationService { * by the sdk into pills. * @param eventReplied the event referenced by the reply * @param replyText the reply text + * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present */ fun replyToMessage(eventReplied: TimelineEvent, - replyText: CharSequence): Cancelable? + replyText: CharSequence, + autoMarkdown: Boolean = false): Cancelable? /** * Get the current EventAnnotationsSummary diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index de48a3011f..606500c4e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -60,9 +60,10 @@ interface SendService { * Method to quote an events content. * @param quotedEvent The event to which we will quote it's content. * @param text the text message to send + * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present * @return a [Cancelable] */ - fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String): Cancelable + fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean): Cancelable /** * Method to send a media asynchronously. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index c496633515..07927b1412 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -131,8 +131,8 @@ internal class DefaultRelationService @AssistedInject constructor( return fetchEditHistoryTask.execute(FetchEditHistoryTask.Params(roomId, eventId)) } - override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence): Cancelable? { - val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText) + override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Cancelable? { + val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText, autoMarkdown) ?.also { saveLocalEcho(it) } ?: return null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt index 076eb06447..a666d40fc3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt @@ -67,7 +67,7 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor: val roomId = replyToEdit.roomId if (replyToEdit.root.sendState.hasFailed()) { // We create a new in memory event for the EventSenderProcessor but we keep the eventId of the failed event. - val editedEvent = eventFactory.createReplyTextEvent(roomId, originalTimelineEvent, newBodyText)?.copy( + val editedEvent = eventFactory.createReplyTextEvent(roomId, originalTimelineEvent, newBodyText, false)?.copy( eventId = replyToEdit.eventId ) ?: return NoOpCancellable updateFailedEchoWithEvent(roomId, replyToEdit.eventId, editedEvent) @@ -78,6 +78,7 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor: replyToEdit, originalTimelineEvent, newBodyText, + true, MessageType.MSGTYPE_TEXT, compatibilityBodyText ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index f7f29f9b34..fb2fb3950a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -97,8 +97,8 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } - override fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String): Cancelable { - return localEchoEventFactory.createQuotedTextEvent(roomId, quotedEvent, text) + override fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean): Cancelable { + return localEchoEventFactory.createQuotedTextEvent(roomId, quotedEvent, text, autoMarkdown) .also { createLocalEcho(it) } .let { sendEvent(it) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index c6b6b9352a..c4caedc407 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -198,6 +198,7 @@ internal class LocalEchoEventFactory @Inject constructor( eventReplaced: TimelineEvent, originalEvent: TimelineEvent, newBodyText: String, + autoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false) @@ -205,9 +206,9 @@ internal class LocalEchoEventFactory @Inject constructor( val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) // As we always supply formatted body for replies we should force the MarkdownParser to produce html. - val newBodyFormatted = markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted() + val newBodyFormatted = markdownParser.parse(newBodyText, force = true, advanced = autoMarkdown).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. - val bodyFormatted = body.formattedText ?: markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted() + val bodyFormatted = body.formattedText ?: markdownParser.parse(body.text, force = true, advanced = autoMarkdown).takeFormatted() val replyFormatted = buildFormattedReply( permalink, userLink, @@ -384,7 +385,8 @@ internal class LocalEchoEventFactory @Inject constructor( fun createReplyTextEvent(roomId: String, eventReplied: TimelineEvent, - replyText: CharSequence): Event? { + replyText: CharSequence, + autoMarkdown: Boolean): Event? { // Fallbacks and event representation // TODO Add error/warning logs when any of this is null val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null @@ -394,9 +396,9 @@ internal class LocalEchoEventFactory @Inject constructor( val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) // As we always supply formatted body for replies we should force the MarkdownParser to produce html. - val replyTextFormatted = markdownParser.parse(replyText, force = true, advanced = false).takeFormatted() + val replyTextFormatted = markdownParser.parse(replyText, force = true, advanced = autoMarkdown).takeFormatted() // Body of the original message may not have formatted version, so may also have to convert to html. - val bodyFormatted = body.formattedText ?: markdownParser.parse(replyText, force = true, advanced = false).takeFormatted() + val bodyFormatted = body.formattedText ?: markdownParser.parse(body.text, force = true, advanced = autoMarkdown).takeFormatted() val replyFormatted = buildFormattedReply( permalink, userLink, @@ -517,11 +519,12 @@ internal class LocalEchoEventFactory @Inject constructor( roomId: String, quotedEvent: TimelineEvent, text: String, + autoMarkdown: Boolean, ): Event { val messageContent = quotedEvent.getLastMessageContent() val textMsg = messageContent?.body val quoteText = legacyRiotQuoteText(textMsg, text) - return createFormattedTextEvent(roomId, markdownParser.parse(quoteText, force = true, advanced = false), MessageType.MSGTYPE_TEXT) + return createFormattedTextEvent(roomId, markdownParser.parse(quoteText, force = true, advanced = autoMarkdown), MessageType.MSGTYPE_TEXT) } private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index 8495cc3970..a63a06928a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -406,13 +406,13 @@ class MessageComposerViewModel @AssistedInject constructor( popDraft() } is SendMode.Quote -> { - room.sendQuotedTextMessage(state.sendMode.timelineEvent, action.text.toString()) + room.sendQuotedTextMessage(state.sendMode.timelineEvent, action.text.toString(), action.autoMarkdown) _viewEvents.post(MessageComposerViewEvents.MessageSent) popDraft() } is SendMode.Reply -> { state.sendMode.timelineEvent.let { - room.replyToMessage(it, action.text.toString()) + room.replyToMessage(it, action.text.toString(), action.autoMarkdown) _viewEvents.post(MessageComposerViewEvents.MessageSent) popDraft() } From 37f0ffab8be972f258366a1802be1fdf27692470 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 7 Jan 2022 14:46:40 +0100 Subject: [PATCH 10/11] Fix test compilation issue --- .../android/sdk/internal/session/room/send/MarkdownParserTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt index 1ed2f89977..8625e97902 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt @@ -49,6 +49,7 @@ class MarkdownParserTest : InstrumentedTest { * Create the same parser than in the RoomModule */ private val markdownParser = MarkdownParser( + Parser.builder().build(), Parser.builder().build(), HtmlRenderer.builder().softbreak("
").build(), TextPillsUtils( From 5b6e70b1b42ae15f177de80821152543e7f14ab7 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 7 Jan 2022 16:32:18 +0200 Subject: [PATCH 11/11] While abortOnError = true for linter, lets always build the reports on error --- .github/workflows/quality.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index c72a29812e..26ee3cf460 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -89,6 +89,7 @@ jobs: - name: Lint analysis run: ./gradlew clean :vector:lint --stacktrace - name: Upload reports + if: always() uses: actions/upload-artifact@v2 with: name: lint-report @@ -117,6 +118,7 @@ jobs: - name: Lint ${{ matrix.target }} release run: ./gradlew clean lint${{ matrix.target }}Release --stacktrace - name: Upload ${{ matrix.target }} linting report + if: always() uses: actions/upload-artifact@v2 with: name: release-lint-report-${{ matrix.target }}