Message state: allow edit and copy when failed

This commit is contained in:
ganfra 2021-03-10 18:10:03 +01:00
parent bbb5dd06a2
commit 6c8e2f1fd6
6 changed files with 115 additions and 31 deletions

View File

@ -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,

View File

@ -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<SendEventTask.Params, String> {
@ -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<SendResponse>(globalErrorReceiver) {
apiCall = roomAPI.send(

View File

@ -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<Event> {

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -59,5 +59,4 @@ data class MessageActionState(
fun canReact() = timelineEvent()?.canReact() == true && actionPermissions.canReact
fun sendState(): SendState? = timelineEvent()?.root?.sendState
}