mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
Try reworking events/timeline process [WIP]
This commit is contained in:
parent
7bcae75314
commit
1d8b81bb04
31 changed files with 287 additions and 511 deletions
|
@ -181,7 +181,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
|||
direction: PaginationDirection,
|
||||
stateIndexOffset: Int = 0) {
|
||||
events.forEach { event ->
|
||||
add(roomId, event, direction, stateIndexOffset)
|
||||
add(roomId, event, direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ import im.vector.matrix.android.internal.crypto.tasks.*
|
|||
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.database.query.whereType
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
|
@ -482,7 +483,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
*/
|
||||
override fun isRoomEncrypted(roomId: String): Boolean {
|
||||
val encryptionEvent = monarchy.fetchCopied {
|
||||
EventEntity.where(it, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION).findFirst()
|
||||
EventEntity.whereType(it, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION).findFirst()
|
||||
}
|
||||
return encryptionEvent != null
|
||||
}
|
||||
|
|
|
@ -17,17 +17,13 @@
|
|||
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.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.toEntity
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.query.find
|
||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.extensions.assertIsManaged
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||
import io.realm.Sort
|
||||
import io.realm.kotlin.createObject
|
||||
|
||||
internal fun ChunkEntity.deleteOnCascade() {
|
||||
|
@ -36,42 +32,11 @@ internal fun ChunkEntity.deleteOnCascade() {
|
|||
this.deleteFromRealm()
|
||||
}
|
||||
|
||||
internal fun ChunkEntity.merge(roomId: String,
|
||||
chunkToMerge: ChunkEntity,
|
||||
direction: PaginationDirection): List<TimelineEventEntity> {
|
||||
assertIsManaged()
|
||||
val isChunkToMergeUnlinked = chunkToMerge.isUnlinked
|
||||
val isCurrentChunkUnlinked = isUnlinked
|
||||
internal fun ChunkEntity.addTimelineEvent(roomId: String,
|
||||
eventEntity: EventEntity,
|
||||
direction: PaginationDirection,
|
||||
roomMemberEvent: Event?): TimelineEventEntity {
|
||||
|
||||
if (isCurrentChunkUnlinked && !isChunkToMergeUnlinked) {
|
||||
this.timelineEvents.forEach { it.root?.isUnlinked = false }
|
||||
}
|
||||
val eventsToMerge: List<TimelineEventEntity>
|
||||
if (direction == PaginationDirection.FORWARDS) {
|
||||
this.nextToken = chunkToMerge.nextToken
|
||||
this.isLastForward = chunkToMerge.isLastForward
|
||||
eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING)
|
||||
} else {
|
||||
this.prevToken = chunkToMerge.prevToken
|
||||
this.isLastBackward = chunkToMerge.isLastBackward
|
||||
eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
}
|
||||
return eventsToMerge
|
||||
.mapNotNull {
|
||||
val event = it.root?.asDomain() ?: return@mapNotNull null
|
||||
add(roomId, event, direction)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ChunkEntity.add(roomId: String,
|
||||
event: Event,
|
||||
direction: PaginationDirection,
|
||||
stateIndexOffset: Int = 0
|
||||
): TimelineEventEntity? {
|
||||
assertIsManaged()
|
||||
if (event.eventId != null && timelineEvents.find(event.eventId) != null) {
|
||||
return null
|
||||
}
|
||||
var currentDisplayIndex = lastDisplayIndex(direction, 0)
|
||||
if (direction == PaginationDirection.FORWARDS) {
|
||||
currentDisplayIndex += 1
|
||||
|
@ -80,22 +45,10 @@ internal fun ChunkEntity.add(roomId: String,
|
|||
currentDisplayIndex -= 1
|
||||
backwardsDisplayIndex = currentDisplayIndex
|
||||
}
|
||||
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
|
||||
if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(event.type)) {
|
||||
currentStateIndex += 1
|
||||
forwardsStateIndex = currentStateIndex
|
||||
} else if (direction == PaginationDirection.BACKWARDS && timelineEvents.isNotEmpty()) {
|
||||
val lastEventType = timelineEvents.last()?.root?.type ?: ""
|
||||
if (EventType.isStateEvent(lastEventType)) {
|
||||
currentStateIndex -= 1
|
||||
backwardsStateIndex = currentStateIndex
|
||||
}
|
||||
}
|
||||
|
||||
val isChunkUnlinked = isUnlinked
|
||||
val localId = TimelineEventEntity.nextId(realm)
|
||||
val eventId = event.eventId ?: ""
|
||||
val senderId = event.senderId ?: ""
|
||||
val eventId = eventEntity.eventId
|
||||
val senderId = eventEntity.sender ?: ""
|
||||
|
||||
val readReceiptsSummaryEntity = ReadReceiptsSummaryEntity.where(realm, eventId).findFirst()
|
||||
?: realm.createObject<ReadReceiptsSummaryEntity>(eventId).apply {
|
||||
|
@ -104,8 +57,9 @@ internal fun ChunkEntity.add(roomId: String,
|
|||
|
||||
// Update RR for the sender of a new message with a dummy one
|
||||
|
||||
if (event.originServerTs != null) {
|
||||
val timestampOfEvent = event.originServerTs.toDouble()
|
||||
val originServerTs = eventEntity.originServerTs
|
||||
if (originServerTs != null) {
|
||||
val timestampOfEvent = originServerTs.toDouble()
|
||||
val readReceiptOfSender = ReadReceiptEntity.getOrCreate(realm, roomId = roomId, userId = senderId)
|
||||
// If the synced RR is older, update
|
||||
if (timestampOfEvent > readReceiptOfSender.originServerTs) {
|
||||
|
@ -117,23 +71,24 @@ internal fun ChunkEntity.add(roomId: String,
|
|||
}
|
||||
}
|
||||
|
||||
val rootEvent = event.toEntity(roomId).apply {
|
||||
this.stateIndex = currentStateIndex
|
||||
this.displayIndex = currentDisplayIndex
|
||||
this.sendState = SendState.SYNCED
|
||||
this.isUnlinked = isChunkUnlinked
|
||||
}
|
||||
val eventEntity = realm.createObject<TimelineEventEntity>().also {
|
||||
val timelineEventEntity = TimelineEventEntity().also {
|
||||
it.localId = localId
|
||||
it.root = realm.copyToRealm(rootEvent)
|
||||
it.root = realm.copyToRealm(eventEntity)
|
||||
it.eventId = eventId
|
||||
it.roomId = roomId
|
||||
it.annotations = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||
it.readReceipts = readReceiptsSummaryEntity
|
||||
it.displayIndex = currentDisplayIndex
|
||||
}
|
||||
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.timelineEvents.size
|
||||
timelineEvents.add(position, eventEntity)
|
||||
return eventEntity
|
||||
if (roomMemberEvent != null) {
|
||||
val roomMemberContent = roomMemberEvent.content.toModel<RoomMemberContent>()
|
||||
timelineEventEntity.senderAvatar = roomMemberContent?.avatarUrl
|
||||
timelineEventEntity.senderName = roomMemberContent?.displayName
|
||||
timelineEventEntity.isUniqueDisplayName = false
|
||||
timelineEventEntity.senderMembershipEventId = roomMemberEvent.eventId
|
||||
}
|
||||
timelineEvents.add(timelineEventEntity)
|
||||
return timelineEventEntity
|
||||
}
|
||||
|
||||
internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||
|
@ -142,10 +97,3 @@ internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaul
|
|||
PaginationDirection.BACKWARDS -> backwardsDisplayIndex
|
||||
} ?: defaultValue
|
||||
}
|
||||
|
||||
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||
return when (direction) {
|
||||
PaginationDirection.FORWARDS -> forwardsStateIndex
|
||||
PaginationDirection.BACKWARDS -> backwardsStateIndex
|
||||
} ?: defaultValue
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ 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.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.query.fastContains
|
||||
import im.vector.matrix.android.internal.extensions.assertIsManaged
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
||||
import io.realm.Realm
|
||||
|
||||
|
@ -38,27 +36,9 @@ internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) {
|
|||
}
|
||||
}
|
||||
|
||||
internal fun RoomEntity.addStateEvent(stateEvent: Event,
|
||||
stateIndex: Int = Int.MIN_VALUE,
|
||||
filterDuplicates: Boolean = false,
|
||||
isUnlinked: Boolean = false) {
|
||||
assertIsManaged()
|
||||
if (stateEvent.eventId == null || (filterDuplicates && fastContains(stateEvent.eventId))) {
|
||||
return
|
||||
} else {
|
||||
val entity = stateEvent.toEntity(roomId).apply {
|
||||
this.stateIndex = stateIndex
|
||||
this.isUnlinked = isUnlinked
|
||||
this.sendState = SendState.SYNCED
|
||||
}
|
||||
untimelinedStateEvents.add(entity)
|
||||
}
|
||||
}
|
||||
internal fun RoomEntity.addSendingEvent(realm: Realm, event: Event) {
|
||||
val senderId = event.senderId ?: return
|
||||
val eventEntity = event.toEntity(roomId).apply {
|
||||
this.sendState = SendState.UNSENT
|
||||
}
|
||||
val eventEntity = event.toEntity(roomId, SendState.UNSENT)
|
||||
val roomMembers = RoomMemberHelper(realm, roomId)
|
||||
val myUser = roomMembers.getLastRoomMember(senderId)
|
||||
val localId = TimelineEventEntity.nextId(realm)
|
||||
|
@ -68,7 +48,7 @@ internal fun RoomEntity.addSendingEvent(realm: Realm, event: Event) {
|
|||
it.roomId = roomId
|
||||
it.senderName = myUser?.displayName
|
||||
it.senderAvatar = myUser?.avatarUrl
|
||||
it.isUniqueDisplayName = roomMembers.isUniqueDisplayName(myUser?.displayName)
|
||||
it.isUniqueDisplayName = roomMembers.isUniqueDisplayName()
|
||||
}
|
||||
sendingTimelineEvents.add(0, timelineEventEntity)
|
||||
}
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.helper
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.query.next
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.extensions.assertIsManaged
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmQuery
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* This is an internal cache to avoid querying all the time the room member events
|
||||
*/
|
||||
@SessionScope
|
||||
internal class TimelineEventSenderVisitor @Inject constructor() {
|
||||
|
||||
internal data class Key(
|
||||
val roomId: String,
|
||||
val stateIndex: Int,
|
||||
val senderId: String
|
||||
)
|
||||
|
||||
internal class Value(
|
||||
var senderAvatar: String? = null,
|
||||
var senderName: String? = null,
|
||||
var isUniqueDisplayName: Boolean = false,
|
||||
var senderMembershipEventId: String? = null
|
||||
)
|
||||
|
||||
private val values = HashMap<Key, Value>()
|
||||
|
||||
fun clear() {
|
||||
values.clear()
|
||||
}
|
||||
|
||||
fun clear(roomId: String, senderId: String) {
|
||||
val keysToRemove = values.keys.filter { it.senderId == senderId && it.roomId == roomId }
|
||||
keysToRemove.forEach {
|
||||
values.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun visit(timelineEventEntities: List<TimelineEventEntity>) = timelineEventEntities.forEach { visit(it) }
|
||||
|
||||
fun visit(timelineEventEntity: TimelineEventEntity) {
|
||||
if (!timelineEventEntity.isValid) {
|
||||
return
|
||||
}
|
||||
val key = Key(
|
||||
roomId = timelineEventEntity.roomId,
|
||||
stateIndex = timelineEventEntity.root?.stateIndex ?: 0,
|
||||
senderId = timelineEventEntity.root?.sender ?: ""
|
||||
)
|
||||
val result = values.getOrPut(key) {
|
||||
timelineEventEntity.computeValue()
|
||||
}
|
||||
timelineEventEntity.apply {
|
||||
this.isUniqueDisplayName = result.isUniqueDisplayName
|
||||
this.senderAvatar = result.senderAvatar
|
||||
this.senderName = result.senderName
|
||||
this.senderMembershipEventId = result.senderMembershipEventId
|
||||
}
|
||||
}
|
||||
|
||||
private fun RealmList<TimelineEventEntity>.buildQuery(sender: String, isUnlinked: Boolean): RealmQuery<TimelineEventEntity> {
|
||||
return where()
|
||||
.equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, sender)
|
||||
.equalTo(TimelineEventEntityFields.ROOT.TYPE, EventType.STATE_ROOM_MEMBER)
|
||||
.equalTo(TimelineEventEntityFields.ROOT.IS_UNLINKED, isUnlinked)
|
||||
}
|
||||
|
||||
private fun TimelineEventEntity.computeValue(): Value {
|
||||
assertIsManaged()
|
||||
val result = Value()
|
||||
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return result
|
||||
val stateIndex = root?.stateIndex ?: return result
|
||||
val senderId = root?.sender ?: return result
|
||||
val chunkEntity = chunk?.firstOrNull() ?: return result
|
||||
val isUnlinked = chunkEntity.isUnlinked
|
||||
var senderMembershipEvent: EventEntity?
|
||||
var senderRoomMemberContent: String?
|
||||
var senderRoomMemberPrevContent: String?
|
||||
|
||||
if (stateIndex <= 0) {
|
||||
senderMembershipEvent = chunkEntity.timelineEvents.buildQuery(senderId, isUnlinked).next(from = stateIndex)?.root
|
||||
senderRoomMemberContent = senderMembershipEvent?.prevContent
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.content
|
||||
} else {
|
||||
senderMembershipEvent = chunkEntity.timelineEvents.buildQuery(senderId, isUnlinked).prev(since = stateIndex)?.root
|
||||
senderRoomMemberContent = senderMembershipEvent?.content
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.prevContent
|
||||
}
|
||||
|
||||
// We fallback to untimelinedStateEvents if we can't find membership events in timeline
|
||||
if (senderMembershipEvent == null) {
|
||||
senderMembershipEvent = roomEntity.untimelinedStateEvents
|
||||
.where()
|
||||
.equalTo(EventEntityFields.STATE_KEY, senderId)
|
||||
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_MEMBER)
|
||||
.prev(since = stateIndex)
|
||||
senderRoomMemberContent = senderMembershipEvent?.content
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.prevContent
|
||||
}
|
||||
|
||||
ContentMapper.map(senderRoomMemberContent).toModel<RoomMemberContent>()?.also {
|
||||
result.senderAvatar = it.avatarUrl
|
||||
result.senderName = it.displayName
|
||||
result.isUniqueDisplayName = RoomMemberHelper(realm, roomId).isUniqueDisplayName(it.displayName)
|
||||
}
|
||||
// We try to fallback on prev content if we got a room member state events with null fields
|
||||
if (root?.type == EventType.STATE_ROOM_MEMBER) {
|
||||
ContentMapper.map(senderRoomMemberPrevContent).toModel<RoomMemberContent>()?.also {
|
||||
if (result.senderAvatar == null && it.avatarUrl != null) {
|
||||
result.senderAvatar = it.avatarUrl
|
||||
}
|
||||
if (result.senderName == null && it.displayName != null) {
|
||||
result.senderName = it.displayName
|
||||
result.isUniqueDisplayName = RoomMemberHelper(realm, roomId).isUniqueDisplayName(it.displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
result.senderMembershipEventId = senderMembershipEvent?.eventId
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import com.squareup.moshi.JsonDataException
|
|||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
|
@ -90,6 +91,8 @@ internal fun EventEntity.asDomain(): Event {
|
|||
return EventMapper.map(this)
|
||||
}
|
||||
|
||||
internal fun Event.toEntity(roomId: String): EventEntity {
|
||||
return EventMapper.map(this, roomId)
|
||||
internal fun Event.toEntity(roomId: String, sendState: SendState): EventEntity {
|
||||
return EventMapper.map(this, roomId).apply {
|
||||
this.sendState = sendState
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ internal class TimelineEventMapper @Inject constructor(private val readReceiptsS
|
|||
?: Event("", timelineEventEntity.eventId),
|
||||
annotations = timelineEventEntity.annotations?.asDomain(),
|
||||
localId = timelineEventEntity.localId,
|
||||
displayIndex = timelineEventEntity.root?.displayIndex ?: 0,
|
||||
displayIndex = timelineEventEntity.displayIndex,
|
||||
senderName = timelineEventEntity.senderName,
|
||||
isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName,
|
||||
senderAvatar = timelineEventEntity.senderAvatar,
|
||||
|
|
|
@ -28,10 +28,7 @@ internal open class ChunkEntity(@Index var prevToken: String? = null,
|
|||
@Index var isLastForward: Boolean = false,
|
||||
@Index var isLastBackward: Boolean = false,
|
||||
var backwardsDisplayIndex: Int? = null,
|
||||
var forwardsDisplayIndex: Int? = null,
|
||||
var backwardsStateIndex: Int? = null,
|
||||
var forwardsStateIndex: Int? = null,
|
||||
var isUnlinked: Boolean = false
|
||||
var forwardsDisplayIndex: Int? = null
|
||||
) : RealmObject() {
|
||||
|
||||
fun identifier() = "${prevToken}_$nextToken"
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class CurrentStateEventEntity(var eventId: String = "",
|
||||
var root: EventEntity? = null,
|
||||
@Index var roomId: String = "",
|
||||
@Index var type: String = "",
|
||||
@Index var stateKey: String = ""
|
||||
) : RealmObject() {
|
||||
companion object
|
||||
}
|
|
@ -24,8 +24,9 @@ import io.realm.RealmObject
|
|||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class EventEntity(@Index var eventId: String = "",
|
||||
internal open class EventEntity(@PrimaryKey var eventId: String = "",
|
||||
@Index var roomId: String = "",
|
||||
@Index var type: String = "",
|
||||
var content: String? = null,
|
||||
|
@ -36,9 +37,6 @@ internal open class EventEntity(@Index var eventId: String = "",
|
|||
var age: Long? = 0,
|
||||
var unsignedData: String? = null,
|
||||
var redacts: String? = null,
|
||||
@Index var stateIndex: Int = 0,
|
||||
@Index var displayIndex: Int = 0,
|
||||
@Index var isUnlinked: Boolean = false,
|
||||
var decryptionResultJson: String? = null,
|
||||
var decryptionErrorCode: String? = null
|
||||
) : RealmObject() {
|
||||
|
@ -61,9 +59,6 @@ internal open class EventEntity(@Index var eventId: String = "",
|
|||
|
||||
companion object
|
||||
|
||||
@LinkingObjects("untimelinedStateEvents")
|
||||
val room: RealmResults<RoomEntity>? = null
|
||||
|
||||
@LinkingObjects("root")
|
||||
val timelineEventEntity: RealmResults<TimelineEventEntity>? = null
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import io.realm.annotations.PrimaryKey
|
|||
|
||||
internal open class RoomEntity(@PrimaryKey var roomId: String = "",
|
||||
var chunks: RealmList<ChunkEntity> = RealmList(),
|
||||
var untimelinedStateEvents: RealmList<EventEntity> = RealmList(),
|
||||
var sendingTimelineEvents: RealmList<TimelineEventEntity> = RealmList(),
|
||||
var areAllMembersLoaded: Boolean = false
|
||||
) : RealmObject() {
|
||||
|
|
|
@ -20,10 +20,12 @@ import io.realm.RealmObject
|
|||
import io.realm.RealmResults
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.LinkingObjects
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class TimelineEventEntity(var localId: Long = 0,
|
||||
@Index var eventId: String = "",
|
||||
@PrimaryKey var eventId: String = "",
|
||||
@Index var roomId: String = "",
|
||||
@Index var displayIndex: Int = 0,
|
||||
var root: EventEntity? = null,
|
||||
var annotations: EventAnnotationsSummaryEntity? = null,
|
||||
var senderName: String? = null,
|
||||
|
|
|
@ -60,12 +60,10 @@ internal fun ChunkEntity.Companion.findIncludingEvent(realm: Realm, eventId: Str
|
|||
internal fun ChunkEntity.Companion.create(
|
||||
realm: Realm,
|
||||
prevToken: String?,
|
||||
nextToken: String?,
|
||||
isUnlinked: Boolean
|
||||
nextToken: String?
|
||||
): ChunkEntity {
|
||||
return realm.createObject<ChunkEntity>().apply {
|
||||
this.prevToken = prevToken
|
||||
this.nextToken = nextToken
|
||||
this.isUnlinked = isUnlinked
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.createObject
|
||||
|
||||
internal fun CurrentStateEventEntity.Companion.where(realm: Realm, roomId: String, stateKey: String, type: String): RealmQuery<CurrentStateEventEntity> {
|
||||
return realm.where(CurrentStateEventEntity::class.java)
|
||||
.equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId)
|
||||
.equalTo(CurrentStateEventEntityFields.STATE_KEY, stateKey)
|
||||
.equalTo(CurrentStateEventEntityFields.TYPE, type)
|
||||
}
|
||||
|
||||
internal fun CurrentStateEventEntity.Companion.getOrNull(realm: Realm, roomId: String, stateKey: String, type: String): CurrentStateEventEntity? {
|
||||
return where(realm, roomId, stateKey, type).findFirst()
|
||||
}
|
||||
|
||||
internal fun CurrentStateEventEntity.Companion.getOrCreate(realm: Realm, roomId: String, stateKey: String, type: String): CurrentStateEventEntity {
|
||||
return getOrNull(realm, roomId, stateKey, type) ?: create(realm, roomId, stateKey, type)
|
||||
}
|
||||
|
||||
private fun create(realm: Realm, roomId: String, stateKey: String, type: String): CurrentStateEventEntity {
|
||||
return realm.createObject<CurrentStateEventEntity>().apply {
|
||||
this.type = type
|
||||
this.roomId = roomId
|
||||
this.stateKey = stateKey
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -35,22 +35,15 @@ internal fun EventEntity.Companion.where(realm: Realm, eventIds: List<String>):
|
|||
.`in`(EventEntityFields.EVENT_ID, eventIds.toTypedArray())
|
||||
}
|
||||
|
||||
internal fun EventEntity.Companion.where(realm: Realm,
|
||||
roomId: String? = null,
|
||||
type: String? = null,
|
||||
linkFilterMode: EventEntity.LinkFilterMode = LINKED_ONLY): RealmQuery<EventEntity> {
|
||||
internal fun EventEntity.Companion.whereType(realm: Realm,
|
||||
type: String,
|
||||
roomId: String? = null
|
||||
): RealmQuery<EventEntity> {
|
||||
val query = realm.where<EventEntity>()
|
||||
if (roomId != null) {
|
||||
query.equalTo(EventEntityFields.ROOM_ID, roomId)
|
||||
}
|
||||
if (type != null) {
|
||||
query.equalTo(EventEntityFields.TYPE, type)
|
||||
}
|
||||
return when (linkFilterMode) {
|
||||
LINKED_ONLY -> query.equalTo(EventEntityFields.IS_UNLINKED, false)
|
||||
UNLINKED_ONLY -> query.equalTo(EventEntityFields.IS_UNLINKED, true)
|
||||
BOTH -> query
|
||||
}
|
||||
return query.equalTo(EventEntityFields.TYPE, type)
|
||||
}
|
||||
|
||||
internal fun EventEntity.Companion.types(realm: Realm,
|
||||
|
@ -60,36 +53,6 @@ internal fun EventEntity.Companion.types(realm: Realm,
|
|||
return query
|
||||
}
|
||||
|
||||
internal fun RealmQuery<EventEntity>.descending(since: Int? = null, strict: Boolean = false): RealmQuery<EventEntity> {
|
||||
if (since != null) {
|
||||
if (strict) {
|
||||
this.lessThan(EventEntityFields.STATE_INDEX, since)
|
||||
} else {
|
||||
this.lessThanOrEqualTo(EventEntityFields.STATE_INDEX, since)
|
||||
}
|
||||
}
|
||||
return this.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
}
|
||||
|
||||
internal fun RealmQuery<EventEntity>.ascending(from: Int? = null, strict: Boolean = true): RealmQuery<EventEntity> {
|
||||
if (from != null) {
|
||||
if (strict) {
|
||||
this.greaterThan(EventEntityFields.STATE_INDEX, from)
|
||||
} else {
|
||||
this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, from)
|
||||
}
|
||||
}
|
||||
return this.sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING)
|
||||
}
|
||||
|
||||
internal fun RealmQuery<EventEntity>.next(from: Int? = null, strict: Boolean = true): EventEntity? {
|
||||
return this.ascending(from, strict).findFirst()
|
||||
}
|
||||
|
||||
internal fun RealmQuery<EventEntity>.prev(since: Int? = null, strict: Boolean = false): EventEntity? {
|
||||
return descending(since, strict).findFirst()
|
||||
}
|
||||
|
||||
internal fun RealmList<EventEntity>.find(eventId: String): EventEntity? {
|
||||
return this.where()
|
||||
.equalTo(EventEntityFields.EVENT_ID, eventId)
|
||||
|
|
|
@ -36,15 +36,15 @@ internal fun isEventRead(monarchy: Monarchy,
|
|||
|
||||
monarchy.doWithRealm { realm ->
|
||||
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) ?: return@doWithRealm
|
||||
val eventToCheck = liveChunk.timelineEvents.find(eventId)?.root
|
||||
val eventToCheck = liveChunk.timelineEvents.find(eventId)
|
||||
|
||||
isEventRead = if (eventToCheck?.sender == userId) {
|
||||
isEventRead = if (eventToCheck?.root?.sender == userId) {
|
||||
true
|
||||
} else {
|
||||
val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst()
|
||||
?: return@doWithRealm
|
||||
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex
|
||||
?: Int.MIN_VALUE
|
||||
?: return@doWithRealm
|
||||
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex
|
||||
?: Int.MIN_VALUE
|
||||
val eventToCheckIndex = eventToCheck?.displayIndex ?: Int.MAX_VALUE
|
||||
|
||||
eventToCheckIndex <= readReceiptIndex
|
||||
|
@ -62,11 +62,11 @@ internal fun isReadMarkerMoreRecent(monarchy: Monarchy,
|
|||
}
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) ?: return false
|
||||
val eventToCheck = liveChunk.timelineEvents.find(eventId)?.root
|
||||
val eventToCheck = liveChunk.timelineEvents.find(eventId)
|
||||
|
||||
val readMarker = ReadMarkerEntity.where(realm, roomId).findFirst() ?: return false
|
||||
val readMarkerIndex = liveChunk.timelineEvents.find(readMarker.eventId)?.root?.displayIndex
|
||||
?: Int.MIN_VALUE
|
||||
val readMarkerIndex = liveChunk.timelineEvents.find(readMarker.eventId)?.displayIndex
|
||||
?: Int.MIN_VALUE
|
||||
val eventToCheckIndex = eventToCheck?.displayIndex ?: Int.MAX_VALUE
|
||||
eventToCheckIndex <= readMarkerIndex
|
||||
}
|
||||
|
|
|
@ -34,22 +34,20 @@ internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, e
|
|||
.`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> {
|
||||
internal fun TimelineEventEntity.Companion.whereRoomId(realm: Realm,
|
||||
roomId: String): RealmQuery<TimelineEventEntity> {
|
||||
return realm.where<TimelineEventEntity>().equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
|
||||
}
|
||||
|
||||
internal fun TimelineEventEntity.Companion.whereType(realm: Realm,
|
||||
type: String,
|
||||
roomId: String? = null): RealmQuery<TimelineEventEntity> {
|
||||
val query = realm.where<TimelineEventEntity>()
|
||||
query.equalTo(TimelineEventEntityFields.ROOT.TYPE, type)
|
||||
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
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm: Realm, senderMembershipEventId: String): List<TimelineEventEntity> {
|
||||
|
@ -71,7 +69,7 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
|
|||
liveEvents
|
||||
}
|
||||
return query
|
||||
?.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
?.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
?.findFirst()
|
||||
}
|
||||
|
||||
|
@ -83,32 +81,6 @@ internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<Strin
|
|||
}
|
||||
}
|
||||
|
||||
internal fun RealmQuery<TimelineEventEntity>.next(from: Int? = null, strict: Boolean = true): TimelineEventEntity? {
|
||||
if (from != null) {
|
||||
if (strict) {
|
||||
this.greaterThan(TimelineEventEntityFields.ROOT.STATE_INDEX, from)
|
||||
} else {
|
||||
this.greaterThanOrEqualTo(TimelineEventEntityFields.ROOT.STATE_INDEX, from)
|
||||
}
|
||||
}
|
||||
return this
|
||||
.sort(TimelineEventEntityFields.ROOT.STATE_INDEX, Sort.ASCENDING)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
internal fun RealmQuery<TimelineEventEntity>.prev(since: Int? = null, strict: Boolean = false): TimelineEventEntity? {
|
||||
if (since != null) {
|
||||
if (strict) {
|
||||
this.lessThan(TimelineEventEntityFields.ROOT.STATE_INDEX, since)
|
||||
} else {
|
||||
this.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.STATE_INDEX, since)
|
||||
}
|
||||
}
|
||||
return this
|
||||
.sort(TimelineEventEntityFields.ROOT.STATE_INDEX, Sort.DESCENDING)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
internal fun RealmList<TimelineEventEntity>.find(eventId: String): TimelineEventEntity? {
|
||||
return this.where()
|
||||
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
|
||||
|
|
|
@ -21,10 +21,9 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomAvatarContent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
||||
import javax.inject.Inject
|
||||
|
@ -40,7 +39,7 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona
|
|||
fun resolve(roomId: String): String? {
|
||||
var res: String? = null
|
||||
monarchy.doWithRealm { realm ->
|
||||
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev()
|
||||
val roomName = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_AVATAR, stateKey = "")?.root
|
||||
res = ContentMapper.map(roomName?.content).toModel<RoomAvatarContent>()?.avatarUrl
|
||||
if (!res.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
|
|
|
@ -24,11 +24,14 @@ import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
|
|||
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.query.*
|
||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.isEventRead
|
||||
import im.vector.matrix.android.internal.database.query.latestEvent
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
|
||||
|
@ -89,10 +92,10 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
}
|
||||
|
||||
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
|
||||
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()
|
||||
val lastCanonicalAliasEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_CANONICAL_ALIAS).prev()
|
||||
val lastAliasesEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()
|
||||
val encryptionEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ENCRYPTION).prev()
|
||||
val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
|
||||
val lastCanonicalAliasEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
|
||||
val lastAliasesEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ALIASES, stateKey = "")?.root
|
||||
val encryptionEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ENCRYPTION, stateKey = "")?.root
|
||||
|
||||
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
||||
// avoid this call if we are sure there are unread events
|
||||
|
|
|
@ -18,9 +18,11 @@ package im.vector.matrix.android.internal.session.room.membership
|
|||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.internal.database.helper.TimelineEventSenderVisitor
|
||||
import im.vector.matrix.android.internal.database.helper.addStateEvent
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.database.mapper.toEntity
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||
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
|
||||
|
@ -47,7 +49,6 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
|
|||
private val syncTokenStore: SyncTokenStore,
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||
private val timelineEventSenderVisitor: TimelineEventSenderVisitor,
|
||||
private val eventBus: EventBus
|
||||
) : LoadRoomMembersTask {
|
||||
|
||||
|
@ -69,13 +70,16 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
|
|||
?: realm.createObject(roomId)
|
||||
|
||||
for (roomMemberEvent in response.roomMemberEvents) {
|
||||
roomEntity.addStateEvent(roomMemberEvent)
|
||||
if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null) {
|
||||
continue
|
||||
}
|
||||
val eventEntity = roomMemberEvent.toEntity(roomId, SendState.SYNCED)
|
||||
CurrentStateEventEntity.getOrCreate(realm, roomId, roomMemberEvent.stateKey, roomMemberEvent.type).apply {
|
||||
eventId = roomMemberEvent.eventId
|
||||
root = eventEntity
|
||||
}
|
||||
roomMemberEventHandler.handle(realm, roomId, roomMemberEvent)
|
||||
}
|
||||
timelineEventSenderVisitor.clear()
|
||||
roomEntity.chunks.flatMap { it.timelineEvents }.forEach {
|
||||
timelineEventSenderVisitor.visit(it)
|
||||
}
|
||||
roomEntity.areAllMembersLoaded = true
|
||||
roomSummaryUpdater.update(realm, roomId, updateMembers = true)
|
||||
}
|
||||
|
|
|
@ -21,14 +21,17 @@ import com.zhuinden.monarchy.Monarchy
|
|||
import im.vector.matrix.android.R
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.*
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomNameContent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import javax.inject.Inject
|
||||
|
@ -57,19 +60,19 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||
var name: CharSequence? = null
|
||||
monarchy.doWithRealm { realm ->
|
||||
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()
|
||||
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev()
|
||||
val roomName = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root
|
||||
name = ContentMapper.map(roomName?.content).toModel<RoomNameContent>()?.name
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
}
|
||||
|
||||
val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_ROOM_CANONICAL_ALIAS).prev()
|
||||
val canonicalAlias = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
|
||||
name = ContentMapper.map(canonicalAlias?.content).toModel<RoomCanonicalAliasContent>()?.canonicalAlias
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
}
|
||||
|
||||
val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()
|
||||
val aliases = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ALIASES, stateKey = "")?.root
|
||||
name = ContentMapper.map(aliases?.content).toModel<RoomAliasesContent>()?.aliases?.firstOrNull()
|
||||
if (!name.isNullOrEmpty()) {
|
||||
return@doWithRealm
|
||||
|
@ -126,7 +129,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||
private fun resolveRoomMemberName(roomMemberSummary: RoomMemberSummaryEntity?,
|
||||
roomMemberHelper: RoomMemberHelper): String? {
|
||||
if (roomMemberSummary == null) return null
|
||||
val isUnique = roomMemberHelper.isUniqueDisplayName(roomMemberSummary.displayName)
|
||||
val isUnique = roomMemberHelper.isUniqueDisplayName()
|
||||
return if (isUnique) {
|
||||
roomMemberSummary.displayName
|
||||
} else {
|
||||
|
|
|
@ -22,10 +22,10 @@ import im.vector.matrix.android.internal.database.model.*
|
|||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.Sort
|
||||
|
||||
/**
|
||||
* This class is an helper around STATE_ROOM_MEMBER events.
|
||||
|
@ -41,11 +41,7 @@ internal class RoomMemberHelper(private val realm: Realm,
|
|||
}
|
||||
|
||||
fun getLastStateEvent(userId: String): EventEntity? {
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.equalTo(EventEntityFields.STATE_KEY, userId)
|
||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
.findFirst()
|
||||
return CurrentStateEventEntity.getOrNull(realm, roomId, userId, EventType.STATE_ROOM_MEMBER)?.root
|
||||
}
|
||||
|
||||
fun getLastRoomMember(userId: String): RoomMemberSummaryEntity? {
|
||||
|
@ -54,16 +50,8 @@ internal class RoomMemberHelper(private val realm: Realm,
|
|||
.findFirst()
|
||||
}
|
||||
|
||||
fun isUniqueDisplayName(displayName: String?): Boolean {
|
||||
if (displayName.isNullOrEmpty()) {
|
||||
return true
|
||||
}
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.contains(EventEntityFields.CONTENT, "\"displayname\":\"$displayName\"")
|
||||
.distinct(EventEntityFields.STATE_KEY)
|
||||
.findAll()
|
||||
.size == 1
|
||||
fun isUniqueDisplayName(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun queryRoomMembersEvent(): RealmQuery<RoomMemberSummaryEntity> {
|
||||
|
|
|
@ -20,7 +20,6 @@ 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.LocalEcho
|
||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||
import im.vector.matrix.android.internal.database.helper.TimelineEventSenderVisitor
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.mapper.EventMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
|
@ -41,8 +40,7 @@ internal interface PruneEventTask : Task<PruneEventTask.Params, Unit> {
|
|||
)
|
||||
}
|
||||
|
||||
internal class DefaultPruneEventTask @Inject constructor(private val monarchy: Monarchy,
|
||||
private val timelineEventSenderVisitor: TimelineEventSenderVisitor) : PruneEventTask {
|
||||
internal class DefaultPruneEventTask @Inject constructor(private val monarchy: Monarchy) : PruneEventTask {
|
||||
|
||||
override suspend fun execute(params: PruneEventTask.Params) {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
|
@ -97,10 +95,10 @@ internal class DefaultPruneEventTask @Inject constructor(private val monarchy: M
|
|||
// }
|
||||
}
|
||||
}
|
||||
// TODO : make it work again. Maybe waits for SQL rework...
|
||||
if (typeToPrune == EventType.STATE_ROOM_MEMBER && stateKey != null) {
|
||||
timelineEventSenderVisitor.clear(roomId = eventToPrune.roomId, senderId = stateKey)
|
||||
val timelineEventsToUpdate = TimelineEventEntity.findWithSenderMembershipEvent(realm, eventToPrune.eventId)
|
||||
timelineEventSenderVisitor.visit(timelineEventsToUpdate)
|
||||
TimelineEventEntity.findWithSenderMembershipEvent(realm, eventToPrune.eventId)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,7 @@ internal class LocalEchoRepository @Inject constructor(private val monarchy: Mon
|
|||
eventBus.post(DefaultTimeline.OnNewTimelineEvents(roomId = roomId, eventIds = listOf(eventId)))
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return@awaitTransaction
|
||||
val eventEntity = event.toEntity(roomId).apply {
|
||||
this.sendState = SendState.UNSENT
|
||||
}
|
||||
val eventEntity = event.toEntity(roomId, SendState.UNSENT)
|
||||
val roomMemberHelper = RoomMemberHelper(realm, roomId)
|
||||
val myUser = roomMemberHelper.getLastRoomMember(senderId)
|
||||
val localId = TimelineEventEntity.nextId(realm)
|
||||
|
@ -64,7 +62,7 @@ internal class LocalEchoRepository @Inject constructor(private val monarchy: Mon
|
|||
it.roomId = roomId
|
||||
it.senderName = myUser?.displayName
|
||||
it.senderAvatar = myUser?.avatarUrl
|
||||
it.isUniqueDisplayName = roomMemberHelper.isUniqueDisplayName(myUser?.displayName)
|
||||
it.isUniqueDisplayName = roomMemberHelper.isUniqueDisplayName()
|
||||
}
|
||||
roomEntity.sendingTimelineEvents.add(0, timelineEventEntity)
|
||||
roomSummaryUpdater.update(realm, roomId)
|
||||
|
@ -103,7 +101,7 @@ internal class LocalEchoRepository @Inject constructor(private val monarchy: Mon
|
|||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||
TimelineEventEntity
|
||||
.findAllInRoomWithSendStates(realm, roomId, SendState.HAS_FAILED_STATES)
|
||||
.sortedByDescending { it.root?.displayIndex ?: 0 }
|
||||
.sortedByDescending { it.displayIndex }
|
||||
.mapNotNull { it.root?.asDomain() }
|
||||
.filter { event ->
|
||||
when (event.getClearType()) {
|
||||
|
|
|
@ -29,9 +29,8 @@ import im.vector.matrix.android.api.util.Optional
|
|||
import im.vector.matrix.android.api.util.toOptional
|
||||
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
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.query.descending
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
@ -51,14 +50,14 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||
|
||||
override fun getStateEvent(eventType: String): Event? {
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||
EventEntity.where(realm, roomId, eventType).prev()?.asDomain()
|
||||
CurrentStateEventEntity.getOrNull(realm, roomId, type = eventType, stateKey = "")?.root?.asDomain()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStateEventLive(eventType: String): LiveData<Optional<Event>> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ realm -> EventEntity.where(realm, roomId, eventType).descending() },
|
||||
{ it.asDomain() }
|
||||
{ realm -> CurrentStateEventEntity.where(realm, roomId, type = eventType, stateKey = "") },
|
||||
{ it.root?.asDomain() }
|
||||
)
|
||||
return Transformations.map(liveData) { results ->
|
||||
results.firstOrNull().toOptional()
|
||||
|
|
|
@ -31,17 +31,18 @@ internal interface ClearUnlinkedEventsTask : Task<ClearUnlinkedEventsTask.Params
|
|||
data class Params(val roomId: String)
|
||||
}
|
||||
|
||||
internal class DefaultClearUnlinkedEventsTask @Inject constructor(private val monarchy: Monarchy) : ClearUnlinkedEventsTask {
|
||||
internal class DefaultClearUnlinkedEventsTask @Inject constructor() : ClearUnlinkedEventsTask {
|
||||
|
||||
override suspend fun execute(params: ClearUnlinkedEventsTask.Params) {
|
||||
monarchy.awaitTransaction { localRealm ->
|
||||
return
|
||||
/*monarchy.awaitTransaction { localRealm ->
|
||||
val unlinkedChunks = ChunkEntity
|
||||
.where(localRealm, roomId = params.roomId)
|
||||
.equalTo(ChunkEntityFields.IS_UNLINKED, true)
|
||||
.findAll()
|
||||
unlinkedChunks.forEach {
|
||||
it.deleteOnCascade()
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import im.vector.matrix.android.internal.database.query.FilterContent
|
|||
import im.vector.matrix.android.internal.database.query.findAllInRoomWithSendStates
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.database.query.whereInRoom
|
||||
import im.vector.matrix.android.internal.database.query.whereRoomId
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.Debouncer
|
||||
|
@ -177,7 +178,7 @@ internal class DefaultTimeline(
|
|||
}
|
||||
}
|
||||
|
||||
nonFilteredEvents = buildEventQuery(realm).sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING).findAll()
|
||||
nonFilteredEvents = buildEventQuery(realm).sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING).findAll()
|
||||
filteredEvents = nonFilteredEvents.where()
|
||||
.filterEventsWithSettings()
|
||||
.findAll()
|
||||
|
@ -260,7 +261,7 @@ internal class DefaultTimeline(
|
|||
// Otherwise, we should check if the event is in the db, but is hidden because of filters
|
||||
return Realm.getInstance(realmConfiguration).use { localRealm ->
|
||||
val nonFilteredEvents = buildEventQuery(localRealm)
|
||||
.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
.findAll()
|
||||
|
||||
val nonFilteredEvent = nonFilteredEvents.where()
|
||||
|
@ -275,11 +276,11 @@ internal class DefaultTimeline(
|
|||
.findFirst() == null
|
||||
|
||||
if (isHidden) {
|
||||
val displayIndex = nonFilteredEvent?.root?.displayIndex
|
||||
val displayIndex = nonFilteredEvent?.displayIndex
|
||||
if (displayIndex != null) {
|
||||
// Then we are looking for the first displayable event after the hidden one
|
||||
val firstDisplayedEvent = filteredEvents.where()
|
||||
.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
|
||||
.lessThanOrEqualTo(TimelineEventEntityFields.DISPLAY_INDEX, displayIndex)
|
||||
.findFirst()
|
||||
firstDisplayedEvent?.eventId
|
||||
} else {
|
||||
|
@ -358,14 +359,14 @@ internal class DefaultTimeline(
|
|||
|
||||
updateState(Timeline.Direction.FORWARDS) {
|
||||
it.copy(
|
||||
hasMoreInCache = firstBuiltEvent == null || firstBuiltEvent.displayIndex < firstCacheEvent?.root?.displayIndex ?: Int.MIN_VALUE,
|
||||
hasMoreInCache = firstBuiltEvent == null || firstBuiltEvent.displayIndex < firstCacheEvent?.displayIndex ?: Int.MIN_VALUE,
|
||||
hasReachedEnd = chunkEntity?.isLastForward ?: false
|
||||
)
|
||||
}
|
||||
|
||||
updateState(Timeline.Direction.BACKWARDS) {
|
||||
it.copy(
|
||||
hasMoreInCache = lastBuiltEvent == null || lastBuiltEvent.displayIndex > lastCacheEvent?.root?.displayIndex ?: Int.MAX_VALUE,
|
||||
hasMoreInCache = lastBuiltEvent == null || lastBuiltEvent.displayIndex > lastCacheEvent?.displayIndex ?: Int.MAX_VALUE,
|
||||
hasReachedEnd = chunkEntity?.isLastBackward ?: false || lastCacheEvent?.root?.type == EventType.STATE_ROOM_CREATE
|
||||
)
|
||||
}
|
||||
|
@ -440,14 +441,14 @@ internal class DefaultTimeline(
|
|||
var shouldFetchInitialEvent = false
|
||||
val currentInitialEventId = initialEventId
|
||||
val initialDisplayIndex = if (currentInitialEventId == null) {
|
||||
filteredEvents.firstOrNull()?.root?.displayIndex
|
||||
filteredEvents.firstOrNull()?.displayIndex
|
||||
} else {
|
||||
val initialEvent = nonFilteredEvents.where()
|
||||
.equalTo(TimelineEventEntityFields.EVENT_ID, initialEventId)
|
||||
.findFirst()
|
||||
|
||||
shouldFetchInitialEvent = initialEvent == null
|
||||
initialEvent?.root?.displayIndex
|
||||
initialEvent?.displayIndex
|
||||
}
|
||||
prevDisplayIndex = initialDisplayIndex
|
||||
nextDisplayIndex = initialDisplayIndex
|
||||
|
@ -476,9 +477,9 @@ internal class DefaultTimeline(
|
|||
var postSnapshot = false
|
||||
changeSet.insertionRanges.forEach { range ->
|
||||
val (startDisplayIndex, direction) = if (range.startIndex == 0) {
|
||||
Pair(filteredEvents[range.length - 1]!!.root!!.displayIndex, Timeline.Direction.FORWARDS)
|
||||
Pair(filteredEvents[range.length - 1]!!.displayIndex, Timeline.Direction.FORWARDS)
|
||||
} else {
|
||||
Pair(filteredEvents[range.startIndex]!!.root!!.displayIndex, Timeline.Direction.BACKWARDS)
|
||||
Pair(filteredEvents[range.startIndex]!!.displayIndex, Timeline.Direction.BACKWARDS)
|
||||
}
|
||||
val state = getState(direction)
|
||||
if (state.isPaginating) {
|
||||
|
@ -579,7 +580,7 @@ internal class DefaultTimeline(
|
|||
if (offsetResults.isEmpty()) {
|
||||
return 0
|
||||
}
|
||||
val offsetIndex = offsetResults.last()!!.root!!.displayIndex
|
||||
val offsetIndex = offsetResults.last()!!.displayIndex
|
||||
if (direction == Timeline.Direction.BACKWARDS) {
|
||||
prevDisplayIndex = offsetIndex - 1
|
||||
} else {
|
||||
|
@ -620,18 +621,18 @@ internal class DefaultTimeline(
|
|||
strict: Boolean): RealmResults<TimelineEventEntity> {
|
||||
val offsetQuery = filteredEvents.where()
|
||||
if (direction == Timeline.Direction.BACKWARDS) {
|
||||
offsetQuery.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
offsetQuery.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
||||
if (strict) {
|
||||
offsetQuery.lessThan(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
|
||||
offsetQuery.lessThan(TimelineEventEntityFields.DISPLAY_INDEX, startDisplayIndex)
|
||||
} else {
|
||||
offsetQuery.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
|
||||
offsetQuery.lessThanOrEqualTo(TimelineEventEntityFields.DISPLAY_INDEX, startDisplayIndex)
|
||||
}
|
||||
} else {
|
||||
offsetQuery.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING)
|
||||
offsetQuery.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
|
||||
if (strict) {
|
||||
offsetQuery.greaterThan(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
|
||||
offsetQuery.greaterThan(TimelineEventEntityFields.DISPLAY_INDEX, startDisplayIndex)
|
||||
} else {
|
||||
offsetQuery.greaterThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, startDisplayIndex)
|
||||
offsetQuery.greaterThanOrEqualTo(TimelineEventEntityFields.DISPLAY_INDEX, startDisplayIndex)
|
||||
}
|
||||
}
|
||||
return offsetQuery
|
||||
|
@ -642,11 +643,11 @@ internal class DefaultTimeline(
|
|||
private fun buildEventQuery(realm: Realm): RealmQuery<TimelineEventEntity> {
|
||||
return if (initialEventId == null) {
|
||||
TimelineEventEntity
|
||||
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.LINKED_ONLY)
|
||||
.whereRoomId(realm, roomId = roomId)
|
||||
.equalTo("${TimelineEventEntityFields.CHUNK}.${ChunkEntityFields.IS_LAST_FORWARD}", true)
|
||||
} else {
|
||||
TimelineEventEntity
|
||||
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH)
|
||||
.whereRoomId(realm, roomId = roomId)
|
||||
.`in`("${TimelineEventEntityFields.CHUNK}.${ChunkEntityFields.TIMELINE_EVENTS.EVENT_ID}", arrayOf(initialEventId))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,12 +76,12 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
|
|||
val timelineEvent = summary.timelineEvent?.firstOrNull() ?: continue
|
||||
val isLoaded = nonFilteredEvents.where()
|
||||
.equalTo(TimelineEventEntityFields.EVENT_ID, timelineEvent.eventId).findFirst() != null
|
||||
val displayIndex = timelineEvent.root?.displayIndex
|
||||
val displayIndex = timelineEvent.displayIndex
|
||||
|
||||
if (isLoaded && displayIndex != null) {
|
||||
if (isLoaded) {
|
||||
// Then we are looking for the first displayable event after the hidden one
|
||||
val firstDisplayedEvent = filteredEvents.where()
|
||||
.lessThanOrEqualTo(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, displayIndex)
|
||||
.lessThanOrEqualTo(TimelineEventEntityFields.DISPLAY_INDEX, displayIndex)
|
||||
.findFirst()
|
||||
|
||||
// If we find one, we should
|
||||
|
|
|
@ -17,12 +17,18 @@
|
|||
package im.vector.matrix.android.internal.session.room.timeline
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
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.room.send.SendState
|
||||
import im.vector.matrix.android.internal.database.helper.*
|
||||
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.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.query.create
|
||||
import im.vector.matrix.android.internal.database.query.find
|
||||
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import io.realm.kotlin.createObject
|
||||
|
@ -32,8 +38,7 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Insert Chunk in DB, and eventually merge with existing chunk event
|
||||
*/
|
||||
internal class TokenChunkEventPersistor @Inject constructor(private val monarchy: Monarchy,
|
||||
private val timelineEventSenderVisitor: TimelineEventSenderVisitor) {
|
||||
internal class TokenChunkEventPersistor @Inject constructor(private val monarchy: Monarchy) {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
|
@ -110,7 +115,6 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
|
|||
monarchy
|
||||
.awaitTransaction { realm ->
|
||||
Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction")
|
||||
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
|
||||
|
@ -124,50 +128,44 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
|
|||
prevToken = receivedChunk.end
|
||||
}
|
||||
|
||||
val shouldSkip = ChunkEntity.find(realm, roomId, nextToken = nextToken) != null
|
||||
|| ChunkEntity.find(realm, roomId, prevToken = prevToken) != null
|
||||
|
||||
val prevChunk = ChunkEntity.find(realm, roomId, nextToken = prevToken)
|
||||
val nextChunk = ChunkEntity.find(realm, roomId, prevToken = nextToken)
|
||||
|
||||
// The current chunk is the one we will keep all along the merge processChanges.
|
||||
// We try to look for a chunk next to the token,
|
||||
// otherwise we create a whole new one which is unlinked (not live)
|
||||
|
||||
var currentChunk = if (direction == PaginationDirection.FORWARDS) {
|
||||
val currentChunk = if (direction == PaginationDirection.FORWARDS) {
|
||||
prevChunk?.apply { this.nextToken = nextToken }
|
||||
} else {
|
||||
nextChunk?.apply { this.prevToken = prevToken }
|
||||
}
|
||||
?: ChunkEntity.create(realm, prevToken, nextToken, isUnlinked = true)
|
||||
?: ChunkEntity.create(realm, prevToken, nextToken)
|
||||
|
||||
if (receivedChunk.events.isEmpty() && receivedChunk.end == receivedChunk.start) {
|
||||
Timber.v("Reach end of $roomId")
|
||||
currentChunk.isLastBackward = true
|
||||
} else if (!shouldSkip) {
|
||||
} else {
|
||||
Timber.v("Add ${receivedChunk.events.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}")
|
||||
val timelineEvents = receivedChunk.events.mapNotNull {
|
||||
currentChunk.add(roomId, it, direction)
|
||||
}
|
||||
// Then we merge chunks if needed
|
||||
if (currentChunk != prevChunk && prevChunk != null) {
|
||||
currentChunk = handleMerge(roomEntity, direction, currentChunk, prevChunk)
|
||||
} else if (currentChunk != nextChunk && nextChunk != null) {
|
||||
currentChunk = handleMerge(roomEntity, direction, currentChunk, nextChunk)
|
||||
val roomMemberEventsByUser = HashMap<String, Event?>()
|
||||
val eventList = if (direction == PaginationDirection.FORWARDS) {
|
||||
receivedChunk.events
|
||||
} else {
|
||||
val newEventIds = receivedChunk.events.mapNotNull { it.eventId }
|
||||
val overlappedChunks = ChunkEntity.findAllIncludingEvents(realm, newEventIds)
|
||||
overlappedChunks
|
||||
.filter { it != currentChunk }
|
||||
.forEach { overlapped ->
|
||||
currentChunk = handleMerge(roomEntity, direction, currentChunk, overlapped)
|
||||
}
|
||||
receivedChunk.events.asReversed()
|
||||
}
|
||||
for (event in eventList) {
|
||||
if (event.eventId == null || event.senderId == null) {
|
||||
continue
|
||||
}
|
||||
val eventEntity = event.toEntity(roomId, SendState.SYNCED)
|
||||
if (event.type == EventType.STATE_ROOM_MEMBER && event.stateKey != null) {
|
||||
roomMemberEventsByUser[event.stateKey] = event
|
||||
}
|
||||
val roomMemberEvent = roomMemberEventsByUser.getOrPut(event.senderId) {
|
||||
CurrentStateEventEntity.getOrNull(realm, roomId, event.senderId, EventType.STATE_ROOM_MEMBER)?.root?.asDomain()
|
||||
}
|
||||
currentChunk.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS, roomMemberEvent)
|
||||
}
|
||||
roomEntity.addOrUpdate(currentChunk)
|
||||
for (stateEvent in receivedChunk.stateEvents) {
|
||||
roomEntity.addStateEvent(stateEvent, isUnlinked = currentChunk.isUnlinked)
|
||||
}
|
||||
timelineEventSenderVisitor.visit(timelineEvents)
|
||||
}
|
||||
}
|
||||
return if (receivedChunk.events.isEmpty()) {
|
||||
|
@ -180,23 +178,4 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
|
|||
Result.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleMerge(roomEntity: RoomEntity,
|
||||
direction: PaginationDirection,
|
||||
currentChunk: ChunkEntity,
|
||||
otherChunk: ChunkEntity): ChunkEntity {
|
||||
// We always merge the bottom chunk into top chunk, so we are always merging backwards
|
||||
Timber.v("Merge ${currentChunk.prevToken} | ${currentChunk.nextToken} with ${otherChunk.prevToken} | ${otherChunk.nextToken}")
|
||||
return if (direction == PaginationDirection.BACKWARDS && !otherChunk.isLastForward) {
|
||||
val events = currentChunk.merge(roomEntity.roomId, otherChunk, PaginationDirection.BACKWARDS)
|
||||
timelineEventSenderVisitor.visit(events)
|
||||
roomEntity.deleteOnCascade(otherChunk)
|
||||
currentChunk
|
||||
} else {
|
||||
val events = otherChunk.merge(roomEntity.roomId, currentChunk, PaginationDirection.BACKWARDS)
|
||||
timelineEventSenderVisitor.visit(events)
|
||||
roomEntity.deleteOnCascade(currentChunk)
|
||||
otherChunk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,14 +22,18 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||
import im.vector.matrix.android.internal.database.helper.*
|
||||
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.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
|
||||
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.find
|
||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||
|
@ -52,7 +56,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
private val roomFullyReadHandler: RoomFullyReadHandler,
|
||||
private val cryptoService: DefaultCryptoService,
|
||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||
private val timelineEventSenderVisitor: TimelineEventSenderVisitor,
|
||||
private val eventBus: EventBus) {
|
||||
|
||||
sealed class HandlingStrategy {
|
||||
|
@ -118,13 +121,16 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
roomEntity.membership = Membership.JOIN
|
||||
|
||||
// State event
|
||||
|
||||
if (roomSync.state?.events?.isNotEmpty() == true) {
|
||||
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
|
||||
?: Int.MIN_VALUE
|
||||
val untimelinedStateIndex = minStateIndex + 1
|
||||
roomSync.state.events.forEach { event ->
|
||||
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
||||
for (event in roomSync.state.events) {
|
||||
if (event.eventId == null || event.stateKey == null) {
|
||||
continue
|
||||
}
|
||||
val eventEntity = event.toEntity(roomId, SendState.SYNCED)
|
||||
CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
|
||||
eventId = event.eventId
|
||||
root = eventEntity
|
||||
}
|
||||
// Give info to crypto module
|
||||
cryptoService.onStateEvent(roomId, event)
|
||||
roomMemberEventHandler.handle(realm, roomId, event)
|
||||
|
@ -193,28 +199,37 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
prevToken: String? = null,
|
||||
isLimited: Boolean = true): ChunkEntity {
|
||||
val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomEntity.roomId)
|
||||
var stateIndexOffset = 0
|
||||
val chunkEntity = if (!isLimited && lastChunk != null) {
|
||||
lastChunk
|
||||
} else {
|
||||
realm.createObject<ChunkEntity>().apply { this.prevToken = prevToken }
|
||||
}
|
||||
if (isLimited && lastChunk != null) {
|
||||
stateIndexOffset = lastChunk.lastStateIndex(PaginationDirection.FORWARDS)
|
||||
}
|
||||
lastChunk?.isLastForward = false
|
||||
chunkEntity.isLastForward = true
|
||||
chunkEntity.isUnlinked = false
|
||||
|
||||
val timelineEvents = ArrayList<TimelineEventEntity>(eventList.size)
|
||||
val eventIds = ArrayList<String>(eventList.size)
|
||||
val roomMemberEventsByUser = HashMap<String, Event?>()
|
||||
|
||||
for (event in eventList) {
|
||||
if(event.eventId != null) {
|
||||
eventIds.add(event.eventId)
|
||||
if (event.eventId == null || event.senderId == null) {
|
||||
continue
|
||||
}
|
||||
chunkEntity.add(roomEntity.roomId, event, PaginationDirection.FORWARDS, stateIndexOffset)?.also {
|
||||
timelineEvents.add(it)
|
||||
eventIds.add(event.eventId)
|
||||
val eventEntity = event.toEntity(roomId, SendState.SYNCED)
|
||||
if (event.isStateEvent() && event.stateKey != null) {
|
||||
CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
|
||||
eventId = event.eventId
|
||||
root = eventEntity
|
||||
}
|
||||
if (event.type == EventType.STATE_ROOM_MEMBER) {
|
||||
roomMemberEventsByUser[event.stateKey] = event
|
||||
roomMemberEventHandler.handle(realm, roomEntity.roomId, event)
|
||||
}
|
||||
}
|
||||
val roomMemberEvent = roomMemberEventsByUser.getOrPut(event.senderId) {
|
||||
CurrentStateEventEntity.getOrNull(realm, roomId, event.senderId, EventType.STATE_ROOM_MEMBER)?.root?.asDomain()
|
||||
}
|
||||
chunkEntity.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS, roomMemberEvent)
|
||||
// Give info to crypto module
|
||||
cryptoService.onLiveEvent(roomEntity.roomId, event)
|
||||
// Try to remove local echo
|
||||
|
@ -227,9 +242,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
Timber.v("Can't find corresponding local echo for tx:$it")
|
||||
}
|
||||
}
|
||||
roomMemberEventHandler.handle(realm, roomEntity.roomId, event)
|
||||
}
|
||||
timelineEventSenderVisitor.visit(timelineEvents)
|
||||
// posting new events to timeline if any is registered
|
||||
eventBus.post(DefaultTimeline.OnNewTimelineEvents(roomId = roomId, eventIds = eventIds))
|
||||
return chunkEntity
|
||||
|
|
|
@ -61,7 +61,7 @@ class ImageMediaViewerActivity : VectorBaseActivity() {
|
|||
ViewCompat.setTransitionName(imageTransitionView, it)
|
||||
}
|
||||
if (mediaData.url.isNullOrEmpty()) {
|
||||
finish()
|
||||
supportFinishAfterTransition()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue