TimelineEvent : update sender data when loading room members and prune event (+ remove RoomSummaryMapper param)

This commit is contained in:
ganfra 2019-07-08 15:32:24 +02:00
parent 7e6e09bc19
commit dd07f5c2a6
21 changed files with 165 additions and 109 deletions

View File

@ -25,8 +25,8 @@ import io.reactivex.schedulers.Schedulers


class RxRoom(private val room: Room) { class RxRoom(private val room: Room) {


fun liveRoomSummary(fetchLastEvent: Boolean): Observable<RoomSummary> { fun liveRoomSummary(): Observable<RoomSummary> {
return room.liveRoomSummary(fetchLastEvent).asObservable().observeOn(Schedulers.computation()) return room.liveRoomSummary().asObservable().observeOn(Schedulers.computation())
} }


fun liveRoomMemberIds(): Observable<List<String>> { fun liveRoomMemberIds(): Observable<List<String>> {

View File

@ -26,8 +26,8 @@ import io.reactivex.schedulers.Schedulers


class RxSession(private val session: Session) { class RxSession(private val session: Session) {


fun liveRoomSummaries(fetchLastEvents: Boolean): Observable<List<RoomSummary>> { fun liveRoomSummaries(): Observable<List<RoomSummary>> {
return session.liveRoomSummaries(fetchLastEvents).asObservable().observeOn(Schedulers.computation()) return session.liveRoomSummaries().asObservable().observeOn(Schedulers.computation())
} }


fun liveGroupSummaries(): Observable<List<GroupSummary>> { fun liveGroupSummaries(): Observable<List<GroupSummary>> {

View File

@ -47,8 +47,8 @@ interface Room :
* A live [RoomSummary] associated with the room * A live [RoomSummary] associated with the room
* You can observe this summary to get dynamic data from this room. * You can observe this summary to get dynamic data from this room.
*/ */
fun liveRoomSummary(fetchLastEvent: Boolean = false): LiveData<RoomSummary> fun liveRoomSummary(): LiveData<RoomSummary>


fun roomSummary(fetchLastEvent: Boolean = false): RoomSummary? fun roomSummary(): RoomSummary?


} }

View File

@ -43,6 +43,6 @@ interface RoomService {
* Get a live list of room summaries. This list is refreshed as soon as the data changes. * Get a live list of room summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of [RoomSummary] * @return the [LiveData] of [RoomSummary]
*/ */
fun liveRoomSummaries(fetchLastEvents: Boolean = true): LiveData<List<RoomSummary>> fun liveRoomSummaries(): LiveData<List<RoomSummary>>


} }

View File

@ -31,8 +31,7 @@ class RealmLiveData<T : RealmModel>(private val realmConfiguration: RealmConfigu


override fun onActive() { override fun onActive() {
val realm = Realm.getInstance(realmConfiguration) val realm = Realm.getInstance(realmConfiguration)
val results = query.invoke(realm).findAll() val results = query.invoke(realm).findAllAsync()
value = results
results.addChangeListener(listener) results.addChangeListener(listener)
this.realm = realm this.realm = realm
this.results = results this.results = results

View File

@ -18,28 +18,17 @@ package im.vector.matrix.android.internal.database.helper


import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.next
import im.vector.matrix.android.internal.database.query.prev
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
import io.realm.RealmList
import io.realm.RealmQuery
import io.realm.Sort import io.realm.Sort


// By default if a chunk is empty we consider it unlinked // By default if a chunk is empty we consider it unlinked
@ -76,10 +65,14 @@ internal fun ChunkEntity.merge(roomId: String,
eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING) eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
} }
val events = eventsToMerge.mapNotNull { it.root?.asDomain() } val events = eventsToMerge.mapNotNull { it.root?.asDomain() }
val eventIds = ArrayList<String>()
events.forEach { event -> events.forEach { event ->
add(roomId, event, direction, isUnlinked = isUnlinked) add(roomId, event, direction, isUnlinked = isUnlinked)
if (event.eventId != null) {
eventIds.add(event.eventId)
}
} }
updateSenderDataFor(roomId, isUnlinked, events) updateSenderDataFor(eventIds)
} }


internal fun ChunkEntity.addAll(roomId: String, internal fun ChunkEntity.addAll(roomId: String,
@ -89,30 +82,20 @@ internal fun ChunkEntity.addAll(roomId: String,
// Set to true for Event retrieved from a Permalink (i.e. not linked to live Chunk) // Set to true for Event retrieved from a Permalink (i.e. not linked to live Chunk)
isUnlinked: Boolean = false) { isUnlinked: Boolean = false) {
assertIsManaged() assertIsManaged()
val eventIds = ArrayList<String>()
events.forEach { event -> events.forEach { event ->
add(roomId, event, direction, stateIndexOffset, isUnlinked) add(roomId, event, direction, stateIndexOffset, isUnlinked)
if (event.eventId != null) {
eventIds.add(event.eventId)
}
} }
updateSenderDataFor(roomId, isUnlinked, events) updateSenderDataFor(eventIds)
} }


private fun ChunkEntity.updateSenderDataFor(roomId: String, isUnlinked: Boolean, events: List<Event>) { internal fun ChunkEntity.updateSenderDataFor(eventIds: List<String>) {
for (event in events) { for (eventId in eventIds) {
val eventId = event.eventId ?: continue
val timelineEventEntity = timelineEvents.find(eventId) ?: continue val timelineEventEntity = timelineEvents.find(eventId) ?: continue
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: continue timelineEventEntity.updateSenderData()
val stateIndex = timelineEventEntity.root?.stateIndex ?: continue
val senderId = timelineEventEntity.root?.sender ?: continue

val senderRoomMemberContent = when {
stateIndex <= 0 -> timelineEvents.build(senderId, isUnlinked).next(from = stateIndex)?.root?.prevContent
else -> timelineEvents.build(senderId, isUnlinked).prev(since = stateIndex)?.root?.content
}
val fallbackContent = senderRoomMemberContent
?: roomEntity.untimelinedStateEvents.build(senderId).prev(since = stateIndex)?.content
val senderRoomMember: RoomMember? = ContentMapper.map(fallbackContent).toModel()
timelineEventEntity.senderAvatar = senderRoomMember?.avatarUrl
timelineEventEntity.senderName = senderRoomMember?.displayName
timelineEventEntity.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(senderRoomMember?.displayName)
} }
} }


@ -161,19 +144,6 @@ private fun ChunkEntity.add(roomId: String,
timelineEvents.add(position, eventEntity) timelineEvents.add(position, eventEntity)
} }


private fun RealmList<TimelineEventEntity>.build(sender: String, isUnlinked: Boolean): RealmQuery<TimelineEventEntity> {
return where()
.equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, sender)
.equalTo(TimelineEventEntityFields.ROOT.TYPE, EventType.STATE_ROOM_MEMBER)
.equalTo(TimelineEventEntityFields.ROOT.IS_UNLINKED, isUnlinked)
}

private fun RealmList<EventEntity>.build(sender: String): RealmQuery<EventEntity> {
return where()
.equalTo(EventEntityFields.STATE_KEY, sender)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_MEMBER)
}

internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int { internal fun ChunkEntity.lastDisplayIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
return when (direction) { return when (direction) {
PaginationDirection.FORWARDS -> forwardsDisplayIndex PaginationDirection.FORWARDS -> forwardsDisplayIndex

View File

@ -72,6 +72,7 @@ internal fun RoomEntity.addSendingEvent(event: Event) {
it.senderName = myUser?.displayName it.senderName = myUser?.displayName
it.senderAvatar = myUser?.avatarUrl it.senderAvatar = myUser?.avatarUrl
it.isUniqueDisplayName = roomMembers.isUniqueDisplayName(myUser?.displayName) it.isUniqueDisplayName = roomMembers.isUniqueDisplayName(myUser?.displayName)
it.senderMembershipEvent = roomMembers.queryRoomMemberEvent(senderId).findFirst()
} }
sendingTimelineEvents.add(0, timelineEventEntity) sendingTimelineEvents.add(0, timelineEventEntity)
} }

View File

@ -0,0 +1,78 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.database.helper

import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
import im.vector.matrix.android.internal.database.query.next
import im.vector.matrix.android.internal.database.query.prev
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import io.realm.RealmList
import io.realm.RealmQuery

internal fun TimelineEventEntity.updateSenderData() {
assertIsManaged()
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return
val stateIndex = root?.stateIndex ?: return
val senderId = root?.sender ?: return
val chunkEntity = chunk?.firstOrNull() ?: return
val isUnlinked = chunkEntity.isUnlinked()
var senderMembershipEvent: EventEntity?
var senderRoomMemberContent: String?
when {
stateIndex <= 0 -> {
senderMembershipEvent = chunkEntity.timelineEvents.buildQuery(senderId, isUnlinked).next(from = stateIndex)?.root
senderRoomMemberContent = senderMembershipEvent?.prevContent
}
else -> {
senderMembershipEvent = chunkEntity.timelineEvents.buildQuery(senderId, isUnlinked).prev(since = stateIndex)?.root
senderRoomMemberContent = senderMembershipEvent?.content
}
}

// We fallback to untimelinedStateEvents if we can't find membership events in timeline
if (senderMembershipEvent == null) {
senderMembershipEvent = roomEntity.untimelinedStateEvents
.where()
.equalTo(EventEntityFields.STATE_KEY, senderId)
.equalTo(EventEntityFields.TYPE, EventType.STATE_ROOM_MEMBER)
.prev(since = stateIndex)
senderRoomMemberContent = senderMembershipEvent?.content
}
val senderRoomMember: RoomMember? = ContentMapper.map(senderRoomMemberContent).toModel()
this.senderAvatar = senderRoomMember?.avatarUrl
this.senderName = senderRoomMember?.displayName
this.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(senderRoomMember?.displayName)
this.senderMembershipEvent = senderMembershipEvent
}

private fun RealmList<TimelineEventEntity>.buildQuery(sender: String, isUnlinked: Boolean): RealmQuery<TimelineEventEntity> {
return where()
.equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, sender)
.equalTo(TimelineEventEntityFields.ROOT.TYPE, EventType.STATE_ROOM_MEMBER)
.equalTo(TimelineEventEntityFields.ROOT.IS_UNLINKED, isUnlinked)
}

View File

@ -29,7 +29,7 @@ internal class RoomSummaryMapper @Inject constructor(
val cryptoService: CryptoService val cryptoService: CryptoService
) { ) {


fun map(roomSummaryEntity: RoomSummaryEntity, getLatestEvent: Boolean = false): RoomSummary { fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
val tags = roomSummaryEntity.tags.map { val tags = roomSummaryEntity.tags.map {
RoomTag(it.tagName, it.tagOrder) RoomTag(it.tagName, it.tagOrder)
} }

View File

@ -35,7 +35,8 @@ internal open class TimelineEventEntity(@PrimaryKey var localId: String = UUID.r
var annotations: EventAnnotationsSummaryEntity? = null, var annotations: EventAnnotationsSummaryEntity? = null,
var senderName: String? = null, var senderName: String? = null,
var isUniqueDisplayName: Boolean = false, var isUniqueDisplayName: Boolean = false,
var senderAvatar: String? = null var senderAvatar: String? = null,
var senderMembershipEvent: EventEntity? = null
) : RealmObject() { ) : RealmObject() {


@LinkingObjects("timelineEvents") @LinkingObjects("timelineEvents")

View File

@ -54,6 +54,12 @@ internal fun TimelineEventEntity.Companion.where(realm: Realm,
} }
} }


internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm: Realm, senderMembershipEventId: String): List<TimelineEventEntity> {
return realm.where<TimelineEventEntity>()
.equalTo(TimelineEventEntityFields.SENDER_MEMBERSHIP_EVENT.EVENT_ID, senderMembershipEventId)
.findAll()
}



internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm, internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
roomId: String, roomId: String,

View File

@ -33,8 +33,6 @@ import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.util.fetchCopied
import im.vector.matrix.android.internal.util.fetchCopyMap
import javax.inject.Inject import javax.inject.Inject


internal class DefaultRoom @Inject constructor(override val roomId: String, internal class DefaultRoom @Inject constructor(override val roomId: String,
@ -55,12 +53,12 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
RelationService by relationService, RelationService by relationService,
MembershipService by roomMembersService { MembershipService by roomMembersService {


override fun liveRoomSummary(fetchLastEvent: Boolean): LiveData<RoomSummary> { override fun liveRoomSummary(): LiveData<RoomSummary> {
val liveRealmData = RealmLiveData<RoomSummaryEntity>(monarchy.realmConfiguration) { realm -> val liveRealmData = RealmLiveData<RoomSummaryEntity>(monarchy.realmConfiguration) { realm ->
RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
} }
return Transformations.map(liveRealmData) { results -> return Transformations.map(liveRealmData) { results ->
val roomSummaries = results.map { roomSummaryMapper.map(it, fetchLastEvent) } val roomSummaries = results.map { roomSummaryMapper.map(it) }


if (roomSummaries.isEmpty()) { if (roomSummaries.isEmpty()) {
// Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache // Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache
@ -71,10 +69,10 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
} }
} }


override fun roomSummary(fetchLastEvent: Boolean): RoomSummary? { override fun roomSummary(): RoomSummary? {
return monarchy.fetchAllMappedSync( return monarchy.fetchAllMappedSync(
{ realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) }, { realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
{ roomSummaryMapper.map(it, fetchLastEvent) } { roomSummaryMapper.map(it) }
).firstOrNull() ).firstOrNull()
} }



View File

@ -52,10 +52,10 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
return roomFactory.create(roomId) return roomFactory.create(roomId)
} }


override fun liveRoomSummaries(fetchLastEvents: Boolean): LiveData<List<RoomSummary>> { override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) }, { realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
{ roomSummaryMapper.map(it, fetchLastEvents) } { roomSummaryMapper.map(it) }
) )
} }
} }

View File

@ -20,15 +20,16 @@ import arrow.core.Try
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.internal.database.helper.addStateEvents import im.vector.matrix.android.internal.database.helper.addStateEvents
import im.vector.matrix.android.internal.database.helper.updateSenderData
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.sync.SyncTokenStore
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.tryTransactionSync import im.vector.matrix.android.internal.util.tryTransactionSync
import io.realm.Realm
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
import javax.inject.Inject import javax.inject.Inject


@ -41,9 +42,9 @@ internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Boolea
} }


internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAPI: RoomAPI, internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAPI: RoomAPI,
private val monarchy: Monarchy, private val monarchy: Monarchy,
private val syncTokenStore: SyncTokenStore, private val syncTokenStore: SyncTokenStore,
private val roomSummaryUpdater: RoomSummaryUpdater private val roomSummaryUpdater: RoomSummaryUpdater
) : LoadRoomMembersTask { ) : LoadRoomMembersTask {


override suspend fun execute(params: LoadRoomMembersTask.Params): Try<Boolean> { override suspend fun execute(params: LoadRoomMembersTask.Params): Try<Boolean> {
@ -68,20 +69,20 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP


val roomMembers = RoomMembers(realm, roomId).getLoaded() val roomMembers = RoomMembers(realm, roomId).getLoaded()
val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) } val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) }

roomEntity.addStateEvents(eventsToInsert) roomEntity.addStateEvents(eventsToInsert)
roomEntity.chunks.flatMap { it.timelineEvents }.forEach {
it.updateSenderData()
}
roomEntity.areAllMembersLoaded = true roomEntity.areAllMembersLoaded = true

roomSummaryUpdater.update(realm, roomId) roomSummaryUpdater.update(realm, roomId)
} }
.map { response } .map { response }
} }


private fun areAllMembersAlreadyLoaded(roomId: String): Boolean { private fun areAllMembersAlreadyLoaded(roomId: String): Boolean {
return monarchy return Realm.getInstance(monarchy.realmConfiguration).use {
.fetchAllCopiedSync { RoomEntity.where(it, roomId) } RoomEntity.where(it, roomId).findFirst()?.areAllMembersLoaded ?: false
.firstOrNull() }
?.areAllMembersLoaded ?: false
} }


} }

View File

@ -29,6 +29,10 @@ import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.Sort import io.realm.Sort


/**
* This class is an helper around STATE_ROOM_MEMBER events.
* It allows to get the live membership of a user.
*/
internal class RoomMembers(private val realm: Realm, internal class RoomMembers(private val realm: Realm,
private val roomId: String private val roomId: String
) { ) {

View File

@ -21,12 +21,14 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.UnsignedData import im.vector.matrix.android.api.session.events.model.UnsignedData
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.helper.updateSenderData
import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.mapper.EventMapper import im.vector.matrix.android.internal.database.mapper.EventMapper
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.findWithSenderMembershipEvent
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.tryTransactionSync import im.vector.matrix.android.internal.util.tryTransactionSync
import io.realm.Realm import io.realm.Realm
@ -59,13 +61,13 @@ internal class DefaultPruneEventTask @Inject constructor(private val monarchy: M
} }


val redactionEventEntity = EventEntity.where(realm, eventId = redactionEvent.eventId val redactionEventEntity = EventEntity.where(realm, eventId = redactionEvent.eventId
?: "").findFirst() ?: "").findFirst()
?: return ?: return
val isLocalEcho = redactionEventEntity.sendState == SendState.UNSENT val isLocalEcho = redactionEventEntity.sendState == SendState.UNSENT
Timber.v("Redact event for ${redactionEvent.redacts} localEcho=$isLocalEcho") Timber.v("Redact event for ${redactionEvent.redacts} localEcho=$isLocalEcho")


val eventToPrune = EventEntity.where(realm, eventId = redactionEvent.redacts).findFirst() val eventToPrune = EventEntity.where(realm, eventId = redactionEvent.redacts).findFirst()
?: return ?: return


val allowedKeys = computeAllowedKeys(eventToPrune.type) val allowedKeys = computeAllowedKeys(eventToPrune.type)
if (allowedKeys.isNotEmpty()) { if (allowedKeys.isNotEmpty()) {
@ -76,7 +78,7 @@ internal class DefaultPruneEventTask @Inject constructor(private val monarchy: M
EventType.MESSAGE -> { EventType.MESSAGE -> {
Timber.d("REDACTION for message ${eventToPrune.eventId}") Timber.d("REDACTION for message ${eventToPrune.eventId}")
val unsignedData = EventMapper.map(eventToPrune).unsignedData val unsignedData = EventMapper.map(eventToPrune).unsignedData
?: UnsignedData(null, null) ?: UnsignedData(null, null)


//was this event a m.replace //was this event a m.replace
// val contentModel = ContentMapper.map(eventToPrune.content)?.toModel<MessageContent>() // val contentModel = ContentMapper.map(eventToPrune.content)?.toModel<MessageContent>()
@ -94,28 +96,34 @@ internal class DefaultPruneEventTask @Inject constructor(private val monarchy: M
// } // }
} }
} }
if (eventToPrune.type == EventType.STATE_ROOM_MEMBER) {
val timelineEventsToUpdate = TimelineEventEntity.findWithSenderMembershipEvent(realm, eventToPrune.eventId)
for (timelineEvent in timelineEventsToUpdate) {
timelineEvent.updateSenderData()
}
}
} }




private fun computeAllowedKeys(type: String): List<String> { private fun computeAllowedKeys(type: String): List<String> {
// Add filtered content, allowed keys in content depends on the event type // Add filtered content, allowed keys in content depends on the event type
return when (type) { return when (type) {
EventType.STATE_ROOM_MEMBER -> listOf("membership") EventType.STATE_ROOM_MEMBER -> listOf("membership")
EventType.STATE_ROOM_CREATE -> listOf("creator") EventType.STATE_ROOM_CREATE -> listOf("creator")
EventType.STATE_ROOM_JOIN_RULES -> listOf("join_rule") EventType.STATE_ROOM_JOIN_RULES -> listOf("join_rule")
EventType.STATE_ROOM_POWER_LEVELS -> listOf("users", EventType.STATE_ROOM_POWER_LEVELS -> listOf("users",
"users_default", "users_default",
"events", "events",
"events_default", "events_default",
"state_default", "state_default",
"ban", "ban",
"kick", "kick",
"redact", "redact",
"invite") "invite")
EventType.STATE_ROOM_ALIASES -> listOf("aliases") EventType.STATE_ROOM_ALIASES -> listOf("aliases")
EventType.STATE_CANONICAL_ALIAS -> listOf("alias") EventType.STATE_CANONICAL_ALIAS -> listOf("alias")
EventType.FEEDBACK -> listOf("type", "target_event_id") EventType.FEEDBACK -> listOf("type", "target_event_id")
else -> emptyList() else -> emptyList()
} }
} }
} }

View File

@ -179,7 +179,7 @@ class PushrulesConditionTest {
} }
} }


override fun liveRoomSummaries(fetchLastEvents: Boolean): LiveData<List<RoomSummary>> { override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
return MutableLiveData() return MutableLiveData()
} }
} }
@ -194,11 +194,11 @@ class PushrulesConditionTest {
return _numberOfJoinedMembers return _numberOfJoinedMembers
} }


override fun liveRoomSummary(fetchLastEvent: Boolean): LiveData<RoomSummary> { override fun liveRoomSummary(): LiveData<RoomSummary> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }


override fun roomSummary(fetchLastEvent: Boolean): RoomSummary? { override fun roomSummary(): RoomSummary? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }



View File

@ -73,7 +73,7 @@ class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState:
private fun observeRoomAndGroup() { private fun observeRoomAndGroup() {
Observable Observable
.combineLatest<List<RoomSummary>, Option<GroupSummary>, List<RoomSummary>>( .combineLatest<List<RoomSummary>, Option<GroupSummary>, List<RoomSummary>>(
session.rx().liveRoomSummaries(fetchLastEvents = true).throttleLast(300, TimeUnit.MILLISECONDS), session.rx().liveRoomSummaries().throttleLast(300, TimeUnit.MILLISECONDS),
selectedGroupStore.observe(), selectedGroupStore.observe(),
BiFunction { rooms, selectedGroupOption -> BiFunction { rooms, selectedGroupOption ->
val selectedGroup = selectedGroupOption.orNull() val selectedGroup = selectedGroupOption.orNull()

View File

@ -500,17 +500,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
} }


private fun observeRoomSummary() { private fun observeRoomSummary() {
room.rx().liveRoomSummary(false) room.rx().liveRoomSummary()
.observeOn(AndroidSchedulers.mainThread())
.flatMap {
if (it.membership != Membership.INVITE || it.latestEvent != null) {
// Not an invitation, or already fetching last event
Observable.just(it)
} else {
// We need the last event
room.rx().liveRoomSummary(true)
}
}
.execute { async -> .execute { async ->
copy( copy(
asyncRoomSummary = async, asyncRoomSummary = async,

View File

@ -86,7 +86,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
private fun observeJoinedRooms() { private fun observeJoinedRooms() {
session session
.rx() .rx()
.liveRoomSummaries(fetchLastEvents = false) .liveRoomSummaries()
.subscribe { list -> .subscribe { list ->
val joinedRoomIds = list val joinedRoomIds = list
// Keep only joined room // Keep only joined room

View File

@ -54,7 +54,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
private fun observeJoinedRooms() { private fun observeJoinedRooms() {
session session
.rx() .rx()
.liveRoomSummaries(fetchLastEvents = false) .liveRoomSummaries()
.subscribe { list -> .subscribe { list ->
withState { state -> withState { state ->
val isRoomJoined = list val isRoomJoined = list