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 2c0e378efb..59d84ef40f 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 @@ -66,11 +66,11 @@ interface RelationService { /** * Edit a text message body. Limited to "m.text" contentType - * @param targetEventId The event to edit + * @param targetEvent The event to edit * @param newBodyText The edited body * @param compatibilityBodyText The text that will appear on clients that don't support yet edition */ - fun editTextMessage(targetEventId: String, + fun editTextMessage(targetEvent: TimelineEvent, msgType: String, newBodyText: CharSequence, newBodyAutoMarkdown: Boolean, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt index 04a4e3e52d..573f2c3a54 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt @@ -15,10 +15,7 @@ */ package org.matrix.android.sdk.internal.crypto.tasks -import kotlinx.coroutines.delay -import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest @@ -27,7 +24,6 @@ import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTa import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import org.matrix.android.sdk.internal.session.room.send.SendResponse import org.matrix.android.sdk.internal.task.Task -import java.lang.IllegalStateException import javax.inject.Inject internal interface SendEventTask : Task { @@ -55,9 +51,6 @@ internal class DefaultSendEventTask @Inject constructor( val event = handleEncryption(params) val localId = event.eventId!! - if((event.content?.get("body") as? String)?.contains("Fail").orFalse()){ - throw IllegalStateException() - } localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENDING) val executeRequest = executeRequest(globalErrorReceiver) { apiCall = roomAPI.send( 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 da0cf45946..778550a7c4 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 @@ -17,14 +17,13 @@ package org.matrix.android.sdk.internal.session.room.relation import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import dagger.assisted.AssistedFactory import com.zhuinden.monarchy.Monarchy +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary -import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.relation.RelationService import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.util.Cancelable @@ -39,6 +38,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith @@ -47,6 +47,7 @@ import timber.log.Timber internal class DefaultRelationService @AssistedInject constructor( @Assisted private val roomId: String, + private val eventEditor: EventEditor, private val eventSenderProcessor: EventSenderProcessor, private val eventFactory: LocalEchoEventFactory, private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, @@ -112,32 +113,19 @@ internal class DefaultRelationService @AssistedInject constructor( .executeBy(taskExecutor) } - override fun editTextMessage(targetEventId: String, + override fun editTextMessage(targetEvent: TimelineEvent, msgType: String, newBodyText: CharSequence, newBodyAutoMarkdown: Boolean, compatibilityBodyText: String): Cancelable { - val event = eventFactory - .createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, msgType, compatibilityBodyText) - .also { saveLocalEcho(it) } - return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + return eventEditor.editTextMessage(targetEvent, msgType, newBodyText, newBodyAutoMarkdown, compatibilityBodyText) } override fun editReply(replyToEdit: TimelineEvent, originalTimelineEvent: TimelineEvent, newBodyText: String, compatibilityBodyText: String): Cancelable { - val event = eventFactory.createReplaceTextOfReply( - roomId, - replyToEdit, - originalTimelineEvent, - newBodyText, - true, - MessageType.MSGTYPE_TEXT, - compatibilityBodyText - ) - .also { saveLocalEcho(it) } - return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + return eventEditor.editReply(replyToEdit, originalTimelineEvent, newBodyText, compatibilityBodyText) } override suspend fun fetchEditHistory(eventId: String): List { 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 new file mode 100644 index 0000000000..5fe06287d2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.relation + +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.api.util.NoOpCancellable +import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider +import org.matrix.android.sdk.internal.database.mapper.toEntity +import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import timber.log.Timber +import javax.inject.Inject + +internal class EventEditor @Inject constructor(private val eventSenderProcessor: EventSenderProcessor, + private val eventFactory: LocalEchoEventFactory, + private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, + private val localEchoRepository: LocalEchoRepository) { + + fun editTextMessage(targetEvent: TimelineEvent, + msgType: String, + newBodyText: CharSequence, + newBodyAutoMarkdown: Boolean, + compatibilityBodyText: String): Cancelable { + val roomId = targetEvent.roomId + if (targetEvent.root.sendState.hasFailed()) { + // We create a new in memory event for the EventSenderProcessor but we keep the eventId of the failed event. + val editedEvent = eventFactory.createTextEvent(roomId, msgType, newBodyText, newBodyAutoMarkdown).copy( + eventId = targetEvent.eventId + ) + updateFailedEchoWithEvent(roomId, targetEvent.eventId, editedEvent) + return eventSenderProcessor.postEvent(editedEvent, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else if (targetEvent.root.sendState.isSent()) { + val event = eventFactory + .createReplaceTextEvent(roomId, targetEvent.eventId, newBodyText, newBodyAutoMarkdown, msgType, compatibilityBodyText) + .also { localEchoRepository.createLocalEcho(it) } + return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else { + // Should we throw? + Timber.w("Can't edit a sending event") + return NoOpCancellable + } + } + + fun editReply(replyToEdit: TimelineEvent, + originalTimelineEvent: TimelineEvent, + newBodyText: String, + compatibilityBodyText: String): Cancelable { + val roomId = replyToEdit.roomId + if (replyToEdit.root.sendState.hasFailed()) { + // We create a new in memory event for the EventSenderProcessor but we keep the eventId of the failed event. + val editedEvent = eventFactory.createReplyTextEvent(roomId, originalTimelineEvent, newBodyText, false)?.copy( + eventId = replyToEdit.eventId + ) ?: return NoOpCancellable + updateFailedEchoWithEvent(roomId, replyToEdit.eventId, editedEvent) + return eventSenderProcessor.postEvent(editedEvent, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else if (replyToEdit.root.sendState.isSent()) { + val event = eventFactory.createReplaceTextOfReply( + roomId, + replyToEdit, + originalTimelineEvent, + newBodyText, + true, + MessageType.MSGTYPE_TEXT, + compatibilityBodyText + ) + .also { localEchoRepository.createLocalEcho(it) } + return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId)) + } else { + // Should we throw? + Timber.w("Can't edit a sending event") + return NoOpCancellable + } + } + + private fun updateFailedEchoWithEvent(roomId: String, failedEchoEventId: String, editedEvent: Event) { + val editedEventEntity = editedEvent.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis()) + localEchoRepository.updateEchoAsync(failedEchoEventId) { _, entity -> + entity.content = editedEventEntity.content + entity.ageLocalTs = editedEventEntity.ageLocalTs + entity.age = editedEventEntity.age + entity.originServerTs = editedEventEntity.originServerTs + entity.sendState = editedEventEntity.sendState + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index fdb7717e49..115b0b29dd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -828,7 +828,7 @@ class RoomDetailViewModel @AssistedInject constructor( val messageContent = state.sendMode.timelineEvent.getLastMessageContent() val existingBody = messageContent?.body ?: "" if (existingBody != action.text) { - room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "", + room.editTextMessage(state.sendMode.timelineEvent, messageContent?.msgType ?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt index b11be0fe77..caee485aba 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt @@ -59,5 +59,4 @@ data class MessageActionState( fun canReact() = timelineEvent()?.canReact() == true && actionPermissions.canReact fun sendState(): SendState? = timelineEvent()?.root?.sendState - }