From e6c74dc1fe05bfff68d51d51cfd247fcb8cffc9f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 9 Jul 2019 18:41:08 +0200 Subject: [PATCH 01/13] Convert a Task to a ConfigurableTask without parameter --- .../matrix/android/internal/crypto/CryptoManager.kt | 6 ++++-- .../android/internal/crypto/keysbackup/KeysBackup.kt | 3 ++- .../internal/session/cache/DefaultCacheService.kt | 5 ++--- .../internal/session/pushers/DefaultPusherService.kt | 3 ++- .../internal/session/room/DefaultRoomDirectoryService.kt | 3 ++- .../internal/session/signout/DefaultSignOutService.kt | 4 ++-- .../matrix/android/internal/task/ConfigurableTask.kt | 9 ++++++++- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 5993548e..d52a457c 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -73,6 +73,7 @@ import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.task.toConfigurableTask import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.fetchCopied @@ -197,7 +198,7 @@ internal class CryptoManager @Inject constructor( override fun getDevicesList(callback: MatrixCallback) { getDevicesTask - .configureWith(Unit) + .toConfigurableTask() .dispatchTo(callback) .executeBy(taskExecutor) } @@ -1054,7 +1055,8 @@ internal class CryptoManager @Inject constructor( } override fun clearCryptoCache(callback: MatrixCallback) { - clearCryptoDataTask.configureWith(Unit) + clearCryptoDataTask + .toConfigurableTask() .dispatchTo(callback) .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt index 9350fd44..88d9aec9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -51,6 +51,7 @@ import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEnt import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.task.* import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskThread @@ -876,7 +877,7 @@ internal class KeysBackup @Inject constructor( override fun getCurrentVersion(callback: MatrixCallback) { getKeysBackupLastVersionTask - .configureWith(Unit) + .toConfigurableTask() .dispatchTo(object : MatrixCallback { override fun onSuccess(data: KeysVersionResult) { callback.onSuccess(data) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt index 34c5c5be..c23c9eea 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt @@ -19,9 +19,8 @@ package im.vector.matrix.android.internal.session.cache import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.internal.di.SessionDatabase -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.task.toConfigurableTask import javax.inject.Inject internal class DefaultCacheService @Inject constructor(@SessionDatabase private val clearCacheTask: ClearCacheTask, @@ -29,7 +28,7 @@ internal class DefaultCacheService @Inject constructor(@SessionDatabase private override fun clearCache(callback: MatrixCallback) { clearCacheTask - .configureWith(Unit) + .toConfigurableTask() .dispatchTo(callback) .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt index 24706841..5d9af0d3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt @@ -29,6 +29,7 @@ import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.task.toConfigurableTask import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory @@ -49,7 +50,7 @@ internal class DefaultPusherService @Inject constructor( override fun refreshPushers() { getPusherTask - .configureWith(Unit) + .toConfigurableTask() .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt index 764d4816..0b13fa3c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt @@ -27,6 +27,7 @@ import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyPro import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.task.toConfigurableTask import javax.inject.Inject internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask, @@ -52,7 +53,7 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu override fun getThirdPartyProtocol(callback: MatrixCallback>) { getThirdPartyProtocolsTask - .configureWith(Unit) + .toConfigurableTask() .dispatchTo(callback) .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt index e8011b8c..fff75d14 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.session.signout import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.signout.SignOutService import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.task.toConfigurableTask import javax.inject.Inject internal class DefaultSignOutService @Inject constructor(private val signOutTask: SignOutTask, @@ -27,7 +27,7 @@ internal class DefaultSignOutService @Inject constructor(private val signOutTask override fun signOut(callback: MatrixCallback) { signOutTask - .configureWith(Unit) + .toConfigurableTask() .dispatchTo(callback) .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt index 02ce07b0..955ccc67 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt @@ -21,7 +21,14 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable internal fun Task.configureWith(params: PARAMS): ConfigurableTask { - return ConfigurableTask (this, params) + return ConfigurableTask(this, params) +} + +/** + * Convert a Task to a ConfigurableTask without parameter + */ +internal fun Task.toConfigurableTask(): ConfigurableTask { + return ConfigurableTask(this, Unit) } internal data class ConfigurableTask( From 228ee52563d8eeaecfb327ee595d8e3959b17509 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 10:07:45 +0200 Subject: [PATCH 02/13] Remove extra space in --- .../room/send/LocalEchoEventFactory.kt | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index c5f7a6e3..dc13a359 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -250,31 +250,29 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials val permalink = PermalinkFactory.createPermalink(eventReplied) ?: return null val userId = eventReplied.senderId ?: return null val userLink = PermalinkFactory.createPermalink(userId) ?: return null -// -//
-// In reply to -// @alice:example.org -//
-// -//
-//
-// This is where the reply goes. + // + //
+ // In reply to + // @alice:example.org + //
+ // + //
+ //
+ // This is where the reply goes. val body = bodyForReply(eventReplied.getClearContent().toModel()) - val replyFallbackTemplateFormatted = """ -
- ${stringProvider.getString(R.string.message_reply_to_prefix)} - %s -
- %s -
-
- %s""".trimIndent().format(permalink, userLink, userId, body.second ?: body.first, replyText) -// -// > <@alice:example.org> This is the original body -// -// This is where the reply goes + val replyFallbackTemplateFormatted = REPLY_PATTERN.format( + permalink, + stringProvider.getString(R.string.message_reply_to_prefix), + userLink, + userId, + body.second ?: body.first, + replyText + ) + // + // > <@alice:example.org> This is the original body + // val lines = body.first.split("\n") - val plainTextBody = StringBuffer("><${userId}>") + val plainTextBody = StringBuffer("><$userId>") lines.firstOrNull()?.also { plainTextBody.append(" $it") } lines.forEachIndexed { index, s -> if (index > 0) { @@ -365,6 +363,9 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials companion object { const val LOCAL_ID_PREFIX = "local." + // No whitespace + const val REPLY_PATTERN = """
%s%s
%s
%s""" + fun isLocalEchoId(eventId: String): Boolean = eventId.startsWith(LOCAL_ID_PREFIX) } } From 0a54801fccfd2c3b9c757de8146e85e97d1d8dbd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 10:16:21 +0200 Subject: [PATCH 03/13] Code clarity --- .../room/send/LocalEchoEventFactory.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index dc13a359..655cafbb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -260,7 +260,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials //
// This is where the reply goes. val body = bodyForReply(eventReplied.getClearContent().toModel()) - val replyFallbackTemplateFormatted = REPLY_PATTERN.format( + val replyFormatted = REPLY_PATTERN.format( permalink, stringProvider.getString(R.string.message_reply_to_prefix), userLink, @@ -272,21 +272,22 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials // > <@alice:example.org> This is the original body // val lines = body.first.split("\n") - val plainTextBody = StringBuffer("><$userId>") - lines.firstOrNull()?.also { plainTextBody.append(" $it") } + val replyFallback = StringBuffer("><$userId>") lines.forEachIndexed { index, s -> - if (index > 0) { - plainTextBody.append("\n>$s") + if (index == 0) { + replyFallback.append(" $s") + } else { + replyFallback.append("\n>$s") } } - plainTextBody.append("\n\n").append(replyText) + replyFallback.append("\n\n").append(replyText) val eventId = eventReplied.eventId ?: return null val content = MessageTextContent( type = MessageType.MSGTYPE_TEXT, format = MessageType.FORMAT_MATRIX_HTML, - body = plainTextBody.toString(), - formattedBody = replyFallbackTemplateFormatted, + body = replyFallback.toString(), + formattedBody = replyFormatted, relatesTo = RelationDefaultContent(null, null, ReplyToContent(eventId)) ) return createEvent(roomId, content) @@ -315,9 +316,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials MessageType.MSGTYPE_IMAGE -> return stringProvider.getString(R.string.reply_to_an_image) to null MessageType.MSGTYPE_VIDEO -> return stringProvider.getString(R.string.reply_to_a_video) to null else -> return (content?.body ?: "") to null - } - } /* From 92e3a02389ffd03c8ace35e15aa5d6db864a88c6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 10:34:32 +0200 Subject: [PATCH 04/13] Create data class instead of Pair --- .../room/send/LocalEchoEventFactory.kt | 18 ++++++------ .../internal/session/room/send/TextContent.kt | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 655cafbb..1550dba8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -265,13 +265,13 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials stringProvider.getString(R.string.message_reply_to_prefix), userLink, userId, - body.second ?: body.first, + body.takeFormatted(), replyText ) // // > <@alice:example.org> This is the original body // - val lines = body.first.split("\n") + val lines = body.text.split("\n") val replyFallback = StringBuffer("><$userId>") lines.forEachIndexed { index, s -> if (index == 0) { @@ -297,7 +297,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials * Returns a pair of used for the fallback event representation * in a reply message. */ - private fun bodyForReply(content: MessageContent?): Pair { + private fun bodyForReply(content: MessageContent?): TextContent { when (content?.type) { MessageType.MSGTYPE_EMOTE, MessageType.MSGTYPE_TEXT, @@ -309,13 +309,13 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials formattedText = content.formattedBody } } - return content.body to formattedText + return TextContent(content.body, formattedText) } - MessageType.MSGTYPE_FILE -> return stringProvider.getString(R.string.reply_to_a_file) to null - MessageType.MSGTYPE_AUDIO -> return stringProvider.getString(R.string.reply_to_an_audio_file) to null - MessageType.MSGTYPE_IMAGE -> return stringProvider.getString(R.string.reply_to_an_image) to null - MessageType.MSGTYPE_VIDEO -> return stringProvider.getString(R.string.reply_to_a_video) to null - else -> return (content?.body ?: "") to null + MessageType.MSGTYPE_FILE -> return TextContent(stringProvider.getString(R.string.reply_to_a_file)) + MessageType.MSGTYPE_AUDIO -> return TextContent(stringProvider.getString(R.string.reply_to_an_audio_file)) + MessageType.MSGTYPE_IMAGE -> return TextContent(stringProvider.getString(R.string.reply_to_an_image)) + MessageType.MSGTYPE_VIDEO -> return TextContent(stringProvider.getString(R.string.reply_to_a_video)) + else -> return TextContent(content?.body ?: "") } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt new file mode 100644 index 00000000..554d84b3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt @@ -0,0 +1,28 @@ +/* + * 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.matrix.android.internal.session.room.send + +/** + * Contains a text and eventually a formatted text + */ +data class TextContent( + val text: String, + + val formattedText: String? = null +) { + fun takeFormatted() = formattedText ?: text +} From 19183022972a38c246766853ee230dd7bc058ac6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 11:29:47 +0200 Subject: [PATCH 05/13] Reply with formatted content --- .../room/model/relation/RelationService.kt | 3 +- .../room/relation/DefaultRelationService.kt | 4 +- .../session/room/send/DefaultSendService.kt | 2 +- .../room/send/LocalEchoEventFactory.kt | 48 +++++++++++-------- .../home/room/detail/RoomDetailViewModel.kt | 4 +- 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt index 2a3c5b6d..7b547c48 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt @@ -77,8 +77,9 @@ interface RelationService { * https://matrix.org/docs/spec/client_server/r0.4.0.html#id350 * @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: Event, replyText: String): Cancelable? + fun replyToMessage(eventReplied: Event, replyText: String, autoMarkdown: Boolean = false): Cancelable? fun getEventSummaryLive(eventId: String): LiveData } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index 27270c3e..58151f18 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -127,8 +127,8 @@ internal class DefaultRelationService @Inject constructor(private val context: C } - override fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? { - val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText)?.also { + override fun replyToMessage(eventReplied: Event, replyText: String, 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/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 71a2c4bb..9a94b05b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -59,7 +59,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun sendFormattedTextMessage(text: String, formattedText: String): Cancelable { - val event = localEchoEventFactory.createFormattedTextEvent(roomId, text, formattedText).also { + val event = localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText)).also { saveLocalEcho(it) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 1550dba8..9d7cf089 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -52,30 +52,41 @@ import javax.inject.Inject internal class LocalEchoEventFactory @Inject constructor(private val credentials: Credentials, private val stringProvider: StringProvider, private val roomSummaryUpdater: RoomSummaryUpdater) { + // TODO Inject + private val parser = Parser.builder().build() + // TODO Inject + private val renderer = HtmlRenderer.builder().build() fun createTextEvent(roomId: String, msgType: String, text: String, autoMarkdown: Boolean): Event { - if (autoMarkdown && msgType == MessageType.MSGTYPE_TEXT) { - val parser = Parser.builder().build() - val document = parser.parse(text) - val renderer = HtmlRenderer.builder().build() - val htmlText = renderer.render(document) - if (isFormattedTextPertinent(text, htmlText)) { //FIXME - return createFormattedTextEvent(roomId, text, htmlText) - } + if (msgType == MessageType.MSGTYPE_TEXT) { + return createFormattedTextEvent(roomId, createTextContent(text, autoMarkdown)) } val content = MessageTextContent(type = msgType, body = text) return createEvent(roomId, content) } + private fun createTextContent(text: String, autoMarkdown: Boolean): TextContent { + if (autoMarkdown) { + val document = parser.parse(text) + val htmlText = renderer.render(document) + + if (isFormattedTextPertinent(text, htmlText)) { + return TextContent(text, htmlText) + } + } + + return TextContent(text) + } + private fun isFormattedTextPertinent(text: String, htmlText: String?) = text != htmlText && htmlText != "

${text.trim()}

\n" - fun createFormattedTextEvent(roomId: String, text: String, formattedText: String): Event { + fun createFormattedTextEvent(roomId: String, textContent: TextContent): Event { val content = MessageTextContent( type = MessageType.MSGTYPE_TEXT, - format = MessageType.FORMAT_MATRIX_HTML, - body = text, - formattedBody = formattedText + format = if (textContent.formattedText == null) MessageType.FORMAT_MATRIX_HTML else null, + body = textContent.text, + formattedBody = textContent.formattedText ) return createEvent(roomId, content) } @@ -87,7 +98,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials newBodyAutoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { - + // TODO Format newBodyText var newContent = MessageTextContent( type = MessageType.MSGTYPE_TEXT, body = newBodyText @@ -202,7 +213,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials type = MessageType.MSGTYPE_AUDIO, body = attachment.name ?: "audio", audioInfo = AudioInfo( - mimeType = attachment.mimeType ?: "audio/mpeg", + mimeType = attachment.mimeType.takeIf { it.isNotBlank() } ?: "audio/mpeg", size = attachment.size ), url = attachment.path @@ -215,7 +226,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials type = MessageType.MSGTYPE_FILE, body = attachment.name ?: "file", info = FileInfo( - mimeType = attachment.mimeType ?: "application/octet-stream", + mimeType = attachment.mimeType.takeIf { it.isNotBlank() } ?: "application/octet-stream", size = attachment.size ), url = attachment.path @@ -244,7 +255,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials return "$LOCAL_ID_PREFIX${UUID.randomUUID()}" } - fun createReplyTextEvent(roomId: String, eventReplied: Event, replyText: String): Event? { + fun createReplyTextEvent(roomId: String, eventReplied: Event, replyText: String, autoMarkdown: Boolean): Event? { //Fallbacks and event representation //TODO Add error/warning logs when any of this is null val permalink = PermalinkFactory.createPermalink(eventReplied) ?: return null @@ -266,7 +277,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials userLink, userId, body.takeFormatted(), - replyText + createTextContent(replyText, autoMarkdown).takeFormatted() ) // // > <@alice:example.org> This is the original body @@ -294,8 +305,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials } /** - * Returns a pair of used for the fallback event representation - * in a reply message. + * Returns a TextContent used for the fallback event representation in a reply message. */ private fun bodyForReply(content: MessageContent?): TextContent { when (content?.type) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 9e0fda91..9c96677b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -47,8 +47,6 @@ import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.features.command.CommandParser import im.vector.riotx.features.command.ParsedCommand import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.rxkotlin.subscribeBy import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer @@ -272,7 +270,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } SendMode.REPLY -> { state.selectedEvent?.let { - room.replyToMessage(it.root, action.text) + room.replyToMessage(it.root, action.text, action.autoMarkdown) setState { copy( sendMode = SendMode.REGULAR, From e5082f662cc0446f7bb34376caaaae91f206cf7d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 14:19:59 +0200 Subject: [PATCH 06/13] Fix actually done TODO --- .../room/send/LocalEchoEventFactory.kt | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 9d7cf089..5f060284 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -98,24 +98,27 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials newBodyAutoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { - // TODO Format newBodyText - var newContent = MessageTextContent( - type = MessageType.MSGTYPE_TEXT, - body = newBodyText - ) - if (newBodyAutoMarkdown) { - val parser = Parser.builder().build() + val newContent = if (newBodyAutoMarkdown) { val document = parser.parse(newBodyText) - val renderer = HtmlRenderer.builder().build() val htmlText = renderer.render(document) if (isFormattedTextPertinent(newBodyText, htmlText)) { - newContent = MessageTextContent( + MessageTextContent( type = MessageType.MSGTYPE_TEXT, format = MessageType.FORMAT_MATRIX_HTML, body = newBodyText, formattedBody = htmlText ) + } else { + MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + body = newBodyText + ) } + } else { + MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + body = newBodyText + ) } val content = MessageTextContent( @@ -226,7 +229,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials type = MessageType.MSGTYPE_FILE, body = attachment.name ?: "file", info = FileInfo( - mimeType = attachment.mimeType.takeIf { it.isNotBlank() } ?: "application/octet-stream", + mimeType = attachment.mimeType.takeIf { it.isNotBlank() } ?: "application/octet-stream", size = attachment.size ), url = attachment.path From 06699eaefce909d5ddd6dd24bcf62292d9b85a81 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 14:40:08 +0200 Subject: [PATCH 07/13] Cleaner code --- .../room/send/LocalEchoEventFactory.kt | 46 ++++--------------- .../internal/session/room/send/TextContent.kt | 13 ++++++ 2 files changed, 22 insertions(+), 37 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index 5f060284..cc449018 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -82,52 +82,24 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials text != htmlText && htmlText != "

${text.trim()}

\n" fun createFormattedTextEvent(roomId: String, textContent: TextContent): Event { - val content = MessageTextContent( - type = MessageType.MSGTYPE_TEXT, - format = if (textContent.formattedText == null) MessageType.FORMAT_MATRIX_HTML else null, - body = textContent.text, - formattedBody = textContent.formattedText - ) - return createEvent(roomId, content) + return createEvent(roomId, textContent.toMessageTextContent()) } - fun createReplaceTextEvent(roomId: String, targetEventId: String, newBodyText: String, newBodyAutoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { - val newContent = if (newBodyAutoMarkdown) { - val document = parser.parse(newBodyText) - val htmlText = renderer.render(document) - if (isFormattedTextPertinent(newBodyText, htmlText)) { + return createEvent(roomId, MessageTextContent( - type = MessageType.MSGTYPE_TEXT, - format = MessageType.FORMAT_MATRIX_HTML, - body = newBodyText, - formattedBody = htmlText - ) - } else { - MessageTextContent( - type = MessageType.MSGTYPE_TEXT, - body = newBodyText - ) - } - } else { - MessageTextContent( - type = MessageType.MSGTYPE_TEXT, - body = newBodyText - ) - } - - val content = MessageTextContent( - type = msgType, - body = compatibilityText, - relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId), - newContent = newContent.toContent() - ) - return createEvent(roomId, content) + type = msgType, + body = compatibilityText, + relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId), + newContent = createTextContent(newBodyText, newBodyAutoMarkdown) + .toMessageTextContent() + .toContent() + )) } fun createMediaEvent(roomId: String, attachment: ContentAttachmentData): Event { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt index 554d84b3..f1f29c20 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt @@ -16,6 +16,9 @@ package im.vector.matrix.android.internal.session.room.send +import im.vector.matrix.android.api.session.room.model.message.MessageTextContent +import im.vector.matrix.android.api.session.room.model.message.MessageType + /** * Contains a text and eventually a formatted text */ @@ -26,3 +29,13 @@ data class TextContent( ) { fun takeFormatted() = formattedText ?: text } + + +fun TextContent.toMessageTextContent(): MessageTextContent { + return MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + format = MessageType.FORMAT_MATRIX_HTML.takeIf { formattedText != null }, + body = text, + formattedBody = formattedText + ) +} From d613abf4b4c480dcd497e89429c48152812f0757 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 15:29:52 +0200 Subject: [PATCH 08/13] i18n edited_suffix --- .../home/room/detail/timeline/factory/MessageItemFactory.kt | 3 +-- vector/src/main/res/values/strings_riotX.xml | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 889df1b5..04ed925b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -311,8 +311,7 @@ class MessageItemFactory @Inject constructor( editSummary: EditAggregatedSummary?): SpannableStringBuilder { val spannable = SpannableStringBuilder() spannable.append(linkifiedBody) - // TODO i18n - val editedSuffix = "(edited)" + val editedSuffix = stringProvider.getString(R.string.edited_suffix) spannable.append(" ").append(editedSuffix) val color = colorProvider.getColorFromAttribute(R.attr.vctr_list_header_secondary_text_color) val editStart = spannable.indexOf(editedSuffix) diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 8919438a..8c492fc4 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -14,5 +14,7 @@ Downloading file %1$s… File %1$s has been downloaded! + "(edited)" + \ No newline at end of file From 7e8cd07e1e3ecb87bfee8edd850617c850daaf5e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 16:32:44 +0200 Subject: [PATCH 09/13] Do not send edition if text is identical --- .../home/room/detail/RoomDetailViewModel.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 9c96677b..708a705c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -232,8 +232,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } SendMode.EDIT -> { - room.editTextMessage(state.selectedEvent?.root?.eventId - ?: "", action.text, action.autoMarkdown) + val messageContent: MessageContent? = + state.selectedEvent?.annotations?.editSummary?.aggregatedContent.toModel() + ?: state.selectedEvent?.root?.getClearContent().toModel() + val nonFormattedBody = messageContent?.body ?: "" + + if (nonFormattedBody != action.text) { + room.editTextMessage(state.selectedEvent?.root?.eventId + ?: "", action.text, action.autoMarkdown) + } else { + Timber.w("Same message content, do not send edition") + } setState { copy( sendMode = SendMode.REGULAR, @@ -244,7 +253,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } SendMode.QUOTE -> { val messageContent: MessageContent? = - state.selectedEvent?.annotations?.editSummary?.aggregatedContent?.toModel() + state.selectedEvent?.annotations?.editSummary?.aggregatedContent.toModel() ?: state.selectedEvent?.root?.getClearContent().toModel() val textMsg = messageContent?.body From 9a57a029962f62a068f393fb78fcd8b8616faf4a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 17:05:32 +0200 Subject: [PATCH 10/13] Cleaner code: add TimelineEvent to special modes --- .../home/room/detail/RoomDetailFragment.kt | 119 ++++++++---------- .../home/room/detail/RoomDetailViewModel.kt | 43 +++---- .../home/room/detail/RoomDetailViewState.kt | 11 +- 3 files changed, 74 insertions(+), 99 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 2b3ddc51..62ffefea 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -33,6 +33,7 @@ import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.TextView import android.widget.Toast +import androidx.annotation.DrawableRes import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProviders @@ -225,79 +226,57 @@ class RoomDetailFragment : } } - roomDetailViewModel.selectSubscribe( - RoomDetailViewState::sendMode, - RoomDetailViewState::selectedEvent, - RoomDetailViewState::roomId) { mode, event, roomId -> + roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode) { mode -> when (mode) { - SendMode.REGULAR -> { - commandAutocompletePolicy.enabled = true - val uid = session.sessionParams.credentials.userId - val meMember = session.getRoom(roomId)?.getRoomMember(uid) - avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView) - composerLayout.collapse() - } - SendMode.EDIT, - SendMode.QUOTE, - SendMode.REPLY -> { - commandAutocompletePolicy.enabled = false - if (event == null) { - //we should ignore? can this happen? - Timber.e("Enter edit mode with no event selected") - return@selectSubscribe - } - //switch to expanded bar - composerLayout.composerRelatedMessageTitle.apply { - text = event.getDisambiguatedDisplayName() - setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId))) - } - - //TODO this is used at several places, find way to refactor? - val messageContent: MessageContent? = - event.annotations?.editSummary?.aggregatedContent?.toModel() - ?: event.root.getClearContent().toModel() - val nonFormattedBody = messageContent?.body ?: "" - var formattedBody: CharSequence? = null - if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) { - val parser = Parser.builder().build() - val document = parser.parse(messageContent.formattedBody - ?: messageContent.body) - formattedBody = Markwon.builder(requireContext()) - .usePlugin(HtmlPlugin.create()).build().render(document) - } - composerLayout.composerRelatedMessageContent.text = formattedBody - ?: nonFormattedBody - - - if (mode == SendMode.EDIT) { - //TODO if it's a reply we should trim the top part of message - composerLayout.composerEditText.setText(nonFormattedBody) - composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_edit)) - } else if (mode == SendMode.QUOTE) { - composerLayout.composerEditText.setText("") - composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_quote)) - } else if (mode == SendMode.REPLY) { - composerLayout.composerEditText.setText("") - composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_reply)) - } - - avatarRenderer.render(event.senderAvatar, event.root.senderId - ?: "", event.senderName, composerLayout.composerRelatedMessageAvatar) - - composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length) - composerLayout.expand { - focusComposerAndShowKeyboard() - } - composerLayout.composerRelatedMessageCloseButton.setOnClickListener { - composerLayout.composerEditText.setText("") - roomDetailViewModel.resetSendMode() - } - - } + SendMode.REGULAR -> exitSpecialMode() + is SendMode.EDIT -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_edit, true) + is SendMode.QUOTE -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_quote, false) + is SendMode.REPLY -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_reply, false) } } } + private fun exitSpecialMode() { + commandAutocompletePolicy.enabled = true + composerLayout.collapse() + } + + private fun enterSpecialMode(event: TimelineEvent, @DrawableRes iconRes: Int, useText: Boolean) { + commandAutocompletePolicy.enabled = false + //switch to expanded bar + composerLayout.composerRelatedMessageTitle.apply { + text = event.getDisambiguatedDisplayName() + setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId))) + } + + //TODO this is used at several places, find way to refactor? + val messageContent: MessageContent? = + event.annotations?.editSummary?.aggregatedContent?.toModel() + ?: event.root.getClearContent().toModel() + val nonFormattedBody = messageContent?.body ?: "" + var formattedBody: CharSequence? = null + if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) { + val parser = Parser.builder().build() + val document = parser.parse(messageContent.formattedBody + ?: messageContent.body) + formattedBody = Markwon.builder(requireContext()) + .usePlugin(HtmlPlugin.create()).build().render(document) + } + composerLayout.composerRelatedMessageContent.text = formattedBody + ?: nonFormattedBody + + composerLayout.composerEditText.setText(if (useText) nonFormattedBody else "") + composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes)) + + avatarRenderer.render(event.senderAvatar, event.root.senderId + ?: "", event.senderName, composerLayout.composerRelatedMessageAvatar) + + composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length) + composerLayout.expand { + focusComposerAndShowKeyboard() + } + } + override fun onResume() { super.onResume() @@ -422,6 +401,10 @@ class RoomDetailFragment : roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, VectorPreferences.isMarkdownEnabled(requireContext()))) } } + composerLayout.composerRelatedMessageCloseButton.setOnClickListener { + composerLayout.composerEditText.setText("") + roomDetailViewModel.resetSendMode() + } } private fun setupAttachmentButton() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 708a705c..b7240ce2 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -124,11 +124,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } - fun enterEditMode(event: TimelineEvent) { + private fun enterEditMode(event: TimelineEvent) { setState { copy( - sendMode = SendMode.EDIT, - selectedEvent = event + sendMode = SendMode.EDIT(event) ) } } @@ -136,8 +135,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro fun resetSendMode() { setState { copy( - sendMode = SendMode.REGULAR, - selectedEvent = null + sendMode = SendMode.REGULAR ) } } @@ -165,7 +163,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro private fun handleSendMessage(action: RoomDetailActions.SendMessage) { withState { state -> when (state.sendMode) { - SendMode.REGULAR -> { + SendMode.REGULAR -> { val slashCommandResult = CommandParser.parseSplashCommand(action.text) when (slashCommandResult) { @@ -231,30 +229,29 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } } - SendMode.EDIT -> { + is SendMode.EDIT -> { val messageContent: MessageContent? = - state.selectedEvent?.annotations?.editSummary?.aggregatedContent.toModel() - ?: state.selectedEvent?.root?.getClearContent().toModel() + state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() + ?: state.sendMode.timelineEvent.root.getClearContent().toModel() val nonFormattedBody = messageContent?.body ?: "" if (nonFormattedBody != action.text) { - room.editTextMessage(state.selectedEvent?.root?.eventId + room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "", action.text, action.autoMarkdown) } else { Timber.w("Same message content, do not send edition") } setState { copy( - sendMode = SendMode.REGULAR, - selectedEvent = null + sendMode = SendMode.REGULAR ) } _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) } - SendMode.QUOTE -> { + is SendMode.QUOTE -> { val messageContent: MessageContent? = - state.selectedEvent?.annotations?.editSummary?.aggregatedContent.toModel() - ?: state.selectedEvent?.root?.getClearContent().toModel() + state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() + ?: state.sendMode.timelineEvent.root.getClearContent().toModel() val textMsg = messageContent?.body val finalText = legacyRiotQuoteText(textMsg, action.text) @@ -271,19 +268,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } setState { copy( - sendMode = SendMode.REGULAR, - selectedEvent = null + sendMode = SendMode.REGULAR ) } _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) } - SendMode.REPLY -> { - state.selectedEvent?.let { + is SendMode.REPLY -> { + state.sendMode.timelineEvent.let { room.replyToMessage(it.root, action.text, action.autoMarkdown) setState { copy( - sendMode = SendMode.REGULAR, - selectedEvent = null + sendMode = SendMode.REGULAR ) } _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) @@ -434,8 +429,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro room.getTimeLineEvent(action.eventId)?.let { setState { copy( - sendMode = SendMode.QUOTE, - selectedEvent = it + sendMode = SendMode.QUOTE(it) ) } } @@ -445,8 +439,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro room.getTimeLineEvent(action.eventId)?.let { setState { copy( - sendMode = SendMode.REPLY, - selectedEvent = it + sendMode = SendMode.REPLY(it) ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt index c32a79da..63171491 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt @@ -32,11 +32,11 @@ import im.vector.matrix.android.api.session.user.model.User * * Depending on the state the bottom toolbar will change (icons/preview/actions...) */ -enum class SendMode { - REGULAR, - QUOTE, - EDIT, - REPLY +sealed class SendMode { + object REGULAR : SendMode() + data class QUOTE(val timelineEvent: TimelineEvent) : SendMode() + data class EDIT(val timelineEvent: TimelineEvent) : SendMode() + data class REPLY(val timelineEvent: TimelineEvent) : SendMode() } data class RoomDetailViewState( @@ -46,7 +46,6 @@ data class RoomDetailViewState( val asyncInviter: Async = Uninitialized, val asyncRoomSummary: Async = Uninitialized, val sendMode: SendMode = SendMode.REGULAR, - val selectedEvent: TimelineEvent? = null, val isEncrypted: Boolean = false ) : MvRxState { From 794fd650a474946b9206db19274eaf4ae257c65f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 17:37:22 +0200 Subject: [PATCH 11/13] Mutualize code, and also, when replying to an edited event, use the last text in the reply prefix content --- .../session/room/model/relation/RelationService.kt | 4 ++-- .../api/session/room/timeline/TimelineEvent.kt | 9 ++++++++- .../session/room/relation/DefaultRelationService.kt | 3 ++- .../session/room/send/LocalEchoEventFactory.kt | 12 +++++++----- .../features/home/room/detail/RoomDetailFragment.kt | 7 ++----- .../features/home/room/detail/RoomDetailViewModel.kt | 2 +- .../timeline/action/MessageActionsViewModel.kt | 5 ++--- .../detail/timeline/factory/MessageItemFactory.kt | 5 ++--- .../notifications/NotifiableEventResolver.kt | 11 +++++------ 9 files changed, 31 insertions(+), 27 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt index 7b547c48..dbd23a58 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt @@ -16,8 +16,8 @@ package im.vector.matrix.android.api.session.room.model.relation import androidx.lifecycle.LiveData -import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.util.Cancelable /** @@ -79,7 +79,7 @@ interface RelationService { * @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: Event, replyText: String, autoMarkdown: Boolean = false): Cancelable? + fun replyToMessage(eventReplied: TimelineEvent, replyText: String, autoMarkdown: Boolean = false): Cancelable? fun getEventSummaryLive(eventId: String): LiveData } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt index 1e1de79a..5ec361ad 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt @@ -16,10 +16,11 @@ package im.vector.matrix.android.api.session.room.timeline -import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary +import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.send.SendState /** @@ -81,3 +82,9 @@ data class TimelineEvent( return EventType.ENCRYPTED == root.type } } + +/** + * Get last MessageContent, after a possible edition + */ +fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSummary?.aggregatedContent?.toModel() + ?: root.getClearContent().toModel() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index 58151f18..ecbd0036 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -27,6 +27,7 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.relation.RelationService +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.RealmLiveData import im.vector.matrix.android.internal.database.helper.addSendingEvent @@ -127,7 +128,7 @@ internal class DefaultRelationService @Inject constructor(private val context: C } - override fun replyToMessage(eventReplied: Event, replyText: String, autoMarkdown: Boolean): Cancelable? { + override fun replyToMessage(eventReplied: TimelineEvent, replyText: String, 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/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index cc449018..72d6861e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -28,6 +28,8 @@ import im.vector.matrix.android.api.session.room.model.relation.ReactionContent import im.vector.matrix.android.api.session.room.model.relation.ReactionInfo import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent import im.vector.matrix.android.api.session.room.model.relation.ReplyToContent +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.internal.database.helper.addSendingEvent import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.where @@ -230,11 +232,11 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials return "$LOCAL_ID_PREFIX${UUID.randomUUID()}" } - fun createReplyTextEvent(roomId: String, eventReplied: Event, replyText: String, autoMarkdown: Boolean): Event? { + fun createReplyTextEvent(roomId: String, eventReplied: TimelineEvent, replyText: String, autoMarkdown: Boolean): Event? { //Fallbacks and event representation //TODO Add error/warning logs when any of this is null - val permalink = PermalinkFactory.createPermalink(eventReplied) ?: return null - val userId = eventReplied.senderId ?: return null + val permalink = PermalinkFactory.createPermalink(eventReplied.root) ?: return null + val userId = eventReplied.root.senderId ?: return null val userLink = PermalinkFactory.createPermalink(userId) ?: return null // //
@@ -245,7 +247,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials //
//
// This is where the reply goes. - val body = bodyForReply(eventReplied.getClearContent().toModel()) + val body = bodyForReply(eventReplied.getLastMessageContent()) val replyFormatted = REPLY_PATTERN.format( permalink, stringProvider.getString(R.string.message_reply_to_prefix), @@ -268,7 +270,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials } replyFallback.append("\n\n").append(replyText) - val eventId = eventReplied.eventId ?: return null + val eventId = eventReplied.root.eventId ?: return null val content = MessageTextContent( type = MessageType.MSGTYPE_TEXT, format = MessageType.FORMAT_MATRIX_HTML, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 62ffefea..d4d1c478 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -54,11 +54,11 @@ import com.otaliastudios.autocomplete.AutocompleteCallback import com.otaliastudios.autocomplete.CharPolicy import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.Session -import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent @@ -249,10 +249,7 @@ class RoomDetailFragment : setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId))) } - //TODO this is used at several places, find way to refactor? - val messageContent: MessageContent? = - event.annotations?.editSummary?.aggregatedContent?.toModel() - ?: event.root.getClearContent().toModel() + val messageContent: MessageContent? = event.getLastMessageContent() val nonFormattedBody = messageContent?.body ?: "" var formattedBody: CharSequence? = null if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index b7240ce2..0b698108 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -275,7 +275,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } is SendMode.REPLY -> { state.sendMode.timelineEvent.let { - room.replyToMessage(it.root, action.text, action.autoMarkdown) + room.replyToMessage(it, action.text, action.autoMarkdown) setState { copy( sendMode = SendMode.REGULAR diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 161a8cc5..6ab70a47 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -21,11 +21,11 @@ import com.squareup.inject.assisted.AssistedInject import dagger.Lazy import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.EventType -import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.rx.RxRoom import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter @@ -57,8 +57,7 @@ data class MessageActionState( fun messageBody(eventHtmlRenderer: EventHtmlRenderer?, noticeEventFormatter: NoticeEventFormatter?): CharSequence? { return when (timelineEvent()?.root?.getClearType()) { EventType.MESSAGE -> { - val messageContent: MessageContent? = timelineEvent()?.annotations?.editSummary?.aggregatedContent?.toModel() - ?: timelineEvent()?.root?.getClearContent().toModel() + val messageContent: MessageContent? = timelineEvent()?.getLastMessageContent() if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) { eventHtmlRenderer?.render(messageContent.formattedBody ?: messageContent.body) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 04ed925b..d8f1c602 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -27,11 +27,11 @@ import dagger.Lazy import im.vector.matrix.android.api.permalinks.MatrixLinkify import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan import im.vector.matrix.android.api.session.events.model.RelationType -import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt import im.vector.riotx.EmojiCompatFontProvider import im.vector.riotx.R @@ -79,8 +79,7 @@ class MessageItemFactory @Inject constructor( } val messageContent: MessageContent = - event.annotations?.editSummary?.aggregatedContent?.toModel() - ?: event.root.getClearContent().toModel() + event.getLastMessageContent() ?: //Malformed content, we should echo something on screen return DefaultItem_().text(stringProvider.getString(R.string.malformed_message)) diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt index e0076589..a98c7535 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt @@ -18,15 +18,14 @@ package im.vector.riotx.features.notifications import androidx.core.app.NotificationCompat import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.content.ContentUrlResolver -import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomMember -import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.riotx.BuildConfig import im.vector.riotx.R @@ -94,8 +93,8 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St Timber.e("## Unable to resolve room for eventId [${event}]") // Ok room is not known in store, but we can still display something val body = - event.annotations?.editSummary?.aggregatedContent?.toModel()?.body - ?: event.root.getClearContent().toModel()?.body + event.getLastMessageContent() + ?.body ?: stringProvider.getString(R.string.notification_unknown_new_event) val roomName = stringProvider.getString(R.string.notification_unknown_room_name) val senderDisplayName = event.senderName ?: event.root.senderId @@ -129,8 +128,8 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St } } - val body = event.annotations?.editSummary?.aggregatedContent?.toModel()?.body - ?: event.root.getClearContent().toModel()?.body + val body = event.getLastMessageContent() + ?.body ?: stringProvider.getString(R.string.notification_unknown_new_event) val roomName = room.roomSummary()?.displayName ?: "" val senderDisplayName = event.senderName ?: event.root.senderId From 3aa30e5f15c1eea0bd1fc6911a21693d38b8151d Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 10 Jul 2019 18:05:43 +0200 Subject: [PATCH 12/13] Fix reply of reply --- .../room/send/LocalEchoEventFactory.kt | 10 ++++-- .../internal/session/room/send/TextContent.kt | 33 +++++++++++++++++++ .../api/pushrules/PushrulesConditionTest.kt | 3 +- .../home/room/detail/RoomDetailFragment.kt | 7 ++-- .../riotx/features/html/EventHtmlRenderer.kt | 4 +++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index cc449018..85776860 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -201,7 +201,8 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials type = MessageType.MSGTYPE_FILE, body = attachment.name ?: "file", info = FileInfo( - mimeType = attachment.mimeType.takeIf { it.isNotBlank() } ?: "application/octet-stream", + mimeType = attachment.mimeType.takeIf { it.isNotBlank() } + ?: "application/octet-stream", size = attachment.size ), url = attachment.path @@ -287,14 +288,17 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials MessageType.MSGTYPE_EMOTE, MessageType.MSGTYPE_TEXT, MessageType.MSGTYPE_NOTICE -> { - //If we already have formatted body, return it? var formattedText: String? = null if (content is MessageTextContent) { if (content.format == MessageType.FORMAT_MATRIX_HTML) { formattedText = content.formattedBody } } - return TextContent(content.body, formattedText) + val isReply = content.relatesTo?.inReplyTo?.eventId != null + return if (isReply) + TextContent(content.body, formattedText).removeInReplyFallbacks() + else + TextContent(content.body, formattedText) } MessageType.MSGTYPE_FILE -> return TextContent(stringProvider.getString(R.string.reply_to_a_file)) MessageType.MSGTYPE_AUDIO -> return TextContent(stringProvider.getString(R.string.reply_to_an_audio_file)) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt index f1f29c20..9e2c785c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/TextContent.kt @@ -39,3 +39,36 @@ fun TextContent.toMessageTextContent(): MessageTextContent { formattedBody = formattedText ) } + +fun TextContent.removeInReplyFallbacks(): TextContent { + return copy( + text = extractUsefulTextFromReply(this.text), + formattedText = this.formattedText?.let { extractUsefulTextFromHtmlReply(it) } + ) +} + +private fun extractUsefulTextFromReply(repliedBody: String): String { + val lines = repliedBody.lines() + var wellFormed = repliedBody.startsWith(">") + var endOfPreviousFound = false + val usefullines = ArrayList() + lines.forEach { + if (it == "") { + endOfPreviousFound = true + return@forEach + } + if (!endOfPreviousFound) { + wellFormed = wellFormed && it.startsWith(">") + } else { + usefullines.add(it) + } + } + return usefullines.joinToString("\n").takeIf { wellFormed } ?: repliedBody +} + +private fun extractUsefulTextFromHtmlReply(repliedBody: String): String { + if (repliedBody.startsWith("")) { + return repliedBody.substring(repliedBody.lastIndexOf("") + "".length).trim() + } + return repliedBody +} diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt index 17faee35..ca3d9215 100644 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt @@ -38,7 +38,6 @@ import org.junit.Test class PushrulesConditionTest { - @Test fun test_eventmatch_type_condition() { val condition = EventMatchCondition("type", "m.room.message") @@ -286,7 +285,7 @@ class PushrulesConditionTest { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? { + override fun replyToMessage(eventReplied: Event, replyText: String, autoMarkdown: Boolean): Cancelable? { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 62ffefea..e849925b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -91,6 +91,7 @@ import im.vector.riotx.features.home.room.detail.timeline.action.MessageMenuView import im.vector.riotx.features.home.room.detail.timeline.action.ViewReactionBottomSheet import im.vector.riotx.features.home.room.detail.timeline.helper.EndlessRecyclerViewScrollListener import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData +import im.vector.riotx.features.html.EventHtmlRenderer import im.vector.riotx.features.html.PillImageSpan import im.vector.riotx.features.invite.VectorInviteView import im.vector.riotx.features.media.ImageContentRenderer @@ -105,8 +106,6 @@ import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* import kotlinx.android.synthetic.main.merge_composer_layout.view.* import org.commonmark.parser.Parser -import ru.noties.markwon.Markwon -import ru.noties.markwon.html.HtmlPlugin import timber.log.Timber import java.io.File import javax.inject.Inject @@ -179,6 +178,7 @@ class RoomDetailFragment : @Inject lateinit var errorFormatter: ErrorFormatter private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback private lateinit var scrollOnHighlightedEventCallback: ScrollOnHighlightedEventCallback + @Inject lateinit var eventHtmlRenderer: EventHtmlRenderer override fun getLayoutResId() = R.layout.fragment_room_detail @@ -259,8 +259,7 @@ class RoomDetailFragment : val parser = Parser.builder().build() val document = parser.parse(messageContent.formattedBody ?: messageContent.body) - formattedBody = Markwon.builder(requireContext()) - .usePlugin(HtmlPlugin.create()).build().render(document) + formattedBody = eventHtmlRenderer.render(document) } composerLayout.composerRelatedMessageContent.text = formattedBody ?: nonFormattedBody diff --git a/vector/src/main/java/im/vector/riotx/features/html/EventHtmlRenderer.kt b/vector/src/main/java/im/vector/riotx/features/html/EventHtmlRenderer.kt index 37cc0cc3..476a70e7 100644 --- a/vector/src/main/java/im/vector/riotx/features/html/EventHtmlRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/html/EventHtmlRenderer.kt @@ -50,6 +50,10 @@ class EventHtmlRenderer @Inject constructor(context: Context, return markwon.toMarkdown(text) } + fun render(node: Node) : CharSequence { + return markwon.render(node) + } + } private class MatrixPlugin private constructor(private val glideRequests: GlideRequests, From 7a08a11b193a78bed23056fdeccaa42072acc61e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 Jul 2019 18:17:03 +0200 Subject: [PATCH 13/13] Fix compilation of test --- .../android/api/pushrules/PushrulesConditionTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt index ca3d9215..70eb658b 100644 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt @@ -157,9 +157,9 @@ class PushrulesConditionTest { content = MessageTextContent("m.text", "A").toContent(), originServerTs = 0, roomId = "3joined").also { - Assert.assertTrue("This room has 3 members",conditionEqual3.isSatisfied(it, session)) - Assert.assertTrue("This room has 3 members",conditionEqual3Bis.isSatisfied(it, session)) - Assert.assertFalse("This room has more than 3 members",conditionLessThan3.isSatisfied(it, session)) + Assert.assertTrue("This room has 3 members", conditionEqual3.isSatisfied(it, session)) + Assert.assertTrue("This room has 3 members", conditionEqual3Bis.isSatisfied(it, session)) + Assert.assertFalse("This room has more than 3 members", conditionLessThan3.isSatisfied(it, session)) } } @@ -285,7 +285,7 @@ class PushrulesConditionTest { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun replyToMessage(eventReplied: Event, replyText: String, autoMarkdown: Boolean): Cancelable? { + override fun replyToMessage(eventReplied: TimelineEvent, replyText: String, autoMarkdown: Boolean): Cancelable? { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }