diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index 55ec1de925..5d7777480f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -23,6 +23,7 @@ import io.realm.kotlin.createObject import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.api.session.room.read.ReadService.Companion.THREAD_ID_MAIN import org.matrix.android.sdk.internal.crypto.model.SessionInfo import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.ChunkEntity @@ -136,17 +137,21 @@ private fun handleReadReceipts(realm: Realm, roomId: String, eventEntity: EventE val originServerTs = eventEntity.originServerTs if (originServerTs != null) { val timestampOfEvent = originServerTs.toDouble() - val readReceiptOfSender = ReadReceiptEntity.getOrCreate(realm, roomId = roomId, userId = senderId, threadId = eventEntity.rootThreadEventId) - // If the synced RR is older, update - if (timestampOfEvent > readReceiptOfSender.originServerTs) { - val previousReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId = readReceiptOfSender.eventId).findFirst() - rrDimber.i{"Handle outdated chunk RR $roomId / $senderId thread ${eventEntity.rootThreadEventId}: event ${readReceiptOfSender.eventId} -> ${eventEntity.eventId}"} - readReceiptOfSender.eventId = eventEntity.eventId - readReceiptOfSender.originServerTs = timestampOfEvent - previousReceiptsSummary?.readReceipts?.remove(readReceiptOfSender) - readReceiptsSummaryEntity.readReceipts.add(readReceiptOfSender) - } else { - rrDimber.i{"Handled chunk RR $roomId / $senderId thread ${eventEntity.rootThreadEventId}: keep ${readReceiptOfSender.eventId} (not ${eventEntity.eventId})"} + // null receipts also update the main receipt + val receiptDestination = eventEntity.rootThreadEventId ?: THREAD_ID_MAIN + setOf(receiptDestination, eventEntity.rootThreadEventId).distinct().forEach { rootThreadEventId -> + val readReceiptOfSender = ReadReceiptEntity.getOrCreate(realm, roomId = roomId, userId = senderId, threadId = rootThreadEventId) + // If the synced RR is older, update + if (timestampOfEvent > readReceiptOfSender.originServerTs) { + val previousReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId = readReceiptOfSender.eventId).findFirst() + rrDimber.i { "Handle outdated chunk RR $roomId / $senderId thread $rootThreadEventId(${eventEntity.rootThreadEventId}): event ${readReceiptOfSender.eventId} -> ${eventEntity.eventId}" } + readReceiptOfSender.eventId = eventEntity.eventId + readReceiptOfSender.originServerTs = timestampOfEvent + previousReceiptsSummary?.readReceipts?.remove(readReceiptOfSender) + readReceiptsSummaryEntity.readReceipts.add(readReceiptOfSender) + } else { + rrDimber.i { "Handled chunk RR $roomId / $senderId thread $rootThreadEventId(${eventEntity.rootThreadEventId}): keep ${readReceiptOfSender.eventId} (not ${eventEntity.eventId})" } + } } } return readReceiptsSummaryEntity diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt index 40dce190c5..64343689ab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt @@ -20,6 +20,7 @@ import de.spiritcroc.matrixsdk.util.DbgUtil import de.spiritcroc.matrixsdk.util.Dimber import io.realm.Realm import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.room.read.ReadService import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.query.createUnmanaged @@ -145,19 +146,22 @@ internal class ReadReceiptHandler @Inject constructor( for ((userId, paramsDict) in userIdsDict) { val ts = paramsDict[TIMESTAMP_KEY] as? Double ?: 0.0 - val threadId = paramsDict[THREAD_ID_KEY] as String? - val receiptEntity = ReadReceiptEntity.getOrCreate(realm, roomId, userId, threadId) - // ensure new ts is superior to the previous one - if (ts > receiptEntity.originServerTs) { - rrDimber.i{"Handle outdated sync RR $roomId / $userId thread $threadId: event ${receiptEntity.eventId} -> $eventId"} - ReadReceiptsSummaryEntity.where(realm, receiptEntity.eventId).findFirst()?.also { - it.readReceipts.remove(receiptEntity) + val syncedThreadId = paramsDict[THREAD_ID_KEY] as String? + val receiptDestination = syncedThreadId ?: ReadService.THREAD_ID_MAIN + setOf(receiptDestination, syncedThreadId).distinct().forEach { threadId -> + val receiptEntity = ReadReceiptEntity.getOrCreate(realm, roomId, userId, threadId) + // ensure new ts is superior to the previous one + if (ts > receiptEntity.originServerTs) { + rrDimber.i { "Handle outdated sync RR $roomId / $userId thread $threadId($syncedThreadId): event ${receiptEntity.eventId} -> $eventId" } + ReadReceiptsSummaryEntity.where(realm, receiptEntity.eventId).findFirst()?.also { + it.readReceipts.remove(receiptEntity) + } + receiptEntity.eventId = eventId + receiptEntity.originServerTs = ts + readReceiptsSummary.readReceipts.add(receiptEntity) + } else { + rrDimber.i { "Handle keep sync RR $roomId / $userId thread $threadId($syncedThreadId): event ${receiptEntity.eventId} (not $eventId)" } } - receiptEntity.eventId = eventId - receiptEntity.originServerTs = ts - readReceiptsSummary.readReceipts.add(receiptEntity) - } else { - rrDimber.i{"Handle keep sync RR $roomId / $userId thread $threadId: event ${receiptEntity.eventId} (not $eventId)"} } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index efa36c8247..701059dce4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -628,7 +628,7 @@ class TimelineEventController @Inject constructor( return if (partialState.isFromThreadTimeline()) { this.threadId == partialState.rootThreadEventId } else { - this.threadId == null || this.threadId == ReadService.THREAD_ID_MAIN + this.threadId == ReadService.THREAD_ID_MAIN } }