Enhance thread summary

Fix deleted root thread messages when show deleted messages are enabled/disabled
This commit is contained in:
ariskotsomitopoulos 2022-01-25 18:21:42 +02:00
parent b1b27bdd0e
commit c19b52cded
5 changed files with 70 additions and 17 deletions

View File

@ -28,5 +28,6 @@ data class ThreadDetails(
val threadSummarySenderInfo: SenderInfo? = null, val threadSummarySenderInfo: SenderInfo? = null,
val threadSummaryLatestTextMessage: String? = null, val threadSummaryLatestTextMessage: String? = null,
val lastMessageTimestamp: Long? = null, val lastMessageTimestamp: Long? = null,
var threadNotificationState: ThreadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE var threadNotificationState: ThreadNotificationState = ThreadNotificationState.NO_NEW_MESSAGE,
val isThread: Boolean = false
) )

View File

@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.database.helper
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.RealmResults
import io.realm.Sort import io.realm.Sort
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
@ -27,10 +26,14 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
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.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.database.query.find
import org.matrix.android.sdk.internal.database.query.findIncludingEvent 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.where
import org.matrix.android.sdk.internal.database.query.whereRoomId import org.matrix.android.sdk.internal.database.query.whereRoomId
private typealias ThreadSummary = Pair<Int, TimelineEventEntity>?
/** /**
* Finds the root thread event and update it with the latest message summary along with the number * Finds the root thread event and update it with the latest message summary along with the number
* of threads included. If there is no root thread event no action is done * of threads included. If there is no root thread event no action is done
@ -39,19 +42,21 @@ internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(
roomId: String, roomId: String,
realm: Realm, currentUserId: String, realm: Realm, currentUserId: String,
shouldUpdateNotifications: Boolean = true) { shouldUpdateNotifications: Boolean = true) {
for ((rootThreadEventId, eventEntity) in this) {
eventEntity.findAllThreadsForRootEventId(eventEntity.realm, rootThreadEventId).let {
if (it.isNullOrEmpty()) return@let
val latestMessage = it.firstOrNull() for ((rootThreadEventId, eventEntity) in this) {
eventEntity.threadSummaryInThread(eventEntity.realm, rootThreadEventId)?.let { threadSummary ->
val numberOfMessages = threadSummary.first
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 = it.size, threadsCounted = numberOfMessages,
latestMessageTimelineEventEntity = latestMessage latestMessageTimelineEventEntity = latestEventInThread
) )
} }
} }
@ -82,16 +87,49 @@ internal fun EventEntity.markEventAsRoot(
threadSummaryLatestMessage = latestMessageTimelineEventEntity threadSummaryLatestMessage = latestMessageTimelineEventEntity
} }
///**
// * Find all TimelineEventEntity that are threads bind to the Event with rootThreadEventId
// * @param rootThreadEventId The root eventId that will try to find bind threads
// */
//internal fun EventEntity.findAllThreadsForRootEventId(realm: Realm, rootThreadEventId: String): RealmResults<TimelineEventEntity> =
// TimelineEventEntity
// .whereRoomId(realm, roomId = roomId)
// .equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
// .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
// .findAll()
/** /**
* Find all TimelineEventEntity that are threads bind to the Event with rootThreadEventId * Count the number of threads for the provided root thread eventId, and finds the latest event message
* @param rootThreadEventId The root eventId that will try to find bind 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
*/ */
internal fun EventEntity.findAllThreadsForRootEventId(realm: Realm, rootThreadEventId: String): RealmResults<TimelineEventEntity> = internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String): ThreadSummary {
TimelineEventEntity
.whereRoomId(realm, roomId = roomId) // Number of messages
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId) val messages = TimelineEventEntity
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) .whereRoomId(realm, roomId = roomId)
.findAll() .equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId)
.count()
.toInt()
if (messages <= 0) return null
// Find latest thread event, we know it exists
var chunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: return null
var result: TimelineEventEntity? = null
// Iterate the chunk until we find our latest event
while (result == null) {
result = chunk.timelineEvents.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)?.firstOrNull {
it.root?.rootThreadEventId == rootThreadEventId
}
chunk = ChunkEntity.find(realm, roomId, nextToken = chunk.prevToken) ?: break
}
result ?: return null
return ThreadSummary(messages, result)
}
/** /**
* Find all TimelineEventEntity that are root threads for the specified room * Find all TimelineEventEntity that are root threads for the specified room

View File

@ -103,6 +103,7 @@ internal object EventMapper {
it.mCryptoErrorReason = eventEntity.decryptionErrorReason it.mCryptoErrorReason = eventEntity.decryptionErrorReason
it.threadDetails = ThreadDetails( it.threadDetails = ThreadDetails(
isRootThread = eventEntity.isRootThread, isRootThread = eventEntity.isRootThread,
isThread = if (it.threadDetails?.isThread == true) true else eventEntity.isThread(),
numberOfThreads = eventEntity.numberOfThreads, numberOfThreads = eventEntity.numberOfThreads,
threadSummarySenderInfo = eventEntity.threadSummaryLatestMessage?.let { timelineEventEntity -> threadSummarySenderInfo = eventEntity.threadSummaryLatestMessage?.let { timelineEventEntity ->
SenderInfo( SenderInfo(

View File

@ -21,6 +21,7 @@ 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.api.session.room.model.relation.RelationContent
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

View File

@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class TimelineEventVisibilityHelper @Inject constructor(private val userPreferencesProvider: UserPreferencesProvider) { class TimelineEventVisibilityHelper @Inject constructor(private val userPreferencesProvider: UserPreferencesProvider) {
@ -137,9 +138,19 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
} }
private fun TimelineEvent.shouldBeHidden(rootThreadEventId: String?, isFromThreadTimeline: Boolean): Boolean { private fun TimelineEvent.shouldBeHidden(rootThreadEventId: String?, isFromThreadTimeline: Boolean): Boolean {
if (root.isRedacted() && !userPreferencesProvider.shouldShowRedactedMessages()) {
if (root.isRedacted() && !userPreferencesProvider.shouldShowRedactedMessages() && root.threadDetails?.isRootThread == false ) {
return true return true
} }
// We should not display deleted thread messages within the normal timeline
if (root.isRedacted() &&
userPreferencesProvider.areThreadMessagesEnabled() &&
root.threadDetails?.isThread == true){
return true
}
if (root.getRelationContent()?.type == RelationType.REPLACE) { if (root.getRelationContent()?.type == RelationType.REPLACE) {
return true return true
} }
@ -155,6 +166,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
return true return true
} }
// Allow only the the threads within the rootThreadEventId along with the root event
if (userPreferencesProvider.areThreadMessagesEnabled() && isFromThreadTimeline) { if (userPreferencesProvider.areThreadMessagesEnabled() && isFromThreadTimeline) {
return if (root.getRootThreadEventId() == rootThreadEventId) { return if (root.getRootThreadEventId() == rootThreadEventId) {
false false