Pagination: remove the live chunk when merging

This commit is contained in:
ganfra 2019-12-04 16:09:32 +01:00
parent f8db684ed2
commit 56e15cf054
6 changed files with 45 additions and 65 deletions

View File

@ -51,36 +51,6 @@ internal fun ChunkEntity.deleteOnCascade() {
this.deleteFromRealm()
}
internal fun ChunkEntity.merge(roomId: String,
chunkToMerge: ChunkEntity,
direction: PaginationDirection) {
assertIsManaged()
val isChunkToMergeUnlinked = chunkToMerge.isUnlinked()
val isCurrentChunkUnlinked = this.isUnlinked()
val isUnlinked = isCurrentChunkUnlinked && isChunkToMergeUnlinked
if (isCurrentChunkUnlinked && !isChunkToMergeUnlinked) {
this.timelineEvents.forEach { it.root?.isUnlinked = false }
}
val eventsToMerge: List<TimelineEventEntity>
if (direction == PaginationDirection.FORWARDS) {
this.nextToken = chunkToMerge.nextToken
this.isLastForward = chunkToMerge.isLastForward
eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING)
} else {
this.prevToken = chunkToMerge.prevToken
this.isLastBackward = chunkToMerge.isLastBackward
eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
}
val events = eventsToMerge.mapNotNull { it.root?.asDomain() }
val eventIds = ArrayList<String>()
events.forEach { event ->
add(realm, roomId, event, direction, isUnlinked = isUnlinked)
if (event.eventId != null) {
eventIds.add(event.eventId)
}
}
}
internal fun ChunkEntity.add(localRealm: Realm,
roomId: String,
@ -115,7 +85,7 @@ internal fun ChunkEntity.add(localRealm: Realm,
val senderId = event.senderId ?: ""
val readReceiptsSummaryEntity = ReadReceiptsSummaryEntity.where(localRealm, eventId).findFirst()
?: ReadReceiptsSummaryEntity(eventId, roomId)
?: ReadReceiptsSummaryEntity(eventId, roomId)
// Update RR for the sender of a new message with a dummy one
@ -152,14 +122,14 @@ internal fun ChunkEntity.add(localRealm: Realm,
internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
return when (direction) {
PaginationDirection.FORWARDS -> forwardsDisplayIndex
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
} ?: defaultValue
PaginationDirection.FORWARDS -> forwardsDisplayIndex
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
} ?: defaultValue
}
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
return when (direction) {
PaginationDirection.FORWARDS -> forwardsStateIndex
PaginationDirection.BACKWARDS -> backwardsStateIndex
} ?: defaultValue
PaginationDirection.FORWARDS -> forwardsStateIndex
PaginationDirection.BACKWARDS -> backwardsStateIndex
} ?: defaultValue
}

View File

@ -26,11 +26,6 @@ import im.vector.matrix.android.internal.database.query.fastContains
import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) {
chunks.remove(chunkEntity)
chunkEntity.deleteOnCascade()
}
internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) {
if (!chunks.contains(chunkEntity)) {
chunks.add(chunkEntity)

View File

@ -57,7 +57,7 @@ internal fun ChunkEntity.Companion.findIncludingEvent(realm: Realm, eventId: Str
return findAllIncludingEvents(realm, listOf(eventId)).firstOrNull()
}
internal fun ChunkEntity.Companion.create(realm: Realm, roomId: String, prevToken: String?, nextToken: String?): ChunkEntity {
internal fun ChunkEntity.Companion.create(realm: Realm, roomId: String, prevToken: String? = null, nextToken: String? = null): ChunkEntity {
return realm.createObject<ChunkEntity>().apply {
this.roomId = roomId
this.prevToken = prevToken

View File

@ -470,13 +470,13 @@ internal class DefaultTimeline(
Pair(filteredEvents[range.startIndex]!!.root!!.displayIndex, Timeline.Direction.BACKWARDS)
}
val state = getState(direction)
if (state.isPaginating) {
postSnapshot = if (state.isPaginating) {
// We are getting new items from pagination
postSnapshot = paginateInternal(startDisplayIndex, direction, state.requestedPaginationCount)
paginateInternal(startDisplayIndex, direction, state.requestedPaginationCount)
} else {
// We are getting new items from sync
buildTimelineEvents(startDisplayIndex, direction, range.length.toLong())
postSnapshot = true
true
}
}
changeSet.changes.forEach { index ->

View File

@ -23,7 +23,9 @@ import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.create
import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.user.UserEntityFactory
import im.vector.matrix.android.internal.util.awaitTransaction
import io.realm.kotlin.createObject
@ -33,7 +35,8 @@ import javax.inject.Inject
/**
* Insert Chunk in DB, and eventually merge with existing chunk event
*/
internal class TokenChunkEventPersistor @Inject constructor(private val monarchy: Monarchy) {
internal class TokenChunkEventPersistor @Inject constructor(private val monarchy: Monarchy,
private val roomSummaryUpdater: RoomSummaryUpdater) {
/**
* <pre>
@ -112,7 +115,7 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction")
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)
?: realm.createObject(roomId)
val nextToken: String?
val prevToken: String?
@ -127,7 +130,6 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
val prevChunk = ChunkEntity.find(realm, roomId, nextToken = prevToken)
val nextChunk = ChunkEntity.find(realm, roomId, prevToken = nextToken)
// The current chunk is the one we will keep all along the merge processChanges.
// We try to look for a chunk next to the token,
// otherwise we create a whole new one
@ -136,22 +138,34 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
} else {
nextChunk?.apply { this.prevToken = prevToken }
}
?: ChunkEntity().apply {
this.roomId = roomId
this.prevToken = prevToken
this.nextToken = nextToken
}
?: realm.createObject<ChunkEntity>().apply {
this.roomId = roomId
this.prevToken = prevToken
this.nextToken = nextToken
}
if (receivedChunk.events.isEmpty() && receivedChunk.end == receivedChunk.start) {
Timber.v("Reach end of $roomId")
currentChunk.isLastBackward = true
if (direction == PaginationDirection.FORWARDS) {
Timber.v("Reach live state of $roomId")
// We make sure we only have one live chunk
ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.deleteOnCascade()
currentChunk.isLastForward = true
currentChunk.nextToken = null
currentChunk.timelineEvents.forEach {
it.root?.isUnlinked = false
}
roomSummaryUpdater.update(realm, roomId, updateMembers = false)
} else {
Timber.v("Reach end of $roomId")
currentChunk.isLastBackward = true
}
} else {
Timber.v("Add ${receivedChunk.events.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}")
for (event in receivedChunk.events) {
currentChunk.add(realm, roomId, event, direction, isUnlinked = !currentChunk.isLastForward)
}
roomEntity.addOrUpdate(currentChunk)
}
roomEntity.addOrUpdate(currentChunk)
}
return if (receivedChunk.events.isEmpty()) {
if (receivedChunk.start != receivedChunk.end) {

View File

@ -20,9 +20,11 @@ package im.vector.matrix.android.internal.session.sync
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
import im.vector.matrix.android.internal.database.helper.add
import im.vector.matrix.android.internal.database.helper.deleteOnCascade
import im.vector.matrix.android.internal.database.helper.lastStateIndex
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.create
import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where
@ -52,10 +54,8 @@ internal class ChunkEntityFactory @Inject constructor(private val cryptoService:
roomId: String,
eventList: List<Event>,
prevToken: String?): ChunkEntity {
val chunkEntity = realm.createObject<ChunkEntity>().apply {
this.roomId = roomId
this.prevToken = prevToken
this.isLastForward = true
val chunkEntity = ChunkEntity.create(realm, roomId, prevToken = prevToken).apply {
isLastForward = true
}
for (event in eventList) {
chunkEntity.add(realm, roomId, event, PaginationDirection.FORWARDS)
@ -76,14 +76,15 @@ internal class ChunkEntityFactory @Inject constructor(private val cryptoService:
val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
val stateIndexOffset = lastChunk?.lastStateIndex(PaginationDirection.FORWARDS) ?: 0
val chunkEntity = if (isLimited || lastChunk == null) {
realm.createObject<ChunkEntity>().apply {
this.roomId = roomId
this.prevToken = prevToken
this.isLastForward = true
ChunkEntity.create(realm, roomId, prevToken = prevToken).apply {
isLastForward = true
}
} else {
lastChunk
}
if (lastChunk != chunkEntity) {
lastChunk?.deleteOnCascade()
}
for (event in eventList) {
chunkEntity.add(realm, roomId, event, PaginationDirection.FORWARDS, stateIndexOffset)
// Give info to crypto module