Skip to content

Commit

Permalink
Introduce state index trying to replicate RoomState logic. WIP #2
Browse files Browse the repository at this point in the history
  • Loading branch information
ganfra committed Nov 14, 2018
1 parent 6115b05 commit 18af7c8
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}

Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,23 @@ 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) {
if (!this.isManaged) {
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,30 @@ 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) {
return@doWithRealm
}

val rootEntity = EventEntity.where(realm, eventId = event.root.eventId).findFirst()
?: 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)
?: return@doWithRealm

val roomMember = RoomMemberExtractor(realm, roomId).extractFrom(rootEntity)
event.enrichWith(EventType.STATE_ROOM_MEMBER, roomMember)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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>> {
Expand All @@ -41,7 +41,7 @@ internal class DefaultTimelineHolder(private val roomId: String,
.filter {
it.canEnrich(enrichedEvent)
}.forEach {
it.enrich(roomId, enrichedEvent)
it.enrich(enrichedEvent)
}
enrichedEvent
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
}
Expand All @@ -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()
Expand All @@ -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)
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@ 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
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
}

Expand Down

0 comments on commit 18af7c8

Please sign in to comment.