diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt index 4c3abab0fd..351ae7aec3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt @@ -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.EventType 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.EventEntity 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.find import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import io.realm.Sort -import io.realm.kotlin.createObject + +internal fun ChunkEntity.deleteOnCascade() { + this.events.deleteAllFromRealm() + this.deleteFromRealm() +} internal fun ChunkEntity.isUnlinked(): Boolean { return events.where().equalTo(EventEntityFields.IS_UNLINKED, true).findAll().isNotEmpty() @@ -37,7 +40,7 @@ internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity, eventsToMerge = chunkToMerge.events } eventsToMerge.forEach { - addOrUpdate(it.asDomain(), direction, isUnlinked = isUnlinked) + add(it.asDomain(), direction, isUnlinked = isUnlinked) } } @@ -47,7 +50,7 @@ internal fun ChunkEntity.addAll(events: List, isUnlinked: Boolean = false) { 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 } } -internal fun ChunkEntity.addOrUpdate(event: Event, - direction: PaginationDirection, - stateIndexOffset: Int = 0, - isUnlinked: Boolean = false) { +internal fun ChunkEntity.add(event: Event, + direction: PaginationDirection, + stateIndexOffset: Int = 0, + isUnlinked: Boolean = false) { if (!isManaged) { throw IllegalStateException("Chunk entity should be managed to use fast contains") } - if (event.eventId == null) { + if (event.eventId == null || events.fastContains(event.eventId)) { return } @@ -77,22 +80,16 @@ internal fun ChunkEntity.addOrUpdate(event: Event, } } - val eventEntity: EventEntity? - if (!events.fastContains(event.eventId)) { - eventEntity = realm.createObject() - eventEntity.fillWith(event) - val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size - events.add(position, eventEntity) - } else { - eventEntity = events.find(event.eventId) - } - eventEntity?.stateIndex = currentStateIndex - eventEntity?.isUnlinked = isUnlinked + val eventEntity = event.asEntity() + eventEntity.stateIndex = currentStateIndex + eventEntity.isUnlinked = isUnlinked + val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size + events.add(position, eventEntity) } internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int { return when (direction) { - PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex - PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex - } ?: defaultValue + PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex + PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex + } ?: defaultValue } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt index 7c9c223af8..4df958b467 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt @@ -8,8 +8,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { chunks.remove(chunkEntity) - chunkEntity.events.deleteAllFromRealm() - chunkEntity.deleteFromRealm() + chunkEntity.deleteOnCascade() } internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 8c88122c55..4dbc8a8cc9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -38,7 +38,7 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module RealmConfiguration.Builder() .directory(directory) .name("disk_store.realm") - .deleteRealmIfMigrationNeeded() + .inMemory() .build() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberExtractor.kt index 08012cb5fd..67763f2660 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberExtractor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/RoomMemberExtractor.kt @@ -18,12 +18,13 @@ internal class RoomMemberExtractor(private val realm: Realm, val sender = event.sender ?: return null // 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() - 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).last(since = event.stateIndex)?.asDomain()?.content() } else { baseQuery(realm, roomId, sender).last(since = event.stateIndex)?.asDomain()?.content() } + return roomMember } private fun baseQuery(realm: Realm, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 57e1fc6c9b..a0feebc02c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -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.send.EventFactory 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.model.ChunkEntity import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom @@ -33,7 +33,7 @@ internal class DefaultSendService(private val roomId: String, monarchy.tryTransactionAsync { realm -> val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) ?: return@tryTransactionAsync - chunkEntity.addOrUpdate(event, PaginationDirection.FORWARDS) + chunkEntity.add(event, PaginationDirection.FORWARDS) chunkEntity.updateDisplayIndexes() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt index 5ea69ba9bd..9ffad74dfa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt @@ -5,6 +5,7 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback 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.addStateEvents import im.vector.matrix.android.internal.database.helper.deleteOnCascade import im.vector.matrix.android.internal.database.helper.merge @@ -62,7 +63,7 @@ internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, nextToken = response.nextToken } - currentChunk.addOrUpdate(response.event, PaginationDirection.FORWARDS, isUnlinked = true) + currentChunk.add(response.event, PaginationDirection.FORWARDS, isUnlinked = true) // Now, handles chunk merge val prevChunk = ChunkEntity.find(realm, roomId, nextToken = response.prevToken) val nextChunk = ChunkEntity.find(realm, roomId, prevToken = response.nextToken) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationDirection.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationDirection.kt index b65a0ca0b5..ffb8dd0be3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationDirection.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationDirection.kt @@ -13,4 +13,11 @@ internal enum class PaginationDirection(val value: String) { */ BACKWARDS("b"); + fun reversed(): PaginationDirection { + return when (this) { + FORWARDS -> BACKWARDS + BACKWARDS -> FORWARDS + } + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt index 02f4e8231c..795ce5c29e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt @@ -5,7 +5,12 @@ import arrow.core.failure import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback 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.RoomEntity import im.vector.matrix.android.internal.database.query.find @@ -63,36 +68,47 @@ internal class PaginationRequest(private val roomAPI: RoomAPI, return monarchy .tryTransactionSync { realm -> 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().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().apply { prevToken = receivedChunk.prevToken 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 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) { - currentChunk.merge(prevChunk, PaginationDirection.BACKWARDS) + newChunk.merge(prevChunk, PaginationDirection.BACKWARDS) roomEntity.deleteOnCascade(prevChunk) } if (nextChunk != null) { - currentChunk.merge(nextChunk, PaginationDirection.FORWARDS) - roomEntity.deleteOnCascade(nextChunk) + nextChunk.merge(newChunk, PaginationDirection.BACKWARDS) + newChunk.deleteOnCascade() + currentChunk = nextChunk } - val eventIds = receivedChunk.events.mapNotNull { it.eventId } + val newEventIds = receivedChunk.events.mapNotNull { it.eventId } ChunkEntity - .findAllIncludingEvents(realm, eventIds) + .findAllIncludingEvents(realm, newEventIds) .filter { it != currentChunk } .forEach { overlapped -> - currentChunk.merge(overlapped, direction) - roomEntity.deleteOnCascade(overlapped) + if (direction == PaginationDirection.BACKWARDS) { + currentChunk.merge(overlapped, PaginationDirection.BACKWARDS) + roomEntity.deleteOnCascade(overlapped) + } else { + overlapped.merge(currentChunk, PaginationDirection.BACKWARDS) + currentChunk = overlapped + } } roomEntity.addOrUpdate(currentChunk) + // TODO : there is an issue with the pagination sending unwanted room member events val isUnlinked = currentChunk.isUnlinked() roomEntity.addStateEvents(receivedChunk.stateEvents, isUnlinked = isUnlinked)