Fix rendering issue with HTML formatted body

This commit is contained in:
Benoit Marty 2019-12-18 10:57:00 +01:00
parent 08970ad8c1
commit 9d26ba3186
7 changed files with 63 additions and 3 deletions

View file

@ -15,6 +15,7 @@ Bugfix 🐛:
- Scroll breadcrumbs to top when opened
- Render default room name when it starts with an emoji (#477)
- Do not display " (IRC)" in display names https://github.com/vector-im/riot-android/issues/444
- Fix rendering issue with HTML formatted body
Translations 🗣:
-

View file

@ -293,6 +293,7 @@ dependencies {
implementation 'me.gujun.android:span:1.7'
implementation "io.noties.markwon:core:$markwon_version"
implementation "io.noties.markwon:html:$markwon_version"
implementation 'com.googlecode.htmlcompressor:htmlcompressor:1.4'
implementation 'me.saket:better-link-movement-method:2.2.0'
implementation 'com.google.android:flexbox:1.1.1'
implementation "androidx.autofill:autofill:$autofill_version"

View file

@ -359,6 +359,11 @@ SOFTWARE.
<br/>
Copyright 2018 Kumar Bibek
</li>
<li>
<b>htmlcompressor</b>
<br/>
Copyright 2017 Sergiy Kovalchuk
</li>
</ul>
<pre>
Apache License

View file

@ -38,6 +38,7 @@ import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.HomeRoomListDataSource
import im.vector.riotx.features.home.group.SelectedGroupDataSource
import im.vector.riotx.features.html.EventHtmlRenderer
import im.vector.riotx.features.html.VectorHtmlCompressor
import im.vector.riotx.features.navigation.Navigator
import im.vector.riotx.features.notifications.*
import im.vector.riotx.features.rageshake.BugReporter
@ -87,6 +88,8 @@ interface VectorComponent {
fun eventHtmlRenderer(): EventHtmlRenderer
fun vectorHtmlCompressor(): VectorHtmlCompressor
fun navigator(): Navigator
fun errorFormatter(): ErrorFormatter

View file

@ -41,6 +41,7 @@ import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotx.features.html.EventHtmlRenderer
import im.vector.riotx.features.html.VectorHtmlCompressor
import java.text.SimpleDateFormat
import java.util.*
@ -82,6 +83,7 @@ data class MessageActionState(
class MessageActionsViewModel @AssistedInject constructor(@Assisted
initialState: MessageActionState,
private val eventHtmlRenderer: Lazy<EventHtmlRenderer>,
private val htmlCompressor: VectorHtmlCompressor,
private val session: Session,
private val noticeEventFormatter: NoticeEventFormatter,
private val stringProvider: StringProvider
@ -170,8 +172,12 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
EventType.MESSAGE -> {
val messageContent: MessageContent? = timelineEvent()?.getLastMessageContent()
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
eventHtmlRenderer.get().render(messageContent.formattedBody
?: messageContent.body)
val html = messageContent.formattedBody
?.takeIf { it.isNotBlank() }
?.let { htmlCompressor.compress(it) }
?: messageContent.body
eventHtmlRenderer.get().render(html)
} else {
messageContent?.body
}

View file

@ -46,6 +46,7 @@ import im.vector.riotx.features.home.room.detail.timeline.tools.createLinkMoveme
import im.vector.riotx.features.home.room.detail.timeline.tools.linkify
import im.vector.riotx.features.html.CodeVisitor
import im.vector.riotx.features.html.EventHtmlRenderer
import im.vector.riotx.features.html.VectorHtmlCompressor
import im.vector.riotx.features.media.ImageContentRenderer
import im.vector.riotx.features.media.VideoContentRenderer
import me.gujun.android.span.span
@ -57,6 +58,7 @@ class MessageItemFactory @Inject constructor(
private val dimensionConverter: DimensionConverter,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val htmlRenderer: Lazy<EventHtmlRenderer>,
private val htmlCompressor: VectorHtmlCompressor,
private val stringProvider: StringProvider,
private val imageContentRenderer: ImageContentRenderer,
private val messageInformationDataFactory: MessageInformationDataFactory,
@ -227,6 +229,7 @@ class MessageItemFactory @Inject constructor(
attributes: AbsMessageItem.Attributes): VectorEpoxyModel<*>? {
val isFormatted = messageContent.formattedBody.isNullOrBlank().not()
return if (isFormatted) {
// First detect if the message contains some code block(s) or inline code
val localFormattedBody = htmlRenderer.get().parse(messageContent.body) as Document
val codeVisitor = CodeVisitor()
codeVisitor.visit(localFormattedBody)
@ -240,7 +243,8 @@ class MessageItemFactory @Inject constructor(
buildMessageTextItem(codeFormatted, false, informationData, highlight, callback, attributes)
}
CodeVisitor.Kind.NONE -> {
val formattedBody = htmlRenderer.get().render(messageContent.formattedBody!!)
val compressed = htmlCompressor.compress(messageContent.formattedBody!!)
val formattedBody = htmlRenderer.get().render(compressed)
buildMessageTextItem(formattedBody, true, informationData, highlight, callback, attributes)
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.html
import com.googlecode.htmlcompressor.compressor.Compressor
import com.googlecode.htmlcompressor.compressor.HtmlCompressor
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class VectorHtmlCompressor @Inject constructor() {
// All default options are suitable so far
private val htmlCompressor: Compressor = HtmlCompressor()
fun compress(html: String): String {
var result = htmlCompressor.compress(html)
// Trim space after <br> and <p>, unfortunately the method setRemoveSurroundingSpaces() from the doc does not exist
result = result.replace("<br> ", "<br>")
result = result.replace("<br/> ", "<br/>")
result = result.replace("<p> ", "<p>")
return result
}
}