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 invitedMembersCount: Int? = 0,
|
||||
var isDirect: Boolean = false,
|
||||
var directUserId: String? = null,
|
||||
var otherMemberIds: RealmList<String> = RealmList(),
|
||||
var notificationCount: 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 io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.RealmResults
|
||||
import io.realm.kotlin.where
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
roomId: String,
|
||||
includesSending: Boolean,
|
||||
includedTypes: List<String> = emptyList(),
|
||||
excludedTypes: List<String> = emptyList()): TimelineEventEntity? {
|
||||
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
|
||||
val eventList = if (roomEntity.sendingTimelineEvents.isNotEmpty()) {
|
||||
val eventList = if (includesSending && roomEntity.sendingTimelineEvents.isNotEmpty()) {
|
||||
roomEntity.sendingTimelineEvents
|
||||
} else {
|
||||
ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents
|
||||
|
@ -19,11 +19,11 @@ package im.vector.matrix.android.internal.session
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
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.internal.crypto.CryptoModule
|
||||
import im.vector.matrix.android.internal.di.MatrixComponent
|
||||
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.content.ContentModule
|
||||
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
|
||||
|
||||
@Component(dependencies = [MatrixComponent::class],
|
||||
modules = [
|
||||
SessionModule::class,
|
||||
RoomModule::class,
|
||||
SyncModule::class,
|
||||
SignOutModule::class,
|
||||
GroupModule::class,
|
||||
UserModule::class,
|
||||
FilterModule::class,
|
||||
GroupModule::class,
|
||||
ContentModule::class,
|
||||
CacheModule::class,
|
||||
CryptoModule::class,
|
||||
PushersModule::class
|
||||
]
|
||||
modules = [
|
||||
SessionModule::class,
|
||||
RoomModule::class,
|
||||
SyncModule::class,
|
||||
SignOutModule::class,
|
||||
GroupModule::class,
|
||||
UserModule::class,
|
||||
FilterModule::class,
|
||||
GroupModule::class,
|
||||
ContentModule::class,
|
||||
CacheModule::class,
|
||||
CryptoModule::class,
|
||||
PushersModule::class,
|
||||
AccountDataModule::class
|
||||
]
|
||||
)
|
||||
@SessionScope
|
||||
internal interface SessionComponent {
|
||||
|
@ -62,7 +62,9 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C
|
||||
roomId: String,
|
||||
membership: Membership? = null,
|
||||
roomSummary: RoomSyncSummary? = null,
|
||||
unreadNotifications: RoomSyncUnreadNotifications? = null) {
|
||||
unreadNotifications: RoomSyncUnreadNotifications? = null,
|
||||
isDirect: Boolean? = null,
|
||||
directUserId: String? = null) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
|
||||
@ -85,7 +87,7 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C
|
||||
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 otherRoomMembers = RoomMembers(realm, roomId)
|
||||
@ -95,6 +97,10 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C
|
||||
.asSequence()
|
||||
.map { it.stateKey }
|
||||
|
||||
if (isDirect != null) {
|
||||
roomSummaryEntity.isDirect = isDirect
|
||||
roomSummaryEntity.directUserId = directUserId
|
||||
}
|
||||
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
|
||||
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
||||
roomSummaryEntity.topic = lastTopicEvent?.content.toModel<RoomTopicContent>()?.topic
|
||||
|
@ -17,22 +17,32 @@
|
||||
package im.vector.matrix.android.internal.session.room.create
|
||||
|
||||
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.CreateRoomResponse
|
||||
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.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.session.SessionScope
|
||||
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.util.tryTransactionSync
|
||||
import io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface CreateRoomTask : Task<CreateRoomParams, String>
|
||||
|
||||
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 {
|
||||
|
||||
|
||||
@ -41,17 +51,48 @@ internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: Ro
|
||||
apiCall = roomAPI.createRoom(params)
|
||||
}.flatMap { createRoomResponse ->
|
||||
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)
|
||||
val rql = RealmQueryLatch<RoomEntity>(realmConfiguration) { realm ->
|
||||
realm.where(RoomEntity::class.java)
|
||||
.equalTo(RoomEntityFields.ROOM_ID, roomId)
|
||||
}
|
||||
|
||||
rql.await()
|
||||
|
||||
return Try.just(roomId)
|
||||
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.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import io.realm.RealmResults
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@ -81,10 +80,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
||||
|
||||
val roomMembers = RoomMembers(realm, roomId)
|
||||
val loadedMembers = roomMembers.queryRoomMembersEvent().findAll()
|
||||
val otherMembersSubset = loadedMembers.where()
|
||||
.notEqualTo(EventEntityFields.STATE_KEY, credentials.userId)
|
||||
.limit(3)
|
||||
.findAll()
|
||||
|
||||
|
||||
if (roomEntity?.membership == Membership.INVITE) {
|
||||
val inviteMeEvent = roomMembers.queryRoomMemberEvent(credentials.userId).findFirst()
|
||||
@ -97,23 +93,29 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
||||
} else {
|
||||
context.getString(R.string.room_displayname_room_invite)
|
||||
}
|
||||
} else {
|
||||
} else if (roomEntity?.membership == Membership.JOIN) {
|
||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
val memberIds: List<String> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
||||
roomSummary.heroes
|
||||
val otherMembersSubset: List<EventEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
||||
roomSummary.heroes.mapNotNull {
|
||||
roomMembers.getStateEvent(it)
|
||||
}
|
||||
} 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)
|
||||
1 -> resolveRoomMember(otherMembersSubset[0], roomMembers)
|
||||
1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers)
|
||||
2 -> context.getString(R.string.room_displayname_two_members,
|
||||
resolveRoomMember(otherMembersSubset[0], roomMembers),
|
||||
resolveRoomMember(otherMembersSubset[1], roomMembers)
|
||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||
resolveRoomMemberName(otherMembersSubset[1], roomMembers)
|
||||
)
|
||||
else -> context.resources.getQuantityString(R.plurals.room_displayname_three_and_more_members,
|
||||
roomMembers.getNumberOfJoinedMembers() - 1,
|
||||
resolveRoomMember(otherMembersSubset[0], roomMembers),
|
||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||
roomMembers.getNumberOfJoinedMembers() - 1)
|
||||
}
|
||||
}
|
||||
@ -122,8 +124,8 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
||||
return name ?: roomId
|
||||
}
|
||||
|
||||
private fun resolveRoomMember(eventEntity: EventEntity?,
|
||||
roomMembers: RoomMembers): String? {
|
||||
private fun resolveRoomMemberName(eventEntity: EventEntity?,
|
||||
roomMembers: RoomMembers): String? {
|
||||
if (eventEntity == null) return null
|
||||
val roomMember = eventEntity.toRoomMember() ?: return null
|
||||
val isUnique = roomMembers.isUniqueDisplayName(roomMember.displayName)
|
||||
|
@ -42,12 +42,16 @@ internal class RoomMembers(private val realm: Realm,
|
||||
RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
}
|
||||
|
||||
fun get(userId: String): RoomMember? {
|
||||
fun getStateEvent(userId: String): EventEntity? {
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
.equalTo(EventEntityFields.STATE_KEY, userId)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
fun get(userId: String): RoomMember? {
|
||||
return getStateEvent(userId)
|
||||
?.let {
|
||||
it.asDomain().content?.toModel<RoomMember>()
|
||||
}
|
||||
|
@ -17,10 +17,15 @@
|
||||
package im.vector.matrix.android.internal.session.room.membership.joining
|
||||
|
||||
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.session.SessionScope
|
||||
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 io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
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> {
|
||||
return executeRequest {
|
||||
return executeRequest<Unit> {
|
||||
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.internal.database.model.ChunkEntity
|
||||
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.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.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.fetchCopied
|
||||
import javax.inject.Inject
|
||||
|
||||
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 {
|
||||
|
||||
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
|
||||
//TODO shouldn't it be latest synced event?
|
||||
val latestEvent = getLatestEvent()
|
||||
val params = SetReadMarkersTask.Params(roomId, fullyReadEventId = latestEvent?.eventId, readReceiptEventId = latestEvent?.eventId)
|
||||
val params = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
||||
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)
|
||||
}
|
||||
|
||||
private fun getLatestEvent(): TimelineEventEntity? {
|
||||
return monarchy.fetchCopied { TimelineEventEntity.latestEvent(it, roomId) }
|
||||
}
|
||||
|
||||
override fun isEventRead(eventId: String): Boolean {
|
||||
var isEventRead = false
|
||||
|
@ -20,7 +20,6 @@ import arrow.core.Try
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
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.EventEntity
|
||||
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.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.task.Task
|
||||
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
||||
import io.realm.Realm
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -40,8 +40,9 @@ internal interface SetReadMarkersTask : Task<SetReadMarkersTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val fullyReadEventId: String?,
|
||||
val readReceiptEventId: String?
|
||||
val markAllAsRead: Boolean = false,
|
||||
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> {
|
||||
val markers = HashMap<String, String>()
|
||||
if (params.fullyReadEventId != null) {
|
||||
if (LocalEchoEventFactory.isLocalEchoId(params.fullyReadEventId)) {
|
||||
val fullyReadEventId: String?
|
||||
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}")
|
||||
} else {
|
||||
markers[READ_MARKER] = params.fullyReadEventId
|
||||
markers[READ_MARKER] = fullyReadEventId
|
||||
}
|
||||
}
|
||||
if (params.readReceiptEventId != null
|
||||
&& !isEventRead(params.roomId, params.readReceiptEventId)) {
|
||||
if (readReceiptEventId != null
|
||||
&& !isEventRead(params.roomId, readReceiptEventId)) {
|
||||
|
||||
if (LocalEchoEventFactory.isLocalEchoId(params.readReceiptEventId)) {
|
||||
Timber.w("Can't set read marker for local event ${params.fullyReadEventId}")
|
||||
if (LocalEchoEventFactory.isLocalEchoId(readReceiptEventId)) {
|
||||
Timber.w("Can't set read receipt for local event ${params.fullyReadEventId}")
|
||||
} else {
|
||||
updateNotificationCountIfNecessary(params.roomId, params.readReceiptEventId)
|
||||
markers[READ_RECEIPT] = params.readReceiptEventId
|
||||
updateNotificationCountIfNecessary(params.roomId, readReceiptEventId)
|
||||
markers[READ_RECEIPT] = readReceiptEventId
|
||||
}
|
||||
}
|
||||
return if (markers.isEmpty()) {
|
||||
@ -83,10 +98,10 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
|
||||
|
||||
private fun updateNotificationCountIfNecessary(roomId: String, eventId: String) {
|
||||
monarchy.tryTransactionAsync { realm ->
|
||||
val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId)?.eventId == eventId
|
||||
val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId == eventId
|
||||
if (isLatestReceived) {
|
||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
?: return@tryTransactionAsync
|
||||
?: return@tryTransactionAsync
|
||||
roomSummary.notificationCount = 0
|
||||
roomSummary.highlightCount = 0
|
||||
}
|
||||
@ -97,13 +112,13 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
|
||||
var isEventRead = false
|
||||
monarchy.doWithRealm {
|
||||
val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst()
|
||||
?: return@doWithRealm
|
||||
?: return@doWithRealm
|
||||
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId)
|
||||
?: return@doWithRealm
|
||||
?: return@doWithRealm
|
||||
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex
|
||||
?: Int.MIN_VALUE
|
||||
?: Int.MIN_VALUE
|
||||
val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex
|
||||
?: Int.MAX_VALUE
|
||||
?: Int.MAX_VALUE
|
||||
isEventRead = eventToCheckIndex <= readReceiptIndex
|
||||
}
|
||||
return isEventRead
|
||||
|
@ -18,27 +18,41 @@ package im.vector.matrix.android.internal.session.sync
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
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.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
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.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.EventEntityFields
|
||||
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.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.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.notification.DefaultPushRuleService
|
||||
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.membership.RoomMembers
|
||||
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.task.TaskExecutor
|
||||
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 pushRuleService: DefaultPushRuleService,
|
||||
private val processForPushTask: ProcessEventForPushTask,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val credentials: Credentials,
|
||||
private val directChatsHelper: DirectChatsHelper,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
|
||||
sealed class HandlingStrategy {
|
||||
@ -118,7 +135,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||
Timber.v("Handle join sync for room $roomId")
|
||||
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
?: realm.createObject(roomId)
|
||||
|
||||
if (roomEntity.membership == Membership.INVITE) {
|
||||
roomEntity.chunks.deleteAllFromRealm()
|
||||
@ -128,7 +145,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||
// State event
|
||||
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
|
||||
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
|
||||
?: Int.MIN_VALUE
|
||||
?: Int.MIN_VALUE
|
||||
val untimelinedStateIndex = minStateIndex + 1
|
||||
roomSync.state.events.forEach { event ->
|
||||
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
||||
@ -169,13 +186,27 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||
InvitedRoomSync): RoomEntity {
|
||||
Timber.v("Handle invited sync for room $roomId")
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
?: realm.createObject(roomId)
|
||||
roomEntity.membership = Membership.INVITE
|
||||
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
|
||||
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
|
||||
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
|
||||
}
|
||||
|
||||
@ -183,7 +214,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||
roomId: String,
|
||||
roomSync: RoomSync): RoomEntity {
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
?: realm.createObject(roomId)
|
||||
|
||||
roomEntity.membership = Membership.LEAVE
|
||||
roomEntity.chunks.deleteAllFromRealm()
|
||||
|
@ -19,8 +19,8 @@ package im.vector.matrix.android.internal.session.sync
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
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.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.UserAccountDataSync
|
||||
import javax.inject.Inject
|
||||
@ -37,19 +37,22 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
||||
}
|
||||
|
||||
private fun handleDirectChatRooms(directMessages: UserAccountDataDirectMessages) {
|
||||
val newDirectRoomIds = directMessages.content.values.flatten()
|
||||
monarchy.runTransactionSync { realm ->
|
||||
|
||||
val oldDirectRooms = RoomSummaryEntity.where(realm)
|
||||
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
||||
.findAll()
|
||||
oldDirectRooms.forEach { it.isDirect = false }
|
||||
|
||||
newDirectRoomIds.forEach { roomId ->
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
if (roomSummaryEntity != null) {
|
||||
roomSummaryEntity.isDirect = true
|
||||
realm.insertOrUpdate(roomSummaryEntity)
|
||||
val oldDirectRooms = RoomSummaryEntity.getDirectRooms(realm)
|
||||
oldDirectRooms.forEach {
|
||||
it.isDirect = false
|
||||
it.directUserId = null
|
||||
}
|
||||
directMessages.content.forEach {
|
||||
val userId = it.key
|
||||
it.value.forEach { roomId ->
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
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.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||
|
||||
@ -29,6 +30,10 @@ internal object UserEntityFactory {
|
||||
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 ?: "",
|
||||
roomMember.displayName ?: "",
|
||||
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…
Reference in New Issue
Block a user