From 171ec4fbdc7081a4e5ad2550071eeb37c395be1e Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 10 Jan 2020 17:03:11 +0100 Subject: [PATCH] Room member list: group by power level --- .../java/im/vector/matrix/rx/OptionalRx.kt | 6 + .../main/java/im/vector/matrix/rx/RxRoom.kt | 10 +- .../session/room/timeline/RoomDataHelper.kt | 4 +- .../SenderNotificationPermissionCondition.kt | 8 +- .../session/room/members/MembershipService.kt | 8 +- .../api/session/room/model/PowerLevels.kt | 121 ------------------ .../session/room/model/PowerLevelsContent.kt | 39 ++++++ .../{RoomMember.kt => RoomMemberSummary.kt} | 2 +- .../room/model/create/CreateRoomParams.kt | 4 +- .../room/powerlevers/PowerLevelsConstants.kt | 26 ++++ .../room/powerlevers/PowerLevelsHelper.kt | 77 +++++++++++ .../api/session/room/state/StateService.kt | 4 + .../matrix/android/api/util/MatrixItem.kt | 4 +- .../matrix/android/api/util/Optional.kt | 8 ++ .../internal/crypto/DefaultCryptoService.kt | 10 +- .../database/helper/RoomEntityHelper.kt | 4 +- .../helper/TimelineEventSenderVisitor.kt | 6 +- ...erMapper.kt => RoomMemberSummaryMapper.kt} | 22 ++-- ...erEntity.kt => RoomMemberSummaryEntity.kt} | 14 +- .../database/model/SessionRealmModule.kt | 2 +- .../database/query/EventEntityQueries.kt | 38 +++--- .../database/query/RoomMemberEntityQueries.kt | 12 +- .../session/room/RoomAvatarResolver.kt | 8 +- .../session/room/RoomSummaryUpdater.kt | 6 +- .../membership/DefaultMembershipService.kt | 24 ++-- .../membership/RoomDisplayNameResolver.kt | 22 ++-- .../membership/RoomMemberEntityFactory.kt | 6 +- .../{RoomMembers.kt => RoomMemberHelper.kt} | 28 ++-- .../session/room/state/DefaultStateService.kt | 25 +++- .../sync/UserAccountDataSyncHandler.kt | 4 +- .../epoxy/profiles/ProfileItemExtensions.kt | 55 ++++++++ .../member/AutocompleteMemberController.kt | 8 +- .../member/AutocompleteMemberPresenter.kt | 6 +- .../home/room/detail/AutoCompleter.kt | 8 +- .../home/room/detail/RoomDetailViewModel.kt | 4 +- .../home/room/detail/RoomDetailViewState.kt | 1 + .../roomprofile/RoomProfileController.kt | 50 ++------ .../members/RoomMemberListController.kt | 39 ++++-- .../members/RoomMemberListFragment.kt | 4 +- .../members/RoomMemberListViewModel.kt | 71 ++++++++-- .../members/RoomMemberListViewState.kt | 16 ++- .../main/res/layout/fragment_room_profile.xml | 2 +- vector/src/main/res/values/strings_riotX.xml | 6 + 43 files changed, 498 insertions(+), 324 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/{RoomMember.kt => RoomMemberSummary.kt} (96%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/{RoomMemberMapper.kt => RoomMemberSummaryMapper.kt} (59%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/{RoomMemberEntity.kt => RoomMemberSummaryEntity.kt} (67%) rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/{RoomMembers.kt => RoomMemberHelper.kt} (78%) create mode 100644 vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt index d608837d4a..a8503ba428 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/OptionalRx.kt @@ -22,3 +22,9 @@ import io.reactivex.Observable fun Observable>.unwrap(): Observable { return filter { it.hasValue() }.map { it.get() } } + +fun Observable>.mapOptional(fn: (T) -> U?): Observable> { + return map { + it.map(fn) + } +} 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 bbf0e76823..9491a69ef1 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 @@ -16,11 +16,12 @@ package im.vector.matrix.rx +import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams 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.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.session.room.send.UserDraft @@ -37,7 +38,7 @@ class RxRoom(private val room: Room) { .startWith(room.roomSummary().toOptional()) } - fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable> { + fun liveRoomMembers(queryParams: RoomMemberQueryParams): Observable> { return room.getRoomMembersLive(queryParams).asObservable() .startWith(room.getRoomMembers(queryParams)) } @@ -52,6 +53,11 @@ class RxRoom(private val room: Room) { .startWith(room.getTimeLineEvent(eventId).toOptional()) } + fun liveStateEvent(eventType: String): Observable> { + return room.getStateEventLive(eventType).asObservable() + .startWith(room.getStateEvent(eventType).toOptional()) + } + fun liveReadMarker(): Observable> { return room.getReadMarkerLive().asObservable() } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt index dd4daee9cd..102d9b65d3 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/RoomDataHelper.kt @@ -21,7 +21,7 @@ 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.toContent 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.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType import kotlin.random.Random @@ -63,7 +63,7 @@ object RoomDataHelper { } fun createFakeRoomMemberEvent(): Event { - val roomMember = RoomMember(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() + val roomMember = RoomMemberSummary(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt index 46fc9a5c6d..657ad29c87 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/SenderNotificationPermissionCondition.kt @@ -16,7 +16,8 @@ package im.vector.matrix.android.api.pushrules import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.room.model.PowerLevels +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) { @@ -28,7 +29,8 @@ class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.se return "User power level <$key>" } - fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean { - return event.senderId != null && powerLevels.getUserPowerLevel(event.senderId) >= powerLevels.notificationLevel(key) + fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean { + val powerLevelsHelper = PowerLevelsHelper(powerLevels) + return event.senderId != null && powerLevelsHelper.getUserPowerLevel(event.senderId) >= powerLevelsHelper.notificationLevel(key) } } 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 6c117d3be7..98bce9476b 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,7 +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.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.Cancelable /** @@ -38,21 +38,21 @@ interface MembershipService { * * @return the roomMember with userId or null */ - fun getRoomMember(userId: String): RoomMember? + fun getRoomMember(userId: String): RoomMemberSummary? /** * Return all the roomMembers of the room with params * @param queryParams the params to query for * @return a roomMember list. */ - fun getRoomMembers(queryParams: RoomMemberQueryParams): List + fun getRoomMembers(queryParams: RoomMemberQueryParams): List /** * Return all the roomMembers of the room filtered by memberships * @param queryParams the params to query for * @return a [LiveData] of roomMember list. */ - fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> + fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> fun getNumberOfJoinedMembers(): Int diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt deleted file mode 100644 index 27f7820156..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevels.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.EventType - -/** - * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. - */ -@JsonClass(generateAdapter = true) -data class PowerLevels( - @Json(name = "ban") val ban: Int = 50, - @Json(name = "kick") val kick: Int = 50, - @Json(name = "invite") val invite: Int = 50, - @Json(name = "redact") val redact: Int = 50, - @Json(name = "events_default") val eventsDefault: Int = 0, - @Json(name = "events") val events: MutableMap = HashMap(), - @Json(name = "users_default") val usersDefault: Int = 0, - @Json(name = "users") val users: MutableMap = HashMap(), - @Json(name = "state_default") val stateDefault: Int = 50, - @Json(name = "notifications") val notifications: Map = HashMap() -) { - - /** - * Returns the user power level of a dedicated user Id - * - * @param userId the user id - * @return the power level - */ - fun getUserPowerLevel(userId: String): Int { - return users.getOrElse(userId) { usersDefault } - } - - /** - * Updates the user power levels of a dedicated user id - * - * @param userId the user - * @param powerLevel the new power level - */ - fun setUserPowerLevel(userId: String, powerLevel: Int) { - users[userId] = powerLevel - } - - /** - * Tell if an user can send an event of type 'eventTypeString'. - * - * @param eventTypeString the event type (in Event.EVENT_TYPE_XXX values) - * @param userId the user id - * @return true if the user can send the event - */ - fun maySendEventOfType(eventTypeString: String, userId: String): Boolean { - return if (eventTypeString.isNotEmpty() && userId.isNotEmpty()) { - getUserPowerLevel(userId) >= minimumPowerLevelForSendingEventAsMessage(eventTypeString) - } else false - } - - /** - * Tells if an user can send a room message. - * - * @param userId the user id - * @return true if the user can send a room message - */ - fun maySendMessage(userId: String): Boolean { - return maySendEventOfType(EventType.MESSAGE, userId) - } - - /** - * Helper to get the minimum power level the user must have to send an event of the given type - * as a message. - * - * @param eventTypeString the type of event (in Event.EVENT_TYPE_XXX values) - * @return the required minimum power level. - */ - fun minimumPowerLevelForSendingEventAsMessage(eventTypeString: String?): Int { - return events[eventTypeString] ?: eventsDefault - } - - /** - * Helper to get the minimum power level the user must have to send an event of the given type - * as a state event. - * - * @param eventTypeString the type of event (in Event.EVENT_TYPE_STATE_ values). - * @return the required minimum power level. - */ - fun minimumPowerLevelForSendingEventAsStateEvent(eventTypeString: String?): Int { - return events[eventTypeString] ?: stateDefault - } - - /** - * Get the notification level for a dedicated key. - * - * @param key the notification key - * @return the level - */ - fun notificationLevel(key: String): Int { - val valAsVoid = notifications[key] ?: return 50 - - // the first implementation was a string value - return if (valAsVoid is String) { - valAsVoid.toInt() - } else { - valAsVoid as Int - } - } -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt new file mode 100644 index 0000000000..3cbb435d76 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/PowerLevelsContent.kt @@ -0,0 +1,39 @@ +/* + * 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.EventType +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants + +/** + * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. + */ +@JsonClass(generateAdapter = true) +data class PowerLevelsContent( + @Json(name = "ban") val ban: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "kick") val kick: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "invite") val invite: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "redact") val redact: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "events_default") val eventsDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL, + @Json(name = "events") val events: MutableMap = HashMap(), + @Json(name = "users_default") val usersDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL, + @Json(name = "users") val users: MutableMap = HashMap(), + @Json(name = "state_default") val stateDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL, + @Json(name = "notifications") val notifications: Map = HashMap() +) 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/RoomMemberSummary.kt similarity index 96% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMember.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomMemberSummary.kt index 994c27be4d..17768362b2 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/RoomMemberSummary.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.room.model /** * Class representing a simplified version of EventType.STATE_ROOM_MEMBER state event content */ -data class RoomMember( +data class RoomMemberSummary( val membership: Membership, val userId: String, val displayName: String? = null, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index bc1e941698..74298b42d5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -24,7 +24,7 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig 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.toContent -import im.vector.matrix.android.api.session.room.model.PowerLevels +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility import im.vector.matrix.android.internal.auth.data.ThreePidMedium @@ -113,7 +113,7 @@ class CreateRoomParams { * The power level content to override in the default power level event */ @Json(name = "power_level_content_override") - var powerLevelContentOverride: PowerLevels? = null + var powerLevelContentOverride: PowerLevelsContent? = null /** * Add the crypto algorithm to the room creation parameters. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt new file mode 100644 index 0000000000..f1610ead88 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsConstants.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2020 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.powerlevers + +object PowerLevelsConstants { + + const val DEFAULT_ROOM_ADMIN_LEVEL = 100 + const val DEFAULT_ROOM_MODERATOR_LEVEL = 50 + const val DEFAULT_ROOM_USER_LEVEL = 0 + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt new file mode 100644 index 0000000000..60bc5b3fc5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevers/PowerLevelsHelper.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2020 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.powerlevers + +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent + +/** + * This class is an helper around PowerLevelsContent. + */ +class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { + + /** + * Returns the user power level of a dedicated user Id + * + * @param userId the user id + * @return the power level + */ + fun getUserPowerLevel(userId: String): Int { + return powerLevelsContent.users.getOrElse(userId) { + powerLevelsContent.usersDefault + } + } + + /** + * Tell if an user can send an event of a certain type + * + * @param eventType the event type to check for + * @param userId the user id + * @return true if the user can send this type of event + */ + fun isAllowedToSend(eventType: String, userId: String): Boolean { + return if (eventType.isNotEmpty() && userId.isNotEmpty()) { + val powerLevel = getUserPowerLevel(userId) + val minimumPowerLevel = powerLevelsContent.events[eventType] + ?: if (EventType.isStateEvent(eventType)) { + powerLevelsContent.stateDefault + } else { + powerLevelsContent.eventsDefault + } + powerLevel >= minimumPowerLevel + } else false + } + + + /** + * Get the notification level for a dedicated key. + * + * @param key the notification key + * @return the level + */ + fun notificationLevel(key: String): Int { + val value = powerLevelsContent.notifications[key] + ?: return PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL + return when (value) { + // the first implementation was a string value + is String -> value.toInt() + is Int -> value + else -> PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt index 06f4a9c7ee..09c77e9860 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/state/StateService.kt @@ -16,8 +16,10 @@ package im.vector.matrix.android.api.session.room.state +import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.util.Optional interface StateService { @@ -27,4 +29,6 @@ interface StateService { fun updateTopic(topic: String, callback: MatrixCallback) fun getStateEvent(eventType: String): Event? + + fun getStateEventLive(eventType: String): LiveData> } 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 d6ef522f41..5b3ca234ac 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,7 +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.RoomMemberSummary 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 @@ -147,4 +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) +fun RoomMemberSummary.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt index 2f602d0b84..a0f677d96d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Optional.kt @@ -27,6 +27,14 @@ data class Optional constructor(private val value: T?) { return value } + fun map(fn: (T) -> U?): Optional { + return if (value == null) { + from(null) + } else { + from(fn(value)) + } + } + fun getOrElse(fn: () -> T): T { return value ?: fn() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index be8918dac7..793e19fc51 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -38,7 +38,7 @@ 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.RoomHistoryVisibility import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting @@ -64,7 +64,7 @@ import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -697,9 +697,9 @@ internal class DefaultCryptoService @Inject constructor( && shouldEncryptForInvitedMembers(roomId) userIds = if (encryptForInvitedMembers) { - RoomMembers(realm, roomId).getActiveRoomMemberIds() + RoomMemberHelper(realm, roomId).getActiveRoomMemberIds() } else { - RoomMembers(realm, roomId).getJoinedRoomMemberIds() + RoomMemberHelper(realm, roomId).getJoinedRoomMemberIds() } } return userIds @@ -722,7 +722,7 @@ internal class DefaultCryptoService @Inject constructor( return } event.stateKey?.let { userId -> - val roomMember: RoomMember? = event.content.toModel() + val roomMember: RoomMemberSummary? = event.content.toModel() val membership = roomMember?.membership if (membership == Membership.JOIN) { // make sure we are tracking the deviceList for this user. 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 19c4715faa..b6c6c6c1ac 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 @@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.query.fastContains import im.vector.matrix.android.internal.extensions.assertIsManaged -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { chunks.remove(chunkEntity) @@ -59,7 +59,7 @@ internal fun RoomEntity.addSendingEvent(event: Event) { val eventEntity = event.toEntity(roomId).apply { this.sendState = SendState.UNSENT } - val roomMembers = RoomMembers(realm, roomId) + val roomMembers = RoomMemberHelper(realm, roomId) val myUser = roomMembers.getLastRoomMember(senderId) val localId = TimelineEventEntity.nextId(realm) val timelineEventEntity = TimelineEventEntity(localId).also { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt index 983de3a50f..17103ce337 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventSenderVisitor.kt @@ -26,7 +26,7 @@ import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.session.SessionScope -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import io.realm.RealmList import io.realm.RealmQuery import javax.inject.Inject @@ -128,7 +128,7 @@ internal class TimelineEventSenderVisitor @Inject constructor() { ContentMapper.map(senderRoomMemberContent).toModel()?.also { result.senderAvatar = it.avatarUrl result.senderName = it.displayName - result.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName) + result.isUniqueDisplayName = RoomMemberHelper(realm, roomId).isUniqueDisplayName(it.displayName) } // 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) { @@ -138,7 +138,7 @@ internal class TimelineEventSenderVisitor @Inject constructor() { } if (result.senderName == null && it.displayName != null) { result.senderName = it.displayName - result.isUniqueDisplayName = RoomMembers(realm, roomId).isUniqueDisplayName(it.displayName) + result.isUniqueDisplayName = RoomMemberHelper(realm, roomId).isUniqueDisplayName(it.displayName) } } } 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/RoomMemberSummaryMapper.kt similarity index 59% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberMapper.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomMemberSummaryMapper.kt index a458c5e506..470772a40e 100644 --- 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/RoomMemberSummaryMapper.kt @@ -16,21 +16,21 @@ 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 +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity -internal object RoomMemberMapper { +internal object RoomMemberSummaryMapper { - fun map(roomMemberEntity: RoomMemberEntity): RoomMember { - return RoomMember( - userId = roomMemberEntity.userId, - avatarUrl = roomMemberEntity.avatarUrl, - displayName = roomMemberEntity.displayName, - membership = roomMemberEntity.membership + fun map(roomMemberSummaryEntity: RoomMemberSummaryEntity): RoomMemberSummary { + return RoomMemberSummary( + userId = roomMemberSummaryEntity.userId, + avatarUrl = roomMemberSummaryEntity.avatarUrl, + displayName = roomMemberSummaryEntity.displayName, + membership = roomMemberSummaryEntity.membership ) } } -internal fun RoomMemberEntity.asDomain(): RoomMember { - return RoomMemberMapper.map(this) +internal fun RoomMemberSummaryEntity.asDomain(): RoomMemberSummary { + return RoomMemberSummaryMapper.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/RoomMemberSummaryEntity.kt similarity index 67% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberEntity.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomMemberSummaryEntity.kt index d6e41f7705..45bf1b3a22 100644 --- 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/RoomMemberSummaryEntity.kt @@ -21,13 +21,13 @@ 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? = null, - var avatarUrl: String? = null , - var reason: String? = null, - var isDirect: Boolean = false +internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = "", + @Index var userId: String = "", + @Index var roomId: String = "", + var displayName: String? = null, + var avatarUrl: String? = null, + var reason: String? = null, + var isDirect: Boolean = false ) : RealmObject() { private var membershipStr: String = Membership.NONE.name 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 07ff1df005..a29e7e89fb 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 @@ -50,6 +50,6 @@ import io.realm.annotations.RealmModule UserDraftsEntity::class, DraftEntity::class, HomeServerCapabilitiesEntity::class, - RoomMemberEntity::class + RoomMemberSummaryEntity::class ]) internal class SessionRealmModule diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 62f53c3d7b..f3f8db0ea0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -60,20 +60,7 @@ internal fun EventEntity.Companion.types(realm: Realm, return query } -internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { - if (from != null) { - if (strict) { - this.greaterThan(EventEntityFields.STATE_INDEX, from) - } else { - this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, from) - } - } - return this - .sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING) - .findFirst() -} - -internal fun RealmQuery.prev(since: Int? = null, strict: Boolean = false): EventEntity? { +internal fun RealmQuery.descending(since: Int? = null, strict: Boolean = false): RealmQuery { if (since != null) { if (strict) { this.lessThan(EventEntityFields.STATE_INDEX, since) @@ -81,9 +68,26 @@ internal fun RealmQuery.prev(since: Int? = null, strict: Boolean = this.lessThanOrEqualTo(EventEntityFields.STATE_INDEX, since) } } - return this - .sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) - .findFirst() + return this.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) +} + +internal fun RealmQuery.ascending(from: Int? = null, strict: Boolean = true): RealmQuery { + if (from != null) { + if (strict) { + this.greaterThan(EventEntityFields.STATE_INDEX, from) + } else { + this.greaterThanOrEqualTo(EventEntityFields.STATE_INDEX, from) + } + } + return this.sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING) +} + +internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { + return this.ascending(from, strict).findFirst() +} + +internal fun RealmQuery.prev(since: Int? = null, strict: Boolean = false): EventEntity? { + return descending(since, strict).findFirst() } internal fun RealmList.find(eventId: String): EventEntity? { 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 index 2ddade0048..e2dd2a7d8c 100644 --- 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 @@ -16,19 +16,19 @@ 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.RoomMemberSummaryEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields 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 { +internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery { val query = realm - .where() - .equalTo(RoomMemberEntityFields.ROOM_ID, roomId) + .where() + .equalTo(RoomMemberSummaryEntityFields.ROOM_ID, roomId) if (userId != null) { - query.equalTo(RoomMemberEntityFields.USER_ID, userId) + query.equalTo(RoomMemberSummaryEntityFields.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 0bb2dc0f27..2893bf5126 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 @@ -22,11 +22,11 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.RoomAvatarContent 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.RoomMemberEntityFields +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields 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 -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import javax.inject.Inject internal class RoomAvatarResolver @Inject constructor(private val monarchy: Monarchy, @@ -45,13 +45,13 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona if (!res.isNullOrEmpty()) { return@doWithRealm } - val roomMembers = RoomMembers(realm, roomId) + val roomMembers = RoomMemberHelper(realm, roomId) 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()?.avatarUrl } else if (members.size == 2) { - val firstOtherMember = members.where().notEqualTo(RoomMemberEntityFields.USER_ID, userId).findFirst() + val firstOtherMember = members.where().notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId).findFirst() res = firstOtherMember?.avatarUrl } } 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 ea5c2e858c..009ca682ed 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 @@ -35,7 +35,7 @@ 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 import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications import io.realm.Realm @@ -114,9 +114,9 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId roomSummaryEntity.isEncrypted = encryptionEvent != null if (updateMembers) { - val otherRoomMembers = RoomMembers(realm, roomId) + val otherRoomMembers = RoomMemberHelper(realm, roomId) .queryRoomMembersEvent() - .notEqualTo(RoomMemberEntityFields.USER_ID, userId) + .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) .findAll() .asSequence() .map { it.userId } 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 679f4a050b..53e0ee729e 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 @@ -24,11 +24,11 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.members.MembershipService import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams 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.RoomMemberSummary import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.mapper.asDomain -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.RoomMemberSummaryEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntityFields import im.vector.matrix.android.internal.query.process import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask @@ -62,14 +62,14 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr .executeBy(taskExecutor) } - override fun getRoomMember(userId: String): RoomMember? { + override fun getRoomMember(userId: String): RoomMemberSummary? { val roomMemberEntity = monarchy.fetchCopied { - RoomMembers(it, roomId).getLastRoomMember(userId) + RoomMemberHelper(it, roomId).getLastRoomMember(userId) } return roomMemberEntity?.asDomain() } - override fun getRoomMembers(queryParams: RoomMemberQueryParams): List { + override fun getRoomMembers(queryParams: RoomMemberQueryParams): List { return monarchy.fetchAllMappedSync( { roomMembersQuery(it, queryParams) @@ -80,7 +80,7 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr ) } - override fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> { + override fun getRoomMembersLive(queryParams: RoomMemberQueryParams): LiveData> { return monarchy.findAllMappedWithChanges( { roomMembersQuery(it, queryParams) @@ -91,15 +91,15 @@ internal class DefaultMembershipService @AssistedInject constructor(@Assisted pr ) } - private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { - return RoomMembers(realm, roomId).queryRoomMembersEvent() - .process(RoomMemberEntityFields.MEMBERSHIP_STR, queryParams.memberships) - .process(RoomMemberEntityFields.DISPLAY_NAME, queryParams.displayName) + private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { + return RoomMemberHelper(realm, roomId).queryRoomMembersEvent() + .process(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) + .process(RoomMemberSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) } override fun getNumberOfJoinedMembers(): Int { return Realm.getInstance(monarchy.realmConfiguration).use { - RoomMembers(it, roomId).getNumberOfJoinedMembers() + RoomMemberHelper(it, roomId).getNumberOfJoinedMembers() } } 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 9382fbc54a..67b222ecb8 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 @@ -26,7 +26,7 @@ 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.RoomEntity -import im.vector.matrix.android.internal.database.model.RoomMemberEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity 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,7 +75,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: return@doWithRealm } - val roomMembers = RoomMembers(realm, roomId) + val roomMembers = RoomMemberHelper(realm, roomId) val activeMembers = roomMembers.queryActiveRoomMembersEvent().findAll() if (roomEntity?.membership == Membership.INVITE) { @@ -83,7 +83,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: val inviterId = inviteMeEvent?.sender name = if (inviterId != null) { activeMembers.where() - .equalTo(RoomMemberEntityFields.USER_ID, inviterId) + .equalTo(RoomMemberSummaryEntityFields.USER_ID, inviterId) .findFirst() ?.displayName } else { @@ -91,7 +91,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: } } else if (roomEntity?.membership == Membership.JOIN) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() - val otherMembersSubset: List = if (roomSummary?.heroes?.isNotEmpty() == true) { + val otherMembersSubset: List = if (roomSummary?.heroes?.isNotEmpty() == true) { roomSummary.heroes.mapNotNull { userId -> roomMembers.getLastRoomMember(userId)?.takeIf { it.membership == Membership.INVITE || it.membership == Membership.JOIN @@ -99,7 +99,7 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: } } else { activeMembers.where() - .notEqualTo(RoomMemberEntityFields.USER_ID, userId) + .notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) .limit(3) .findAll() .createSnapshot() @@ -123,14 +123,14 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context: return name ?: roomId } - private fun resolveRoomMemberName(roomMember: RoomMemberEntity?, - roomMembers: RoomMembers): String? { - if (roomMember == null) return null - val isUnique = roomMembers.isUniqueDisplayName(roomMember.displayName) + private fun resolveRoomMemberName(roomMemberSummary: RoomMemberSummaryEntity?, + roomMemberHelper: RoomMemberHelper): String? { + if (roomMemberSummary == null) return null + val isUnique = roomMemberHelper.isUniqueDisplayName(roomMemberSummary.displayName) return if (isUnique) { - roomMember.displayName + roomMemberSummary.displayName } else { - "${roomMember.displayName} (${roomMember.userId})" + "${roomMemberSummary.displayName} (${roomMemberSummary.userId})" } } } 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 index bc970668bf..291d2af3a6 100644 --- 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 @@ -17,13 +17,13 @@ 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 +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity internal object RoomMemberEntityFactory { - fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberEntity { + fun create(roomId: String, userId: String, roomMember: RoomMemberContent): RoomMemberSummaryEntity { val primaryKey = "${roomId}_$userId" - return RoomMemberEntity( + return RoomMemberSummaryEntity( primaryKey = primaryKey, userId = userId, roomId = roomId, 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/RoomMemberHelper.kt similarity index 78% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMemberHelper.kt index e3775f5ade..029afcbe40 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/RoomMemberHelper.kt @@ -20,7 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.model.Membership 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.RoomMemberEntity +import im.vector.matrix.android.internal.database.model.RoomMemberSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.query.where import io.realm.Realm @@ -32,8 +32,8 @@ import io.realm.Sort * It allows to get the live membership of a user. */ -internal class RoomMembers(private val realm: Realm, - private val roomId: String +internal class RoomMemberHelper(private val realm: Realm, + private val roomId: String ) { private val roomSummary: RoomSummaryEntity? by lazy { @@ -48,8 +48,8 @@ internal class RoomMembers(private val realm: Realm, .findFirst() } - fun getLastRoomMember(userId: String): RoomMemberEntity? { - return RoomMemberEntity + fun getLastRoomMember(userId: String): RoomMemberSummaryEntity? { + return RoomMemberSummaryEntity .where(realm, roomId, userId) .findFirst() } @@ -66,26 +66,26 @@ internal class RoomMembers(private val realm: Realm, .size == 1 } - fun queryRoomMembersEvent(): RealmQuery { - return RoomMemberEntity.where(realm, roomId) + fun queryRoomMembersEvent(): RealmQuery { + return RoomMemberSummaryEntity.where(realm, roomId) } - fun queryJoinedRoomMembersEvent(): RealmQuery { + fun queryJoinedRoomMembersEvent(): RealmQuery { return queryRoomMembersEvent() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) } - fun queryInvitedRoomMembersEvent(): RealmQuery { + fun queryInvitedRoomMembersEvent(): RealmQuery { return queryRoomMembersEvent() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) } - fun queryActiveRoomMembersEvent(): RealmQuery { + fun queryActiveRoomMembersEvent(): RealmQuery { return queryRoomMembersEvent() .beginGroup() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.INVITE.name) .or() - .equalTo(RoomMemberEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) + .equalTo(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) .endGroup() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt index 785fd9ae71..d52e5272cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt @@ -16,25 +16,34 @@ package im.vector.matrix.android.internal.session.room.state +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations 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.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.state.StateService +import im.vector.matrix.android.api.util.Optional +import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.model.RoomSummaryEntity +import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields +import im.vector.matrix.android.internal.database.query.descending 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.SessionDatabase 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 im.vector.matrix.android.internal.util.fetchCopyMap import io.realm.Realm import io.realm.RealmConfiguration internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String, - @SessionDatabase - private val realmConfiguration: RealmConfiguration, + private val monarchy: Monarchy, private val taskExecutor: TaskExecutor, private val sendStateTask: SendStateTask ) : StateService { @@ -45,11 +54,21 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private } override fun getStateEvent(eventType: String): Event? { - return Realm.getInstance(realmConfiguration).use { realm -> + return Realm.getInstance(monarchy.realmConfiguration).use { realm -> EventEntity.where(realm, roomId, eventType).prev()?.asDomain() } } + override fun getStateEventLive(eventType: String): LiveData> { + val liveData = monarchy.findAllMappedWithChanges( + { realm -> EventEntity.where(realm, roomId, eventType).descending()}, + { it.asDomain() } + ) + return Transformations.map(liveData) { results -> + results.firstOrNull().toOptional() + } + } + override fun updateTopic(topic: String, callback: MatrixCallback) { val params = SendStateTask.Params(roomId, EventType.STATE_ROOM_TOPIC, 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 9bc8c86be5..d7d19802c5 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 @@ -28,7 +28,7 @@ import im.vector.matrix.android.internal.database.query.getDirectRooms import im.vector.matrix.android.internal.database.query.getOrCreate import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.UserId -import im.vector.matrix.android.internal.session.room.membership.RoomMembers +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync import im.vector.matrix.android.internal.session.sync.model.accountdata.* import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper @@ -69,7 +69,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc var hasUpdate = false monarchy.doWithRealm { realm -> invites.forEach { (roomId, _) -> - val myUserStateEvent = RoomMembers(realm, roomId).getLastStateEvent(userId) + val myUserStateEvent = RoomMemberHelper(realm, roomId).getLastStateEvent(userId) val inviterId = myUserStateEvent?.sender val myUserRoomMember: RoomMemberContent? = myUserStateEvent?.let { it.asDomain().content?.toModel() } val isDirect = myUserRoomMember?.isDirect diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt new file mode 100644 index 0000000000..4314a5d97a --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileItemExtensions.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2020 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.riotx.core.epoxy.profiles + +import androidx.annotation.DrawableRes +import com.airbnb.epoxy.EpoxyController +import im.vector.riotx.core.epoxy.DividerItem_ + +fun EpoxyController.buildProfileSection(title: String) { + profileItemSection { + id("section_$title") + title(title) + } +} + +fun EpoxyController.buildProfileAction( + id: String, + title: String, + subtitle: String? = null, + @DrawableRes icon: Int = 0, + destructive: Boolean = false, + divider: Boolean = true, + action: () -> Unit +) { + + profileItemAction { + iconRes(icon) + id("action_$id") + subtitle(subtitle) + destructive(destructive) + title(title) + listener { _ -> + action() + } + } + + DividerItem_() + .id("divider_$title") + .addIf(divider, this) +} diff --git a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt index 1c8dc99196..8102a5a45f 100644 --- a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt +++ b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberController.kt @@ -17,20 +17,20 @@ package im.vector.riotx.features.autocomplete.member import com.airbnb.epoxy.TypedEpoxyController -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary 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 AutocompleteMemberController @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/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt index 84a33173b8..970df6c403 100644 --- a/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt +++ b/vector/src/main/java/im/vector/riotx/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -25,14 +25,14 @@ import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams 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.RoomMemberSummary import im.vector.riotx.features.autocomplete.AutocompleteClickListener class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, @Assisted val roomId: String, private val session: Session, private val controller: AutocompleteMemberController -) : RecyclerViewPresenter(context), AutocompleteClickListener { +) : RecyclerViewPresenter(context), AutocompleteClickListener { private val room = session.getRoom(roomId)!! @@ -51,7 +51,7 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, return controller.adapter } - override fun onItemClick(t: RoomMember) { + override fun onItemClick(t: RoomMemberSummary) { dispatchClick(t) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt index 7ca647ea3e..314baab011 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/AutoCompleter.kt @@ -27,7 +27,7 @@ import com.otaliastudios.autocomplete.CharPolicy import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject 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.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem @@ -108,13 +108,13 @@ class AutoCompleter @AssistedInject constructor( private fun setupMembers(backgroundDrawable: ColorDrawable, editText: EditText) { val autocompleteMemberPresenter = autocompleteMemberPresenterFactory.create(roomId) - Autocomplete.on(editText) + Autocomplete.on(editText) .with(CharPolicy('@', true)) .with(autocompleteMemberPresenter) .with(ELEVATION) .with(backgroundDrawable) - .with(object : AutocompleteCallback { - override fun onPopupItemClicked(editable: Editable, item: RoomMember): Boolean { + .with(object : AutocompleteCallback { + override fun onPopupItemClicked(editable: Editable, item: RoomMemberSummary): Boolean { insertMatrixItem(editText, editable, "@", item.toMatrixItem()) return true } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index c93358a04e..e780d36411 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -40,7 +40,7 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities 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.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageType @@ -210,7 +210,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro invisibleEventsObservable.accept(action) } - fun getMember(userId: String): RoomMember? { + fun getMember(userId: String): RoomMemberSummary? { return room.getRoomMember(userId) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt index 165ef7b625..588d398e7f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.sync.SyncState diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index 390686567b..f2f266bc5a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -23,6 +23,8 @@ import com.airbnb.epoxy.TypedEpoxyController import im.vector.riotx.R import im.vector.riotx.core.epoxy.DividerItem_ import im.vector.riotx.core.epoxy.dividerItem +import im.vector.riotx.core.epoxy.profiles.buildProfileAction +import im.vector.riotx.core.epoxy.profiles.buildProfileSection import im.vector.riotx.core.epoxy.profiles.profileItemAction import im.vector.riotx.core.epoxy.profiles.profileItemSection import im.vector.riotx.core.resources.StringProvider @@ -50,13 +52,13 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri val roomSummary = data.roomSummary() // Security - buildSection(stringProvider.getString(R.string.room_profile_section_security)) + buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) val learnMoreSubtitle = if (data.isEncrypted) { R.string.room_profile_encrypted_subtitle } else { R.string.room_profile_not_encrypted_subtitle } - buildAction( + buildProfileAction( id = "learn_more", title = stringProvider.getString(R.string.room_profile_section_security_learn_more), subtitle = stringProvider.getString(learnMoreSubtitle), @@ -64,33 +66,33 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri ) // More - buildSection(stringProvider.getString(R.string.room_profile_section_more)) - buildAction( + buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) + buildProfileAction( id = "settings", title = stringProvider.getString(R.string.room_profile_section_more_settings), icon = R.drawable.ic_room_profile_settings, action = { callback?.onSettingsClicked() } ) - buildAction( + buildProfileAction( id = "notifications", title = stringProvider.getString(R.string.room_profile_section_more_notifications), icon = R.drawable.ic_room_profile_notification, action = { callback?.onNotificationsClicked() } ) val numberOfMembers = roomSummary?.joinedMembersCount?.toString() ?: "-" - buildAction( + buildProfileAction( id = "member_list", title = stringProvider.getString(R.string.room_profile_section_more_member_list, numberOfMembers), icon = R.drawable.ic_room_profile_member_list, action = { callback?.onMemberListClicked() } ) - buildAction( + buildProfileAction( id = "uploads", title = stringProvider.getString(R.string.room_profile_section_more_uploads), icon = R.drawable.ic_room_profile_uploads, action = { callback?.onUploadsClicked() } ) - buildAction( + buildProfileAction( id = "leave", title = stringProvider.getString(R.string.room_profile_section_more_leave), divider = false, @@ -99,38 +101,6 @@ class RoomProfileController @Inject constructor(private val stringProvider: Stri ) } - private fun buildSection(title: String) { - profileItemSection { - id("section_$title") - title(title) - } - } - - private fun buildAction( - id: String, - title: String, - subtitle: String? = null, - @DrawableRes icon: Int = 0, - destructive: Boolean = false, - divider: Boolean = true, - action: () -> Unit - ) { - - profileItemAction { - iconRes(icon) - id("action_$id") - subtitle(subtitle) - destructive(destructive) - title(title) - listener { _ -> - action() - } - } - - DividerItem_() - .id("divider_$title") - .addIf(divider, this) - } } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt index f9a9b1537d..857b03b263 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListController.kt @@ -17,33 +17,48 @@ package im.vector.riotx.features.roomprofile.members import com.airbnb.epoxy.TypedEpoxyController -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.toMatrixItem -import im.vector.riotx.core.epoxy.profiles.profileItemAction +import im.vector.riotx.core.epoxy.profiles.buildProfileSection import im.vector.riotx.core.epoxy.profiles.profileMatrixItem -import im.vector.riotx.features.autocomplete.autocompleteMatrixItem +import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.features.home.AvatarRenderer import javax.inject.Inject -class RoomMemberListController @Inject constructor(private val avatarRenderer: AvatarRenderer) : TypedEpoxyController() { +class RoomMemberListController @Inject constructor(private val avatarRenderer: AvatarRenderer, + private val stringProvider: StringProvider) : TypedEpoxyController() { interface Callback { - fun onRoomMemberClicked(roomMember: RoomMember) + fun onRoomMemberClicked(roomMember: RoomMemberSummary) } var callback: Callback? = null + init { + setData(null) + } + override fun buildModels(data: RoomMemberListViewState?) { - data?.roomMembers?.invoke()?.forEach { roomMember -> - profileMatrixItem { - id(roomMember.userId) - matrixItem(roomMember.toMatrixItem()) - avatarRenderer(avatarRenderer) - clickListener { _ -> - callback?.onRoomMemberClicked(roomMember) + val roomMembersByPowerLevel = data?.roomMemberSummaries?.invoke() ?: return + for ((powerLevelCategory, roomMemberList) in roomMembersByPowerLevel) { + if (roomMemberList.isEmpty()) { + continue + } + buildProfileSection( + stringProvider.getString(powerLevelCategory.titleRes) + ) + roomMemberList.forEach { roomMember -> + profileMatrixItem { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(avatarRenderer) + clickListener { _ -> + callback?.onRoomMemberClicked(roomMember) + } } } } } + } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt index 12dec3783a..4b9955bf2f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListFragment.kt @@ -21,7 +21,7 @@ import android.view.View import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup @@ -63,7 +63,7 @@ class RoomMemberListFragment @Inject constructor( renderRoomSummary(viewState) } - override fun onRoomMemberClicked(roomMember: RoomMember) { + override fun onRoomMemberClicked(roomMember: RoomMemberSummary) { navigator.openRoomMemberProfile(roomMember.userId, roomId = roomProfileArgs.roomId, context = requireActivity()) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index 38cb09a099..0056d332a0 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -23,14 +23,20 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.query.QueryStringValue import im.vector.matrix.android.api.session.Session +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.members.roomMemberQueryParams import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.api.session.room.model.PowerLevelsContent +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsConstants +import im.vector.matrix.android.api.session.room.powerlevers.PowerLevelsHelper +import im.vector.matrix.rx.mapOptional import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap import im.vector.riotx.core.platform.VectorViewModel -import im.vector.riotx.core.utils.DataSource -import im.vector.riotx.core.utils.PublishDataSource -import im.vector.riotx.features.roomprofile.RoomProfileViewEvents +import io.reactivex.Observable +import io.reactivex.functions.BiFunction class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, private val session: Session) @@ -53,10 +59,32 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState private val room = session.getRoom(initialState.roomId)!! init { - observeRoomMembers() + observeRoomMemberSummaries() observeRoomSummary() } + private fun observeRoomMemberSummaries() { + val roomMemberQueryParams = roomMemberQueryParams { + displayName = QueryStringValue.IsNotEmpty + memberships = Membership.activeMemberships() + } + Observable + .combineLatest, PowerLevelsContent, RoomMemberSummaries>( + room.rx().liveRoomMembers(roomMemberQueryParams), + room.rx() + .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + .mapOptional { it.content.toModel() } + .unwrap(), + BiFunction { roomMembers, powerLevelsContent -> + buildRoomMemberSummaries(powerLevelsContent, roomMembers) + } + ) + .execute { async -> + copy(roomMemberSummaries = async) + } + + } + private fun observeRoomSummary() { room.rx().liveRoomSummary() .unwrap() @@ -65,16 +93,33 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState } } - private fun observeRoomMembers() { - val queryParams = roomMemberQueryParams { - displayName = QueryStringValue.IsNotEmpty - memberships = Membership.activeMemberships() - } - room.rx() - .liveRoomMembers(queryParams) - .execute { - copy(roomMembers = it) + + private fun buildRoomMemberSummaries(powerLevelsContent: PowerLevelsContent, roomMembers: List): RoomMemberSummaries { + val admins = ArrayList() + val moderators = ArrayList() + val users = ArrayList(roomMembers.size) + val customs = ArrayList() + val invites = ArrayList() + val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) + roomMembers + .forEach { roomMember -> + val memberPowerLevel = powerLevelsHelper.getUserPowerLevel(roomMember.userId) + when { + roomMember.membership == Membership.INVITE -> invites.add(roomMember) + memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL -> admins.add(roomMember) + memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL -> moderators.add(roomMember) + memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL -> users.add(roomMember) + else -> customs.add(roomMember) + } } + + return mapOf( + PowerLevelCategory.ADMIN to admins, + PowerLevelCategory.MODERATOR to moderators, + PowerLevelCategory.CUSTOM to customs, + PowerLevelCategory.INVITE to invites, + PowerLevelCategory.USER to users + ) } override fun handle(action: RoomMemberListAction) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt index 223d2c5af6..bc616994ba 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewState.kt @@ -16,19 +16,31 @@ package im.vector.riotx.features.roomprofile.members +import androidx.annotation.StringRes import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import im.vector.matrix.android.api.session.room.model.RoomMember +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.R import im.vector.riotx.features.roomprofile.RoomProfileArgs data class RoomMemberListViewState( val roomId: String, val roomSummary: Async = Uninitialized, - val roomMembers: Async> = Uninitialized + val roomMemberSummaries: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) } + +typealias RoomMemberSummaries = Map> + +enum class PowerLevelCategory(@StringRes val titleRes: Int) { + ADMIN(R.string.room_member_power_level_admins), + MODERATOR(R.string.room_member_power_level_moderators), + CUSTOM(R.string.room_member_power_level_custom), + INVITE(R.string.room_member_power_level_invites), + USER(R.string.room_member_power_level_users) +} diff --git a/vector/src/main/res/layout/fragment_room_profile.xml b/vector/src/main/res/layout/fragment_room_profile.xml index 78e54f76e2..c9f79caec7 100644 --- a/vector/src/main/res/layout/fragment_room_profile.xml +++ b/vector/src/main/res/layout/fragment_room_profile.xml @@ -17,7 +17,7 @@ style="@style/VectorAppBarLayoutStyle" android:layout_width="match_parent" app:contentScrim="?riotx_background" - app:scrimAnimationDuration="400" + app:scrimAnimationDuration="250" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:titleEnabled="false" diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index fdfd8f85ef..72f3113308 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -35,4 +35,10 @@ Leave Room "Leaving the room..." + Admins + Moderators + Custom + Invites + Users +