Introduce state index trying to replicate RoomState logic. WIP #2

This commit is contained in:
ganfra 2018-11-14 19:32:42 +01:00
parent 6115b05d97
commit 18af7c83f3
12 changed files with 113 additions and 61 deletions

View file

@ -4,9 +4,9 @@ import im.vector.matrix.android.api.session.events.model.EnrichedEvent
interface EnrichedEventInterceptor {
fun enrich(roomId: String, event: EnrichedEvent)
fun canEnrich(event: EnrichedEvent): Boolean
fun enrich(event: EnrichedEvent)
}

View file

@ -6,18 +6,10 @@ data class EnrichedEvent(val root: Event) {
val metadata = HashMap<String, Any>()
fun enrichWith(events: List<Event>) {
events.forEach { enrichWith(it) }
}
fun enrichWith(event: Event?) {
if (event == null) {
fun enrichWith(key: String?, data: Any?) {
if (key == null || data == null) {
return
}
enrichWith(event.type, event)
}
fun enrichWith(key: String, data: Any) {
if (!metadata.containsKey(key)) {
metadata[key] = data
}
@ -35,5 +27,5 @@ data class EnrichedEvent(val root: Event) {
}
fun EnrichedEvent.roomMember(): RoomMember? {
return getMetadata<Event>(EventType.STATE_ROOM_MEMBER)?.content<RoomMember>()
return getMetadata<RoomMember>(EventType.STATE_ROOM_MEMBER)
}

View file

@ -41,6 +41,14 @@ data class Event(
return moshiAdapter.fromJsonValue(data)
}
internal inline fun <reified T> pickContent(stateIndex: Int): T? {
return if (stateIndex < 0) {
prevContent<T>()
} else {
content<T>()
}
}
companion object {
internal val CONTENT_TYPE: ParameterizedType = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
}

View file

@ -3,7 +3,9 @@ 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) {
@ -11,6 +13,13 @@ internal fun ChunkEntity.add(event: Event, stateIndex: Int, paginationDirection:
throw IllegalStateException("Chunk entity should be managed to use fast contains")
}
if (event.eventId == null) {
return
}
if (EventEntity.where(realm, event.eventId).findFirst() != null) {
return
}
val eventEntity = event.asEntity()
eventEntity.stateIndex = stateIndex

View file

@ -26,11 +26,21 @@ internal fun EventEntity.Companion.where(realm: Realm, roomId: String? = null, t
return query
}
internal fun RealmQuery<EventEntity>.last(from: Int? = null): EventEntity? {
if (from != null) {
this.lessThanOrEqualTo(EventEntityFields.STATE_INDEX, from)
internal fun RealmQuery<EventEntity>.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
}
return this
.sort(EventEntityFields.STATE_INDEX, sort)
.findFirst()
}
internal fun RealmQuery<EventEntity>.last(): EventEntity? {
return this
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
.findFirst()

View file

@ -4,20 +4,19 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
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.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.query.last
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor
internal class MessageEventInterceptor(val monarchy: Monarchy) : EnrichedEventInterceptor {
internal class MessageEventInterceptor(private val monarchy: Monarchy,
private val roomId: String) : EnrichedEventInterceptor {
override fun canEnrich(event: EnrichedEvent): Boolean {
return event.root.type == EventType.MESSAGE
}
override fun enrich(roomId: String, event: EnrichedEvent) {
override fun enrich(event: EnrichedEvent) {
monarchy.doWithRealm { realm ->
if (event.root.eventId == null) {
@ -25,14 +24,10 @@ internal class MessageEventInterceptor(val monarchy: Monarchy) : EnrichedEventIn
}
val rootEntity = EventEntity.where(realm, eventId = event.root.eventId).findFirst()
?: return@doWithRealm
?: return@doWithRealm
val roomMember = EventEntity
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
.equalTo(EventEntityFields.STATE_KEY, event.root.sender)
.last(from = rootEntity.stateIndex)
?.asDomain()
event.enrichWith(roomMember)
val roomMember = RoomMemberExtractor(realm, roomId).extractFrom(rootEntity)
event.enrichWith(EventType.STATE_ROOM_MEMBER, roomMember)
}
}

View file

@ -5,10 +5,12 @@ 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.model.EventEntity
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.room.timeline.PaginationDirection
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
@ -51,12 +53,12 @@ internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI,
.tryTransactionSync { realm ->
// We ignore all the already known members
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 roomMembers = RoomMembers(realm, roomId).getLoaded()
val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }
val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert)
val chunk = stateEventsChunkHandler.handle(realm, roomId, eventsToInsert, PaginationDirection.BACKWARDS)
if (!roomEntity.chunks.contains(chunk)) {
roomEntity.chunks.add(chunk)
}

View file

@ -0,0 +1,41 @@
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.mapper.asDomain
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.last
import im.vector.matrix.android.internal.database.query.where
import io.realm.Realm
import io.realm.RealmQuery
internal class RoomMemberExtractor(private val realm: Realm,
private val roomId: String) {
fun extractFrom(event: EventEntity): RoomMember? {
val stateIndex = event.stateIndex
// First of all, try to get the most suitable state event coming from a chunk
return buildQuery(realm, roomId, event.sender)
.findMostSuitableStateEvent(stateIndex = stateIndex)
?.asDomain()
?.pickContent(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()
}
private fun buildQuery(realm: Realm, roomId: String, sender: String?): RealmQuery<EventEntity> {
return EventEntity
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
.equalTo(EventEntityFields.STATE_KEY, sender)
}
}

View file

@ -23,7 +23,7 @@ internal class DefaultTimelineHolder(private val roomId: String,
init {
boundaryCallback.limit = PAGE_SIZE
eventInterceptors.add(MessageEventInterceptor(monarchy))
eventInterceptors.add(MessageEventInterceptor(monarchy, roomId))
}
override fun liveTimeline(): LiveData<PagedList<EnrichedEvent>> {
@ -41,7 +41,7 @@ internal class DefaultTimelineHolder(private val roomId: String,
.filter {
it.canEnrich(enrichedEvent)
}.forEach {
it.enrich(roomId, enrichedEvent)
it.enrich(enrichedEvent)
}
enrichedEvent
}

View file

@ -8,12 +8,9 @@ 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.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.database.query.*
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
@ -65,10 +62,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
@ -114,7 +111,7 @@ internal class PaginationRequest(private val roomAPI: RoomAPI,
currentChunk.updateStateIndex(currentStateIndex, direction)
val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents)
val stateEventsChunk = stateEventsChunkHandler.handle(realm, roomId, receivedChunk.stateEvents, direction)
if (!roomEntity.chunks.contains(stateEventsChunk)) {
roomEntity.chunks.add(stateEventsChunk)
}

View file

@ -12,11 +12,7 @@ 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.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 im.vector.matrix.android.internal.session.sync.model.*
import io.realm.Realm
import io.realm.kotlin.createObject
@ -43,9 +39,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)
}
@ -55,7 +51,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()
@ -64,7 +60,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)
val chunkEntity = stateEventsChunkHandler.handle(realm, roomId, roomSync.state.events, PaginationDirection.FORWARDS)
if (!roomEntity.chunks.contains(chunkEntity)) {
roomEntity.chunks.add(chunkEntity)
}
@ -117,7 +113,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()
@ -153,7 +149,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
var currentStateIndex = chunkEntity.nextStateIndex
eventList.forEach { event ->
if (event.isStateEvent() && currentStateIndex != 0) {
if (event.isStateEvent()) {
currentStateIndex += 1
}
chunkEntity.add(event, currentStateIndex, PaginationDirection.FORWARDS)

View file

@ -4,7 +4,6 @@ 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.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.query.findWithNextToken
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
import io.realm.Realm
@ -12,18 +11,21 @@ import io.realm.kotlin.createObject
internal class StateEventsChunkHandler {
fun handle(realm: Realm, roomId: String, stateEvents: List<Event>): ChunkEntity {
fun handle(realm: Realm, roomId: String, stateEvents: List<Event>, direction: PaginationDirection): 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
}
?: 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
}
val stateIndex = chunkEntity.stateIndex(direction) + direction.incrementStateIndex
stateEvents.forEach { event ->
chunkEntity.add(event, EventEntity.DEFAULT_STATE_INDEX, PaginationDirection.FORWARDS)
chunkEntity.add(event, stateIndex, PaginationDirection.FORWARDS)
}
chunkEntity.updateStateIndex(stateIndex, direction)
return chunkEntity
}