From 40f981e4544da530c1d33fc8a45b44b20be55afe Mon Sep 17 00:00:00 2001
From: ganfra <francoisg@matrix.org>
Date: Thu, 15 Nov 2018 12:37:59 +0100
Subject: [PATCH] State index management should be ok now

---
 .../matrix/android/api/session/Session.kt     |  2 +-
 .../database/helper/ChunkEntityHelper.kt      |  5 ---
 .../database/query/ChunkEntityQueries.kt      |  2 --
 .../room/members/LoadRoomMembersRequest.kt    |  2 +-
 .../room/members/RoomMemberExtractor.kt       | 35 +++++++++++++------
 .../room/timeline/PaginationRequest.kt        | 17 +++++----
 .../internal/session/sync/RoomSyncHandler.kt  | 18 ++++++----
 .../session/sync/StateEventsChunkHandler.kt   | 20 ++++++-----
 8 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
index 5297cca2a5..cd4af40182 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt
@@ -1,9 +1,9 @@
 package im.vector.matrix.android.api.session
 
 import android.support.annotation.MainThread
+import im.vector.matrix.android.api.auth.data.SessionParams
 import im.vector.matrix.android.api.session.group.GroupService
 import im.vector.matrix.android.api.session.room.RoomService
-import im.vector.matrix.android.api.auth.data.SessionParams
 
 interface Session : RoomService, GroupService {
 
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 789adbb2fa..554a0f9fa7 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,9 +3,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.EventEntity
 import im.vector.matrix.android.internal.database.query.fastContains
-import im.vector.matrix.android.internal.database.query.where
 import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
 
 internal fun ChunkEntity.add(event: Event, stateIndex: Int, paginationDirection: PaginationDirection) {
@@ -16,9 +14,6 @@ internal fun ChunkEntity.add(event: Event, stateIndex: Int, paginationDirection:
     if (event.eventId == null) {
         return
     }
-    if (EventEntity.where(realm, event.eventId).findFirst() != null) {
-        return
-    }
 
     val eventEntity = event.asEntity()
     eventEntity.stateIndex = stateIndex
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 ec21f17871..74b4ab8251 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
@@ -12,8 +12,6 @@ import io.realm.kotlin.where
 internal fun ChunkEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<ChunkEntity> {
     return realm.where<ChunkEntity>()
             .equalTo("${ChunkEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId)
-            .notEqualTo(ChunkEntityFields.PREV_TOKEN, DBConstants.STATE_EVENTS_CHUNK_TOKEN)
-            .notEqualTo(ChunkEntityFields.NEXT_TOKEN, DBConstants.STATE_EVENTS_CHUNK_TOKEN)
 }
 
 internal fun ChunkEntity.Companion.findWithPrevToken(realm: Realm, roomId: String, prevToken: String?): ChunkEntity? {
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 b24b3bf834..f0b8e5697f 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
@@ -58,7 +58,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, PaginationDirection.BACKWARDS)
+                    val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
                     if (!roomEntity.chunks.contains(chunk)) {
                         roomEntity.chunks.add(chunk)
                     }
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 be2bb86367..daaf41a4fc 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,12 +2,14 @@ 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.findMostSuitableStateEvent
+import im.vector.matrix.android.internal.database.query.findWithNextToken
 import im.vector.matrix.android.internal.database.query.last
-import im.vector.matrix.android.internal.database.query.where
 import io.realm.Realm
 import io.realm.RealmQuery
 
@@ -16,25 +18,36 @@ internal class RoomMemberExtractor(private val realm: Realm,
 
     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
-        return buildQuery(realm, roomId, event.sender)
+        val roomMember = buildQuery(chunkEntity, event.sender)
                 .findMostSuitableStateEvent(stateIndex = stateIndex)
                 ?.asDomain()
-                ?.pickContent(stateIndex)
+                ?.pickContent<RoomMember>(stateIndex)
 
-        // If the content is null, we try get the last state event, not coming from a chunk
-                ?: buildQuery(realm, roomId, event.sender)
-                        .last()
-                        ?.asDomain()
-                        ?.content()
+        if (roomMember != null) {
+            return roomMember
+        }
 
+        // If the content is null, we try get the last state event coming from a state events chunk
+        val stateChunkEntity = ChunkEntity.findWithNextToken(realm, roomId, DBConstants.STATE_EVENTS_CHUNK_TOKEN)
+                               ?: return null
+
+        return buildQuery(stateChunkEntity, event.sender)
+                .last()
+                ?.asDomain()
+                ?.content()
     }
 
-    private fun buildQuery(realm: Realm, roomId: String, sender: String?): RealmQuery<EventEntity> {
-        return EventEntity
-                .where(realm, roomId, EventType.STATE_ROOM_MEMBER)
+    private fun buildQuery(chunk: ChunkEntity,
+                           sender: String?): RealmQuery<EventEntity> {
+        return chunk.events
+                .where()
+                .equalTo(EventEntityFields.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/PaginationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt
index 2530885bc9..2a71a98496 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
@@ -8,9 +8,12 @@ import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.internal.database.helper.add
 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.RoomEntity
-import im.vector.matrix.android.internal.database.query.*
+import im.vector.matrix.android.internal.database.query.fastContains
+import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
+import im.vector.matrix.android.internal.database.query.findWithNextToken
+import im.vector.matrix.android.internal.database.query.findWithPrevToken
+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
@@ -62,10 +65,10 @@ 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 = ChunkEntity.findWithPrevToken(realm, roomId, receivedChunk.nextToken)
-                            ?: realm.createObject()
+                                       ?: realm.createObject()
 
                     currentChunk.prevToken = receivedChunk.prevToken
 
@@ -79,10 +82,10 @@ internal class PaginationRequest(private val roomAPI: RoomAPI,
                     val incrementStateIndex = direction.incrementStateIndex
 
                     receivedChunk.events.forEach { event ->
-                        currentChunk.add(event, currentStateIndex, direction)
                         if (EventType.isStateEvent(event.type)) {
                             currentStateIndex += incrementStateIndex
                         }
+                        currentChunk.add(event, currentStateIndex, direction)
                     }
 
                     if (prevChunk != null) {
@@ -110,8 +113,8 @@ internal class PaginationRequest(private val roomAPI: RoomAPI,
 
                     currentChunk.updateStateIndex(currentStateIndex, direction)
 
-
-                    val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents, direction)
+                    // TODO : there is an issue with the pagination sending unwanted room member events
+                    val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents)
                     if (!roomEntity.chunks.contains(stateEventsChunk)) {
                         roomEntity.chunks.add(stateEventsChunk)
                     }
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 b1bcfa9617..10365ddb52 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
@@ -12,7 +12,11 @@ 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
-import im.vector.matrix.android.internal.session.sync.model.*
+import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
+import im.vector.matrix.android.internal.session.sync.model.RoomSync
+import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral
+import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
+import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
 import io.realm.Realm
 import io.realm.kotlin.createObject
 
@@ -39,9 +43,9 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
 
     private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
         val rooms = when (handlingStrategy) {
-            is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
+            is HandlingStrategy.JOINED  -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
             is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
-            is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
+            is HandlingStrategy.LEFT    -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
         }
         realm.insertOrUpdate(rooms)
     }
@@ -51,7 +55,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
                                  roomSync: RoomSync): RoomEntity {
 
         val roomEntity = RoomEntity.where(realm, roomId).findFirst()
-                ?: RoomEntity(roomId)
+                         ?: RoomEntity(roomId)
 
         if (roomEntity.membership == MyMembership.INVITED) {
             roomEntity.chunks.deleteAllFromRealm()
@@ -60,7 +64,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
         roomEntity.membership = MyMembership.JOINED
 
         if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
-            val chunkEntity = stateEventsChunkHandler.handle(realm, roomId, roomSync.state.events, PaginationDirection.FORWARDS)
+            val chunkEntity = stateEventsChunkHandler.handle(realm, roomId, roomSync.state.events)
             if (!roomEntity.chunks.contains(chunkEntity)) {
                 roomEntity.chunks.add(chunkEntity)
             }
@@ -113,7 +117,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
                                   roomSummary: RoomSyncSummary) {
 
         val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
-                ?: RoomSummaryEntity(roomId)
+                                ?: RoomSummaryEntity(roomId)
 
         if (roomSummary.heroes.isNotEmpty()) {
             roomSummaryEntity.heroes.clear()
@@ -150,7 +154,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
         var currentStateIndex = chunkEntity.nextStateIndex
         eventList.forEach { event ->
             if (event.isStateEvent()) {
-                currentStateIndex += 1
+                currentStateIndex += PaginationDirection.FORWARDS.incrementStateIndex
             }
             chunkEntity.add(event, currentStateIndex, PaginationDirection.FORWARDS)
         }
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
index d46ecd0a77..785eedf64c 100644
--- 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
@@ -11,19 +11,21 @@ import io.realm.kotlin.createObject
 
 internal class StateEventsChunkHandler {
 
-    fun handle(realm: Realm, roomId: String, stateEvents: List<Event>, direction: PaginationDirection): ChunkEntity {
+    fun handle(realm: Realm, roomId: String, stateEvents: List<Event>): ChunkEntity {
         val chunkEntity = ChunkEntity.findWithNextToken(realm, roomId, DBConstants.STATE_EVENTS_CHUNK_TOKEN)
-                ?: realm.createObject<ChunkEntity>()
-                        .apply {
-                            prevToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN
-                            nextToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN
-                            nextStateIndex = Int.MIN_VALUE / 2
-                            prevStateIndex = Int.MIN_VALUE / 2
-                        }
+                          ?: realm.createObject<ChunkEntity>()
+                                  .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
+        val direction = PaginationDirection.FORWARDS
         val stateIndex = chunkEntity.stateIndex(direction) + direction.incrementStateIndex
         stateEvents.forEach { event ->
-            chunkEntity.add(event, stateIndex, PaginationDirection.FORWARDS)
+            chunkEntity.add(event, stateIndex, direction)
         }
         chunkEntity.updateStateIndex(stateIndex, direction)
         return chunkEntity