mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Room members: introduce RoomMemberEntity to be able to query. Still work to do.
This commit is contained in:
parent
90f2199eb7
commit
00f316ba5d
@ -17,9 +17,7 @@
|
||||
package im.vector.matrix.rx
|
||||
|
||||
import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.model.*
|
||||
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
|
||||
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
@ -33,8 +31,8 @@ class RxRoom(private val room: Room) {
|
||||
return room.getRoomSummaryLive().asObservable()
|
||||
}
|
||||
|
||||
fun liveRoomMemberIds(): Observable<List<String>> {
|
||||
return room.getRoomMemberIdsLive().asObservable()
|
||||
fun liveRoomMembers(memberships: List<Membership>): Observable<List<RoomMember>> {
|
||||
return room.getRoomMembersLive(memberships).asObservable()
|
||||
}
|
||||
|
||||
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
|
||||
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room.members
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
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.util.Cancelable
|
||||
|
||||
@ -41,11 +42,11 @@ interface MembershipService {
|
||||
fun getRoomMember(userId: String): RoomMember?
|
||||
|
||||
/**
|
||||
* Return all the roomMembers ids of the room
|
||||
*
|
||||
* Return all the roomMembers of the room filtered by memberships
|
||||
* @param memberships list of accepted memberships
|
||||
* @return a [LiveData] of roomMember list.
|
||||
*/
|
||||
fun getRoomMemberIdsLive(): LiveData<List<String>>
|
||||
fun getRoomMembersLive(memberships: List<Membership>): LiveData<List<RoomMember>>
|
||||
|
||||
fun getNumberOfJoinedMembers(): Int
|
||||
|
||||
|
@ -43,4 +43,13 @@ enum class Membership(val value: String) {
|
||||
fun isLeft(): Boolean {
|
||||
return this == KNOCK || this == LEAVE || this == BAN
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun activeMemberships(): List<Membership> {
|
||||
return listOf(INVITE, JOIN)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,23 +16,12 @@
|
||||
|
||||
package im.vector.matrix.android.api.session.room.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||
|
||||
/**
|
||||
* Class representing the EventType.STATE_ROOM_MEMBER state event content
|
||||
* Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomMember(
|
||||
@Json(name = "membership") val membership: Membership,
|
||||
@Json(name = "reason") val reason: String? = null,
|
||||
@Json(name = "displayname") val displayName: String? = null,
|
||||
@Json(name = "avatar_url") val avatarUrl: String? = null,
|
||||
@Json(name = "is_direct") val isDirect: Boolean = false,
|
||||
@Json(name = "third_party_invite") val thirdPartyInvite: Invite? = null,
|
||||
@Json(name = "unsigned") val unsignedData: UnsignedData? = null
|
||||
) {
|
||||
val safeReason
|
||||
get() = reason?.takeIf { it.isNotBlank() }
|
||||
}
|
||||
val membership: Membership,
|
||||
val userId: String,
|
||||
val displayName: String? = null,
|
||||
val avatarUrl: String? = null
|
||||
)
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.api.session.room.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||
|
||||
/**
|
||||
* Class representing the EventType.STATE_ROOM_MEMBER state event content
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomMemberContent(
|
||||
@Json(name = "membership") val membership: Membership,
|
||||
@Json(name = "reason") val reason: String? = null,
|
||||
@Json(name = "displayname") val displayName: String? = null,
|
||||
@Json(name = "avatar_url") val avatarUrl: String? = null,
|
||||
@Json(name = "is_direct") val isDirect: Boolean = false,
|
||||
@Json(name = "third_party_invite") val thirdPartyInvite: Invite? = null,
|
||||
@Json(name = "unsigned") val unsignedData: UnsignedData? = null
|
||||
) {
|
||||
val safeReason
|
||||
get() = reason?.takeIf { it.isNotBlank() }
|
||||
}
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.api.util
|
||||
|
||||
import im.vector.matrix.android.BuildConfig
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
@ -146,3 +147,4 @@ fun GroupSummary.toMatrixItem() = MatrixItem.GroupItem(groupId, displayName, ava
|
||||
fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatarUrl)
|
||||
fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl)
|
||||
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name, avatarUrl)
|
||||
fun RoomMember.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
||||
|
@ -60,7 +60,7 @@ internal fun RoomEntity.addSendingEvent(event: Event) {
|
||||
this.sendState = SendState.UNSENT
|
||||
}
|
||||
val roomMembers = RoomMembers(realm, roomId)
|
||||
val myUser = roomMembers.get(senderId)
|
||||
val myUser = roomMembers.getLastRoomMember(senderId)
|
||||
val localId = TimelineEventEntity.nextId(realm)
|
||||
val timelineEventEntity = TimelineEventEntity(localId).also {
|
||||
it.root = eventEntity
|
||||
@ -69,7 +69,6 @@ internal fun RoomEntity.addSendingEvent(event: Event) {
|
||||
it.senderName = myUser?.displayName
|
||||
it.senderAvatar = myUser?.avatarUrl
|
||||
it.isUniqueDisplayName = roomMembers.isUniqueDisplayName(myUser?.displayName)
|
||||
it.senderMembershipEvent = roomMembers.queryRoomMemberEvent(senderId).findFirst()
|
||||
}
|
||||
sendingTimelineEvents.add(0, timelineEventEntity)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.database.helper
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.query.next
|
||||
@ -64,7 +65,7 @@ internal fun TimelineEventEntity.updateSenderData() {
|
||||
senderRoomMemberPrevContent = senderMembershipEvent?.prevContent
|
||||
}
|
||||
|
||||
ContentMapper.map(senderRoomMemberContent).toModel<RoomMember>()?.also {
|
||||
ContentMapper.map(senderRoomMemberContent).toModel<RoomMemberContent>()?.also {
|
||||
this.senderAvatar = it.avatarUrl
|
||||
this.senderName = it.displayName
|
||||
this.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName)
|
||||
@ -72,7 +73,7 @@ internal fun TimelineEventEntity.updateSenderData() {
|
||||
|
||||
// We try to fallback on prev content if we got a room member state events with null fields
|
||||
if (root?.type == EventType.STATE_ROOM_MEMBER) {
|
||||
ContentMapper.map(senderRoomMemberPrevContent).toModel<RoomMember>()?.also {
|
||||
ContentMapper.map(senderRoomMemberPrevContent).toModel<RoomMemberContent>()?.also {
|
||||
if (this.senderAvatar == null && it.avatarUrl != null) {
|
||||
this.senderAvatar = it.avatarUrl
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.mapper
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntity
|
||||
|
||||
internal object RoomMemberMapper {
|
||||
|
||||
fun map(roomMemberEntity: RoomMemberEntity): RoomMember {
|
||||
return RoomMember(
|
||||
userId = roomMemberEntity.userId,
|
||||
avatarUrl = roomMemberEntity.avatarUrl,
|
||||
displayName = roomMemberEntity.displayName,
|
||||
membership = roomMemberEntity.membership
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun RoomMemberEntity.asDomain(): RoomMember {
|
||||
return RoomMemberMapper.map(this)
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.model
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class RoomMemberEntity(@PrimaryKey var primaryKey: String = "",
|
||||
@Index var userId: String = "",
|
||||
@Index var roomId: String = "",
|
||||
var displayName: String = "",
|
||||
var avatarUrl: String = "",
|
||||
var reason: String? = null,
|
||||
var isDirect: Boolean = false
|
||||
) : RealmObject() {
|
||||
|
||||
private var membershipStr: String = Membership.NONE.name
|
||||
var membership: Membership
|
||||
get() {
|
||||
return Membership.valueOf(membershipStr)
|
||||
}
|
||||
set(value) {
|
||||
membershipStr = value.name
|
||||
}
|
||||
|
||||
companion object
|
||||
}
|
@ -42,6 +42,7 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
||||
var breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
|
||||
var canonicalAlias: String? = null,
|
||||
var aliases: RealmList<String> = RealmList(),
|
||||
// this is required for querying
|
||||
var flatAliases: String = ""
|
||||
) : RealmObject() {
|
||||
|
||||
|
@ -49,6 +49,7 @@ import io.realm.annotations.RealmModule
|
||||
ReadMarkerEntity::class,
|
||||
UserDraftsEntity::class,
|
||||
DraftEntity::class,
|
||||
HomeServerCapabilitiesEntity::class
|
||||
HomeServerCapabilitiesEntity::class,
|
||||
RoomMemberEntity::class
|
||||
])
|
||||
internal class SessionRealmModule
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
|
||||
internal fun RoomMemberEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery<RoomMemberEntity> {
|
||||
val query = realm
|
||||
.where<RoomMemberEntity>()
|
||||
.equalTo(RoomMemberEntityFields.ROOM_ID, roomId)
|
||||
|
||||
if (userId != null) {
|
||||
query.equalTo(RoomMemberEntityFields.USER_ID, userId)
|
||||
}
|
||||
return query
|
||||
}
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
@ -47,19 +48,16 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona
|
||||
return@doWithRealm
|
||||
}
|
||||
val roomMembers = RoomMembers(realm, roomId)
|
||||
val members = roomMembers.queryRoomMembersEvent().findAll()
|
||||
val members = roomMembers.queryActiveRoomMembersEvent().findAll()
|
||||
// detect if it is a room with no more than 2 members (i.e. an alone or a 1:1 chat)
|
||||
if (members.size == 1) {
|
||||
res = members.firstOrNull()?.toRoomMember()?.avatarUrl
|
||||
res = members.firstOrNull()?.avatarUrl
|
||||
} else if (members.size == 2) {
|
||||
val firstOtherMember = members.where().notEqualTo(EventEntityFields.STATE_KEY, userId).findFirst()
|
||||
res = firstOtherMember?.toRoomMember()?.avatarUrl
|
||||
val firstOtherMember = members.where().notEqualTo(RoomMemberEntityFields.USER_ID, userId).findFirst()
|
||||
res = firstOtherMember?.avatarUrl
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
private fun EventEntity?.toRoomMember(): RoomMember? {
|
||||
return ContentMapper.map(this?.content).toModel<RoomMember>()
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.query.*
|
||||
@ -113,10 +113,10 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
||||
if (updateMembers) {
|
||||
val otherRoomMembers = RoomMembers(realm, roomId)
|
||||
.queryRoomMembersEvent()
|
||||
.notEqualTo(EventEntityFields.STATE_KEY, userId)
|
||||
.notEqualTo(RoomMemberEntityFields.USER_ID, userId)
|
||||
.findAll()
|
||||
.asSequence()
|
||||
.map { it.stateKey }
|
||||
.map { it.userId }
|
||||
|
||||
roomSummaryEntity.otherMemberIds.clear()
|
||||
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
|
||||
|
@ -21,7 +21,6 @@ import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.members.MembershipService
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
@ -33,6 +32,7 @@ import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRo
|
||||
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 io.realm.Realm
|
||||
|
||||
internal class DefaultMembershipService @AssistedInject constructor(@Assisted private val roomId: String,
|
||||
private val monarchy: Monarchy,
|
||||
@ -58,29 +58,27 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr
|
||||
}
|
||||
|
||||
override fun getRoomMember(userId: String): RoomMember? {
|
||||
val eventEntity = monarchy.fetchCopied {
|
||||
RoomMembers(it, roomId).queryRoomMemberEvent(userId).findFirst()
|
||||
val roomMemberEntity = monarchy.fetchCopied {
|
||||
RoomMembers(it, roomId).getLastRoomMember(userId)
|
||||
}
|
||||
return eventEntity?.asDomain()?.content.toModel()
|
||||
return roomMemberEntity?.asDomain()
|
||||
}
|
||||
|
||||
override fun getRoomMemberIdsLive(): LiveData<List<String>> {
|
||||
override fun getRoomMembersLive(memberships: List<Membership>): LiveData<List<RoomMember>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{
|
||||
RoomMembers(it, roomId).queryRoomMembersEvent()
|
||||
},
|
||||
{
|
||||
it.stateKey!!
|
||||
it.asDomain()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getNumberOfJoinedMembers(): Int {
|
||||
var result = 0
|
||||
monarchy.runTransactionSync {
|
||||
result = RoomMembers(it, roomId).getNumberOfJoinedMembers()
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use {
|
||||
RoomMembers(it, roomId).getNumberOfJoinedMembers()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun invite(userId: String, reason: String?, callback: MatrixCallback<Unit>): Cancelable {
|
||||
|
@ -44,7 +44,8 @@ internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Unit>
|
||||
internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAPI: RoomAPI,
|
||||
private val monarchy: Monarchy,
|
||||
private val syncTokenStore: SyncTokenStore,
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val roomMemberEventHandler: RoomMemberEventHandler
|
||||
) : LoadRoomMembersTask {
|
||||
|
||||
override suspend fun execute(params: LoadRoomMembersTask.Params) {
|
||||
@ -66,9 +67,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP
|
||||
|
||||
for (roomMemberEvent in response.roomMemberEvents) {
|
||||
roomEntity.addStateEvent(roomMemberEvent)
|
||||
UserEntityFactory.createOrNull(roomMemberEvent)?.also {
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
roomMemberEventHandler.handle(realm, roomId, roomMemberEvent)
|
||||
}
|
||||
roomEntity.chunks.flatMap { it.timelineEvents }.forEach {
|
||||
it.updateSenderData()
|
||||
|
@ -23,9 +23,10 @@ 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.*
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntity
|
||||
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
|
||||
@ -75,43 +76,46 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
||||
}
|
||||
|
||||
val roomMembers = RoomMembers(realm, roomId)
|
||||
val loadedMembers = roomMembers.queryRoomMembersEvent().findAll()
|
||||
val activeMembers = roomMembers.queryActiveRoomMembersEvent().findAll()
|
||||
|
||||
if (roomEntity?.membership == Membership.INVITE) {
|
||||
val inviteMeEvent = roomMembers.queryRoomMemberEvent(userId).findFirst()
|
||||
val inviteMeEvent = roomMembers.getLastStateEvent(userId)
|
||||
val inviterId = inviteMeEvent?.sender
|
||||
name = if (inviterId != null) {
|
||||
val inviterMemberEvent = loadedMembers.where()
|
||||
.equalTo(EventEntityFields.STATE_KEY, inviterId)
|
||||
activeMembers.where()
|
||||
.equalTo(RoomMemberEntityFields.USER_ID, inviterId)
|
||||
.findFirst()
|
||||
inviterMemberEvent?.toRoomMember()?.displayName
|
||||
?.displayName
|
||||
} else {
|
||||
context.getString(R.string.room_displayname_room_invite)
|
||||
}
|
||||
} else if (roomEntity?.membership == Membership.JOIN) {
|
||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
val otherMembersSubset: List<EventEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
||||
roomSummary.heroes.mapNotNull {
|
||||
roomMembers.getStateEvent(it)
|
||||
val otherMembersSubset: List<RoomMemberEntity> = if (roomSummary?.heroes?.isNotEmpty() == true) {
|
||||
roomSummary.heroes.mapNotNull { userId ->
|
||||
roomMembers.getLastRoomMember(userId)?.takeIf {
|
||||
it.membership == Membership.INVITE || it.membership == Membership.JOIN
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loadedMembers.where()
|
||||
.notEqualTo(EventEntityFields.STATE_KEY, userId)
|
||||
activeMembers.where()
|
||||
.notEqualTo(RoomMemberEntityFields.USER_ID, userId)
|
||||
.limit(3)
|
||||
.findAll()
|
||||
.createSnapshot()
|
||||
}
|
||||
val otherMembersCount = roomMembers.getNumberOfMembers() - 1
|
||||
val otherMembersCount = otherMembersSubset.count()
|
||||
name = when (otherMembersCount) {
|
||||
0 -> context.getString(R.string.room_displayname_empty_room)
|
||||
1 -> resolveRoomMemberName(otherMembersSubset[0], roomMembers)
|
||||
2 -> context.getString(R.string.room_displayname_two_members,
|
||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||
resolveRoomMemberName(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,
|
||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||
roomMembers.getNumberOfJoinedMembers() - 1)
|
||||
roomMembers.getNumberOfJoinedMembers() - 1,
|
||||
resolveRoomMemberName(otherMembersSubset[0], roomMembers),
|
||||
roomMembers.getNumberOfJoinedMembers() - 1)
|
||||
}
|
||||
}
|
||||
return@doWithRealm
|
||||
@ -119,19 +123,14 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
|
||||
return name ?: roomId
|
||||
}
|
||||
|
||||
private fun resolveRoomMemberName(eventEntity: EventEntity?,
|
||||
private fun resolveRoomMemberName(roomMember: RoomMemberEntity?,
|
||||
roomMembers: RoomMembers): String? {
|
||||
if (eventEntity == null) return null
|
||||
val roomMember = eventEntity.toRoomMember() ?: return null
|
||||
if (roomMember == null) return null
|
||||
val isUnique = roomMembers.isUniqueDisplayName(roomMember.displayName)
|
||||
return if (isUnique) {
|
||||
roomMember.displayName
|
||||
} else {
|
||||
"${roomMember.displayName} (${eventEntity.stateKey})"
|
||||
"${roomMember.displayName} (${roomMember.userId})"
|
||||
}
|
||||
}
|
||||
|
||||
private fun EventEntity?.toRoomMember(): RoomMember? {
|
||||
return ContentMapper.map(this?.content).toModel<RoomMember>()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.room.membership
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntity
|
||||
|
||||
internal object RoomMemberEntityFactory {
|
||||
|
||||
fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberEntity {
|
||||
val primaryKey = "${roomId}_${userId}"
|
||||
return RoomMemberEntity(
|
||||
primaryKey = primaryKey,
|
||||
userId = userId,
|
||||
roomId = roomId,
|
||||
displayName = roomMember.displayName ?: "",
|
||||
avatarUrl = roomMember.avatarUrl ?: ""
|
||||
).apply {
|
||||
membership = roomMember.membership
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.room.membership
|
||||
|
||||
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.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.session.user.UserEntityFactory
|
||||
import io.realm.Realm
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomMemberEventHandler @Inject constructor() {
|
||||
|
||||
fun handle(realm: Realm, roomId: String, event: Event): Boolean {
|
||||
if (event.type != EventType.STATE_ROOM_MEMBER) {
|
||||
return false
|
||||
}
|
||||
val roomMember = event.content.toModel<RoomMemberContent>() ?: return false
|
||||
val userId = event.stateKey ?: return false
|
||||
val roomMemberEntity = RoomMemberEntityFactory.create(roomId, userId, roomMember)
|
||||
realm.insertOrUpdate(roomMemberEntity)
|
||||
if (roomMember.membership == Membership.JOIN || roomMember.membership == Membership.INVITE) {
|
||||
val userEntity = UserEntityFactory.create(userId, roomMember)
|
||||
realm.insertOrUpdate(userEntity)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -21,8 +21,9 @@ 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.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.RoomMemberEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import io.realm.Realm
|
||||
@ -42,19 +43,18 @@ internal class RoomMembers(private val realm: Realm,
|
||||
RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
}
|
||||
|
||||
fun getStateEvent(userId: String): EventEntity? {
|
||||
fun getLastStateEvent(userId: String): EventEntity? {
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
.equalTo(EventEntityFields.STATE_KEY, userId)
|
||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
fun get(userId: String): RoomMember? {
|
||||
return getStateEvent(userId)
|
||||
?.let {
|
||||
it.asDomain().content?.toModel<RoomMember>()
|
||||
}
|
||||
fun getLastRoomMember(userId: String): RoomMemberEntity? {
|
||||
return RoomMemberEntity
|
||||
.where(realm, roomId, userId)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
fun isUniqueDisplayName(displayName: String?): Boolean {
|
||||
@ -69,36 +69,35 @@ internal class RoomMembers(private val realm: Realm,
|
||||
.size == 1
|
||||
}
|
||||
|
||||
fun queryRoomMembersEvent(): RealmQuery<EventEntity> {
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
|
||||
.isNotNull(EventEntityFields.STATE_KEY)
|
||||
.distinct(EventEntityFields.STATE_KEY)
|
||||
.isNotNull(EventEntityFields.CONTENT)
|
||||
fun queryRoomMembersEvent(): RealmQuery<RoomMemberEntity> {
|
||||
return RoomMemberEntity.where(realm, roomId)
|
||||
}
|
||||
|
||||
fun queryJoinedRoomMembersEvent(): RealmQuery<EventEntity> {
|
||||
return queryRoomMembersEvent().contains(EventEntityFields.CONTENT, "\"membership\":\"join\"")
|
||||
fun queryJoinedRoomMembersEvent(): RealmQuery<RoomMemberEntity> {
|
||||
return queryRoomMembersEvent().equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
|
||||
}
|
||||
|
||||
fun queryInvitedRoomMembersEvent(): RealmQuery<EventEntity> {
|
||||
return queryRoomMembersEvent().contains(EventEntityFields.CONTENT, "\"membership\":\"invite\"")
|
||||
fun queryInvitedRoomMembersEvent(): RealmQuery<RoomMemberEntity> {
|
||||
return queryRoomMembersEvent().equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name)
|
||||
}
|
||||
|
||||
fun queryRoomMemberEvent(userId: String): RealmQuery<EventEntity> {
|
||||
fun queryActiveRoomMembersEvent(): RealmQuery<RoomMemberEntity> {
|
||||
return queryRoomMembersEvent()
|
||||
.equalTo(EventEntityFields.STATE_KEY, userId)
|
||||
.beginGroup()
|
||||
.equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name)
|
||||
.or()
|
||||
.equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
|
||||
.endGroup()
|
||||
}
|
||||
|
||||
fun getNumberOfJoinedMembers(): Int {
|
||||
return roomSummary?.joinedMembersCount
|
||||
?: queryJoinedRoomMembersEvent().findAll().size
|
||||
?: queryJoinedRoomMembersEvent().findAll().size
|
||||
}
|
||||
|
||||
fun getNumberOfInvitedMembers(): Int {
|
||||
return roomSummary?.invitedMembersCount
|
||||
?: queryInvitedRoomMembersEvent().findAll().size
|
||||
?: queryInvitedRoomMembersEvent().findAll().size
|
||||
}
|
||||
|
||||
fun getNumberOfMembers(): Int {
|
||||
@ -111,7 +110,7 @@ internal class RoomMembers(private val realm: Realm,
|
||||
* @return a roomMember id list of joined or invited members.
|
||||
*/
|
||||
fun getActiveRoomMemberIds(): List<String> {
|
||||
return getRoomMemberIdsFiltered { it.membership == Membership.JOIN || it.membership == Membership.INVITE }
|
||||
return queryActiveRoomMembersEvent().findAll().map { it.userId }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,21 +119,6 @@ internal class RoomMembers(private val realm: Realm,
|
||||
* @return a roomMember id list of joined members.
|
||||
*/
|
||||
fun getJoinedRoomMemberIds(): List<String> {
|
||||
return getRoomMemberIdsFiltered { it.membership == Membership.JOIN }
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Private
|
||||
* ========================================================================================== */
|
||||
|
||||
private fun getRoomMemberIdsFiltered(predicate: (RoomMember) -> Boolean): List<String> {
|
||||
return RoomMembers(realm, roomId)
|
||||
.queryRoomMembersEvent()
|
||||
.findAll()
|
||||
.map { it.asDomain() }
|
||||
.associateBy { it.stateKey!! }
|
||||
.filterValues { predicate(it.content.toModel<RoomMember>()!!) }
|
||||
.keys
|
||||
.toList()
|
||||
return queryJoinedRoomMembersEvent().findAll().map { it.userId }
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import im.vector.matrix.android.internal.database.query.create
|
||||
import im.vector.matrix.android.internal.database.query.find
|
||||
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberEventHandler
|
||||
import im.vector.matrix.android.internal.session.user.UserEntityFactory
|
||||
import im.vector.matrix.android.internal.util.awaitTransaction
|
||||
import io.realm.kotlin.createObject
|
||||
@ -154,9 +155,6 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
|
||||
for (event in receivedChunk.events) {
|
||||
event.eventId?.also { eventIds.add(it) }
|
||||
currentChunk.add(roomId, event, direction, isUnlinked = currentChunk.isUnlinked())
|
||||
UserEntityFactory.createOrNull(event)?.also {
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
}
|
||||
// Then we merge chunks if needed
|
||||
if (currentChunk != prevChunk && prevChunk != null) {
|
||||
@ -175,9 +173,6 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
|
||||
roomEntity.addOrUpdate(currentChunk)
|
||||
for (stateEvent in receivedChunk.stateEvents) {
|
||||
roomEntity.addStateEvent(stateEvent, isUnlinked = currentChunk.isUnlinked())
|
||||
UserEntityFactory.createOrNull(stateEvent)?.also {
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
}
|
||||
currentChunk.updateSenderDataFor(eventIds)
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberEventHandler
|
||||
import im.vector.matrix.android.internal.session.room.read.FullyReadContent
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||
import im.vector.matrix.android.internal.session.sync.model.*
|
||||
@ -46,7 +47,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val roomTagHandler: RoomTagHandler,
|
||||
private val roomFullyReadHandler: RoomFullyReadHandler,
|
||||
private val cryptoService: DefaultCryptoService) {
|
||||
private val cryptoService: DefaultCryptoService,
|
||||
private val roomMemberEventHandler: RoomMemberEventHandler) {
|
||||
|
||||
sealed class HandlingStrategy {
|
||||
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||
@ -119,9 +121,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
||||
// Give info to crypto module
|
||||
cryptoService.onStateEvent(roomId, event)
|
||||
UserEntityFactory.createOrNull(event)?.also {
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
roomMemberEventHandler.handle(realm, roomId, event)
|
||||
}
|
||||
}
|
||||
if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) {
|
||||
@ -206,9 +206,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
Timber.v("Can't find corresponding local echo for tx:$it")
|
||||
}
|
||||
}
|
||||
UserEntityFactory.createOrNull(event)?.also {
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
roomMemberEventHandler.handle(realm, roomEntity.roomId, event)
|
||||
}
|
||||
chunkEntity.updateSenderDataFor(eventIds)
|
||||
return chunkEntity
|
||||
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.pushrules.RuleScope
|
||||
import im.vector.matrix.android.api.pushrules.RuleSetKey
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
@ -69,9 +70,9 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
||||
var hasUpdate = false
|
||||
monarchy.doWithRealm { realm ->
|
||||
invites.forEach { (roomId, _) ->
|
||||
val myUserStateEvent = RoomMembers(realm, roomId).getStateEvent(userId)
|
||||
val myUserStateEvent = RoomMembers(realm, roomId).getLastStateEvent(userId)
|
||||
val inviterId = myUserStateEvent?.sender
|
||||
val myUserRoomMember: RoomMember? = myUserStateEvent?.let { it.asDomain().content?.toModel() }
|
||||
val myUserRoomMember: RoomMemberContent? = myUserStateEvent?.let { it.asDomain().content?.toModel() }
|
||||
val isDirect = myUserRoomMember?.isDirect
|
||||
if (inviterId != null && inviterId != userId && isDirect == true) {
|
||||
directChats
|
||||
|
@ -16,27 +16,16 @@
|
||||
|
||||
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.api.session.room.model.RoomMemberContent
|
||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||
|
||||
internal object UserEntityFactory {
|
||||
|
||||
fun createOrNull(event: Event): UserEntity? {
|
||||
if (event.type != EventType.STATE_ROOM_MEMBER) {
|
||||
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 ?: ""
|
||||
fun create(userId: String, roomMember: RoomMemberContent): UserEntity {
|
||||
return UserEntity(
|
||||
userId = userId,
|
||||
displayName = roomMember.displayName ?: "",
|
||||
avatarUrl = roomMember.avatarUrl ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -14,23 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.autocomplete.user
|
||||
package im.vector.riotx.features.autocomplete.member
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotx.features.autocomplete.autocompleteMatrixItem
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteUserController @Inject constructor() : TypedEpoxyController<List<User>>() {
|
||||
class AutocompleteMemberController @Inject constructor() : TypedEpoxyController<List<RoomMember>>() {
|
||||
|
||||
var listener: AutocompleteClickListener<User>? = null
|
||||
var listener: AutocompleteClickListener<RoomMember>? = null
|
||||
|
||||
@Inject lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
override fun buildModels(data: List<User>?) {
|
||||
override fun buildModels(data: List<RoomMember>?) {
|
||||
if (data.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
@ -14,20 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.autocomplete.user
|
||||
package im.vector.riotx.features.autocomplete.member
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.otaliastudios.autocomplete.RecyclerViewPresenter
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.riotx.features.autocomplete.AutocompleteClickListener
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteUserPresenter @Inject constructor(context: Context,
|
||||
private val controller: AutocompleteUserController
|
||||
) : RecyclerViewPresenter<User>(context), AutocompleteClickListener<User> {
|
||||
class AutocompleteMemberPresenter @Inject constructor(context: Context,
|
||||
private val controller: AutocompleteMemberController
|
||||
) : RecyclerViewPresenter<RoomMember>(context), AutocompleteClickListener<RoomMember> {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
@ -41,21 +41,21 @@ class AutocompleteUserPresenter @Inject constructor(context: Context,
|
||||
return controller.adapter
|
||||
}
|
||||
|
||||
override fun onItemClick(t: User) {
|
||||
override fun onItemClick(t: RoomMember) {
|
||||
dispatchClick(t)
|
||||
}
|
||||
|
||||
override fun onQuery(query: CharSequence?) {
|
||||
callback?.onQueryUsers(query)
|
||||
callback?.onQueryMembers(query)
|
||||
}
|
||||
|
||||
fun render(users: Async<List<User>>) {
|
||||
if (users is Success) {
|
||||
controller.setData(users())
|
||||
fun render(members: Async<List<RoomMember>>) {
|
||||
if (members is Success) {
|
||||
controller.setData(members())
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onQueryUsers(query: CharSequence?)
|
||||
fun onQueryMembers(query: CharSequence?)
|
||||
}
|
||||
}
|
@ -61,6 +61,7 @@ import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
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.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
@ -88,7 +89,7 @@ import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresente
|
||||
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
|
||||
import im.vector.riotx.features.autocomplete.group.AutocompleteGroupPresenter
|
||||
import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter
|
||||
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
||||
import im.vector.riotx.features.autocomplete.member.AutocompleteMemberPresenter
|
||||
import im.vector.riotx.features.command.Command
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.home.getColorFromUserId
|
||||
@ -144,7 +145,7 @@ class RoomDetailFragment @Inject constructor(
|
||||
private val timelineEventController: TimelineEventController,
|
||||
private val commandAutocompletePolicy: CommandAutocompletePolicy,
|
||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter,
|
||||
private val autocompleteUserPresenter: AutocompleteUserPresenter,
|
||||
private val autocompleteMemberPresenter: AutocompleteMemberPresenter,
|
||||
private val autocompleteRoomPresenter: AutocompleteRoomPresenter,
|
||||
private val autocompleteGroupPresenter: AutocompleteGroupPresenter,
|
||||
private val permalinkHandler: PermalinkHandler,
|
||||
@ -156,7 +157,7 @@ class RoomDetailFragment @Inject constructor(
|
||||
) :
|
||||
VectorBaseFragment(),
|
||||
TimelineEventController.Callback,
|
||||
AutocompleteUserPresenter.Callback,
|
||||
AutocompleteMemberPresenter.Callback,
|
||||
AutocompleteRoomPresenter.Callback,
|
||||
AutocompleteGroupPresenter.Callback,
|
||||
VectorInviteView.Callback,
|
||||
@ -693,14 +694,14 @@ class RoomDetailFragment @Inject constructor(
|
||||
})
|
||||
.build()
|
||||
|
||||
autocompleteUserPresenter.callback = this
|
||||
Autocomplete.on<User>(composerLayout.composerEditText)
|
||||
autocompleteMemberPresenter.callback = this
|
||||
Autocomplete.on<RoomMember>(composerLayout.composerEditText)
|
||||
.with(CharPolicy('@', true))
|
||||
.with(autocompleteUserPresenter)
|
||||
.with(autocompleteMemberPresenter)
|
||||
.with(elevation)
|
||||
.with(backgroundDrawable)
|
||||
.with(object : AutocompleteCallback<User> {
|
||||
override fun onPopupItemClicked(editable: Editable, item: User): Boolean {
|
||||
.with(object : AutocompleteCallback<RoomMember> {
|
||||
override fun onPopupItemClicked(editable: Editable, item: RoomMember): Boolean {
|
||||
// Detect last '@' and remove it
|
||||
var startIndex = editable.lastIndexOf("@")
|
||||
if (startIndex == -1) {
|
||||
@ -834,7 +835,7 @@ class RoomDetailFragment @Inject constructor(
|
||||
}
|
||||
|
||||
private fun renderTextComposerState(state: TextComposerViewState) {
|
||||
autocompleteUserPresenter.render(state.asyncUsers)
|
||||
autocompleteMemberPresenter.render(state.asyncMembers)
|
||||
autocompleteRoomPresenter.render(state.asyncRooms)
|
||||
autocompleteGroupPresenter.render(state.asyncGroups)
|
||||
}
|
||||
@ -1163,9 +1164,9 @@ class RoomDetailFragment @Inject constructor(
|
||||
roomDetailViewModel.handle(RoomDetailAction.EnterTrackingUnreadMessagesState)
|
||||
}
|
||||
|
||||
// AutocompleteUserPresenter.Callback
|
||||
// AutocompleteMemberPresenter.Callback
|
||||
|
||||
override fun onQueryUsers(query: CharSequence?) {
|
||||
override fun onQueryMembers(query: CharSequence?) {
|
||||
textComposerViewModel.handle(TextComposerAction.QueryUsers(query))
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,8 @@ import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
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.RoomSummary
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.rx.rx
|
||||
@ -90,17 +92,15 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
}
|
||||
|
||||
private fun observeUsersQuery() {
|
||||
Observable.combineLatest<List<String>, Option<AutocompleteQuery>, List<User>>(
|
||||
room.rx().liveRoomMemberIds(),
|
||||
Observable.combineLatest<List<RoomMember>, Option<AutocompleteQuery>, List<RoomMember>>(
|
||||
room.rx().liveRoomMembers(Membership.activeMemberships()),
|
||||
usersQueryObservable.throttleLast(300, TimeUnit.MILLISECONDS),
|
||||
BiFunction { roomMemberIds, query ->
|
||||
val users = roomMemberIds.mapNotNull { session.getUser(it) }
|
||||
|
||||
BiFunction { roomMembers, query ->
|
||||
val filter = query.orNull()
|
||||
if (filter.isNullOrBlank()) {
|
||||
users
|
||||
roomMembers
|
||||
} else {
|
||||
users.filter {
|
||||
roomMembers.filter {
|
||||
it.displayName?.contains(filter, ignoreCase = true) ?: false
|
||||
}
|
||||
}
|
||||
@ -108,7 +108,7 @@ class TextComposerViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
}
|
||||
).execute { async ->
|
||||
copy(
|
||||
asyncUsers = async
|
||||
asyncMembers = async
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,13 @@ import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||
|
||||
data class TextComposerViewState(val roomId: String,
|
||||
val asyncUsers: Async<List<User>> = Uninitialized,
|
||||
val asyncMembers: Async<List<RoomMember>> = Uninitialized,
|
||||
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
||||
val asyncGroups: Async<List<GroupSummary>> = Uninitialized
|
||||
) : MvRxState {
|
||||
|
@ -128,8 +128,8 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
||||
}
|
||||
|
||||
private fun formatRoomMemberEvent(event: Event, senderName: String?): String? {
|
||||
val eventContent: RoomMember? = event.getClearContent().toModel()
|
||||
val prevEventContent: RoomMember? = event.prevContent.toModel()
|
||||
val eventContent: RoomMemberContent? = event.getClearContent().toModel()
|
||||
val prevEventContent: RoomMemberContent? = event.prevContent.toModel()
|
||||
val isMembershipEvent = prevEventContent?.membership != eventContent?.membership
|
||||
return if (isMembershipEvent) {
|
||||
buildMembershipNotice(event, senderName, eventContent, prevEventContent)
|
||||
@ -166,7 +166,7 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
||||
?: sp.getString(R.string.notice_room_canonical_alias_unset, senderName)
|
||||
}
|
||||
|
||||
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String {
|
||||
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMemberContent?, prevEventContent: RoomMemberContent?): String {
|
||||
val displayText = StringBuilder()
|
||||
// Check display name has been changed
|
||||
if (eventContent?.displayName != prevEventContent?.displayName) {
|
||||
@ -198,7 +198,7 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
|
||||
return displayText.toString()
|
||||
}
|
||||
|
||||
private fun buildMembershipNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
private fun buildMembershipNotice(event: Event, senderName: String?, eventContent: RoomMemberContent?, prevEventContent: RoomMemberContent?): String? {
|
||||
val senderDisplayName = senderName ?: event.senderId ?: ""
|
||||
val targetDisplayName = eventContent?.displayName ?: prevEventContent?.displayName ?: event.stateKey ?: ""
|
||||
return when (eventContent?.membership) {
|
||||
|
@ -24,6 +24,7 @@ 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.RoomMemberContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.room.timeline.getEditedEventId
|
||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageBody
|
||||
@ -163,7 +164,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
|
||||
}
|
||||
|
||||
private fun resolveStateRoomEvent(event: Event, session: Session): NotifiableEvent? {
|
||||
val content = event.content?.toModel<RoomMember>() ?: return null
|
||||
val content = event.content?.toModel<RoomMemberContent>() ?: return null
|
||||
val roomId = event.roomId ?: return null
|
||||
val dName = event.senderId?.let { session.getUser(it)?.displayName }
|
||||
if (Membership.INVITE == content.membership) {
|
||||
|
Loading…
Reference in New Issue
Block a user