Skip to content

Commit e596196

Browse files
authored
Merge pull request #8716 from element-hq/feature/fga/fix_event_edition_reply
Fix a bunch of issues related to edition and reply #5969
2 parents 7a2520b + 2ada4c8 commit e596196

File tree

9 files changed

+63
-47
lines changed

9 files changed

+63
-47
lines changed

changelog.d/5969.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix some issues related to edition and reply of events.

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ interface RelationService {
117117
fun editReply(
118118
replyToEdit: TimelineEvent,
119119
originalTimelineEvent: TimelineEvent,
120-
newBodyText: String,
120+
newBodyText: CharSequence,
121121
newFormattedBodyText: String? = null,
122122
compatibilityBodyText: String = "* $newBodyText"
123123
): Cancelable

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocati
3737
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
3838
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
3939
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
40-
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
4140
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
4241
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
4342
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
4443
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
4544
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
4645
import org.matrix.android.sdk.api.util.ContentUtils
46+
import org.matrix.android.sdk.api.util.ContentUtils.ensureCorrectFormattedBodyInTextReply
4747
import org.matrix.android.sdk.api.util.ContentUtils.extractUsefulTextFromReply
4848

4949
/**
@@ -160,37 +160,17 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
160160

161161
fun TimelineEvent.getLastEditNewContent(): Content? {
162162
val lastContent = annotations?.editSummary?.latestEdit?.getClearContent()?.toModel<MessageContent>()?.newContent
163-
return if (isReply()) {
164-
val previousFormattedBody = root.getClearContent().toModel<MessageTextContent>()?.formattedBody
165-
if (previousFormattedBody?.isNotEmpty() == true) {
166-
val lastMessageContent = lastContent.toModel<MessageTextContent>()
167-
lastMessageContent?.let { ensureCorrectFormattedBodyInTextReply(it, previousFormattedBody) }?.toContent() ?: lastContent
168-
} else {
169-
lastContent
170-
}
171-
} else {
172-
lastContent
173-
}
174-
}
175-
176-
private const val MX_REPLY_END_TAG = "</mx-reply>"
177-
178-
/**
179-
* Not every client sends a formatted body in the last edited event since this is not required in the
180-
* [Matrix specification](https://spec.matrix.org/v1.4/client-server-api/#applying-mnew_content).
181-
* We must ensure there is one so that it is still considered as a reply when rendering the message.
182-
*/
183-
private fun ensureCorrectFormattedBodyInTextReply(messageTextContent: MessageTextContent, previousFormattedBody: String): MessageTextContent {
184163
return when {
185-
messageTextContent.formattedBody.isNullOrEmpty() && previousFormattedBody.contains(MX_REPLY_END_TAG) -> {
186-
// take previous formatted body with the new body content
187-
val newFormattedBody = previousFormattedBody.replaceAfterLast(MX_REPLY_END_TAG, messageTextContent.body)
188-
messageTextContent.copy(
189-
formattedBody = newFormattedBody,
190-
format = MessageFormat.FORMAT_MATRIX_HTML,
191-
)
164+
isReply() -> {
165+
val originalFormattedBody = root.getClearContent().toModel<MessageTextContent>()?.formattedBody
166+
val lastMessageContent = lastContent.toModel<MessageTextContent>()
167+
if (lastMessageContent != null && originalFormattedBody?.isNotEmpty() == true) {
168+
ensureCorrectFormattedBodyInTextReply(lastMessageContent, originalFormattedBody).toContent()
169+
} else {
170+
lastContent
171+
}
192172
}
193-
else -> messageTextContent
173+
else -> lastContent
194174
}
195175
}
196176

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.matrix.android.sdk.api.util
1717

18+
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
19+
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
1820
import org.matrix.android.sdk.internal.util.unescapeHtml
1921

2022
object ContentUtils {
@@ -38,15 +40,36 @@ object ContentUtils {
3840
}
3941

4042
fun extractUsefulTextFromHtmlReply(repliedBody: String): String {
41-
if (repliedBody.startsWith("<mx-reply>")) {
42-
val closingTagIndex = repliedBody.lastIndexOf("</mx-reply>")
43+
if (repliedBody.startsWith(MX_REPLY_START_TAG)) {
44+
val closingTagIndex = repliedBody.lastIndexOf(MX_REPLY_END_TAG)
4345
if (closingTagIndex != -1) {
44-
return repliedBody.substring(closingTagIndex + "</mx-reply>".length).trim()
46+
return repliedBody.substring(closingTagIndex + MX_REPLY_END_TAG.length).trim()
4547
}
4648
}
4749
return repliedBody
4850
}
4951

52+
/**
53+
* Not every client sends a formatted body in the last edited event since this is not required in the
54+
* [Matrix specification](https://spec.matrix.org/v1.4/client-server-api/#applying-mnew_content).
55+
* We must ensure there is one so that it is still considered as a reply when rendering the message.
56+
*/
57+
fun ensureCorrectFormattedBodyInTextReply(messageTextContent: MessageTextContent, originalFormattedBody: String): MessageTextContent {
58+
return when {
59+
messageTextContent.formattedBody != null &&
60+
!messageTextContent.formattedBody.contains(MX_REPLY_END_TAG) &&
61+
originalFormattedBody.contains(MX_REPLY_END_TAG) -> {
62+
// take previous formatted body with the new body content
63+
val newFormattedBody = originalFormattedBody.replaceAfterLast(MX_REPLY_END_TAG, messageTextContent.body)
64+
messageTextContent.copy(
65+
formattedBody = newFormattedBody,
66+
format = MessageFormat.FORMAT_MATRIX_HTML,
67+
)
68+
}
69+
else -> messageTextContent
70+
}
71+
}
72+
5073
@Suppress("RegExpRedundantEscape")
5174
fun formatSpoilerTextFromHtml(formattedBody: String): String {
5275
// var reason = "",
@@ -57,4 +80,6 @@ object ContentUtils {
5780
}
5881

5982
private const val SPOILER_CHAR = ""
83+
private const val MX_REPLY_START_TAG = "<mx-reply>"
84+
private const val MX_REPLY_END_TAG = "</mx-reply>"
6085
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ internal class DefaultRelationService @AssistedInject constructor(
115115
override fun editReply(
116116
replyToEdit: TimelineEvent,
117117
originalTimelineEvent: TimelineEvent,
118-
newBodyText: String,
118+
newBodyText: CharSequence,
119119
newFormattedBodyText: String?,
120120
compatibilityBodyText: String
121121
): Cancelable {

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ internal class EventEditor @Inject constructor(
106106
fun editReply(
107107
replyToEdit: TimelineEvent,
108108
originalTimelineEvent: TimelineEvent,
109-
newBodyText: String,
109+
newBodyText: CharSequence,
110110
newBodyFormattedText: String?,
111111
compatibilityBodyText: String
112112
): Cancelable {
@@ -131,6 +131,7 @@ internal class EventEditor @Inject constructor(
131131
replyToEdit,
132132
originalTimelineEvent,
133133
newBodyText,
134+
newBodyFormattedText,
134135
true,
135136
MessageType.MSGTYPE_TEXT,
136137
compatibilityBodyText

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ internal class LocalEchoEventFactory @Inject constructor(
312312
roomId: String,
313313
eventReplaced: TimelineEvent,
314314
originalEvent: TimelineEvent,
315-
newBodyText: String,
315+
replyText: CharSequence,
316+
replyTextFormatted: String?,
316317
autoMarkdown: Boolean,
317318
msgType: String,
318319
compatibilityText: String,
@@ -321,22 +322,23 @@ internal class LocalEchoEventFactory @Inject constructor(
321322
val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false)
322323
val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: ""
323324

324-
val body = bodyForReply(timelineEvent = originalEvent)
325+
val bodyOfRepliedEvent = bodyForReply(timelineEvent = originalEvent)
325326
// As we always supply formatted body for replies we should force the MarkdownParser to produce html.
326-
val newBodyFormatted = markdownParser.parse(newBodyText, force = true, advanced = autoMarkdown).takeFormatted()
327+
val newBodyFormatted = replyTextFormatted ?: markdownParser.parse(replyText, force = true, advanced = autoMarkdown).takeFormatted()
327328
// Body of the original message may not have formatted version, so may also have to convert to html.
328-
val bodyFormatted = body.formattedText ?: markdownParser.parse(body.text, force = true, advanced = autoMarkdown).takeFormatted()
329+
val formattedBodyOfRepliedEvent =
330+
bodyOfRepliedEvent.formattedText ?: markdownParser.parse(text = bodyOfRepliedEvent.text, force = true, advanced = autoMarkdown).takeFormatted()
329331
val replyFormatted = buildFormattedReply(
330332
permalink,
331333
userLink,
332334
originalEvent.senderInfo.disambiguatedDisplayName,
333-
bodyFormatted,
335+
formattedBodyOfRepliedEvent,
334336
newBodyFormatted
335337
)
336338
//
337339
// > <@alice:example.org> This is the original body
338340
//
339-
val replyFallback = buildReplyFallback(body, originalEvent.root.senderId ?: "", newBodyText)
341+
val replyFallback = buildReplyFallback(bodyOfRepliedEvent, originalEvent.root.senderId ?: "", replyText.toString())
340342

341343
return createMessageEvent(
342344
roomId,

vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ class MessageComposerViewModel @AssistedInject constructor(
178178
private fun handleEnterEditMode(room: Room, action: MessageComposerAction.EnterEditMode) {
179179
room.getTimelineEvent(action.eventId)?.let { timelineEvent ->
180180
val formatted = vectorPreferences.isRichTextEditorEnabled()
181-
setState { copy(sendMode = SendMode.Edit(timelineEvent, timelineEvent.getTextEditableContent(formatted))) }
181+
val editableContent = timelineEvent.getTextEditableContent(formatted)
182+
setState { copy(sendMode = SendMode.Edit(timelineEvent, editableContent)) }
182183
}
183184
}
184185

@@ -578,7 +579,7 @@ class MessageComposerViewModel @AssistedInject constructor(
578579
if (inReplyTo != null) {
579580
// TODO check if same content?
580581
room.getTimelineEvent(inReplyTo)?.let {
581-
room.relationService().editReply(state.sendMode.timelineEvent, it, action.text.toString(), action.formattedText)
582+
room.relationService().editReply(state.sendMode.timelineEvent, it, action.text, action.formattedText)
582583
}
583584
} else {
584585
val messageContent = state.sendMode.timelineEvent.getVectorLastMessageContent()
@@ -624,14 +625,14 @@ class MessageComposerViewModel @AssistedInject constructor(
624625
state.rootThreadEventId?.let {
625626
room.relationService().replyInThread(
626627
rootThreadEventId = it,
627-
replyInThreadText = action.text.toString(),
628+
replyInThreadText = action.text,
628629
autoMarkdown = action.autoMarkdown,
629630
formattedText = action.formattedText,
630631
eventReplied = timelineEvent
631632
)
632633
} ?: room.relationService().replyToMessage(
633634
eventReplied = timelineEvent,
634-
replyText = action.text.toString(),
635+
replyText = action.text,
635636
replyFormattedText = action.formattedText,
636637
autoMarkdown = action.autoMarkdown,
637638
showInThread = showInThread,

vector/src/main/java/im/vector/app/features/home/room/detail/composer/PlainTextComposerLayout.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollConte
4848
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
4949
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
5050
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
51+
import org.matrix.android.sdk.api.util.ContentUtils
5152
import org.matrix.android.sdk.api.util.MatrixItem
5253
import org.matrix.android.sdk.api.util.toMatrixItem
5354
import javax.inject.Inject
@@ -188,7 +189,12 @@ class PlainTextComposerLayout @JvmOverloads constructor(
188189
var formattedBody: CharSequence? = null
189190
if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) {
190191
val parser = Parser.builder().build()
191-
val document = parser.parse(messageContent.formattedBody ?: messageContent.body)
192+
193+
val bodyToParse = messageContent.formattedBody?.let {
194+
ContentUtils.extractUsefulTextFromHtmlReply(it)
195+
} ?: ContentUtils.extractUsefulTextFromReply(messageContent.body)
196+
197+
val document = parser.parse(bodyToParse)
192198
formattedBody = eventHtmlRenderer.render(document, pillsPostProcessor)
193199
}
194200
views.composerRelatedMessageContent.text = (formattedBody ?: nonFormattedBody)

0 commit comments

Comments
 (0)