Realm-kotlin : continue migrating session db

This commit is contained in:
ganfra 2022-10-06 19:28:09 +02:00
parent 5d73118c8c
commit 6edea43ab4
30 changed files with 383 additions and 401 deletions

View File

@ -16,9 +16,8 @@
package org.matrix.android.sdk.internal.database.helper package org.matrix.android.sdk.internal.database.helper
import io.realm.Realm import io.realm.kotlin.MutableRealm
import io.realm.kotlin.TypedRealm import io.realm.kotlin.TypedRealm
import io.realm.kotlin.createObject
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
@ -32,14 +31,13 @@ import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.database.model.cleanUp
import org.matrix.android.sdk.internal.database.query.find import org.matrix.android.sdk.internal.database.query.find
import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.extensions.realm
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
import timber.log.Timber import timber.log.Timber
@ -63,6 +61,7 @@ internal fun ChunkEntity.addStateEvent(roomId: String, stateEvent: EventEntity,
} }
internal fun ChunkEntity.addTimelineEvent( internal fun ChunkEntity.addTimelineEvent(
realm: MutableRealm,
roomId: String, roomId: String,
eventEntity: EventEntity, eventEntity: EventEntity,
direction: PaginationDirection, direction: PaginationDirection,
@ -79,32 +78,33 @@ internal fun ChunkEntity.addTimelineEvent(
// Update RR for the sender of a new message with a dummy one // Update RR for the sender of a new message with a dummy one
val readReceiptsSummaryEntity = if (!ownedByThreadChunk) handleReadReceipts(realm, roomId, eventEntity, senderId) else null val readReceiptsSummaryEntity = if (!ownedByThreadChunk) handleReadReceipts(realm, roomId, eventEntity, senderId) else null
val timelineEventEntity = realm.createObject<TimelineEventEntity>().apply { val timelineEventEntity = realm.copyToRealm(
this.localId = localId TimelineEventEntity().apply {
this.root = eventEntity this.localId = localId
this.eventId = eventId this.root = eventEntity
this.roomId = roomId this.eventId = eventId
this.annotations = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst() this.roomId = roomId
?.also { it.cleanUp(eventEntity.sender) } this.annotations = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).first().find()
this.readReceipts = readReceiptsSummaryEntity ?.also { realm.cleanUp(it, eventEntity.sender) }
this.displayIndex = displayIndex this.readReceipts = readReceiptsSummaryEntity
this.ownedByThreadChunk = ownedByThreadChunk this.displayIndex = displayIndex
val roomMemberContent = roomMemberContentsByUser?.get(senderId) this.ownedByThreadChunk = ownedByThreadChunk
this.senderAvatar = roomMemberContent?.avatarUrl val roomMemberContent = roomMemberContentsByUser?.get(senderId)
this.senderName = roomMemberContent?.displayName this.senderAvatar = roomMemberContent?.avatarUrl
isUniqueDisplayName = if (roomMemberContent?.displayName != null) { this.senderName = roomMemberContent?.displayName
computeIsUnique(realm, roomId, isLastForward, roomMemberContent, roomMemberContentsByUser) isUniqueDisplayName = if (roomMemberContent?.displayName != null) {
} else { computeIsUnique(realm, roomId, isLastForward, roomMemberContent, roomMemberContentsByUser)
true } else {
} true
} }
// numberOfTimelineEvents++ }
)
timelineEvents.add(timelineEventEntity) timelineEvents.add(timelineEventEntity)
return timelineEventEntity return timelineEventEntity
} }
internal fun computeIsUnique( internal fun computeIsUnique(
realm: Realm, realm: TypedRealm,
roomId: String, roomId: String,
isLastForward: Boolean, isLastForward: Boolean,
senderRoomMemberContent: RoomMemberContent, senderRoomMemberContent: RoomMemberContent,
@ -116,8 +116,8 @@ internal fun computeIsUnique(
return if (isLastForward) { return if (isLastForward) {
val isLiveUnique = RoomMemberSummaryEntity val isLiveUnique = RoomMemberSummaryEntity
.where(realm, roomId) .where(realm, roomId)
.equalTo(RoomMemberSummaryEntityFields.DISPLAY_NAME, senderRoomMemberContent.displayName) .query("displayName == $0", senderRoomMemberContent.displayName)
.findAll() .find()
.none { .none {
!roomMemberContentsByUser.containsKey(it.userId) !roomMemberContentsByUser.containsKey(it.userId)
} }
@ -127,18 +127,19 @@ internal fun computeIsUnique(
} }
} }
private fun handleReadReceipts(realm: Realm, roomId: String, eventEntity: EventEntity, senderId: String): ReadReceiptsSummaryEntity { private fun handleReadReceipts(realm: MutableRealm, roomId: String, eventEntity: EventEntity, senderId: String): ReadReceiptsSummaryEntity {
val readReceiptsSummaryEntity = ReadReceiptsSummaryEntity.where(realm, eventEntity.eventId).findFirst() val readReceiptsSummaryEntity = ReadReceiptsSummaryEntity.where(realm, eventEntity.eventId).find()
?: realm.createObject<ReadReceiptsSummaryEntity>(eventEntity.eventId).apply { ?: realm.copyToRealm(ReadReceiptsSummaryEntity().apply {
this.eventId = eventEntity.eventId
this.roomId = roomId this.roomId = roomId
} })
val originServerTs = eventEntity.originServerTs val originServerTs = eventEntity.originServerTs
if (originServerTs != null) { if (originServerTs != null) {
val timestampOfEvent = originServerTs.toDouble() val timestampOfEvent = originServerTs.toDouble()
val readReceiptOfSender = ReadReceiptEntity.getOrCreate(realm, roomId = roomId, userId = senderId) val readReceiptOfSender = ReadReceiptEntity.getOrCreate(realm, roomId = roomId, userId = senderId)
// If the synced RR is older, update // If the synced RR is older, update
if (timestampOfEvent > readReceiptOfSender.originServerTs) { if (timestampOfEvent > readReceiptOfSender.originServerTs) {
val previousReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId = readReceiptOfSender.eventId).findFirst() val previousReceiptsSummary = ReadReceiptsSummaryEntity.where(realm, eventId = readReceiptOfSender.eventId).find()
readReceiptOfSender.eventId = eventEntity.eventId readReceiptOfSender.eventId = eventEntity.eventId
readReceiptOfSender.originServerTs = timestampOfEvent readReceiptOfSender.originServerTs = timestampOfEvent
previousReceiptsSummary?.readReceipts?.remove(readReceiptOfSender) previousReceiptsSummary?.readReceipts?.remove(readReceiptOfSender)

View File

@ -20,6 +20,8 @@ import com.squareup.moshi.JsonDataException
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.Sort import io.realm.Sort
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.TypedRealm
import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.api.session.events.model.isRedacted import org.matrix.android.sdk.api.session.events.model.isRedacted
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -48,13 +50,13 @@ private typealias Summary = Pair<Int, TimelineEventEntity>?
*/ */
internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded( internal fun Map<String, EventEntity>.updateThreadSummaryIfNeeded(
roomId: String, roomId: String,
realm: Realm, realm: MutableRealm,
currentUserId: String, currentUserId: String,
chunkEntity: ChunkEntity? = null, chunkEntity: ChunkEntity? = null,
shouldUpdateNotifications: Boolean = true shouldUpdateNotifications: Boolean = true
) { ) {
for ((rootThreadEventId, eventEntity) in this) { for ((rootThreadEventId, eventEntity) in this) {
eventEntity.threadSummaryInThread(eventEntity.realm, rootThreadEventId, chunkEntity)?.let { threadSummary -> eventEntity.threadSummaryInThread(realm, rootThreadEventId, chunkEntity)?.let { threadSummary ->
val inThreadMessages = threadSummary.first val inThreadMessages = threadSummary.first
val latestEventInThread = threadSummary.second val latestEventInThread = threadSummary.second
@ -105,7 +107,7 @@ internal fun EventEntity.markEventAsRoot(
* @param chunkEntity the chunk entity * @param chunkEntity the chunk entity
* @return A ThreadSummary containing the counted threads and the latest event message * @return A ThreadSummary containing the counted threads and the latest event message
*/ */
internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary { internal fun EventEntity.threadSummaryInThread(realm: TypedRealm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary {
val inThreadMessages = countInThreadMessages( val inThreadMessages = countInThreadMessages(
realm = realm, realm = realm,
roomId = roomId, roomId = roomId,
@ -141,12 +143,12 @@ internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId:
* Counts the number of thread replies in the main timeline thread summary, * Counts the number of thread replies in the main timeline thread summary,
* with respect to redactions. * with respect to redactions.
*/ */
internal fun countInThreadMessages(realm: Realm, roomId: String, rootThreadEventId: String): Int = internal fun countInThreadMessages(realm: TypedRealm, roomId: String, rootThreadEventId: String): Int =
TimelineEventEntity TimelineEventEntity
.whereRoomId(realm, roomId = roomId) .whereRoomId(realm, roomId = roomId)
.equalTo(TimelineEventEntityFields.ROOT.ROOT_THREAD_EVENT_ID, rootThreadEventId) .query("root.rootThreadEventId == $0", rootThreadEventId)
.distinct(TimelineEventEntityFields.ROOT.EVENT_ID) .distinct("root.eventId")
.findAll() .find()
.filterNot { timelineEvent -> .filterNot { timelineEvent ->
timelineEvent.root timelineEvent.root
?.unsignedData ?.unsignedData

View File

@ -16,10 +16,10 @@
package org.matrix.android.sdk.internal.database.helper package org.matrix.android.sdk.internal.database.helper
import io.realm.Realm import io.realm.kotlin.MutableRealm
import io.realm.RealmQuery import io.realm.kotlin.TypedRealm
import io.realm.Sort import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.createObject import io.realm.kotlin.query.Sort
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
@ -38,27 +38,28 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.cleanUp
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntityFields import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.getOrNull
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.extensions.realm
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
import org.matrix.android.sdk.internal.session.room.timeline.TimelineEventDecryptor import org.matrix.android.sdk.internal.session.room.timeline.TimelineEventDecryptor
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
internal fun ThreadSummaryEntity.updateThreadSummary( internal fun ThreadSummaryEntity.updateThreadSummary(
realm: TypedRealm,
rootThreadEventEntity: EventEntity, rootThreadEventEntity: EventEntity,
numberOfThreads: Int?, numberOfThreads: Int?,
latestThreadEventEntity: EventEntity?, latestThreadEventEntity: EventEntity?,
isUserParticipating: Boolean, isUserParticipating: Boolean,
roomMemberContentsByUser: HashMap<String, RoomMemberContent?> roomMemberContentsByUser: HashMap<String, RoomMemberContent?>
) { ) {
updateThreadSummaryRootEvent(rootThreadEventEntity, roomMemberContentsByUser) updateThreadSummaryRootEvent(realm, rootThreadEventEntity, roomMemberContentsByUser)
updateThreadSummaryLatestEvent(latestThreadEventEntity, roomMemberContentsByUser) updateThreadSummaryLatestEvent(realm, latestThreadEventEntity, roomMemberContentsByUser)
this.isUserParticipating = isUserParticipating this.isUserParticipating = isUserParticipating
numberOfThreads?.let { numberOfThreads?.let {
// Update number of threads only when there is an actual value // Update number of threads only when there is an actual value
@ -70,6 +71,7 @@ internal fun ThreadSummaryEntity.updateThreadSummary(
* Updates the root thread event properties. * Updates the root thread event properties.
*/ */
internal fun ThreadSummaryEntity.updateThreadSummaryRootEvent( internal fun ThreadSummaryEntity.updateThreadSummaryRootEvent(
realm: TypedRealm,
rootThreadEventEntity: EventEntity, rootThreadEventEntity: EventEntity,
roomMemberContentsByUser: HashMap<String, RoomMemberContent?> roomMemberContentsByUser: HashMap<String, RoomMemberContent?>
) { ) {
@ -89,6 +91,7 @@ internal fun ThreadSummaryEntity.updateThreadSummaryRootEvent(
* Updates the latest thread event properties. * Updates the latest thread event properties.
*/ */
internal fun ThreadSummaryEntity.updateThreadSummaryLatestEvent( internal fun ThreadSummaryEntity.updateThreadSummaryLatestEvent(
realm: TypedRealm,
latestThreadEventEntity: EventEntity?, latestThreadEventEntity: EventEntity?,
roomMemberContentsByUser: HashMap<String, RoomMemberContent?> roomMemberContentsByUser: HashMap<String, RoomMemberContent?>
) { ) {
@ -104,19 +107,19 @@ internal fun ThreadSummaryEntity.updateThreadSummaryLatestEvent(
} }
} }
private fun EventEntity.toTimelineEventEntity(roomMemberContentsByUser: HashMap<String, RoomMemberContent?>): TimelineEventEntity { private fun EventEntity.toTimelineEventEntity(realm: MutableRealm, roomMemberContentsByUser: HashMap<String, RoomMemberContent?>): TimelineEventEntity {
val roomId = roomId val roomId = roomId
val eventId = eventId val eventId = eventId
val localId = TimelineEventEntity.nextId(realm) val localId = TimelineEventEntity.nextId(realm)
val senderId = sender ?: "" val senderId = sender ?: ""
val timelineEventEntity = realm.createObject<TimelineEventEntity>().apply { val timelineEventEntity = TimelineEventEntity().apply {
this.localId = localId this.localId = localId
this.root = this@toTimelineEventEntity this.root = this@toTimelineEventEntity
this.eventId = eventId this.eventId = eventId
this.roomId = roomId this.roomId = roomId
this.annotations = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst() this.annotations = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).first().find()
?.also { it.cleanUp(sender) } ?.also { realm.cleanUp(it, sender) }
this.ownedByThreadChunk = true // To skip it from the original event flow this.ownedByThreadChunk = true // To skip it from the original event flow
val roomMemberContent = roomMemberContentsByUser[senderId] val roomMemberContent = roomMemberContentsByUser[senderId]
this.senderAvatar = roomMemberContent?.avatarUrl this.senderAvatar = roomMemberContent?.avatarUrl
@ -132,7 +135,7 @@ private fun EventEntity.toTimelineEventEntity(roomMemberContentsByUser: HashMap<
internal fun ThreadSummaryEntity.Companion.createOrUpdate( internal fun ThreadSummaryEntity.Companion.createOrUpdate(
threadSummaryType: ThreadSummaryUpdateType, threadSummaryType: ThreadSummaryUpdateType,
realm: Realm, realm: MutableRealm,
roomId: String, roomId: String,
threadEventEntity: EventEntity? = null, threadEventEntity: EventEntity? = null,
rootThreadEvent: Event? = null, rootThreadEvent: Event? = null,
@ -173,6 +176,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
val isUserParticipating = rootThreadEvent.unsignedData.relations.latestThread.isUserParticipating == true || rootThreadEvent.senderId == userId val isUserParticipating = rootThreadEvent.unsignedData.relations.latestThread.isUserParticipating == true || rootThreadEvent.senderId == userId
roomMemberContentsByUser.addSenderState(realm, roomId, rootThreadEvent.senderId) roomMemberContentsByUser.addSenderState(realm, roomId, rootThreadEvent.senderId)
threadSummary.updateThreadSummary( threadSummary.updateThreadSummary(
realm = realm,
rootThreadEventEntity = rootThreadEventEntity, rootThreadEventEntity = rootThreadEventEntity,
numberOfThreads = numberOfThreads, numberOfThreads = numberOfThreads,
latestThreadEventEntity = latestThreadEventEntity, latestThreadEventEntity = latestThreadEventEntity,
@ -190,7 +194,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
if (threadSummary != null) { if (threadSummary != null) {
// ThreadSummary exists so lets add the latest event // ThreadSummary exists so lets add the latest event
Timber.i("###THREADS ThreadSummaryHelper ADD root eventId:$rootThreadEventId exists, lets update latest thread event.") Timber.i("###THREADS ThreadSummaryHelper ADD root eventId:$rootThreadEventId exists, lets update latest thread event.")
threadSummary.updateThreadSummaryLatestEvent(threadEventEntity, roomMemberContentsByUser) threadSummary.updateThreadSummaryLatestEvent(realm, threadEventEntity, roomMemberContentsByUser)
threadSummary.numberOfThreads++ threadSummary.numberOfThreads++
if (threadEventEntity.sender == userId) { if (threadEventEntity.sender == userId) {
threadSummary.isUserParticipating = true threadSummary.isUserParticipating = true
@ -202,6 +206,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
// Root thread event entity exists so lets create a new record // Root thread event entity exists so lets create a new record
ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEventEntity.eventId).let { ThreadSummaryEntity.getOrCreate(realm, roomId, rootThreadEventEntity.eventId).let {
it.updateThreadSummary( it.updateThreadSummary(
realm = realm,
rootThreadEventEntity = rootThreadEventEntity, rootThreadEventEntity = rootThreadEventEntity,
numberOfThreads = 1, numberOfThreads = 1,
latestThreadEventEntity = threadEventEntity, latestThreadEventEntity = threadEventEntity,
@ -259,7 +264,7 @@ private fun requestDecryption(eventDecryptor: TimelineEventDecryptor?, event: Ev
/** /**
* If we don't have any new state on this user, get it from db. * If we don't have any new state on this user, get it from db.
*/ */
private fun HashMap<String, RoomMemberContent?>.addSenderState(realm: Realm, roomId: String, senderId: String) { private fun HashMap<String, RoomMemberContent?>.addSenderState(realm: TypedRealm, roomId: String, senderId: String) {
getOrPut(senderId) { getOrPut(senderId) {
CurrentStateEventEntity CurrentStateEventEntity
.getOrNull(realm, roomId, senderId, EventType.STATE_ROOM_MEMBER) .getOrNull(realm, roomId, senderId, EventType.STATE_ROOM_MEMBER)
@ -271,7 +276,7 @@ private fun HashMap<String, RoomMemberContent?>.addSenderState(realm: Realm, roo
/** /**
* Create an EventEntity for the root thread event or get an existing one. * Create an EventEntity for the root thread event or get an existing one.
*/ */
private fun createEventEntity(realm: Realm, roomId: String, event: Event, currentTimeMillis: Long): EventEntity { private fun createEventEntity(realm: MutableRealm, roomId: String, event: Event, currentTimeMillis: Long): EventEntity {
val ageLocalTs = currentTimeMillis - (event.unsignedData?.age ?: 0) val ageLocalTs = currentTimeMillis - (event.unsignedData?.age ?: 0)
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
} }
@ -281,7 +286,7 @@ private fun createEventEntity(realm: Realm, roomId: String, event: Event, curren
* state * state
*/ */
private fun createLatestEventEntity( private fun createLatestEventEntity(
realm: Realm, realm: MutableRealm,
roomId: String, roomId: String,
rootThreadEvent: Event, rootThreadEvent: Event,
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>, roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
@ -308,7 +313,7 @@ private fun getLatestEvent(rootThreadEvent: Event): Event? {
* @param realm the realm instance * @param realm the realm instance
* @param roomId The id of the room * @param roomId The id of the room
*/ */
internal fun ThreadSummaryEntity.Companion.findAllThreadsForRoomId(realm: Realm, roomId: String): RealmQuery<ThreadSummaryEntity> = internal fun ThreadSummaryEntity.Companion.findAllThreadsForRoomId(realm: TypedRealm, roomId: String): RealmQuery<ThreadSummaryEntity> =
ThreadSummaryEntity ThreadSummaryEntity
.where(realm, roomId = roomId) .where(realm, roomId = roomId)
.sort(ThreadSummaryEntityFields.LATEST_THREAD_EVENT_ENTITY.ORIGIN_SERVER_TS, Sort.DESCENDING) .sort(ThreadSummaryEntityFields.LATEST_THREAD_EVENT_ENTITY.ORIGIN_SERVER_TS, Sort.DESCENDING)
@ -316,24 +321,25 @@ internal fun ThreadSummaryEntity.Companion.findAllThreadsForRoomId(realm: Realm,
/** /**
* Enhance each [ThreadSummary] root and latest event with the equivalent decrypted text edition/replacement. * Enhance each [ThreadSummary] root and latest event with the equivalent decrypted text edition/replacement.
*/ */
internal fun List<ThreadSummary>.enhanceWithEditions(realm: Realm, roomId: String): List<ThreadSummary> = internal fun List<ThreadSummary>.enhanceWithEditions(realm: TypedRealm, roomId: String): List<ThreadSummary> =
this.map { this.map {
it.addEditionIfNeeded(realm, roomId, true) it.addEditionIfNeeded(realm, roomId, true)
it.addEditionIfNeeded(realm, roomId, false) it.addEditionIfNeeded(realm, roomId, false)
it it
} }
private fun ThreadSummary.addEditionIfNeeded(realm: Realm, roomId: String, enhanceRoot: Boolean) { private fun ThreadSummary.addEditionIfNeeded(realm: TypedRealm, roomId: String, enhanceRoot: Boolean) {
val eventId = if (enhanceRoot) rootEventId else latestEvent?.eventId ?: return val eventId = if (enhanceRoot) rootEventId else latestEvent?.eventId ?: return
EventAnnotationsSummaryEntity EventAnnotationsSummaryEntity
.where(realm, roomId, eventId) .where(realm, roomId, eventId)
.findFirst() .first()
.find()
?.editSummary ?.editSummary
?.editions ?.editions
?.lastOrNull() ?.lastOrNull()
?.eventId ?.eventId
?.let { editedEventId -> ?.let { editedEventId ->
TimelineEventEntity.where(realm, roomId, eventId = editedEventId).findFirst()?.let { editedEvent -> TimelineEventEntity.where(realm, roomId, eventId = editedEventId).first().find()?.let { editedEvent ->
if (enhanceRoot) { if (enhanceRoot) {
threadEditions.rootThreadEdition = editedEvent.root?.asDomain()?.getDecryptedTextSummary() ?: "(edited)" threadEditions.rootThreadEdition = editedEvent.root?.asDomain()?.getDecryptedTextSummary() ?: "(edited)"
} else { } else {

View File

@ -16,22 +16,22 @@
package org.matrix.android.sdk.internal.database.mapper package org.matrix.android.sdk.internal.database.mapper
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
import org.matrix.android.sdk.internal.database.RealmObjectMapper
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
import javax.inject.Inject import javax.inject.Inject
internal class LiveLocationShareAggregatedSummaryMapper @Inject constructor() : internal class LiveLocationShareAggregatedSummaryMapper @Inject constructor() :
Monarchy.Mapper<LiveLocationShareAggregatedSummary, LiveLocationShareAggregatedSummaryEntity> { RealmObjectMapper<LiveLocationShareAggregatedSummaryEntity, LiveLocationShareAggregatedSummary> {
override fun map(entity: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary { override fun map(realmObject: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary {
return LiveLocationShareAggregatedSummary( return LiveLocationShareAggregatedSummary(
userId = entity.userId, userId = realmObject.userId,
isActive = entity.isActive, isActive = realmObject.isActive,
endOfLiveTimestampMillis = entity.endOfLiveTimestampMillis, endOfLiveTimestampMillis = realmObject.endOfLiveTimestampMillis,
lastLocationDataContent = ContentMapper.map(entity.lastLocationContent).toModel<MessageBeaconLocationDataContent>() lastLocationDataContent = ContentMapper.map(realmObject.lastLocationContent).toModel<MessageBeaconLocationDataContent>()
) )
} }
} }

View File

@ -16,41 +16,30 @@
package org.matrix.android.sdk.internal.database.mapper package org.matrix.android.sdk.internal.database.mapper
import io.realm.Realm import io.realm.kotlin.TypedRealm
import io.realm.RealmList
import io.realm.kotlin.isManaged
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.extensions.realm import org.matrix.android.sdk.internal.di.SessionDatabase
import javax.inject.Inject import javax.inject.Inject
internal class ReadReceiptsSummaryMapper @Inject constructor( internal class ReadReceiptsSummaryMapper @Inject constructor(
private val realmSessionProvider: RealmSessionProvider @SessionDatabase private val realmInstance: RealmInstance,
) { ) {
fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> { fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> {
if (readReceiptsSummaryEntity == null) { val readReceipts = readReceiptsSummaryEntity?.readReceipts.orEmpty()
return emptyList() val realm = realmInstance.getBlockingRealm()
} return map(readReceipts, realm)
val readReceipts = readReceiptsSummaryEntity.readReceipts
// Avoid opening a new realm if we already have one opened
return if (readReceiptsSummaryEntity.isManaged()) {
map(readReceipts, readReceiptsSummaryEntity.realm)
} else {
realmSessionProvider.withRealm { realm ->
map(readReceipts, realm)
}
}
} }
private fun map(readReceipts: RealmList<ReadReceiptEntity>, realm: Realm): List<ReadReceipt> { private fun map(readReceipts: List<ReadReceiptEntity>, realm: TypedRealm): List<ReadReceipt> {
return readReceipts return readReceipts
.mapNotNull { .mapNotNull {
val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).findFirst() val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).first().find()
?: return@mapNotNull null ?: return@mapNotNull null
ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong()) ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong())
} }

View File

@ -15,11 +15,13 @@
*/ */
package org.matrix.android.sdk.internal.database.model package org.matrix.android.sdk.internal.database.model
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.ext.realmListOf import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.types.RealmList import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey import io.realm.kotlin.types.annotations.PrimaryKey
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
import timber.log.Timber
internal class EventAnnotationsSummaryEntity : RealmObject { internal class EventAnnotationsSummaryEntity : RealmObject {
@PrimaryKey @PrimaryKey
@ -52,6 +54,18 @@ internal class EventAnnotationsSummaryEntity : RealmObject {
companion object companion object
} }
internal fun MutableRealm.cleanUp(entity: EventAnnotationsSummaryEntity, originalEventSenderId: String?) {
originalEventSenderId ?: return
entity.editSummary?.editions?.filter {
it.senderId != originalEventSenderId
}
?.forEach {
Timber.w("Deleting an edition from ${it.senderId} of event sent by $originalEventSenderId")
delete(it)
}
}
/* /*
internal fun EventAnnotationsSummaryEntity.deleteOnCascade() { internal fun EventAnnotationsSummaryEntity.deleteOnCascade() {
reactionsSummary.deleteAllFromRealm() reactionsSummary.deleteAllFromRealm()

View File

@ -25,6 +25,6 @@ import io.realm.kotlin.types.RealmObject
*/ */
internal class UserDraftsEntity : RealmObject { internal class UserDraftsEntity : RealmObject {
var userDrafts: RealmList<DraftEntity> = realmListOf() var userDrafts: RealmList<DraftEntity> = realmListOf()
var roomId: String = ""
companion object companion object
} }

View File

@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.database.query
import io.realm.kotlin.MutableRealm import io.realm.kotlin.MutableRealm
import io.realm.kotlin.TypedRealm import io.realm.kotlin.TypedRealm
import io.realm.kotlin.query.RealmQuery import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.types.RealmList
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.database.andIf import org.matrix.android.sdk.internal.database.andIf
import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntity
@ -91,9 +92,7 @@ internal fun EventEntity.Companion.whereTypes(
} }
internal fun RealmList<EventEntity>.find(eventId: String): EventEntity? { internal fun RealmList<EventEntity>.find(eventId: String): EventEntity? {
return this.where() return return firstOrNull { it.eventId == eventId }
.equalTo(EventEntityFields.EVENT_ID, eventId)
.findFirst()
} }
internal fun RealmList<EventEntity>.fastContains(eventId: String): Boolean { internal fun RealmList<EventEntity>.fastContains(eventId: String): Boolean {

View File

@ -16,79 +16,80 @@
package org.matrix.android.sdk.internal.database.query package org.matrix.android.sdk.internal.database.query
import io.realm.Realm import io.realm.kotlin.MutableRealm
import io.realm.RealmQuery import io.realm.kotlin.TypedRealm
import io.realm.kotlin.where import io.realm.kotlin.query.RealmQuery
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where(
realm: Realm, realm: TypedRealm,
eventId: String, eventId: String,
): RealmQuery<LiveLocationShareAggregatedSummaryEntity> { ): RealmQuery<LiveLocationShareAggregatedSummaryEntity> {
return realm.where<LiveLocationShareAggregatedSummaryEntity>() return realm.query(LiveLocationShareAggregatedSummaryEntity::class)
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId) .query("eventId == $0", eventId)
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where(
realm: Realm, realm: TypedRealm,
roomId: String, roomId: String,
eventId: String, eventId: String,
): RealmQuery<LiveLocationShareAggregatedSummaryEntity> { ): RealmQuery<LiveLocationShareAggregatedSummaryEntity> {
return LiveLocationShareAggregatedSummaryEntity return LiveLocationShareAggregatedSummaryEntity
.whereRoomId(realm, roomId = roomId) .whereRoomId(realm, roomId = roomId)
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId) .query("eventId == $0", eventId)
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.whereRoomId( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.whereRoomId(
realm: Realm, realm: TypedRealm,
roomId: String roomId: String
): RealmQuery<LiveLocationShareAggregatedSummaryEntity> { ): RealmQuery<LiveLocationShareAggregatedSummaryEntity> {
return realm.where<LiveLocationShareAggregatedSummaryEntity>() return realm.query(LiveLocationShareAggregatedSummaryEntity::class)
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, roomId) .query("roomId == $0", roomId)
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.create( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.create(
realm: Realm, realm: MutableRealm,
roomId: String, roomId: String,
eventId: String, eventId: String,
): LiveLocationShareAggregatedSummaryEntity { ): LiveLocationShareAggregatedSummaryEntity {
val obj = realm.createObject(LiveLocationShareAggregatedSummaryEntity::class.java, eventId).apply { val entity = realm.copyToRealm(
this.roomId = roomId LiveLocationShareAggregatedSummaryEntity().apply {
} this.eventId = eventId
this.roomId = roomId
}
)
val annotationSummary = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId = roomId, eventId = eventId) val annotationSummary = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId = roomId, eventId = eventId)
annotationSummary.liveLocationShareAggregatedSummary = obj annotationSummary.liveLocationShareAggregatedSummary = entity
return entity
return obj
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.getOrCreate( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.getOrCreate(
realm: Realm, realm: MutableRealm,
roomId: String, roomId: String,
eventId: String, eventId: String,
): LiveLocationShareAggregatedSummaryEntity { ): LiveLocationShareAggregatedSummaryEntity {
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst() return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).first().find()
?: LiveLocationShareAggregatedSummaryEntity.create(realm, roomId, eventId) ?: LiveLocationShareAggregatedSummaryEntity.create(realm, roomId, eventId)
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get(
realm: Realm, realm: TypedRealm,
roomId: String, roomId: String,
eventId: String, eventId: String,
): LiveLocationShareAggregatedSummaryEntity? { ): LiveLocationShareAggregatedSummaryEntity? {
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst() return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).first().find()
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get(
realm: Realm, realm: TypedRealm,
eventId: String, eventId: String,
): LiveLocationShareAggregatedSummaryEntity? { ): LiveLocationShareAggregatedSummaryEntity? {
return LiveLocationShareAggregatedSummaryEntity.where(realm, eventId).findFirst() return LiveLocationShareAggregatedSummaryEntity.where(realm, eventId).first().find()
} }
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveInRoomForUser( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveInRoomForUser(
realm: Realm, realm: TypedRealm,
roomId: String, roomId: String,
userId: String, userId: String,
ignoredEventId: String, ignoredEventId: String,
@ -96,11 +97,11 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveIn
): List<LiveLocationShareAggregatedSummaryEntity> { ): List<LiveLocationShareAggregatedSummaryEntity> {
return LiveLocationShareAggregatedSummaryEntity return LiveLocationShareAggregatedSummaryEntity
.whereRoomId(realm, roomId = roomId) .whereRoomId(realm, roomId = roomId)
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.USER_ID, userId) .query("userId == $0", userId)
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) .query("isActive == true")
.notEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, ignoredEventId) .query("eventId != $0", ignoredEventId)
.lessThan(LiveLocationShareAggregatedSummaryEntityFields.START_OF_LIVE_TIMESTAMP_MILLIS, startOfLiveTimestampThreshold) .query("startOfLiveTimestampMillis < $0", startOfLiveTimestampThreshold)
.findAll() .find()
.toList() .toList()
} }
@ -108,12 +109,12 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveIn
* A live is considered as running when active and with at least a last known location. * A live is considered as running when active and with at least a last known location.
*/ */
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findRunningLiveInRoom( internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findRunningLiveInRoom(
realm: Realm, realm: TypedRealm,
roomId: String, roomId: String,
): RealmQuery<LiveLocationShareAggregatedSummaryEntity> { ): RealmQuery<LiveLocationShareAggregatedSummaryEntity> {
return LiveLocationShareAggregatedSummaryEntity return LiveLocationShareAggregatedSummaryEntity
.whereRoomId(realm, roomId = roomId) .whereRoomId(realm, roomId = roomId)
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) .query("isActive == true")
.isNotEmpty(LiveLocationShareAggregatedSummaryEntityFields.USER_ID) .query("userId != ''")
.isNotNull(LiveLocationShareAggregatedSummaryEntityFields.LAST_LOCATION_CONTENT) .query("lastLocationContent != ''")
} }

View File

@ -19,10 +19,7 @@ import io.realm.kotlin.TypedRealm
import io.realm.kotlin.query.RealmQuery import io.realm.kotlin.query.RealmQuery
import org.matrix.android.sdk.api.session.pushrules.RuleKind import org.matrix.android.sdk.api.session.pushrules.RuleKind
import org.matrix.android.sdk.internal.database.andIf import org.matrix.android.sdk.internal.database.andIf
import org.matrix.android.sdk.internal.database.model.PushRuleEntity
import org.matrix.android.sdk.internal.database.model.PushRuleEntityFields
import org.matrix.android.sdk.internal.database.model.PushRulesEntity import org.matrix.android.sdk.internal.database.model.PushRulesEntity
import org.matrix.android.sdk.internal.database.model.PushRulesEntityFields
import org.matrix.android.sdk.internal.database.model.PusherEntity import org.matrix.android.sdk.internal.database.model.PusherEntity
internal fun PusherEntity.Companion.where( internal fun PusherEntity.Companion.where(
@ -45,15 +42,3 @@ internal fun PushRulesEntity.Companion.where(
.query("kindStr == $0", kind.name) .query("kindStr == $0", kind.name)
} }
/*
internal fun PushRuleEntity.Companion.where(
realm: TypedRealm,
scope: String,
ruleId: String
): RealmQuery<PushRuleEntity> {
return realm.query(PushRuleEntity::class)
.equalTo(PushRuleEntityFields.PARENT.SCOPE, scope)
.equalTo(PushRuleEntityFields.RULE_ID, ruleId)
}
*/

View File

@ -133,9 +133,7 @@ internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<Strin
} }
internal fun RealmList<TimelineEventEntity>.find(eventId: String): TimelineEventEntity? { internal fun RealmList<TimelineEventEntity>.find(eventId: String): TimelineEventEntity? {
return where() return firstOrNull { it.eventId == eventId }
.equalTo(TimelineEventEntityFields.EVENT_ID, eventId)
.findFirst()
} }
internal fun TimelineEventEntity.Companion.findAllInRoomWithSendStates( internal fun TimelineEventEntity.Companion.findAllInRoomWithSendStates(

View File

@ -16,16 +16,14 @@
package org.matrix.android.sdk.internal.database.query package org.matrix.android.sdk.internal.database.query
import io.realm.Realm import io.realm.kotlin.TypedRealm
import io.realm.RealmQuery import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.where import org.matrix.android.sdk.internal.database.andIf
import org.matrix.android.sdk.internal.database.model.UserDraftsEntity import org.matrix.android.sdk.internal.database.model.UserDraftsEntity
import org.matrix.android.sdk.internal.database.model.UserDraftsEntityFields
internal fun UserDraftsEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<UserDraftsEntity> { internal fun UserDraftsEntity.Companion.where(realm: TypedRealm, roomId: String? = null): RealmQuery<UserDraftsEntity> {
val query = realm.where<UserDraftsEntity>() return realm.query(UserDraftsEntity::class)
if (roomId != null) { .andIf(roomId != null) {
query.equalTo(UserDraftsEntityFields.ROOM_SUMMARY_ENTITY.ROOM_ID, roomId) query("roomId == $0", roomId!!)
} }
return query
} }

View File

@ -16,8 +16,6 @@
package org.matrix.android.sdk.internal.session.room.create package org.matrix.android.sdk.internal.session.room.create
import com.zhuinden.monarchy.Monarchy
import io.realm.RealmConfiguration
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.failure.MatrixError
@ -26,10 +24,9 @@ import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.awaitTransaction
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
@ -40,7 +37,6 @@ import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -49,13 +45,11 @@ internal interface CreateRoomTask : Task<CreateRoomParams, String>
internal class DefaultCreateRoomTask @Inject constructor( internal class DefaultCreateRoomTask @Inject constructor(
private val roomAPI: RoomAPI, private val roomAPI: RoomAPI,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker, private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker,
private val directChatsHelper: DirectChatsHelper, private val directChatsHelper: DirectChatsHelper,
private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val readMarkersTask: SetReadMarkersTask, private val readMarkersTask: SetReadMarkersTask,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val createRoomBodyBuilder: CreateRoomBodyBuilder, private val createRoomBodyBuilder: CreateRoomBodyBuilder,
private val globalErrorReceiver: GlobalErrorReceiver, private val globalErrorReceiver: GlobalErrorReceiver,
private val clock: Clock, private val clock: Clock,
@ -93,17 +87,16 @@ internal class DefaultCreateRoomTask @Inject constructor(
val roomId = createRoomResponse.roomId val roomId = createRoomResponse.roomId
// Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before) // Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before)
try { try {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmInstance, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(RoomSummaryEntity::class.java) RoomSummaryEntity.where(realm, roomId)
.equalTo(RoomSummaryEntityFields.ROOM_ID, roomId) .query("membershipStr == $0", Membership.JOIN.name)
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
} }
} catch (exception: TimeoutCancellationException) { } catch (exception: TimeoutCancellationException) {
throw CreateRoomFailure.CreatedWithTimeout(roomId) throw CreateRoomFailure.CreatedWithTimeout(roomId)
} }
awaitTransaction(realmConfiguration) { realmInstance.write {
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = clock.epochMillis() RoomSummaryEntity.where(this, roomId).first().find()?.lastActivityTime = clock.epochMillis()
} }
handleDirectChatCreation(roomId, createRoomBody.getDirectUserId()) handleDirectChatCreation(roomId, createRoomBody.getDirectUserId())
@ -113,8 +106,8 @@ internal class DefaultCreateRoomTask @Inject constructor(
private suspend fun handleDirectChatCreation(roomId: String, otherUserId: String?) { private suspend fun handleDirectChatCreation(roomId: String, otherUserId: String?) {
otherUserId ?: return // This is not a direct room otherUserId ?: return // This is not a direct room
monarchy.awaitTransaction { realm -> realmInstance.write {
RoomSummaryEntity.where(realm, roomId).findFirst()?.apply { RoomSummaryEntity.where(this, roomId).first().find()?.apply {
this.directUserId = otherUserId this.directUserId = otherUserId
this.isDirect = true this.isDirect = true
} }

View File

@ -17,76 +17,75 @@
package org.matrix.android.sdk.internal.session.room.draft package org.matrix.android.sdk.internal.session.room.draft
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.asLiveData
import com.zhuinden.monarchy.Monarchy import io.realm.kotlin.MutableRealm
import io.realm.Realm
import io.realm.kotlin.createObject
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.session.room.send.UserDraft
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.clearWith
import org.matrix.android.sdk.internal.database.mapper.DraftMapper import org.matrix.android.sdk.internal.database.mapper.DraftMapper
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.UserDraftsEntity import org.matrix.android.sdk.internal.database.model.UserDraftsEntity
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.mapOptional
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class DraftRepository @Inject constructor( internal class DraftRepository @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val realmSessionProvider: RealmSessionProvider
) { ) {
suspend fun saveDraft(roomId: String, userDraft: UserDraft) { suspend fun saveDraft(roomId: String, userDraft: UserDraft) {
monarchy.awaitTransaction { realmInstance.write {
saveDraftInDb(it, userDraft, roomId) saveDraftInDb(this, userDraft, roomId)
} }
} }
suspend fun deleteDraft(roomId: String) { suspend fun deleteDraft(roomId: String) {
monarchy.awaitTransaction { realmInstance.write {
deleteDraftFromDb(it, roomId) deleteDraftFromDb(this, roomId)
} }
} }
fun getDraft(roomId: String): UserDraft? { fun getDraft(roomId: String): UserDraft? {
return realmSessionProvider.withRealm { realm -> val realm = realmInstance.getBlockingRealm()
UserDraftsEntity.where(realm, roomId).findFirst() return UserDraftsEntity.where(realm, roomId).first()
?.userDrafts .find()
?.firstOrNull() ?.let { mapUserDrafts(it) }
?.let {
DraftMapper.map(it)
}
}
} }
fun getDraftsLive(roomId: String): LiveData<Optional<UserDraft>> { fun getDraftsLive(roomId: String): LiveData<Optional<UserDraft>> {
val liveData = monarchy.findAllMappedWithChanges( return realmInstance.queryFirst {
{ UserDraftsEntity.where(it, roomId) }, UserDraftsEntity.where(it, roomId).first()
{ }
it.userDrafts.map { draft -> .mapOptional(::mapUserDrafts)
DraftMapper.map(draft) .asLiveData()
} }
}
) private fun mapUserDrafts(userDraftsEntity: UserDraftsEntity): UserDraft? {
return Transformations.map(liveData) { return userDraftsEntity.userDrafts.firstOrNull()?.let { draft ->
it.firstOrNull()?.firstOrNull().toOptional() DraftMapper.map(draft)
} }
} }
private fun deleteDraftFromDb(realm: Realm, roomId: String) { private fun deleteDraftFromDb(realm: MutableRealm, roomId: String) {
UserDraftsEntity.where(realm, roomId).findFirst()?.userDrafts?.clear() UserDraftsEntity.where(realm, roomId).first().find()?.userDrafts?.clearWith {
realm.delete(it)
}
} }
private fun saveDraftInDb(realm: Realm, draft: UserDraft, roomId: String) { private fun saveDraftInDb(realm: MutableRealm, draft: UserDraft, roomId: String) {
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).first().find()
?: realm.createObject(roomId) ?: realm.copyToRealm(
RoomSummaryEntity().apply {
this.roomId = roomId
}
)
val userDraftsEntity = roomSummaryEntity.userDrafts val userDraftsEntity = roomSummaryEntity.userDrafts
?: realm.createObject<UserDraftsEntity>().also { ?: realm.copyToRealm(UserDraftsEntity()).also {
roomSummaryEntity.userDrafts = it roomSummaryEntity.userDrafts = it
} }

View File

@ -17,8 +17,7 @@
package org.matrix.android.sdk.internal.session.room.location package org.matrix.android.sdk.internal.session.room.location
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.asLiveData
import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
@ -27,16 +26,17 @@ import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareR
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.query.findRunningLiveInRoom import org.matrix.android.sdk.internal.database.query.findRunningLiveInRoom
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.util.mapOptional
internal class DefaultLocationSharingService @AssistedInject constructor( internal class DefaultLocationSharingService @AssistedInject constructor(
@Assisted private val roomId: String, @Assisted private val roomId: String,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val sendStaticLocationTask: SendStaticLocationTask, private val sendStaticLocationTask: SendStaticLocationTask,
private val sendLiveLocationTask: SendLiveLocationTask, private val sendLiveLocationTask: SendLiveLocationTask,
private val startLiveLocationShareTask: StartLiveLocationShareTask, private val startLiveLocationShareTask: StartLiveLocationShareTask,
@ -113,20 +113,16 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
} }
override fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>> { override fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>> {
return monarchy.findAllMappedWithChanges( return realmInstance.queryList(liveLocationShareAggregatedSummaryMapper) {
{ LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) }, LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId)
liveLocationShareAggregatedSummaryMapper }.asLiveData()
)
} }
override fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>> { override fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>> {
return Transformations.map( return realmInstance.queryFirst {
monarchy.findAllMappedWithChanges( LiveLocationShareAggregatedSummaryEntity.where(it, roomId = roomId, eventId = beaconInfoEventId).first()
{ LiveLocationShareAggregatedSummaryEntity.where(it, roomId = roomId, eventId = beaconInfoEventId) },
liveLocationShareAggregatedSummaryMapper
)
) {
it.firstOrNull().toOptional()
} }
.mapOptional(liveLocationShareAggregatedSummaryMapper::map)
.asLiveData()
} }
} }

View File

@ -16,19 +16,17 @@
package org.matrix.android.sdk.internal.session.room.membership package org.matrix.android.sdk.internal.session.room.membership
import com.zhuinden.monarchy.Monarchy
import io.realm.kotlin.createObject
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.crypto.DeviceListManager import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
@ -41,7 +39,6 @@ import org.matrix.android.sdk.internal.session.room.RoomDataSource
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -57,7 +54,7 @@ internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Unit>
internal class DefaultLoadRoomMembersTask @Inject constructor( internal class DefaultLoadRoomMembersTask @Inject constructor(
private val roomAPI: RoomAPI, private val roomAPI: RoomAPI,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val roomDataSource: RoomDataSource, private val roomDataSource: RoomDataSource,
private val syncTokenStore: SyncTokenStore, private val syncTokenStore: SyncTokenStore,
private val roomSummaryUpdater: RoomSummaryUpdater, private val roomSummaryUpdater: RoomSummaryUpdater,
@ -78,10 +75,9 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
private suspend fun waitPreviousRequestToFinish(params: LoadRoomMembersTask.Params) { private suspend fun waitPreviousRequestToFinish(params: LoadRoomMembersTask.Params) {
try { try {
awaitNotEmptyResult(monarchy.realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> awaitNotEmptyResult(realmInstance, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(RoomEntity::class.java) RoomEntity.where(realm, roomId = params.roomId)
.equalTo(RoomEntityFields.ROOM_ID, params.roomId) .query("membersLoadStatusStr == $0", RoomMembersLoadStatusType.LOADED.name)
.equalTo(RoomEntityFields.MEMBERS_LOAD_STATUS_STR, RoomMembersLoadStatusType.LOADED.name)
} }
} catch (exception: TimeoutCancellationException) { } catch (exception: TimeoutCancellationException) {
// Timeout, do the request anyway (?) // Timeout, do the request anyway (?)
@ -109,7 +105,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
private suspend fun insertInDb(response: RoomMembersResponse, roomId: String) { private suspend fun insertInDb(response: RoomMembersResponse, roomId: String) {
val chunks = response.roomMemberEvents.chunked(500) val chunks = response.roomMemberEvents.chunked(500)
chunks.forEach { roomMemberEvents -> chunks.forEach { roomMemberEvents ->
monarchy.awaitTransaction { realm -> realmInstance.write {
Timber.v("Insert ${roomMemberEvents.size} member events in room $roomId") Timber.v("Insert ${roomMemberEvents.size} member events in room $roomId")
// We ignore all the already known members // We ignore all the already known members
val now = clock.epochMillis() val now = clock.epochMillis()
@ -118,9 +114,9 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
continue continue
} }
val ageLocalTs = now - (roomMemberEvent.unsignedData?.age ?: 0) val ageLocalTs = now - (roomMemberEvent.unsignedData?.age ?: 0)
val eventEntity = roomMemberEvent.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) val eventEntity = roomMemberEvent.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(this, EventInsertType.PAGINATION)
CurrentStateEventEntity.getOrCreate( CurrentStateEventEntity.getOrCreate(
realm, this,
roomId, roomId,
roomMemberEvent.stateKey, roomMemberEvent.stateKey,
roomMemberEvent.type roomMemberEvent.type
@ -128,15 +124,14 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
eventId = roomMemberEvent.eventId eventId = roomMemberEvent.eventId
root = eventEntity root = eventEntity
} }
roomMemberEventHandler.handle(realm, roomId, roomMemberEvent, false) roomMemberEventHandler.handle(this, roomId, roomMemberEvent, false)
} }
} }
} }
monarchy.awaitTransaction { realm -> realmInstance.write {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() val roomEntity = RoomEntity.getOrCreate(this, roomId)
?: realm.createObject(roomId)
roomEntity.membersLoadStatus = RoomMembersLoadStatusType.LOADED roomEntity.membersLoadStatus = RoomMembersLoadStatusType.LOADED
roomSummaryUpdater.update(realm, roomId, updateMembers = true) roomSummaryUpdater.update(this, roomId, updateMembers = true)
} }
if (cryptoSessionInfoProvider.isRoomEncrypted(roomId)) { if (cryptoSessionInfoProvider.isRoomEncrypted(roomId)) {
deviceListManager.onRoomMembersLoadedFor(roomId) deviceListManager.onRoomMembersLoadedFor(roomId)
@ -144,8 +139,8 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
} }
private suspend fun setRoomMembersLoadStatus(roomId: String, status: RoomMembersLoadStatusType) { private suspend fun setRoomMembersLoadStatus(roomId: String, status: RoomMembersLoadStatusType) {
monarchy.awaitTransaction { realm -> realmInstance.write {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId) val roomEntity = RoomEntity.getOrCreate(this, roomId)
roomEntity.membersLoadStatus = status roomEntity.membersLoadStatus = status
} }
} }

View File

@ -17,22 +17,16 @@
package org.matrix.android.sdk.internal.session.room.notification package org.matrix.android.sdk.internal.session.room.notification
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import org.matrix.android.sdk.api.session.pushrules.RuleScope
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService
import org.matrix.android.sdk.internal.database.model.PushRuleEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
internal class DefaultRoomPushRuleService @AssistedInject constructor( internal class DefaultRoomPushRuleService @AssistedInject constructor(
@Assisted private val roomId: String, @Assisted private val roomId: String,
private val setRoomNotificationStateTask: SetRoomNotificationStateTask, private val setRoomNotificationStateTask: SetRoomNotificationStateTask,
@SessionDatabase private val monarchy: Monarchy private val roomPushRuleDataSource: RoomPushRuleDataSource,
) : ) :
RoomPushRuleService { RoomPushRuleService {
@ -42,26 +36,10 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(
} }
override fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> { override fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> {
return Transformations.map(getPushRuleForRoom()) { return roomPushRuleDataSource.getLiveRoomNotificationState(roomId)
it?.toRoomNotificationState() ?: RoomNotificationState.ALL_MESSAGES
}
} }
override suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) { override suspend fun setRoomNotificationState(roomNotificationState: RoomNotificationState) {
setRoomNotificationStateTask.execute(SetRoomNotificationStateTask.Params(roomId, roomNotificationState)) setRoomNotificationStateTask.execute(SetRoomNotificationStateTask.Params(roomId, roomNotificationState))
} }
private fun getPushRuleForRoom(): LiveData<RoomPushRule?> {
val liveData = monarchy.findAllMappedWithChanges(
{ realm ->
PushRuleEntity.where(realm, scope = RuleScope.GLOBAL, ruleId = roomId)
},
{ result ->
result.toRoomPushRule()
}
)
return Transformations.map(liveData) { results ->
results.firstOrNull()
}
}
} }

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.session.room.notification
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import io.realm.kotlin.TypedRealm
import io.realm.kotlin.query.RealmSingleQuery
import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.session.pushrules.RuleScope
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.model.PushRulesEntity
import org.matrix.android.sdk.internal.di.SessionDatabase
import javax.inject.Inject
internal class RoomPushRuleDataSource @Inject constructor(@SessionDatabase private val realmInstance: RealmInstance) {
suspend fun getCurrentRoomPushRule(roomId: String): RoomPushRule? {
val realm = realmInstance.getRealm()
return queryPushRulesEntity(realm, roomId)
.find()
?.toRoomPushRule(ruleId = roomId)
}
fun getLiveRoomNotificationState(roomId: String): LiveData<RoomNotificationState> {
return realmInstance.queryFirst {
queryPushRulesEntity(it, roomId)
}.map {
val pushRulesEntity = it.getOrNull()
pushRulesEntity
?.toRoomPushRule(ruleId = roomId)
?.toRoomNotificationState()
?: RoomNotificationState.ALL_MESSAGES
}.asLiveData()
}
private fun queryPushRulesEntity(realm: TypedRealm, roomId: String): RealmSingleQuery<PushRulesEntity> {
return realm.query(PushRulesEntity::class)
.query("scope == $0", RuleScope.GLOBAL)
.query("ANY pushRules.ruleId == $0", roomId)
.first()
}
}

View File

@ -25,20 +25,20 @@ import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.pushrules.toJson import org.matrix.android.sdk.api.session.pushrules.toJson
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
import org.matrix.android.sdk.internal.database.model.PushRuleEntity import org.matrix.android.sdk.internal.database.model.PushRulesEntity
internal fun PushRuleEntity.toRoomPushRule(): RoomPushRule? { internal fun PushRulesEntity.toRoomPushRule(ruleId: String): RoomPushRule? {
val kind = parent?.firstOrNull()?.kind val pushRuleEntity = pushRules.firstOrNull { it.ruleId == ruleId } ?: return null
val pushRule = when (kind) { val pushRule = when (kind) {
RuleSetKey.OVERRIDE -> { RuleSetKey.OVERRIDE -> {
PushRulesMapper.map(this) PushRulesMapper.map(pushRuleEntity)
} }
RuleSetKey.ROOM -> { RuleSetKey.ROOM -> {
PushRulesMapper.mapRoomRule(this) PushRulesMapper.mapRoomRule(pushRuleEntity)
} }
else -> null else -> null
} }
return if (pushRule == null || kind == null) { return if (pushRule == null) {
null null
} else { } else {
RoomPushRule(kind, pushRule) RoomPushRule(kind, pushRule)

View File

@ -16,13 +16,7 @@
package org.matrix.android.sdk.internal.session.room.notification package org.matrix.android.sdk.internal.session.room.notification
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import org.matrix.android.sdk.api.session.pushrules.RuleScope
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
import org.matrix.android.sdk.internal.database.model.PushRuleEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.pushers.AddPushRuleTask import org.matrix.android.sdk.internal.session.pushers.AddPushRuleTask
import org.matrix.android.sdk.internal.session.pushers.RemovePushRuleTask import org.matrix.android.sdk.internal.session.pushers.RemovePushRuleTask
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
@ -36,16 +30,14 @@ internal interface SetRoomNotificationStateTask : Task<SetRoomNotificationStateT
} }
internal class DefaultSetRoomNotificationStateTask @Inject constructor( internal class DefaultSetRoomNotificationStateTask @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, private val roomPushRuleDataSource: RoomPushRuleDataSource,
private val removePushRuleTask: RemovePushRuleTask, private val removePushRuleTask: RemovePushRuleTask,
private val addPushRuleTask: AddPushRuleTask private val addPushRuleTask: AddPushRuleTask
) : ) :
SetRoomNotificationStateTask { SetRoomNotificationStateTask {
override suspend fun execute(params: SetRoomNotificationStateTask.Params) { override suspend fun execute(params: SetRoomNotificationStateTask.Params) {
val currentRoomPushRule = Realm.getInstance(monarchy.realmConfiguration).use { val currentRoomPushRule = roomPushRuleDataSource.getCurrentRoomPushRule(params.roomId)
PushRuleEntity.where(it, scope = RuleScope.GLOBAL, ruleId = params.roomId).findFirst()?.toRoomPushRule()
}
if (currentRoomPushRule != null) { if (currentRoomPushRule != null) {
removePushRuleTask.execute(RemovePushRuleTask.Params(currentRoomPushRule.kind, currentRoomPushRule.rule.ruleId)) removePushRuleTask.execute(RemovePushRuleTask.Params(currentRoomPushRule.kind, currentRoomPushRule.rule.ruleId))
} }

View File

@ -17,15 +17,15 @@
package org.matrix.android.sdk.internal.session.room.read package org.matrix.android.sdk.internal.session.room.read
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.asLiveData
import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.api.session.room.read.ReadService import org.matrix.android.sdk.api.session.room.read.ReadService
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper
import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
@ -34,10 +34,11 @@ import org.matrix.android.sdk.internal.database.query.isEventRead
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.util.mapOptional
internal class DefaultReadService @AssistedInject constructor( internal class DefaultReadService @AssistedInject constructor(
@Assisted private val roomId: String, @Assisted private val roomId: String,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val setReadMarkersTask: SetReadMarkersTask, private val setReadMarkersTask: SetReadMarkersTask,
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper, private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
@UserId private val userId: String @UserId private val userId: String
@ -68,47 +69,40 @@ internal class DefaultReadService @AssistedInject constructor(
} }
override fun isEventRead(eventId: String): Boolean { override fun isEventRead(eventId: String): Boolean {
return isEventRead(monarchy.realmConfiguration, userId, roomId, eventId) val realm = realmInstance.getBlockingRealm()
return isEventRead(realm, userId, roomId, eventId)
} }
override fun getReadMarkerLive(): LiveData<Optional<String>> { override fun getReadMarkerLive(): LiveData<Optional<String>> {
val liveRealmData = monarchy.findAllMappedWithChanges( return realmInstance.queryFirst {
{ ReadMarkerEntity.where(it, roomId) }, ReadMarkerEntity.where(it, roomId).first()
{ it.eventId }
)
return Transformations.map(liveRealmData) {
it.firstOrNull().toOptional()
} }
.mapOptional { it.eventId }
.asLiveData()
} }
override fun getMyReadReceiptLive(): LiveData<Optional<String>> { override fun getMyReadReceiptLive(): LiveData<Optional<String>> {
val liveRealmData = monarchy.findAllMappedWithChanges( return realmInstance.queryFirst {
{ ReadReceiptEntity.where(it, roomId = roomId, userId = userId) }, ReadReceiptEntity.where(it, roomId = roomId, userId = userId).first()
{ it.eventId }
)
return Transformations.map(liveRealmData) {
it.firstOrNull().toOptional()
} }
.mapOptional { it.eventId }
.asLiveData()
} }
override fun getUserReadReceipt(userId: String): String? { override fun getUserReadReceipt(userId: String): String? {
var eventId: String? = null val realm = realmInstance.getBlockingRealm()
monarchy.doWithRealm { return ReadReceiptEntity.where(realm, roomId = roomId, userId = userId)
eventId = ReadReceiptEntity.where(it, roomId = roomId, userId = userId) .first()
.findFirst() .find()
?.eventId ?.eventId
}
return eventId
} }
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> { override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
val liveRealmData = monarchy.findAllMappedWithChanges( return realmInstance.queryFirst {
{ ReadReceiptsSummaryEntity.where(it, eventId) }, ReadReceiptsSummaryEntity.where(it, eventId)
{ readReceiptsSummaryMapper.map(it) } }.map {
) readReceiptsSummaryMapper.map(it.getOrNull())
return Transformations.map(liveRealmData) { }.asLiveData()
it.firstOrNull().orEmpty()
}
} }
private fun ReadService.MarkAsReadParams.forceReadMarker(): Boolean { private fun ReadService.MarkAsReadParams.forceReadMarker(): Boolean {

View File

@ -15,10 +15,10 @@
*/ */
package org.matrix.android.sdk.internal.session.room.relation.threads package org.matrix.android.sdk.internal.session.room.relation.threads
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummaryUpdateType import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummaryUpdateType
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.helper.createOrUpdate import org.matrix.android.sdk.internal.database.helper.createOrUpdate
import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
@ -32,7 +32,6 @@ import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -53,7 +52,7 @@ internal interface FetchThreadSummariesTask : Task<FetchThreadSummariesTask.Para
internal class DefaultFetchThreadSummariesTask @Inject constructor( internal class DefaultFetchThreadSummariesTask @Inject constructor(
private val roomAPI: RoomAPI, private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver, private val globalErrorReceiver: GlobalErrorReceiver,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val cryptoService: DefaultCryptoService, private val cryptoService: DefaultCryptoService,
@UserId private val userId: String, @UserId private val userId: String,
private val clock: Clock, private val clock: Clock,
@ -82,8 +81,8 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
params: FetchThreadSummariesTask.Params params: FetchThreadSummariesTask.Params
): Result { ): Result {
val rootThreadList = response.events val rootThreadList = response.events
monarchy.awaitTransaction { realm -> realmInstance.write {
val roomEntity = RoomEntity.where(realm, roomId = params.roomId).findFirst() ?: return@awaitTransaction val roomEntity = RoomEntity.where(this, roomId = params.roomId).first().find() ?: return@write
val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>() val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>()
for (rootThreadEvent in rootThreadList) { for (rootThreadEvent in rootThreadList) {
@ -93,7 +92,7 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
ThreadSummaryEntity.createOrUpdate( ThreadSummaryEntity.createOrUpdate(
threadSummaryType = ThreadSummaryUpdateType.REPLACE, threadSummaryType = ThreadSummaryUpdateType.REPLACE,
realm = realm, realm = this,
roomId = params.roomId, roomId = params.roomId,
rootThreadEvent = rootThreadEvent, rootThreadEvent = rootThreadEvent,
roomMemberContentsByUser = roomMemberContentsByUser, roomMemberContentsByUser = roomMemberContentsByUser,

View File

@ -16,11 +16,8 @@
package org.matrix.android.sdk.internal.session.room.summary package org.matrix.android.sdk.internal.session.room.summary
import io.realm.Realm
import io.realm.kotlin.MutableRealm import io.realm.kotlin.MutableRealm
import io.realm.kotlin.TypedRealm import io.realm.kotlin.TypedRealm
import io.realm.kotlin.createObject
import io.realm.kotlin.deleteFromRealm
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
@ -48,7 +45,6 @@ import org.matrix.android.sdk.internal.database.clearWith
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntity import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntity
@ -60,8 +56,6 @@ import org.matrix.android.sdk.internal.database.query.getOrNull
import org.matrix.android.sdk.internal.database.query.isEventRead import org.matrix.android.sdk.internal.database.query.isEventRead
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.clearWith
import org.matrix.android.sdk.internal.extensions.realm
import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.query.process
import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataDataSource import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataDataSource
@ -372,17 +366,14 @@ internal class RoomSummaryUpdater @Inject constructor(
val parent = RoomSummaryEntity.where(realm, entry.key.roomId).first().find() val parent = RoomSummaryEntity.where(realm, entry.key.roomId).first().find()
if (parent != null) { if (parent != null) {
val flattenParentsIds = (flattenSpaceParents[parent.roomId] ?: emptyList()) + listOf(parent.roomId) val flattenParentsIds = (flattenSpaceParents[parent.roomId] ?: emptyList()) + listOf(parent.roomId)
entry.value.forEach { child -> entry.value.forEach { child ->
RoomSummaryEntity.where(realm, child.roomId).first().find()?.let { childSum -> RoomSummaryEntity.where(realm, child.roomId).first().find()?.let { childSum ->
childSum.directParentNames.add(parent.displayName()) parent.displayName()?.also { directParentName ->
childSum.directParentNames.add(directParentName)
if (childSum.flattenParentIds == null) {
childSum.flattenParentIds = ""
} }
flattenParentsIds.forEach { flattenParentsIds.forEach {
if (childSum.flattenParentIds?.contains(it) != true) { if (!childSum.flattenParentIds.contains(it)) {
childSum.flattenParentIds += "|$it" childSum.flattenParentIds.add(it)
} }
} }
} }
@ -400,7 +391,7 @@ internal class RoomSummaryUpdater @Inject constructor(
val relatedSpaces = lookupMap.keys val relatedSpaces = lookupMap.keys
.filter { it.roomType == RoomType.SPACE } .filter { it.roomType == RoomType.SPACE }
.filter { .filter {
dmRoom.otherMemberIds.toList().intersect(it.otherMemberIds.toList()).isNotEmpty() dmRoom.otherMemberIds.toSet().intersect(it.otherMemberIds.toSet()).isNotEmpty()
} }
.map { it.roomId } .map { it.roomId }
.distinct() .distinct()
@ -412,7 +403,7 @@ internal class RoomSummaryUpdater @Inject constructor(
}.distinct() }.distinct()
if (flattenRelated.isNotEmpty()) { if (flattenRelated.isNotEmpty()) {
// we keep real m.child/m.parent relations and add the one for common memberships // we keep real m.child/m.parent relations and add the one for common memberships
dmRoom.flattenParentIds += "|${flattenRelated.joinToString("|")}|" dmRoom.flattenParentIds += flattenRelated
} }
// Timber.v("## SPACES: flatten of ${dmRoom.otherMemberIds.joinToString(",")} is ${dmRoom.flattenParentIds}") // Timber.v("## SPACES: flatten of ${dmRoom.otherMemberIds.joinToString(",")} is ${dmRoom.flattenParentIds}")
} }
@ -429,7 +420,7 @@ internal class RoomSummaryUpdater @Inject constructor(
realm.query(RoomSummaryEntity::class) realm.query(RoomSummaryEntity::class)
.process(RoomSummaryEntityFields.MEMBERSHIP_STR, listOf(Membership.JOIN)) .process(RoomSummaryEntityFields.MEMBERSHIP_STR, listOf(Membership.JOIN))
.query("roomType != $0", RoomType.SPACE) .query("roomType != $0", RoomType.SPACE)
.query("flattenParentIds CONTAINS $0", space.roomId) .query("ANY flattenParentIds == $0", space.roomId)
.find().forEach { .find().forEach {
highlightCount += it.highlightCount highlightCount += it.highlightCount
notificationCount += it.notificationCount notificationCount += it.notificationCount

View File

@ -17,30 +17,26 @@
package org.matrix.android.sdk.internal.session.room.threads package org.matrix.android.sdk.internal.session.room.threads
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy import androidx.lifecycle.asLiveData
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.realm.Realm
import org.matrix.android.sdk.api.session.room.threads.ThreadsService import org.matrix.android.sdk.api.session.room.threads.ThreadsService
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.helper.enhanceWithEditions import org.matrix.android.sdk.internal.database.helper.enhanceWithEditions
import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId import org.matrix.android.sdk.internal.database.helper.findAllThreadsForRoomId
import org.matrix.android.sdk.internal.database.mapper.ThreadSummaryMapper import org.matrix.android.sdk.internal.database.mapper.ThreadSummaryMapper
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
internal class DefaultThreadsService @AssistedInject constructor( internal class DefaultThreadsService @AssistedInject constructor(
@Assisted private val roomId: String, @Assisted private val roomId: String,
@UserId private val userId: String,
private val fetchThreadTimelineTask: FetchThreadTimelineTask, private val fetchThreadTimelineTask: FetchThreadTimelineTask,
private val fetchThreadSummariesTask: FetchThreadSummariesTask, private val fetchThreadSummariesTask: FetchThreadSummariesTask,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val timelineEventMapper: TimelineEventMapper,
private val threadSummaryMapper: ThreadSummaryMapper private val threadSummaryMapper: ThreadSummaryMapper
) : ThreadsService { ) : ThreadsService {
@ -50,25 +46,22 @@ internal class DefaultThreadsService @AssistedInject constructor(
} }
override fun getAllThreadSummariesLive(): LiveData<List<ThreadSummary>> { override fun getAllThreadSummariesLive(): LiveData<List<ThreadSummary>> {
return monarchy.findAllMappedWithChanges( return realmInstance.queryList({ threadSummaryMapper.map(roomId, it) }) {
{ ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId) }, ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId)
{ }.asLiveData()
threadSummaryMapper.map(roomId, it)
}
)
} }
override fun getAllThreadSummaries(): List<ThreadSummary> { override fun getAllThreadSummaries(): List<ThreadSummary> {
return monarchy.fetchAllMappedSync( val realm = realmInstance.getBlockingRealm()
{ ThreadSummaryEntity.findAllThreadsForRoomId(it, roomId = roomId) }, return ThreadSummaryEntity.findAllThreadsForRoomId(realm, roomId = roomId).find()
{ threadSummaryMapper.map(roomId, it) } .map {
) threadSummaryMapper.map(roomId, it)
}
} }
override fun enhanceThreadWithEditions(threads: List<ThreadSummary>): List<ThreadSummary> { override fun enhanceThreadWithEditions(threads: List<ThreadSummary>): List<ThreadSummary> {
return Realm.getInstance(monarchy.realmConfiguration).use { val realm = realmInstance.getBlockingRealm()
threads.enhanceWithEditions(it, roomId) return threads.enhanceWithEditions(realm, roomId)
}
} }
override suspend fun fetchThreadTimeline(rootThreadEventId: String, from: String, limit: Int) { override suspend fun fetchThreadTimeline(rootThreadEventId: String, from: String, limit: Int) {

View File

@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.session.sync.handler.room package org.matrix.android.sdk.internal.session.sync.handler.room
import io.realm.Realm import io.realm.kotlin.MutableRealm
import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
@ -26,7 +26,7 @@ import javax.inject.Inject
internal class RoomFullyReadHandler @Inject constructor() { internal class RoomFullyReadHandler @Inject constructor() {
fun handle(realm: Realm, roomId: String, content: FullyReadContent?) { fun handle(realm: MutableRealm, roomId: String, content: FullyReadContent?) {
if (content == null) { if (content == null) {
return return
} }

View File

@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.sync.handler.room
import dagger.Lazy import dagger.Lazy
import io.realm.kotlin.MutableRealm import io.realm.kotlin.MutableRealm
import io.realm.kotlin.createObject
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
@ -40,6 +39,7 @@ import org.matrix.android.sdk.api.session.sync.model.RoomSync
import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.database.clearWith
import org.matrix.android.sdk.internal.database.helper.addIfNecessary import org.matrix.android.sdk.internal.database.helper.addIfNecessary
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
import org.matrix.android.sdk.internal.database.helper.createOrUpdate import org.matrix.android.sdk.internal.database.helper.createOrUpdate
@ -65,7 +65,6 @@ import org.matrix.android.sdk.internal.database.query.getOrNull
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.clearWith
import org.matrix.android.sdk.internal.session.StreamEventsManager import org.matrix.android.sdk.internal.session.StreamEventsManager
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
@ -233,7 +232,7 @@ internal class RoomSyncHandler @Inject constructor(
val roomEntity = RoomEntity.getOrCreate(realm, roomId) val roomEntity = RoomEntity.getOrCreate(realm, roomId)
if (roomEntity.membership == Membership.INVITE) { if (roomEntity.membership == Membership.INVITE) {
roomEntity.chunks.deleteAllFromRealm() realm.delete(roomEntity.chunks)
} }
roomEntity.membership = Membership.JOIN roomEntity.membership = Membership.JOIN
@ -359,10 +358,10 @@ internal class RoomSyncHandler @Inject constructor(
} }
} }
} }
val leftMember = RoomMemberSummaryEntity.where(realm, roomId, userId).findFirst() val leftMember = RoomMemberSummaryEntity.where(realm, roomId, userId).first().find()
val membership = leftMember?.membership ?: Membership.LEAVE val membership = leftMember?.membership ?: Membership.LEAVE
roomEntity.membership = membership roomEntity.membership = membership
roomEntity.chunks.clearWith { it.deleteOnCascade(deleteStateEvents = true, canDeleteRoot = true) } roomEntity.chunks.clearWith { realm.deleteOnCascade(it, deleteStateEvents = true, canDeleteRoot = true) }
roomTypingUsersHandler.handle(realm, roomId, null) roomTypingUsersHandler.handle(realm, roomId, null)
roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE) roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE)
roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications) roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications)
@ -386,12 +385,14 @@ internal class RoomSyncHandler @Inject constructor(
} else { } else {
// Delete all chunks of the room in case of gap. // Delete all chunks of the room in case of gap.
ChunkEntity.findAll(realm, roomId).forEach { ChunkEntity.findAll(realm, roomId).forEach {
it.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = true) realm.deleteOnCascade(it, deleteStateEvents = false, canDeleteRoot = true)
}
realm.createObject<ChunkEntity>().apply {
this.prevToken = prevToken
this.isLastForward = true
} }
realm.copyToRealm(
ChunkEntity().apply {
this.prevToken = prevToken
this.isLastForward = true
}
)
} }
val eventIds = ArrayList<String>(eventList.size) val eventIds = ArrayList<String>(eventList.size)
val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>() val roomMemberContentsByUser = HashMap<String, RoomMemberContent?>()
@ -444,6 +445,7 @@ internal class RoomSyncHandler @Inject constructor(
} }
val timelineEventAdded = chunkEntity.addTimelineEvent( val timelineEventAdded = chunkEntity.addTimelineEvent(
realm = realm,
roomId = roomId, roomId = roomId,
eventEntity = eventEntity, eventEntity = eventEntity,
direction = PaginationDirection.FORWARDS, direction = PaginationDirection.FORWARDS,
@ -491,14 +493,14 @@ internal class RoomSyncHandler @Inject constructor(
} }
} }
// Finally delete the local echo // Finally delete the local echo
sendingEventEntity.deleteOnCascade(true) realm.deleteOnCascade(sendingEventEntity, true)
} else { } else {
Timber.v("Can't find corresponding local echo for tx:$it") Timber.v("Can't find corresponding local echo for tx:$it")
} }
} }
} }
// Handle deletion of [stuck] local echos if needed // Handle deletion of [stuck] local echos if needed
deleteLocalEchosIfNeeded(insertType, roomEntity, eventList) deleteLocalEchosIfNeeded(realm, insertType, roomEntity, eventList)
if (lightweightSettingsStorage.areThreadMessagesEnabled()) { if (lightweightSettingsStorage.areThreadMessagesEnabled()) {
optimizedThreadSummaryMap.updateThreadSummaryIfNeeded( optimizedThreadSummaryMap.updateThreadSummaryIfNeeded(
roomId = roomId, roomId = roomId,
@ -625,7 +627,7 @@ internal class RoomSyncHandler @Inject constructor(
* While we cannot know when a specific event arrived from the pagination (no transactionId included), after each room /sync * While we cannot know when a specific event arrived from the pagination (no transactionId included), after each room /sync
* we clear all SENT events, and we are sure that we will receive it from /sync or pagination * we clear all SENT events, and we are sure that we will receive it from /sync or pagination
*/ */
private fun deleteLocalEchosIfNeeded(insertType: EventInsertType, roomEntity: RoomEntity, eventList: List<Event>) { private fun deleteLocalEchosIfNeeded(realm: MutableRealm, insertType: EventInsertType, roomEntity: RoomEntity, eventList: List<Event>) {
// Skip deletion if we are on initial sync // Skip deletion if we are on initial sync
if (insertType == EventInsertType.INITIAL_SYNC) return if (insertType == EventInsertType.INITIAL_SYNC) return
// Skip deletion if there are no timeline events or there is no event received from the current user // Skip deletion if there are no timeline events or there is no event received from the current user
@ -634,7 +636,7 @@ internal class RoomSyncHandler @Inject constructor(
timelineEvent.root?.sendState == SendState.SENT timelineEvent.root?.sendState == SendState.SENT
}.forEach { }.forEach {
roomEntity.sendingTimelineEvents.remove(it) roomEntity.sendingTimelineEvents.remove(it)
it.deleteOnCascade(true) realm.deleteOnCascade(it, true)
} }
} }
} }

View File

@ -16,21 +16,19 @@
package org.matrix.android.sdk.internal.session.sync.handler.room package org.matrix.android.sdk.internal.session.sync.handler.room
import io.realm.Realm import io.realm.kotlin.MutableRealm
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomTagEntity
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
import javax.inject.Inject import javax.inject.Inject
internal class RoomTagHandler @Inject constructor() { internal class RoomTagHandler @Inject constructor() {
fun handle(realm: Realm, roomId: String, content: RoomTagContent?) { fun handle(realm: MutableRealm, roomId: String, content: RoomTagContent?) {
if (content == null) { if (content == null) {
return return
} }
val tags = content.tags.entries.map { (tagName, params) -> val tags = content.tags.entries.map { (tagName, params) ->
RoomTagEntity(tagName, params["order"] as? Double)
Pair(tagName, params["order"] as? Double) Pair(tagName, params["order"] as? Double)
} }
RoomSummaryEntity.getOrCreate(realm, roomId).updateTags(tags) RoomSummaryEntity.getOrCreate(realm, roomId).updateTags(tags)

View File

@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.session.sync.handler.room package org.matrix.android.sdk.internal.session.sync.handler.room
import io.realm.Realm import io.realm.kotlin.MutableRealm
import org.matrix.android.sdk.api.session.room.sender.SenderInfo import org.matrix.android.sdk.api.session.room.sender.SenderInfo
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
@ -29,7 +29,7 @@ internal class RoomTypingUsersHandler @Inject constructor(
) { ) {
// TODO This could be handled outside of the Realm transaction. Use the new aggregator? // TODO This could be handled outside of the Realm transaction. Use the new aggregator?
fun handle(realm: Realm, roomId: String, ephemeralResult: RoomSyncHandler.EphemeralResult?) { fun handle(realm: MutableRealm, roomId: String, ephemeralResult: RoomSyncHandler.EphemeralResult?) {
val roomMemberHelper = RoomMemberHelper(realm, roomId) val roomMemberHelper = RoomMemberHelper(realm, roomId)
val typingIds = ephemeralResult?.typingUserIds?.filter { it != userId }.orEmpty() val typingIds = ephemeralResult?.typingUserIds?.filter { it != userId }.orEmpty()
val senderInfo = typingIds.map { userId -> val senderInfo = typingIds.map { userId ->

View File

@ -16,8 +16,8 @@
package org.matrix.android.sdk.internal.session.sync.handler.room package org.matrix.android.sdk.internal.session.sync.handler.room
import com.zhuinden.monarchy.Monarchy import io.realm.kotlin.MutableRealm
import io.realm.Realm import io.realm.kotlin.TypedRealm
import io.realm.kotlin.where import io.realm.kotlin.where
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.api.session.sync.model.SyncResponse
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.database.RealmInstance
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.EventMapper import org.matrix.android.sdk.internal.database.mapper.EventMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
@ -63,7 +64,7 @@ import javax.inject.Inject
*/ */
internal class ThreadsAwarenessHandler @Inject constructor( internal class ThreadsAwarenessHandler @Inject constructor(
private val permalinkFactory: PermalinkFactory, private val permalinkFactory: PermalinkFactory,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val realmInstance: RealmInstance,
private val lightweightSettingsStorage: LightweightSettingsStorage, private val lightweightSettingsStorage: LightweightSettingsStorage,
private val getEventTask: GetEventTask, private val getEventTask: GetEventTask,
private val clock: Clock, private val clock: Clock,
@ -98,21 +99,20 @@ internal class ThreadsAwarenessHandler @Inject constructor(
* @param eventList a list with the events to examine * @param eventList a list with the events to examine
*/ */
suspend fun fetchRootThreadEventsIfNeeded(eventList: List<Event>) { suspend fun fetchRootThreadEventsIfNeeded(eventList: List<Event>) {
if (eventList.isNullOrEmpty()) return if (eventList.isEmpty()) return
val threadsToFetch = emptyMap<String, String>().toMutableMap() val threadsToFetch = emptyMap<String, String>().toMutableMap()
Realm.getInstance(monarchy.realmConfiguration).use { realm -> val realm = realmInstance.getRealm()
eventList.asSequence() eventList.asSequence()
.filter { .filter {
isThreadEvent(it) && it.roomId != null isThreadEvent(it) && it.roomId != null
}.mapNotNull { event -> }.mapNotNull { event ->
getRootThreadEventId(event)?.let { getRootThreadEventId(event)?.let {
Pair(it, event.roomId!!) Pair(it, event.roomId!!)
}
}.forEach { (rootThreadEventId, roomId) ->
EventEntity.where(realm, rootThreadEventId).findFirst() ?: run { threadsToFetch[rootThreadEventId] = roomId }
} }
} }.forEach { (rootThreadEventId, roomId) ->
EventEntity.where(realm, rootThreadEventId).first().find() ?: run { threadsToFetch[rootThreadEventId] = roomId }
}
fetchThreadsEvents(threadsToFetch) fetchThreadsEvents(threadsToFetch)
} }
@ -126,12 +126,12 @@ internal class ThreadsAwarenessHandler @Inject constructor(
} }
} }
if (eventEntityList.isNullOrEmpty()) return if (eventEntityList.isEmpty()) return
// Transaction should be done on its own thread, like below // Transaction should be done on its own thread, like below
monarchy.awaitTransaction { realm -> realmInstance.write {
eventEntityList.forEach { eventEntityList.forEach {
it.copyToRealmOrIgnore(realm, EventInsertType.INCREMENTAL_SYNC) it.copyToRealmOrIgnore(this, EventInsertType.INCREMENTAL_SYNC)
} }
} }
} }
@ -159,7 +159,7 @@ internal class ThreadsAwarenessHandler @Inject constructor(
* @return The content to inject in the roomSyncHandler live events * @return The content to inject in the roomSyncHandler live events
*/ */
fun makeEventThreadAware( fun makeEventThreadAware(
realm: Realm, realm: MutableRealm,
roomId: String?, roomId: String?,
event: Event?, event: Event?,
eventEntity: EventEntity? = null eventEntity: EventEntity? = null
@ -217,7 +217,7 @@ internal class ThreadsAwarenessHandler @Inject constructor(
* @return The content to inject in the roomSyncHandler live events * @return The content to inject in the roomSyncHandler live events
*/ */
private fun handleRootThreadEventsIfNeeded( private fun handleRootThreadEventsIfNeeded(
realm: Realm, realm: MutableRealm,
roomId: String, roomId: String,
eventEntity: EventEntity?, eventEntity: EventEntity?,
event: Event event: Event
@ -244,7 +244,7 @@ internal class ThreadsAwarenessHandler @Inject constructor(
* @return The content to inject in the roomSyncHandler live events * @return The content to inject in the roomSyncHandler live events
*/ */
private fun handleEventsThatRelatesTo( private fun handleEventsThatRelatesTo(
realm: Realm, realm: MutableRealm,
roomId: String, roomId: String,
event: Event, event: Event,
eventBody: String, eventBody: String,
@ -364,7 +364,7 @@ internal class ThreadsAwarenessHandler @Inject constructor(
return updateEventEntity(event, eventEntity, eventPayload, messageTextContent) return updateEventEntity(event, eventEntity, eventPayload, messageTextContent)
} }
private fun eventThatRelatesTo(realm: Realm, currentEventId: String, rootThreadEventId: String): List<EventEntity>? { private fun eventThatRelatesTo(realm: TypedRealm, currentEventId: String, rootThreadEventId: String): List<EventEntity>? {
val threadList = realm.where<EventEntity>() val threadList = realm.where<EventEntity>()
.beginGroup() .beginGroup()
.equalTo(EventEntityFields.ROOT_THREAD_EVENT_ID, rootThreadEventId) .equalTo(EventEntityFields.ROOT_THREAD_EVENT_ID, rootThreadEventId)
@ -383,8 +383,8 @@ internal class ThreadsAwarenessHandler @Inject constructor(
* Try to get the event form the local DB, if the event does not exist null * Try to get the event form the local DB, if the event does not exist null
* will be returned. * will be returned.
*/ */
private fun getEventFromDB(realm: Realm, eventId: String): Event? { private fun getEventFromDB(realm: TypedRealm, eventId: String): Event? {
val eventEntity = EventEntity.where(realm, eventId = eventId).findFirst() ?: return null val eventEntity = EventEntity.where(realm, eventId = eventId).first().find() ?: return null
return EventMapper.map(eventEntity) return EventMapper.map(eventEntity)
} }

View File

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.session.sync.parsing package org.matrix.android.sdk.internal.session.sync.parsing
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.MutableRealm
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent
@ -37,7 +38,7 @@ internal class RoomSyncAccountDataHandler @Inject constructor(
private val roomFullyReadHandler: RoomFullyReadHandler private val roomFullyReadHandler: RoomFullyReadHandler
) { ) {
fun handle(realm: Realm, roomId: String, accountData: RoomSyncAccountData) { fun handle(realm: MutableRealm, roomId: String, accountData: RoomSyncAccountData) {
if (accountData.events.isNullOrEmpty()) { if (accountData.events.isNullOrEmpty()) {
return return
} }