From 4a4c0a3da1d8768e5046166ad1f0af19bc73675a Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 27 May 2019 18:08:29 +0200 Subject: [PATCH] Added auto markdown (as per preference) Fix / show formatted message preview upon composer in edit/quote/reply Fix / use aggregated content to decide for actions on long click --- matrix-sdk-android/build.gradle | 3 ++ .../room/model/annotation/RelationService.kt | 2 +- .../api/session/room/send/SendService.kt | 2 +- .../room/annotation/DefaultRelationService.kt | 4 +- .../session/room/send/DefaultSendService.kt | 4 +- .../room/send/LocalEchoEventFactory.kt | 41 ++++++++++++++++--- .../home/room/detail/RoomDetailActions.kt | 2 +- .../home/room/detail/RoomDetailFragment.kt | 20 +++++++-- .../home/room/detail/RoomDetailViewModel.kt | 4 +- .../timeline/action/MessageMenuViewModel.kt | 3 +- 10 files changed, 65 insertions(+), 20 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index c04907ab..5211a223 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -91,6 +91,7 @@ dependencies { def moshi_version = '1.8.0' def lifecycle_version = '2.0.0' def coroutines_version = "1.0.1" + def markwon_version = '3.0.0-SNAPSHOT' implementation fileTree(dir: 'libs', include: ['*.aar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -112,6 +113,8 @@ dependencies { implementation "com.squareup.moshi:moshi-adapters:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" + implementation "ru.noties.markwon:core:$markwon_version" + // Database implementation 'com.github.Zhuinden:realm-monarchy:0.5.1' kapt 'dk.ilios:realmfieldnameshelper:1.1.1' diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/RelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/RelationService.kt index 5185babf..5838aef7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/annotation/RelationService.kt @@ -60,7 +60,7 @@ interface RelationService { * @param newBodyText The edited body * @param compatibilityBodyText The text that will appear on clients that don't support yet edition */ - fun editTextMessage(targetEventId: String, newBodyText: String, compatibilityBodyText: String = "* $newBodyText"): Cancelable + fun editTextMessage(targetEventId: String, newBodyText: String, newBodyAutoMarkdown: Boolean, compatibilityBodyText: String = "* $newBodyText"): Cancelable fun replyToMessage(eventReplied: Event, replyText: String) : Cancelable? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt index 551ab545..f6f4708a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt @@ -33,7 +33,7 @@ interface SendService { * @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE * @return a [Cancelable] */ - fun sendTextMessage(text: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable + fun sendTextMessage(text: String, msgType: String = MessageType.MSGTYPE_TEXT, autoMarkdown: Boolean = false): Cancelable fun sendFormattedTextMessage(text: String,formattedText: String): Cancelable /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultRelationService.kt index ec36f683..5cd1873c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/annotation/DefaultRelationService.kt @@ -160,8 +160,8 @@ internal class DefaultRelationService(private val roomId: String, .build() } - override fun editTextMessage(targetEventId: String, newBodyText: String, compatibilityBodyText: String): Cancelable { - val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, MessageType.MSGTYPE_TEXT, compatibilityBodyText) + override fun editTextMessage(targetEventId: String, newBodyText: String, newBodyAutoMarkdown: Boolean, compatibilityBodyText: String): Cancelable { + val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, MessageType.MSGTYPE_TEXT, compatibilityBodyText) val sendContentWorkerParams = SendEventWorker.Params(roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) 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 1d6197a3..6b16ffaf 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 @@ -49,8 +49,8 @@ internal class DefaultSendService(private val roomId: String, : SendService { - override fun sendTextMessage(text: String, msgType: String): Cancelable { - val event = eventFactory.createTextEvent(roomId, msgType, text).also { + override fun sendTextMessage(text: String, msgType: String, autoMarkdown: Boolean): Cancelable { + val event = eventFactory.createTextEvent(roomId, msgType, text, autoMarkdown).also { saveLocalEcho(it) } val sendWork = createSendEventWork(event) 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 6b1cdb11..a7b261de 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 @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.session.room.send import android.media.MediaMetadataRetriever +import android.text.TextUtils import im.vector.matrix.android.R import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.permalinks.PermalinkFactory @@ -29,10 +30,21 @@ import im.vector.matrix.android.api.session.room.model.annotation.ReplyToContent import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.internal.session.content.ThumbnailExtractor import im.vector.matrix.android.internal.util.StringProvider +import org.commonmark.parser.Parser +import org.commonmark.renderer.html.HtmlRenderer internal class LocalEchoEventFactory(private val credentials: Credentials, private val stringProvider: StringProvider) { - fun createTextEvent(roomId: String, msgType: String, text: String): Event { + 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 (!TextUtils.equals(text, htmlText)) { + return createFormattedTextEvent(roomId, text, htmlText) + } + } val content = MessageTextContent(type = msgType, body = text) return createEvent(roomId, content) } @@ -48,15 +60,32 @@ internal class LocalEchoEventFactory(private val credentials: Credentials, priva } - fun createReplaceTextEvent(roomId: String, targetEventId: String, newBodyText: String, msgType: String, compatibilityText: String): Event { + fun createReplaceTextEvent(roomId: String, targetEventId: String, newBodyText: String, newBodyAutoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { + + var newContent = MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + body = newBodyText + ) + if (newBodyAutoMarkdown) { + val parser = Parser.builder().build() + val document = parser.parse(newBodyText) + val renderer = HtmlRenderer.builder().build() + val htmlText = renderer.render(document) + if (!TextUtils.equals(newBodyText, htmlText)) { + newContent = MessageTextContent( + type = MessageType.MSGTYPE_TEXT, + format = MessageType.FORMAT_MATRIX_HTML, + body = newBodyText, + formattedBody = htmlText + ) + } + } + val content = MessageTextContent( type = msgType, body = compatibilityText, relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId), - newContent = MessageTextContent( - type = MessageType.MSGTYPE_TEXT, - body = newBodyText - ).toContent() + newContent = newContent.toContent() ) return createEvent(roomId, content) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt index 7835c2da..3ab393a0 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActions.kt @@ -23,7 +23,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent sealed class RoomDetailActions { - data class SendMessage(val text: String) : RoomDetailActions() + data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions() data class SendMedia(val mediaFiles: List) : RoomDetailActions() object IsDisplayed : RoomDetailActions() data class EventDisplayed(val event: TimelineEvent) : RoomDetailActions() diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 4b2a010d..ef930771 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -93,13 +93,17 @@ import im.vector.riotredesign.features.media.ImageMediaViewerActivity import im.vector.riotredesign.features.media.VideoContentRenderer import im.vector.riotredesign.features.media.VideoMediaViewerActivity import im.vector.riotredesign.features.reactions.EmojiReactionPickerActivity +import im.vector.riotredesign.features.settings.PreferencesManager 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 org.koin.android.ext.android.inject import org.koin.android.scope.ext.android.bindScope import org.koin.android.scope.ext.android.getOrCreateScope import org.koin.core.parameter.parametersOf +import ru.noties.markwon.Markwon +import ru.noties.markwon.html.HtmlPlugin import timber.log.Timber import java.io.File @@ -227,12 +231,20 @@ class RoomDetailFragment : val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel() ?: event.root.content.toModel() - val eventTextBody = messageContent?.body - composerLayout.composerRelatedMessageContent.text = eventTextBody + 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) { - composerLayout.composerEditText.setText(eventTextBody) + //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("") @@ -378,7 +390,7 @@ class RoomDetailFragment : composerLayout.sendButton.setOnClickListener { val textMessage = composerLayout.composerEditText.text.toString() if (textMessage.isNotBlank()) { - roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage)) + roomDetailViewModel.process(RoomDetailActions.SendMessage(textMessage, PreferencesManager.isMarkdownEnabled(requireContext()))) } } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 40bb323d..bbb1baa2 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -138,7 +138,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, when (slashCommandResult) { is ParsedCommand.ErrorNotACommand -> { // Send the text message to the room - room.sendTextMessage(action.text) + room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown) _sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.MessageSent)) } is ParsedCommand.ErrorSyntax -> { @@ -199,7 +199,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, } } SendMode.EDIT -> { - room.editTextMessage(state?.selectedEvent?.root?.eventId ?: "", action.text) + room.editTextMessage(state.selectedEvent?.root?.eventId ?: "", action.text, action.autoMarkdown) setState { copy( sendMode = SendMode.REGULAR, diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt index d225b510..86ddf866 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt @@ -50,7 +50,8 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel