Timeline is back

This commit is contained in:
Valere 2019-07-05 17:00:13 +02:00
parent cbfd2af74b
commit f01e796271
5 changed files with 67 additions and 41 deletions

View File

@ -18,6 +18,7 @@ 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.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
@ -112,11 +113,16 @@ internal fun ChunkEntity.add(roomId: String,
} }
} }


val eventEntity = TimelineEventEntity().apply { val eventEntity = TimelineEventEntity().also {
this.root = event.toEntity(roomId) it.root = event.toEntity(roomId).apply {
this.eventId = event.eventId ?: "" this.stateIndex = currentStateIndex
this.roomId = roomId this.isUnlinked = isUnlinked
this.annotations = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() this.displayIndex = currentDisplayIndex
this.sendState = SendState.SYNCED
}
it.eventId = event.eventId ?: ""
it.roomId = roomId
it.annotations = EventAnnotationsSummaryEntity.where(realm, it.eventId).findFirst()
} }
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.timelineEvents.size val position = if (direction == PaginationDirection.FORWARDS) 0 else this.timelineEvents.size
timelineEvents.add(position, eventEntity) timelineEvents.add(position, eventEntity)

View File

@ -37,6 +37,24 @@ internal fun TimelineEventEntity.Companion.where(realm: Realm, eventIds: List<St
return realm.where<TimelineEventEntity>().`in`(TimelineEventEntityFields.EVENT_ID, eventIds.toTypedArray()) return realm.where<TimelineEventEntity>().`in`(TimelineEventEntityFields.EVENT_ID, eventIds.toTypedArray())
} }


internal fun TimelineEventEntity.Companion.where(realm: Realm,
roomId: String? = null,
type: String? = null,
linkFilterMode: EventEntity.LinkFilterMode = LINKED_ONLY): RealmQuery<TimelineEventEntity> {
val query = realm.where<TimelineEventEntity>()
if (roomId != null) {
query.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
}
if (type != null) {
query.equalTo(TimelineEventEntityFields.ROOT.TYPE, type)
}
return when (linkFilterMode) {
LINKED_ONLY -> query.equalTo(TimelineEventEntityFields.ROOT.IS_UNLINKED, false)
UNLINKED_ONLY -> query.equalTo(TimelineEventEntityFields.ROOT.IS_UNLINKED, true)
BOTH -> query
}
}



internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm, internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
roomId: String, roomId: String,
@ -51,9 +69,9 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
} }
val query = eventList?.where() val query = eventList?.where()
if (includedTypes.isNotEmpty()) { if (includedTypes.isNotEmpty()) {
query?.`in`(EventEntityFields.TYPE, includedTypes.toTypedArray()) query?.`in`(TimelineEventEntityFields.ROOT.TYPE, includedTypes.toTypedArray())
} else if (excludedTypes.isNotEmpty()) { } else if (excludedTypes.isNotEmpty()) {
query?.not()?.`in`(EventEntityFields.TYPE, excludedTypes.toTypedArray()) query?.not()?.`in`(TimelineEventEntityFields.ROOT.TYPE, excludedTypes.toTypedArray())
} }
return query return query
?.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING) ?.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.mapper.asDomain 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.EventEntity
import im.vector.matrix.android.internal.database.query.types
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
@ -41,7 +42,7 @@ internal class EventsPruner @Inject constructor(@SessionDatabase realmConfigurat
private val taskExecutor: TaskExecutor) : private val taskExecutor: TaskExecutor) :
RealmLiveEntityObserver<EventEntity>(realmConfiguration) { RealmLiveEntityObserver<EventEntity>(realmConfiguration) {


override val query = Monarchy.Query<EventEntity> { EventEntity.where(it, type = EventType.REDACTION) } override val query = Monarchy.Query<EventEntity> { EventEntity.types(it, listOf(EventType.REDACTION)) }


override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) { override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) {
Timber.v("Event pruner called with ${inserted.size} insertions") Timber.v("Event pruner called with ${inserted.size} insertions")

View File

@ -27,12 +27,12 @@ import im.vector.matrix.android.api.util.addTo
import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.NewSessionListener
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.model.ChunkEntity 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.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.EventEntity 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.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.findIncludingEvent import im.vector.matrix.android.internal.database.query.findIncludingEvent
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
@ -91,7 +91,7 @@ internal class DefaultTimeline(
private val cancelableBag = CancelableBag() private val cancelableBag = CancelableBag()
private val debouncer = Debouncer(mainHandler) private val debouncer = Debouncer(mainHandler)


private lateinit var liveEvents: RealmResults<EventEntity> private lateinit var liveEvents: RealmResults<TimelineEventEntity>
private var roomEntity: RoomEntity? = null private var roomEntity: RoomEntity? = null


private var prevDisplayIndex: Int = DISPLAY_INDEX_UNKNOWN private var prevDisplayIndex: Int = DISPLAY_INDEX_UNKNOWN
@ -105,7 +105,7 @@ internal class DefaultTimeline(


private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity> private lateinit var eventRelations: RealmResults<EventAnnotationsSummaryEntity>


private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<EventEntity>> { results, changeSet -> private val eventsChangeListener = OrderedRealmCollectionChangeListener<RealmResults<TimelineEventEntity>> { results, changeSet ->
if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL) { if (changeSet.state == OrderedCollectionChangeSet.State.INITIAL) {
handleInitialLoad() handleInitialLoad()
} else { } else {
@ -118,9 +118,9 @@ internal class DefaultTimeline(
} }
changeSet.insertionRanges.forEach { range -> changeSet.insertionRanges.forEach { range ->
val (startDisplayIndex, direction) = if (range.startIndex == 0) { val (startDisplayIndex, direction) = if (range.startIndex == 0) {
Pair(liveEvents[range.length - 1]!!.displayIndex, Timeline.Direction.FORWARDS) Pair(liveEvents[range.length - 1]!!.root!!.displayIndex, Timeline.Direction.FORWARDS)
} else { } else {
Pair(liveEvents[range.startIndex]!!.displayIndex, Timeline.Direction.BACKWARDS) Pair(liveEvents[range.startIndex]!!.root!!.displayIndex, Timeline.Direction.BACKWARDS)
} }
val state = getPaginationState(direction) val state = getPaginationState(direction)
if (state.isPaginating) { if (state.isPaginating) {
@ -233,7 +233,7 @@ internal class DefaultTimeline(
} }


liveEvents = buildEventQuery(realm) liveEvents = buildEventQuery(realm)
.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) .sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
.findAllAsync() .findAllAsync()
.also { it.addChangeListener(eventsChangeListener) } .also { it.addChangeListener(eventsChangeListener) }


@ -268,13 +268,13 @@ internal class DefaultTimeline(


private fun hasMoreInCache(direction: Timeline.Direction): Boolean { private fun hasMoreInCache(direction: Timeline.Direction): Boolean {
val localRealm = Realm.getInstance(realmConfiguration) val localRealm = Realm.getInstance(realmConfiguration)
val eventEntity = buildEventQuery(localRealm).findFirst(direction) ?: return false val timelineEventEntity = buildEventQuery(localRealm).findFirst(direction) ?: return false
val hasMoreInCache = if (direction == Timeline.Direction.FORWARDS) { val hasMoreInCache = if (direction == Timeline.Direction.FORWARDS) {
val firstEvent = builtEvents.firstOrNull() ?: return true val firstEvent = builtEvents.firstOrNull() ?: return true
firstEvent.displayIndex < eventEntity.displayIndex firstEvent.displayIndex < timelineEventEntity.root!!.displayIndex
} else { } else {
val lastEvent = builtEvents.lastOrNull() ?: return true val lastEvent = builtEvents.lastOrNull() ?: return true
lastEvent.displayIndex > eventEntity.displayIndex lastEvent.displayIndex > timelineEventEntity.root!!.displayIndex
} }
localRealm.close() localRealm.close()
return hasMoreInCache return hasMoreInCache
@ -287,7 +287,7 @@ internal class DefaultTimeline(
currentChunk.isLastForward currentChunk.isLastForward
} else { } else {
val eventEntity = buildEventQuery(localRealm).findFirst(direction) val eventEntity = buildEventQuery(localRealm).findFirst(direction)
currentChunk.isLastBackward || eventEntity?.type == EventType.STATE_ROOM_CREATE currentChunk.isLastBackward || eventEntity?.root?.type == EventType.STATE_ROOM_CREATE
} }
localRealm.close() localRealm.close()
return hasReachedEnd return hasReachedEnd
@ -360,11 +360,11 @@ internal class DefaultTimeline(
private fun handleInitialLoad() { private fun handleInitialLoad() {
var shouldFetchInitialEvent = false var shouldFetchInitialEvent = false
val initialDisplayIndex = if (isLive) { val initialDisplayIndex = if (isLive) {
liveEvents.firstOrNull()?.displayIndex liveEvents.firstOrNull()?.root?.displayIndex
} else { } else {
val initialEvent = liveEvents.where().equalTo(EventEntityFields.EVENT_ID, initialEventId).findFirst() val initialEvent = liveEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, initialEventId).findFirst()
shouldFetchInitialEvent = initialEvent == null shouldFetchInitialEvent = initialEvent == null
initialEvent?.displayIndex initialEvent?.root?.displayIndex
} ?: DISPLAY_INDEX_UNKNOWN } ?: DISPLAY_INDEX_UNKNOWN


prevDisplayIndex = initialDisplayIndex prevDisplayIndex = initialDisplayIndex
@ -448,14 +448,14 @@ internal class DefaultTimeline(
if (offsetResults.isEmpty()) { if (offsetResults.isEmpty()) {
return 0 return 0
} }
val offsetIndex = offsetResults.last()!!.displayIndex val offsetIndex = offsetResults.last()!!.root!!.displayIndex
if (direction == Timeline.Direction.BACKWARDS) { if (direction == Timeline.Direction.BACKWARDS) {
prevDisplayIndex = offsetIndex - 1 prevDisplayIndex = offsetIndex - 1
} else { } else {
nextDisplayIndex = offsetIndex + 1 nextDisplayIndex = offsetIndex + 1
} }
offsetResults.forEach { eventEntity -> offsetResults.forEach { eventEntity ->
val timelineEvent = timelineEventFactory.create(eventEntity, eventEntity.realm) val timelineEvent = eventEntity.asDomain()
val position = if (direction == Timeline.Direction.FORWARDS) 0 else builtEvents.size val position = if (direction == Timeline.Direction.FORWARDS) 0 else builtEvents.size
builtEvents.add(position, timelineEvent) builtEvents.add(position, timelineEvent)
//Need to shift :/ //Need to shift :/
@ -472,16 +472,16 @@ internal class DefaultTimeline(
*/ */
private fun getOffsetResults(startDisplayIndex: Int, private fun getOffsetResults(startDisplayIndex: Int,
direction: Timeline.Direction, direction: Timeline.Direction,
count: Long): RealmResults<EventEntity> { count: Long): RealmResults<TimelineEventEntity> {
val offsetQuery = liveEvents.where() val offsetQuery = liveEvents.where()
if (direction == Timeline.Direction.BACKWARDS) { if (direction == Timeline.Direction.BACKWARDS) {
offsetQuery offsetQuery
.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) .sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
.lessThanOrEqualTo(EventEntityFields.DISPLAY_INDEX, startDisplayIndex) .lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
} else { } else {
offsetQuery offsetQuery
.sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) .sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING)
.greaterThanOrEqualTo(EventEntityFields.DISPLAY_INDEX, startDisplayIndex) .greaterThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
} }
return offsetQuery return offsetQuery
.filterAllowedTypes() .filterAllowedTypes()
@ -490,15 +490,15 @@ internal class DefaultTimeline(
} }




private fun buildEventQuery(realm: Realm): RealmQuery<EventEntity> { private fun buildEventQuery(realm: Realm): RealmQuery<TimelineEventEntity> {
return if (initialEventId == null) { return if (initialEventId == null) {
EventEntity TimelineEventEntity
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.LINKED_ONLY) .where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.LINKED_ONLY)
.equalTo("${EventEntityFields.CHUNK}.${ChunkEntityFields.IS_LAST_FORWARD}", true) .equalTo("${TimelineEventEntityFields.CHUNK}.${ChunkEntityFields.IS_LAST_FORWARD}", true)
} else { } else {
EventEntity TimelineEventEntity
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH) .where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH)
.`in`("${EventEntityFields.CHUNK}.${ChunkEntityFields.EVENTS.EVENT_ID}", arrayOf(initialEventId)) .`in`("${TimelineEventEntityFields.CHUNK}.${ChunkEntityFields.TIMELINE_EVENTS.EVENT_ID}", arrayOf(initialEventId))
} }
} }


@ -514,7 +514,7 @@ internal class DefaultTimeline(
realm.executeTransaction { realm.executeTransaction {
val unlinkedChunks = ChunkEntity val unlinkedChunks = ChunkEntity
.where(it, roomId = roomId) .where(it, roomId = roomId)
.equalTo(ChunkEntityFields.EVENTS.IS_UNLINKED, true) .equalTo("${ChunkEntityFields.TIMELINE_EVENTS.ROOT}.${EventEntityFields.IS_UNLINKED}", true)
.findAll() .findAll()
unlinkedChunks.deleteAllFromRealm() unlinkedChunks.deleteAllFromRealm()
} }
@ -537,19 +537,19 @@ internal class DefaultTimeline(
return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS return if (this == Timeline.Direction.BACKWARDS) PaginationDirection.BACKWARDS else PaginationDirection.FORWARDS
} }


private fun RealmQuery<EventEntity>.findFirst(direction: Timeline.Direction): EventEntity? { private fun RealmQuery<TimelineEventEntity>.findFirst(direction: Timeline.Direction): TimelineEventEntity? {
return if (direction == Timeline.Direction.FORWARDS) { return if (direction == Timeline.Direction.FORWARDS) {
sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
} else { } else {
sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING)
} }
.filterAllowedTypes() .filterAllowedTypes()
.findFirst() .findFirst()
} }


private fun RealmQuery<EventEntity>.filterAllowedTypes(): RealmQuery<EventEntity> { private fun RealmQuery<TimelineEventEntity>.filterAllowedTypes(): RealmQuery<TimelineEventEntity> {
if (allowedTypes != null) { if (allowedTypes != null) {
`in`(EventEntityFields.TYPE, allowedTypes.toTypedArray()) `in`(TimelineEventEntityFields.ROOT.TYPE, allowedTypes.toTypedArray())
} }
return this return this
} }

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.model.EventEntity 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.model.EventEntityFields
import im.vector.matrix.android.internal.database.query.types
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
@ -38,7 +39,7 @@ internal class UserEntityUpdater @Inject constructor(@SessionDatabase realmConfi


override val query = Monarchy.Query<EventEntity> { override val query = Monarchy.Query<EventEntity> {
EventEntity EventEntity
.where(it, type = EventType.STATE_ROOM_MEMBER) .types(it, listOf(EventType.STATE_ROOM_MEMBER))
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) .sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
.distinct(EventEntityFields.STATE_KEY) .distinct(EventEntityFields.STATE_KEY)