diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 6a6fadc95a..07036f4b65 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -156,11 +156,12 @@ interface SendService { /** * Redact (delete) the given event. - * @param event The event to redact - * @param reason Optional reason string + * @param event the event to redact + * @param reason optional reason string + * @param withRelations the list of relation types to redact with this event * @param additionalContent additional content to put in the event content */ - fun redactEvent(event: Event, reason: String?, additionalContent: Content? = null): Cancelable + fun redactEvent(event: Event, reason: String?, withRelations: List? = null, additionalContent: Content? = null): Cancelable /** * Schedule this message to be resent. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt index 5f645c4880..5ebaadb1b5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/RedactEventTask.kt @@ -52,7 +52,7 @@ internal class DefaultRedactEventTask @Inject constructor( txId = params.txID, roomId = params.roomId, eventId = params.eventId, - body = EventRedactBody(params.reason, withRelations) + body = EventRedactBody(params.reason, withRelations, withRelations) ) } return response.eventId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 2b7e9a04a1..fe55beb997 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -65,6 +65,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo045 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo046 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo047 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo048 +import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo049 import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import javax.inject.Inject @@ -73,7 +74,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private val normalizer: Normalizer ) : MatrixRealmMigration( dbName = "Session", - schemaVersion = 48L, + schemaVersion = 49L, ) { /** * Forces all RealmSessionStoreMigration instances to be equal. @@ -131,5 +132,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion < 46) MigrateSessionTo046(realm).perform() if (oldVersion < 47) MigrateSessionTo047(realm).perform() if (oldVersion < 48) MigrateSessionTo048(realm).perform() + if (oldVersion < 49) MigrateSessionTo049(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo049.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo049.kt new file mode 100644 index 0000000000..31a5305777 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo049.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 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.database.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields +import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo049(realm: DynamicRealm) : RealmMigrator(realm, 49) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("HomeServerCapabilitiesEntity") + ?.addField(HomeServerCapabilitiesEntityFields.CAN_REDACT_EVENT_WITH_RELATIONS, Boolean::class.java) + ?.transform { obj -> + obj.set(HomeServerCapabilitiesEntityFields.CAN_REDACT_EVENT_WITH_RELATIONS, false) + } + ?.forceRefreshOfHomeServerCapabilities() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRedactBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRedactBody.kt index 00778754e6..580ccc095e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRedactBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRedactBody.kt @@ -25,5 +25,8 @@ internal data class EventRedactBody( val reason: String? = null, @Json(name = "with_relations") - val withRelations: List? = null + val withRelations: List? = null, + + @Json(name = "org.matrix.msc3912.with_relations") + val withRelationsUnstable: List? = null, ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index 9cdbc7ff46..d29e7d8f36 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -140,11 +140,11 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } - override fun redactEvent(event: Event, reason: String?, additionalContent: Content?): Cancelable { + override fun redactEvent(event: Event, reason: String?, withRelations: List?, additionalContent: Content?): Cancelable { // TODO manage media/attachements? - val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason, additionalContent) + val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason, withRelations, additionalContent) .also { createLocalEcho(it) } - return eventSenderProcessor.postRedaction(redactionEcho, reason) + return eventSenderProcessor.postRedaction(redactionEcho, reason, withRelations) } override fun resendTextMessage(localEcho: TimelineEvent): Cancelable { 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 d974c597ac..528865910e 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 @@ -70,6 +70,7 @@ import org.matrix.android.sdk.api.util.TextContent import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory +import org.matrix.android.sdk.internal.session.room.EventRedactBody import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils import org.matrix.android.sdk.internal.util.time.Clock import java.util.UUID @@ -795,8 +796,13 @@ internal class LocalEchoEventFactory @Inject constructor( } } */ - fun createRedactEvent(roomId: String, eventId: String, reason: String?, additionalContent: Content? = null): Event { + fun createRedactEvent(roomId: String, eventId: String, reason: String?, withRelations: List? = null, additionalContent: Content? = null): Event { val localId = LocalEcho.createLocalEchoId() + val content = if (reason != null || withRelations != null) { + EventRedactBody(reason, withRelations, withRelations).toContent().plus(additionalContent.orEmpty()) + } else { + additionalContent + } return Event( roomId = roomId, originServerTs = dummyOriginServerTs(), @@ -804,7 +810,7 @@ internal class LocalEchoEventFactory @Inject constructor( eventId = localId, type = EventType.REDACTION, redacts = eventId, - content = reason?.let { mapOf("reason" to it).toContent().plus(additionalContent.orEmpty()) } ?: additionalContent, + content = content, unsignedData = UnsignedData(age = null, transactionId = localId) ) } @@ -830,10 +836,10 @@ internal class LocalEchoEventFactory @Inject constructor( createMessageEvent( roomId, textContent.toThreadTextContent( - rootThreadEventId = rootThreadEventId, - latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId), - msgType = MessageType.MSGTYPE_TEXT - ), + rootThreadEventId = rootThreadEventId, + latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId), + msgType = MessageType.MSGTYPE_TEXT + ), additionalContent, ) } else { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt index 765c282b65..b40a831af0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt @@ -19,10 +19,12 @@ import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.SessionComponent +import org.matrix.android.sdk.internal.session.room.EventRedactBody import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams @@ -43,11 +45,13 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters, ses val roomId: String, val eventId: String, val reason: String?, + val withRelations: List? = null, override val lastFailureMessage: String? = null ) : SessionWorkerParams @Inject lateinit var roomAPI: RoomAPI @Inject lateinit var globalErrorReceiver: GlobalErrorReceiver + @Inject lateinit var homeServerCapabilitiesService: HomeServerCapabilitiesService override fun injectWith(injector: SessionComponent) { injector.inject(this) @@ -55,13 +59,20 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters, ses override suspend fun doSafeWork(params: Params): Result { val eventId = params.eventId + val withRelations = if (homeServerCapabilitiesService.getHomeServerCapabilities().canRedactEventWithRelations && + !params.withRelations.isNullOrEmpty()) { + params.withRelations + } else { + null + } + return runCatching { executeRequest(globalErrorReceiver) { roomAPI.redactEvent( params.txID, params.roomId, eventId, - if (params.reason == null) emptyMap() else mapOf("reason" to params.reason) + EventRedactBody(params.reason, withRelations, withRelations) ) } }.fold( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index b1a37d9824..b285e90c9a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -26,9 +26,9 @@ internal interface EventSenderProcessor : SessionLifecycleObserver { fun postEvent(event: Event, encrypt: Boolean): Cancelable - fun postRedaction(redactionLocalEcho: Event, reason: String?, withRelations: List?): Cancelable + fun postRedaction(redactionLocalEcho: Event, reason: String?, withRelations: List? = null): Cancelable - fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?, withRelations: List?): Cancelable + fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?, withRelations: List? = null): Cancelable fun postTask(task: QueuedTask): Cancelable diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt index 0eedd4bd4d..bf60a9ae06 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt @@ -19,8 +19,10 @@ package org.matrix.android.sdk.internal.session.room.send.queue import android.content.Context import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.crypto.CryptoService +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.di.SessionId +import org.matrix.android.sdk.internal.session.room.EventRedactBody import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import timber.log.Timber import javax.inject.Inject @@ -107,10 +109,18 @@ internal class QueueMemento @Inject constructor( info.redactionLocalEcho?.let { localEchoRepository.getUpToDateEcho(it) }?.let { localEchoRepository.updateSendState(it.eventId!!, it.roomId, SendState.UNSENT) // try to get reason - val reason = it.content?.get("reason") as? String + val body = it.content.toModel() if (it.redacts != null && it.roomId != null) { Timber.d("## Send -Reschedule redact $info") - eventProcessor.postTask(queuedTaskFactory.createRedactTask(it.eventId, it.redacts, it.roomId, reason)) + eventProcessor.postTask( + queuedTaskFactory.createRedactTask( + redactionLocalEcho = it.eventId, + eventId = it.redacts, + roomId = it.roomId, + reason = body?.reason, + withRelations = body?.withRelations ?: body?.withRelationsUnstable + ) + ) } } // postTask(queuedTaskFactory.createRedactTask(info.eventToRedactId, info.) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTaskFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTaskFactory.kt index 973f70cdcf..46df7e29f3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTaskFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTaskFactory.kt @@ -43,7 +43,7 @@ internal class QueuedTaskFactory @Inject constructor( ) } - fun createRedactTask(redactionLocalEcho: String, eventId: String, roomId: String, reason: String?, withRelations: List?): QueuedTask { + fun createRedactTask(redactionLocalEcho: String, eventId: String, roomId: String, reason: String?, withRelations: List? = null): QueuedTask { return RedactQueuedTask( redactionLocalEchoId = redactionLocalEcho, toRedactEventId = eventId,