mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 03:48:12 +03:00
Introduce state index trying to replicate RoomState logic. WIP #2
This commit is contained in:
parent
6115b05d97
commit
18af7c83f3
12 changed files with 113 additions and 61 deletions
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue