Merge pull request #5627 from vector-im/feature/aris/threads_summary_event_redaction
Thread redaction will now update the thread summary counter
This commit is contained in:
commit
3c06d56b87
@ -46,3 +46,5 @@ data class UnsignedData(
|
|||||||
@Json(name = "replaces_state") val replacesState: String? = null
|
@Json(name = "replaces_state") val replacesState: String? = null
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun UnsignedData?.isRedacted() = this?.redactedEvent != null
|
||||||
|
@ -16,9 +16,12 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.database.helper
|
package org.matrix.android.sdk.internal.database.helper
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonDataException
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.Sort
|
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.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
||||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
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.findLastForwardChunkOfRoom
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.database.query.whereRoomId
|
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>?
|
private typealias Summary = Pair<Int, TimelineEventEntity>?
|
||||||
|
|
||||||
@ -48,14 +53,14 @@ internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(
|
|||||||
for ((rootThreadEventId, eventEntity) in this) {
|
for ((rootThreadEventId, eventEntity) in this) {
|
||||||
eventEntity.threadSummaryInThread(eventEntity.realm, rootThreadEventId, chunkEntity)?.let { threadSummary ->
|
eventEntity.threadSummaryInThread(eventEntity.realm, rootThreadEventId, chunkEntity)?.let { threadSummary ->
|
||||||
|
|
||||||
val numberOfMessages = threadSummary.first
|
val inThreadMessages = threadSummary.first
|
||||||
val latestEventInThread = threadSummary.second
|
val latestEventInThread = threadSummary.second
|
||||||
|
|
||||||
// If this is a thread message, find its root event if exists
|
// If this is a thread message, find its root event if exists
|
||||||
val rootThreadEvent = if (eventEntity.isThread()) eventEntity.findRootThreadEvent() else eventEntity
|
val rootThreadEvent = if (eventEntity.isThread()) eventEntity.findRootThreadEvent() else eventEntity
|
||||||
|
|
||||||
rootThreadEvent?.markEventAsRoot(
|
rootThreadEvent?.markEventAsRoot(
|
||||||
threadsCounted = numberOfMessages,
|
inThreadMessages = inThreadMessages,
|
||||||
latestMessageTimelineEventEntity = latestEventInThread
|
latestMessageTimelineEventEntity = latestEventInThread
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -81,28 +86,27 @@ internal fun EventEntity.findRootThreadEvent(): EventEntity? =
|
|||||||
* Mark or update the current event a root thread event
|
* Mark or update the current event a root thread event
|
||||||
*/
|
*/
|
||||||
internal fun EventEntity.markEventAsRoot(
|
internal fun EventEntity.markEventAsRoot(
|
||||||
threadsCounted: Int,
|
inThreadMessages: Int,
|
||||||
latestMessageTimelineEventEntity: TimelineEventEntity?) {
|
latestMessageTimelineEventEntity: TimelineEventEntity?) {
|
||||||
isRootThread = true
|
isRootThread = true
|
||||||
numberOfThreads = threadsCounted
|
numberOfThreads = inThreadMessages
|
||||||
threadSummaryLatestMessage = latestMessageTimelineEventEntity
|
threadSummaryLatestMessage = latestMessageTimelineEventEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the number of threads for the provided root thread eventId, and finds the latest event message
|
* 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
|
* @param rootThreadEventId The root eventId that will find the number of threads
|
||||||
* @return A ThreadSummary containing the counted threads and the latest event message
|
* @return A ThreadSummary containing the counted threads and the latest event message
|
||||||
*/
|
*/
|
||||||
internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary {
|
internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary {
|
||||||
// Number of messages
|
val inThreadMessages = countInThreadMessages(
|
||||||
val messages = TimelineEventEntity
|
realm = realm,
|
||||||
.whereRoomId(realm, roomId = roomId)
|
roomId = roomId,
|
||||||
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
|
rootThreadEventId = rootThreadEventId
|
||||||
.distinct(TimelineEventEntityFields.ROOT.EVENT_ID)
|
)
|
||||||
.count()
|
|
||||||
.toInt()
|
|
||||||
|
|
||||||
if (messages <= 0) return null
|
if (inThreadMessages <= 0) return null
|
||||||
|
|
||||||
// Find latest thread event, we know it exists
|
// Find latest thread event, we know it exists
|
||||||
var chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: chunkEntity ?: return null
|
var chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: chunkEntity ?: return null
|
||||||
@ -124,9 +128,38 @@ internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId:
|
|||||||
|
|
||||||
result ?: return null
|
result ?: return null
|
||||||
|
|
||||||
return Summary(messages, result)
|
return Summary(inThreadMessages, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of thread replies in the main timeline thread summary,
|
||||||
|
* with respect to redactions.
|
||||||
|
*/
|
||||||
|
internal fun countInThreadMessages(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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lets compare them in case user is moving forward in the timeline and we cannot know the
|
* Lets compare them in case user is moving forward in the timeline and we cannot know the
|
||||||
* exact chunk sequence while currentChunk is not yet committed in the DB
|
* exact chunk sequence while currentChunk is not yet committed in the DB
|
||||||
|
@ -44,6 +44,7 @@ internal open class EventEntity(@Index var eventId: String = "",
|
|||||||
// Thread related, no need to create a new Entity for performance
|
// Thread related, no need to create a new Entity for performance
|
||||||
@Index var isRootThread: Boolean = false,
|
@Index var isRootThread: Boolean = false,
|
||||||
@Index var rootThreadEventId: String? = null,
|
@Index var rootThreadEventId: String? = null,
|
||||||
|
// Number messages within the thread
|
||||||
var numberOfThreads: Int = 0,
|
var numberOfThreads: Int = 0,
|
||||||
var threadSummaryLatestMessage: TimelineEventEntity? = null
|
var threadSummaryLatestMessage: TimelineEventEntity? = null
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
@ -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.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
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.api.session.events.model.UnsignedData
|
||||||
|
import org.matrix.android.sdk.internal.database.helper.countInThreadMessages
|
||||||
|
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.ContentMapper
|
||||||
import org.matrix.android.sdk.internal.database.mapper.EventMapper
|
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.EventEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
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.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.findWithSenderMembershipEvent
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
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.unsignedData = MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(modified)
|
||||||
eventToPrune.decryptionResultJson = null
|
eventToPrune.decryptionResultJson = null
|
||||||
eventToPrune.decryptionErrorCode = null
|
eventToPrune.decryptionErrorCode = null
|
||||||
|
|
||||||
|
handleTimelineThreadSummaryIfNeeded(realm, eventToPrune, isLocalEcho)
|
||||||
}
|
}
|
||||||
// EventType.REACTION -> {
|
// EventType.REACTION -> {
|
||||||
// eventRelationsAggregationUpdater.handleReactionRedact(eventToPrune, realm, userId)
|
// 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 inThreadMessages = countInThreadMessages(
|
||||||
|
realm = realm,
|
||||||
|
roomId = roomId,
|
||||||
|
rootThreadEventId = rootThreadEventId
|
||||||
|
)
|
||||||
|
|
||||||
|
rootThreadEvent.numberOfThreads = inThreadMessages
|
||||||
|
if (inThreadMessages == 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> {
|
private fun computeAllowedKeys(type: String): List<String> {
|
||||||
// Add filtered content, allowed keys in content depends on the event type
|
// Add filtered content, allowed keys in content depends on the event type
|
||||||
return when (type) {
|
return when (type) {
|
||||||
|
Loading…
Reference in New Issue
Block a user