Thread redaction will now update the thread summary counter

Root threads with 0 threads replies will become normal messages and removed from thread summaries
This commit is contained in:
ariskotsomitopoulos 2022-03-24 13:50:07 +02:00
parent 84b34f75de
commit 3c7495bd60
3 changed files with 82 additions and 9 deletions

View file

@ -46,3 +46,5 @@ data class UnsignedData(
@Json(name = "replaces_state") val replacesState: String? = null
)
fun UnsignedData?.isRedacted() = this?.redactedEvent != null

View file

@ -16,9 +16,12 @@
package org.matrix.android.sdk.internal.database.helper
import com.squareup.moshi.JsonDataException
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.Sort
import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.api.session.events.model.isRedacted
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
import org.matrix.android.sdk.internal.database.mapper.asDomain
@ -33,6 +36,8 @@ import org.matrix.android.sdk.internal.database.query.findIncludingEvent
import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.whereRoomId
import org.matrix.android.sdk.internal.di.MoshiProvider
import timber.log.Timber
private typealias Summary = Pair<Int, TimelineEventEntity>?
@ -90,19 +95,18 @@ internal fun EventEntity.markEventAsRoot(
/**
* Count the number of threads for the provided root thread eventId, and finds the latest event message
* note: Redactions are handled by RedactionEventProcessor
* @param rootThreadEventId The root eventId that will find the number of threads
* @return A ThreadSummary containing the counted threads and the latest event message
*/
internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary {
// Number of messages
val messages = TimelineEventEntity
.whereRoomId(realm, roomId = roomId)
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
.distinct(TimelineEventEntityFields.ROOT.EVENT_ID)
.count()
.toInt()
val numberOfThread = countThreads(
realm = realm,
roomId = roomId,
rootThreadEventId = rootThreadEventId
) ?: return null
if (messages <= 0) return null
if (numberOfThread <= 0) return null
// Find latest thread event, we know it exists
var chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: chunkEntity ?: return null
@ -124,7 +128,36 @@ internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId:
result ?: return null
return Summary(messages, result)
return Summary(numberOfThread, result)
}
/**
* Counts the number of threads in the main timeline thread summary,
* with respect to redactions.
*/
internal fun countThreads(realm: Realm, roomId: String, rootThreadEventId: String): Int? =
TimelineEventEntity
.whereRoomId(realm, roomId = roomId)
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
.distinct(TimelineEventEntityFields.ROOT.EVENT_ID)
.findAll()
?.filterNot { timelineEvent ->
timelineEvent.root
?.unsignedData
?.takeIf { it.isNotBlank() }
?.toUnsignedData()
.isRedacted()
}?.size
/**
* Mapping string to UnsignedData using Moshi
*/
private fun String.toUnsignedData(): UnsignedData? =
try {
MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).fromJson(this)
} catch (ex: JsonDataException) {
Timber.e(ex, "Failed to parse UnsignedData")
null
}
/**

View file

@ -21,11 +21,14 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.internal.database.helper.countThreads
import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.EventMapper
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
import org.matrix.android.sdk.internal.database.query.findWithSenderMembershipEvent
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.MoshiProvider
@ -89,6 +92,8 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
eventToPrune.unsignedData = MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(modified)
eventToPrune.decryptionResultJson = null
eventToPrune.decryptionErrorCode = null
handleTimelineThreadSummaryIfNeeded(realm, eventToPrune, isLocalEcho)
}
// EventType.REACTION -> {
// eventRelationsAggregationUpdater.handleReactionRedact(eventToPrune, realm, userId)
@ -104,6 +109,39 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
}
}
/**
* Invalidates the number of threads in the main timeline thread summary,
* with respect to redactions.
*/
private fun handleTimelineThreadSummaryIfNeeded(
realm: Realm,
eventToPrune: EventEntity,
isLocalEcho: Boolean,
) {
if (eventToPrune.isThread() && !isLocalEcho) {
val roomId = eventToPrune.roomId
val rootThreadEvent = eventToPrune.findRootThreadEvent() ?: return
val rootThreadEventId = eventToPrune.rootThreadEventId ?: return
val numberOfThreads = countThreads(
realm = realm,
roomId = roomId,
rootThreadEventId = rootThreadEventId
) ?: return
rootThreadEvent.numberOfThreads = numberOfThreads
if (numberOfThreads == 0) {
// We should also clear the thread summary list
rootThreadEvent.isRootThread = false
rootThreadEvent.threadSummaryLatestMessage = null
ThreadSummaryEntity
.where(realm, roomId = roomId, rootThreadEventId)
.findFirst()
?.deleteFromRealm()
}
}
}
private fun computeAllowedKeys(type: String): List<String> {
// Add filtered content, allowed keys in content depends on the event type
return when (type) {