diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index e5ebc536ff..bf4e924cf0 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -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> { - return room.getRoomMemberIdsLive().asObservable() + fun liveRoomMembers(memberships: List): Observable> { + return room.getRoomMembersLive(memberships).asObservable() } fun liveAnnotationSummary(eventId: String): Observable> { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt index 34af2cf572..12f0378af7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt @@ -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> + fun getRoomMembersLive(memberships: List): LiveData> fun getNumberOfJoinedMembers(): Int diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt index 1894effc7a..4d35d3b4dd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/Membership.kt @@ -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 { + return listOf(INVITE, JOIN) + } + } + } + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt index 6a4d8e3c94..994c27be4d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt @@ -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 +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberContent.kt new file mode 100644 index 0000000000..deeeb8ba52 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberContent.kt @@ -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() } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt index 4c8082b77e..d6ef522f41 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt @@ -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) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt index 948af2af96..19c4715faa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt @@ -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) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt index 36ed2f7edf..c5e2aef910 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt @@ -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()?.also { + ContentMapper.map(senderRoomMemberContent).toModel()?.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()?.also { + ContentMapper.map(senderRoomMemberPrevContent).toModel()?.also { if (this.senderAvatar == null && it.avatarUrl != null) { this.senderAvatar = it.avatarUrl } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt new file mode 100644 index 0000000000..a458c5e506 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt @@ -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) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt new file mode 100644 index 0000000000..c532857fe1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt @@ -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 +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt index 406c8700b6..2fa892d874 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -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 = RealmList(), + // this is required for querying var flatAliases: String = "" ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt index 6059d3faf7..07ff1df005 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt @@ -49,6 +49,7 @@ import io.realm.annotations.RealmModule ReadMarkerEntity::class, UserDraftsEntity::class, DraftEntity::class, - HomeServerCapabilitiesEntity::class + HomeServerCapabilitiesEntity::class, + RoomMemberEntity::class ]) internal class SessionRealmModule diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt new file mode 100644 index 0000000000..3eb1da87a1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomMemberEntityQueries.kt @@ -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 { + val query = realm + .where() + .equalTo(RoomMemberEntityFields.ROOM_ID, roomId) + + if (userId != null) { + query.equalTo(RoomMemberEntityFields.USER_ID, userId) + } + return query +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt index c9d5aeb6bb..acfb57bd46 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAvatarResolver.kt @@ -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() - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index e9f33a547b..536484de5a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -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) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index 00c1c2c4ca..2b89f86d3e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -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> { + override fun getRoomMembersLive(memberships: List): LiveData> { 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): Cancelable { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt index 7d9332ee84..6affd120a8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt @@ -44,7 +44,8 @@ internal interface LoadRoomMembersTask : Task 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() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt index 21270308ed..9382fbc54a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -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 = if (roomSummary?.heroes?.isNotEmpty() == true) { - roomSummary.heroes.mapNotNull { - roomMembers.getStateEvent(it) + val otherMembersSubset: List = 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() - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt new file mode 100644 index 0000000000..933431b77f --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEntityFactory.kt @@ -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 + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEventHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEventHandler.kt new file mode 100644 index 0000000000..73c4c8cdc1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberEventHandler.kt @@ -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() ?: 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 + } + + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt index 9fba1d8f02..ca397a0608 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt @@ -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() - } + 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 { - 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 { + return RoomMemberEntity.where(realm, roomId) } - fun queryJoinedRoomMembersEvent(): RealmQuery { - return queryRoomMembersEvent().contains(EventEntityFields.CONTENT, "\"membership\":\"join\"") + fun queryJoinedRoomMembersEvent(): RealmQuery { + return queryRoomMembersEvent().equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) } - fun queryInvitedRoomMembersEvent(): RealmQuery { - return queryRoomMembersEvent().contains(EventEntityFields.CONTENT, "\"membership\":\"invite\"") + fun queryInvitedRoomMembersEvent(): RealmQuery { + return queryRoomMembersEvent().equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) } - fun queryRoomMemberEvent(userId: String): RealmQuery { + fun queryActiveRoomMembersEvent(): RealmQuery { 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 { - 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 { - return getRoomMemberIdsFiltered { it.membership == Membership.JOIN } - } - - /* ========================================================================================== - * Private - * ========================================================================================== */ - - private fun getRoomMemberIdsFiltered(predicate: (RoomMember) -> Boolean): List { - return RoomMembers(realm, roomId) - .queryRoomMembersEvent() - .findAll() - .map { it.asDomain() } - .associateBy { it.stateKey!! } - .filterValues { predicate(it.content.toModel()!!) } - .keys - .toList() + return queryJoinedRoomMembersEvent().findAll().map { it.userId } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt index 7030509bfc..3727d1f5b0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -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) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index a080a5158e..ea59cc1fd2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -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) : 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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt index 35988e6c6f..571c9b4c27 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityFactory.kt index 2ded32b7db..f931db1cff 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserEntityFactory.kt @@ -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() ?: 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 ?: "" ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/autocomplete/user/AutocompleteUserController.kt b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt similarity index 80% rename from vector/src/main/java/im/vector/riotx/features/autocomplete/user/AutocompleteUserController.kt rename to vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt index 53a87fe27a..1c8dc99196 100644 --- a/vector/src/main/java/im/vector/riotx/features/autocomplete/user/AutocompleteUserController.kt +++ b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt @@ -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>() { +class AutocompleteMemberController @Inject constructor() : TypedEpoxyController>() { - var listener: AutocompleteClickListener? = null + var listener: AutocompleteClickListener? = null @Inject lateinit var avatarRenderer: AvatarRenderer - override fun buildModels(data: List?) { + override fun buildModels(data: List?) { if (data.isNullOrEmpty()) { return } diff --git a/vector/src/main/java/im/vector/riotx/features/autocomplete/user/AutocompleteUserPresenter.kt b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt similarity index 66% rename from vector/src/main/java/im/vector/riotx/features/autocomplete/user/AutocompleteUserPresenter.kt rename to vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt index 01dceb5399..b39d1d9b44 100644 --- a/vector/src/main/java/im/vector/riotx/features/autocomplete/user/AutocompleteUserPresenter.kt +++ b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -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(context), AutocompleteClickListener { +class AutocompleteMemberPresenter @Inject constructor(context: Context, + private val controller: AutocompleteMemberController +) : RecyclerViewPresenter(context), AutocompleteClickListener { 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>) { - if (users is Success) { - controller.setData(users()) + fun render(members: Async>) { + if (members is Success) { + controller.setData(members()) } } interface Callback { - fun onQueryUsers(query: CharSequence?) + fun onQueryMembers(query: CharSequence?) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 334445870c..eff9df9d18 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -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(composerLayout.composerEditText) + autocompleteMemberPresenter.callback = this + Autocomplete.on(composerLayout.composerEditText) .with(CharPolicy('@', true)) - .with(autocompleteUserPresenter) + .with(autocompleteMemberPresenter) .with(elevation) .with(backgroundDrawable) - .with(object : AutocompleteCallback { - override fun onPopupItemClicked(editable: Editable, item: User): Boolean { + .with(object : AutocompleteCallback { + 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)) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewModel.kt index f7ec78c6c4..25f92d4ae2 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewModel.kt @@ -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, Option, List>( - room.rx().liveRoomMemberIds(), + Observable.combineLatest, Option, List>( + 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 ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewState.kt index e863970afe..0320818f67 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerViewState.kt @@ -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> = Uninitialized, + val asyncMembers: Async> = Uninitialized, val asyncRooms: Async> = Uninitialized, val asyncGroups: Async> = Uninitialized ) : MvRxState { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 9cb045c01e..a201890912 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -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) { diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt index e38e7d548a..ef620a7df3 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEventResolver.kt @@ -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() ?: return null + val content = event.content?.toModel() ?: return null val roomId = event.roomId ?: return null val dName = event.senderId?.let { session.getUser(it)?.displayName } if (Membership.INVITE == content.membership) {