forked from GitHub-Mirror/riotX-android
Timeline : fix merging issues
This commit is contained in:
parent
1269715b5c
commit
de90cbe73e
@ -4,7 +4,11 @@ import com.zhuinden.monarchy.Monarchy
|
|||||||
import im.vector.matrix.android.InstrumentedTest
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.database.helper.*
|
import im.vector.matrix.android.internal.database.helper.add
|
||||||
|
import im.vector.matrix.android.internal.database.helper.addAll
|
||||||
|
import im.vector.matrix.android.internal.database.helper.isUnlinked
|
||||||
|
import im.vector.matrix.android.internal.database.helper.lastStateIndex
|
||||||
|
import im.vector.matrix.android.internal.database.helper.merge
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
@ -102,7 +106,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
val chunk2: ChunkEntity = realm.createObject()
|
val chunk2: ChunkEntity = realm.createObject()
|
||||||
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.events.size shouldEqual 60
|
chunk1.events.size shouldEqual 60
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +122,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk2.isLast = false
|
chunk2.isLast = false
|
||||||
chunk1.addAll("roomId", eventsForChunk1, PaginationDirection.FORWARDS)
|
chunk1.addAll("roomId", eventsForChunk1, PaginationDirection.FORWARDS)
|
||||||
chunk2.addAll("roomId", eventsForChunk2, PaginationDirection.BACKWARDS)
|
chunk2.addAll("roomId", eventsForChunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.events.size shouldEqual 40
|
chunk1.events.size shouldEqual 40
|
||||||
chunk1.isLast.shouldBeTrue()
|
chunk1.isLast.shouldBeTrue()
|
||||||
}
|
}
|
||||||
@ -131,7 +135,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
val chunk2: ChunkEntity = realm.createObject()
|
val chunk2: ChunkEntity = realm.createObject()
|
||||||
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = false)
|
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = false)
|
||||||
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.isUnlinked().shouldBeFalse()
|
chunk1.isUnlinked().shouldBeFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +147,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
val chunk2: ChunkEntity = realm.createObject()
|
val chunk2: ChunkEntity = realm.createObject()
|
||||||
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.isUnlinked().shouldBeTrue()
|
chunk1.isUnlinked().shouldBeTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +161,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.prevToken = prevToken
|
chunk1.prevToken = prevToken
|
||||||
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk1.merge(chunk2, PaginationDirection.FORWARDS)
|
chunk1.merge("roomId", chunk2, PaginationDirection.FORWARDS)
|
||||||
chunk1.prevToken shouldEqual prevToken
|
chunk1.prevToken shouldEqual prevToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,7 +175,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||||||
chunk1.nextToken = nextToken
|
chunk1.nextToken = nextToken
|
||||||
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk1.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
chunk2.addAll("roomId", createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
chunk1.merge("roomId", chunk2, PaginationDirection.BACKWARDS)
|
||||||
chunk1.nextToken shouldEqual nextToken
|
chunk1.nextToken shouldEqual nextToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package im.vector.matrix.android.internal.database.helper
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.mapper.toEntity
|
import im.vector.matrix.android.internal.database.mapper.toEntity
|
||||||
import im.vector.matrix.android.internal.database.mapper.updateWith
|
import im.vector.matrix.android.internal.database.mapper.updateWith
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
@ -12,18 +13,21 @@ import im.vector.matrix.android.internal.session.room.timeline.PaginationDirecti
|
|||||||
import io.realm.Sort
|
import io.realm.Sort
|
||||||
|
|
||||||
internal fun ChunkEntity.deleteOnCascade() {
|
internal fun ChunkEntity.deleteOnCascade() {
|
||||||
|
assertIsManaged()
|
||||||
this.events.deleteAllFromRealm()
|
this.events.deleteAllFromRealm()
|
||||||
this.deleteFromRealm()
|
this.deleteFromRealm()
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default if a chunk is empty we consider it unlinked
|
// By default if a chunk is empty we consider it unlinked
|
||||||
internal fun ChunkEntity.isUnlinked(): Boolean {
|
internal fun ChunkEntity.isUnlinked(): Boolean {
|
||||||
|
assertIsManaged()
|
||||||
return events.where().equalTo(EventEntityFields.IS_UNLINKED, false).findAll().isEmpty()
|
return events.where().equalTo(EventEntityFields.IS_UNLINKED, false).findAll().isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity,
|
internal fun ChunkEntity.merge(roomId: String,
|
||||||
|
chunkToMerge: ChunkEntity,
|
||||||
direction: PaginationDirection) {
|
direction: PaginationDirection) {
|
||||||
|
assertIsManaged()
|
||||||
val isChunkToMergeUnlinked = chunkToMerge.isUnlinked()
|
val isChunkToMergeUnlinked = chunkToMerge.isUnlinked()
|
||||||
val isCurrentChunkUnlinked = this.isUnlinked()
|
val isCurrentChunkUnlinked = this.isUnlinked()
|
||||||
val isUnlinked = isCurrentChunkUnlinked && isChunkToMergeUnlinked
|
val isUnlinked = isCurrentChunkUnlinked && isChunkToMergeUnlinked
|
||||||
@ -41,7 +45,7 @@ internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity,
|
|||||||
eventsToMerge = chunkToMerge.events
|
eventsToMerge = chunkToMerge.events
|
||||||
}
|
}
|
||||||
eventsToMerge.forEach {
|
eventsToMerge.forEach {
|
||||||
add(it, direction, isUnlinked = isUnlinked)
|
add(roomId, it.asDomain(), direction, isUnlinked = isUnlinked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +54,7 @@ internal fun ChunkEntity.addAll(roomId: String,
|
|||||||
direction: PaginationDirection,
|
direction: PaginationDirection,
|
||||||
stateIndexOffset: Int = 0,
|
stateIndexOffset: Int = 0,
|
||||||
isUnlinked: Boolean = false) {
|
isUnlinked: Boolean = false) {
|
||||||
|
assertIsManaged()
|
||||||
events.forEach { event ->
|
events.forEach { event ->
|
||||||
add(roomId, event, direction, stateIndexOffset, isUnlinked)
|
add(roomId, event, direction, stateIndexOffset, isUnlinked)
|
||||||
}
|
}
|
||||||
@ -66,22 +70,12 @@ internal fun ChunkEntity.add(roomId: String,
|
|||||||
stateIndexOffset: Int = 0,
|
stateIndexOffset: Int = 0,
|
||||||
isUnlinked: Boolean = false) {
|
isUnlinked: Boolean = false) {
|
||||||
|
|
||||||
add(event.toEntity(roomId), direction, stateIndexOffset, isUnlinked)
|
assertIsManaged()
|
||||||
}
|
if (event.eventId.isNullOrEmpty() || events.fastContains(event.eventId)) {
|
||||||
|
|
||||||
internal fun ChunkEntity.add(eventEntity: EventEntity,
|
|
||||||
direction: PaginationDirection,
|
|
||||||
stateIndexOffset: Int = 0,
|
|
||||||
isUnlinked: Boolean = false) {
|
|
||||||
if (!isManaged) {
|
|
||||||
throw IllegalStateException("Chunk entity should be managed to use fast contains")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventEntity.eventId.isEmpty() || events.fastContains(eventEntity.eventId)) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
|
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
|
||||||
if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(eventEntity.type)) {
|
if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(event.type)) {
|
||||||
currentStateIndex += 1
|
currentStateIndex += 1
|
||||||
} else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) {
|
} else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) {
|
||||||
val lastEventType = events.last()?.type ?: ""
|
val lastEventType = events.last()?.type ?: ""
|
||||||
@ -89,14 +83,21 @@ internal fun ChunkEntity.add(eventEntity: EventEntity,
|
|||||||
currentStateIndex -= 1
|
currentStateIndex -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val eventEntity = event.toEntity(roomId)
|
||||||
eventEntity.updateWith(currentStateIndex, isUnlinked)
|
eventEntity.updateWith(currentStateIndex, isUnlinked)
|
||||||
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
|
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
|
||||||
events.add(position, eventEntity)
|
events.add(position, eventEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ChunkEntity.assertIsManaged() {
|
||||||
|
if (!isManaged) {
|
||||||
|
throw IllegalStateException("Chunk entity should be managed to use this function")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||||
return when (direction) {
|
return when (direction) {
|
||||||
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
|
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
|
||||||
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
|
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
|
||||||
} ?: defaultValue
|
} ?: defaultValue
|
||||||
}
|
}
|
@ -2,7 +2,12 @@ package im.vector.matrix.android.internal.session.room.timeline
|
|||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.internal.database.helper.*
|
import im.vector.matrix.android.internal.database.helper.addAll
|
||||||
|
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
||||||
|
import im.vector.matrix.android.internal.database.helper.addStateEvents
|
||||||
|
import im.vector.matrix.android.internal.database.helper.deleteOnCascade
|
||||||
|
import im.vector.matrix.android.internal.database.helper.isUnlinked
|
||||||
|
import im.vector.matrix.android.internal.database.helper.merge
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
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.model.RoomEntity
|
||||||
import im.vector.matrix.android.internal.database.query.create
|
import im.vector.matrix.android.internal.database.query.create
|
||||||
@ -21,7 +26,7 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
|
|||||||
return monarchy
|
return monarchy
|
||||||
.tryTransactionSync { realm ->
|
.tryTransactionSync { realm ->
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||||
?: throw IllegalStateException("You shouldn't use this method without a room")
|
?: throw IllegalStateException("You shouldn't use this method without a room")
|
||||||
|
|
||||||
val nextToken: String?
|
val nextToken: String?
|
||||||
val prevToken: String?
|
val prevToken: String?
|
||||||
@ -41,10 +46,10 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
|
|||||||
|
|
||||||
var currentChunk = if (direction == PaginationDirection.FORWARDS) {
|
var currentChunk = if (direction == PaginationDirection.FORWARDS) {
|
||||||
prevChunk?.apply { this.nextToken = nextToken }
|
prevChunk?.apply { this.nextToken = nextToken }
|
||||||
?: ChunkEntity.create(realm, prevToken, nextToken)
|
?: ChunkEntity.create(realm, prevToken, nextToken)
|
||||||
} else {
|
} else {
|
||||||
nextChunk?.apply { this.prevToken = prevToken }
|
nextChunk?.apply { this.prevToken = prevToken }
|
||||||
?: ChunkEntity.create(realm, prevToken, nextToken)
|
?: ChunkEntity.create(realm, prevToken, nextToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentChunk.addAll(roomId, receivedChunk.events, direction, isUnlinked = currentChunk.isUnlinked())
|
currentChunk.addAll(roomId, receivedChunk.events, direction, isUnlinked = currentChunk.isUnlinked())
|
||||||
@ -75,11 +80,11 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
|
|||||||
|
|
||||||
// We always merge the bottom chunk into top chunk, so we are always merging backwards
|
// We always merge the bottom chunk into top chunk, so we are always merging backwards
|
||||||
return if (direction == PaginationDirection.BACKWARDS) {
|
return if (direction == PaginationDirection.BACKWARDS) {
|
||||||
currentChunk.merge(otherChunk, PaginationDirection.BACKWARDS)
|
currentChunk.merge(roomEntity.roomId, otherChunk, PaginationDirection.BACKWARDS)
|
||||||
roomEntity.deleteOnCascade(otherChunk)
|
roomEntity.deleteOnCascade(otherChunk)
|
||||||
currentChunk
|
currentChunk
|
||||||
} else {
|
} else {
|
||||||
otherChunk.merge(currentChunk, PaginationDirection.BACKWARDS)
|
otherChunk.merge(roomEntity.roomId, currentChunk, PaginationDirection.BACKWARDS)
|
||||||
roomEntity.deleteOnCascade(currentChunk)
|
roomEntity.deleteOnCascade(currentChunk)
|
||||||
otherChunk
|
otherChunk
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user