element-hq / element-hq/element-android
[SDK] Reply event 'body' contains HTML instead of plain text
- Dominant language
- Kotlin
- Stars
- 3.7k
- Forks
- 917
- PR merge metrics
- No merged PRs in 30d
Description
### Description
The `body` field of reply events can contain HTML markup, even though it should contain the plain-text representation of the message.
I think the bug come from that :
When creating a reply event, `createNewRepliedEvent()` calls `createReplyTextContent()`.
```
val replyText = localEchoEventFactory
.bodyForReply(currentTimelineEvent.getLastMessageContent(), true).takeFormatted()
val newContent = localEchoEventFactory.createReplyTextContent(
timelineEventMapper.map(timelineEventEntity),
replyText,
null,
false,
showInThread = false,
isRedactedEvent = isRedactedEvent
).toContent()
```
```
fun createReplyTextContent(
eventReplied: TimelineEvent,
replyText: CharSequence,
replyTextFormatted: CharSequence?,
autoMarkdown: Boolean,
rootThreadEventId: String? = null,
showInThread: Boolean,
isRedactedEvent: Boolean = false
): MessageContent? {
// Fallbacks and event representation
// TODO Add error/warning logs when any of this is null
val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null
val userId = eventReplied.root.senderId ?: return null
val userLink = permalinkFactory.createPermalink(userId, false) ?: return null
val body = bodyForReply(timelineEvent = eventReplied, isRedactedEvent = isRedactedEvent)
// As we always supply formatted body for replies we should force the MarkdownParser to produce html.
val finalReplyTextFormatted = replyTextFormatted?.toString() ?: 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(body.text, force = true, advanced = autoMarkdown).takeFormatted()
val replyFormatted = buildFormattedReply(
permalink,
userLink,
userId,
bodyFormatted,
finalReplyTextFormatted
)
//
// > <@alice:example.org> This is the original body
//
val replyFallback = buildReplyFallback(body, userId, replyText.toString())
val eventId = eventReplied.root.eventId ?: return null
return MessageTextContent(
msgType = MessageType.MSGTYPE_TEXT,
format = MessageFormat.FORMAT_MATRIX_HTML,
body = replyFallback,
formattedBody = replyFormatted,
relatesTo = generateReplyRelationContent(
eventId = eventId,
rootThreadEventId = rootThreadEventId,
showInThread = showInThread
)
)
}
```
The issue appears to be caused by `createNewRepliedEvent()` passing a formatted version of the reply text as `replyText`, while passing `null` as `replyTextFormatted`.
As a result, `createReplyTextContent()` receives the already-formatted HTML content as `replyText` and `null` as `replyTextFormatted`. It then uses `replyText` to build both the formatted reply and the reply fallback.
Since `replyText` already contains formatted content, `replyFallback` also contain HTML markup. `replyFallback` is then assigned directly to the body field.
This results in HTML markup being present in `body`, whereas `body` should contain the plain-text representation and the formatted representation should only be present in `formatted_body`.
Contributor guide
Research direction
Start at createNewRepliedEvent() and follow its call to createReplyTextContent(). Check how replyText and replyTextFormatted are passed and how buildReplyFallback() produces the body. Done means reply events use plain text in body while formatted HTML remains in formatted_body.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kotlin
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 70/100