WIP on chunk merging : required to merge chunks wherever they are (permalink)
This commit is contained in:
parent
b3ba542e09
commit
c396c2bec7
@ -3,15 +3,18 @@ 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.asDomain
|
||||||
import im.vector.matrix.android.internal.database.mapper.fillWith
|
import im.vector.matrix.android.internal.database.mapper.asEntity
|
||||||
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.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.query.fastContains
|
import im.vector.matrix.android.internal.database.query.fastContains
|
||||||
import im.vector.matrix.android.internal.database.query.find
|
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
import io.realm.Sort
|
import io.realm.Sort
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
|
internal fun ChunkEntity.deleteOnCascade() {
|
||||||
|
this.events.deleteAllFromRealm()
|
||||||
|
this.deleteFromRealm()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.isUnlinked(): Boolean {
|
internal fun ChunkEntity.isUnlinked(): Boolean {
|
||||||
return events.where().equalTo(EventEntityFields.IS_UNLINKED, true).findAll().isNotEmpty()
|
return events.where().equalTo(EventEntityFields.IS_UNLINKED, true).findAll().isNotEmpty()
|
||||||
@ -37,7 +40,7 @@ internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity,
|
|||||||
eventsToMerge = chunkToMerge.events
|
eventsToMerge = chunkToMerge.events
|
||||||
}
|
}
|
||||||
eventsToMerge.forEach {
|
eventsToMerge.forEach {
|
||||||
addOrUpdate(it.asDomain(), direction, isUnlinked = isUnlinked)
|
add(it.asDomain(), direction, isUnlinked = isUnlinked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +50,7 @@ internal fun ChunkEntity.addAll(events: List<Event>,
|
|||||||
isUnlinked: Boolean = false) {
|
isUnlinked: Boolean = false) {
|
||||||
|
|
||||||
events.forEach { event ->
|
events.forEach { event ->
|
||||||
addOrUpdate(event, direction, stateIndexOffset, isUnlinked)
|
add(event, direction, stateIndexOffset, isUnlinked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,15 +58,15 @@ internal fun ChunkEntity.updateDisplayIndexes() {
|
|||||||
events.forEachIndexed { index, eventEntity -> eventEntity.displayIndex = index }
|
events.forEachIndexed { index, eventEntity -> eventEntity.displayIndex = index }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.addOrUpdate(event: Event,
|
internal fun ChunkEntity.add(event: Event,
|
||||||
direction: PaginationDirection,
|
direction: PaginationDirection,
|
||||||
stateIndexOffset: Int = 0,
|
stateIndexOffset: Int = 0,
|
||||||
isUnlinked: Boolean = false) {
|
isUnlinked: Boolean = false) {
|
||||||
if (!isManaged) {
|
if (!isManaged) {
|
||||||
throw IllegalStateException("Chunk entity should be managed to use fast contains")
|
throw IllegalStateException("Chunk entity should be managed to use fast contains")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.eventId == null) {
|
if (event.eventId == null || events.fastContains(event.eventId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,22 +80,16 @@ internal fun ChunkEntity.addOrUpdate(event: Event,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val eventEntity: EventEntity?
|
val eventEntity = event.asEntity()
|
||||||
if (!events.fastContains(event.eventId)) {
|
eventEntity.stateIndex = currentStateIndex
|
||||||
eventEntity = realm.createObject()
|
eventEntity.isUnlinked = isUnlinked
|
||||||
eventEntity.fillWith(event)
|
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)
|
|
||||||
} else {
|
|
||||||
eventEntity = events.find(event.eventId)
|
|
||||||
}
|
|
||||||
eventEntity?.stateIndex = currentStateIndex
|
|
||||||
eventEntity?.isUnlinked = isUnlinked
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
@ -8,8 +8,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity
|
|||||||
|
|
||||||
internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) {
|
internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) {
|
||||||
chunks.remove(chunkEntity)
|
chunks.remove(chunkEntity)
|
||||||
chunkEntity.events.deleteAllFromRealm()
|
chunkEntity.deleteOnCascade()
|
||||||
chunkEntity.deleteFromRealm()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) {
|
internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) {
|
||||||
|
@ -38,7 +38,7 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module
|
|||||||
RealmConfiguration.Builder()
|
RealmConfiguration.Builder()
|
||||||
.directory(directory)
|
.directory(directory)
|
||||||
.name("disk_store.realm")
|
.name("disk_store.realm")
|
||||||
.deleteRealmIfMigrationNeeded()
|
.inMemory()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +18,13 @@ internal class RoomMemberExtractor(private val realm: Realm,
|
|||||||
val sender = event.sender ?: return null
|
val sender = event.sender ?: return null
|
||||||
// When stateIndex is negative, we try to get the next stateEvent prevContent()
|
// When stateIndex is negative, we try to get the next stateEvent prevContent()
|
||||||
// If prevContent is null we fallback to the Int.MIN state events content()
|
// If prevContent is null we fallback to the Int.MIN state events content()
|
||||||
return if (event.stateIndex <= 0) {
|
val roomMember: RoomMember? = if (event.stateIndex <= 0) {
|
||||||
baseQuery(realm, roomId, sender).next(from = event.stateIndex)?.asDomain()?.prevContent()
|
baseQuery(realm, roomId, sender).next(from = event.stateIndex)?.asDomain()?.prevContent()
|
||||||
?: baseQuery(realm, roomId, sender).last(since = event.stateIndex)?.asDomain()?.content()
|
?: baseQuery(realm, roomId, sender).last(since = event.stateIndex)?.asDomain()?.content()
|
||||||
} else {
|
} else {
|
||||||
baseQuery(realm, roomId, sender).last(since = event.stateIndex)?.asDomain()?.content()
|
baseQuery(realm, roomId, sender).last(since = event.stateIndex)?.asDomain()?.content()
|
||||||
}
|
}
|
||||||
|
return roomMember
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun baseQuery(realm: Realm,
|
private fun baseQuery(realm: Realm,
|
||||||
|
@ -7,7 +7,7 @@ import im.vector.matrix.android.api.session.events.model.Event
|
|||||||
import im.vector.matrix.android.api.session.room.SendService
|
import im.vector.matrix.android.api.session.room.SendService
|
||||||
import im.vector.matrix.android.api.session.room.send.EventFactory
|
import im.vector.matrix.android.api.session.room.send.EventFactory
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
import im.vector.matrix.android.internal.database.helper.add
|
||||||
import im.vector.matrix.android.internal.database.helper.updateDisplayIndexes
|
import im.vector.matrix.android.internal.database.helper.updateDisplayIndexes
|
||||||
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.query.findLastLiveChunkFromRoom
|
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||||
@ -33,7 +33,7 @@ internal class DefaultSendService(private val roomId: String,
|
|||||||
monarchy.tryTransactionAsync { realm ->
|
monarchy.tryTransactionAsync { realm ->
|
||||||
val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
|
val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
|
||||||
?: return@tryTransactionAsync
|
?: return@tryTransactionAsync
|
||||||
chunkEntity.addOrUpdate(event, PaginationDirection.FORWARDS)
|
chunkEntity.add(event, PaginationDirection.FORWARDS)
|
||||||
chunkEntity.updateDisplayIndexes()
|
chunkEntity.updateDisplayIndexes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.zhuinden.monarchy.Monarchy
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
||||||
|
import im.vector.matrix.android.internal.database.helper.add
|
||||||
import im.vector.matrix.android.internal.database.helper.addStateEvents
|
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.deleteOnCascade
|
||||||
import im.vector.matrix.android.internal.database.helper.merge
|
import im.vector.matrix.android.internal.database.helper.merge
|
||||||
@ -62,7 +63,7 @@ internal class GetContextOfEventRequest(private val roomAPI: RoomAPI,
|
|||||||
nextToken = response.nextToken
|
nextToken = response.nextToken
|
||||||
}
|
}
|
||||||
|
|
||||||
currentChunk.addOrUpdate(response.event, PaginationDirection.FORWARDS, isUnlinked = true)
|
currentChunk.add(response.event, PaginationDirection.FORWARDS, isUnlinked = true)
|
||||||
// Now, handles chunk merge
|
// Now, handles chunk merge
|
||||||
val prevChunk = ChunkEntity.find(realm, roomId, nextToken = response.prevToken)
|
val prevChunk = ChunkEntity.find(realm, roomId, nextToken = response.prevToken)
|
||||||
val nextChunk = ChunkEntity.find(realm, roomId, prevToken = response.nextToken)
|
val nextChunk = ChunkEntity.find(realm, roomId, prevToken = response.nextToken)
|
||||||
|
@ -13,4 +13,11 @@ internal enum class PaginationDirection(val value: String) {
|
|||||||
*/
|
*/
|
||||||
BACKWARDS("b");
|
BACKWARDS("b");
|
||||||
|
|
||||||
|
fun reversed(): PaginationDirection {
|
||||||
|
return when (this) {
|
||||||
|
FORWARDS -> BACKWARDS
|
||||||
|
BACKWARDS -> FORWARDS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -5,7 +5,12 @@ import arrow.core.failure
|
|||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
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.find
|
import im.vector.matrix.android.internal.database.query.find
|
||||||
@ -63,36 +68,47 @@ internal class PaginationRequest(private val roomAPI: RoomAPI,
|
|||||||
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 currentChunk = realm.createObject<ChunkEntity>().apply {
|
// We create a new chunk with prev and next token as a base
|
||||||
|
// In case of permalink, we may not encounter other chunks, so it can be added
|
||||||
|
val newChunk = realm.createObject<ChunkEntity>().apply {
|
||||||
prevToken = receivedChunk.prevToken
|
prevToken = receivedChunk.prevToken
|
||||||
nextToken = receivedChunk.nextToken
|
nextToken = receivedChunk.nextToken
|
||||||
}
|
}
|
||||||
currentChunk.addAll(receivedChunk.events, direction, isUnlinked = true)
|
newChunk.addAll(receivedChunk.events, direction, isUnlinked = true)
|
||||||
|
|
||||||
// Now, handles chunk merge
|
// The current chunk is the one we will keep all along the merge process.
|
||||||
|
var currentChunk = newChunk
|
||||||
val prevChunk = ChunkEntity.find(realm, roomId, nextToken = receivedChunk.prevToken)
|
val prevChunk = ChunkEntity.find(realm, roomId, nextToken = receivedChunk.prevToken)
|
||||||
val nextChunk = ChunkEntity.find(realm, roomId, prevToken = receivedChunk.nextToken)
|
val nextChunk = ChunkEntity.find(realm, roomId, prevToken = receivedChunk.nextToken)
|
||||||
|
|
||||||
|
// We always merge the bottom chunk into top chunk, so we are always merging backwards
|
||||||
if (prevChunk != null) {
|
if (prevChunk != null) {
|
||||||
currentChunk.merge(prevChunk, PaginationDirection.BACKWARDS)
|
newChunk.merge(prevChunk, PaginationDirection.BACKWARDS)
|
||||||
roomEntity.deleteOnCascade(prevChunk)
|
roomEntity.deleteOnCascade(prevChunk)
|
||||||
}
|
}
|
||||||
if (nextChunk != null) {
|
if (nextChunk != null) {
|
||||||
currentChunk.merge(nextChunk, PaginationDirection.FORWARDS)
|
nextChunk.merge(newChunk, PaginationDirection.BACKWARDS)
|
||||||
roomEntity.deleteOnCascade(nextChunk)
|
newChunk.deleteOnCascade()
|
||||||
|
currentChunk = nextChunk
|
||||||
}
|
}
|
||||||
val eventIds = receivedChunk.events.mapNotNull { it.eventId }
|
val newEventIds = receivedChunk.events.mapNotNull { it.eventId }
|
||||||
ChunkEntity
|
ChunkEntity
|
||||||
.findAllIncludingEvents(realm, eventIds)
|
.findAllIncludingEvents(realm, newEventIds)
|
||||||
.filter { it != currentChunk }
|
.filter { it != currentChunk }
|
||||||
.forEach { overlapped ->
|
.forEach { overlapped ->
|
||||||
currentChunk.merge(overlapped, direction)
|
if (direction == PaginationDirection.BACKWARDS) {
|
||||||
roomEntity.deleteOnCascade(overlapped)
|
currentChunk.merge(overlapped, PaginationDirection.BACKWARDS)
|
||||||
|
roomEntity.deleteOnCascade(overlapped)
|
||||||
|
} else {
|
||||||
|
overlapped.merge(currentChunk, PaginationDirection.BACKWARDS)
|
||||||
|
currentChunk = overlapped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
roomEntity.addOrUpdate(currentChunk)
|
roomEntity.addOrUpdate(currentChunk)
|
||||||
|
|
||||||
// TODO : there is an issue with the pagination sending unwanted room member events
|
// TODO : there is an issue with the pagination sending unwanted room member events
|
||||||
val isUnlinked = currentChunk.isUnlinked()
|
val isUnlinked = currentChunk.isUnlinked()
|
||||||
roomEntity.addStateEvents(receivedChunk.stateEvents, isUnlinked = isUnlinked)
|
roomEntity.addStateEvents(receivedChunk.stateEvents, isUnlinked = isUnlinked)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user