Read receipts: fix not appearing RR
This commit is contained in:
parent
21deb2551d
commit
06dcf75a32
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.matrix.android.internal.database.mapper
|
package im.vector.matrix.android.internal.database.mapper
|
||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptEntityFields
|
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
|
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
@ -40,9 +39,6 @@ internal class ReadReceiptsSummaryMapper @Inject constructor(@SessionDatabase pr
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
ReadReceipt(user.asDomain(), it.originServerTs.toLong())
|
ReadReceipt(user.asDomain(), it.originServerTs.toLong())
|
||||||
}
|
}
|
||||||
.sortedByDescending {
|
|
||||||
it.originServerTs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,11 @@ import javax.inject.Inject
|
||||||
internal class TimelineEventMapper @Inject constructor(private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper) {
|
internal class TimelineEventMapper @Inject constructor(private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper) {
|
||||||
|
|
||||||
fun map(timelineEventEntity: TimelineEventEntity, correctedReadReceipts: List<ReadReceipt>? = null): TimelineEvent {
|
fun map(timelineEventEntity: TimelineEventEntity, correctedReadReceipts: List<ReadReceipt>? = null): TimelineEvent {
|
||||||
val readReceipts = correctedReadReceipts ?: timelineEventEntity.readReceipts?.let {
|
val readReceipts = correctedReadReceipts ?: timelineEventEntity.readReceipts
|
||||||
|
?.let {
|
||||||
readReceiptsSummaryMapper.map(it)
|
readReceiptsSummaryMapper.map(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
return TimelineEvent(
|
return TimelineEvent(
|
||||||
root = timelineEventEntity.root?.asDomain()
|
root = timelineEventEntity.root?.asDomain()
|
||||||
?: Event("", timelineEventEntity.eventId),
|
?: Event("", timelineEventEntity.eventId),
|
||||||
|
@ -38,7 +40,9 @@ internal class TimelineEventMapper @Inject constructor(private val readReceiptsS
|
||||||
senderName = timelineEventEntity.senderName,
|
senderName = timelineEventEntity.senderName,
|
||||||
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
||||||
senderAvatar = timelineEventEntity.senderAvatar,
|
senderAvatar = timelineEventEntity.senderAvatar,
|
||||||
readReceipts = readReceipts ?: emptyList()
|
readReceipts = readReceipts?.sortedByDescending {
|
||||||
|
it.originServerTs
|
||||||
|
} ?: emptyList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,8 @@ internal class DefaultReadService @Inject constructor(private val roomId: String
|
||||||
return Transformations.map(liveEntity) { realmResults ->
|
return Transformations.map(liveEntity) { realmResults ->
|
||||||
realmResults.firstOrNull()?.let {
|
realmResults.firstOrNull()?.let {
|
||||||
readReceiptsSummaryMapper.map(it)
|
readReceiptsSummaryMapper.map(it)
|
||||||
|
}?.sortedByDescending {
|
||||||
|
it.originServerTs
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.session.room.timeline
|
package im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
import android.util.SparseArray
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
@ -34,8 +33,6 @@ import im.vector.matrix.android.internal.database.model.ChunkEntityFields
|
||||||
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntityFields
|
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||||
|
@ -68,7 +65,7 @@ import kotlin.collections.HashMap
|
||||||
private const val MIN_FETCHING_COUNT = 30
|
private const val MIN_FETCHING_COUNT = 30
|
||||||
private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE
|
private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE
|
||||||
|
|
||||||
private const val EDIT_FILTER_LIKE = "{*\"m.relates_to\"*\"rel_type\":*\"m.replace\"*}"
|
internal const val EDIT_FILTER_LIKE = "{*\"m.relates_to\"*\"rel_type\":*\"m.replace\"*}"
|
||||||
|
|
||||||
internal class DefaultTimeline(
|
internal class DefaultTimeline(
|
||||||
private val roomId: String,
|
private val roomId: String,
|
||||||
|
@ -79,9 +76,9 @@ internal class DefaultTimeline(
|
||||||
private val paginationTask: PaginationTask,
|
private val paginationTask: PaginationTask,
|
||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
private val timelineEventMapper: TimelineEventMapper,
|
private val timelineEventMapper: TimelineEventMapper,
|
||||||
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
|
private val settings: TimelineSettings,
|
||||||
private val settings: TimelineSettings
|
private val hiddenReadReceipts: TimelineHiddenReadReceipts
|
||||||
) : Timeline {
|
) : Timeline, TimelineHiddenReadReceipts.Delegate {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD")
|
val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD")
|
||||||
|
@ -104,9 +101,6 @@ internal class DefaultTimeline(
|
||||||
|
|
||||||
private lateinit var liveEvents: RealmResults<TimelineEventEntity>
|
private lateinit var liveEvents: RealmResults<TimelineEventEntity>
|
||||||
private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>
|
private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>
|
||||||
private var hiddenReadReceipts: RealmResults<ReadReceiptsSummaryEntity>? = null
|
|
||||||
private val correctedReadReceiptsEventByIndex = SparseArray<String>()
|
|
||||||
private val correctedReadReceiptsByEvent = HashMap<String, MutableList<ReadReceipt>>()
|
|
||||||
|
|
||||||
private var roomEntity: RoomEntity? = null
|
private var roomEntity: RoomEntity? = null
|
||||||
|
|
||||||
|
@ -118,10 +112,8 @@ internal class DefaultTimeline(
|
||||||
private val backwardsPaginationState = AtomicReference(PaginationState())
|
private val backwardsPaginationState = AtomicReference(PaginationState())
|
||||||
private val forwardsPaginationState = AtomicReference(PaginationState())
|
private val forwardsPaginationState = AtomicReference(PaginationState())
|
||||||
|
|
||||||
|
|
||||||
private val timelineID = UUID.randomUUID().toString()
|
private val timelineID = UUID.randomUUID().toString()
|
||||||
|
|
||||||
|
|
||||||
private val eventDecryptor = TimelineEventDecryptor(realmConfiguration, timelineID, cryptoService)
|
private val eventDecryptor = TimelineEventDecryptor(realmConfiguration, timelineID, cryptoService)
|
||||||
|
|
||||||
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
|
private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
|
||||||
|
@ -162,7 +154,7 @@ internal class DefaultTimeline(
|
||||||
builtEventsIdMap[eventId]?.let { builtIndex ->
|
builtEventsIdMap[eventId]?.let { builtIndex ->
|
||||||
//Update an existing event
|
//Update an existing event
|
||||||
builtEvents[builtIndex]?.let { te ->
|
builtEvents[builtIndex]?.let { te ->
|
||||||
builtEvents[builtIndex] = timelineEventMapper.map(eventEntity, correctedReadReceiptsByEvent[te.root.eventId])
|
builtEvents[builtIndex] = timelineEventMapper.map(eventEntity, hiddenReadReceipts.correctedReadReceipts(te.root.eventId))
|
||||||
hasChanged = true
|
hasChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,56 +184,8 @@ internal class DefaultTimeline(
|
||||||
postSnapshot()
|
postSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val hiddenReadReceiptsListener = OrderedRealmCollectionChangeListener<RealmResults<ReadReceiptsSummaryEntity>> { collection, changeSet ->
|
|
||||||
var hasChange = false
|
|
||||||
changeSet.deletions.forEach {
|
|
||||||
val eventId = correctedReadReceiptsEventByIndex[it]
|
|
||||||
val timelineEvent = liveEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, eventId).findFirst()
|
|
||||||
builtEventsIdMap[eventId]?.let { builtIndex ->
|
|
||||||
builtEvents[builtIndex]?.let { te ->
|
|
||||||
builtEvents[builtIndex] = te.copy(readReceipts = readReceiptsSummaryMapper.map(timelineEvent?.readReceipts))
|
|
||||||
hasChange = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
correctedReadReceiptsEventByIndex.clear()
|
|
||||||
correctedReadReceiptsByEvent.clear()
|
|
||||||
val loadedReadReceipts = collection.where().greaterThan("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.DISPLAY_INDEX}", prevDisplayIndex).findAll()
|
|
||||||
loadedReadReceipts.forEachIndexed { index, summary ->
|
|
||||||
val timelineEvent = summary?.timelineEvent?.firstOrNull()
|
|
||||||
val displayIndex = timelineEvent?.root?.displayIndex
|
|
||||||
if (displayIndex != null) {
|
|
||||||
val firstDisplayedEvent = liveEvents.where()
|
|
||||||
.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
|
|
||||||
.lessThan(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
|
|
||||||
.findFirst()
|
|
||||||
|
|
||||||
if (firstDisplayedEvent != null) {
|
// Public methods ******************************************************************************
|
||||||
correctedReadReceiptsEventByIndex.put(index, firstDisplayedEvent.eventId)
|
|
||||||
correctedReadReceiptsByEvent.getOrPut(firstDisplayedEvent.eventId, {
|
|
||||||
readReceiptsSummaryMapper.map(firstDisplayedEvent.readReceipts).toMutableList()
|
|
||||||
}).addAll(
|
|
||||||
readReceiptsSummaryMapper.map(summary)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (correctedReadReceiptsByEvent.isNotEmpty()) {
|
|
||||||
correctedReadReceiptsByEvent.forEach { (eventId, correctedReadReceipts) ->
|
|
||||||
builtEventsIdMap[eventId]?.let { builtIndex ->
|
|
||||||
builtEvents[builtIndex]?.let { te ->
|
|
||||||
builtEvents[builtIndex] = te.copy(readReceipts = correctedReadReceipts)
|
|
||||||
hasChange = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasChange) {
|
|
||||||
postSnapshot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public methods ******************************************************************************
|
|
||||||
|
|
||||||
override fun paginate(direction: Timeline.Direction, count: Int) {
|
override fun paginate(direction: Timeline.Direction, count: Int) {
|
||||||
BACKGROUND_HANDLER.post {
|
BACKGROUND_HANDLER.post {
|
||||||
|
@ -295,12 +239,7 @@ internal class DefaultTimeline(
|
||||||
.findAllAsync()
|
.findAllAsync()
|
||||||
.also { it.addChangeListener(relationsListener) }
|
.also { it.addChangeListener(relationsListener) }
|
||||||
|
|
||||||
hiddenReadReceipts = ReadReceiptsSummaryEntity.whereInRoom(realm, roomId)
|
hiddenReadReceipts.start(realm, liveEvents, this)
|
||||||
.isNotEmpty(ReadReceiptsSummaryEntityFields.TIMELINE_EVENT)
|
|
||||||
.isNotEmpty(ReadReceiptsSummaryEntityFields.READ_RECEIPTS.`$`)
|
|
||||||
.filterReceiptsWithSettings()
|
|
||||||
.findAllAsync()
|
|
||||||
.also { it.addChangeListener(hiddenReadReceiptsListener) }
|
|
||||||
|
|
||||||
isReady.set(true)
|
isReady.set(true)
|
||||||
}
|
}
|
||||||
|
@ -315,8 +254,8 @@ internal class DefaultTimeline(
|
||||||
cancelableBag.cancel()
|
cancelableBag.cancel()
|
||||||
roomEntity?.sendingTimelineEvents?.removeAllChangeListeners()
|
roomEntity?.sendingTimelineEvents?.removeAllChangeListeners()
|
||||||
eventRelations.removeAllChangeListeners()
|
eventRelations.removeAllChangeListeners()
|
||||||
hiddenReadReceipts?.removeAllChangeListeners()
|
|
||||||
liveEvents.removeAllChangeListeners()
|
liveEvents.removeAllChangeListeners()
|
||||||
|
hiddenReadReceipts.dispose()
|
||||||
backgroundRealm.getAndSet(null).also {
|
backgroundRealm.getAndSet(null).also {
|
||||||
it.close()
|
it.close()
|
||||||
}
|
}
|
||||||
|
@ -328,6 +267,22 @@ internal class DefaultTimeline(
|
||||||
return hasMoreInCache(direction) || !hasReachedEnd(direction)
|
return hasMoreInCache(direction) || !hasReachedEnd(direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TimelineHiddenReadReceipts.Delegate
|
||||||
|
|
||||||
|
override fun rebuildEvent(eventId: String, readReceipts: List<ReadReceipt>): Boolean {
|
||||||
|
return builtEventsIdMap[eventId]?.let { builtIndex ->
|
||||||
|
//Update the relation of existing event
|
||||||
|
builtEvents[builtIndex]?.let { te ->
|
||||||
|
builtEvents[builtIndex] = te.copy(readReceipts = readReceipts)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReadReceiptsUpdated() {
|
||||||
|
postSnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
// Private methods *****************************************************************************
|
// Private methods *****************************************************************************
|
||||||
|
|
||||||
private fun hasMoreInCache(direction: Timeline.Direction): Boolean {
|
private fun hasMoreInCache(direction: Timeline.Direction): Boolean {
|
||||||
|
@ -608,7 +563,7 @@ internal class DefaultTimeline(
|
||||||
debouncer.debounce("post_snapshot", runnable, 50)
|
debouncer.debounce("post_snapshot", runnable, 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extension methods ***************************************************************************
|
// Extension methods ***************************************************************************
|
||||||
|
|
||||||
private fun Timeline.Direction.toPaginationDirection(): PaginationDirection {
|
private fun Timeline.Direction.toPaginationDirection(): PaginationDirection {
|
||||||
return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS
|
return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS
|
||||||
|
@ -634,23 +589,6 @@ internal class DefaultTimeline(
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* We are looking for receipts related to filtered events. So, it's the opposite of [filterEventsWithSettings] method.
|
|
||||||
*/
|
|
||||||
private fun RealmQuery<ReadReceiptsSummaryEntity>.filterReceiptsWithSettings(): RealmQuery<ReadReceiptsSummaryEntity> {
|
|
||||||
beginGroup()
|
|
||||||
if (settings.filterTypes) {
|
|
||||||
not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
|
|
||||||
}
|
|
||||||
if (settings.filterTypes && settings.filterEdits) {
|
|
||||||
or()
|
|
||||||
}
|
|
||||||
if (settings.filterEdits) {
|
|
||||||
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", EDIT_FILTER_LIKE)
|
|
||||||
}
|
|
||||||
endGroup()
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class PaginationState(
|
private data class PaginationState(
|
||||||
|
|
|
@ -53,8 +53,8 @@ internal class DefaultTimelineService @Inject constructor(private val roomId: St
|
||||||
paginationTask,
|
paginationTask,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
timelineEventMapper,
|
timelineEventMapper,
|
||||||
readReceiptsSummaryMapper,
|
settings,
|
||||||
settings
|
TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.matrix.android.internal.session.room.timeline
|
||||||
|
|
||||||
|
import android.util.SparseArray
|
||||||
|
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
|
||||||
|
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntityFields
|
||||||
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||||
|
import im.vector.matrix.android.internal.database.query.whereInRoom
|
||||||
|
import io.realm.OrderedRealmCollectionChangeListener
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmQuery
|
||||||
|
import io.realm.RealmResults
|
||||||
|
|
||||||
|
internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
|
||||||
|
private val roomId: String,
|
||||||
|
private val settings: TimelineSettings) {
|
||||||
|
|
||||||
|
interface Delegate {
|
||||||
|
fun rebuildEvent(eventId: String, readReceipts: List<ReadReceipt>): Boolean
|
||||||
|
fun onReadReceiptsUpdated()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val correctedReadReceiptsEventByIndex = SparseArray<String>()
|
||||||
|
private val correctedReadReceiptsByEvent = HashMap<String, MutableList<ReadReceipt>>()
|
||||||
|
|
||||||
|
private lateinit var hiddenReadReceipts: RealmResults<ReadReceiptsSummaryEntity>
|
||||||
|
private lateinit var liveEvents: RealmResults<TimelineEventEntity>
|
||||||
|
private lateinit var delegate: Delegate
|
||||||
|
|
||||||
|
private val hiddenReadReceiptsListener = OrderedRealmCollectionChangeListener<RealmResults<ReadReceiptsSummaryEntity>> { collection, changeSet ->
|
||||||
|
var hasChange = false
|
||||||
|
changeSet.deletions.forEach {
|
||||||
|
val eventId = correctedReadReceiptsEventByIndex[it]
|
||||||
|
val timelineEvent = liveEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, eventId).findFirst()
|
||||||
|
val readReceipts = readReceiptsSummaryMapper.map(timelineEvent?.readReceipts)
|
||||||
|
hasChange = hasChange || delegate.rebuildEvent(eventId, readReceipts)
|
||||||
|
}
|
||||||
|
correctedReadReceiptsEventByIndex.clear()
|
||||||
|
correctedReadReceiptsByEvent.clear()
|
||||||
|
hiddenReadReceipts.forEachIndexed { index, summary ->
|
||||||
|
val timelineEvent = summary?.timelineEvent?.firstOrNull()
|
||||||
|
val displayIndex = timelineEvent?.root?.displayIndex
|
||||||
|
if (displayIndex != null) {
|
||||||
|
val firstDisplayedEvent = liveEvents.where()
|
||||||
|
.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
|
||||||
|
.findFirst()
|
||||||
|
|
||||||
|
if (firstDisplayedEvent != null) {
|
||||||
|
correctedReadReceiptsEventByIndex.put(index, firstDisplayedEvent.eventId)
|
||||||
|
correctedReadReceiptsByEvent.getOrPut(firstDisplayedEvent.eventId, {
|
||||||
|
readReceiptsSummaryMapper.map(firstDisplayedEvent.readReceipts).toMutableList()
|
||||||
|
}).addAll(
|
||||||
|
readReceiptsSummaryMapper.map(summary)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (correctedReadReceiptsByEvent.isNotEmpty()) {
|
||||||
|
correctedReadReceiptsByEvent.forEach { (eventId, correctedReadReceipts) ->
|
||||||
|
val sortedReadReceipts = correctedReadReceipts.sortedByDescending {
|
||||||
|
it.originServerTs
|
||||||
|
}
|
||||||
|
hasChange = hasChange || delegate.rebuildEvent(eventId, sortedReadReceipts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasChange) {
|
||||||
|
delegate.onReadReceiptsUpdated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun start(realm: Realm, liveEvents: RealmResults<TimelineEventEntity>, delegate: Delegate) {
|
||||||
|
this.liveEvents = liveEvents
|
||||||
|
this.delegate = delegate
|
||||||
|
this.hiddenReadReceipts = ReadReceiptsSummaryEntity.whereInRoom(realm, roomId)
|
||||||
|
.isNotEmpty(ReadReceiptsSummaryEntityFields.TIMELINE_EVENT)
|
||||||
|
.isNotEmpty(ReadReceiptsSummaryEntityFields.READ_RECEIPTS.`$`)
|
||||||
|
.filterReceiptsWithSettings()
|
||||||
|
.findAllAsync()
|
||||||
|
.also { it.addChangeListener(hiddenReadReceiptsListener) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dispose() {
|
||||||
|
this.hiddenReadReceipts?.removeAllChangeListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun correctedReadReceipts(eventId: String?): List<ReadReceipt>? {
|
||||||
|
return correctedReadReceiptsByEvent[eventId]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We are looking for receipts related to filtered events. So, it's the opposite of [filterEventsWithSettings] method.
|
||||||
|
*/
|
||||||
|
private fun RealmQuery<ReadReceiptsSummaryEntity>.filterReceiptsWithSettings(): RealmQuery<ReadReceiptsSummaryEntity> {
|
||||||
|
beginGroup()
|
||||||
|
if (settings.filterTypes) {
|
||||||
|
not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
|
||||||
|
}
|
||||||
|
if (settings.filterTypes && settings.filterEdits) {
|
||||||
|
or()
|
||||||
|
}
|
||||||
|
if (settings.filterEdits) {
|
||||||
|
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", EDIT_FILTER_LIKE)
|
||||||
|
}
|
||||||
|
endGroup()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -78,7 +78,9 @@ internal class ReadReceiptHandler @Inject constructor() {
|
||||||
for ((eventId, receiptDict) in content) {
|
for ((eventId, receiptDict) in content) {
|
||||||
val userIdsDict = receiptDict[READ_KEY] ?: continue
|
val userIdsDict = receiptDict[READ_KEY] ?: continue
|
||||||
val readReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId).findFirst()
|
val readReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId).findFirst()
|
||||||
?: realm.createObject(ReadReceiptsSummaryEntity::class.java, eventId)
|
?: realm.createObject(ReadReceiptsSummaryEntity::class.java, eventId).apply {
|
||||||
|
this.roomId = roomId
|
||||||
|
}
|
||||||
|
|
||||||
for ((userId, paramsDict) in userIdsDict) {
|
for ((userId, paramsDict) in userIdsDict) {
|
||||||
val ts = paramsDict[TIMESTAMP_KEY] ?: 0.0
|
val ts = paramsDict[TIMESTAMP_KEY] ?: 0.0
|
||||||
|
|
Loading…
Reference in New Issue