User Account Data: fix sync issues with direct invites

This commit is contained in:
ganfra 2019-07-30 17:32:31 +02:00
parent 087cc0e6e3
commit 4b4156996d
4 changed files with 72 additions and 40 deletions

View File

@ -34,14 +34,10 @@ import im.vector.matrix.android.internal.database.mapper.asDomain
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.EventEntityFields import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
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.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.isDirect
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
import im.vector.matrix.android.internal.session.mapWithProgress import im.vector.matrix.android.internal.session.mapWithProgress
import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService
import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask
@ -69,9 +65,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
private val tokenStore: SyncTokenStore, private val tokenStore: SyncTokenStore,
private val pushRuleService: DefaultPushRuleService, private val pushRuleService: DefaultPushRuleService,
private val processForPushTask: ProcessEventForPushTask, private val processForPushTask: ProcessEventForPushTask,
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val credentials: Credentials, private val credentials: Credentials,
private val directChatsHelper: DirectChatsHelper,
private val taskExecutor: TaskExecutor) { private val taskExecutor: TaskExecutor) {


sealed class HandlingStrategy { sealed class HandlingStrategy {
@ -192,21 +186,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events) val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
roomEntity.addOrUpdate(chunkEntity) roomEntity.addOrUpdate(chunkEntity)
} }
val myUserStateEvent = RoomMembers(realm, roomId).getStateEvent(credentials.userId) roomSummaryUpdater.update(realm, roomId, Membership.INVITE)
val inviterId = myUserStateEvent?.sender
val myUserRoomMember: RoomMember? = myUserStateEvent?.let { it.asDomain().content?.toModel() }
val isDirect = myUserRoomMember?.isDirect
if (isDirect == true && inviterId != null) {
val isAlreadyDirect = RoomSummaryEntity.isDirect(realm, roomId)
if (!isAlreadyDirect) {
val directChatsMap = directChatsHelper.getDirectChats(include = Pair(inviterId, roomId))
val updateUserAccountParams = UpdateUserAccountDataTask.DirectChatParams(
directMessages = directChatsMap
)
updateUserAccountDataTask.configureWith(updateUserAccountParams).executeBy(taskExecutor)
}
}
roomSummaryUpdater.update(realm, roomId, Membership.INVITE, isDirect = isDirect, directUserId = inviterId)
return roomEntity return roomEntity
} }



View File

@ -17,11 +17,23 @@
package im.vector.matrix.android.internal.session.sync package im.vector.matrix.android.internal.session.sync


import arrow.core.Try import arrow.core.Try
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.R import im.vector.matrix.android.R
import im.vector.matrix.android.api.auth.data.Credentials
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.crypto.CryptoManager import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.isDirect
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
import im.vector.matrix.android.internal.session.reportSubtask import im.vector.matrix.android.internal.session.reportSubtask
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -88,9 +100,7 @@ internal class SyncResponseHandler @Inject constructor(private val roomSyncHandl
measureTimeMillis { measureTimeMillis {
reportSubtask(reporter, R.string.initial_sync_start_importing_account_data, 100, 0.1f) { reportSubtask(reporter, R.string.initial_sync_start_importing_account_data, 100, 0.1f) {
Timber.v("Handle accountData") Timber.v("Handle accountData")
if (syncResponse.accountData != null) { userAccountDataSyncHandler.handle(syncResponse.accountData, syncResponse.rooms?.invite)
userAccountDataSyncHandler.handle(syncResponse.accountData)
}
} }
}.also { }.also {
Timber.v("Finish handling accountData in $it ms") Timber.v("Finish handling accountData in $it ms")
@ -98,7 +108,6 @@ internal class SyncResponseHandler @Inject constructor(private val roomSyncHandl


Timber.v("On sync completed") Timber.v("On sync completed")
cryptoSyncHandler.onSyncCompleted(syncResponse) cryptoSyncHandler.onSyncCompleted(syncResponse)

} }
Timber.v("Finish handling sync in $measure ms") Timber.v("Finish handling sync in $measure ms")
syncResponse syncResponse

View File

@ -17,28 +17,45 @@
package im.vector.matrix.android.internal.session.sync package im.vector.matrix.android.internal.session.sync


import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
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.asDomain
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.query.getDirectRooms import im.vector.matrix.android.internal.database.query.getDirectRooms
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync
import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import io.realm.Realm
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject


internal class UserAccountDataSyncHandler @Inject constructor(private val monarchy: Monarchy) { internal class UserAccountDataSyncHandler @Inject constructor(private val monarchy: Monarchy,
private val credentials: Credentials,
private val directChatsHelper: DirectChatsHelper,
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val taskExecutor: TaskExecutor) {


fun handle(accountData: UserAccountDataSync) { fun handle(accountData: UserAccountDataSync?, invites: Map<String, InvitedRoomSync>?) {
accountData.list.forEach { accountData?.list?.forEach {
when (it) { when (it) {
is UserAccountDataDirectMessages -> handleDirectChatRooms(it) is UserAccountDataDirectMessages -> handleDirectChatRooms(it)
else -> return@forEach else -> return@forEach
} }
} }
monarchy.doWithRealm { realm ->
synchronizeWithServerIfNeeded(realm, invites)
}
} }


private fun handleDirectChatRooms(directMessages: UserAccountDataDirectMessages) { private fun handleDirectChatRooms(directMessages: UserAccountDataDirectMessages) {
monarchy.runTransactionSync { realm -> monarchy.runTransactionSync { realm ->

val oldDirectRooms = RoomSummaryEntity.getDirectRooms(realm) val oldDirectRooms = RoomSummaryEntity.getDirectRooms(realm)
oldDirectRooms.forEach { oldDirectRooms.forEach {
it.isDirect = false it.isDirect = false
@ -57,4 +74,39 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
} }
} }
} }

// If we get some direct chat invites, we synchronize the user account data including those.
private fun synchronizeWithServerIfNeeded(realm: Realm, invites: Map<String, InvitedRoomSync>?) {
if (invites.isNullOrEmpty()) return

val directChats = directChatsHelper.getDirectChats()
val directChatInvites = HashMap<String, MutableList<String>>().apply {
directChats.forEach { (inviterId, roomIds) ->
put(inviterId, ArrayList(roomIds))
}
}
var hasUpdate = false
invites.forEach { (roomId, _) ->
val myUserStateEvent = RoomMembers(realm, roomId).getStateEvent(credentials.userId)
val inviterId = myUserStateEvent?.sender
val myUserRoomMember: RoomMember? = myUserStateEvent?.let { it.asDomain().content?.toModel() }
val isDirect = myUserRoomMember?.isDirect
if (inviterId != null && inviterId != credentials.userId && isDirect == true) {
directChatInvites.getOrPut(inviterId, { arrayListOf() }).apply {
if (contains(roomId)) {
Timber.v("Direct chats already include room $roomId with user $inviterId")
} else {
this.add(roomId)
hasUpdate = true
}
}
}
}
if (hasUpdate) {
val updateUserAccountParams = UpdateUserAccountDataTask.DirectChatParams(
directMessages = directChatInvites
)
updateUserAccountDataTask.configureWith(updateUserAccountParams).executeBy(taskExecutor)
}
}
} }

View File

@ -26,7 +26,7 @@ import javax.inject.Inject


internal class DirectChatsHelper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) { internal class DirectChatsHelper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {


fun getDirectChats(include: Pair<String, String>? = null, filterRoomId: String? = null): Map<String, List<String>> { fun getDirectChats(filterRoomId: String? = null): Map<String, List<String>> {
return Realm.getInstance(realmConfiguration).use { realm -> return Realm.getInstance(realmConfiguration).use { realm ->
val currentDirectRooms = RoomSummaryEntity.getDirectRooms(realm) val currentDirectRooms = RoomSummaryEntity.getDirectRooms(realm)
val directChatsMap = mutableMapOf<String, MutableList<String>>() val directChatsMap = mutableMapOf<String, MutableList<String>>()
@ -37,15 +37,6 @@ internal class DirectChatsHelper @Inject constructor(@SessionDatabase private va
add(directRoom.roomId) add(directRoom.roomId)
} }
} }
if (include != null) {
directChatsMap.getOrPut(include.first, { arrayListOf() }).apply {
if (contains(include.second)) {
Timber.v("Direct chats already include room ${include.second} with user ${include.first}")
} else {
add(include.second)
}
}
}
directChatsMap directChatsMap
} }
} }