From 926e64bb6ea3798c24ab7d088b2ceb24a1a306d9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 23 Jul 2025 13:30:34 +0200 Subject: [PATCH 1/8] misc (sdk) : update matrix patterns to handle new roomId format (and so fix permalinks) --- .../matrix/android/sdk/api/MatrixPatterns.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt index 0f7e9ca6a8..4ec809a6d0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt @@ -30,15 +30,21 @@ object MatrixPatterns { // Note: TLD is not mandatory (localhost, IP address...) private const val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?" + private const val BASE_64_ALPHABET = "[0-9A-Za-z/\\+=]+" + private const val BASE_64_URL_SAFE_ALPHABET = "[0-9A-Za-z/\\-_]+" + // regex pattern to find matrix user ids in a string. // See https://matrix.org/docs/spec/appendices#historical-user-ids private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX" val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) // regex pattern to find room ids in a string. - private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX" + private const val MATRIX_ROOM_IDENTIFIER_REGEX = "^!.+$DOMAIN_REGEX$" private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = MATRIX_ROOM_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) + private const val MATRIX_ROOM_IDENTIFIER_DOMAINLESS_REGEX = "!$BASE_64_URL_SAFE_ALPHABET" + private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER_DOMAINLESS = MATRIX_ROOM_IDENTIFIER_DOMAINLESS_REGEX.toRegex() + // regex pattern to find room aliases in a string. private const val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX" private val PATTERN_CONTAIN_MATRIX_ALIAS = MATRIX_ROOM_ALIAS_REGEX.toRegex(RegexOption.IGNORE_CASE) @@ -48,11 +54,11 @@ object MatrixPatterns { private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = MATRIX_EVENT_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) // regex pattern to find message ids in a string. - private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+" + private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$$BASE_64_ALPHABET" private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = MATRIX_EVENT_IDENTIFIER_V3_REGEX.toRegex(RegexOption.IGNORE_CASE) // Ref: https://matrix.org/docs/spec/rooms/v4#event-ids - private const val MATRIX_EVENT_IDENTIFIER_V4_REGEX = "\\$[A-Z0-9\\-_]+" + private const val MATRIX_EVENT_IDENTIFIER_V4_REGEX = "\\$$BASE_64_URL_SAFE_ALPHABET" private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4 = MATRIX_EVENT_IDENTIFIER_V4_REGEX.toRegex(RegexOption.IGNORE_CASE) // regex pattern to find group ids in a string. @@ -76,7 +82,10 @@ object MatrixPatterns { PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER, PATTERN_CONTAIN_MATRIX_ALIAS, PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER, + PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER_DOMAINLESS, PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER, + PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3, + PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4, PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER ) @@ -97,7 +106,9 @@ object MatrixPatterns { * @return true if the string is a valid room Id */ fun isRoomId(str: String?): Boolean { - return str != null && str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER + return str != null && + (str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER || + str matches PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER_DOMAINLESS) } /** From fd3f7e3a240f287b6bd10fac033e88f268f3d57c Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 24 Jul 2025 22:25:10 +0200 Subject: [PATCH 2/8] change (power level) : support new InfinitePowerLevel (first draft) --- .../src/main/res/values/strings.xml | 2 + .../sdk/session/space/SpaceHierarchyTest.kt | 8 +-- .../SenderNotificationPermissionCondition.kt | 8 +-- .../sdk/api/session/room/RoomExtensions.kt | 18 ++++++ .../session/room/model/PowerLevelsContent.kt | 32 ++++++---- .../room/model/create/RoomCreateContent.kt | 26 +++++++- .../sdk/api/session/room/powerlevels/Role.kt | 30 ++++----- ...owerLevelsHelper.kt => RoomPowerLevels.kt} | 60 ++++++++++++------ .../room/powerlevels/UserPowerLevel.kt | 35 +++++++++++ .../session/permalinks/ViaParameterFinder.kt | 13 +--- .../pushers/DefaultConditionResolver.kt | 16 ++--- .../EventRelationsAggregationProcessor.kt | 16 ++--- .../poll/DefaultPollAggregationProcessor.kt | 6 +- .../poll/PollAggregationProcessor.kt | 4 +- .../room/powerlevels/RoomPowerLevels.kt | 23 +++++++ .../room/state/StateEventDataSource.kt | 1 + .../room/summary/RoomSummaryUpdater.kt | 21 +++++-- .../room/version/DefaultRoomVersionService.kt | 15 +++-- .../session/space/DefaultSpaceService.kt | 18 +++--- .../internal/session/widgets/WidgetManager.kt | 13 +--- .../DefaultPollAggregationProcessorTest.kt | 10 +-- ...faultCreateLocalRoomStateEventsTaskTest.kt | 4 +- .../home/room/detail/TimelineViewModel.kt | 9 ++- .../composer/MessageComposerViewModel.kt | 3 +- .../action/MessageActionsViewModel.kt | 11 ++-- .../factory/MergedHeaderItemFactory.kt | 15 +++-- .../timeline/format/NoticeEventFormatter.kt | 6 +- .../location/LocationSharingViewModel.kt | 7 +-- .../powerlevel/PowerLevelsFlowFactory.kt | 25 ++++++-- .../RoomMemberProfileAction.kt | 3 +- .../RoomMemberProfileController.kt | 11 ++-- .../RoomMemberProfileFragment.kt | 8 +-- .../RoomMemberProfileViewEvents.kt | 5 +- .../RoomMemberProfileViewModel.kt | 38 ++++++------ .../RoomMemberProfileViewState.kt | 4 +- .../powerlevel/EditPowerLevelDialogs.kt | 27 ++++---- .../roomprofile/RoomProfileViewModel.kt | 12 ++-- .../roomprofile/alias/RoomAliasViewModel.kt | 7 +-- .../banned/RoomBannedMemberListViewModel.kt | 12 ++-- .../members/RoomMemberListComparator.kt | 53 ++++++++++++++++ .../members/RoomMemberListController.kt | 37 ++++++----- .../members/RoomMemberListViewModel.kt | 62 +++++++++---------- .../members/RoomMemberListViewState.kt | 11 +++- .../members/RoomMemberSummaryComparator.kt | 52 ---------------- .../roomprofile/permissions/RoleFormatter.kt | 5 +- .../permissions/RoomPermissionsAction.kt | 3 +- .../permissions/RoomPermissionsController.kt | 26 +++----- .../permissions/RoomPermissionsFragment.kt | 6 +- .../permissions/RoomPermissionsViewModel.kt | 42 +++++++------ .../settings/RoomSettingsViewModel.kt | 23 +++---- .../app/features/spaces/SpaceMenuViewModel.kt | 20 +++--- .../spaces/create/CreateSpaceViewModelTask.kt | 5 +- .../spaces/explore/SpaceDirectoryViewModel.kt | 13 ++-- .../leave/SpaceLeaveAdvancedViewModel.kt | 15 +++-- .../people/SpacePeopleListController.kt | 12 ++-- .../spaces/share/ShareSpaceViewModel.kt | 7 +-- .../usecase/StartVoiceBroadcastUseCase.kt | 11 ++-- .../features/widgets/WidgetPostAPIHandler.kt | 12 ++-- .../app/features/widgets/WidgetViewModel.kt | 13 ++-- .../app/features/MemberListViewModelTest.kt | 4 +- 60 files changed, 566 insertions(+), 448 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/{PowerLevelsHelper.kt => RoomPowerLevels.kt} (59%) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt delete mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryComparator.kt diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 803510ed76..02fef53306 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -120,6 +120,7 @@ %1$s modified %2$s widget You modified %1$s widget + Owner Admin Moderator Default @@ -2383,6 +2384,7 @@ Invites Users + Owner in %1$s Admin in %1$s Moderator in %1$s Default in %1$s diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index de661275a7..6c9d0ad983 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest @@ -500,12 +500,12 @@ class SpaceHierarchyTest : InstrumentedTest { room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!) commonTestHelper.retryPeriodically { - val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!! + val roomPowerLevels = aliceSession.getRoom(bobRoomId)!! .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) ?.content ?.toModel() - ?.let { PowerLevelsHelper(it) } - powerLevelsHelper!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT) + ?.let { RoomPowerLevels(it) } + roomPowerLevels!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT) } aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/SenderNotificationPermissionCondition.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/SenderNotificationPermissionCondition.kt index 82f5023c2f..c3e9b9f527 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/SenderNotificationPermissionCondition.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/SenderNotificationPermissionCondition.kt @@ -16,8 +16,7 @@ package org.matrix.android.sdk.api.session.pushrules import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels class SenderNotificationPermissionCondition( /** @@ -35,8 +34,7 @@ class SenderNotificationPermissionCondition( override fun technicalDescription() = "User power level <$key>" - fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean { - val powerLevelsHelper = PowerLevelsHelper(powerLevels) - return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevels.notificationLevel(key) + fun isSatisfied(event: Event, roomPowerLevels: RoomPowerLevels): Boolean { + return event.senderId != null && roomPowerLevels.isUserAbleToTriggerNotification(event.senderId, key) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt index b30c60554f..29638857bc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt @@ -17,7 +17,13 @@ package org.matrix.android.sdk.api.session.room import org.matrix.android.sdk.api.query.QueryStateEventValue +import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent /** @@ -34,3 +40,15 @@ fun Room.getTimelineEvent(eventId: String): TimelineEvent? = */ fun Room.getStateEvent(eventType: String, stateKey: QueryStateEventValue): Event? = stateService().getStateEvent(eventType, stateKey) + +/** + * Get the current RoomPowerLevels of the room. + */ +fun Room.getRoomPowerLevels(): RoomPowerLevels { + val powerLevelsContent = getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)?.content?.toModel() + val roomCreateContent = getStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)?.getRoomCreateContentWithSender() + return RoomPowerLevels( + powerLevelsContent, + roomCreateContent + ) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index 0329828130..894640a488 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -18,7 +18,8 @@ package org.matrix.android.sdk.api.session.room.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent.Companion.NOTIFICATIONS_ROOM_KEY +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel /** * Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content. @@ -88,7 +89,7 @@ data class PowerLevelsContent( * Get the notification level for a dedicated key. * * @param key the notification key - * @return the level, default to Moderator if the key is not found + * @return the level */ fun notificationLevel(key: String): Int { return when (val value = notifications.orEmpty()[key]) { @@ -96,10 +97,9 @@ data class PowerLevelsContent( is String -> value.toInt() is Double -> value.toInt() is Int -> value - else -> Role.Moderator.value + else -> defaultNotificationLevel(key) } } - companion object { /** * Key to use for content.notifications and get the level required to trigger an @room notification. Defaults to 50 if unspecified. @@ -108,11 +108,21 @@ data class PowerLevelsContent( } } +private fun defaultNotificationLevel(key: String): Int { + return when (key) { + NOTIFICATIONS_ROOM_KEY -> UserPowerLevel.Moderator.value + else -> UserPowerLevel.User.value + } +} + + // Fallback to default value, defined in the Matrix specification -fun PowerLevelsContent.banOrDefault() = ban ?: Role.Moderator.value -fun PowerLevelsContent.kickOrDefault() = kick ?: Role.Moderator.value -fun PowerLevelsContent.inviteOrDefault() = invite ?: Role.Moderator.value -fun PowerLevelsContent.redactOrDefault() = redact ?: Role.Moderator.value -fun PowerLevelsContent.eventsDefaultOrDefault() = eventsDefault ?: Role.Default.value -fun PowerLevelsContent.usersDefaultOrDefault() = usersDefault ?: Role.Default.value -fun PowerLevelsContent.stateDefaultOrDefault() = stateDefault ?: Role.Moderator.value +fun PowerLevelsContent?.banOrDefault() = this?.ban ?: UserPowerLevel.Moderator.value +fun PowerLevelsContent?.kickOrDefault() = this?.kick ?: UserPowerLevel.Moderator.value +fun PowerLevelsContent?.inviteOrDefault() = this?.invite ?: UserPowerLevel.User.value +fun PowerLevelsContent?.redactOrDefault() = this?.redact ?: UserPowerLevel.Moderator.value +fun PowerLevelsContent?.eventsDefaultOrDefault() = this?.eventsDefault ?: UserPowerLevel.User.value +fun PowerLevelsContent?.usersDefaultOrDefault() = this?.usersDefault ?: UserPowerLevel.User.value +fun PowerLevelsContent?.stateDefaultOrDefault() = this?.stateDefault ?: UserPowerLevel.Moderator.value + +fun PowerLevelsContent?.notificationLevelOrDefault(key: String) = this?.notificationLevel(key) ?: defaultNotificationLevel(key) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomCreateContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomCreateContent.kt index d73c941a86..2408f4a004 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomCreateContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomCreateContent.kt @@ -18,15 +18,39 @@ package org.matrix.android.sdk.api.session.room.model.create import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel /** * Content of a m.room.create type event. */ @JsonClass(generateAdapter = true) data class RoomCreateContent( + // Creator should be replaced by the sender of the event @Json(name = "creator") val creator: String? = null, @Json(name = "room_version") val roomVersion: String? = null, @Json(name = "predecessor") val predecessor: Predecessor? = null, // Defines the room type, see #RoomType (user extensible) - @Json(name = "type") val type: String? = null + @Json(name = "type") val type: String? = null, + @Json(name = "additional_creators") val additionalCreators: List? = null, ) + +data class RoomCreateContentWithSender( + val senderId: String, + val inner: RoomCreateContent +) { + val creators = setOf(senderId) + inner.additionalCreators.orEmpty().toSet() +} + +fun Event.getRoomCreateContentWithSender(): RoomCreateContentWithSender? { + if (this.type != EventType.STATE_ROOM_CREATE) return null + val innerContent = getClearContent().toModel() ?: return null + val senderId = senderId ?: return null + return RoomCreateContentWithSender(senderId, innerContent) +} + +fun RoomCreateContent.explicitlyPrivilegeRoomCreators(): Boolean { + val supportedRoomVersions = listOf("org.matrix.hydra.11", "12") + return supportedRoomVersions.contains(roomVersion) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt index c5cc573458..57cd75e70d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt @@ -17,26 +17,22 @@ package org.matrix.android.sdk.api.session.room.powerlevels -sealed class Role(open val value: Int) : Comparable { - object Admin : Role(100) - object Moderator : Role(50) - object Default : Role(0) - data class Custom(override val value: Int) : Role(value) +enum class Role { + Creator, + SuperAdmin, + Admin, + Moderator, + User; - override fun compareTo(other: Role): Int { - return value.compareTo(other.value) - } companion object { - - // Order matters, default value should be checked after defined roles - fun fromValue(value: Int, default: Int): Role { - return when (value) { - Admin.value -> Admin - Moderator.value -> Moderator - Default.value, - default -> Default - else -> Custom(value) + fun getSuggestedRole(userPowerLevel: UserPowerLevel): Role { + return when { + userPowerLevel == UserPowerLevel.Infinite -> Creator + userPowerLevel >= UserPowerLevel.SuperAdmin -> SuperAdmin + userPowerLevel >= UserPowerLevel.Admin -> Admin + userPowerLevel >= UserPowerLevel.Moderator -> Moderator + else -> User } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt similarity index 59% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt index 36993074aa..d7fbeb8769 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt @@ -19,17 +19,23 @@ package org.matrix.android.sdk.api.session.room.powerlevels import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.banOrDefault +import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.model.create.explicitlyPrivilegeRoomCreators import org.matrix.android.sdk.api.session.room.model.eventsDefaultOrDefault import org.matrix.android.sdk.api.session.room.model.inviteOrDefault import org.matrix.android.sdk.api.session.room.model.kickOrDefault +import org.matrix.android.sdk.api.session.room.model.notificationLevelOrDefault import org.matrix.android.sdk.api.session.room.model.redactOrDefault import org.matrix.android.sdk.api.session.room.model.stateDefaultOrDefault import org.matrix.android.sdk.api.session.room.model.usersDefaultOrDefault /** - * This class is an helper around PowerLevelsContent. + * This class is an helper around PowerLevelsContent and RoomCreateContent. */ -class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { +class RoomPowerLevels( + val powerLevelsContent: PowerLevelsContent?, + private val roomCreateContent: RoomCreateContentWithSender?, +) { /** * Returns the user power level of a dedicated user Id. @@ -37,10 +43,14 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @param userId the user id * @return the power level */ - fun getUserPowerLevelValue(userId: String): Int { - return powerLevelsContent.users + fun getUserPowerLevel(userId: String): UserPowerLevel { + if (shouldGiveInfinitePowerLevel(userId)) return UserPowerLevel.Infinite + if (powerLevelsContent == null) return UserPowerLevel.User + val value = powerLevelsContent.users ?.get(userId) ?: powerLevelsContent.usersDefaultOrDefault() + + return UserPowerLevel.Value(value) } /** @@ -50,9 +60,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @return the power level */ fun getUserRole(userId: String): Role { - val value = getUserPowerLevelValue(userId) - // I think we should use powerLevelsContent.usersDefault, but Ganfra told me that it was like that on riot-Web - return Role.fromValue(value, powerLevelsContent.eventsDefaultOrDefault()) + val value = getUserPowerLevel(userId) + return Role.getSuggestedRole(value) } /** @@ -65,14 +74,14 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { */ fun isUserAllowedToSend(userId: String, isState: Boolean, eventType: String?): Boolean { return if (userId.isNotEmpty()) { - val powerLevel = getUserPowerLevelValue(userId) - val minimumPowerLevel = powerLevelsContent.events?.get(eventType) + val powerLevel = getUserPowerLevel(userId) + val minimumPowerLevel = powerLevelsContent?.events?.get(eventType) ?: if (isState) { powerLevelsContent.stateDefaultOrDefault() } else { powerLevelsContent.eventsDefaultOrDefault() } - powerLevel >= minimumPowerLevel + powerLevel >= UserPowerLevel.Value(minimumPowerLevel) } else false } @@ -82,8 +91,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @return true if able to invite */ fun isUserAbleToInvite(userId: String): Boolean { - val powerLevel = getUserPowerLevelValue(userId) - return powerLevel >= powerLevelsContent.inviteOrDefault() + val powerLevel = getUserPowerLevel(userId) + return powerLevel >= UserPowerLevel.Value(powerLevelsContent.inviteOrDefault()) } /** @@ -92,8 +101,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @return true if able to ban */ fun isUserAbleToBan(userId: String): Boolean { - val powerLevel = getUserPowerLevelValue(userId) - return powerLevel >= powerLevelsContent.banOrDefault() + val powerLevel = getUserPowerLevel(userId) + return powerLevel >= UserPowerLevel.Value(powerLevelsContent.banOrDefault()) } /** @@ -102,8 +111,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @return true if able to kick */ fun isUserAbleToKick(userId: String): Boolean { - val powerLevel = getUserPowerLevelValue(userId) - return powerLevel >= powerLevelsContent.kickOrDefault() + val powerLevel = getUserPowerLevel(userId) + return powerLevel >= UserPowerLevel.Value(powerLevelsContent.kickOrDefault()) } /** @@ -112,7 +121,22 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { * @return true if able to redact */ fun isUserAbleToRedact(userId: String): Boolean { - val powerLevel = getUserPowerLevelValue(userId) - return powerLevel >= powerLevelsContent.redactOrDefault() + val powerLevel = getUserPowerLevel(userId) + return powerLevel >= UserPowerLevel.Value(powerLevelsContent.redactOrDefault()) + } + + fun isUserAbleToTriggerNotification(userId: String, notificationKey: String): Boolean { + val userPowerLevel = getUserPowerLevel(userId) + val notificationPowerLevel = UserPowerLevel.Value(powerLevelsContent.notificationLevelOrDefault(key = notificationKey)) + return userPowerLevel >= notificationPowerLevel + } + + private fun shouldGiveInfinitePowerLevel(userId: String): Boolean { + if (roomCreateContent == null) return false + return if (roomCreateContent.inner.explicitlyPrivilegeRoomCreators()) { + roomCreateContent.creators.contains(userId) + } else { + false + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt new file mode 100644 index 0000000000..8932f16685 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package org.matrix.android.sdk.api.session.room.powerlevels + +sealed interface UserPowerLevel : Comparable { + data object Infinite : UserPowerLevel + + @JvmInline + value class Value(val value: Int) : UserPowerLevel + + override fun compareTo(other: UserPowerLevel): Int { + return when (this) { + Infinite -> when (other) { + Infinite -> 0 + is Value -> 1 + } + is Value -> when (other) { + Infinite -> -1 + is Value -> value.compareTo(other.value) + } + } + } + + companion object { + val User = Value(0) + val Moderator = Value(50) + val Admin = Value(100) + val SuperAdmin = Value(150) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt index 5fb20bb259..7f1277864c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt @@ -17,15 +17,11 @@ package org.matrix.android.sdk.internal.session.permalinks import org.matrix.android.sdk.api.MatrixPatterns.getServerName -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.RoomGetter +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import java.net.URLEncoder import javax.inject.Inject @@ -101,10 +97,7 @@ internal class ViaParameterFinder @Inject constructor( } fun userCanInvite(userId: String, roomId: String): Boolean { - val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - ?.content?.toModel() - ?.let { PowerLevelsHelper(it) } - - return powerLevelsHelper?.isUserAbleToInvite(userId) ?: false + val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId) + return roomPowerLevels.isUserAbleToInvite(userId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt index c2310f4fda..c1fb93b3ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt @@ -24,15 +24,20 @@ import org.matrix.android.sdk.api.session.pushrules.ContainsDisplayNameCondition import org.matrix.android.sdk.api.session.pushrules.EventMatchCondition import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermissionCondition +import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.RoomGetter +import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import javax.inject.Inject internal class DefaultConditionResolver @Inject constructor( private val roomGetter: RoomGetter, - @UserId private val userId: String + @UserId private val userId: String, + private val stateEventDataSource: StateEventDataSource, ) : ConditionResolver { override fun resolveEventMatchCondition( @@ -55,13 +60,8 @@ internal class DefaultConditionResolver @Inject constructor( ): Boolean { val roomId = event.roomId ?: return false val room = roomGetter.getRoom(roomId) ?: return false - - val powerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - ?.content - ?.toModel() - ?: PowerLevelsContent() - - return condition.isSatisfied(event, powerLevelsContent) + val roomPowerLevels = room.getRoomPowerLevels() + return condition.isSatisfied(event, roomPowerLevels) } override fun resolveContainsDisplayNameCondition( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index edc10bd187..3b963c13cb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.session.room import io.realm.Realm -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.crypto.verification.VerificationState import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation import org.matrix.android.sdk.api.session.events.model.Event @@ -27,7 +26,6 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon import org.matrix.android.sdk.api.session.events.model.getRelationContent import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent @@ -36,7 +34,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.verification.toState import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent @@ -62,6 +60,7 @@ import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor import org.matrix.android.sdk.internal.session.room.aggregation.utd.EncryptedReferenceAggregationProcessor +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import org.matrix.android.sdk.internal.util.time.Clock import timber.log.Timber @@ -216,9 +215,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } in EventType.POLL_END.values -> { sessionManager.getSessionComponent(sessionId)?.session()?.let { session -> - getPowerLevelsHelper(event.roomId)?.let { - pollAggregationProcessor.handlePollEndEvent(session, it, realm, event) - } + val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(event.roomId) + pollAggregationProcessor.handlePollEndEvent(session, roomPowerLevels, realm, event) } } in EventType.STATE_ROOM_BEACON_INFO.values -> { @@ -381,12 +379,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } - private fun getPowerLevelsHelper(roomId: String): PowerLevelsHelper? { - return stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - ?.content?.toModel() - ?.let { PowerLevelsHelper(it) } - } - private fun handleInitialAggregatedRelations( realm: Realm, event: Event, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt index ca224cd543..5a6648934e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt @@ -32,7 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.VoteInfo import org.matrix.android.sdk.api.session.room.model.VoteSummary import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.internal.database.mapper.ContentMapper @@ -160,13 +160,13 @@ internal class DefaultPollAggregationProcessor @Inject constructor( return true } - override fun handlePollEndEvent(session: Session, powerLevelsHelper: PowerLevelsHelper, realm: Realm, event: Event): Boolean { + override fun handlePollEndEvent(session: Session, roomPowerLevels: RoomPowerLevels, realm: Realm, event: Event): Boolean { val roomId = event.roomId ?: return false val pollEventId = event.getRelationContent()?.eventId ?: return false val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId val isPollOwner = pollOwnerId == event.senderId - if (!isPollOwner && !powerLevelsHelper.isUserAbleToRedact(event.senderId ?: "")) { + if (!isPollOwner && !roomPowerLevels.isUserAbleToRedact(event.senderId ?: "")) { return false } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt index 33a69b720a..578ba6064e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll import io.realm.Realm import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels internal interface PollAggregationProcessor { /** @@ -48,7 +48,7 @@ internal interface PollAggregationProcessor { */ fun handlePollEndEvent( session: Session, - powerLevelsHelper: PowerLevelsHelper, + roomPowerLevels: RoomPowerLevels, realm: Realm, event: Event ): Boolean diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt new file mode 100644 index 0000000000..d3556324e7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package org.matrix.android.sdk.internal.session.room.powerlevels + +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels +import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource + +internal fun StateEventDataSource.getRoomPowerLevels(roomId: String): RoomPowerLevels { + val powerLevelsContent = getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) + ?.content?.toModel() + val roomCreateContent = getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)?.getRoomCreateContentWithSender() + return RoomPowerLevels(powerLevelsContent, roomCreateContent) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt index 723d604ad4..433a4399a3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt @@ -96,3 +96,4 @@ internal class StateEventDataSource @Inject constructor( } } } + diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index cbb75398c4..8820af2034 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -34,7 +34,8 @@ import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.VersioningState import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications @@ -313,13 +314,25 @@ internal class RoomSummaryUpdater @Inject constructor( // check if sender can post child relation in parent? val senderId = parentInfo.stateEventSender val parentRoomId = parentInfo.roomId - val powerLevelsHelper = CurrentStateEventEntity + val powerLevelsContent = CurrentStateEventEntity .getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS) ?.root ?.let { ContentMapper.map(it.content).toModel() } - ?.let { PowerLevelsHelper(it) } - isValidRelation = powerLevelsHelper?.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) ?: false + val roomCreateContent = CurrentStateEventEntity + .getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_CREATE) + ?.root + ?.let { + val content = ContentMapper.map(it.content).toModel() + val sender = it.sender + if (content != null && sender != null) { + RoomCreateContentWithSender(sender, content) + } else { + null + } + } + val roomPowerLevels = RoomPowerLevels(powerLevelsContent, roomCreateContent) + isValidRelation = roomPowerLevels.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) } if (isValidRelation) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt index 0bde3a11d2..fd99bb7158 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt @@ -25,7 +25,8 @@ import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.version.RoomVersionService import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource @@ -71,11 +72,17 @@ internal class DefaultRoomVersionService @AssistedInject constructor( } override fun userMayUpgradeRoom(userId: String): Boolean { - val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) + val powerLevelsContent = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) ?.content?.toModel() - ?.let { PowerLevelsHelper(it) } - return powerLevelsHelper?.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_TOMBSTONE) ?: false + val roomCreateContent = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty) + ?.getRoomCreateContentWithSender() + + val roomPowerLevels = RoomPowerLevels( + powerLevelsContent = powerLevelsContent, + roomCreateContent = roomCreateContent + ) + return roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_TOMBSTONE) } companion object { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index cd13b03017..80ecc4fa81 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -35,8 +35,9 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.space.JoinSpaceResult import org.matrix.android.sdk.api.session.space.Space @@ -47,11 +48,13 @@ import org.matrix.android.sdk.api.session.space.model.SpaceChildContent import org.matrix.android.sdk.api.session.space.model.SpaceChildSummaryEvent import org.matrix.android.sdk.api.session.space.model.SpaceParentContent import org.matrix.android.sdk.api.session.space.peeking.SpacePeekResult +import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.RoomGetter import org.matrix.android.sdk.internal.session.room.SpaceGetter import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask @@ -83,7 +86,7 @@ internal class DefaultSpaceService @Inject constructor( if (isPublic) { this.roomAliasName = roomAliasLocalPart this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( - invite = if (isPublic) Role.Default.value else Role.Moderator.value + invite = UserPowerLevel.User.value ) this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE @@ -253,15 +256,8 @@ internal class DefaultSpaceService @Inject constructor( if (roomSummaryDataSource.getRoomSummary(parentSpaceId)?.membership != Membership.JOIN) { throw UnsupportedOperationException("Cannot add canonical child if not member of parent") } - val powerLevelsEvent = stateEventDataSource.getStateEvent( - roomId = parentSpaceId, - eventType = EventType.STATE_ROOM_POWER_LEVELS, - stateKey = QueryStringValue.IsEmpty - ) - val powerLevelsContent = powerLevelsEvent?.content?.toModel() - ?: throw UnsupportedOperationException("Cannot add canonical child, missing power level") - val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) - if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) { + val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(parentSpaceId) + if (!roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) { throw UnsupportedOperationException("Cannot add canonical child, not enough power level") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt index 7359fdbd91..8f197706ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt @@ -30,15 +30,13 @@ import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory @@ -200,12 +198,7 @@ internal class WidgetManager @Inject constructor( } fun hasPermissionsToHandleWidgets(roomId: String): Boolean { - val powerLevelsEvent = stateEventDataSource.getStateEvent( - roomId = roomId, - eventType = EventType.STATE_ROOM_POWER_LEVELS, - stateKey = QueryStringValue.IsEmpty - ) - val powerLevelsContent = powerLevelsEvent?.content?.toModel() ?: return false - return PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(userId, true, EventType.STATE_ROOM_WIDGET_LEGACY) + val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId) + return roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_WIDGET_LEGACY) } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt index 248c4b322d..bdfffa9d51 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt @@ -33,7 +33,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.getTimelineEvent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity @@ -255,9 +255,9 @@ class DefaultPollAggregationProcessorTest { every { room.getTimelineEvent(eventId) } returns if (hasExistingTimelineEvent) A_TIMELINE_EVENT else null } - private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): PowerLevelsHelper { - val powerLevelsHelper = mockk() - every { powerLevelsHelper.isUserAbleToRedact(userId) } returns isAbleToRedact - return powerLevelsHelper + private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): RoomPowerLevels { + val roomPowerLevels = mockk() + every { roomPowerLevels.isUserAbleToRedact(userId) } returns isAbleToRedact + return roomPowerLevels } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt index 1c2cf293b6..0669776394 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt @@ -376,8 +376,8 @@ internal class DefaultCreateLocalRoomStateEventsTaskTest { powerLevelsContent.kick shouldBeEqualTo Role.Moderator.value powerLevelsContent.invite shouldBeEqualTo Role.Moderator.value powerLevelsContent.redact shouldBeEqualTo Role.Moderator.value - powerLevelsContent.eventsDefault shouldBeEqualTo Role.Default.value - powerLevelsContent.usersDefault shouldBeEqualTo Role.Default.value + powerLevelsContent.eventsDefault shouldBeEqualTo Role.User.value + powerLevelsContent.usersDefault shouldBeEqualTo Role.User.value powerLevelsContent.stateDefault shouldBeEqualTo Role.Moderator.value // Guest access result.find { it.type == EventType.STATE_ROOM_GUEST_ACCESS } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 618b31796f..72b72990e8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -110,7 +110,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachme import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.read.ReadService import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent @@ -304,11 +303,11 @@ class TimelineViewModel @AssistedInject constructor( private fun observePowerLevel() { if (room == null) return PowerLevelsFlowFactory(room).createFlow() - .onEach { - val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId) + .onEach { powerLevels -> + val canInvite = powerLevels.isUserAbleToInvite(session.myUserId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) - val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE) - val isAllowedToSetupEncryption = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) + val isAllowedToStartWebRTCCall = powerLevels.isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE) + val isAllowedToSetupEncryption = powerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) setState { copy( canInvite = canInvite, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index 928adb63f4..00bc1890e7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -69,7 +69,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.session.room.timeline.getRelationContent import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent @@ -183,7 +182,7 @@ class MessageComposerViewModel @AssistedInject constructor( PowerLevelsFlowFactory(room).createFlow(), room.flow().liveRoomSummary().unwrap() ) { pl, sum -> - val canSendMessage = PowerLevelsHelper(pl).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) + val canSendMessage = pl.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) if (canSendMessage) { val isE2E = sum.isEncrypted if (isE2E) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 1028a0548f..8a4e3bb797 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -49,7 +49,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited @@ -117,11 +117,10 @@ class MessageActionsViewModel @AssistedInject constructor( return } PowerLevelsFlowFactory(room).createFlow() - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) - val canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION) - val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId) - val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) + .onEach { roomPowerLevels -> + val canReact = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.REACTION) + val canRedact = roomPowerLevels.isUserAbleToRedact(session.myUserId) + val canSendMessage = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact) setState { copy(actionPermissions = permissions) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index 45ba700d1f..c36cd14641 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -30,10 +30,11 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject @@ -303,9 +304,7 @@ class MergedHeaderItemFactory @Inject constructor( collapsedEventIds.removeAll(mergedEventIds) } val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() } - val powerLevelsHelper = activeSessionHolder.getSafeActiveSession()?.getRoom(event.roomId) - ?.let { it.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)?.content?.toModel() } - ?.let { PowerLevelsHelper(it) } + val roomPowerLevels = activeSessionHolder.getSafeActiveSession()?.getRoom(event.roomId)?.getRoomPowerLevels() val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: "" val attributes = MergedRoomCreationItem.Attributes( isCollapsed = isCollapsed, @@ -320,10 +319,10 @@ class MergedHeaderItemFactory @Inject constructor( callback = callback, currentUserId = currentUserId, roomSummary = partialState.roomSummary, - canInvite = powerLevelsHelper?.isUserAbleToInvite(currentUserId) ?: false, - canChangeAvatar = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false, - canChangeTopic = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false, - canChangeName = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false + canInvite = roomPowerLevels?.isUserAbleToInvite(currentUserId) ?: false, + canChangeAvatar = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false, + canChangeTopic = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false, + canChangeName = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false ) MergedRoomCreationItem_() .id(mergeId) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index bd1c903f1f..5a8d3fc41a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -41,7 +41,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.widgets.model.WidgetContent import timber.log.Timber @@ -122,8 +122,8 @@ class NoticeEventFormatter @Inject constructor( userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys) val diffs = ArrayList() userIds.forEach { userId -> - val from = PowerLevelsHelper(previousPowerLevelsContent).getUserRole(userId) - val to = PowerLevelsHelper(powerLevelsContent).getUserRole(userId) + val from = RoomPowerLevels(previousPowerLevelsContent,null).getUserRole(userId) + val to = RoomPowerLevels(powerLevelsContent, null).getUserRole(userId) if (from != to) { val fromStr = roleFormatter.format(from) val toStr = roleFormatter.format(to) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 82820e90cb..dfa1ec4549 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -34,7 +34,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getUserOrDefault -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.util.toMatrixItem import timber.log.Timber @@ -75,11 +75,10 @@ class LocationSharingViewModel @AssistedInject constructor( private fun observePowerLevelsForLiveLocationSharing() { PowerLevelsFlowFactory(room).createFlow() .distinctUntilChanged() - .setOnEach { - val powerLevelsHelper = PowerLevelsHelper(it) + .setOnEach { roomPowerLevels -> val canShareLiveLocation = EventType.STATE_ROOM_BEACON_INFO.values .all { beaconInfoType -> - powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, beaconInfoType) + roomPowerLevels.isUserAllowedToSend(session.myUserId, true, beaconInfoType) } copy(canShareLiveLocation = canShareLiveLocation) diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt index f065334ae7..689add249c 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt @@ -9,23 +9,40 @@ package im.vector.app.features.powerlevel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent +import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap class PowerLevelsFlowFactory(private val room: Room) { - fun createFlow(): Flow { - return room.flow() + fun createFlow(): Flow { + val flowRoom = room.flow() + val powerLevelsFlow = flowRoom .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) .mapOptional { it.content.toModel() } .flowOn(Dispatchers.Default) - .unwrap() + + val roomCreateFlow = flowRoom + .liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty) + .mapOptional { event -> + event.getRoomCreateContentWithSender() + } + .flowOn(Dispatchers.Default) + + return combine(powerLevelsFlow, roomCreateFlow) { powerLevelsContent, roomCreateContent -> + RoomPowerLevels( + powerLevelsContent = powerLevelsContent.getOrNull(), + roomCreateContent = roomCreateContent.getOrNull() + ) + } } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt index 8e764470c7..ade308339e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt @@ -8,6 +8,7 @@ package im.vector.app.features.roommemberprofile import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel sealed class RoomMemberProfileAction : VectorViewModelAction { object RetryFetchingInfo : RoomMemberProfileAction() @@ -18,7 +19,7 @@ sealed class RoomMemberProfileAction : VectorViewModelAction { object InviteUser : RoomMemberProfileAction() object VerifyUser : RoomMemberProfileAction() object ShareRoomMemberProfile : RoomMemberProfileAction() - data class SetPowerLevel(val previousValue: Int, val newValue: Int, val askForValidation: Boolean) : RoomMemberProfileAction() + data class SetPowerLevel(val previousValue: UserPowerLevel, val newValue: UserPowerLevel.Value, val askForValidation: Boolean) : RoomMemberProfileAction() data class SetUserColorOverride(val newColorSpec: String) : RoomMemberProfileAction() data class OpenOrCreateDm(val userId: String) : RoomMemberProfileAction() } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 95ee18682e..f4420d6a42 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -17,8 +17,8 @@ import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import im.vector.lib.strings.CommonStrings import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import javax.inject.Inject class RoomMemberProfileController @Inject constructor( @@ -38,7 +38,7 @@ class RoomMemberProfileController @Inject constructor( fun onOverrideColorClicked() fun onJumpToReadReceiptClicked() fun onMentionClicked() - fun onEditPowerLevel(currentRole: Role) + fun onEditPowerLevel(userPowerLevel: UserPowerLevel) fun onKickClicked(isSpace: Boolean) fun onBanClicked(isSpace: Boolean, isUserBanned: Boolean) fun onCancelInviteClicked() @@ -243,11 +243,10 @@ class RoomMemberProfileController @Inject constructor( } private fun buildAdminSection(state: RoomMemberProfileViewState) { - val powerLevelsContent = state.powerLevelsContent ?: return val powerLevelsStr = state.userPowerLevelString() ?: return - val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) - val userPowerLevel = powerLevelsHelper.getUserRole(state.userId) - val myPowerLevel = powerLevelsHelper.getUserRole(session.myUserId) + val roomPowerLevels = state.roomPowerLevels ?: return + val userPowerLevel = roomPowerLevels.getUserPowerLevel(state.userId) + val myPowerLevel = roomPowerLevels.getUserPowerLevel(session.myUserId) if ((!state.isMine && myPowerLevel <= userPowerLevel)) { return } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 29cb42a686..836e3857cc 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -51,7 +51,7 @@ import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import im.vector.lib.strings.CommonStrings import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel -import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -377,9 +377,9 @@ class RoomMemberProfileFragment : .show() } - override fun onEditPowerLevel(currentRole: Role) { - EditPowerLevelDialogs.showChoice(requireActivity(), CommonStrings.power_level_edit_title, currentRole) { newPowerLevel -> - viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true)) + override fun onEditPowerLevel(userPowerLevel: UserPowerLevel) { + EditPowerLevelDialogs.showChoice(requireActivity(), CommonStrings.power_level_edit_title, userPowerLevel) { newPowerLevel -> + viewModel.handle(RoomMemberProfileAction.SetPowerLevel(userPowerLevel, newPowerLevel, true)) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt index d806f521ad..11bb19c51b 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewEvents.kt @@ -8,6 +8,7 @@ package im.vector.app.features.roommemberprofile import im.vector.app.core.platform.VectorViewEvents +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel /** * Transient events for RoomMemberProfile. @@ -22,8 +23,8 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents { object OnInviteActionSuccess : RoomMemberProfileViewEvents() object OnKickActionSuccess : RoomMemberProfileViewEvents() object OnBanActionSuccess : RoomMemberProfileViewEvents() - data class ShowPowerLevelValidation(val currentValue: Int, val newValue: Int) : RoomMemberProfileViewEvents() - data class ShowPowerLevelDemoteWarning(val currentValue: Int, val newValue: Int) : RoomMemberProfileViewEvents() + data class ShowPowerLevelValidation(val currentValue: UserPowerLevel, val newValue: UserPowerLevel.Value) : RoomMemberProfileViewEvents() + data class ShowPowerLevelDemoteWarning(val currentValue: UserPowerLevel, val newValue: UserPowerLevel.Value) : RoomMemberProfileViewEvents() data class StartVerification( val userId: String, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 91024ee136..92ba7acfb5 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -42,9 +42,9 @@ import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm import org.matrix.android.sdk.api.session.room.model.RoomType -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem @@ -233,15 +233,15 @@ class RoomMemberProfileViewModel @AssistedInject constructor( if (room == null || action.previousValue == action.newValue) { return@withState } - val currentPowerLevelsContent = state.powerLevelsContent ?: return@withState - val myPowerLevel = PowerLevelsHelper(currentPowerLevelsContent).getUserPowerLevelValue(session.myUserId) + val roomPowerLevels = state.roomPowerLevels ?: return@withState + val myPowerLevel = roomPowerLevels.getUserPowerLevel(session.myUserId) if (action.askForValidation && action.newValue >= myPowerLevel) { _viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelValidation(action.previousValue, action.newValue)) } else if (action.askForValidation && state.isMine) { _viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelDemoteWarning(action.previousValue, action.newValue)) } else { - val newPowerLevelsContent = currentPowerLevelsContent - .setUserPowerLevel(state.userId, action.newValue) + val newPowerLevelsContent = (roomPowerLevels.powerLevelsContent ?: PowerLevelsContent()) + .setUserPowerLevel(state.userId, action.newValue.value) .toContent() viewModelScope.launch { _viewEvents.post(RoomMemberProfileViewEvents.Loading()) @@ -361,19 +361,17 @@ class RoomMemberProfileViewModel @AssistedInject constructor( private fun observeRoomSummaryAndPowerLevels(room: Room) { val roomSummaryLive = room.flow().liveRoomSummary().unwrap() - val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() - - powerLevelsContentLive - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) + val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + powerLevelsFlow + .onEach { roomPowerLevels -> val permissions = ActionPermissions( - canKick = powerLevelsHelper.isUserAbleToKick(session.myUserId), - canBan = powerLevelsHelper.isUserAbleToBan(session.myUserId), - canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId), - canEditPowerLevel = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_POWER_LEVELS) + canKick = roomPowerLevels.isUserAbleToKick(session.myUserId), + canBan = roomPowerLevels.isUserAbleToBan(session.myUserId), + canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId), + canEditPowerLevel = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_POWER_LEVELS) ) setState { - copy(powerLevelsContent = it, actionPermissions = permissions) + copy(roomPowerLevels = roomPowerLevels, actionPermissions = permissions) } }.launchIn(viewModelScope) @@ -388,14 +386,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor( copy(isRoomEncrypted = false) } } - roomSummaryLive.combine(powerLevelsContentLive) { roomSummary, powerLevelsContent -> + roomSummaryLive.combine(powerLevelsFlow) { roomSummary, roomPowerLevels -> val roomName = roomSummary.toMatrixItem().getBestName() - val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) - when (val userPowerLevel = powerLevelsHelper.getUserRole(initialState.userId)) { + when (roomPowerLevels.getUserRole(initialState.userId)) { + Role.SuperAdmin, + Role.Creator, Role.Admin -> stringProvider.getString(CommonStrings.room_member_power_level_admin_in, roomName) Role.Moderator -> stringProvider.getString(CommonStrings.room_member_power_level_moderator_in, roomName) - Role.Default -> stringProvider.getString(CommonStrings.room_member_power_level_default_in, roomName) - is Role.Custom -> stringProvider.getString(CommonStrings.room_member_power_level_custom_in, userPowerLevel.value, roomName) + Role.User -> stringProvider.getString(CommonStrings.room_member_power_level_default_in, roomName) } }.execute { copy(userPowerLevelString = it) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt index 7d550c891d..a51eeef8e3 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -12,7 +12,7 @@ import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.util.MatrixItem data class RoomMemberProfileViewState( @@ -24,7 +24,7 @@ data class RoomMemberProfileViewState( val isIgnored: Async = Uninitialized, val isRoomEncrypted: Boolean = false, val isAlgorithmSupported: Boolean = true, - val powerLevelsContent: PowerLevelsContent? = null, + val roomPowerLevels: RoomPowerLevels? = null, val userPowerLevelString: Async = Uninitialized, val userMatrixItem: Async = Uninitialized, val userMXCrossSigningInfo: MXCrossSigningInfo? = null, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt index ffe6314592..6d24f198f4 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt @@ -19,6 +19,7 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.databinding.DialogEditPowerLevelBinding import im.vector.lib.strings.CommonStrings import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel object EditPowerLevelDialogs { @@ -26,21 +27,21 @@ object EditPowerLevelDialogs { fun showChoice( activity: Activity, @StringRes titleRes: Int, - currentRole: Role, - listener: (Int) -> Unit + currentPowerLevel: UserPowerLevel, + listener: (UserPowerLevel.Value) -> Unit ) { val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null) val views = DialogEditPowerLevelBinding.bind(dialogLayout) views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId -> views.powerLevelCustomEditLayout.isVisible = checkedId == R.id.powerLevelCustomRadio } - views.powerLevelCustomEdit.setText("${currentRole.value}") - + val currentRole = Role.getSuggestedRole(currentPowerLevel) when (currentRole) { + Role.Creator -> views.powerLevelAdminRadio.isChecked = true + Role.SuperAdmin -> views.powerLevelAdminRadio.isChecked = true Role.Admin -> views.powerLevelAdminRadio.isChecked = true Role.Moderator -> views.powerLevelModeratorRadio.isChecked = true - Role.Default -> views.powerLevelDefaultRadio.isChecked = true - else -> views.powerLevelCustomRadio.isChecked = true + Role.User -> views.powerLevelDefaultRadio.isChecked = true } MaterialAlertDialogBuilder(activity) @@ -48,14 +49,14 @@ object EditPowerLevelDialogs { .setView(dialogLayout) .setPositiveButton(CommonStrings.edit) { _, _ -> val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) { - R.id.powerLevelAdminRadio -> Role.Admin.value - R.id.powerLevelModeratorRadio -> Role.Moderator.value - R.id.powerLevelDefaultRadio -> Role.Default.value - else -> { - views.powerLevelCustomEdit.text?.toString()?.toInt() ?: currentRole.value - } + R.id.powerLevelAdminRadio -> UserPowerLevel.Admin + R.id.powerLevelModeratorRadio -> UserPowerLevel.Moderator + R.id.powerLevelDefaultRadio -> UserPowerLevel.User + else -> null + } + if(newValue != null) { + listener(newValue) } - listener(newValue) } .setNegativeButton(CommonStrings.action_cancel, null) .setOnKeyListener(DialogInterface.OnKeyListener diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index 83b1c5a950..deb609509a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -39,7 +39,7 @@ import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.state.isPublic import org.matrix.android.sdk.flow.FlowRoom import org.matrix.android.sdk.flow.flow @@ -115,9 +115,8 @@ class RoomProfileViewModel @AssistedInject constructor( private fun observePowerLevels() { val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) - val canUpdateRoomState = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) + .onEach { roomPowerLevels -> + val canUpdateRoomState = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) setState { copy(canUpdateRoomState = canUpdateRoomState) } @@ -158,10 +157,9 @@ class RoomProfileViewModel @AssistedInject constructor( private fun observePermissions() { PowerLevelsFlowFactory(room) .createFlow() - .setOnEach { - val powerLevelsHelper = PowerLevelsHelper(it) + .setOnEach { roomPowerLevels -> val permissions = RoomProfileViewState.ActionPermissions( - canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) + canEnableEncryption = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) ) copy(actionPermissions = permissions) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index a80e7b2050..73f8e92434 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap @@ -127,10 +127,9 @@ class RoomAliasViewModel @AssistedInject constructor( private fun observePowerLevel() { PowerLevelsFlowFactory(room) .createFlow() - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) + .onEach { roomPowerLevels -> val permissions = RoomAliasViewState.ActionPermissions( - canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend( + canChangeCanonicalAlias = roomPowerLevels.isUserAllowedToSend( userId = session.myUserId, isState = true, eventType = EventType.STATE_ROOM_CANONICAL_ALIAS diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index df697bd501..acfdbb8b1c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap @@ -62,12 +62,10 @@ class RoomBannedMemberListViewModel @AssistedInject constructor( ) } - val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() - - powerLevelsContentLive - .setOnEach { - val powerLevelsHelper = PowerLevelsHelper(it) - copy(canUserBan = powerLevelsHelper.isUserAbleToBan(session.myUserId)) + val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + powerLevelsFlow + .setOnEach { roomPowerLevels -> + copy(canUserBan = roomPowerLevels.isUserAbleToBan(session.myUserId)) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt new file mode 100644 index 0000000000..a4ce99f63d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2020-2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package im.vector.app.features.roomprofile.members + +import javax.inject.Inject + +class RoomMemberListComparator @Inject constructor() : Comparator { + + override fun compare(leftRoomMember: RoomMemberWithPowerLevel?, rightRoomMember: RoomMemberWithPowerLevel?): Int { + return when (leftRoomMember) { + null -> + when (rightRoomMember) { + null -> 0 + else -> 1 + } + else -> + when (rightRoomMember) { + null -> -1 + else -> + when { + leftRoomMember.powerLevel > rightRoomMember.powerLevel -> 1 + leftRoomMember.powerLevel < rightRoomMember.powerLevel -> -1 + leftRoomMember.summary.displayName.isNullOrBlank() -> + when { + rightRoomMember.summary.displayName.isNullOrBlank() -> { + // No display names, compare ids + leftRoomMember.summary.userId.compareTo(rightRoomMember.summary.userId) + } + else -> 1 + } + else -> + when { + rightRoomMember.summary.displayName.isNullOrBlank() -> -1 + else -> { + when (leftRoomMember.summary.displayName) { + rightRoomMember.summary.displayName -> + // Same display name, compare id + leftRoomMember.summary.userId.compareTo(rightRoomMember.summary.userId) + else -> + leftRoomMember.summary.displayName!!.compareTo(rightRoomMember.summary.displayName!!, true) + } + } + } + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt index 88f55aac70..24d2dddc8e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt @@ -16,11 +16,13 @@ import im.vector.app.core.extensions.join import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roomprofile.permissions.RoleFormatter import me.gujun.android.span.span import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent +import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -29,7 +31,8 @@ class RoomMemberListController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val stringProvider: StringProvider, private val colorProvider: ColorProvider, - private val roomMemberSummaryFilter: RoomMemberSummaryFilter + private val roomMemberSummaryFilter: RoomMemberSummaryFilter, + private val roleFormatter: RoleFormatter, ) : TypedEpoxyController() { interface Callback { @@ -56,13 +59,13 @@ class RoomMemberListController @Inject constructor( .orEmpty() var threePidInvitesDone = filteredThreePidInvites.isEmpty() - for ((powerLevelCategory, roomMemberList) in roomMembersByPowerLevel) { - val filteredRoomMemberList = roomMemberList.filter { roomMemberSummaryFilter.test(it) } + for ((category, roomMemberList) in roomMembersByPowerLevel) { + val filteredRoomMemberList = roomMemberList.filter { roomMemberSummaryFilter.test(it.summary) } if (filteredRoomMemberList.isEmpty()) { continue } - if (powerLevelCategory == RoomMemberListCategories.USER && !threePidInvitesDone) { + if (category == RoomMemberListCategories.USER && !threePidInvitesDone) { // If there is no regular invite, display threepid invite before the regular user buildProfileSection( stringProvider.getString(RoomMemberListCategories.INVITE.titleRes) @@ -73,20 +76,20 @@ class RoomMemberListController @Inject constructor( } buildProfileSection( - stringProvider.getString(powerLevelCategory.titleRes) + stringProvider.getString(category.titleRes) ) filteredRoomMemberList.join( each = { _, roomMember -> - buildRoomMember(roomMember, powerLevelCategory, host, data) + buildRoomMember(roomMember, host, data) }, between = { _, roomMemberBefore -> dividerItem { - id("divider_${roomMemberBefore.userId}") + id("divider_${roomMemberBefore.summary.userId}") } } ) - if (powerLevelCategory == RoomMemberListCategories.INVITE && !threePidInvitesDone) { + if (category == RoomMemberListCategories.INVITE && !threePidInvitesDone) { // Display the threepid invite after the regular invite dividerItem { id("divider_threepidinvites") @@ -108,24 +111,24 @@ class RoomMemberListController @Inject constructor( } private fun buildRoomMember( - roomMember: RoomMemberSummary, - powerLevelCategory: RoomMemberListCategories, + roomMember: RoomMemberWithPowerLevel, host: RoomMemberListController, data: RoomMemberListViewState ) { - val powerLabel = stringProvider.getString(powerLevelCategory.titleRes) + val role = Role.getSuggestedRole(roomMember.powerLevel) + val powerLabel = roleFormatter.format(role) profileMatrixItemWithPowerLevelWithPresence { - id(roomMember.userId) - matrixItem(roomMember.toMatrixItem()) + id(roomMember.summary.userId) + matrixItem(roomMember.summary.toMatrixItem()) avatarRenderer(host.avatarRenderer) - userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) + userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.summary.userId)) clickListener { - host.callback?.onRoomMemberClicked(roomMember) + host.callback?.onRoomMemberClicked(roomMember.summary) } showPresence(true) - userPresence(roomMember.userPresence) - ignoredUser(roomMember.userId in data.ignoredUserIds) + userPresence(roomMember.summary.userPresence) + ignoredUser(roomMember.summary.userId in data.ignoredUserIds) powerLevelLabel( span { span(powerLabel) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 465da6a9c2..2974e10649 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -31,22 +31,19 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class RoomMemberListViewModel @AssistedInject constructor( @Assisted initialState: RoomMemberListViewState, - private val roomMemberSummaryComparator: RoomMemberSummaryComparator, + private val roomMemberListComparator: RoomMemberListComparator, private val session: Session ) : VectorViewModel(initialState) { @@ -75,14 +72,12 @@ class RoomMemberListViewModel @AssistedInject constructor( memberships = Membership.activeMemberships() } + val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() combine( roomFlow.liveRoomMembers(roomMemberQueryParams), - roomFlow - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - .mapOptional { it.content.toModel() } - .unwrap() - ) { roomMembers, powerLevelsContent -> - buildRoomMemberSummaries(powerLevelsContent, roomMembers) + powerLevelsFlow, + ) { roomMembers, roomPowerLevels -> + buildRoomMemberSummaries(roomPowerLevels, roomMembers) } .execute { async -> copy(roomMemberSummaries = async) @@ -143,10 +138,10 @@ class RoomMemberListViewModel @AssistedInject constructor( private fun observePowerLevel() { PowerLevelsFlowFactory(room).createFlow() - .onEach { + .onEach { roomPowerLevels -> val permissions = ActionPermissions( - canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId), - canRevokeThreePidInvite = PowerLevelsHelper(it).isUserAllowedToSend( + canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId), + canRevokeThreePidInvite = roomPowerLevels.isUserAllowedToSend( userId = session.myUserId, isState = true, eventType = EventType.STATE_ROOM_THIRD_PARTY_INVITE @@ -184,31 +179,34 @@ class RoomMemberListViewModel @AssistedInject constructor( } } - 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) + private fun buildRoomMemberSummaries(roomPowerLevels: RoomPowerLevels, roomMembers: List): RoomMembersByRole { + val admins = ArrayList() + val moderators = ArrayList() + val users = ArrayList(roomMembers.size) + val invites = ArrayList() roomMembers .forEach { roomMember -> - val userRole = powerLevelsHelper.getUserRole(roomMember.userId) + val powerLevel = roomPowerLevels.getUserPowerLevel(roomMember.userId) + val userRole = Role.getSuggestedRole(powerLevel) + val roomMemberWithPowerLevel = RoomMemberWithPowerLevel( + powerLevel = powerLevel, + summary = roomMember, + ) when { - roomMember.membership == Membership.INVITE -> invites.add(roomMember) - userRole == Role.Admin -> admins.add(roomMember) - userRole == Role.Moderator -> moderators.add(roomMember) - userRole == Role.Default -> users.add(roomMember) - else -> customs.add(roomMember) + roomMember.membership == Membership.INVITE -> invites.add(roomMemberWithPowerLevel) + userRole == Role.SuperAdmin || + userRole == Role.Creator || + userRole == Role.Admin -> admins.add(roomMemberWithPowerLevel) + userRole == Role.Moderator -> moderators.add(roomMemberWithPowerLevel) + userRole == Role.User -> users.add(roomMemberWithPowerLevel) } } return listOf( - RoomMemberListCategories.ADMIN to admins.sortedWith(roomMemberSummaryComparator), - RoomMemberListCategories.MODERATOR to moderators.sortedWith(roomMemberSummaryComparator), - RoomMemberListCategories.CUSTOM to customs.sortedWith(roomMemberSummaryComparator), - RoomMemberListCategories.INVITE to invites.sortedWith(roomMemberSummaryComparator), - RoomMemberListCategories.USER to users.sortedWith(roomMemberSummaryComparator) + RoomMemberListCategories.ADMIN to admins.sortedWith(roomMemberListComparator), + RoomMemberListCategories.MODERATOR to moderators.sortedWith(roomMemberListComparator), + RoomMemberListCategories.INVITE to invites.sortedWith(roomMemberListComparator), + RoomMemberListCategories.USER to users.sortedWith(roomMemberListComparator) ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt index 90da131cb5..d68a9fceba 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt @@ -18,11 +18,12 @@ import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel data class RoomMemberListViewState( val roomId: String, val roomSummary: Async = Uninitialized, - val roomMemberSummaries: Async = Uninitialized, + val roomMemberSummaries: Async = Uninitialized, val areAllMembersLoaded: Boolean = false, val ignoredUserIds: List = emptyList(), val filter: String = "", @@ -41,12 +42,16 @@ data class ActionPermissions( val canRevokeThreePidInvite: Boolean = false ) -typealias RoomMemberSummaries = List>> +data class RoomMemberWithPowerLevel( + val powerLevel: UserPowerLevel, + val summary: RoomMemberSummary, +) + +typealias RoomMembersByRole = List>> enum class RoomMemberListCategories(@StringRes val titleRes: Int) { ADMIN(CommonStrings.room_member_power_level_admins), MODERATOR(CommonStrings.room_member_power_level_moderators), - CUSTOM(CommonStrings.room_member_power_level_custom), INVITE(CommonStrings.room_member_power_level_invites), USER(CommonStrings.room_member_power_level_users) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryComparator.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryComparator.kt deleted file mode 100644 index fbff99b287..0000000000 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryComparator.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020-2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package im.vector.app.features.roomprofile.members - -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import javax.inject.Inject - -class RoomMemberSummaryComparator @Inject constructor() : Comparator { - - override fun compare(leftRoomMemberSummary: RoomMemberSummary?, rightRoomMemberSummary: RoomMemberSummary?): Int { - return when (leftRoomMemberSummary) { - null -> - when (rightRoomMemberSummary) { - null -> 0 - else -> 1 - } - else -> - when (rightRoomMemberSummary) { - null -> -1 - else -> - when { - leftRoomMemberSummary.displayName.isNullOrBlank() -> - when { - rightRoomMemberSummary.displayName.isNullOrBlank() -> { - // No display names, compare ids - leftRoomMemberSummary.userId.compareTo(rightRoomMemberSummary.userId) - } - else -> 1 - } - else -> - when { - rightRoomMemberSummary.displayName.isNullOrBlank() -> -1 - else -> { - when (leftRoomMemberSummary.displayName) { - rightRoomMemberSummary.displayName -> - // Same display name, compare id - leftRoomMemberSummary.userId.compareTo(rightRoomMemberSummary.userId) - else -> - leftRoomMemberSummary.displayName!!.compareTo(rightRoomMemberSummary.displayName!!, true) - } - } - } - } - } - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoleFormatter.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoleFormatter.kt index 6bc62cb53d..4febb822ad 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoleFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoleFormatter.kt @@ -19,8 +19,9 @@ class RoleFormatter @Inject constructor( return when (role) { Role.Admin -> stringProvider.getString(CommonStrings.power_level_admin) Role.Moderator -> stringProvider.getString(CommonStrings.power_level_moderator) - Role.Default -> stringProvider.getString(CommonStrings.power_level_default) - is Role.Custom -> stringProvider.getString(CommonStrings.power_level_custom, role.value) + Role.User -> stringProvider.getString(CommonStrings.power_level_default) + Role.Creator -> stringProvider.getString(CommonStrings.power_level_owner) + Role.SuperAdmin -> stringProvider.getString(CommonStrings.power_level_owner) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt index a8eb77bd03..cb4376c98f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt @@ -8,9 +8,10 @@ package im.vector.app.features.roomprofile.permissions import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel sealed class RoomPermissionsAction : VectorViewModelAction { object ToggleShowAllPermissions : RoomPermissionsAction() - data class UpdatePermission(val editablePermission: EditablePermission, val powerLevel: Int) : RoomPermissionsAction() + data class UpdatePermission(val editablePermission: EditablePermission, val powerLevel: UserPowerLevel.Value) : RoomPermissionsAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt index 2e5d89e409..e5255eb435 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt @@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.redactOrDefault import org.matrix.android.sdk.api.session.room.model.stateDefaultOrDefault import org.matrix.android.sdk.api.session.room.model.usersDefaultOrDefault import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import javax.inject.Inject class RoomPermissionsController @Inject constructor( @@ -34,7 +35,7 @@ class RoomPermissionsController @Inject constructor( ) : TypedEpoxyController() { interface Callback { - fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) + fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel) fun toggleShowAllPermissions() } @@ -165,7 +166,8 @@ class RoomPermissionsController @Inject constructor( editable: Boolean, isSpace: Boolean ) { - val currentRole = getCurrentRole(editablePermission, content) + val currentPowerLevel = getPowerLevel(editablePermission, content) + val currentRole = Role.getSuggestedRole(currentPowerLevel) buildProfileAction( id = editablePermission.labelResId.toString(), title = stringProvider.getString( @@ -177,12 +179,12 @@ class RoomPermissionsController @Inject constructor( action = { callback ?.takeIf { editable } - ?.onEditPermission(editablePermission, currentRole) + ?.onEditPermission(editablePermission, currentPowerLevel) } ) } - private fun getCurrentRole(editablePermission: EditablePermission, content: PowerLevelsContent): Role { + private fun getPowerLevel(editablePermission: EditablePermission, content: PowerLevelsContent): UserPowerLevel.Value { val value = when (editablePermission) { is EditablePermission.EventTypeEditablePermission -> content.events?.get(editablePermission.eventType) ?: content.stateDefaultOrDefault() is EditablePermission.DefaultRole -> content.usersDefaultOrDefault() @@ -194,20 +196,6 @@ class RoomPermissionsController @Inject constructor( is EditablePermission.RemoveMessagesSentByOthers -> content.redactOrDefault() is EditablePermission.NotifyEveryone -> content.notificationLevel(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY) } - - return Role.fromValue( - value, - when (editablePermission) { - is EditablePermission.EventTypeEditablePermission -> content.stateDefaultOrDefault() - is EditablePermission.DefaultRole -> Role.Default.value - is EditablePermission.SendMessages -> Role.Default.value - is EditablePermission.InviteUsers -> Role.Moderator.value - is EditablePermission.ChangeSettings -> Role.Moderator.value - is EditablePermission.KickUsers -> Role.Moderator.value - is EditablePermission.BanUsers -> Role.Moderator.value - is EditablePermission.RemoveMessagesSentByOthers -> Role.Moderator.value - is EditablePermission.NotifyEveryone -> Role.Moderator.value - } - ) + return UserPowerLevel.Value(value) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt index 5d92a96b5f..ea1afbcfc5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt @@ -26,7 +26,7 @@ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.lib.strings.CommonStrings -import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -93,8 +93,8 @@ class RoomPermissionsFragment : } } - override fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) { - EditPowerLevelDialogs.showChoice(requireActivity(), editablePermission.labelResId, currentRole) { newPowerLevel -> + override fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel) { + EditPowerLevelDialogs.showChoice(requireActivity(), editablePermission.labelResId, currentPowerLevel) { newPowerLevel -> viewModel.handle(RoomPermissionsAction.UpdatePermission(editablePermission, newPowerLevel)) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 0b942d78cb..3757a1ddd0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -9,6 +9,7 @@ package im.vector.app.features.roomprofile.permissions import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -24,7 +25,6 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap @@ -61,19 +61,23 @@ class RoomPermissionsViewModel @AssistedInject constructor( private fun observePowerLevel() { PowerLevelsFlowFactory(room) .createFlow() - .onEach { powerLevelContent -> - val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) + .onEach { roomPowerLevels -> val permissions = RoomPermissionsViewState.ActionPermissions( - canChangePowerLevels = powerLevelsHelper.isUserAllowedToSend( + canChangePowerLevels = roomPowerLevels.isUserAllowedToSend( userId = session.myUserId, isState = true, eventType = EventType.STATE_ROOM_POWER_LEVELS ) ) + val powerLevelsContent = roomPowerLevels.powerLevelsContent setState { copy( actionPermissions = permissions, - currentPowerLevelsContent = Success(powerLevelContent) + currentPowerLevelsContent = if (powerLevelsContent != null) { + Success(powerLevelsContent) + } else { + Uninitialized + } ) } }.launchIn(viewModelScope) @@ -94,26 +98,26 @@ class RoomPermissionsViewModel @AssistedInject constructor( private fun updatePermission(action: RoomPermissionsAction.UpdatePermission) { withState { state -> - val currentPowerLevel = state.currentPowerLevelsContent.invoke() ?: return@withState + val currentPowerLevelsContent = state.currentPowerLevelsContent.invoke() ?: return@withState postLoading(true) viewModelScope.launch { try { val newPowerLevelsContent = when (action.editablePermission) { - is EditablePermission.EventTypeEditablePermission -> currentPowerLevel.copy( - events = currentPowerLevel.events.orEmpty().toMutableMap().apply { - put(action.editablePermission.eventType, action.powerLevel) + is EditablePermission.EventTypeEditablePermission -> currentPowerLevelsContent.copy( + events = currentPowerLevelsContent.events.orEmpty().toMutableMap().apply { + put(action.editablePermission.eventType, action.powerLevel.value) } ) - is EditablePermission.DefaultRole -> currentPowerLevel.copy(usersDefault = action.powerLevel) - is EditablePermission.SendMessages -> currentPowerLevel.copy(eventsDefault = action.powerLevel) - is EditablePermission.InviteUsers -> currentPowerLevel.copy(invite = action.powerLevel) - is EditablePermission.ChangeSettings -> currentPowerLevel.copy(stateDefault = action.powerLevel) - is EditablePermission.KickUsers -> currentPowerLevel.copy(kick = action.powerLevel) - is EditablePermission.BanUsers -> currentPowerLevel.copy(ban = action.powerLevel) - is EditablePermission.RemoveMessagesSentByOthers -> currentPowerLevel.copy(redact = action.powerLevel) - is EditablePermission.NotifyEveryone -> currentPowerLevel.copy( - notifications = currentPowerLevel.notifications.orEmpty().toMutableMap().apply { - put(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY, action.powerLevel) + is EditablePermission.DefaultRole -> currentPowerLevelsContent.copy(usersDefault = action.powerLevel.value) + is EditablePermission.SendMessages -> currentPowerLevelsContent.copy(eventsDefault = action.powerLevel.value) + is EditablePermission.InviteUsers -> currentPowerLevelsContent.copy(invite = action.powerLevel.value) + is EditablePermission.ChangeSettings -> currentPowerLevelsContent.copy(stateDefault = action.powerLevel.value) + is EditablePermission.KickUsers -> currentPowerLevelsContent.copy(kick = action.powerLevel.value) + is EditablePermission.BanUsers -> currentPowerLevelsContent.copy(ban = action.powerLevel.value) + is EditablePermission.RemoveMessagesSentByOthers -> currentPowerLevelsContent.copy(redact = action.powerLevel.value) + is EditablePermission.NotifyEveryone -> currentPowerLevelsContent.copy( + notifications = currentPowerLevelsContent.notifications.orEmpty().toMutableMap().apply { + put(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY, action.powerLevel.value) } ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index af043b9ab2..188068afb4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -32,7 +32,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap @@ -115,28 +114,26 @@ class RoomSettingsViewModel @AssistedInject constructor( ) } - val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() - - powerLevelsContentLive - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) + val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + powerLevelsFlow + .onEach { roomPowerLevels -> val permissions = RoomSettingsViewState.ActionPermissions( - canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR), - canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), - canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), - canChangeHistoryVisibility = powerLevelsHelper.isUserAllowedToSend( + canChangeAvatar = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR), + canChangeName = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), + canChangeTopic = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), + canChangeHistoryVisibility = roomPowerLevels.isUserAllowedToSend( session.myUserId, true, EventType.STATE_ROOM_HISTORY_VISIBILITY ), - canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend( + canChangeJoinRule = roomPowerLevels.isUserAllowedToSend( session.myUserId, true, EventType.STATE_ROOM_JOIN_RULES ) && - powerLevelsHelper.isUserAllowedToSend( + roomPowerLevels.isUserAllowedToSend( session.myUserId, true, EventType.STATE_ROOM_GUEST_ACCESS ), - canAddChildren = powerLevelsHelper.isUserAllowedToSend( + canAddChildren = roomPowerLevels.isUserAllowedToSend( session.myUserId, true, EventType.STATE_SPACE_CHILD ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index 404cfac9de..cebfb63f1f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -32,7 +32,6 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow @@ -74,20 +73,19 @@ class SpaceMenuViewModel @AssistedInject constructor( PowerLevelsFlowFactory(room) .createFlow() - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) + .onEach { roomPowerLevels -> - val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId) - val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) + val canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId) + val canAddChild = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) - val canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR) - val canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME) - val canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC) + val canChangeAvatar = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR) + val canChangeName = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME) + val canChangeTopic = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC) - val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin + val isAdmin = roomPowerLevels.getUserRole(session.myUserId) == Role.Admin val otherAdminCount = roomSummary?.otherMemberIds - ?.map { powerLevelsHelper.getUserRole(it) } - ?.count { it is Role.Admin } + ?.map { roomPowerLevels.getUserRole(it) } + ?.count { it == Role.Admin } ?: 0 val isLastAdmin = isAdmin && otherAdminCount == 0 diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt index bf91be4fa0..ca65b99fd4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.session.space.CreateSpaceParams import timber.log.Timber import javax.inject.Inject @@ -65,7 +66,7 @@ class CreateSpaceViewModelTask @Inject constructor( if (params.isPublic) { this.roomAliasName = params.spaceAlias this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( - invite = Role.Default.value + invite = UserPowerLevel.User.value ) this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE @@ -79,7 +80,7 @@ class CreateSpaceViewModelTask @Inject constructor( } ) this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( - invite = Role.Moderator.value + invite = UserPowerLevel.Moderator.value ) } }) diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index ae112908fc..3627dd5469 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -36,7 +36,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow import timber.log.Timber @@ -96,16 +95,14 @@ class SpaceDirectoryViewModel @AssistedInject constructor( private fun observePermissions() { val room = session.getRoom(initialState.spaceId) ?: return - val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() - powerLevelsContentLive - .onEach { - val powerLevelsHelper = PowerLevelsHelper(it) + powerLevelsFlow + .onEach { roomPowerLevels -> setState { copy( - canAddRooms = powerLevelsHelper.isUserAllowedToSend( - session.myUserId, true, - EventType.STATE_SPACE_CHILD + canAddRooms = roomPowerLevels.isUserAllowedToSend( + userId = session.myUserId, isState = true, eventType = EventType.STATE_SPACE_CHILD ) ) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index de68b697ef..b8a3bbd07d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -31,10 +31,11 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow @@ -50,14 +51,12 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( init { val space = session.getRoom(initialState.spaceId) val spaceSummary = space?.roomSummary() - - val powerLevelsEvent = space?.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - powerLevelsEvent?.content?.toModel()?.let { powerLevelsContent -> - val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) - val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin + val roomPowerLevels = space?.getRoomPowerLevels() + roomPowerLevels?.let { + val isAdmin = roomPowerLevels.getUserRole(session.myUserId) == Role.Admin val otherAdminCount = spaceSummary?.otherMemberIds - ?.map { powerLevelsHelper.getUserRole(it) } - ?.count { it is Role.Admin } + ?.map { roomPowerLevels.getUserRole(it) } + ?.count { it == Role.Admin } ?: 0 val isLastAdmin = isAdmin && otherAdminCount == 0 setState { diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt index 1011952528..f8b5fada1a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt @@ -54,7 +54,7 @@ class SpacePeopleListController @Inject constructor( memberSummaries.forEach { memberEntry -> val filtered = memberEntry.second - .filter { roomMemberSummaryFilter.test(it) } + .filter { roomMemberSummaryFilter.test(it.summary) } if (filtered.isNotEmpty()) { dividerItem { id("divider_type_${memberEntry.first.titleRes}") @@ -65,10 +65,10 @@ class SpacePeopleListController @Inject constructor( .join( each = { _, roomMember -> profileMatrixItemWithPowerLevel { - id(roomMember.userId) - matrixItem(roomMember.toMatrixItem()) + id(roomMember.summary.userId) + matrixItem(roomMember.summary.toMatrixItem()) avatarRenderer(host.avatarRenderer) - userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) + userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.summary.userId)) .apply { val pl = host.toPowerLevelLabel(memberEntry.first) if (memberEntry.first == RoomMemberListCategories.INVITE) { @@ -106,13 +106,13 @@ class SpacePeopleListController @Inject constructor( } clickListener { - host.listener?.onSpaceMemberClicked(roomMember) + host.listener?.onSpaceMemberClicked(roomMember.summary) } } }, between = { _, roomMemberBefore -> dividerItem { - id("divider_${roomMemberBefore.userId}") + id("divider_${roomMemberBefore.summary.userId}") } } ) diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt index 09b23bbed8..f5b6a0a06c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt @@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoomSummary -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels class ShareSpaceViewModel @AssistedInject constructor( @Assisted private val initialState: ShareSpaceViewState, @@ -52,11 +52,10 @@ class ShareSpaceViewModel @AssistedInject constructor( val room = session.getRoom(initialState.spaceId) ?: return PowerLevelsFlowFactory(room) .createFlow() - .onEach { powerLevelContent -> - val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) + .onEach { roomPowerLevels -> setState { copy( - canInviteByMxId = powerLevelsHelper.isUserAbleToInvite(session.myUserId) + canInviteByMxId = roomPowerLevels.isUserAbleToInvite(session.myUserId) ) } } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt index 233cb82d5d..973ea11f47 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt @@ -33,10 +33,11 @@ import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap import timber.log.Timber @@ -139,12 +140,8 @@ class StartVoiceBroadcastUseCase @Inject constructor( @VisibleForTesting fun assertHasEnoughPowerLevels(room: Room) { - val powerLevelsHelper = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - ?.content - ?.toModel() - ?.let { PowerLevelsHelper(it) } - - if (powerLevelsHelper?.isUserAllowedToSend(session.myUserId, true, VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO) != true) { + val roomPowerLevels = room.getRoomPowerLevels() + if (!roomPowerLevels.isUserAllowedToSend(session.myUserId, true, VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO)) { Timber.d("## StartVoiceBroadcastUseCase: Cannot start voice broadcast: no permission") throw VoiceBroadcastFailure.RecordingError.NoPermission } diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt index 61f79662b0..326567fb07 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt @@ -25,10 +25,11 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.widgets.WidgetPostAPIMediator import org.matrix.android.sdk.api.util.JsonDict import timber.log.Timber @@ -146,13 +147,8 @@ class WidgetPostAPIHandler @AssistedInject constructor( Timber.d("## canSendEvent() : eventType $eventType isState $isState") - val powerLevelsEvent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - val powerLevelsContent = powerLevelsEvent?.content?.toModel() - val canSend = if (powerLevelsContent == null) { - false - } else { - PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(session.myUserId, isState, eventType) - } + val roomPowerLevels = room.getRoomPowerLevels() + val canSend = roomPowerLevels.isUserAllowedToSend(session.myUserId, isState, eventType) if (canSend) { Timber.d("## canSendEvent() returns true") widgetPostAPIMediator.sendBoolResponse(true, eventData) diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index 6580b52fca..1778423db7 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -19,9 +19,11 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -31,7 +33,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional @@ -102,11 +104,10 @@ class WidgetViewModel @AssistedInject constructor( if (room == null) { return } - room.flow().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - .mapOptional { it.content.toModel() } - .unwrap() - .map { - PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, null) + PowerLevelsFlowFactory(room) + .createFlow() + .map { roomPowerLevels -> + roomPowerLevels.isUserAllowedToSend(session.myUserId, true, null) } .setOnEach { copy(canManageWidgets = it) diff --git a/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt b/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt index 18ea98db3e..0f540bd2cd 100644 --- a/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt @@ -13,7 +13,7 @@ import com.airbnb.mvrx.test.MavericksTestRule import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.members.RoomMemberListViewModel import im.vector.app.features.roomprofile.members.RoomMemberListViewState -import im.vector.app.features.roomprofile.members.RoomMemberSummaryComparator +import im.vector.app.features.roomprofile.members.RoomMemberListComparator import im.vector.app.test.test import im.vector.app.test.testCoroutineDispatchers import io.mockk.coEvery @@ -266,7 +266,7 @@ class MemberListViewModelTest { private fun createViewModel(): RoomMemberListViewModel { return RoomMemberListViewModel( RoomMemberListViewState(args), - RoomMemberSummaryComparator(), + RoomMemberListComparator(), fakeSession, ) } From 2d21c15e3b3937fc2e01821fdc4d9975f90599ad Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 28 Jul 2025 16:49:57 +0200 Subject: [PATCH 3/8] change (power level) : continue handling v12 room --- .../sdk/api/session/room/powerlevels/Role.kt | 1 - .../room/powerlevels/RoomPowerLevels.kt | 2 +- .../timeline/format/NoticeEventFormatter.kt | 4 +- .../im/vector/app/features/powerlevel/Role.kt | 12 ++++ .../RoomMemberProfileController.kt | 4 +- .../RoomMemberProfileFragment.kt | 2 +- .../RoomMemberProfileViewModel.kt | 4 +- .../powerlevel/EditPowerLevelDialogs.kt | 32 ++++----- .../members/RoomMemberListComparator.kt | 4 +- .../permissions/RoomPermissionsController.kt | 2 +- .../permissions/RoomPermissionsFragment.kt | 2 +- .../app/features/spaces/SpaceMenuViewModel.kt | 4 +- .../leave/SpaceLeaveAdvanceViewState.kt | 2 +- .../leave/SpaceLeaveAdvancedFragment.kt | 2 +- .../leave/SpaceLeaveAdvancedViewModel.kt | 20 ++---- .../res/layout/dialog_edit_power_level.xml | 72 +++++++------------ 16 files changed, 77 insertions(+), 92 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/powerlevel/Role.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt index 57cd75e70d..dce48c8069 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/Role.kt @@ -24,7 +24,6 @@ enum class Role { Moderator, User; - companion object { fun getSuggestedRole(userPowerLevel: UserPowerLevel): Role { return when { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt index d7fbeb8769..0d66136730 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/RoomPowerLevels.kt @@ -59,7 +59,7 @@ class RoomPowerLevels( * @param userId the user id * @return the power level */ - fun getUserRole(userId: String): Role { + fun getSuggestedRole(userId: String): Role { val value = getUserPowerLevel(userId) return Role.getSuggestedRole(value) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 5a8d3fc41a..0e328bdbba 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -122,8 +122,8 @@ class NoticeEventFormatter @Inject constructor( userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys) val diffs = ArrayList() userIds.forEach { userId -> - val from = RoomPowerLevels(previousPowerLevelsContent,null).getUserRole(userId) - val to = RoomPowerLevels(powerLevelsContent, null).getUserRole(userId) + val from = RoomPowerLevels(previousPowerLevelsContent,null).getSuggestedRole(userId) + val to = RoomPowerLevels(powerLevelsContent, null).getSuggestedRole(userId) if (from != to) { val fromStr = roleFormatter.format(from) val toStr = roleFormatter.format(to) diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt b/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt new file mode 100644 index 0000000000..d61bbef768 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package im.vector.app.features.powerlevel + +import org.matrix.android.sdk.api.session.room.powerlevels.Role + +fun Role.isOwner() = this == Role.Creator || this == Role.SuperAdmin diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index f4420d6a42..308fdc5bce 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -17,7 +17,6 @@ import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import im.vector.lib.strings.CommonStrings import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import javax.inject.Inject @@ -38,7 +37,7 @@ class RoomMemberProfileController @Inject constructor( fun onOverrideColorClicked() fun onJumpToReadReceiptClicked() fun onMentionClicked() - fun onEditPowerLevel(userPowerLevel: UserPowerLevel) + fun onEditPowerLevel(userPowerLevel: UserPowerLevel.Value) fun onKickClicked(isSpace: Boolean) fun onBanClicked(isSpace: Boolean, isUserBanned: Boolean) fun onCancelInviteClicked() @@ -250,6 +249,7 @@ class RoomMemberProfileController @Inject constructor( if ((!state.isMine && myPowerLevel <= userPowerLevel)) { return } + if(userPowerLevel !is UserPowerLevel.Value) return val membership = state.asyncMembership() ?: return val canKick = !state.isMine && state.actionPermissions.canKick val canBan = !state.isMine && state.actionPermissions.canBan diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 836e3857cc..7f0ec9b57a 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -377,7 +377,7 @@ class RoomMemberProfileFragment : .show() } - override fun onEditPowerLevel(userPowerLevel: UserPowerLevel) { + override fun onEditPowerLevel(userPowerLevel: UserPowerLevel.Value) { EditPowerLevelDialogs.showChoice(requireActivity(), CommonStrings.power_level_edit_title, userPowerLevel) { newPowerLevel -> viewModel.handle(RoomMemberProfileAction.SetPowerLevel(userPowerLevel, newPowerLevel, true)) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 92ba7acfb5..14e286fd1d 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -388,9 +388,9 @@ class RoomMemberProfileViewModel @AssistedInject constructor( } roomSummaryLive.combine(powerLevelsFlow) { roomSummary, roomPowerLevels -> val roomName = roomSummary.toMatrixItem().getBestName() - when (roomPowerLevels.getUserRole(initialState.userId)) { + when (roomPowerLevels.getSuggestedRole(initialState.userId)) { Role.SuperAdmin, - Role.Creator, + Role.Creator -> stringProvider.getString(CommonStrings.room_member_power_level_owner_in, roomName) Role.Admin -> stringProvider.getString(CommonStrings.room_member_power_level_admin_in, roomName) Role.Moderator -> stringProvider.getString(CommonStrings.room_member_power_level_moderator_in, roomName) Role.User -> stringProvider.getString(CommonStrings.room_member_power_level_default_in, roomName) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt index 6d24f198f4..c750b91f8f 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt @@ -17,6 +17,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard import im.vector.app.databinding.DialogEditPowerLevelBinding +import im.vector.app.features.powerlevel.isOwner import im.vector.lib.strings.CommonStrings import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel @@ -27,46 +28,45 @@ object EditPowerLevelDialogs { fun showChoice( activity: Activity, @StringRes titleRes: Int, - currentPowerLevel: UserPowerLevel, + currentPowerLevel: UserPowerLevel.Value, listener: (UserPowerLevel.Value) -> Unit ) { val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null) val views = DialogEditPowerLevelBinding.bind(dialogLayout) - views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId -> - views.powerLevelCustomEditLayout.isVisible = checkedId == R.id.powerLevelCustomRadio - } val currentRole = Role.getSuggestedRole(currentPowerLevel) when (currentRole) { - Role.Creator -> views.powerLevelAdminRadio.isChecked = true - Role.SuperAdmin -> views.powerLevelAdminRadio.isChecked = true + Role.Creator, + Role.SuperAdmin -> views.powerLevelOwnerRadio.isChecked = true Role.Admin -> views.powerLevelAdminRadio.isChecked = true Role.Moderator -> views.powerLevelModeratorRadio.isChecked = true Role.User -> views.powerLevelDefaultRadio.isChecked = true } - + views.powerLevelOwnerRadio.isVisible = currentRole.isOwner() MaterialAlertDialogBuilder(activity) .setTitle(titleRes) .setView(dialogLayout) .setPositiveButton(CommonStrings.edit) { _, _ -> val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) { + R.id.powerLevelOwnerRadio -> UserPowerLevel.SuperAdmin R.id.powerLevelAdminRadio -> UserPowerLevel.Admin R.id.powerLevelModeratorRadio -> UserPowerLevel.Moderator R.id.powerLevelDefaultRadio -> UserPowerLevel.User else -> null } - if(newValue != null) { + if (newValue != null) { listener(newValue) } } .setNegativeButton(CommonStrings.action_cancel, null) - .setOnKeyListener(DialogInterface.OnKeyListener - { dialog, keyCode, event -> - if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { - dialog.cancel() - return@OnKeyListener true - } - false - }) + .setOnKeyListener( + DialogInterface.OnKeyListener + { dialog, keyCode, event -> + if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { + dialog.cancel() + return@OnKeyListener true + } + false + }) .setOnDismissListener { dialogLayout.hideKeyboard() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt index a4ce99f63d..ffb4eaf522 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListComparator.kt @@ -23,8 +23,8 @@ class RoomMemberListComparator @Inject constructor() : Comparator -1 else -> when { - leftRoomMember.powerLevel > rightRoomMember.powerLevel -> 1 - leftRoomMember.powerLevel < rightRoomMember.powerLevel -> -1 + leftRoomMember.powerLevel > rightRoomMember.powerLevel -> -1 + leftRoomMember.powerLevel < rightRoomMember.powerLevel -> 1 leftRoomMember.summary.displayName.isNullOrBlank() -> when { rightRoomMember.summary.displayName.isNullOrBlank() -> { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt index e5255eb435..78a405db49 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt @@ -35,7 +35,7 @@ class RoomPermissionsController @Inject constructor( ) : TypedEpoxyController() { interface Callback { - fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel) + fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel.Value) fun toggleShowAllPermissions() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt index ea1afbcfc5..d2e94bf504 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt @@ -93,7 +93,7 @@ class RoomPermissionsFragment : } } - override fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel) { + override fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel.Value) { EditPowerLevelDialogs.showChoice(requireActivity(), editablePermission.labelResId, currentPowerLevel) { newPowerLevel -> viewModel.handle(RoomPermissionsAction.UpdatePermission(editablePermission, newPowerLevel)) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index cebfb63f1f..c8ac7af8b8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -82,9 +82,9 @@ class SpaceMenuViewModel @AssistedInject constructor( val canChangeName = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME) val canChangeTopic = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC) - val isAdmin = roomPowerLevels.getUserRole(session.myUserId) == Role.Admin + val isAdmin = roomPowerLevels.getSuggestedRole(session.myUserId) == Role.Admin val otherAdminCount = roomSummary?.otherMemberIds - ?.map { roomPowerLevels.getUserRole(it) } + ?.map { roomPowerLevels.getSuggestedRole(it) } ?.count { it == Role.Admin } ?: 0 val isLastAdmin = isAdmin && otherAdminCount == 0 diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt index 48199cd2a2..0b1968b31e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt @@ -21,7 +21,7 @@ data class SpaceLeaveAdvanceViewState( val currentFilter: String = "", val leaveState: Async = Uninitialized, val isFilteringEnabled: Boolean = false, - val isLastAdmin: Boolean = false + val isLastOwner: Boolean = false ) : MavericksState { constructor(args: SpaceBottomSheetSettingsArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt index 1d9ff43a9d..1c1f33faa8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt @@ -61,7 +61,7 @@ class SpaceLeaveAdvancedFragment : state.spaceSummary?.let { summary -> val warningMessage: CharSequence? = when { summary.otherMemberIds.isEmpty() -> getString(CommonStrings.space_leave_prompt_msg_only_you) - state.isLastAdmin -> getString(CommonStrings.space_leave_prompt_msg_as_admin) + state.isLastOwner -> getString(CommonStrings.space_leave_prompt_msg_as_admin) !summary.isPublic -> getString(CommonStrings.space_leave_prompt_msg_private) else -> null } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index b8a3bbd07d..3c06244888 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -20,23 +20,17 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.powerlevel.isOwner import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import okhttp3.internal.toImmutableList -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.SpaceFilter import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.getRoomPowerLevels -import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels -import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap @@ -53,14 +47,14 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( val spaceSummary = space?.roomSummary() val roomPowerLevels = space?.getRoomPowerLevels() roomPowerLevels?.let { - val isAdmin = roomPowerLevels.getUserRole(session.myUserId) == Role.Admin - val otherAdminCount = spaceSummary?.otherMemberIds - ?.map { roomPowerLevels.getUserRole(it) } - ?.count { it == Role.Admin } + val isOwner = roomPowerLevels.getSuggestedRole(session.myUserId).isOwner() + val otherOwnersCount = spaceSummary?.otherMemberIds + ?.map { roomPowerLevels.getSuggestedRole(it) } + ?.count { it.isOwner() } ?: 0 - val isLastAdmin = isAdmin && otherAdminCount == 0 + val isLastOwner = isOwner && otherOwnersCount == 0 setState { - copy(isLastAdmin = isLastAdmin) + copy(isLastOwner = isLastOwner) } } diff --git a/vector/src/main/res/layout/dialog_edit_power_level.xml b/vector/src/main/res/layout/dialog_edit_power_level.xml index bf91dc3367..00f6fbce56 100644 --- a/vector/src/main/res/layout/dialog_edit_power_level.xml +++ b/vector/src/main/res/layout/dialog_edit_power_level.xml @@ -1,5 +1,7 @@ - - + android:text="@string/power_level_owner" + android:textColor="?vctr_content_primary" /> - - - - - - - - - - - + android:text="@string/power_level_admin" + android:textColor="?vctr_content_primary" /> - + - + - + From 864346c3c0a5f4039c93483060efff0a46a56cf1 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 29 Jul 2025 16:34:23 +0200 Subject: [PATCH 4/8] change (leave room) : warn on last admin when leaving rooms --- .../src/main/res/values/strings.xml | 1 + .../sdk/api/session/room/RoomExtensions.kt | 3 + .../home/room/list/RoomListFragment.kt | 27 ++------ .../home/room/list/RoomListViewModel.kt | 8 ++- .../room/list/home/HomeRoomListFragment.kt | 28 ++------ .../room/list/home/HomeRoomListViewModel.kt | 6 +- .../im/vector/app/features/powerlevel/Role.kt | 31 +++++++++ .../app/features/room/LeaveRoomPrompt.kt | 67 +++++++++++++++++++ .../roomprofile/RoomProfileFragment.kt | 26 +++---- .../roomprofile/RoomProfileViewModel.kt | 9 +++ .../roomprofile/RoomProfileViewState.kt | 1 + .../leave/SpaceLeaveAdvanceViewState.kt | 2 +- .../leave/SpaceLeaveAdvancedFragment.kt | 28 ++++---- .../leave/SpaceLeaveAdvancedViewModel.kt | 22 ++---- 14 files changed, 163 insertions(+), 96 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 02fef53306..f1922645ac 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -686,6 +686,7 @@ Leave room Are you sure you want to leave the room? This room is not public. You will not be able to rejoin without an invite. + You\'re the only admin of this room. Leaving it will mean no one has control over it. Direct Messages diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt index 29638857bc..f9c04ac34e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.api.session.room +import kotlinx.coroutines.flow.Flow import org.matrix.android.sdk.api.query.QueryStateEventValue import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event @@ -25,6 +26,8 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.internal.di.UserId /** * Get a TimelineEvent using the TimelineService of a Room. diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index 1e04880382..51b413058e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -25,7 +25,6 @@ import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.epoxy.LayoutManagerStateRestorer @@ -45,6 +44,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA import im.vector.app.features.home.room.list.widget.NotifsFabMenuView import im.vector.app.features.matrixto.OriginOfMatrixTo import im.vector.app.features.notifications.NotificationDrawerManager +import im.vector.app.features.room.LeaveRoomPrompt import im.vector.lib.strings.CommonStrings import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn @@ -422,7 +422,7 @@ class RoomListFragment : } } - private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) { + private suspend fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) { when (quickAction) { is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { roomListViewModel.handle(RoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY)) @@ -451,26 +451,11 @@ class RoomListFragment : } } - private fun promptLeaveRoom(roomId: String) { - val isPublicRoom = roomListViewModel.isPublicRoom(roomId) - val message = buildString { - append(getString(CommonStrings.room_participants_leave_prompt_msg)) - if (!isPublicRoom) { - append("\n\n") - append(getString(CommonStrings.room_participants_leave_private_warning)) - } + private suspend fun promptLeaveRoom(roomId: String) { + val warning = roomListViewModel.getLeaveRoomWarning(roomId) + LeaveRoomPrompt.show(requireContext(), warning) { + roomListViewModel.handle(RoomListAction.LeaveRoom(roomId)) } - MaterialAlertDialogBuilder( - requireContext(), - if (isPublicRoom) 0 else im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive - ) - .setTitle(CommonStrings.room_participants_leave_prompt_title) - .setMessage(message) - .setPositiveButton(CommonStrings.action_leave) { _, _ -> - roomListViewModel.handle(RoomListAction.LeaveRoom(roomId)) - } - .setNegativeButton(CommonStrings.action_cancel, null) - .show() } override fun invalidate() = withState(roomListViewModel) { state -> diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 907c4cca72..d5821fab81 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -26,6 +26,8 @@ import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom import im.vector.app.features.analytics.plan.JoinedRoom import im.vector.app.features.displayname.getBestName import im.vector.app.features.invite.AutoAcceptInvites +import im.vector.app.features.room.LeaveRoomPrompt +import im.vector.app.features.room.getLeaveRoomWarning import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.distinctUntilChanged @@ -41,7 +43,6 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.api.session.room.state.isPublic import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.flow.flow import timber.log.Timber @@ -150,10 +151,11 @@ class RoomListViewModel @AssistedInject constructor( } } - fun isPublicRoom(roomId: String): Boolean { - return session.getRoom(roomId)?.stateService()?.isPublic().orFalse() + suspend fun getLeaveRoomWarning(roomId: String): LeaveRoomPrompt.Warning { + return session.getLeaveRoomWarning(roomId) } + // PRIVATE METHODS ***************************************************************************** private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt index 02d702685a..a6421f19a0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListFragment.kt @@ -18,7 +18,6 @@ import androidx.recyclerview.widget.ConcatAdapter.Config.StableIdMode import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.mvrx.fragmentViewModel -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.epoxy.LayoutManagerStateRestorer import im.vector.app.core.extensions.cleanup @@ -36,7 +35,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA import im.vector.app.features.home.room.list.home.header.HomeRoomFilter import im.vector.app.features.home.room.list.home.header.HomeRoomsHeadersController import im.vector.app.features.home.room.list.home.invites.InvitesActivity -import im.vector.lib.strings.CommonStrings +import im.vector.app.features.room.LeaveRoomPrompt import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -103,7 +102,7 @@ class HomeRoomListFragment : } } - private fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) { + private suspend fun handleQuickActions(quickAction: RoomListQuickActionsSharedAction) { when (quickAction) { is RoomListQuickActionsSharedAction.NotificationsAllNoisy -> { roomListViewModel.handle(HomeRoomListAction.ChangeRoomNotificationState(quickAction.roomId, RoomNotificationState.ALL_MESSAGES_NOISY)) @@ -185,26 +184,11 @@ class HomeRoomListFragment : concatAdapter.addAdapter(roomsAdapter) } - private fun promptLeaveRoom(roomId: String) { - val isPublicRoom = roomListViewModel.isPublicRoom(roomId) - val message = buildString { - append(getString(CommonStrings.room_participants_leave_prompt_msg)) - if (!isPublicRoom) { - append("\n\n") - append(getString(CommonStrings.room_participants_leave_private_warning)) - } + private suspend fun promptLeaveRoom(roomId: String) { + val warning = roomListViewModel.getLeaveRoomWarning(roomId) + LeaveRoomPrompt.show(requireContext(), warning) { + roomListViewModel.handle(HomeRoomListAction.LeaveRoom(roomId)) } - MaterialAlertDialogBuilder( - requireContext(), - if (isPublicRoom) 0 else im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive - ) - .setTitle(CommonStrings.room_participants_leave_prompt_title) - .setMessage(message) - .setPositiveButton(CommonStrings.action_leave) { _, _ -> - roomListViewModel.handle(HomeRoomListAction.LeaveRoom(roomId)) - } - .setNegativeButton(CommonStrings.action_cancel, null) - .show() } private fun onInvitesCounterClicked() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt index d232dab3fb..2b367d75e9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt @@ -26,6 +26,8 @@ import im.vector.app.features.analytics.extensions.toTrackingValue import im.vector.app.features.analytics.plan.UserProperties import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.room.list.home.header.HomeRoomFilter +import im.vector.app.features.room.LeaveRoomPrompt +import im.vector.app.features.room.getLeaveRoomWarning import im.vector.lib.strings.CommonStrings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -331,8 +333,8 @@ class HomeRoomListViewModel @AssistedInject constructor( filteredPagedRoomSummariesLive.queryParams = getFilteredQueryParams(newFilter, filteredPagedRoomSummariesLive.queryParams) } - fun isPublicRoom(roomId: String): Boolean { - return session.getRoom(roomId)?.stateService()?.isPublic().orFalse() + suspend fun getLeaveRoomWarning(roomId: String): LeaveRoomPrompt.Warning { + return session.getLeaveRoomWarning(roomId) } private fun handleSelectRoom(action: HomeRoomListAction.SelectRoom) = withState { diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt b/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt index d61bbef768..6ac6449820 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt @@ -7,6 +7,37 @@ package im.vector.app.features.powerlevel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.flow.flow fun Role.isOwner() = this == Role.Creator || this == Role.SuperAdmin + +fun Room.membersByRoleFlow(): Flow>> { + val roomMembersFlow = flow().liveRoomMembers(roomMemberQueryParams()) + val roomPowerLevelsFlow = PowerLevelsFlowFactory(this).createFlow() + return combine(roomMembersFlow, roomPowerLevelsFlow) { roomMembers, roomPowerLevels -> + roomMembers.groupBy { roomPowerLevels.getSuggestedRole(it.userId) } + }.distinctUntilChanged() +} + +fun Room.isLastAdminFlow(userId: String): Flow { + return membersByRoleFlow().map { membersByRole -> + val creatorMembers = membersByRole[Role.Creator].orEmpty() + val superAdminMembers = membersByRole[Role.SuperAdmin].orEmpty() + val adminMembers = membersByRole[Role.Admin].orEmpty() + val joinedAdmins = (adminMembers + creatorMembers + superAdminMembers).filter { it.membership == Membership.JOIN } + if (joinedAdmins.size == 1) { + joinedAdmins.first().userId == userId + } else { + false + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt b/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt new file mode 100644 index 0000000000..a43e7e35ed --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package im.vector.app.features.room + +import android.content.Context +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import im.vector.app.features.powerlevel.isLastAdminFlow +import im.vector.app.features.room.LeaveRoomPrompt.Warning +import im.vector.lib.strings.CommonStrings +import im.vector.lib.ui.styles.R +import kotlinx.coroutines.flow.first +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.state.isPublic + +object LeaveRoomPrompt { + + enum class Warning { + LAST_ADMIN, + PRIVATE_ROOM, + NONE + }; + + fun show( + context: Context, + warning: Warning, + onLeaveClick: () -> Unit + ) { + val hasWarning = warning != Warning.NONE + val message = buildString { + append(context.getString(CommonStrings.room_participants_leave_prompt_msg)) + if (hasWarning) append("\n\n") + when (warning) { + Warning.LAST_ADMIN -> append(context.getString(CommonStrings.room_participants_leave_last_admin)) + Warning.PRIVATE_ROOM -> append(context.getString(CommonStrings.room_participants_leave_private_warning)) + Warning.NONE -> Unit + } + } + MaterialAlertDialogBuilder( + context, + if (hasWarning) R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive else 0 + ) + .setTitle(CommonStrings.room_participants_leave_prompt_title) + .setMessage(message) + .setPositiveButton(CommonStrings.action_leave) { _, _ -> + onLeaveClick() + } + .setNegativeButton(CommonStrings.action_cancel, null) + .show() + } +} + +suspend fun Session.getLeaveRoomWarning(roomId: String): Warning { + val room = getRoom(roomId) ?: return Warning.NONE + val isLastAdmin = room.isLastAdminFlow(myUserId).first() + return when { + isLastAdmin -> Warning.LAST_ADMIN + !room.stateService().isPublic() -> Warning.PRIVATE_ROOM + else -> Warning.NONE + } +} + diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 1a56de9fab..54dc119696 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -45,6 +45,7 @@ import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.app.features.navigation.SettingsActivityPayload +import im.vector.app.features.room.LeaveRoomPrompt import im.vector.lib.strings.CommonStrings import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -320,25 +321,16 @@ class RoomProfileFragment : } override fun onLeaveRoomClicked() { - val isPublicRoom = roomProfileViewModel.isPublicRoom() - val message = buildString { - append(getString(CommonStrings.room_participants_leave_prompt_msg)) - if (!isPublicRoom) { - append("\n\n") - append(getString(CommonStrings.room_participants_leave_private_warning)) + withState(roomProfileViewModel){ state -> + val warning = when { + state.isLastAdmin -> LeaveRoomPrompt.Warning.LAST_ADMIN + state.roomSummary()?.isPublic == false -> LeaveRoomPrompt.Warning.PRIVATE_ROOM + else -> LeaveRoomPrompt.Warning.NONE + } + LeaveRoomPrompt.show(requireContext(), warning){ + roomProfileViewModel.handle(RoomProfileAction.LeaveRoom) } } - MaterialAlertDialogBuilder( - requireContext(), - if (isPublicRoom) 0 else im.vector.lib.ui.styles.R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive - ) - .setTitle(CommonStrings.room_participants_leave_prompt_title) - .setMessage(message) - .setPositiveButton(CommonStrings.action_leave) { _, _ -> - roomProfileViewModel.handle(RoomProfileAction.LeaveRoom) - } - .setNegativeButton(CommonStrings.action_cancel, null) - .show() } override fun onRoomAliasesClicked() { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index deb609509a..87c760ce45 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -20,6 +20,7 @@ import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.powerlevel.PowerLevelsFlowFactory +import im.vector.app.features.powerlevel.isLastAdminFlow import im.vector.app.features.session.coroutineScope import im.vector.lib.strings.CommonStrings import kotlinx.coroutines.Dispatchers @@ -72,6 +73,14 @@ class RoomProfileViewModel @AssistedInject constructor( observePermissions() observePowerLevels() observeCryptoSettings(flowRoom) + observeIsLastAdmin() + } + + private fun observeIsLastAdmin() { + room.isLastAdminFlow(session.myUserId) + .onEach { isLastAdmin -> + setState { copy(isLastAdmin = isLastAdmin) } + }.launchIn(viewModelScope) } private fun observeCryptoSettings(flowRoom: FlowRoom) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt index d6784dc8a2..580b5a1283 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt @@ -30,6 +30,7 @@ data class RoomProfileViewState( val encryptToVerifiedDeviceOnly: Async = Uninitialized, val globalCryptoConfig: Async = Uninitialized, val unverifiedDevicesInTheRoom: Async = Uninitialized, + val isLastAdmin: Boolean = false ) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt index 0b1968b31e..48199cd2a2 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt @@ -21,7 +21,7 @@ data class SpaceLeaveAdvanceViewState( val currentFilter: String = "", val leaveState: Async = Uninitialized, val isFilteringEnabled: Boolean = false, - val isLastOwner: Boolean = false + val isLastAdmin: Boolean = false ) : MavericksState { constructor(args: SpaceBottomSheetSettingsArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt index 1c1f33faa8..3712c5c0ad 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt @@ -50,27 +50,12 @@ class SpaceLeaveAdvancedFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - controller.listener = this withState(viewModel) { state -> setupToolbar(views.toolbar) .setSubtitle(state.spaceSummary?.name) .allowBack() - - state.spaceSummary?.let { summary -> - val warningMessage: CharSequence? = when { - summary.otherMemberIds.isEmpty() -> getString(CommonStrings.space_leave_prompt_msg_only_you) - state.isLastOwner -> getString(CommonStrings.space_leave_prompt_msg_as_admin) - !summary.isPublic -> getString(CommonStrings.space_leave_prompt_msg_private) - else -> null - } - - views.spaceLeavePromptDescription.isVisible = warningMessage != null - views.spaceLeavePromptDescription.text = warningMessage - } - - views.spaceLeavePromptTitle.text = getString(CommonStrings.space_leave_prompt_msg_with_name, state.spaceSummary?.name ?: "") } views.roomList.configureWith(controller) @@ -107,6 +92,19 @@ class SpaceLeaveAdvancedFragment : override fun invalidate() = withState(viewModel) { state -> super.invalidate() + state.spaceSummary?.let { summary -> + val warningMessage: CharSequence? = when { + summary.otherMemberIds.isEmpty() -> getString(CommonStrings.space_leave_prompt_msg_only_you) + state.isLastAdmin -> getString(CommonStrings.space_leave_prompt_msg_as_admin) + !summary.isPublic -> getString(CommonStrings.space_leave_prompt_msg_private) + else -> null + } + views.spaceLeavePromptDescription.isVisible = warningMessage != null + views.spaceLeavePromptDescription.text = warningMessage + } + + views.spaceLeavePromptTitle.text = getString(CommonStrings.space_leave_prompt_msg_with_name, state.spaceSummary?.name ?: "") + if (state.isFilteringEnabled) { views.appBarLayout.setExpanded(false) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index 3c06244888..7f0a2d4008 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -20,7 +20,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.isOwner +import im.vector.app.features.powerlevel.isLastAdminFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.SpaceFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom -import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.flow.flow @@ -44,20 +43,13 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( init { val space = session.getRoom(initialState.spaceId) - val spaceSummary = space?.roomSummary() - val roomPowerLevels = space?.getRoomPowerLevels() - roomPowerLevels?.let { - val isOwner = roomPowerLevels.getSuggestedRole(session.myUserId).isOwner() - val otherOwnersCount = spaceSummary?.otherMemberIds - ?.map { roomPowerLevels.getSuggestedRole(it) } - ?.count { it.isOwner() } - ?: 0 - val isLastOwner = isOwner && otherOwnersCount == 0 - setState { - copy(isLastOwner = isLastOwner) - } - } + space?.isLastAdminFlow(session.myUserId) + ?.onEach { isLastAdmin -> + setState { copy(isLastAdmin = isLastAdmin) } + }?.launchIn(viewModelScope) + + val spaceSummary = space?.roomSummary() setState { copy(spaceSummary = spaceSummary) } session.getRoom(initialState.spaceId) ?.flow() From 15cf848cf18247ace81bb8577ce488d03ad3f501 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 30 Jul 2025 12:12:51 +0200 Subject: [PATCH 5/8] test : try to fix some tests --- .../session/room/model/PowerLevelsContent.kt | 2 +- ...faultCreateLocalRoomStateEventsTaskTest.kt | 15 ++++++------- .../app/core/utils/PermissionChecker.kt | 21 ++++++++++++++++--- .../features/location/LocationTrackerTest.kt | 4 +++- .../live/map/LiveLocationMapViewModelTest.kt | 3 +++ .../app/test/fakes/FakePermissionChecker.kt | 16 ++++++++++++++ 6 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakePermissionChecker.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index 894640a488..cc9a5b5459 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -35,7 +35,7 @@ data class PowerLevelsContent( */ @Json(name = "kick") val kick: Int? = null, /** - * The level required to invite a user. Defaults to 50 if unspecified. + * The level required to invite a user. Defaults to 0 if unspecified. */ @Json(name = "invite") val invite: Int? = null, /** diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt index 0669776394..664e7d4b64 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt @@ -52,6 +52,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.internal.session.profile.ThirdPartyIdentifier.Companion.MEDIUM_EMAIL @@ -372,13 +373,13 @@ internal class DefaultCreateLocalRoomStateEventsTaskTest { // Power levels val powerLevelsContent = result.find { it.type == EventType.STATE_ROOM_POWER_LEVELS }?.content.toModel() powerLevelsContent.shouldNotBeNull() - powerLevelsContent.ban shouldBeEqualTo Role.Moderator.value - powerLevelsContent.kick shouldBeEqualTo Role.Moderator.value - powerLevelsContent.invite shouldBeEqualTo Role.Moderator.value - powerLevelsContent.redact shouldBeEqualTo Role.Moderator.value - powerLevelsContent.eventsDefault shouldBeEqualTo Role.User.value - powerLevelsContent.usersDefault shouldBeEqualTo Role.User.value - powerLevelsContent.stateDefault shouldBeEqualTo Role.Moderator.value + powerLevelsContent.ban shouldBeEqualTo UserPowerLevel.Moderator.value + powerLevelsContent.kick shouldBeEqualTo UserPowerLevel.Moderator.value + powerLevelsContent.invite shouldBeEqualTo UserPowerLevel.User.value + powerLevelsContent.redact shouldBeEqualTo UserPowerLevel.Moderator.value + powerLevelsContent.eventsDefault shouldBeEqualTo UserPowerLevel.User.value + powerLevelsContent.usersDefault shouldBeEqualTo UserPowerLevel.User.value + powerLevelsContent.stateDefault shouldBeEqualTo UserPowerLevel.Moderator.value // Guest access result.find { it.type == EventType.STATE_ROOM_GUEST_ACCESS } ?.content.toModel()?.guestAccess shouldBeEqualTo GuestAccess.Forbidden diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt index 3840e028c0..1323edf188 100644 --- a/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt @@ -10,12 +10,27 @@ package im.vector.app.core.utils import android.content.Context import android.content.pm.PackageManager import androidx.core.app.ActivityCompat +import dagger.Binds +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import javax.inject.Inject -class PermissionChecker @Inject constructor( +interface PermissionChecker { + + @InstallIn(SingletonComponent::class) + @dagger.Module + interface Module { + @Binds + fun bindPermissionChecker(permissionChecker: AndroidPermissionChecker): PermissionChecker + } + + fun checkPermission(vararg permissions: String): Boolean +} + +class AndroidPermissionChecker @Inject constructor( private val applicationContext: Context, -) { - fun checkPermission(vararg permissions: String): Boolean { +) : PermissionChecker { + override fun checkPermission(vararg permissions: String): Boolean { return permissions.any { permission -> ActivityCompat.checkSelfPermission(applicationContext, permission) != PackageManager.PERMISSION_GRANTED } diff --git a/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt b/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt index 75660bb16f..40afb31b8f 100644 --- a/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt +++ b/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt @@ -14,6 +14,7 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeContext import im.vector.app.test.fakes.FakeLocationManager +import im.vector.app.test.fakes.FakePermissionChecker import im.vector.app.test.fixtures.aBuildMeta import im.vector.app.test.test import io.mockk.every @@ -29,6 +30,7 @@ import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test private const val A_LATITUDE = 1.2 @@ -48,7 +50,7 @@ class LocationTrackerTest { @Before fun setUp() { mockkStatic("im.vector.app.features.session.SessionCoroutineScopesKt") - locationTracker = LocationTracker(fakeContext.instance, fakeActiveSessionHolder.instance, aBuildMeta()) + locationTracker = LocationTracker(fakeContext.instance, fakeActiveSessionHolder.instance, aBuildMeta(), FakePermissionChecker()) fakeLocationManager.givenRemoveUpdates(locationTracker) } diff --git a/vector/src/test/java/im/vector/app/features/location/live/map/LiveLocationMapViewModelTest.kt b/vector/src/test/java/im/vector/app/features/location/live/map/LiveLocationMapViewModelTest.kt index 108bdbed34..cc76a806b9 100644 --- a/vector/src/test/java/im/vector/app/features/location/live/map/LiveLocationMapViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/location/live/map/LiveLocationMapViewModelTest.kt @@ -12,6 +12,7 @@ import im.vector.app.features.location.LocationData import im.vector.app.features.location.live.StopLiveLocationShareUseCase import im.vector.app.test.fakes.FakeLocationSharingServiceConnection import im.vector.app.test.fakes.FakeLocationTracker +import im.vector.app.test.fakes.FakePermissionChecker import im.vector.app.test.fakes.FakeSession import im.vector.app.test.test import io.mockk.every @@ -38,6 +39,7 @@ class LiveLocationMapViewModelTest { private val fakeLocationSharingServiceConnection = FakeLocationSharingServiceConnection() private val fakeStopLiveLocationShareUseCase = mockk() private val fakeLocationTracker = FakeLocationTracker() + private val fakePermissionChecker = FakePermissionChecker() private fun createViewModel(): LiveLocationMapViewModel { return LiveLocationMapViewModel( @@ -47,6 +49,7 @@ class LiveLocationMapViewModelTest { locationSharingServiceConnection = fakeLocationSharingServiceConnection.instance, stopLiveLocationShareUseCase = fakeStopLiveLocationShareUseCase, locationTracker = fakeLocationTracker.instance, + permissionChecker = fakePermissionChecker ) } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakePermissionChecker.kt b/vector/src/test/java/im/vector/app/test/fakes/FakePermissionChecker.kt new file mode 100644 index 0000000000..5665a706a7 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakePermissionChecker.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2022-2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package im.vector.app.test.fakes + +import im.vector.app.core.utils.PermissionChecker + +class FakePermissionChecker(val permissionResult: Boolean = true) : PermissionChecker { + override fun checkPermission(vararg permissions: String): Boolean { + return permissionResult + } +} From cc99104060951ef441955f5c59d704bf7f1d6c3d Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 30 Jul 2025 15:03:04 +0200 Subject: [PATCH 6/8] misc : code clean up --- .../matrix/android/sdk/session/space/SpaceHierarchyTest.kt | 2 +- .../matrix/android/sdk/api/session/room/RoomExtensions.kt | 3 --- .../sdk/api/session/room/model/PowerLevelsContent.kt | 1 - .../internal/session/pushers/DefaultConditionResolver.kt | 7 ------- .../session/room/EventRelationsAggregationProcessor.kt | 1 - .../internal/session/room/state/StateEventDataSource.kt | 1 - .../sdk/internal/session/space/DefaultSpaceService.kt | 2 -- .../create/DefaultCreateLocalRoomStateEventsTaskTest.kt | 1 - .../room/detail/timeline/action/MessageActionsViewModel.kt | 1 - .../detail/timeline/factory/MergedHeaderItemFactory.kt | 4 ---- .../room/detail/timeline/format/NoticeEventFormatter.kt | 2 +- .../app/features/home/room/list/RoomListViewModel.kt | 1 - .../features/home/room/list/home/HomeRoomListViewModel.kt | 1 - .../app/features/location/LocationSharingViewModel.kt | 1 - .../app/features/powerlevel/PowerLevelsFlowFactory.kt | 1 - .../java/im/vector/app/features/room/LeaveRoomPrompt.kt | 3 +-- .../roommemberprofile/RoomMemberProfileController.kt | 2 +- .../vector/app/features/roomprofile/RoomProfileFragment.kt | 4 ++-- .../app/features/roomprofile/RoomProfileViewModel.kt | 1 - .../app/features/roomprofile/alias/RoomAliasViewModel.kt | 1 - .../roomprofile/banned/RoomBannedMemberListViewModel.kt | 1 - .../app/features/spaces/create/CreateSpaceViewModelTask.kt | 1 - .../app/features/spaces/share/ShareSpaceViewModel.kt | 1 - .../recording/usecase/StartVoiceBroadcastUseCase.kt | 6 ------ .../im/vector/app/features/widgets/WidgetPostAPIHandler.kt | 3 --- .../java/im/vector/app/features/widgets/WidgetViewModel.kt | 7 ------- .../java/im/vector/app/features/MemberListViewModelTest.kt | 2 +- .../im/vector/app/features/location/LocationTrackerTest.kt | 1 - 28 files changed, 7 insertions(+), 55 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 6c9d0ad983..df17fbf4a8 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -40,8 +40,8 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest import org.matrix.android.sdk.common.SessionTestParams diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt index f9c04ac34e..29638857bc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.api.session.room -import kotlinx.coroutines.flow.Flow import org.matrix.android.sdk.api.query.QueryStateEventValue import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event @@ -26,8 +25,6 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.internal.di.UserId /** * Get a TimelineEvent using the TimelineService of a Room. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index cc9a5b5459..890b7d64b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -115,7 +115,6 @@ private fun defaultNotificationLevel(key: String): Int { } } - // Fallback to default value, defined in the Matrix specification fun PowerLevelsContent?.banOrDefault() = this?.ban ?: UserPowerLevel.Moderator.value fun PowerLevelsContent?.kickOrDefault() = this?.kick ?: UserPowerLevel.Moderator.value diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt index c1fb93b3ff..3bc12eecb1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt @@ -15,20 +15,13 @@ */ package org.matrix.android.sdk.internal.session.pushers -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.pushrules.ConditionResolver import org.matrix.android.sdk.api.session.pushrules.ContainsDisplayNameCondition import org.matrix.android.sdk.api.session.pushrules.EventMatchCondition import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermissionCondition import org.matrix.android.sdk.api.session.room.getRoomPowerLevels -import org.matrix.android.sdk.api.session.room.getStateEvent -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.RoomGetter import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index 3b963c13cb..821aeaa494 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -34,7 +34,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.verification.toState import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt index 433a4399a3..723d604ad4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt @@ -96,4 +96,3 @@ internal class StateEventDataSource @Inject constructor( } } } - diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index 80ecc4fa81..9b6e87a736 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -35,8 +35,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels -import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.space.JoinSpaceResult diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt index 664e7d4b64..b05a890c1b 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/create/DefaultCreateLocalRoomStateEventsTaskTest.kt @@ -51,7 +51,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.user.model.User diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 8a4e3bb797..36f63f0b20 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -49,7 +49,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index c36cd14641..012421bf8d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -24,17 +24,13 @@ import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovement import im.vector.lib.strings.CommonPlurals import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.getRoomPowerLevels -import org.matrix.android.sdk.api.session.room.getStateEvent -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 0e328bdbba..aea064736a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -122,7 +122,7 @@ class NoticeEventFormatter @Inject constructor( userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys) val diffs = ArrayList() userIds.forEach { userId -> - val from = RoomPowerLevels(previousPowerLevelsContent,null).getSuggestedRole(userId) + val from = RoomPowerLevels(previousPowerLevelsContent, null).getSuggestedRole(userId) val to = RoomPowerLevels(powerLevelsContent, null).getSuggestedRole(userId) if (from != to) { val fromStr = roleFormatter.format(from) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index d5821fab81..a4dae15519 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -155,7 +155,6 @@ class RoomListViewModel @AssistedInject constructor( return session.getLeaveRoomWarning(roomId) } - // PRIVATE METHODS ***************************************************************************** private fun handleSelectRoom(action: RoomListAction.SelectRoom) = withState { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt index 2b367d75e9..b84de29630 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/HomeRoomListViewModel.kt @@ -55,7 +55,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import org.matrix.android.sdk.api.session.room.state.isPublic import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toOption diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index dfa1ec4549..c000c2423a 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -34,7 +34,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getUserOrDefault -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.util.toMatrixItem import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt index 689add249c..fbbfac98dc 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt @@ -16,7 +16,6 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow diff --git a/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt b/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt index a43e7e35ed..ac06dd471c 100644 --- a/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt +++ b/vector/src/main/java/im/vector/app/features/room/LeaveRoomPrompt.kt @@ -24,7 +24,7 @@ object LeaveRoomPrompt { LAST_ADMIN, PRIVATE_ROOM, NONE - }; + } fun show( context: Context, @@ -64,4 +64,3 @@ suspend fun Session.getLeaveRoomWarning(roomId: String): Warning { else -> Warning.NONE } } - diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 308fdc5bce..bef3c5bda5 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -249,7 +249,7 @@ class RoomMemberProfileController @Inject constructor( if ((!state.isMine && myPowerLevel <= userPowerLevel)) { return } - if(userPowerLevel !is UserPowerLevel.Value) return + if (userPowerLevel !is UserPowerLevel.Value) return val membership = state.asyncMembership() ?: return val canKick = !state.isMine && state.actionPermissions.canKick val canBan = !state.isMine && state.actionPermissions.canBan diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 54dc119696..6c1f3b14fd 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -321,13 +321,13 @@ class RoomProfileFragment : } override fun onLeaveRoomClicked() { - withState(roomProfileViewModel){ state -> + withState(roomProfileViewModel) { state -> val warning = when { state.isLastAdmin -> LeaveRoomPrompt.Warning.LAST_ADMIN state.roomSummary()?.isPublic == false -> LeaveRoomPrompt.Warning.PRIVATE_ROOM else -> LeaveRoomPrompt.Warning.NONE } - LeaveRoomPrompt.show(requireContext(), warning){ + LeaveRoomPrompt.show(requireContext(), warning) { roomProfileViewModel.handle(RoomProfileAction.LeaveRoom) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index 87c760ce45..e2e70ee8b4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -40,7 +40,6 @@ import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.state.isPublic import org.matrix.android.sdk.flow.FlowRoom import org.matrix.android.sdk.flow.flow diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 73f8e92434..a2bd121cf9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index acfdbb8b1c..6c0bbcb0e5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt index ca65b99fd4..f12ab7b938 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -26,7 +26,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset -import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel import org.matrix.android.sdk.api.session.space.CreateSpaceParams import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt index f5b6a0a06c..b4d58f61a0 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt @@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoomSummary -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels class ShareSpaceViewModel @AssistedInject constructor( @Assisted private val initialState: ShareSpaceViewState, diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt index 973ea11f47..3add1751b1 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/recording/usecase/StartVoiceBroadcastUseCase.kt @@ -25,19 +25,13 @@ import im.vector.lib.multipicker.utils.toMultiPickerAudioType import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.jetbrains.annotations.VisibleForTesting -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.toContent -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.getRoomPowerLevels -import org.matrix.android.sdk.api.session.room.getStateEvent -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt index 326567fb07..40c64c7f19 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt @@ -23,13 +23,10 @@ import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.api.session.room.getStateEvent import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.widgets.WidgetPostAPIMediator import org.matrix.android.sdk.api.util.JsonDict import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index 1778423db7..f1df2cff2a 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -23,21 +23,14 @@ import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Content -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional -import org.matrix.android.sdk.flow.unwrap import timber.log.Timber import javax.net.ssl.HttpsURLConnection diff --git a/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt b/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt index 0f540bd2cd..c833c46366 100644 --- a/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/MemberListViewModelTest.kt @@ -11,9 +11,9 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.test.MavericksTestRule import im.vector.app.features.roomprofile.RoomProfileArgs +import im.vector.app.features.roomprofile.members.RoomMemberListComparator import im.vector.app.features.roomprofile.members.RoomMemberListViewModel import im.vector.app.features.roomprofile.members.RoomMemberListViewState -import im.vector.app.features.roomprofile.members.RoomMemberListComparator import im.vector.app.test.test import im.vector.app.test.testCoroutineDispatchers import io.mockk.coEvery diff --git a/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt b/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt index 40afb31b8f..f2d7f8a1d4 100644 --- a/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt +++ b/vector/src/test/java/im/vector/app/features/location/LocationTrackerTest.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Test private const val A_LATITUDE = 1.2 From fe0b54e8989201c8336ad6318a2368523c7f4ca7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 30 Jul 2025 15:34:49 +0200 Subject: [PATCH 7/8] misc : fix copyright --- .../session/room/powerlevels/UserPowerLevel.kt | 16 +++++++++++++--- .../session/room/powerlevels/RoomPowerLevels.kt | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt index 8932f16685..7d213cb269 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/UserPowerLevel.kt @@ -1,8 +1,18 @@ /* - * Copyright 2025 New Vector Ltd. + * Copyright 2025 The Matrix.org Foundation C.I.C. + * + * 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. * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. */ package org.matrix.android.sdk.api.session.room.powerlevels diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt index d3556324e7..bbdbc4c123 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt @@ -1,8 +1,18 @@ /* - * Copyright 2025 New Vector Ltd. + * Copyright 2025 The Matrix.org Foundation C.I.C. + * + * 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. * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. */ package org.matrix.android.sdk.internal.session.room.powerlevels From a239f5fb08ef9fb7c9619e39a7298379e5531513 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Aug 2025 18:19:25 +0200 Subject: [PATCH 8/8] misc : clean up after PR review --- .../org/matrix/android/sdk/flow/FlowRoom.kt | 5 ++ .../sdk/api/session/room/RoomExtensions.kt | 12 +---- .../api/session/room/state/StateService.kt | 3 ++ .../pushers/DefaultConditionResolver.kt | 2 - .../room/powerlevels/RoomPowerLevels.kt | 42 +++++++++++++++-- .../session/room/state/DefaultStateService.kt | 11 +++++ .../room/version/DefaultRoomVersionService.kt | 15 +----- .../home/room/detail/TimelineViewModel.kt | 3 +- .../composer/MessageComposerViewModel.kt | 3 +- .../action/MessageActionsViewModel.kt | 3 +- .../location/LocationSharingViewModel.kt | 4 +- .../powerlevel/PowerLevelsFlowFactory.kt | 47 ------------------- .../im/vector/app/features/powerlevel/Role.kt | 2 +- .../RoomMemberProfileViewModel.kt | 3 +- .../roomprofile/RoomProfileViewModel.kt | 6 +-- .../roomprofile/alias/RoomAliasViewModel.kt | 4 +- .../banned/RoomBannedMemberListViewModel.kt | 3 +- .../members/RoomMemberListViewModel.kt | 5 +- .../permissions/RoomPermissionsViewModel.kt | 4 +- .../settings/RoomSettingsViewModel.kt | 3 +- .../app/features/spaces/SpaceMenuViewModel.kt | 4 +- .../spaces/explore/SpaceDirectoryViewModel.kt | 3 +- .../spaces/share/ShareSpaceViewModel.kt | 5 +- .../app/features/widgets/WidgetViewModel.kt | 4 +- 24 files changed, 81 insertions(+), 115 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt index 94f09e0bf5..88dd677cb8 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowRoom.kt @@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.util.Optional @@ -95,6 +96,10 @@ class FlowRoom(private val room: Room) { } } + fun liveRoomPowerLevels(): Flow { + return room.stateService().getRoomPowerLevelsLive().asFlow() + } + fun liveReadMarker(): Flow> { return room.readService().getReadMarkerLive().asFlow() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt index 29638857bc..6da0f8df52 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomExtensions.kt @@ -17,12 +17,7 @@ package org.matrix.android.sdk.api.session.room import org.matrix.android.sdk.api.query.QueryStateEventValue -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent @@ -45,10 +40,5 @@ fun Room.getStateEvent(eventType: String, stateKey: QueryStateEventValue): Event * Get the current RoomPowerLevels of the room. */ fun Room.getRoomPowerLevels(): RoomPowerLevels { - val powerLevelsContent = getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)?.content?.toModel() - val roomCreateContent = getStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)?.getRoomCreateContentWithSender() - return RoomPowerLevels( - powerLevelsContent, - roomCreateContent - ) + return stateService().getRoomPowerLevels() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 6ca63c2c49..6e01d7edfa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.Optional @@ -106,4 +107,6 @@ interface StateService { suspend fun setJoinRulePublic() suspend fun setJoinRuleInviteOnly() suspend fun setJoinRuleRestricted(allowList: List) + fun getRoomPowerLevels(): RoomPowerLevels + fun getRoomPowerLevelsLive(): LiveData } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt index 3bc12eecb1..3a7a43c8ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultConditionResolver.kt @@ -24,13 +24,11 @@ import org.matrix.android.sdk.api.session.pushrules.SenderNotificationPermission import org.matrix.android.sdk.api.session.room.getRoomPowerLevels import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.RoomGetter -import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import javax.inject.Inject internal class DefaultConditionResolver @Inject constructor( private val roomGetter: RoomGetter, @UserId private val userId: String, - private val stateEventDataSource: StateEventDataSource, ) : ConditionResolver { override fun resolveEventMatchCondition( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt index bbdbc4c123..9243672f1a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/powerlevels/RoomPowerLevels.kt @@ -17,17 +17,53 @@ package org.matrix.android.sdk.internal.session.room.powerlevels +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels +import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource internal fun StateEventDataSource.getRoomPowerLevels(roomId: String): RoomPowerLevels { - val powerLevelsContent = getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - ?.content?.toModel() - val roomCreateContent = getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)?.getRoomCreateContentWithSender() + val powerLevelsEvent = getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) + val roomCreateEvent = getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty) + return createRoomPowerLevels(powerLevelsEvent = powerLevelsEvent, roomCreateEvent = roomCreateEvent) +} + +internal fun StateEventDataSource.getRoomPowerLevelsLive(roomId: String): LiveData { + val powerLevelsEventLive = getStateEventLive(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) + val roomCreateEventLive = getStateEventLive(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty) + val resultLiveData = MediatorLiveData() + + fun emitIfReady(powerLevelEvent: Optional?, roomCreateEvent: Optional?) { + if (powerLevelEvent != null && roomCreateEvent != null) { + val roomPowerLevels = createRoomPowerLevels(powerLevelEvent.get(), roomCreateEvent.get()) + resultLiveData.postValue(roomPowerLevels) + } + } + resultLiveData.apply { + var powerLevelEvent: Optional? = null + var roomCreateEvent: Optional? = null + + addSource(powerLevelsEventLive) { + powerLevelEvent = it + emitIfReady(powerLevelEvent, roomCreateEvent) + } + addSource(roomCreateEventLive) { + roomCreateEvent = it + emitIfReady(powerLevelEvent, roomCreateEvent) + } + } + return resultLiveData +} + +private fun createRoomPowerLevels(powerLevelsEvent: Event?, roomCreateEvent: Event?): RoomPowerLevels { + val powerLevelsContent = powerLevelsEvent?.content?.toModel() + val roomCreateContent = roomCreateEvent?.getRoomCreateContentWithSender() return RoomPowerLevels(powerLevelsContent, roomCreateContent) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index ad47b82428..b939490ae3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -31,11 +31,14 @@ import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent +import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.state.StateService import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.MimeTypes import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.session.content.FileUploader +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevelsLive internal class DefaultStateService @AssistedInject constructor( @Assisted private val roomId: String, @@ -65,6 +68,14 @@ internal class DefaultStateService @AssistedInject constructor( return stateEventDataSource.getStateEventsLive(roomId, eventTypes, stateKey) } + override fun getRoomPowerLevels(): RoomPowerLevels { + return stateEventDataSource.getRoomPowerLevels(roomId) + } + + override fun getRoomPowerLevelsLive(): LiveData { + return stateEventDataSource.getRoomPowerLevelsLive(roomId) + } + override suspend fun sendStateEvent( eventType: String, stateKey: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt index fd99bb7158..0fec0e4c17 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/version/DefaultRoomVersionService.kt @@ -23,12 +23,10 @@ import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels import org.matrix.android.sdk.api.session.room.version.RoomVersionService import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource +import org.matrix.android.sdk.internal.session.room.powerlevels.getRoomPowerLevels import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource internal class DefaultRoomVersionService @AssistedInject constructor( @@ -72,16 +70,7 @@ internal class DefaultRoomVersionService @AssistedInject constructor( } override fun userMayUpgradeRoom(userId: String): Boolean { - val powerLevelsContent = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - ?.content?.toModel() - - val roomCreateContent = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty) - ?.getRoomCreateContentWithSender() - - val roomPowerLevels = RoomPowerLevels( - powerLevelsContent = powerLevelsContent, - roomCreateContent = roomCreateContent - ) + val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId) return roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_TOMBSTONE) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 72b72990e8..f92a062546 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -54,7 +54,6 @@ import im.vector.app.features.home.room.typing.TypingHelper import im.vector.app.features.location.live.StopLiveLocationShareUseCase import im.vector.app.features.location.live.tracking.LocationSharingServiceConnection import im.vector.app.features.notifications.NotificationDrawerManager -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.raw.wellknown.CryptoConfig import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault import im.vector.app.features.raw.wellknown.withElementWellKnown @@ -302,7 +301,7 @@ class TimelineViewModel @AssistedInject constructor( private fun observePowerLevel() { if (room == null) return - PowerLevelsFlowFactory(room).createFlow() + room.flow().liveRoomPowerLevels() .onEach { powerLevels -> val canInvite = powerLevels.isUserAbleToInvite(session.myUserId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index 00bc1890e7..4e645f93ae 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -30,7 +30,6 @@ import im.vector.app.features.home.room.detail.ChatEffect import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView import im.vector.app.features.home.room.detail.toMessageType -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.voice.VoiceFailure @@ -179,7 +178,7 @@ class MessageComposerViewModel @AssistedInject constructor( private fun observePowerLevelAndEncryption(room: Room) { combine( - PowerLevelsFlowFactory(room).createFlow(), + room.flow().liveRoomPowerLevels(), room.flow().liveRoomSummary().unwrap() ) { pl, sum -> val canSendMessage = pl.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 36f63f0b20..2280a26842 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -23,7 +23,6 @@ import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormat import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.html.VectorHtmlCompressor -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.reactions.data.EmojiDataSource import im.vector.app.features.settings.VectorPreferences import im.vector.lib.strings.CommonStrings @@ -115,7 +114,7 @@ class MessageActionsViewModel @AssistedInject constructor( if (room == null) { return } - PowerLevelsFlowFactory(room).createFlow() + room.flow().liveRoomPowerLevels() .onEach { roomPowerLevels -> val canReact = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.REACTION) val canRedact = roomPowerLevels.isUserAbleToRedact(session.myUserId) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index c000c2423a..9c8dd1dc8e 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -19,7 +19,6 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.utils.PermissionChecker import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.location.domain.usecase.CompareLocationsUseCase -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged @@ -35,6 +34,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getUserOrDefault import org.matrix.android.sdk.api.util.toMatrixItem +import org.matrix.android.sdk.flow.flow import timber.log.Timber /** @@ -72,7 +72,7 @@ class LocationSharingViewModel @AssistedInject constructor( } private fun observePowerLevelsForLiveLocationSharing() { - PowerLevelsFlowFactory(room).createFlow() + room.flow().liveRoomPowerLevels() .distinctUntilChanged() .setOnEach { roomPowerLevels -> val canShareLiveLocation = EventType.STATE_ROOM_BEACON_INFO.values diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt b/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt deleted file mode 100644 index fbbfac98dc..0000000000 --- a/vector/src/main/java/im/vector/app/features/powerlevel/PowerLevelsFlowFactory.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2020-2024 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package im.vector.app.features.powerlevel - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.Room -import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent -import org.matrix.android.sdk.api.session.room.model.create.getRoomCreateContentWithSender -import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.mapOptional - -class PowerLevelsFlowFactory(private val room: Room) { - - fun createFlow(): Flow { - val flowRoom = room.flow() - val powerLevelsFlow = flowRoom - .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) - .mapOptional { it.content.toModel() } - .flowOn(Dispatchers.Default) - - val roomCreateFlow = flowRoom - .liveStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty) - .mapOptional { event -> - event.getRoomCreateContentWithSender() - } - .flowOn(Dispatchers.Default) - - return combine(powerLevelsFlow, roomCreateFlow) { powerLevelsContent, roomCreateContent -> - RoomPowerLevels( - powerLevelsContent = powerLevelsContent.getOrNull(), - roomCreateContent = roomCreateContent.getOrNull() - ) - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt b/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt index 6ac6449820..5330c3c4be 100644 --- a/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt +++ b/vector/src/main/java/im/vector/app/features/powerlevel/Role.kt @@ -22,7 +22,7 @@ fun Role.isOwner() = this == Role.Creator || this == Role.SuperAdmin fun Room.membersByRoleFlow(): Flow>> { val roomMembersFlow = flow().liveRoomMembers(roomMemberQueryParams()) - val roomPowerLevelsFlow = PowerLevelsFlowFactory(this).createFlow() + val roomPowerLevelsFlow = flow().liveRoomPowerLevels() return combine(roomMembersFlow, roomPowerLevelsFlow) { roomMembers, roomPowerLevels -> roomMembers.groupBy { roomPowerLevels.getSuggestedRole(it.userId) } }.distinctUntilChanged() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 14e286fd1d..47d4cf423e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -23,7 +23,6 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.lib.strings.CommonStrings import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine @@ -361,7 +360,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor( private fun observeRoomSummaryAndPowerLevels(room: Room) { val roomSummaryLive = room.flow().liveRoomSummary().unwrap() - val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsFlow = room.flow().liveRoomPowerLevels() powerLevelsFlow .onEach { roomPowerLevels -> val permissions = ActionPermissions( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index e2e70ee8b4..eae4d47de6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -19,7 +19,6 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.home.ShortcutCreator -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.powerlevel.isLastAdminFlow import im.vector.app.features.session.coroutineScope import im.vector.lib.strings.CommonStrings @@ -121,7 +120,7 @@ class RoomProfileViewModel @AssistedInject constructor( } private fun observePowerLevels() { - val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsContentLive = room.flow().liveRoomPowerLevels() powerLevelsContentLive .onEach { roomPowerLevels -> val canUpdateRoomState = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) @@ -163,8 +162,7 @@ class RoomProfileViewModel @AssistedInject constructor( } private fun observePermissions() { - PowerLevelsFlowFactory(room) - .createFlow() + room.flow().liveRoomPowerLevels() .setOnEach { roomPowerLevels -> val permissions = RoomProfileViewState.ActionPermissions( canEnableEncryption = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index a2bd121cf9..2c1a112620 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -18,7 +18,6 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -124,8 +123,7 @@ class RoomAliasViewModel @AssistedInject constructor( } private fun observePowerLevel() { - PowerLevelsFlowFactory(room) - .createFlow() + room.flow().liveRoomPowerLevels() .onEach { roomPowerLevels -> val permissions = RoomAliasViewState.ActionPermissions( canChangeCanonicalAlias = roomPowerLevels.isUserAllowedToSend( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 6c0bbcb0e5..4d373dfaa8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -15,7 +15,6 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.lib.strings.CommonStrings import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -61,7 +60,7 @@ class RoomBannedMemberListViewModel @AssistedInject constructor( ) } - val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsFlow = room.flow().liveRoomPowerLevels() powerLevelsFlow .setOnEach { roomPowerLevels -> copy(canUserBan = roomPowerLevels.isUserAbleToBan(session.myUserId)) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 2974e10649..65e02f63d4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -16,7 +16,6 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -72,7 +71,7 @@ class RoomMemberListViewModel @AssistedInject constructor( memberships = Membership.activeMemberships() } - val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsFlow = room.flow().liveRoomPowerLevels() combine( roomFlow.liveRoomMembers(roomMemberQueryParams), powerLevelsFlow, @@ -137,7 +136,7 @@ class RoomMemberListViewModel @AssistedInject constructor( } private fun observePowerLevel() { - PowerLevelsFlowFactory(room).createFlow() + room.flow().liveRoomPowerLevels() .onEach { roomPowerLevels -> val permissions = ActionPermissions( canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId), diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 3757a1ddd0..7f88ae88c7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -16,7 +16,6 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -59,8 +58,7 @@ class RoomPermissionsViewModel @AssistedInject constructor( } private fun observePowerLevel() { - PowerLevelsFlowFactory(room) - .createFlow() + room.flow().liveRoomPowerLevels() .onEach { roomPowerLevels -> val permissions = RoomPermissionsViewState.ActionPermissions( canChangePowerLevels = roomPowerLevels.isUserAllowedToSend( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 188068afb4..c603927d77 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -15,7 +15,6 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -114,7 +113,7 @@ class RoomSettingsViewModel @AssistedInject constructor( ) } - val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsFlow = room.flow().liveRoomPowerLevels() powerLevelsFlow .onEach { roomPowerLevels -> val permissions = RoomSettingsViewState.ActionPermissions( diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index c8ac7af8b8..6744548872 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -20,7 +20,6 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -71,8 +70,7 @@ class SpaceMenuViewModel @AssistedInject constructor( } }.launchIn(viewModelScope) - PowerLevelsFlowFactory(room) - .createFlow() + room.flow().liveRoomPowerLevels() .onEach { roomPowerLevels -> val canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId) diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 3627dd5469..ed73ace342 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -21,7 +21,6 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom import im.vector.app.features.analytics.plan.JoinedRoom -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -95,7 +94,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor( private fun observePermissions() { val room = session.getRoom(initialState.spaceId) ?: return - val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow() + val powerLevelsFlow = room.flow().liveRoomPowerLevels() powerLevelsFlow .onEach { roomPowerLevels -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt index b4d58f61a0..88c3348ed7 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceViewModel.kt @@ -16,13 +16,13 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoomSummary +import org.matrix.android.sdk.flow.flow class ShareSpaceViewModel @AssistedInject constructor( @Assisted private val initialState: ShareSpaceViewState, @@ -49,8 +49,7 @@ class ShareSpaceViewModel @AssistedInject constructor( private fun observePowerLevel() { val room = session.getRoom(initialState.spaceId) ?: return - PowerLevelsFlowFactory(room) - .createFlow() + room.flow().liveRoomPowerLevels() .onEach { roomPowerLevels -> setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index f1df2cff2a..1c4c19b4f5 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -19,7 +19,6 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -97,8 +96,7 @@ class WidgetViewModel @AssistedInject constructor( if (room == null) { return } - PowerLevelsFlowFactory(room) - .createFlow() + room.flow().liveRoomPowerLevels() .map { roomPowerLevels -> roomPowerLevels.isUserAllowedToSend(session.myUserId, true, null) }