forked from GitHub-Mirror/riotX-android
Direct chat: handle user account data
This commit is contained in:
parent
2c81e41288
commit
151ae7f4dd
@ -32,6 +32,7 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
|||||||
var joinedMembersCount: Int? = 0,
|
var joinedMembersCount: Int? = 0,
|
||||||
var invitedMembersCount: Int? = 0,
|
var invitedMembersCount: Int? = 0,
|
||||||
var isDirect: Boolean = false,
|
var isDirect: Boolean = false,
|
||||||
|
var directUserId: String? = null,
|
||||||
var otherMemberIds: RealmList<String> = RealmList(),
|
var otherMemberIds: RealmList<String> = RealmList(),
|
||||||
var notificationCount: Int = 0,
|
var notificationCount: Int = 0,
|
||||||
var highlightCount: Int = 0,
|
var highlightCount: Int = 0,
|
||||||
|
@ -20,6 +20,7 @@ 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 io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
|
import io.realm.RealmResults
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
|
||||||
internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<RoomSummaryEntity> {
|
internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<RoomSummaryEntity> {
|
||||||
@ -29,3 +30,20 @@ internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = n
|
|||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun RoomSummaryEntity.Companion.getDirectRooms(realm: Realm): RealmResults<RoomSummaryEntity> {
|
||||||
|
return RoomSummaryEntity.where(realm)
|
||||||
|
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
||||||
|
.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun RoomSummaryEntity.Companion.isDirect(realm: Realm, roomId: String): Boolean {
|
||||||
|
return RoomSummaryEntity.where(realm)
|
||||||
|
.equalTo(RoomSummaryEntityFields.ROOM_ID, roomId)
|
||||||
|
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
||||||
|
.findAll()
|
||||||
|
.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,11 +65,12 @@ internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm:
|
|||||||
|
|
||||||
internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
|
internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
|
includesSending: Boolean,
|
||||||
includedTypes: List<String> = emptyList(),
|
includedTypes: List<String> = emptyList(),
|
||||||
excludedTypes: List<String> = emptyList()): TimelineEventEntity? {
|
excludedTypes: List<String> = emptyList()): TimelineEventEntity? {
|
||||||
|
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
|
||||||
val eventList = if (roomEntity.sendingTimelineEvents.isNotEmpty()) {
|
val eventList = if (includesSending && roomEntity.sendingTimelineEvents.isNotEmpty()) {
|
||||||
roomEntity.sendingTimelineEvents
|
roomEntity.sendingTimelineEvents
|
||||||
} else {
|
} else {
|
||||||
ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents
|
ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents
|
||||||
|
@ -19,11 +19,11 @@ package im.vector.matrix.android.internal.session
|
|||||||
import dagger.BindsInstance
|
import dagger.BindsInstance
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoModule
|
import im.vector.matrix.android.internal.crypto.CryptoModule
|
||||||
import im.vector.matrix.android.internal.di.MatrixComponent
|
import im.vector.matrix.android.internal.di.MatrixComponent
|
||||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||||
|
import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule
|
||||||
import im.vector.matrix.android.internal.session.cache.CacheModule
|
import im.vector.matrix.android.internal.session.cache.CacheModule
|
||||||
import im.vector.matrix.android.internal.session.content.ContentModule
|
import im.vector.matrix.android.internal.session.content.ContentModule
|
||||||
import im.vector.matrix.android.internal.session.content.UploadContentWorker
|
import im.vector.matrix.android.internal.session.content.UploadContentWorker
|
||||||
@ -46,20 +46,21 @@ import im.vector.matrix.android.internal.session.user.UserModule
|
|||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
|
|
||||||
@Component(dependencies = [MatrixComponent::class],
|
@Component(dependencies = [MatrixComponent::class],
|
||||||
modules = [
|
modules = [
|
||||||
SessionModule::class,
|
SessionModule::class,
|
||||||
RoomModule::class,
|
RoomModule::class,
|
||||||
SyncModule::class,
|
SyncModule::class,
|
||||||
SignOutModule::class,
|
SignOutModule::class,
|
||||||
GroupModule::class,
|
GroupModule::class,
|
||||||
UserModule::class,
|
UserModule::class,
|
||||||
FilterModule::class,
|
FilterModule::class,
|
||||||
GroupModule::class,
|
GroupModule::class,
|
||||||
ContentModule::class,
|
ContentModule::class,
|
||||||
CacheModule::class,
|
CacheModule::class,
|
||||||
CryptoModule::class,
|
CryptoModule::class,
|
||||||
PushersModule::class
|
PushersModule::class,
|
||||||
]
|
AccountDataModule::class
|
||||||
|
]
|
||||||
)
|
)
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal interface SessionComponent {
|
internal interface SessionComponent {
|
||||||
|
@ -62,7 +62,9 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C
|
|||||||
roomId: String,
|
roomId: String,
|
||||||
membership: Membership? = null,
|
membership: Membership? = null,
|
||||||
roomSummary: RoomSyncSummary? = null,
|
roomSummary: RoomSyncSummary? = null,
|
||||||
unreadNotifications: RoomSyncUnreadNotifications? = null) {
|
unreadNotifications: RoomSyncUnreadNotifications? = null,
|
||||||
|
isDirect: Boolean? = null,
|
||||||
|
directUserId: String? = null) {
|
||||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
|
|
||||||
@ -85,7 +87,7 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C
|
|||||||
roomSummaryEntity.membership = membership
|
roomSummaryEntity.membership = membership
|
||||||
}
|
}
|
||||||
|
|
||||||
val latestEvent = TimelineEventEntity.latestEvent(realm, roomId, includedTypes = PREVIEWABLE_TYPES)
|
val latestEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, includedTypes = PREVIEWABLE_TYPES)
|
||||||
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain()
|
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain()
|
||||||
|
|
||||||
val otherRoomMembers = RoomMembers(realm, roomId)
|
val otherRoomMembers = RoomMembers(realm, roomId)
|
||||||
@ -95,6 +97,10 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C
|
|||||||
.asSequence()
|
.asSequence()
|
||||||
.map { it.stateKey }
|
.map { it.stateKey }
|
||||||
|
|
||||||
|
if (isDirect != null) {
|
||||||
|
roomSummaryEntity.isDirect = isDirect
|
||||||
|
roomSummaryEntity.directUserId = directUserId
|
||||||
|
}
|
||||||
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
|
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
|
||||||
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
||||||
roomSummaryEntity.topic = lastTopicEvent?.content.toModel<RoomTopicContent>()?.topic
|
roomSummaryEntity.topic = lastTopicEvent?.content.toModel<RoomTopicContent>()?.topic
|
||||||
|
@ -17,22 +17,32 @@
|
|||||||
package im.vector.matrix.android.internal.session.room.create
|
package im.vector.matrix.android.internal.session.room.create
|
||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
|
||||||
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
||||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
|
||||||
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.RoomEntityFields
|
import im.vector.matrix.android.internal.database.model.RoomEntityFields
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
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.read.SetReadMarkersTask
|
||||||
|
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.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
import im.vector.matrix.android.internal.util.tryTransactionSync
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface CreateRoomTask : Task<CreateRoomParams, String>
|
internal interface CreateRoomTask : Task<CreateRoomParams, String>
|
||||||
|
|
||||||
internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: RoomAPI,
|
internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: RoomAPI,
|
||||||
|
private val monarchy: Monarchy,
|
||||||
|
private val directChatsHelper: DirectChatsHelper,
|
||||||
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
|
private val readMarkersTask: SetReadMarkersTask,
|
||||||
@SessionDatabase private val realmConfiguration: RealmConfiguration) : CreateRoomTask {
|
@SessionDatabase private val realmConfiguration: RealmConfiguration) : CreateRoomTask {
|
||||||
|
|
||||||
|
|
||||||
@ -41,17 +51,48 @@ internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: Ro
|
|||||||
apiCall = roomAPI.createRoom(params)
|
apiCall = roomAPI.createRoom(params)
|
||||||
}.flatMap { createRoomResponse ->
|
}.flatMap { createRoomResponse ->
|
||||||
val roomId = createRoomResponse.roomId!!
|
val roomId = createRoomResponse.roomId!!
|
||||||
|
|
||||||
// TODO Maybe do the same code for join room request ?
|
|
||||||
// Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before)
|
// Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before)
|
||||||
val rql = RealmQueryLatch<RoomEntity>(realmConfiguration) { realm ->
|
val rql = RealmQueryLatch<RoomEntity>(realmConfiguration) { realm ->
|
||||||
realm.where(RoomEntity::class.java)
|
realm.where(RoomEntity::class.java)
|
||||||
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
rql.await()
|
rql.await()
|
||||||
|
Try.just(roomId)
|
||||||
return Try.just(roomId)
|
}.flatMap { roomId ->
|
||||||
|
if (params.isDirect()) {
|
||||||
|
handleDirectChatCreation(params, roomId)
|
||||||
|
} else {
|
||||||
|
Try.just(roomId)
|
||||||
|
}
|
||||||
|
}.flatMap { roomId ->
|
||||||
|
setReadMarkers(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun handleDirectChatCreation(params: CreateRoomParams, roomId: String): Try<String> {
|
||||||
|
val otherUserId = params.getFirstInvitedUserId()
|
||||||
|
?: return Try.raise(IllegalStateException("You can't create a direct room without an invitedUser"))
|
||||||
|
|
||||||
|
return monarchy.tryTransactionSync { realm ->
|
||||||
|
RoomSummaryEntity.where(realm, roomId).findFirst()?.apply {
|
||||||
|
this.directUserId = otherUserId
|
||||||
|
this.isDirect = true
|
||||||
|
}
|
||||||
|
}.flatMap {
|
||||||
|
val directChats = directChatsHelper.getDirectChats()
|
||||||
|
updateUserAccountDataTask.execute(UpdateUserAccountDataTask.DirectChatParams(directMessages = directChats))
|
||||||
|
}.flatMap {
|
||||||
|
Try.just(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun setReadMarkers(roomId: String): Try<String> {
|
||||||
|
val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
||||||
|
return readMarkersTask
|
||||||
|
.execute(setReadMarkerParams)
|
||||||
|
.flatMap {
|
||||||
|
Try.just(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ 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.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.query.prev
|
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 io.realm.RealmResults
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,10 +80,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||||||
|
|
||||||
val roomMembers = RoomMembers(realm, roomId)
|
val roomMembers = RoomMembers(realm, roomId)
|
||||||
val loadedMembers = roomMembers.queryRoomMembersEvent().findAll()
|
val loadedMembers = roomMembers.queryRoomMembersEvent().findAll()
|
||||||
val otherMembersSubset = loadedMembers.where()
|
|
||||||
.notEqualTo(EventEntityFields.STATE_KEY, credentials.userId)
|
|
||||||
.limit(3)
|
|
||||||
.findAll()
|
|
||||||
|
|
||||||
if (roomEntity?.membership == Membership.INVITE) {
|
if (roomEntity?.membership == Membership.INVITE) {
|
||||||
val inviteMeEvent = roomMembers.queryRoomMemberEvent(credentials.userId).findFirst()
|
val inviteMeEvent = roomMembers.queryRoomMemberEvent(credentials.userId).findFirst()
|
||||||
@ -97,23 +93,29 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||||||
} else {
|
} else {
|
||||||
context.getString(R.string.room_displayname_room_invite)
|
context.getString(R.string.room_displayname_room_invite)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (roomEntity?.membership == Membership.JOIN) {
|
||||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
val memberIds: List<String> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
val otherMembersSubset: List<EventEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
||||||
roomSummary.heroes
|
roomSummary.heroes.mapNotNull {
|
||||||
|
roomMembers.getStateEvent(it)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
otherMembersSubset.mapNotNull { it.stateKey }
|
loadedMembers.where()
|
||||||
|
.notEqualTo(EventEntityFields.STATE_KEY, credentials.userId)
|
||||||
|
.limit(3)
|
||||||
|
.findAll()
|
||||||
}
|
}
|
||||||
name = when (memberIds.size) {
|
val otherMembersCount = roomMembers.getNumberOfMembers() - 1
|
||||||
|
name = when (otherMembersCount) {
|
||||||
0 -> context.getString(R.string.room_displayname_empty_room)
|
0 -> context.getString(R.string.room_displayname_empty_room)
|
||||||
1 -> resolveRoomMember(otherMembersSubset[0], roomMembers)
|
1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers)
|
||||||
2 -> context.getString(R.string.room_displayname_two_members,
|
2 -> context.getString(R.string.room_displayname_two_members,
|
||||||
resolveRoomMember(otherMembersSubset[0], roomMembers),
|
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||||
resolveRoomMember(otherMembersSubset[1], roomMembers)
|
resolveRoomMemberName(otherMembersSubset[1], roomMembers)
|
||||||
)
|
)
|
||||||
else -> context.resources.getQuantityString(R.plurals.room_displayname_three_and_more_members,
|
else -> context.resources.getQuantityString(R.plurals.room_displayname_three_and_more_members,
|
||||||
roomMembers.getNumberOfJoinedMembers() - 1,
|
roomMembers.getNumberOfJoinedMembers() - 1,
|
||||||
resolveRoomMember(otherMembersSubset[0], roomMembers),
|
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||||
roomMembers.getNumberOfJoinedMembers() - 1)
|
roomMembers.getNumberOfJoinedMembers() - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,8 +124,8 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
|||||||
return name ?: roomId
|
return name ?: roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveRoomMember(eventEntity: EventEntity?,
|
private fun resolveRoomMemberName(eventEntity: EventEntity?,
|
||||||
roomMembers: RoomMembers): String? {
|
roomMembers: RoomMembers): String? {
|
||||||
if (eventEntity == null) return null
|
if (eventEntity == null) return null
|
||||||
val roomMember = eventEntity.toRoomMember() ?: return null
|
val roomMember = eventEntity.toRoomMember() ?: return null
|
||||||
val isUnique = roomMembers.isUniqueDisplayName(roomMember.displayName)
|
val isUnique = roomMembers.isUniqueDisplayName(roomMember.displayName)
|
||||||
|
@ -42,12 +42,16 @@ internal class RoomMembers(private val realm: Realm,
|
|||||||
RoomSummaryEntity.where(realm, roomId).findFirst()
|
RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(userId: String): RoomMember? {
|
fun getStateEvent(userId: String): EventEntity? {
|
||||||
return EventEntity
|
return EventEntity
|
||||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||||
.equalTo(EventEntityFields.STATE_KEY, userId)
|
.equalTo(EventEntityFields.STATE_KEY, userId)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(userId: String): RoomMember? {
|
||||||
|
return getStateEvent(userId)
|
||||||
?.let {
|
?.let {
|
||||||
it.asDomain().content?.toModel<RoomMember>()
|
it.asDomain().content?.toModel<RoomMember>()
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,15 @@
|
|||||||
package im.vector.matrix.android.internal.session.room.membership.joining
|
package im.vector.matrix.android.internal.session.room.membership.joining
|
||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
|
import im.vector.matrix.android.internal.database.RealmQueryLatch
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomEntityFields
|
||||||
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
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.read.SetReadMarkersTask
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
|
internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
|
||||||
@ -29,12 +34,30 @@ internal interface JoinRoomTask : Task<JoinRoomTask.Params, Unit> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: RoomAPI) : JoinRoomTask {
|
internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: RoomAPI,
|
||||||
|
private val readMarkersTask: SetReadMarkersTask,
|
||||||
|
@SessionDatabase private val realmConfiguration: RealmConfiguration) : JoinRoomTask {
|
||||||
|
|
||||||
override suspend fun execute(params: JoinRoomTask.Params): Try<Unit> {
|
override suspend fun execute(params: JoinRoomTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest<Unit> {
|
||||||
apiCall = roomAPI.join(params.roomId, HashMap())
|
apiCall = roomAPI.join(params.roomId, HashMap())
|
||||||
|
}.flatMap {
|
||||||
|
val roomId = params.roomId
|
||||||
|
// Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before)
|
||||||
|
val rql = RealmQueryLatch<RoomEntity>(realmConfiguration) { realm ->
|
||||||
|
realm.where(RoomEntity::class.java)
|
||||||
|
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
||||||
|
}
|
||||||
|
rql.await()
|
||||||
|
Try.just(roomId)
|
||||||
|
}.flatMap { roomId ->
|
||||||
|
setReadMarkers(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun setReadMarkers(roomId: String): Try<Unit> {
|
||||||
|
val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
||||||
|
return readMarkersTask.execute(setReadMarkerParams)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,11 @@ import im.vector.matrix.android.api.auth.data.Credentials
|
|||||||
import im.vector.matrix.android.api.session.room.read.ReadService
|
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||||
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.ReadReceiptEntity
|
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
|
||||||
import im.vector.matrix.android.internal.database.query.find
|
import im.vector.matrix.android.internal.database.query.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.latestEvent
|
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.util.fetchCopied
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultReadService @Inject constructor(private val roomId: String,
|
internal class DefaultReadService @Inject constructor(private val roomId: String,
|
||||||
@ -39,9 +36,7 @@ internal class DefaultReadService @Inject constructor(private val roomId: String
|
|||||||
private val credentials: Credentials) : ReadService {
|
private val credentials: Credentials) : ReadService {
|
||||||
|
|
||||||
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
|
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
|
||||||
//TODO shouldn't it be latest synced event?
|
val params = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
||||||
val latestEvent = getLatestEvent()
|
|
||||||
val params = SetReadMarkersTask.Params(roomId, fullyReadEventId = latestEvent?.eventId, readReceiptEventId = latestEvent?.eventId)
|
|
||||||
setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor)
|
setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +50,6 @@ internal class DefaultReadService @Inject constructor(private val roomId: String
|
|||||||
setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor)
|
setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLatestEvent(): TimelineEventEntity? {
|
|
||||||
return monarchy.fetchCopied { TimelineEventEntity.latestEvent(it, roomId) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEventRead(eventId: String): Boolean {
|
override fun isEventRead(eventId: String): Boolean {
|
||||||
var isEventRead = false
|
var isEventRead = false
|
||||||
|
@ -20,7 +20,6 @@ import arrow.core.Try
|
|||||||
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.auth.data.Credentials
|
||||||
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.EventEntity
|
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
||||||
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.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
@ -33,6 +32,7 @@ import im.vector.matrix.android.internal.session.room.RoomAPI
|
|||||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
||||||
|
import io.realm.Realm
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -40,8 +40,9 @@ internal interface SetReadMarkersTask : Task<SetReadMarkersTask.Params, Unit> {
|
|||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val fullyReadEventId: String?,
|
val markAllAsRead: Boolean = false,
|
||||||
val readReceiptEventId: String?
|
val fullyReadEventId: String? = null,
|
||||||
|
val readReceiptEventId: String? = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,21 +56,35 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
|
|||||||
|
|
||||||
override suspend fun execute(params: SetReadMarkersTask.Params): Try<Unit> {
|
override suspend fun execute(params: SetReadMarkersTask.Params): Try<Unit> {
|
||||||
val markers = HashMap<String, String>()
|
val markers = HashMap<String, String>()
|
||||||
if (params.fullyReadEventId != null) {
|
val fullyReadEventId: String?
|
||||||
if (LocalEchoEventFactory.isLocalEchoId(params.fullyReadEventId)) {
|
val readReceiptEventId: String?
|
||||||
|
|
||||||
|
if (params.markAllAsRead) {
|
||||||
|
val latestSyncedEventId = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
|
TimelineEventEntity.latestEvent(realm, roomId = params.roomId, includesSending = false)?.eventId
|
||||||
|
}
|
||||||
|
fullyReadEventId = latestSyncedEventId
|
||||||
|
readReceiptEventId = latestSyncedEventId
|
||||||
|
} else {
|
||||||
|
fullyReadEventId = params.fullyReadEventId
|
||||||
|
readReceiptEventId = params.readReceiptEventId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullyReadEventId != null) {
|
||||||
|
if (LocalEchoEventFactory.isLocalEchoId(fullyReadEventId)) {
|
||||||
Timber.w("Can't set read marker for local event ${params.fullyReadEventId}")
|
Timber.w("Can't set read marker for local event ${params.fullyReadEventId}")
|
||||||
} else {
|
} else {
|
||||||
markers[READ_MARKER] = params.fullyReadEventId
|
markers[READ_MARKER] = fullyReadEventId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params.readReceiptEventId != null
|
if (readReceiptEventId != null
|
||||||
&& !isEventRead(params.roomId, params.readReceiptEventId)) {
|
&& !isEventRead(params.roomId, readReceiptEventId)) {
|
||||||
|
|
||||||
if (LocalEchoEventFactory.isLocalEchoId(params.readReceiptEventId)) {
|
if (LocalEchoEventFactory.isLocalEchoId(readReceiptEventId)) {
|
||||||
Timber.w("Can't set read marker for local event ${params.fullyReadEventId}")
|
Timber.w("Can't set read receipt for local event ${params.fullyReadEventId}")
|
||||||
} else {
|
} else {
|
||||||
updateNotificationCountIfNecessary(params.roomId, params.readReceiptEventId)
|
updateNotificationCountIfNecessary(params.roomId, readReceiptEventId)
|
||||||
markers[READ_RECEIPT] = params.readReceiptEventId
|
markers[READ_RECEIPT] = readReceiptEventId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if (markers.isEmpty()) {
|
return if (markers.isEmpty()) {
|
||||||
@ -83,10 +98,10 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
|
|||||||
|
|
||||||
private fun updateNotificationCountIfNecessary(roomId: String, eventId: String) {
|
private fun updateNotificationCountIfNecessary(roomId: String, eventId: String) {
|
||||||
monarchy.tryTransactionAsync { realm ->
|
monarchy.tryTransactionAsync { realm ->
|
||||||
val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId)?.eventId == eventId
|
val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId == eventId
|
||||||
if (isLatestReceived) {
|
if (isLatestReceived) {
|
||||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
?: return@tryTransactionAsync
|
?: return@tryTransactionAsync
|
||||||
roomSummary.notificationCount = 0
|
roomSummary.notificationCount = 0
|
||||||
roomSummary.highlightCount = 0
|
roomSummary.highlightCount = 0
|
||||||
}
|
}
|
||||||
@ -97,13 +112,13 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
|
|||||||
var isEventRead = false
|
var isEventRead = false
|
||||||
monarchy.doWithRealm {
|
monarchy.doWithRealm {
|
||||||
val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst()
|
val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst()
|
||||||
?: return@doWithRealm
|
?: return@doWithRealm
|
||||||
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId)
|
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId)
|
||||||
?: return@doWithRealm
|
?: return@doWithRealm
|
||||||
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex
|
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex
|
||||||
?: Int.MIN_VALUE
|
?: Int.MIN_VALUE
|
||||||
val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex
|
val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex
|
||||||
?: Int.MAX_VALUE
|
?: Int.MAX_VALUE
|
||||||
isEventRead = eventToCheckIndex <= readReceiptIndex
|
isEventRead = eventToCheckIndex <= readReceiptIndex
|
||||||
}
|
}
|
||||||
return isEventRead
|
return isEventRead
|
||||||
|
@ -18,27 +18,41 @@ package im.vector.matrix.android.internal.session.sync
|
|||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
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.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.events.model.toModel
|
||||||
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.api.session.room.model.RoomMember
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.CryptoManager
|
||||||
import im.vector.matrix.android.internal.database.helper.*
|
import im.vector.matrix.android.internal.database.helper.add
|
||||||
|
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
||||||
|
import im.vector.matrix.android.internal.database.helper.addStateEvent
|
||||||
|
import im.vector.matrix.android.internal.database.helper.updateSenderDataFor
|
||||||
|
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.UserEntity
|
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
|
||||||
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.room.membership.RoomMembers
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
import im.vector.matrix.android.internal.session.sync.model.*
|
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
||||||
|
import im.vector.matrix.android.internal.session.sync.model.RoomSync
|
||||||
|
import im.vector.matrix.android.internal.session.sync.model.RoomSyncAccountData
|
||||||
|
import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral
|
||||||
|
import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
|
||||||
import im.vector.matrix.android.internal.session.user.UserEntityFactory
|
import im.vector.matrix.android.internal.session.user.UserEntityFactory
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
@ -55,6 +69,9 @@ 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 directChatsHelper: DirectChatsHelper,
|
||||||
private val taskExecutor: TaskExecutor) {
|
private val taskExecutor: TaskExecutor) {
|
||||||
|
|
||||||
sealed class HandlingStrategy {
|
sealed class HandlingStrategy {
|
||||||
@ -118,7 +135,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
Timber.v("Handle join sync for room $roomId")
|
Timber.v("Handle join sync for room $roomId")
|
||||||
|
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
|
|
||||||
if (roomEntity.membership == Membership.INVITE) {
|
if (roomEntity.membership == Membership.INVITE) {
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
@ -128,7 +145,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
// State event
|
// State event
|
||||||
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
|
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
|
||||||
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
|
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
|
||||||
?: Int.MIN_VALUE
|
?: Int.MIN_VALUE
|
||||||
val untimelinedStateIndex = minStateIndex + 1
|
val untimelinedStateIndex = minStateIndex + 1
|
||||||
roomSync.state.events.forEach { event ->
|
roomSync.state.events.forEach { event ->
|
||||||
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
||||||
@ -169,13 +186,27 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
InvitedRoomSync): RoomEntity {
|
InvitedRoomSync): RoomEntity {
|
||||||
Timber.v("Handle invited sync for room $roomId")
|
Timber.v("Handle invited sync for room $roomId")
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
roomEntity.membership = Membership.INVITE
|
roomEntity.membership = Membership.INVITE
|
||||||
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
||||||
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
|
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
|
||||||
roomEntity.addOrUpdate(chunkEntity)
|
roomEntity.addOrUpdate(chunkEntity)
|
||||||
}
|
}
|
||||||
roomSummaryUpdater.update(realm, roomId, Membership.INVITE)
|
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 (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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +214,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
|||||||
roomId: String,
|
roomId: String,
|
||||||
roomSync: RoomSync): RoomEntity {
|
roomSync: RoomSync): RoomEntity {
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
|
|
||||||
roomEntity.membership = Membership.LEAVE
|
roomEntity.membership = Membership.LEAVE
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
|
@ -19,8 +19,8 @@ package im.vector.matrix.android.internal.session.sync
|
|||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
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.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.SessionScope
|
|
||||||
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 javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -37,19 +37,22 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDirectChatRooms(directMessages: UserAccountDataDirectMessages) {
|
private fun handleDirectChatRooms(directMessages: UserAccountDataDirectMessages) {
|
||||||
val newDirectRoomIds = directMessages.content.values.flatten()
|
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
|
|
||||||
val oldDirectRooms = RoomSummaryEntity.where(realm)
|
val oldDirectRooms = RoomSummaryEntity.getDirectRooms(realm)
|
||||||
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
oldDirectRooms.forEach {
|
||||||
.findAll()
|
it.isDirect = false
|
||||||
oldDirectRooms.forEach { it.isDirect = false }
|
it.directUserId = null
|
||||||
|
}
|
||||||
newDirectRoomIds.forEach { roomId ->
|
directMessages.content.forEach {
|
||||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val userId = it.key
|
||||||
if (roomSummaryEntity != null) {
|
it.value.forEach { roomId ->
|
||||||
roomSummaryEntity.isDirect = true
|
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
realm.insertOrUpdate(roomSummaryEntity)
|
if (roomSummaryEntity != null) {
|
||||||
|
roomSummaryEntity.isDirect = true
|
||||||
|
roomSummaryEntity.directUserId = userId
|
||||||
|
realm.insertOrUpdate(roomSummaryEntity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.user
|
|||||||
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.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||||
|
|
||||||
@ -29,6 +30,10 @@ internal object UserEntityFactory {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val roomMember = event.content.toModel<RoomMember>() ?: return null
|
val roomMember = event.content.toModel<RoomMember>() ?: return null
|
||||||
|
// We only use JOIN and INVITED memberships to create User data
|
||||||
|
if (roomMember.membership != Membership.JOIN || roomMember.membership != Membership.INVITE) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
return UserEntity(event.stateKey ?: "",
|
return UserEntity(event.stateKey ?: "",
|
||||||
roomMember.displayName ?: "",
|
roomMember.displayName ?: "",
|
||||||
roomMember.avatarUrl ?: ""
|
roomMember.avatarUrl ?: ""
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.session.user.accountdata
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
|
import retrofit2.http.Path
|
||||||
|
|
||||||
|
interface AccountDataAPI {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set some account_data for the client.
|
||||||
|
*
|
||||||
|
* @param userId the user id
|
||||||
|
* @param type the type
|
||||||
|
* @param params the put params
|
||||||
|
*/
|
||||||
|
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/account_data/{type}")
|
||||||
|
fun setAccountData(@Path("userId") userId: String, @Path("type") type: String, @Body params: Any): Call<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a bearer token from the homeserver that the user can
|
||||||
|
* present to a third party in order to prove their ownership
|
||||||
|
* of the Matrix account they are logged into.
|
||||||
|
*
|
||||||
|
* @param userId the user id
|
||||||
|
* @param body the body content
|
||||||
|
*/
|
||||||
|
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/openid/request_token")
|
||||||
|
fun openIdToken(@Path("userId") userId: String, @Body body: Map<Any, Any>): Call<Map<Any, Any>>
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.session.user.accountdata
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
|
||||||
|
@Module
|
||||||
|
internal abstract class AccountDataModule {
|
||||||
|
|
||||||
|
@Module
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Provides
|
||||||
|
fun providesAccountDataAPI(retrofit: Retrofit): AccountDataAPI {
|
||||||
|
return retrofit.create(AccountDataAPI::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindUpdateUserAccountDataTask(updateUserAccountDataTask: DefaultUpdateUserAcountDataTask): UpdateUserAccountDataTask
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.session.user.accountdata
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.getDirectRooms
|
||||||
|
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class DirectChatsHelper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {
|
||||||
|
|
||||||
|
fun getDirectChats(include: Pair<String, String>? = null, filterRoomId: String? = null): Map<String, List<String>> {
|
||||||
|
return Realm.getInstance(realmConfiguration).use { realm ->
|
||||||
|
val currentDirectRooms = RoomSummaryEntity.getDirectRooms(realm)
|
||||||
|
val directChatsMap = mutableMapOf<String, MutableList<String>>()
|
||||||
|
for (directRoom in currentDirectRooms) {
|
||||||
|
if (directRoom.roomId == filterRoomId) continue
|
||||||
|
val directUserId = directRoom.directUserId ?: continue
|
||||||
|
directChatsMap.getOrPut(directUserId, { arrayListOf() }).apply {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.session.user.accountdata
|
||||||
|
|
||||||
|
import arrow.core.Try
|
||||||
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
|
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
|
||||||
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Params, Unit> {
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
val type: String
|
||||||
|
fun getData(): Any
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DirectChatParams(override val type: String = UserAccountData.TYPE_DIRECT_MESSAGES,
|
||||||
|
private val directMessages: Map<String, List<String>>
|
||||||
|
) : Params {
|
||||||
|
|
||||||
|
override fun getData(): Any {
|
||||||
|
return directMessages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultUpdateUserAcountDataTask @Inject constructor(private val accountDataApi: AccountDataAPI,
|
||||||
|
private val credentials: Credentials) : UpdateUserAccountDataTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: UpdateUserAccountDataTask.Params): Try<Unit> {
|
||||||
|
|
||||||
|
return executeRequest {
|
||||||
|
apiCall = accountDataApi.setAccountData(credentials.userId, params.type, params.getData())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user