diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt index e49b1f0a73..a9c5ba19a8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt @@ -109,10 +109,15 @@ interface RelationService { * @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 + * @param showInThread If true, relation will be added to the reply in order to be visible from within threads + * @param rootThreadEventId If show in thread is true then we need the rootThreadEventId to generate the relation */ fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, - autoMarkdown: Boolean = false): Cancelable? + autoMarkdown: Boolean = false, + showInThread: Boolean = false, + rootThreadEventId: String? = null + ): Cancelable? /** * Get the current EventAnnotationsSummary diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt index b0cbc607ca..806b07316d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt @@ -132,6 +132,9 @@ internal fun TimelineEventEntity.Companion.findAllThreadsForRoomId(realm: Realm, .equalTo(TimelineEventEntityFields.ROOT.IS_ROOT_THREAD, true) .sort("${TimelineEventEntityFields.ROOT.THREAD_SUMMARY_LATEST_MESSAGE}.${TimelineEventEntityFields.ROOT.ORIGIN_SERVER_TS}", Sort.DESCENDING) +/** + * Map each timelineEvent with the equivalent decrypted text edition/replacement for root threads + */ internal fun List.mapEventsWithEdition(realm: Realm, roomId: String): List = this.map { EventAnnotationsSummaryEntity diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index 08154b9cbd..ac5e90733b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -137,12 +137,20 @@ internal class DefaultRelationService @AssistedInject constructor( return fetchEditHistoryTask.execute(FetchEditHistoryTask.Params(roomId, eventId)) } - override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Cancelable? { + override fun replyToMessage( + eventReplied: TimelineEvent, + replyText: CharSequence, + autoMarkdown: Boolean, + showInThread: Boolean, + rootThreadEventId: String? + ): Cancelable? { val event = eventFactory.createReplyTextEvent( roomId = roomId, eventReplied = eventReplied, replyText = replyText, - autoMarkdown = autoMarkdown) + autoMarkdown = autoMarkdown, + rootThreadEventId = rootThreadEventId, + showInThread = showInThread) ?.also { saveLocalEcho(it) } ?: return null @@ -182,7 +190,9 @@ internal class DefaultRelationService @AssistedInject constructor( eventReplied = eventReplied, replyText = replyInThreadText, autoMarkdown = autoMarkdown, - rootThreadEventId = rootThreadEventId) + rootThreadEventId = rootThreadEventId, + showInThread = false + ) ?.also { saveLocalEcho(it) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt index 7e3d7dfde8..ea94b93767 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt @@ -71,7 +71,9 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor: roomId = roomId, eventReplied = originalTimelineEvent, replyText = newBodyText, - autoMarkdown = false)?.copy( + autoMarkdown = false, + showInThread = false + )?.copy( eventId = replyToEdit.eventId ) ?: return NoOpCancellable updateFailedEchoWithEvent(roomId, replyToEdit.eventId, editedEvent) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 29ab2c7f0b..17c396b724 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -469,7 +469,8 @@ internal class LocalEchoEventFactory @Inject constructor( eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean, - rootThreadEventId: String? = null): Event? { + rootThreadEventId: String? = null, + showInThread: Boolean): Event? { // Fallbacks and event representation // TODO Add error/warning logs when any of this is null val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null @@ -500,7 +501,10 @@ internal class LocalEchoEventFactory @Inject constructor( format = MessageFormat.FORMAT_MATRIX_HTML, body = replyFallback, formattedBody = replyFormatted, - relatesTo = generateReplyRelationContent(eventId = eventId, rootThreadEventId = rootThreadEventId)) + relatesTo = generateReplyRelationContent( + eventId = eventId, + rootThreadEventId = rootThreadEventId, + showAsReply = showInThread )) return createMessageEvent(roomId, content) } @@ -516,12 +520,12 @@ internal class LocalEchoEventFactory @Inject constructor( * } * } */ - private fun generateReplyRelationContent(eventId: String, rootThreadEventId: String? = null): RelationDefaultContent = + private fun generateReplyRelationContent(eventId: String, rootThreadEventId: String? = null, showAsReply: Boolean): RelationDefaultContent = rootThreadEventId?.let { RelationDefaultContent( type = RelationType.IO_THREAD, eventId = it, - inReplyTo = ReplyToContent(eventId = eventId, renderIn = arrayListOf("m.thread"))) + inReplyTo = ReplyToContent(eventId = eventId, renderIn = if (showAsReply) arrayListOf("m.thread") else null)) } ?: RelationDefaultContent(null, null, ReplyToContent(eventId = eventId)) private fun buildFormattedReply(permalink: String, userLink: String, userId: String, bodyFormatted: String, newBodyFormatted: String): String { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index 39c43f9709..766417c320 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -26,6 +26,7 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.features.attachments.toContentAttachmentData import im.vector.app.features.command.CommandParser import im.vector.app.features.command.ParsedCommand @@ -44,6 +45,8 @@ import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId +import org.matrix.android.sdk.api.session.events.model.isThread import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent @@ -495,13 +498,21 @@ class MessageComposerViewModel @AssistedInject constructor( } is SendMode.Reply -> { val timelineEvent = state.sendMode.timelineEvent + val showInThread = state.sendMode.timelineEvent.root.isThread() && state.rootThreadEventId == null + val rootThreadEventId = if(showInThread) timelineEvent.root.getRootThreadEventId() else null state.rootThreadEventId?.let { room.replyInThread( rootThreadEventId = it, replyInThreadText = action.text.toString(), autoMarkdown = action.autoMarkdown, eventReplied = timelineEvent) - } ?: room.replyToMessage(timelineEvent, action.text.toString(), action.autoMarkdown) + } ?: room.replyToMessage( + eventReplied = timelineEvent, + replyText = action.text.toString(), + autoMarkdown = action.autoMarkdown, + showInThread = showInThread, + rootThreadEventId = rootThreadEventId + ) _viewEvents.post(MessageComposerViewEvents.MessageSent) popDraft()