From bc1462486dd26b376ce07d725c96594c1858f151 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 22 Nov 2018 11:20:50 +0100 Subject: [PATCH] State index : rework the algorithm to manage limited chunks and avoid using wrong state events (Int.Min overriding) --- .idea/dictionaries/ganfra.xml | 1 + .../database/helper/ChunkEntityHelper.kt | 27 ++++++++--- .../database/helper/RoomEntityHelper.kt | 16 +++++++ .../internal/database/model/ChunkEntity.kt | 18 -------- .../internal/database/model/EventEntity.kt | 3 ++ .../internal/database/model/RoomEntity.kt | 5 +- .../database/query/ChunkEntityQueries.kt | 3 -- .../database/query/EventEntityQueries.kt | 31 +++++++++---- .../internal/session/room/RoomModule.kt | 4 +- .../room/members/LoadRoomMembersRequest.kt | 9 ++-- .../room/members/RoomMemberExtractor.kt | 46 +++++++------------ .../room/timeline/GetContextOfEventRequest.kt | 9 ++-- .../session/room/timeline/GetEventRequest.kt | 4 +- .../room/timeline/PaginationRequest.kt | 9 ++-- .../internal/session/sync/RoomSyncHandler.kt | 41 +++++++++++------ .../session/sync/StateEventsChunkHandler.kt | 32 ------------- .../internal/session/sync/SyncModule.kt | 6 +-- 17 files changed, 120 insertions(+), 144 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/StateEventsChunkHandler.kt diff --git a/.idea/dictionaries/ganfra.xml b/.idea/dictionaries/ganfra.xml index 806a1048..1ca7a97a 100644 --- a/.idea/dictionaries/ganfra.xml +++ b/.idea/dictionaries/ganfra.xml @@ -6,6 +6,7 @@ merlins moshi synchronizer + untimelined \ No newline at end of file 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 07316c10..03b4b1e8 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 @@ -1,12 +1,15 @@ 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.asEntity import im.vector.matrix.android.internal.database.model.ChunkEntity +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 internal fun ChunkEntity.merge(chunkEntity: ChunkEntity, @@ -25,16 +28,16 @@ internal fun ChunkEntity.merge(chunkEntity: ChunkEntity, internal fun ChunkEntity.addAll(events: List, direction: PaginationDirection, - updateStateIndex: Boolean = true) { + stateIndexOffset: Int = 0) { events.forEach { event -> - addOrUpdate(event, direction, updateStateIndex) + addOrUpdate(event, direction, stateIndexOffset) } } internal fun ChunkEntity.addOrUpdate(event: Event, direction: PaginationDirection, - updateStateIndex: Boolean = true) { + stateIndexOffset: Int = 0) { if (!isManaged) { throw IllegalStateException("Chunk entity should be managed to use fast contains") } @@ -43,11 +46,16 @@ internal fun ChunkEntity.addOrUpdate(event: Event, return } - if (updateStateIndex && event.isStateEvent()) { - updateStateIndex(direction) + var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset) + if (direction == PaginationDirection.FORWARDS && event.isStateEvent()) { + currentStateIndex += 1 + } else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) { + val lastEventType = events.last()?.type ?: "" + if (EventType.isStateEvent(lastEventType)) { + currentStateIndex -= 1 + } } - val currentStateIndex = stateIndex(direction) if (!events.fastContains(event.eventId)) { val eventEntity = event.asEntity() eventEntity.stateIndex = currentStateIndex @@ -57,4 +65,11 @@ internal fun ChunkEntity.addOrUpdate(event: Event, val eventEntity = events.find(event.eventId) eventEntity?.stateIndex = currentStateIndex } +} + +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 } \ 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 7cf8ecbb..0b69f2c0 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 @@ -1,5 +1,7 @@ package im.vector.matrix.android.internal.database.helper +import im.vector.matrix.android.api.session.events.model.Event +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.RoomEntity @@ -15,4 +17,18 @@ internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) { if (!chunks.contains(chunkEntity)) { chunks.add(chunkEntity) } +} + +internal fun RoomEntity.addStateEvents(stateEvents: List, stateIndex: Int = Int.MIN_VALUE) { + if (!isManaged) { + throw IllegalStateException("Chunk entity should be managed to use fast contains") + } + stateEvents.forEach { event -> + if (event.eventId == null) { + return@forEach + } + val eventEntity = event.asEntity() + eventEntity.stateIndex = stateIndex + untimelinedStateEvents.add(eventEntity) + } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt index 64aa9e28..354720df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt @@ -1,6 +1,5 @@ package im.vector.matrix.android.internal.database.model -import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import io.realm.RealmList import io.realm.RealmObject import io.realm.RealmResults @@ -8,8 +7,6 @@ import io.realm.annotations.LinkingObjects internal open class ChunkEntity(var prevToken: String? = null, var nextToken: String? = null, - var prevStateIndex: Int = -1, - var nextStateIndex: Int = 1, var isLast: Boolean = false, var events: RealmList = RealmList() ) : RealmObject() { @@ -18,19 +15,4 @@ internal open class ChunkEntity(var prevToken: String? = null, val room: RealmResults? = null companion object - - fun stateIndex(direction: PaginationDirection): Int { - return when (direction) { - PaginationDirection.FORWARDS -> nextStateIndex - PaginationDirection.BACKWARDS -> prevStateIndex - } - } - - fun updateStateIndex(direction: PaginationDirection) { - when (direction) { - PaginationDirection.FORWARDS -> nextStateIndex += 1 - PaginationDirection.BACKWARDS -> prevStateIndex -= 1 - } - } - } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index 2bcb91e8..96ba986a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -24,4 +24,7 @@ internal open class EventEntity(var eventId: String = "", @LinkingObjects("events") val chunk: RealmResults? = null + @LinkingObjects("untimelinedStateEvents") + val room: RealmResults? = null + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt index e8df6e34..906dff0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt @@ -8,8 +8,9 @@ import io.realm.annotations.PrimaryKey import kotlin.properties.Delegates internal open class RoomEntity(@PrimaryKey var roomId: String = "", - var chunks: RealmList = RealmList(), - var areAllMembersLoaded: Boolean = false + var chunks: RealmList = RealmList(), + var untimelinedStateEvents: RealmList = RealmList(), + var areAllMembersLoaded: Boolean = false ) : RealmObject() { private var membershipStr: String = MyMembership.NONE.name diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt index 5f1a7a15..e3b071a2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt @@ -1,6 +1,5 @@ package im.vector.matrix.android.internal.database.query -import im.vector.matrix.android.internal.database.DBConstants import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntityFields import im.vector.matrix.android.internal.database.model.RoomEntityFields @@ -34,7 +33,5 @@ internal fun ChunkEntity.Companion.findLastLiveChunkFromRoom(realm: Realm, roomI internal fun ChunkEntity.Companion.findAllIncludingEvents(realm: Realm, eventIds: List): RealmResults { return realm.where() .`in`(ChunkEntityFields.EVENTS.EVENT_ID, eventIds.toTypedArray()) - .notEqualTo(ChunkEntityFields.PREV_TOKEN, DBConstants.STATE_EVENTS_CHUNK_TOKEN) - .notEqualTo(ChunkEntityFields.NEXT_TOKEN, DBConstants.STATE_EVENTS_CHUNK_TOKEN) .findAll() } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index f54881a3..91a828c7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -18,7 +18,11 @@ internal fun EventEntity.Companion.where(realm: Realm, eventId: String): RealmQu internal fun EventEntity.Companion.where(realm: Realm, roomId: String? = null, type: String? = null): RealmQuery { val query = realm.where() if (roomId != null) { - query.equalTo("${EventEntityFields.CHUNK}.${ChunkEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId) + query.beginGroup() + .equalTo("${EventEntityFields.CHUNK}.${ChunkEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId) + .or() + .equalTo("${EventEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId) + .endGroup() } if (type != null) { query.equalTo(EventEntityFields.TYPE, type) @@ -26,21 +30,28 @@ internal fun EventEntity.Companion.where(realm: Realm, roomId: String? = null, t return query } -internal fun RealmQuery.findMostSuitableStateEvent(stateIndex: Int): EventEntity? { - val sort: Sort = if (stateIndex < 0) { - this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, stateIndex) - Sort.ASCENDING - } else { - this.lessThanOrEqualTo(EventEntityFields.STATE_INDEX, stateIndex) - Sort.DESCENDING +internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { + if (from != null) { + if (strict) { + this.greaterThan(EventEntityFields.STATE_INDEX, from) + } else { + this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, from) + } } return this - .sort(EventEntityFields.STATE_INDEX, sort) + .sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING) .findFirst() } -internal fun RealmQuery.last(): EventEntity? { +internal fun RealmQuery.last(since: Int? = null, strict: Boolean = false): EventEntity? { + if (since != null) { + if (strict) { + this.lessThan(EventEntityFields.STATE_INDEX, since) + } else { + this.lessThanOrEqualTo(EventEntityFields.STATE_INDEX, since) + } + } return this .sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) .findFirst() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index bfd9052e..14dee22b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -25,11 +25,11 @@ class RoomModule : Module { } scope(DefaultSession.SCOPE) { - LoadRoomMembersRequest(get(), get(), get(), get()) + LoadRoomMembersRequest(get(), get(), get()) } scope(DefaultSession.SCOPE) { - PaginationRequest(get(), get(), get(), get()) + PaginationRequest(get(), get(), get()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt index 430b08b6..c3fca61d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt @@ -5,12 +5,11 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.model.Membership 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.addStateEvents import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.tryTransactionSync @@ -20,8 +19,7 @@ import kotlinx.coroutines.withContext internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI, private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val stateEventsChunkHandler: StateEventsChunkHandler) { + private val coroutineDispatchers: MatrixCoroutineDispatchers) { fun execute(roomId: String, streamToken: String?, @@ -57,8 +55,7 @@ internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI, val roomMembers = RoomMembers(realm, roomId).getLoaded() val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) } - val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert) - roomEntity.addOrUpdate(chunk) + roomEntity.addStateEvents(eventsToInsert) roomEntity.areAllMembersLoaded = true } .map { response } 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 d7c51b71..08012cb5 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 @@ -2,14 +2,12 @@ package im.vector.matrix.android.internal.session.room.members import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.model.RoomMember -import im.vector.matrix.android.internal.database.DBConstants import im.vector.matrix.android.internal.database.mapper.asDomain -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.find -import im.vector.matrix.android.internal.database.query.findMostSuitableStateEvent import im.vector.matrix.android.internal.database.query.last +import im.vector.matrix.android.internal.database.query.next +import im.vector.matrix.android.internal.database.query.where import io.realm.Realm import io.realm.RealmQuery @@ -17,35 +15,23 @@ internal class RoomMemberExtractor(private val realm: Realm, private val roomId: String) { fun extractFrom(event: EventEntity): RoomMember? { - val stateIndex = event.stateIndex - val chunkEntity = event.chunk?.firstOrNull() - ?: throw IllegalStateException("An event should be attached to a chunk") - - // First of all, try to get the most suitable state event coming from a chunk - val roomMember = buildQuery(chunkEntity, event.sender) - .findMostSuitableStateEvent(stateIndex = stateIndex) - ?.asDomain() - ?.pickContent(stateIndex) - - if (roomMember != null) { - return roomMember + 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) { + 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() } - - // If the content is null, we try get the last state event coming from a state events chunk - val stateChunkEntity = ChunkEntity.find(realm, roomId, nextToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN) - ?: return null - - return buildQuery(stateChunkEntity, event.sender) - .last() - ?.asDomain() - ?.content() } - private fun buildQuery(chunk: ChunkEntity, - sender: String?): RealmQuery { - return chunk.events - .where() - .equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_MEMBER) + private fun baseQuery(realm: Realm, + roomId: String, + sender: String): RealmQuery { + + return EventEntity + .where(realm, roomId = roomId, type = EventType.STATE_ROOM_MEMBER) .equalTo(EventEntityFields.STATE_KEY, sender) } 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 fcab9e82..248c616a 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 @@ -6,6 +6,7 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable 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.merge import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -15,7 +16,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.legacy.util.FilterUtil import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.tryTransactionSync @@ -26,8 +26,7 @@ import kotlinx.coroutines.withContext internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val stateEventsChunkHandler: StateEventsChunkHandler + private val coroutineDispatchers: MatrixCoroutineDispatchers ) { fun execute(roomId: String, @@ -91,9 +90,7 @@ internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, } */ roomEntity.addOrUpdate(currentChunk) - - val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, response.stateEvents) - roomEntity.addOrUpdate(stateEventsChunk) + roomEntity.addStateEvents(response.stateEvents) } .map { response } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt index 1a8ebc7b..28abd072 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt @@ -6,7 +6,6 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope @@ -15,8 +14,7 @@ import kotlinx.coroutines.withContext internal class GetEventRequest(private val roomAPI: RoomAPI, private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val stateEventsChunkHandler: StateEventsChunkHandler + private val coroutineDispatchers: MatrixCoroutineDispatchers ) { fun execute(roomId: String, 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 5ca59f71..29a3e6c9 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 @@ -7,6 +7,7 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable 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.merge import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -17,7 +18,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.legacy.util.FilterUtil import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.session.sync.StateEventsChunkHandler import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.tryTransactionSync @@ -28,8 +28,7 @@ import kotlinx.coroutines.withContext internal class PaginationRequest(private val roomAPI: RoomAPI, private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val stateEventsChunkHandler: StateEventsChunkHandler + private val coroutineDispatchers: MatrixCoroutineDispatchers ) { fun execute(roomId: String, @@ -92,10 +91,8 @@ internal class PaginationRequest(private val roomAPI: RoomAPI, } roomEntity.addOrUpdate(currentChunk) - // TODO : there is an issue with the pagination sending unwanted room member events - val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents) - roomEntity.addOrUpdate(stateEventsChunk) + roomEntity.addStateEvents(receivedChunk.stateEvents) } .map { receivedChunk } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 7b122711..adf0b9c7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -6,10 +6,11 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.model.MyMembership 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.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.model.RoomSummaryEntity -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.timeline.PaginationDirection @@ -23,7 +24,6 @@ import io.realm.kotlin.createObject internal class RoomSyncHandler(private val monarchy: Monarchy, - private val stateEventsChunkHandler: StateEventsChunkHandler, private val readReceiptHandler: ReadReceiptHandler) { sealed class HandlingStrategy { @@ -56,7 +56,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, roomSync: RoomSync): RoomEntity { val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: RoomEntity(roomId) + ?: realm.createObject(roomId) if (roomEntity.membership == MyMembership.INVITED) { roomEntity.chunks.deleteAllFromRealm() @@ -64,13 +64,27 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, roomEntity.membership = MyMembership.JOINED + val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) + val isInitialSync = lastChunk == null + val lastStateIndex = lastChunk?.lastStateIndex(PaginationDirection.FORWARDS) ?: 0 + val numberOfStateEvents = roomSync.state?.events?.size ?: 0 + val stateIndexOffset = lastStateIndex + numberOfStateEvents + if (roomSync.state != null && roomSync.state.events.isNotEmpty()) { - val chunkEntity = stateEventsChunkHandler.handle(realm, roomId, roomSync.state.events) - roomEntity.addOrUpdate(chunkEntity) + val untimelinedStateIndex = if (isInitialSync) Int.MIN_VALUE else stateIndexOffset + roomEntity.addStateEvents(roomSync.state.events, stateIndex = untimelinedStateIndex) } if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) { - val chunkEntity = handleTimelineEvents(realm, roomId, roomSync.timeline.events, roomSync.timeline.prevToken, isLimited = roomSync.timeline.limited) + val timelineStateOffset = if (isInitialSync || roomSync.timeline.limited.not()) 0 else stateIndexOffset + val chunkEntity = handleTimelineEvents( + realm, + roomId, + roomSync.timeline.events, + roomSync.timeline.prevToken, + roomSync.timeline.limited, + timelineStateOffset + ) roomEntity.addOrUpdate(chunkEntity) } @@ -111,22 +125,19 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, roomId: String, eventList: List, prevToken: String? = null, - nextToken: String? = null, - isLimited: Boolean = true): ChunkEntity { + isLimited: Boolean = true, + stateIndexOffset: Int = 0): ChunkEntity { val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) - val chunkEntity = if (!isLimited) { + val chunkEntity = if (!isLimited && lastChunk != null) { lastChunk } else { - val eventIds = eventList.filter { it.eventId != null }.map { it.eventId!! } - ChunkEntity.findAllIncludingEvents(realm, eventIds).firstOrNull() - } ?: realm.createObject().apply { this.prevToken = prevToken } + realm.createObject().apply { this.prevToken = prevToken } + } lastChunk?.isLast = false chunkEntity.isLast = true - chunkEntity.nextToken = nextToken - - chunkEntity.addAll(eventList, PaginationDirection.FORWARDS) + chunkEntity.addAll(eventList, PaginationDirection.FORWARDS, stateIndexOffset) return chunkEntity } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/StateEventsChunkHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/StateEventsChunkHandler.kt deleted file mode 100644 index 8046b74f..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/StateEventsChunkHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package im.vector.matrix.android.internal.session.sync - -import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.internal.database.DBConstants -import im.vector.matrix.android.internal.database.helper.addAll -import im.vector.matrix.android.internal.database.model.ChunkEntity -import im.vector.matrix.android.internal.database.query.find -import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection -import io.realm.Realm -import io.realm.kotlin.createObject - -internal class StateEventsChunkHandler { - - fun handle(realm: Realm, roomId: String, stateEvents: List): ChunkEntity { - val chunkEntity = ChunkEntity.find(realm, roomId, nextToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN) - ?: realm.createObject() - .apply { - prevToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN - nextToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN - nextStateIndex = Int.MIN_VALUE - prevStateIndex = Int.MIN_VALUE - } - - // We always consider going forwards as data from server are the most recent - chunkEntity.addAll(stateEvents, direction = PaginationDirection.FORWARDS, updateStateIndex = false) - return chunkEntity - } - - -} - - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt index 86b739e4..0ae3e618 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt @@ -17,16 +17,12 @@ internal class SyncModule : Module { retrofit.create(SyncAPI::class.java) } - scope(DefaultSession.SCOPE) { - StateEventsChunkHandler() - } - scope(DefaultSession.SCOPE) { ReadReceiptHandler() } scope(DefaultSession.SCOPE) { - RoomSyncHandler(get(), get(), get()) + RoomSyncHandler(get(), get()) } scope(DefaultSession.SCOPE) {