Merge pull request #7988 from vector-im/yostyle/msc3912

Implement MSC3912 to delete server side all the data of a deleted voice broadcast
This commit is contained in:
Benoit Marty 2023-01-25 10:32:21 +01:00 committed by GitHub
commit c802e2d0f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 221 additions and 72 deletions

1
changelog.d/7967.feature Normal file
View File

@ -0,0 +1 @@
[Voice Broadcast] Use MSC3912 to delete server side all the related events

1
changelog.d/7988.sdk Normal file
View File

@ -0,0 +1 @@
Implement [MSC3912](https://github.com/matrix-org/matrix-spec-proposals/pull/3912): Relation-based redactions

View File

@ -75,6 +75,11 @@ data class HomeServerCapabilities(
* True if the home server supports remote toggle of Pusher for a given device.
*/
val canRemotelyTogglePushNotificationsOfDevices: Boolean = false,
/**
* True if the home server supports event redaction with relations.
*/
var canRedactEventWithRelations: Boolean = false,
) {
enum class RoomCapabilitySupport {

View File

@ -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<String>? = null, additionalContent: Content? = null): Cancelable
/**
* Schedule this message to be resent.

View File

@ -58,6 +58,8 @@ private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771"
private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773"
private const val FEATURE_REMOTE_TOGGLE_PUSH_NOTIFICATIONS_MSC3881 = "org.matrix.msc3881"
private const val FEATURE_EVENT_REDACTION_WITH_RELATIONS = "org.matrix.msc3912"
private const val FEATURE_EVENT_REDACTION_WITH_RELATIONS_STABLE = "org.matrix.msc3912.stable"
/**
* Return true if the SDK supports this homeserver version.
@ -153,3 +155,13 @@ private fun Versions.getMaxVersion(): HomeServerVersion {
internal fun Versions.doesServerSupportRemoteToggleOfPushNotifications(): Boolean {
return unstableFeatures?.get(FEATURE_REMOTE_TOGGLE_PUSH_NOTIFICATIONS_MSC3881).orFalse()
}
/**
* Indicate if the server supports MSC3912: https://github.com/matrix-org/matrix-spec-proposals/pull/3912.
*
* @return true if event redaction with relations is supported
*/
internal fun Versions.doesServerSupportRedactEventWithRelations(): Boolean {
return unstableFeatures?.get(FEATURE_EVENT_REDACTION_WITH_RELATIONS).orFalse() ||
unstableFeatures?.get(FEATURE_EVENT_REDACTION_WITH_RELATIONS_STABLE).orFalse()
}

View File

@ -15,9 +15,12 @@
*/
package org.matrix.android.sdk.internal.crypto.tasks
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.session.room.send.model.EventRedactBody
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
@ -26,22 +29,34 @@ internal interface RedactEventTask : Task<RedactEventTask.Params, String> {
val txID: String,
val roomId: String,
val eventId: String,
val reason: String?
val reason: String?,
val withRelations: List<String>?,
)
}
internal class DefaultRedactEventTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver
private val globalErrorReceiver: GlobalErrorReceiver,
private val homeServerCapabilitiesDataSource: HomeServerCapabilitiesDataSource,
) : RedactEventTask {
override suspend fun execute(params: RedactEventTask.Params): String {
val withRelations = if (homeServerCapabilitiesDataSource.getHomeServerCapabilities()?.canRedactEventWithRelations.orFalse() &&
!params.withRelations.isNullOrEmpty()) {
params.withRelations
} else {
null
}
val response = executeRequest(globalErrorReceiver) {
roomAPI.redactEvent(
txId = params.txID,
roomId = params.roomId,
eventId = params.eventId,
reason = if (params.reason == null) emptyMap() else mapOf("reason" to params.reason)
body = EventRedactBody(
reason = params.reason,
withRelations = withRelations,
)
)
}
return response.eventId

View File

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

View File

@ -47,6 +47,7 @@ internal object HomeServerCapabilitiesMapper {
canLoginWithQrCode = entity.canLoginWithQrCode,
canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications,
canRemotelyTogglePushNotificationsOfDevices = entity.canRemotelyTogglePushNotificationsOfDevices,
canRedactEventWithRelations = entity.canRedactEventWithRelations,
)
}

View File

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

View File

@ -34,6 +34,7 @@ internal open class HomeServerCapabilitiesEntity(
var canLoginWithQrCode: Boolean = false,
var canUseThreadReadReceiptsAndNotifications: Boolean = false,
var canRemotelyTogglePushNotificationsOfDevices: Boolean = false,
var canRedactEventWithRelations: Boolean = false,
) : RealmObject() {
companion object

View File

@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.auth.version.Versions
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
import org.matrix.android.sdk.internal.auth.version.doesServerSupportRedactEventWithRelations
import org.matrix.android.sdk.internal.auth.version.doesServerSupportRemoteToggleOfPushNotifications
import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreadUnreadNotifications
import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads
@ -154,6 +155,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
getVersionResult.doesServerSupportQrCodeLogin()
homeServerCapabilitiesEntity.canRemotelyTogglePushNotificationsOfDevices =
getVersionResult.doesServerSupportRemoteToggleOfPushNotifications()
homeServerCapabilitiesEntity.canRedactEventWithRelations =
getVersionResult.doesServerSupportRedactEventWithRelations()
}
if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {

View File

@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
import org.matrix.android.sdk.internal.session.room.relation.threads.ThreadSummariesResponse
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
import org.matrix.android.sdk.internal.session.room.send.SendResponse
import org.matrix.android.sdk.internal.session.room.send.model.EventRedactBody
import org.matrix.android.sdk.internal.session.room.tags.TagBody
import org.matrix.android.sdk.internal.session.room.timeline.EventContextResponse
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
@ -61,7 +62,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "publicRooms")
suspend fun publicRooms(
@Query("server") server: String?,
@Body publicRoomsParams: PublicRoomsParams
@Body publicRoomsParams: PublicRoomsParams,
): PublicRoomsResponse
/**
@ -91,7 +92,7 @@ internal interface RoomAPI {
@Query("from") from: String,
@Query("dir") dir: String,
@Query("limit") limit: Int?,
@Query("filter") filter: String?
@Query("filter") filter: String?,
): PaginationResponse
/**
@ -107,7 +108,7 @@ internal interface RoomAPI {
@Path("roomId") roomId: String,
@Query("at") syncToken: String?,
@Query("membership") membership: Membership?,
@Query("not_membership") notMembership: Membership?
@Query("not_membership") notMembership: Membership?,
): RoomMembersResponse
/**
@ -123,7 +124,7 @@ internal interface RoomAPI {
@Path("txId") txId: String,
@Path("roomId") roomId: String,
@Path("eventType") eventType: String,
@Body content: Content?
@Body content: Content?,
): SendResponse
/**
@ -139,7 +140,7 @@ internal interface RoomAPI {
@Path("roomId") roomId: String,
@Path("eventId") eventId: String,
@Query("limit") limit: Int,
@Query("filter") filter: String? = null
@Query("filter") filter: String? = null,
): EventContextResponse
/**
@ -151,7 +152,7 @@ internal interface RoomAPI {
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/event/{eventId}")
suspend fun getEvent(
@Path("roomId") roomId: String,
@Path("eventId") eventId: String
@Path("eventId") eventId: String,
): Event
/**
@ -163,7 +164,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/read_markers")
suspend fun sendReadMarker(
@Path("roomId") roomId: String,
@Body markers: Map<String, String>
@Body markers: Map<String, String>,
)
/**
@ -174,7 +175,7 @@ internal interface RoomAPI {
@Path("roomId") roomId: String,
@Path("receiptType") receiptType: String,
@Path("eventId") eventId: String,
@Body body: ReadBody
@Body body: ReadBody,
)
/**
@ -187,7 +188,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/invite")
suspend fun invite(
@Path("roomId") roomId: String,
@Body body: InviteBody
@Body body: InviteBody,
)
/**
@ -199,7 +200,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/invite")
suspend fun invite3pid(
@Path("roomId") roomId: String,
@Body body: ThreePidInviteBody
@Body body: ThreePidInviteBody,
)
/**
@ -213,7 +214,7 @@ internal interface RoomAPI {
suspend fun sendStateEvent(
@Path("roomId") roomId: String,
@Path("state_event_type") stateEventType: String,
@Body params: JsonDict
@Body params: JsonDict,
): SendResponse
/**
@ -229,7 +230,7 @@ internal interface RoomAPI {
@Path("roomId") roomId: String,
@Path("state_event_type") stateEventType: String,
@Path("state_key") stateKey: String,
@Body params: JsonDict
@Body params: JsonDict,
): SendResponse
/**
@ -257,7 +258,7 @@ internal interface RoomAPI {
@Path("eventType") eventType: String,
@Query("from") from: String? = null,
@Query("to") to: String? = null,
@Query("limit") limit: Int? = null
@Query("limit") limit: Int? = null,
): RelationsResponse
/**
@ -277,7 +278,7 @@ internal interface RoomAPI {
@Path("relationType") relationType: String,
@Query("from") from: String? = null,
@Query("to") to: String? = null,
@Query("limit") limit: Int? = null
@Query("limit") limit: Int? = null,
): RelationsResponse
/**
@ -291,7 +292,7 @@ internal interface RoomAPI {
suspend fun join(
@Path("roomIdOrAlias") roomIdOrAlias: String,
@Query("server_name") viaServers: List<String>,
@Body params: JsonDict
@Body params: JsonDict,
): JoinRoomResponse
/**
@ -303,7 +304,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/leave")
suspend fun leave(
@Path("roomId") roomId: String,
@Body params: Map<String, String?>
@Body params: Map<String, String?>,
)
/**
@ -315,7 +316,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/ban")
suspend fun ban(
@Path("roomId") roomId: String,
@Body userIdAndReason: UserIdAndReason
@Body userIdAndReason: UserIdAndReason,
)
/**
@ -327,7 +328,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/unban")
suspend fun unban(
@Path("roomId") roomId: String,
@Body userIdAndReason: UserIdAndReason
@Body userIdAndReason: UserIdAndReason,
)
/**
@ -339,7 +340,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/kick")
suspend fun kick(
@Path("roomId") roomId: String,
@Body userIdAndReason: UserIdAndReason
@Body userIdAndReason: UserIdAndReason,
)
/**
@ -350,14 +351,14 @@ internal interface RoomAPI {
* @param txId the transaction Id
* @param roomId the room id
* @param eventId the event to delete
* @param reason json containing reason key {"reason": "Indecent material"}
* @param body body containing reason key {"reason": "Indecent material"} and with_relations
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/redact/{eventId}/{txnId}")
suspend fun redactEvent(
@Path("txnId") txId: String,
@Path("roomId") roomId: String,
@Path("eventId") eventId: String,
@Body reason: Map<String, String>
@Body body: EventRedactBody,
): SendResponse
/**
@ -371,7 +372,7 @@ internal interface RoomAPI {
suspend fun reportContent(
@Path("roomId") roomId: String,
@Path("eventId") eventId: String,
@Body body: ReportContentBody
@Body body: ReportContentBody,
)
/**
@ -388,7 +389,7 @@ internal interface RoomAPI {
suspend fun sendTypingState(
@Path("roomId") roomId: String,
@Path("userId") userId: String,
@Body body: TypingBody
@Body body: TypingBody,
)
/*
@ -403,7 +404,7 @@ internal interface RoomAPI {
@Path("userId") userId: String,
@Path("roomId") roomId: String,
@Path("tag") tag: String,
@Body body: TagBody
@Body body: TagBody,
)
/**
@ -413,7 +414,7 @@ internal interface RoomAPI {
suspend fun deleteTag(
@Path("userId") userId: String,
@Path("roomId") roomId: String,
@Path("tag") tag: String
@Path("tag") tag: String,
)
/**
@ -424,7 +425,7 @@ internal interface RoomAPI {
@Path("userId") userId: String,
@Path("roomId") roomId: String,
@Path("type") type: String,
@Body content: JsonDict
@Body content: JsonDict,
)
/**
@ -437,7 +438,7 @@ internal interface RoomAPI {
suspend fun deleteRoomAccountData(
@Path("userId") userId: String,
@Path("roomId") roomId: String,
@Path("type") type: String
@Path("type") type: String,
)
/**
@ -450,7 +451,7 @@ internal interface RoomAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/upgrade")
suspend fun upgradeRoom(
@Path("roomId") roomId: String,
@Body body: RoomUpgradeBody
@Body body: RoomUpgradeBody,
): RoomUpgradeResponse
/**
@ -462,7 +463,7 @@ internal interface RoomAPI {
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "im.nheko.summary/rooms/{roomIdOrAlias}/summary")
suspend fun getRoomSummary(
@Path("roomIdOrAlias") roomidOrAlias: String,
@Query("via") viaServers: List<String>?
@Query("via") viaServers: List<String>?,
): RoomStrippedState
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "rooms/{roomId}/threads")
@ -470,6 +471,6 @@ internal interface RoomAPI {
@Path("roomId") roomId: String,
@Query("include") include: String? = "all",
@Query("from") from: String? = null,
@Query("limit") limit: Int? = null
@Query("limit") limit: Int? = null,
): ThreadSummariesResponse
}

View File

@ -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<String>?, 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 {

View File

@ -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.send.model.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,16 @@ 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<String>? = null, additionalContent: Content? = null): Event {
val localId = LocalEcho.createLocalEchoId()
val content = if (reason != null || withRelations != null) {
EventRedactBody(
reason = reason,
withRelations = withRelations,
).toContent().plus(additionalContent.orEmpty())
} else {
additionalContent
}
return Event(
roomId = roomId,
originServerTs = dummyOriginServerTs(),
@ -804,7 +813,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 +839,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 {

View File

@ -20,8 +20,8 @@ import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.crypto.tasks.RedactEventTask
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.RoomAPI
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
@ -43,27 +43,29 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters, ses
val roomId: String,
val eventId: String,
val reason: String?,
val withRelations: List<String>? = null,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@Inject lateinit var roomAPI: RoomAPI
@Inject lateinit var globalErrorReceiver: GlobalErrorReceiver
@Inject lateinit var redactEventTask: RedactEventTask
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val eventId = params.eventId
return runCatching {
executeRequest(globalErrorReceiver) {
roomAPI.redactEvent(
params.txID,
params.roomId,
eventId,
if (params.reason == null) emptyMap() else mapOf("reason" to params.reason)
)
}
redactEventTask.execute(
RedactEventTask.Params(
txID = params.txID,
roomId = params.roomId,
eventId = params.eventId,
reason = params.reason,
withRelations = params.withRelations,
)
)
}.fold(
{
Result.success()

View File

@ -0,0 +1,29 @@
/*
* Copyright 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.session.room.send.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
internal data class EventRedactBody(
@Json(name = "reason")
val reason: String? = null,
@Json(name = "org.matrix.msc3912.with_relations")
val withRelations: List<String>? = null,
)

View File

@ -26,9 +26,9 @@ internal interface EventSenderProcessor : SessionLifecycleObserver {
fun postEvent(event: Event, encrypt: Boolean): Cancelable
fun postRedaction(redactionLocalEcho: Event, reason: String?): Cancelable
fun postRedaction(redactionLocalEcho: Event, reason: String?, withRelations: List<String>? = null): Cancelable
fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?): Cancelable
fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?, withRelations: List<String>? = null): Cancelable
fun postTask(task: QueuedTask): Cancelable

View File

@ -101,12 +101,18 @@ internal class EventSenderProcessorCoroutine @Inject constructor(
return postTask(task)
}
override fun postRedaction(redactionLocalEcho: Event, reason: String?): Cancelable {
return postRedaction(redactionLocalEcho.eventId!!, redactionLocalEcho.redacts!!, redactionLocalEcho.roomId!!, reason)
override fun postRedaction(redactionLocalEcho: Event, reason: String?, withRelations: List<String>?): Cancelable {
return postRedaction(redactionLocalEcho.eventId!!, redactionLocalEcho.redacts!!, redactionLocalEcho.roomId!!, reason, withRelations)
}
override fun postRedaction(redactionLocalEchoId: String, eventToRedactId: String, roomId: String, reason: String?): Cancelable {
val task = queuedTaskFactory.createRedactTask(redactionLocalEchoId, eventToRedactId, roomId, reason)
override fun postRedaction(
redactionLocalEchoId: String,
eventToRedactId: String,
roomId: String,
reason: String?,
withRelations: List<String>?
): Cancelable {
val task = queuedTaskFactory.createRedactTask(redactionLocalEchoId, eventToRedactId, roomId, reason, withRelations)
return postTask(task)
}

View File

@ -19,9 +19,11 @@ 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.send.LocalEchoRepository
import org.matrix.android.sdk.internal.session.room.send.model.EventRedactBody
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<EventRedactBody>()
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,
)
)
}
}
// postTask(queuedTaskFactory.createRedactTask(info.eventToRedactId, info.)

View File

@ -43,12 +43,13 @@ internal class QueuedTaskFactory @Inject constructor(
)
}
fun createRedactTask(redactionLocalEcho: String, eventId: String, roomId: String, reason: String?): QueuedTask {
fun createRedactTask(redactionLocalEcho: String, eventId: String, roomId: String, reason: String?, withRelations: List<String>? = null): QueuedTask {
return RedactQueuedTask(
redactionLocalEchoId = redactionLocalEcho,
toRedactEventId = eventId,
roomId = roomId,
reason = reason,
withRelations = withRelations,
redactEventTask = redactEventTask,
localEchoRepository = localEchoRepository,
cancelSendTracker = cancelSendTracker

View File

@ -26,13 +26,14 @@ internal class RedactQueuedTask(
val redactionLocalEchoId: String,
private val roomId: String,
private val reason: String?,
private val withRelations: List<String>?,
private val redactEventTask: RedactEventTask,
private val localEchoRepository: LocalEchoRepository,
private val cancelSendTracker: CancelSendTracker
) : QueuedTask(queueIdentifier = roomId, taskIdentifier = redactionLocalEchoId) {
override suspend fun doExecute() {
redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason))
redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason, withRelations))
}
override fun onTaskFailed() {

View File

@ -20,6 +20,7 @@ import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
import im.vector.app.features.voicebroadcast.model.isVoiceBroadcast
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
@ -49,3 +50,7 @@ fun TimelineEvent.getVectorLastMessageContent(): MessageContent? {
else -> getLastMessageContent()
}
}
fun TimelineEvent.isVoiceBroadcast(): Boolean {
return root.isVoiceBroadcast()
}

View File

@ -32,6 +32,7 @@ import im.vector.app.R
import im.vector.app.SpaceStateHandler
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.isVoiceBroadcast
import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.BuildMeta
@ -859,12 +860,18 @@ class TimelineViewModel @AssistedInject constructor(
private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
val event = room?.getTimelineEvent(action.targetEventId) ?: return
if (event.isLiveLocation()) {
viewModelScope.launch {
redactLiveLocationShareEventUseCase.execute(event.root, room, action.reason)
when {
event.isLiveLocation() -> {
viewModelScope.launch {
redactLiveLocationShareEventUseCase.execute(event.root, room, action.reason)
}
}
event.isVoiceBroadcast() -> {
room.sendService().redactEvent(event.root, action.reason, listOf(RelationType.REFERENCE))
}
else -> {
room.sendService().redactEvent(event.root, action.reason)
}
} else {
room.sendService().redactEvent(event.root, action.reason)
}
}

View File

@ -49,7 +49,9 @@ value class VoiceBroadcastEvent(val root: Event) {
get() = root.content.toModel()
}
fun Event.isVoiceBroadcast() = type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO
/**
* Map a [VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO] state event to a [VoiceBroadcastEvent].
*/
fun Event.asVoiceBroadcastEvent() = if (type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO) VoiceBroadcastEvent(this) else null
fun Event.asVoiceBroadcastEvent() = if (isVoiceBroadcast()) VoiceBroadcastEvent(this) else null