1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-05 15:52:47 +02:00

change (power level) : support new InfinitePowerLevel (first draft)

This commit is contained in:
ganfra
2025-07-24 22:25:10 +02:00
parent 926e64bb6e
commit fd3f7e3a24
60 changed files with 566 additions and 448 deletions

View File

@@ -120,6 +120,7 @@
<string name="notice_widget_modified">%1$s modified %2$s widget</string> <string name="notice_widget_modified">%1$s modified %2$s widget</string>
<string name="notice_widget_modified_by_you">You modified %1$s widget</string> <string name="notice_widget_modified_by_you">You modified %1$s widget</string>
<string name="power_level_owner">Owner</string>
<string name="power_level_admin">Admin</string> <string name="power_level_admin">Admin</string>
<string name="power_level_moderator">Moderator</string> <string name="power_level_moderator">Moderator</string>
<string name="power_level_default">Default</string> <string name="power_level_default">Default</string>
@@ -2383,6 +2384,7 @@
<string name="room_member_power_level_invites">Invites</string> <string name="room_member_power_level_invites">Invites</string>
<string name="room_member_power_level_users">Users</string> <string name="room_member_power_level_users">Users</string>
<string name="room_member_power_level_owner_in">Owner in %1$s</string>
<string name="room_member_power_level_admin_in">Admin in %1$s</string> <string name="room_member_power_level_admin_in">Admin in %1$s</string>
<string name="room_member_power_level_moderator_in">Moderator in %1$s</string> <string name="room_member_power_level_moderator_in">Moderator in %1$s</string>
<string name="room_member_power_level_default_in">Default in %1$s</string> <string name="room_member_power_level_default_in">Default in %1$s</string>

View File

@@ -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.RoomType
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams 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.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.powerlevels.Role
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest 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!!) room.stateService().sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, stateKey = "", newPowerLevelsContent!!)
commonTestHelper.retryPeriodically { commonTestHelper.retryPeriodically {
val powerLevelsHelper = aliceSession.getRoom(bobRoomId)!! val roomPowerLevels = aliceSession.getRoom(bobRoomId)!!
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
?.content ?.content
?.toModel<PowerLevelsContent>() ?.toModel<PowerLevelsContent>()
?.let { PowerLevelsHelper(it) } ?.let { RoomPowerLevels(it) }
powerLevelsHelper!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT) roomPowerLevels!!.isUserAllowedToSend(aliceSession.myUserId, true, EventType.STATE_SPACE_PARENT)
} }
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))

View File

@@ -16,8 +16,7 @@
package org.matrix.android.sdk.api.session.pushrules 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.events.model.Event
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.PowerLevelsHelper
class SenderNotificationPermissionCondition( class SenderNotificationPermissionCondition(
/** /**
@@ -35,8 +34,7 @@ class SenderNotificationPermissionCondition(
override fun technicalDescription() = "User power level <$key>" override fun technicalDescription() = "User power level <$key>"
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean { fun isSatisfied(event: Event, roomPowerLevels: RoomPowerLevels): Boolean {
val powerLevelsHelper = PowerLevelsHelper(powerLevels) return event.senderId != null && roomPowerLevels.isUserAbleToTriggerNotification(event.senderId, key)
return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevels.notificationLevel(key)
} }
} }

View File

@@ -17,7 +17,13 @@
package org.matrix.android.sdk.api.session.room package org.matrix.android.sdk.api.session.room
import org.matrix.android.sdk.api.query.QueryStateEventValue 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.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 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? = fun Room.getStateEvent(eventType: String, stateKey: QueryStateEventValue): Event? =
stateService().getStateEvent(eventType, stateKey) 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<PowerLevelsContent>()
val roomCreateContent = getStateEvent(EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)?.getRoomCreateContentWithSender()
return RoomPowerLevels(
powerLevelsContent,
roomCreateContent
)
}

View File

@@ -18,7 +18,8 @@ package org.matrix.android.sdk.api.session.room.model
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass 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. * 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. * Get the notification level for a dedicated key.
* *
* @param key the notification 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 { fun notificationLevel(key: String): Int {
return when (val value = notifications.orEmpty()[key]) { return when (val value = notifications.orEmpty()[key]) {
@@ -96,10 +97,9 @@ data class PowerLevelsContent(
is String -> value.toInt() is String -> value.toInt()
is Double -> value.toInt() is Double -> value.toInt()
is Int -> value is Int -> value
else -> Role.Moderator.value else -> defaultNotificationLevel(key)
} }
} }
companion object { companion object {
/** /**
* Key to use for content.notifications and get the level required to trigger an @room notification. Defaults to 50 if unspecified. * 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 // Fallback to default value, defined in the Matrix specification
fun PowerLevelsContent.banOrDefault() = ban ?: Role.Moderator.value fun PowerLevelsContent?.banOrDefault() = this?.ban ?: UserPowerLevel.Moderator.value
fun PowerLevelsContent.kickOrDefault() = kick ?: Role.Moderator.value fun PowerLevelsContent?.kickOrDefault() = this?.kick ?: UserPowerLevel.Moderator.value
fun PowerLevelsContent.inviteOrDefault() = invite ?: Role.Moderator.value fun PowerLevelsContent?.inviteOrDefault() = this?.invite ?: UserPowerLevel.User.value
fun PowerLevelsContent.redactOrDefault() = redact ?: Role.Moderator.value fun PowerLevelsContent?.redactOrDefault() = this?.redact ?: UserPowerLevel.Moderator.value
fun PowerLevelsContent.eventsDefaultOrDefault() = eventsDefault ?: Role.Default.value fun PowerLevelsContent?.eventsDefaultOrDefault() = this?.eventsDefault ?: UserPowerLevel.User.value
fun PowerLevelsContent.usersDefaultOrDefault() = usersDefault ?: Role.Default.value fun PowerLevelsContent?.usersDefaultOrDefault() = this?.usersDefault ?: UserPowerLevel.User.value
fun PowerLevelsContent.stateDefaultOrDefault() = stateDefault ?: Role.Moderator.value fun PowerLevelsContent?.stateDefaultOrDefault() = this?.stateDefault ?: UserPowerLevel.Moderator.value
fun PowerLevelsContent?.notificationLevelOrDefault(key: String) = this?.notificationLevel(key) ?: defaultNotificationLevel(key)

View File

@@ -18,15 +18,39 @@ package org.matrix.android.sdk.api.session.room.model.create
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass 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. * Content of a m.room.create type event.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class RoomCreateContent( data class RoomCreateContent(
// Creator should be replaced by the sender of the event
@Json(name = "creator") val creator: String? = null, @Json(name = "creator") val creator: String? = null,
@Json(name = "room_version") val roomVersion: String? = null, @Json(name = "room_version") val roomVersion: String? = null,
@Json(name = "predecessor") val predecessor: Predecessor? = null, @Json(name = "predecessor") val predecessor: Predecessor? = null,
// Defines the room type, see #RoomType (user extensible) // 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<String>? = 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<RoomCreateContent>() ?: 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)
}

View File

@@ -17,26 +17,22 @@
package org.matrix.android.sdk.api.session.room.powerlevels package org.matrix.android.sdk.api.session.room.powerlevels
sealed class Role(open val value: Int) : Comparable<Role> { enum class Role {
object Admin : Role(100) Creator,
object Moderator : Role(50) SuperAdmin,
object Default : Role(0) Admin,
data class Custom(override val value: Int) : Role(value) Moderator,
User;
override fun compareTo(other: Role): Int {
return value.compareTo(other.value)
}
companion object { companion object {
fun getSuggestedRole(userPowerLevel: UserPowerLevel): Role {
// Order matters, default value should be checked after defined roles return when {
fun fromValue(value: Int, default: Int): Role { userPowerLevel == UserPowerLevel.Infinite -> Creator
return when (value) { userPowerLevel >= UserPowerLevel.SuperAdmin -> SuperAdmin
Admin.value -> Admin userPowerLevel >= UserPowerLevel.Admin -> Admin
Moderator.value -> Moderator userPowerLevel >= UserPowerLevel.Moderator -> Moderator
Default.value, else -> User
default -> Default
else -> Custom(value)
} }
} }
} }

View File

@@ -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.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.banOrDefault 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.eventsDefaultOrDefault
import org.matrix.android.sdk.api.session.room.model.inviteOrDefault 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.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.redactOrDefault
import org.matrix.android.sdk.api.session.room.model.stateDefaultOrDefault 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.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. * 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 * @param userId the user id
* @return the power level * @return the power level
*/ */
fun getUserPowerLevelValue(userId: String): Int { fun getUserPowerLevel(userId: String): UserPowerLevel {
return powerLevelsContent.users if (shouldGiveInfinitePowerLevel(userId)) return UserPowerLevel.Infinite
if (powerLevelsContent == null) return UserPowerLevel.User
val value = powerLevelsContent.users
?.get(userId) ?.get(userId)
?: powerLevelsContent.usersDefaultOrDefault() ?: powerLevelsContent.usersDefaultOrDefault()
return UserPowerLevel.Value(value)
} }
/** /**
@@ -50,9 +60,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @return the power level * @return the power level
*/ */
fun getUserRole(userId: String): Role { fun getUserRole(userId: String): Role {
val value = getUserPowerLevelValue(userId) val value = getUserPowerLevel(userId)
// I think we should use powerLevelsContent.usersDefault, but Ganfra told me that it was like that on riot-Web return Role.getSuggestedRole(value)
return Role.fromValue(value, powerLevelsContent.eventsDefaultOrDefault())
} }
/** /**
@@ -65,14 +74,14 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
*/ */
fun isUserAllowedToSend(userId: String, isState: Boolean, eventType: String?): Boolean { fun isUserAllowedToSend(userId: String, isState: Boolean, eventType: String?): Boolean {
return if (userId.isNotEmpty()) { return if (userId.isNotEmpty()) {
val powerLevel = getUserPowerLevelValue(userId) val powerLevel = getUserPowerLevel(userId)
val minimumPowerLevel = powerLevelsContent.events?.get(eventType) val minimumPowerLevel = powerLevelsContent?.events?.get(eventType)
?: if (isState) { ?: if (isState) {
powerLevelsContent.stateDefaultOrDefault() powerLevelsContent.stateDefaultOrDefault()
} else { } else {
powerLevelsContent.eventsDefaultOrDefault() powerLevelsContent.eventsDefaultOrDefault()
} }
powerLevel >= minimumPowerLevel powerLevel >= UserPowerLevel.Value(minimumPowerLevel)
} else false } else false
} }
@@ -82,8 +91,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @return true if able to invite * @return true if able to invite
*/ */
fun isUserAbleToInvite(userId: String): Boolean { fun isUserAbleToInvite(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId) val powerLevel = getUserPowerLevel(userId)
return powerLevel >= powerLevelsContent.inviteOrDefault() return powerLevel >= UserPowerLevel.Value(powerLevelsContent.inviteOrDefault())
} }
/** /**
@@ -92,8 +101,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @return true if able to ban * @return true if able to ban
*/ */
fun isUserAbleToBan(userId: String): Boolean { fun isUserAbleToBan(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId) val powerLevel = getUserPowerLevel(userId)
return powerLevel >= powerLevelsContent.banOrDefault() return powerLevel >= UserPowerLevel.Value(powerLevelsContent.banOrDefault())
} }
/** /**
@@ -102,8 +111,8 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @return true if able to kick * @return true if able to kick
*/ */
fun isUserAbleToKick(userId: String): Boolean { fun isUserAbleToKick(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId) val powerLevel = getUserPowerLevel(userId)
return powerLevel >= powerLevelsContent.kickOrDefault() return powerLevel >= UserPowerLevel.Value(powerLevelsContent.kickOrDefault())
} }
/** /**
@@ -112,7 +121,22 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @return true if able to redact * @return true if able to redact
*/ */
fun isUserAbleToRedact(userId: String): Boolean { fun isUserAbleToRedact(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId) val powerLevel = getUserPowerLevel(userId)
return powerLevel >= powerLevelsContent.redactOrDefault() 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
}
} }
} }

View File

@@ -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<UserPowerLevel> {
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)
}
}

View File

@@ -17,15 +17,11 @@
package org.matrix.android.sdk.internal.session.permalinks package org.matrix.android.sdk.internal.session.permalinks
import org.matrix.android.sdk.api.MatrixPatterns.getServerName 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.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership 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.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter 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 org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import java.net.URLEncoder import java.net.URLEncoder
import javax.inject.Inject import javax.inject.Inject
@@ -101,10 +97,7 @@ internal class ViaParameterFinder @Inject constructor(
} }
fun userCanInvite(userId: String, roomId: String): Boolean { fun userCanInvite(userId: String, roomId: String): Boolean {
val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId)
?.content?.toModel<PowerLevelsContent>() return roomPowerLevels.isUserAbleToInvite(userId)
?.let { PowerLevelsHelper(it) }
return powerLevelsHelper?.isUserAbleToInvite(userId) ?: false
} }
} }

View File

@@ -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.EventMatchCondition
import org.matrix.android.sdk.api.session.pushrules.RoomMemberCountCondition 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.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.getStateEvent
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 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.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter import org.matrix.android.sdk.internal.session.room.RoomGetter
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import javax.inject.Inject import javax.inject.Inject
internal class DefaultConditionResolver @Inject constructor( internal class DefaultConditionResolver @Inject constructor(
private val roomGetter: RoomGetter, private val roomGetter: RoomGetter,
@UserId private val userId: String @UserId private val userId: String,
private val stateEventDataSource: StateEventDataSource,
) : ConditionResolver { ) : ConditionResolver {
override fun resolveEventMatchCondition( override fun resolveEventMatchCondition(
@@ -55,13 +60,8 @@ internal class DefaultConditionResolver @Inject constructor(
): Boolean { ): Boolean {
val roomId = event.roomId ?: return false val roomId = event.roomId ?: return false
val room = roomGetter.getRoom(roomId) ?: return false val room = roomGetter.getRoom(roomId) ?: return false
val roomPowerLevels = room.getRoomPowerLevels()
val powerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) return condition.isSatisfied(event, roomPowerLevels)
?.content
?.toModel<PowerLevelsContent>()
?: PowerLevelsContent()
return condition.isSatisfied(event, powerLevelsContent)
} }
override fun resolveContainsDisplayNameCondition( override fun resolveContainsDisplayNameCondition(

View File

@@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.session.room package org.matrix.android.sdk.internal.session.room
import io.realm.Realm 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.crypto.verification.VerificationState
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
import org.matrix.android.sdk.api.session.events.model.Event 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.getRelationContent
import org.matrix.android.sdk.api.session.events.model.toContent 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.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.ReferencesAggregatedContent
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent 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.MessagePollResponseContent
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent 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.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.SessionManager
import org.matrix.android.sdk.internal.crypto.verification.toState import org.matrix.android.sdk.internal.crypto.verification.toState
import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent 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.livelocation.LiveLocationAggregationProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor 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.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.session.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber import timber.log.Timber
@@ -216,9 +215,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
} }
in EventType.POLL_END.values -> { in EventType.POLL_END.values -> {
sessionManager.getSessionComponent(sessionId)?.session()?.let { session -> sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
getPowerLevelsHelper(event.roomId)?.let { val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(event.roomId)
pollAggregationProcessor.handlePollEndEvent(session, it, realm, event) pollAggregationProcessor.handlePollEndEvent(session, roomPowerLevels, realm, event)
}
} }
} }
in EventType.STATE_ROOM_BEACON_INFO.values -> { 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<PowerLevelsContent>()
?.let { PowerLevelsHelper(it) }
}
private fun handleInitialAggregatedRelations( private fun handleInitialAggregatedRelations(
realm: Realm, realm: Realm,
event: Event, event: Event,

View File

@@ -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.VoteSummary
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent 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.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.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
@@ -160,13 +160,13 @@ internal class DefaultPollAggregationProcessor @Inject constructor(
return true 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 roomId = event.roomId ?: return false
val pollEventId = event.getRelationContent()?.eventId ?: return false val pollEventId = event.getRelationContent()?.eventId ?: return false
val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId
val isPollOwner = pollOwnerId == event.senderId val isPollOwner = pollOwnerId == event.senderId
if (!isPollOwner && !powerLevelsHelper.isUserAbleToRedact(event.senderId ?: "")) { if (!isPollOwner && !roomPowerLevels.isUserAbleToRedact(event.senderId ?: "")) {
return false return false
} }

View File

@@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll
import io.realm.Realm import io.realm.Realm
import org.matrix.android.sdk.api.session.Session 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.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 { internal interface PollAggregationProcessor {
/** /**
@@ -48,7 +48,7 @@ internal interface PollAggregationProcessor {
*/ */
fun handlePollEndEvent( fun handlePollEndEvent(
session: Session, session: Session,
powerLevelsHelper: PowerLevelsHelper, roomPowerLevels: RoomPowerLevels,
realm: Realm, realm: Realm,
event: Event event: Event
): Boolean ): Boolean

View File

@@ -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<PowerLevelsContent>()
val roomCreateContent = getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)?.getRoomCreateContentWithSender()
return RoomPowerLevels(powerLevelsContent, roomCreateContent)
}

View File

@@ -96,3 +96,4 @@ internal class StateEventDataSource @Inject constructor(
} }
} }
} }

View File

@@ -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.RoomType
import org.matrix.android.sdk.api.session.room.model.VersioningState 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.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.room.send.SendState
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary
import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications 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? // check if sender can post child relation in parent?
val senderId = parentInfo.stateEventSender val senderId = parentInfo.stateEventSender
val parentRoomId = parentInfo.roomId val parentRoomId = parentInfo.roomId
val powerLevelsHelper = CurrentStateEventEntity val powerLevelsContent = CurrentStateEventEntity
.getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS) .getOrNull(realm, parentRoomId, "", EventType.STATE_ROOM_POWER_LEVELS)
?.root ?.root
?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() } ?.let { ContentMapper.map(it.content).toModel<PowerLevelsContent>() }
?.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<RoomCreateContent>()
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) { if (isValidRelation) {

View File

@@ -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.homeserver.RoomVersionStatus
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 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.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.api.session.room.version.RoomVersionService
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource 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 { 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<PowerLevelsContent>() ?.content?.toModel<PowerLevelsContent>()
?.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 { companion object {

View File

@@ -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.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo 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.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.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.CreateSpaceParams
import org.matrix.android.sdk.api.session.space.JoinSpaceResult import org.matrix.android.sdk.api.session.space.JoinSpaceResult
import org.matrix.android.sdk.api.session.space.Space 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.SpaceChildSummaryEvent
import org.matrix.android.sdk.api.session.space.model.SpaceParentContent 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.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.di.UserId
import org.matrix.android.sdk.internal.session.room.RoomGetter 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.SpaceGetter
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask 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.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.state.StateEventDataSource
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
@@ -83,7 +86,7 @@ internal class DefaultSpaceService @Inject constructor(
if (isPublic) { if (isPublic) {
this.roomAliasName = roomAliasLocalPart this.roomAliasName = roomAliasLocalPart
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( 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.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
@@ -253,15 +256,8 @@ internal class DefaultSpaceService @Inject constructor(
if (roomSummaryDataSource.getRoomSummary(parentSpaceId)?.membership != Membership.JOIN) { if (roomSummaryDataSource.getRoomSummary(parentSpaceId)?.membership != Membership.JOIN) {
throw UnsupportedOperationException("Cannot add canonical child if not member of parent") throw UnsupportedOperationException("Cannot add canonical child if not member of parent")
} }
val powerLevelsEvent = stateEventDataSource.getStateEvent( val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(parentSpaceId)
roomId = parentSpaceId, if (!roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
eventType = EventType.STATE_ROOM_POWER_LEVELS,
stateKey = QueryStringValue.IsEmpty
)
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>()
?: throw UnsupportedOperationException("Cannot add canonical child, missing power level")
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
throw UnsupportedOperationException("Cannot add canonical child, not enough power level") throw UnsupportedOperationException("Cannot add canonical child, not enough power level")
} }
} }

View File

@@ -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.Content
import org.matrix.android.sdk.api.session.events.model.Event 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.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.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.WidgetManagementFailure
import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager 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.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource import org.matrix.android.sdk.internal.session.user.accountdata.UserAccountDataDataSource
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
@@ -200,12 +198,7 @@ internal class WidgetManager @Inject constructor(
} }
fun hasPermissionsToHandleWidgets(roomId: String): Boolean { fun hasPermissionsToHandleWidgets(roomId: String): Boolean {
val powerLevelsEvent = stateEventDataSource.getStateEvent( val roomPowerLevels = stateEventDataSource.getRoomPowerLevels(roomId)
roomId = roomId, return roomPowerLevels.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_WIDGET_LEGACY)
eventType = EventType.STATE_ROOM_POWER_LEVELS,
stateKey = QueryStringValue.IsEmpty
)
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>() ?: return false
return PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(userId, true, EventType.STATE_ROOM_WIDGET_LEGACY)
} }
} }

View File

@@ -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.getRoom
import org.matrix.android.sdk.api.session.room.Room 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.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.EventAnnotationsSummaryEntity
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity 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 every { room.getTimelineEvent(eventId) } returns if (hasExistingTimelineEvent) A_TIMELINE_EVENT else null
} }
private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): PowerLevelsHelper { private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): RoomPowerLevels {
val powerLevelsHelper = mockk<PowerLevelsHelper>() val roomPowerLevels = mockk<RoomPowerLevels>()
every { powerLevelsHelper.isUserAbleToRedact(userId) } returns isAbleToRedact every { roomPowerLevels.isUserAbleToRedact(userId) } returns isAbleToRedact
return powerLevelsHelper return roomPowerLevels
} }
} }

View File

@@ -376,8 +376,8 @@ internal class DefaultCreateLocalRoomStateEventsTaskTest {
powerLevelsContent.kick shouldBeEqualTo Role.Moderator.value powerLevelsContent.kick shouldBeEqualTo Role.Moderator.value
powerLevelsContent.invite shouldBeEqualTo Role.Moderator.value powerLevelsContent.invite shouldBeEqualTo Role.Moderator.value
powerLevelsContent.redact shouldBeEqualTo Role.Moderator.value powerLevelsContent.redact shouldBeEqualTo Role.Moderator.value
powerLevelsContent.eventsDefault shouldBeEqualTo Role.Default.value powerLevelsContent.eventsDefault shouldBeEqualTo Role.User.value
powerLevelsContent.usersDefault shouldBeEqualTo Role.Default.value powerLevelsContent.usersDefault shouldBeEqualTo Role.User.value
powerLevelsContent.stateDefault shouldBeEqualTo Role.Moderator.value powerLevelsContent.stateDefault shouldBeEqualTo Role.Moderator.value
// Guest access // Guest access
result.find { it.type == EventType.STATE_ROOM_GUEST_ACCESS } result.find { it.type == EventType.STATE_ROOM_GUEST_ACCESS }

View File

@@ -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.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent 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.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.read.ReadService
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -304,11 +303,11 @@ class TimelineViewModel @AssistedInject constructor(
private fun observePowerLevel() { private fun observePowerLevel() {
if (room == null) return if (room == null) return
PowerLevelsFlowFactory(room).createFlow() PowerLevelsFlowFactory(room).createFlow()
.onEach { .onEach { powerLevels ->
val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId) val canInvite = powerLevels.isUserAbleToInvite(session.myUserId)
val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId)
val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE) val isAllowedToStartWebRTCCall = powerLevels.isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE)
val isAllowedToSetupEncryption = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) val isAllowedToSetupEncryption = powerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
setState { setState {
copy( copy(
canInvite = canInvite, canInvite = canInvite,

View File

@@ -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.MessageContentWithFormattedBody
import org.matrix.android.sdk.api.session.room.model.message.MessageType 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.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.send.UserDraft
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
@@ -183,7 +182,7 @@ class MessageComposerViewModel @AssistedInject constructor(
PowerLevelsFlowFactory(room).createFlow(), PowerLevelsFlowFactory(room).createFlow(),
room.flow().liveRoomSummary().unwrap() room.flow().liveRoomSummary().unwrap()
) { pl, sum -> ) { pl, sum ->
val canSendMessage = PowerLevelsHelper(pl).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) val canSendMessage = pl.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
if (canSendMessage) { if (canSendMessage) {
val isE2E = sum.isEncrypted val isE2E = sum.isEncrypted
if (isE2E) { if (isE2E) {

View File

@@ -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.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent 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.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.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
@@ -117,11 +117,10 @@ class MessageActionsViewModel @AssistedInject constructor(
return return
} }
PowerLevelsFlowFactory(room).createFlow() PowerLevelsFlowFactory(room).createFlow()
.onEach { .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it) val canReact = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.REACTION)
val canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION) val canRedact = roomPowerLevels.isUserAbleToRedact(session.myUserId)
val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId) val canSendMessage = roomPowerLevels.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact) val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact)
setState { setState {
copy(actionPermissions = permissions) copy(actionPermissions = permissions)

View File

@@ -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.content.EncryptionEventContent
import org.matrix.android.sdk.api.session.events.model.toModel 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.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.getStateEvent
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 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.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.room.timeline.TimelineEvent
import javax.inject.Inject import javax.inject.Inject
@@ -303,9 +304,7 @@ class MergedHeaderItemFactory @Inject constructor(
collapsedEventIds.removeAll(mergedEventIds) collapsedEventIds.removeAll(mergedEventIds)
} }
val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() } val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }
val powerLevelsHelper = activeSessionHolder.getSafeActiveSession()?.getRoom(event.roomId) val roomPowerLevels = activeSessionHolder.getSafeActiveSession()?.getRoom(event.roomId)?.getRoomPowerLevels()
?.let { it.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)?.content?.toModel<PowerLevelsContent>() }
?.let { PowerLevelsHelper(it) }
val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: "" val currentUserId = activeSessionHolder.getSafeActiveSession()?.myUserId ?: ""
val attributes = MergedRoomCreationItem.Attributes( val attributes = MergedRoomCreationItem.Attributes(
isCollapsed = isCollapsed, isCollapsed = isCollapsed,
@@ -320,10 +319,10 @@ class MergedHeaderItemFactory @Inject constructor(
callback = callback, callback = callback,
currentUserId = currentUserId, currentUserId = currentUserId,
roomSummary = partialState.roomSummary, roomSummary = partialState.roomSummary,
canInvite = powerLevelsHelper?.isUserAbleToInvite(currentUserId) ?: false, canInvite = roomPowerLevels?.isUserAbleToInvite(currentUserId) ?: false,
canChangeAvatar = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false, canChangeAvatar = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false,
canChangeTopic = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false, canChangeTopic = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false,
canChangeName = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false canChangeName = roomPowerLevels?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false
) )
MergedRoomCreationItem_() MergedRoomCreationItem_()
.id(mergeId) .id(mergeId)

View File

@@ -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.RoomTopicContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent 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.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.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
import timber.log.Timber import timber.log.Timber
@@ -122,8 +122,8 @@ class NoticeEventFormatter @Inject constructor(
userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys) userIds.addAll(previousPowerLevelsContent.users.orEmpty().keys)
val diffs = ArrayList<String>() val diffs = ArrayList<String>()
userIds.forEach { userId -> userIds.forEach { userId ->
val from = PowerLevelsHelper(previousPowerLevelsContent).getUserRole(userId) val from = RoomPowerLevels(previousPowerLevelsContent,null).getUserRole(userId)
val to = PowerLevelsHelper(powerLevelsContent).getUserRole(userId) val to = RoomPowerLevels(powerLevelsContent, null).getUserRole(userId)
if (from != to) { if (from != to) {
val fromStr = roleFormatter.format(from) val fromStr = roleFormatter.format(from)
val toStr = roleFormatter.format(to) val toStr = roleFormatter.format(to)

View File

@@ -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.events.model.EventType
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getUserOrDefault 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 org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber import timber.log.Timber
@@ -75,11 +75,10 @@ class LocationSharingViewModel @AssistedInject constructor(
private fun observePowerLevelsForLiveLocationSharing() { private fun observePowerLevelsForLiveLocationSharing() {
PowerLevelsFlowFactory(room).createFlow() PowerLevelsFlowFactory(room).createFlow()
.distinctUntilChanged() .distinctUntilChanged()
.setOnEach { .setOnEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it)
val canShareLiveLocation = EventType.STATE_ROOM_BEACON_INFO.values val canShareLiveLocation = EventType.STATE_ROOM_BEACON_INFO.values
.all { beaconInfoType -> .all { beaconInfoType ->
powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, beaconInfoType) roomPowerLevels.isUserAllowedToSend(session.myUserId, true, beaconInfoType)
} }
copy(canShareLiveLocation = canShareLiveLocation) copy(canShareLiveLocation = canShareLiveLocation)

View File

@@ -9,23 +9,40 @@ package im.vector.app.features.powerlevel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import org.matrix.android.sdk.api.query.QueryStringValue 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.EventType
import org.matrix.android.sdk.api.session.events.model.toModel 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.Room
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 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.flow
import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.mapOptional
import org.matrix.android.sdk.flow.unwrap
class PowerLevelsFlowFactory(private val room: Room) { class PowerLevelsFlowFactory(private val room: Room) {
fun createFlow(): Flow<PowerLevelsContent> { fun createFlow(): Flow<RoomPowerLevels> {
return room.flow() val flowRoom = room.flow()
val powerLevelsFlow = flowRoom
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) .liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty)
.mapOptional { it.content.toModel<PowerLevelsContent>() } .mapOptional { it.content.toModel<PowerLevelsContent>() }
.flowOn(Dispatchers.Default) .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()
)
}
} }
} }

View File

@@ -8,6 +8,7 @@
package im.vector.app.features.roommemberprofile package im.vector.app.features.roommemberprofile
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
sealed class RoomMemberProfileAction : VectorViewModelAction { sealed class RoomMemberProfileAction : VectorViewModelAction {
object RetryFetchingInfo : RoomMemberProfileAction() object RetryFetchingInfo : RoomMemberProfileAction()
@@ -18,7 +19,7 @@ sealed class RoomMemberProfileAction : VectorViewModelAction {
object InviteUser : RoomMemberProfileAction() object InviteUser : RoomMemberProfileAction()
object VerifyUser : RoomMemberProfileAction() object VerifyUser : RoomMemberProfileAction()
object ShareRoomMemberProfile : 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 SetUserColorOverride(val newColorSpec: String) : RoomMemberProfileAction()
data class OpenOrCreateDm(val userId: String) : RoomMemberProfileAction() data class OpenOrCreateDm(val userId: String) : RoomMemberProfileAction()
} }

View File

@@ -17,8 +17,8 @@ import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import im.vector.lib.strings.CommonStrings import im.vector.lib.strings.CommonStrings
import org.matrix.android.sdk.api.session.Session 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.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.Role
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
import javax.inject.Inject import javax.inject.Inject
class RoomMemberProfileController @Inject constructor( class RoomMemberProfileController @Inject constructor(
@@ -38,7 +38,7 @@ class RoomMemberProfileController @Inject constructor(
fun onOverrideColorClicked() fun onOverrideColorClicked()
fun onJumpToReadReceiptClicked() fun onJumpToReadReceiptClicked()
fun onMentionClicked() fun onMentionClicked()
fun onEditPowerLevel(currentRole: Role) fun onEditPowerLevel(userPowerLevel: UserPowerLevel)
fun onKickClicked(isSpace: Boolean) fun onKickClicked(isSpace: Boolean)
fun onBanClicked(isSpace: Boolean, isUserBanned: Boolean) fun onBanClicked(isSpace: Boolean, isUserBanned: Boolean)
fun onCancelInviteClicked() fun onCancelInviteClicked()
@@ -243,11 +243,10 @@ class RoomMemberProfileController @Inject constructor(
} }
private fun buildAdminSection(state: RoomMemberProfileViewState) { private fun buildAdminSection(state: RoomMemberProfileViewState) {
val powerLevelsContent = state.powerLevelsContent ?: return
val powerLevelsStr = state.userPowerLevelString() ?: return val powerLevelsStr = state.userPowerLevelString() ?: return
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) val roomPowerLevels = state.roomPowerLevels ?: return
val userPowerLevel = powerLevelsHelper.getUserRole(state.userId) val userPowerLevel = roomPowerLevels.getUserPowerLevel(state.userId)
val myPowerLevel = powerLevelsHelper.getUserRole(session.myUserId) val myPowerLevel = roomPowerLevels.getUserPowerLevel(session.myUserId)
if ((!state.isMine && myPowerLevel <= userPowerLevel)) { if ((!state.isMine && myPowerLevel <= userPowerLevel)) {
return return
} }

View File

@@ -51,7 +51,7 @@ import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
import im.vector.lib.strings.CommonStrings import im.vector.lib.strings.CommonStrings
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel 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 org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject import javax.inject.Inject
@@ -377,9 +377,9 @@ class RoomMemberProfileFragment :
.show() .show()
} }
override fun onEditPowerLevel(currentRole: Role) { override fun onEditPowerLevel(userPowerLevel: UserPowerLevel) {
EditPowerLevelDialogs.showChoice(requireActivity(), CommonStrings.power_level_edit_title, currentRole) { newPowerLevel -> EditPowerLevelDialogs.showChoice(requireActivity(), CommonStrings.power_level_edit_title, userPowerLevel) { newPowerLevel ->
viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true)) viewModel.handle(RoomMemberProfileAction.SetPowerLevel(userPowerLevel, newPowerLevel, true))
} }
} }

View File

@@ -8,6 +8,7 @@
package im.vector.app.features.roommemberprofile package im.vector.app.features.roommemberprofile
import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
/** /**
* Transient events for RoomMemberProfile. * Transient events for RoomMemberProfile.
@@ -22,8 +23,8 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents {
object OnInviteActionSuccess : RoomMemberProfileViewEvents() object OnInviteActionSuccess : RoomMemberProfileViewEvents()
object OnKickActionSuccess : RoomMemberProfileViewEvents() object OnKickActionSuccess : RoomMemberProfileViewEvents()
object OnBanActionSuccess : RoomMemberProfileViewEvents() object OnBanActionSuccess : RoomMemberProfileViewEvents()
data class ShowPowerLevelValidation(val currentValue: Int, val newValue: Int) : RoomMemberProfileViewEvents() data class ShowPowerLevelValidation(val currentValue: UserPowerLevel, val newValue: UserPowerLevel.Value) : RoomMemberProfileViewEvents()
data class ShowPowerLevelDemoteWarning(val currentValue: Int, val newValue: Int) : RoomMemberProfileViewEvents() data class ShowPowerLevelDemoteWarning(val currentValue: UserPowerLevel, val newValue: UserPowerLevel.Value) : RoomMemberProfileViewEvents()
data class StartVerification( data class StartVerification(
val userId: String, val userId: String,

View File

@@ -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.Room
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams 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.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.RoomEncryptionAlgorithm
import org.matrix.android.sdk.api.session.room.model.RoomType 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.room.powerlevels.Role
import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
@@ -233,15 +233,15 @@ class RoomMemberProfileViewModel @AssistedInject constructor(
if (room == null || action.previousValue == action.newValue) { if (room == null || action.previousValue == action.newValue) {
return@withState return@withState
} }
val currentPowerLevelsContent = state.powerLevelsContent ?: return@withState val roomPowerLevels = state.roomPowerLevels ?: return@withState
val myPowerLevel = PowerLevelsHelper(currentPowerLevelsContent).getUserPowerLevelValue(session.myUserId) val myPowerLevel = roomPowerLevels.getUserPowerLevel(session.myUserId)
if (action.askForValidation && action.newValue >= myPowerLevel) { if (action.askForValidation && action.newValue >= myPowerLevel) {
_viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelValidation(action.previousValue, action.newValue)) _viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelValidation(action.previousValue, action.newValue))
} else if (action.askForValidation && state.isMine) { } else if (action.askForValidation && state.isMine) {
_viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelDemoteWarning(action.previousValue, action.newValue)) _viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelDemoteWarning(action.previousValue, action.newValue))
} else { } else {
val newPowerLevelsContent = currentPowerLevelsContent val newPowerLevelsContent = (roomPowerLevels.powerLevelsContent ?: PowerLevelsContent())
.setUserPowerLevel(state.userId, action.newValue) .setUserPowerLevel(state.userId, action.newValue.value)
.toContent() .toContent()
viewModelScope.launch { viewModelScope.launch {
_viewEvents.post(RoomMemberProfileViewEvents.Loading()) _viewEvents.post(RoomMemberProfileViewEvents.Loading())
@@ -361,19 +361,17 @@ class RoomMemberProfileViewModel @AssistedInject constructor(
private fun observeRoomSummaryAndPowerLevels(room: Room) { private fun observeRoomSummaryAndPowerLevels(room: Room) {
val roomSummaryLive = room.flow().liveRoomSummary().unwrap() val roomSummaryLive = room.flow().liveRoomSummary().unwrap()
val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow()
powerLevelsFlow
powerLevelsContentLive .onEach { roomPowerLevels ->
.onEach {
val powerLevelsHelper = PowerLevelsHelper(it)
val permissions = ActionPermissions( val permissions = ActionPermissions(
canKick = powerLevelsHelper.isUserAbleToKick(session.myUserId), canKick = roomPowerLevels.isUserAbleToKick(session.myUserId),
canBan = powerLevelsHelper.isUserAbleToBan(session.myUserId), canBan = roomPowerLevels.isUserAbleToBan(session.myUserId),
canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId), canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId),
canEditPowerLevel = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_POWER_LEVELS) canEditPowerLevel = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_POWER_LEVELS)
) )
setState { setState {
copy(powerLevelsContent = it, actionPermissions = permissions) copy(roomPowerLevels = roomPowerLevels, actionPermissions = permissions)
} }
}.launchIn(viewModelScope) }.launchIn(viewModelScope)
@@ -388,14 +386,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor(
copy(isRoomEncrypted = false) copy(isRoomEncrypted = false)
} }
} }
roomSummaryLive.combine(powerLevelsContentLive) { roomSummary, powerLevelsContent -> roomSummaryLive.combine(powerLevelsFlow) { roomSummary, roomPowerLevels ->
val roomName = roomSummary.toMatrixItem().getBestName() val roomName = roomSummary.toMatrixItem().getBestName()
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) when (roomPowerLevels.getUserRole(initialState.userId)) {
when (val userPowerLevel = powerLevelsHelper.getUserRole(initialState.userId)) { Role.SuperAdmin,
Role.Creator,
Role.Admin -> stringProvider.getString(CommonStrings.room_member_power_level_admin_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.Moderator -> stringProvider.getString(CommonStrings.room_member_power_level_moderator_in, roomName)
Role.Default -> stringProvider.getString(CommonStrings.room_member_power_level_default_in, roomName) Role.User -> 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)
} }
}.execute { }.execute {
copy(userPowerLevelString = it) copy(userPowerLevelString = it)

View File

@@ -12,7 +12,7 @@ import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo 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.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 import org.matrix.android.sdk.api.util.MatrixItem
data class RoomMemberProfileViewState( data class RoomMemberProfileViewState(
@@ -24,7 +24,7 @@ data class RoomMemberProfileViewState(
val isIgnored: Async<Boolean> = Uninitialized, val isIgnored: Async<Boolean> = Uninitialized,
val isRoomEncrypted: Boolean = false, val isRoomEncrypted: Boolean = false,
val isAlgorithmSupported: Boolean = true, val isAlgorithmSupported: Boolean = true,
val powerLevelsContent: PowerLevelsContent? = null, val roomPowerLevels: RoomPowerLevels? = null,
val userPowerLevelString: Async<String> = Uninitialized, val userPowerLevelString: Async<String> = Uninitialized,
val userMatrixItem: Async<MatrixItem> = Uninitialized, val userMatrixItem: Async<MatrixItem> = Uninitialized,
val userMXCrossSigningInfo: MXCrossSigningInfo? = null, val userMXCrossSigningInfo: MXCrossSigningInfo? = null,

View File

@@ -19,6 +19,7 @@ import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.databinding.DialogEditPowerLevelBinding import im.vector.app.databinding.DialogEditPowerLevelBinding
import im.vector.lib.strings.CommonStrings 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.Role
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
object EditPowerLevelDialogs { object EditPowerLevelDialogs {
@@ -26,21 +27,21 @@ object EditPowerLevelDialogs {
fun showChoice( fun showChoice(
activity: Activity, activity: Activity,
@StringRes titleRes: Int, @StringRes titleRes: Int,
currentRole: Role, currentPowerLevel: UserPowerLevel,
listener: (Int) -> Unit listener: (UserPowerLevel.Value) -> Unit
) { ) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null) val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null)
val views = DialogEditPowerLevelBinding.bind(dialogLayout) val views = DialogEditPowerLevelBinding.bind(dialogLayout)
views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId -> views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId ->
views.powerLevelCustomEditLayout.isVisible = checkedId == R.id.powerLevelCustomRadio views.powerLevelCustomEditLayout.isVisible = checkedId == R.id.powerLevelCustomRadio
} }
views.powerLevelCustomEdit.setText("${currentRole.value}") val currentRole = Role.getSuggestedRole(currentPowerLevel)
when (currentRole) { when (currentRole) {
Role.Creator -> views.powerLevelAdminRadio.isChecked = true
Role.SuperAdmin -> views.powerLevelAdminRadio.isChecked = true
Role.Admin -> views.powerLevelAdminRadio.isChecked = true Role.Admin -> views.powerLevelAdminRadio.isChecked = true
Role.Moderator -> views.powerLevelModeratorRadio.isChecked = true Role.Moderator -> views.powerLevelModeratorRadio.isChecked = true
Role.Default -> views.powerLevelDefaultRadio.isChecked = true Role.User -> views.powerLevelDefaultRadio.isChecked = true
else -> views.powerLevelCustomRadio.isChecked = true
} }
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)
@@ -48,14 +49,14 @@ object EditPowerLevelDialogs {
.setView(dialogLayout) .setView(dialogLayout)
.setPositiveButton(CommonStrings.edit) { _, _ -> .setPositiveButton(CommonStrings.edit) { _, _ ->
val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) { val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) {
R.id.powerLevelAdminRadio -> Role.Admin.value R.id.powerLevelAdminRadio -> UserPowerLevel.Admin
R.id.powerLevelModeratorRadio -> Role.Moderator.value R.id.powerLevelModeratorRadio -> UserPowerLevel.Moderator
R.id.powerLevelDefaultRadio -> Role.Default.value R.id.powerLevelDefaultRadio -> UserPowerLevel.User
else -> { else -> null
views.powerLevelCustomEdit.text?.toString()?.toInt() ?: currentRole.value }
} if(newValue != null) {
listener(newValue)
} }
listener(newValue)
} }
.setNegativeButton(CommonStrings.action_cancel, null) .setNegativeButton(CommonStrings.action_cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener .setOnKeyListener(DialogInterface.OnKeyListener

View File

@@ -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.members.roomMemberQueryParams
import org.matrix.android.sdk.api.session.room.model.Membership 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.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.api.session.room.state.isPublic
import org.matrix.android.sdk.flow.FlowRoom import org.matrix.android.sdk.flow.FlowRoom
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
@@ -115,9 +115,8 @@ class RoomProfileViewModel @AssistedInject constructor(
private fun observePowerLevels() { private fun observePowerLevels() {
val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow()
powerLevelsContentLive powerLevelsContentLive
.onEach { .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it) val canUpdateRoomState = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
val canUpdateRoomState = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION)
setState { setState {
copy(canUpdateRoomState = canUpdateRoomState) copy(canUpdateRoomState = canUpdateRoomState)
} }
@@ -158,10 +157,9 @@ class RoomProfileViewModel @AssistedInject constructor(
private fun observePermissions() { private fun observePermissions() {
PowerLevelsFlowFactory(room) PowerLevelsFlowFactory(room)
.createFlow() .createFlow()
.setOnEach { .setOnEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it)
val permissions = RoomProfileViewState.ActionPermissions( 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) copy(actionPermissions = permissions)
} }

View File

@@ -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.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom 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.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.flow
import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.mapOptional
import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.flow.unwrap
@@ -127,10 +127,9 @@ class RoomAliasViewModel @AssistedInject constructor(
private fun observePowerLevel() { private fun observePowerLevel() {
PowerLevelsFlowFactory(room) PowerLevelsFlowFactory(room)
.createFlow() .createFlow()
.onEach { .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it)
val permissions = RoomAliasViewState.ActionPermissions( val permissions = RoomAliasViewState.ActionPermissions(
canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend( canChangeCanonicalAlias = roomPowerLevels.isUserAllowedToSend(
userId = session.myUserId, userId = session.myUserId,
isState = true, isState = true,
eventType = EventType.STATE_ROOM_CANONICAL_ALIAS eventType = EventType.STATE_ROOM_CANONICAL_ALIAS

View File

@@ -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.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent 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.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.flow
import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.flow.unwrap
@@ -62,12 +62,10 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(
) )
} }
val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow()
powerLevelsFlow
powerLevelsContentLive .setOnEach { roomPowerLevels ->
.setOnEach { copy(canUserBan = roomPowerLevels.isUserAbleToBan(session.myUserId))
val powerLevelsHelper = PowerLevelsHelper(it)
copy(canUserBan = powerLevelsHelper.isUserAbleToBan(session.myUserId))
} }
} }

View File

@@ -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<RoomMemberWithPowerLevel> {
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)
}
}
}
}
}
}
}
}

View File

@@ -16,11 +16,13 @@ import im.vector.app.core.extensions.join
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.roomprofile.permissions.RoleFormatter
import me.gujun.android.span.span 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.Event
import org.matrix.android.sdk.api.session.events.model.toModel 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.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent 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.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject import javax.inject.Inject
@@ -29,7 +31,8 @@ class RoomMemberListController @Inject constructor(
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val roomMemberSummaryFilter: RoomMemberSummaryFilter private val roomMemberSummaryFilter: RoomMemberSummaryFilter,
private val roleFormatter: RoleFormatter,
) : TypedEpoxyController<RoomMemberListViewState>() { ) : TypedEpoxyController<RoomMemberListViewState>() {
interface Callback { interface Callback {
@@ -56,13 +59,13 @@ class RoomMemberListController @Inject constructor(
.orEmpty() .orEmpty()
var threePidInvitesDone = filteredThreePidInvites.isEmpty() var threePidInvitesDone = filteredThreePidInvites.isEmpty()
for ((powerLevelCategory, roomMemberList) in roomMembersByPowerLevel) { for ((category, roomMemberList) in roomMembersByPowerLevel) {
val filteredRoomMemberList = roomMemberList.filter { roomMemberSummaryFilter.test(it) } val filteredRoomMemberList = roomMemberList.filter { roomMemberSummaryFilter.test(it.summary) }
if (filteredRoomMemberList.isEmpty()) { if (filteredRoomMemberList.isEmpty()) {
continue continue
} }
if (powerLevelCategory == RoomMemberListCategories.USER && !threePidInvitesDone) { if (category == RoomMemberListCategories.USER && !threePidInvitesDone) {
// If there is no regular invite, display threepid invite before the regular user // If there is no regular invite, display threepid invite before the regular user
buildProfileSection( buildProfileSection(
stringProvider.getString(RoomMemberListCategories.INVITE.titleRes) stringProvider.getString(RoomMemberListCategories.INVITE.titleRes)
@@ -73,20 +76,20 @@ class RoomMemberListController @Inject constructor(
} }
buildProfileSection( buildProfileSection(
stringProvider.getString(powerLevelCategory.titleRes) stringProvider.getString(category.titleRes)
) )
filteredRoomMemberList.join( filteredRoomMemberList.join(
each = { _, roomMember -> each = { _, roomMember ->
buildRoomMember(roomMember, powerLevelCategory, host, data) buildRoomMember(roomMember, host, data)
}, },
between = { _, roomMemberBefore -> between = { _, roomMemberBefore ->
dividerItem { 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 // Display the threepid invite after the regular invite
dividerItem { dividerItem {
id("divider_threepidinvites") id("divider_threepidinvites")
@@ -108,24 +111,24 @@ class RoomMemberListController @Inject constructor(
} }
private fun buildRoomMember( private fun buildRoomMember(
roomMember: RoomMemberSummary, roomMember: RoomMemberWithPowerLevel,
powerLevelCategory: RoomMemberListCategories,
host: RoomMemberListController, host: RoomMemberListController,
data: RoomMemberListViewState data: RoomMemberListViewState
) { ) {
val powerLabel = stringProvider.getString(powerLevelCategory.titleRes) val role = Role.getSuggestedRole(roomMember.powerLevel)
val powerLabel = roleFormatter.format(role)
profileMatrixItemWithPowerLevelWithPresence { profileMatrixItemWithPowerLevelWithPresence {
id(roomMember.userId) id(roomMember.summary.userId)
matrixItem(roomMember.toMatrixItem()) matrixItem(roomMember.summary.toMatrixItem())
avatarRenderer(host.avatarRenderer) avatarRenderer(host.avatarRenderer)
userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.summary.userId))
clickListener { clickListener {
host.callback?.onRoomMemberClicked(roomMember) host.callback?.onRoomMemberClicked(roomMember.summary)
} }
showPresence(true) showPresence(true)
userPresence(roomMember.userPresence) userPresence(roomMember.summary.userPresence)
ignoredUser(roomMember.userId in data.ignoredUserIds) ignoredUser(roomMember.summary.userId in data.ignoredUserIds)
powerLevelLabel( powerLevelLabel(
span { span {
span(powerLabel) { span(powerLabel) {

View File

@@ -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.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel 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.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.getRoom
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams 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.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.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.Role
import org.matrix.android.sdk.api.session.room.powerlevels.RoomPowerLevels
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.mapOptional
import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.flow.unwrap
import timber.log.Timber import timber.log.Timber
class RoomMemberListViewModel @AssistedInject constructor( class RoomMemberListViewModel @AssistedInject constructor(
@Assisted initialState: RoomMemberListViewState, @Assisted initialState: RoomMemberListViewState,
private val roomMemberSummaryComparator: RoomMemberSummaryComparator, private val roomMemberListComparator: RoomMemberListComparator,
private val session: Session private val session: Session
) : ) :
VectorViewModel<RoomMemberListViewState, RoomMemberListAction, EmptyViewEvents>(initialState) { VectorViewModel<RoomMemberListViewState, RoomMemberListAction, EmptyViewEvents>(initialState) {
@@ -75,14 +72,12 @@ class RoomMemberListViewModel @AssistedInject constructor(
memberships = Membership.activeMemberships() memberships = Membership.activeMemberships()
} }
val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow()
combine( combine(
roomFlow.liveRoomMembers(roomMemberQueryParams), roomFlow.liveRoomMembers(roomMemberQueryParams),
roomFlow powerLevelsFlow,
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) ) { roomMembers, roomPowerLevels ->
.mapOptional { it.content.toModel<PowerLevelsContent>() } buildRoomMemberSummaries(roomPowerLevels, roomMembers)
.unwrap()
) { roomMembers, powerLevelsContent ->
buildRoomMemberSummaries(powerLevelsContent, roomMembers)
} }
.execute { async -> .execute { async ->
copy(roomMemberSummaries = async) copy(roomMemberSummaries = async)
@@ -143,10 +138,10 @@ class RoomMemberListViewModel @AssistedInject constructor(
private fun observePowerLevel() { private fun observePowerLevel() {
PowerLevelsFlowFactory(room).createFlow() PowerLevelsFlowFactory(room).createFlow()
.onEach { .onEach { roomPowerLevels ->
val permissions = ActionPermissions( val permissions = ActionPermissions(
canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId), canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId),
canRevokeThreePidInvite = PowerLevelsHelper(it).isUserAllowedToSend( canRevokeThreePidInvite = roomPowerLevels.isUserAllowedToSend(
userId = session.myUserId, userId = session.myUserId,
isState = true, isState = true,
eventType = EventType.STATE_ROOM_THIRD_PARTY_INVITE eventType = EventType.STATE_ROOM_THIRD_PARTY_INVITE
@@ -184,31 +179,34 @@ class RoomMemberListViewModel @AssistedInject constructor(
} }
} }
private fun buildRoomMemberSummaries(powerLevelsContent: PowerLevelsContent, roomMembers: List<RoomMemberSummary>): RoomMemberSummaries { private fun buildRoomMemberSummaries(roomPowerLevels: RoomPowerLevels, roomMembers: List<RoomMemberSummary>): RoomMembersByRole {
val admins = ArrayList<RoomMemberSummary>() val admins = ArrayList<RoomMemberWithPowerLevel>()
val moderators = ArrayList<RoomMemberSummary>() val moderators = ArrayList<RoomMemberWithPowerLevel>()
val users = ArrayList<RoomMemberSummary>(roomMembers.size) val users = ArrayList<RoomMemberWithPowerLevel>(roomMembers.size)
val customs = ArrayList<RoomMemberSummary>() val invites = ArrayList<RoomMemberWithPowerLevel>()
val invites = ArrayList<RoomMemberSummary>()
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
roomMembers roomMembers
.forEach { roomMember -> .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 { when {
roomMember.membership == Membership.INVITE -> invites.add(roomMember) roomMember.membership == Membership.INVITE -> invites.add(roomMemberWithPowerLevel)
userRole == Role.Admin -> admins.add(roomMember) userRole == Role.SuperAdmin ||
userRole == Role.Moderator -> moderators.add(roomMember) userRole == Role.Creator ||
userRole == Role.Default -> users.add(roomMember) userRole == Role.Admin -> admins.add(roomMemberWithPowerLevel)
else -> customs.add(roomMember) userRole == Role.Moderator -> moderators.add(roomMemberWithPowerLevel)
userRole == Role.User -> users.add(roomMemberWithPowerLevel)
} }
} }
return listOf( return listOf(
RoomMemberListCategories.ADMIN to admins.sortedWith(roomMemberSummaryComparator), RoomMemberListCategories.ADMIN to admins.sortedWith(roomMemberListComparator),
RoomMemberListCategories.MODERATOR to moderators.sortedWith(roomMemberSummaryComparator), RoomMemberListCategories.MODERATOR to moderators.sortedWith(roomMemberListComparator),
RoomMemberListCategories.CUSTOM to customs.sortedWith(roomMemberSummaryComparator), RoomMemberListCategories.INVITE to invites.sortedWith(roomMemberListComparator),
RoomMemberListCategories.INVITE to invites.sortedWith(roomMemberSummaryComparator), RoomMemberListCategories.USER to users.sortedWith(roomMemberListComparator)
RoomMemberListCategories.USER to users.sortedWith(roomMemberSummaryComparator)
) )
} }

View File

@@ -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.events.model.Event
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary 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.model.RoomSummary
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
data class RoomMemberListViewState( data class RoomMemberListViewState(
val roomId: String, val roomId: String,
val roomSummary: Async<RoomSummary> = Uninitialized, val roomSummary: Async<RoomSummary> = Uninitialized,
val roomMemberSummaries: Async<RoomMemberSummaries> = Uninitialized, val roomMemberSummaries: Async<RoomMembersByRole> = Uninitialized,
val areAllMembersLoaded: Boolean = false, val areAllMembersLoaded: Boolean = false,
val ignoredUserIds: List<String> = emptyList(), val ignoredUserIds: List<String> = emptyList(),
val filter: String = "", val filter: String = "",
@@ -41,12 +42,16 @@ data class ActionPermissions(
val canRevokeThreePidInvite: Boolean = false val canRevokeThreePidInvite: Boolean = false
) )
typealias RoomMemberSummaries = List<Pair<RoomMemberListCategories, List<RoomMemberSummary>>> data class RoomMemberWithPowerLevel(
val powerLevel: UserPowerLevel,
val summary: RoomMemberSummary,
)
typealias RoomMembersByRole = List<Pair<RoomMemberListCategories, List<RoomMemberWithPowerLevel>>>
enum class RoomMemberListCategories(@StringRes val titleRes: Int) { enum class RoomMemberListCategories(@StringRes val titleRes: Int) {
ADMIN(CommonStrings.room_member_power_level_admins), ADMIN(CommonStrings.room_member_power_level_admins),
MODERATOR(CommonStrings.room_member_power_level_moderators), MODERATOR(CommonStrings.room_member_power_level_moderators),
CUSTOM(CommonStrings.room_member_power_level_custom),
INVITE(CommonStrings.room_member_power_level_invites), INVITE(CommonStrings.room_member_power_level_invites),
USER(CommonStrings.room_member_power_level_users) USER(CommonStrings.room_member_power_level_users)
} }

View File

@@ -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<RoomMemberSummary> {
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)
}
}
}
}
}
}
}
}

View File

@@ -19,8 +19,9 @@ class RoleFormatter @Inject constructor(
return when (role) { return when (role) {
Role.Admin -> stringProvider.getString(CommonStrings.power_level_admin) Role.Admin -> stringProvider.getString(CommonStrings.power_level_admin)
Role.Moderator -> stringProvider.getString(CommonStrings.power_level_moderator) Role.Moderator -> stringProvider.getString(CommonStrings.power_level_moderator)
Role.Default -> stringProvider.getString(CommonStrings.power_level_default) Role.User -> stringProvider.getString(CommonStrings.power_level_default)
is Role.Custom -> stringProvider.getString(CommonStrings.power_level_custom, role.value) Role.Creator -> stringProvider.getString(CommonStrings.power_level_owner)
Role.SuperAdmin -> stringProvider.getString(CommonStrings.power_level_owner)
} }
} }
} }

View File

@@ -8,9 +8,10 @@
package im.vector.app.features.roomprofile.permissions package im.vector.app.features.roomprofile.permissions
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
sealed class RoomPermissionsAction : VectorViewModelAction { sealed class RoomPermissionsAction : VectorViewModelAction {
object ToggleShowAllPermissions : RoomPermissionsAction() object ToggleShowAllPermissions : RoomPermissionsAction()
data class UpdatePermission(val editablePermission: EditablePermission, val powerLevel: Int) : RoomPermissionsAction() data class UpdatePermission(val editablePermission: EditablePermission, val powerLevel: UserPowerLevel.Value) : RoomPermissionsAction()
} }

View File

@@ -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.stateDefaultOrDefault
import org.matrix.android.sdk.api.session.room.model.usersDefaultOrDefault 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.Role
import org.matrix.android.sdk.api.session.room.powerlevels.UserPowerLevel
import javax.inject.Inject import javax.inject.Inject
class RoomPermissionsController @Inject constructor( class RoomPermissionsController @Inject constructor(
@@ -34,7 +35,7 @@ class RoomPermissionsController @Inject constructor(
) : TypedEpoxyController<RoomPermissionsViewState>() { ) : TypedEpoxyController<RoomPermissionsViewState>() {
interface Callback { interface Callback {
fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel)
fun toggleShowAllPermissions() fun toggleShowAllPermissions()
} }
@@ -165,7 +166,8 @@ class RoomPermissionsController @Inject constructor(
editable: Boolean, editable: Boolean,
isSpace: Boolean isSpace: Boolean
) { ) {
val currentRole = getCurrentRole(editablePermission, content) val currentPowerLevel = getPowerLevel(editablePermission, content)
val currentRole = Role.getSuggestedRole(currentPowerLevel)
buildProfileAction( buildProfileAction(
id = editablePermission.labelResId.toString(), id = editablePermission.labelResId.toString(),
title = stringProvider.getString( title = stringProvider.getString(
@@ -177,12 +179,12 @@ class RoomPermissionsController @Inject constructor(
action = { action = {
callback callback
?.takeIf { editable } ?.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) { val value = when (editablePermission) {
is EditablePermission.EventTypeEditablePermission -> content.events?.get(editablePermission.eventType) ?: content.stateDefaultOrDefault() is EditablePermission.EventTypeEditablePermission -> content.events?.get(editablePermission.eventType) ?: content.stateDefaultOrDefault()
is EditablePermission.DefaultRole -> content.usersDefaultOrDefault() is EditablePermission.DefaultRole -> content.usersDefaultOrDefault()
@@ -194,20 +196,6 @@ class RoomPermissionsController @Inject constructor(
is EditablePermission.RemoveMessagesSentByOthers -> content.redactOrDefault() is EditablePermission.RemoveMessagesSentByOthers -> content.redactOrDefault()
is EditablePermission.NotifyEveryone -> content.notificationLevel(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY) is EditablePermission.NotifyEveryone -> content.notificationLevel(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY)
} }
return UserPowerLevel.Value(value)
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
}
)
} }
} }

View File

@@ -26,7 +26,7 @@ import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.RoomProfileArgs
import im.vector.lib.strings.CommonStrings 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 org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject import javax.inject.Inject
@@ -93,8 +93,8 @@ class RoomPermissionsFragment :
} }
} }
override fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) { override fun onEditPermission(editablePermission: EditablePermission, currentPowerLevel: UserPowerLevel) {
EditPowerLevelDialogs.showChoice(requireActivity(), editablePermission.labelResId, currentRole) { newPowerLevel -> EditPowerLevelDialogs.showChoice(requireActivity(), editablePermission.labelResId, currentPowerLevel) { newPowerLevel ->
viewModel.handle(RoomPermissionsAction.UpdatePermission(editablePermission, newPowerLevel)) viewModel.handle(RoomPermissionsAction.UpdatePermission(editablePermission, newPowerLevel))
} }
} }

View File

@@ -9,6 +9,7 @@ package im.vector.app.features.roomprofile.permissions
import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject 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.events.model.toContent
import org.matrix.android.sdk.api.session.getRoom 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.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.flow
import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.flow.unwrap
@@ -61,19 +61,23 @@ class RoomPermissionsViewModel @AssistedInject constructor(
private fun observePowerLevel() { private fun observePowerLevel() {
PowerLevelsFlowFactory(room) PowerLevelsFlowFactory(room)
.createFlow() .createFlow()
.onEach { powerLevelContent -> .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(powerLevelContent)
val permissions = RoomPermissionsViewState.ActionPermissions( val permissions = RoomPermissionsViewState.ActionPermissions(
canChangePowerLevels = powerLevelsHelper.isUserAllowedToSend( canChangePowerLevels = roomPowerLevels.isUserAllowedToSend(
userId = session.myUserId, userId = session.myUserId,
isState = true, isState = true,
eventType = EventType.STATE_ROOM_POWER_LEVELS eventType = EventType.STATE_ROOM_POWER_LEVELS
) )
) )
val powerLevelsContent = roomPowerLevels.powerLevelsContent
setState { setState {
copy( copy(
actionPermissions = permissions, actionPermissions = permissions,
currentPowerLevelsContent = Success(powerLevelContent) currentPowerLevelsContent = if (powerLevelsContent != null) {
Success(powerLevelsContent)
} else {
Uninitialized
}
) )
} }
}.launchIn(viewModelScope) }.launchIn(viewModelScope)
@@ -94,26 +98,26 @@ class RoomPermissionsViewModel @AssistedInject constructor(
private fun updatePermission(action: RoomPermissionsAction.UpdatePermission) { private fun updatePermission(action: RoomPermissionsAction.UpdatePermission) {
withState { state -> withState { state ->
val currentPowerLevel = state.currentPowerLevelsContent.invoke() ?: return@withState val currentPowerLevelsContent = state.currentPowerLevelsContent.invoke() ?: return@withState
postLoading(true) postLoading(true)
viewModelScope.launch { viewModelScope.launch {
try { try {
val newPowerLevelsContent = when (action.editablePermission) { val newPowerLevelsContent = when (action.editablePermission) {
is EditablePermission.EventTypeEditablePermission -> currentPowerLevel.copy( is EditablePermission.EventTypeEditablePermission -> currentPowerLevelsContent.copy(
events = currentPowerLevel.events.orEmpty().toMutableMap().apply { events = currentPowerLevelsContent.events.orEmpty().toMutableMap().apply {
put(action.editablePermission.eventType, action.powerLevel) put(action.editablePermission.eventType, action.powerLevel.value)
} }
) )
is EditablePermission.DefaultRole -> currentPowerLevel.copy(usersDefault = action.powerLevel) is EditablePermission.DefaultRole -> currentPowerLevelsContent.copy(usersDefault = action.powerLevel.value)
is EditablePermission.SendMessages -> currentPowerLevel.copy(eventsDefault = action.powerLevel) is EditablePermission.SendMessages -> currentPowerLevelsContent.copy(eventsDefault = action.powerLevel.value)
is EditablePermission.InviteUsers -> currentPowerLevel.copy(invite = action.powerLevel) is EditablePermission.InviteUsers -> currentPowerLevelsContent.copy(invite = action.powerLevel.value)
is EditablePermission.ChangeSettings -> currentPowerLevel.copy(stateDefault = action.powerLevel) is EditablePermission.ChangeSettings -> currentPowerLevelsContent.copy(stateDefault = action.powerLevel.value)
is EditablePermission.KickUsers -> currentPowerLevel.copy(kick = action.powerLevel) is EditablePermission.KickUsers -> currentPowerLevelsContent.copy(kick = action.powerLevel.value)
is EditablePermission.BanUsers -> currentPowerLevel.copy(ban = action.powerLevel) is EditablePermission.BanUsers -> currentPowerLevelsContent.copy(ban = action.powerLevel.value)
is EditablePermission.RemoveMessagesSentByOthers -> currentPowerLevel.copy(redact = action.powerLevel) is EditablePermission.RemoveMessagesSentByOthers -> currentPowerLevelsContent.copy(redact = action.powerLevel.value)
is EditablePermission.NotifyEveryone -> currentPowerLevel.copy( is EditablePermission.NotifyEveryone -> currentPowerLevelsContent.copy(
notifications = currentPowerLevel.notifications.orEmpty().toMutableMap().apply { notifications = currentPowerLevelsContent.notifications.orEmpty().toMutableMap().apply {
put(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY, action.powerLevel) put(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY, action.powerLevel.value)
} }
) )
} }

View File

@@ -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.RoomGuestAccessContent
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent 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.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.flow
import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.mapOptional
import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.flow.unwrap
@@ -115,28 +114,26 @@ class RoomSettingsViewModel @AssistedInject constructor(
) )
} }
val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow()
powerLevelsFlow
powerLevelsContentLive .onEach { roomPowerLevels ->
.onEach {
val powerLevelsHelper = PowerLevelsHelper(it)
val permissions = RoomSettingsViewState.ActionPermissions( val permissions = RoomSettingsViewState.ActionPermissions(
canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR), canChangeAvatar = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR),
canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), canChangeName = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME),
canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), canChangeTopic = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC),
canChangeHistoryVisibility = powerLevelsHelper.isUserAllowedToSend( canChangeHistoryVisibility = roomPowerLevels.isUserAllowedToSend(
session.myUserId, true, session.myUserId, true,
EventType.STATE_ROOM_HISTORY_VISIBILITY EventType.STATE_ROOM_HISTORY_VISIBILITY
), ),
canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend( canChangeJoinRule = roomPowerLevels.isUserAllowedToSend(
session.myUserId, true, session.myUserId, true,
EventType.STATE_ROOM_JOIN_RULES EventType.STATE_ROOM_JOIN_RULES
) && ) &&
powerLevelsHelper.isUserAllowedToSend( roomPowerLevels.isUserAllowedToSend(
session.myUserId, true, session.myUserId, true,
EventType.STATE_ROOM_GUEST_ACCESS EventType.STATE_ROOM_GUEST_ACCESS
), ),
canAddChildren = powerLevelsHelper.isUserAllowedToSend( canAddChildren = roomPowerLevels.isUserAllowedToSend(
session.myUserId, true, session.myUserId, true,
EventType.STATE_SPACE_CHILD EventType.STATE_SPACE_CHILD
) )

View File

@@ -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.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary 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.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.Role
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
@@ -74,20 +73,19 @@ class SpaceMenuViewModel @AssistedInject constructor(
PowerLevelsFlowFactory(room) PowerLevelsFlowFactory(room)
.createFlow() .createFlow()
.onEach { .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it)
val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId) val canInvite = roomPowerLevels.isUserAbleToInvite(session.myUserId)
val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) val canAddChild = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD)
val canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR) val canChangeAvatar = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR)
val canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME) val canChangeName = roomPowerLevels.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME)
val canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC) 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 val otherAdminCount = roomSummary?.otherMemberIds
?.map { powerLevelsHelper.getUserRole(it) } ?.map { roomPowerLevels.getUserRole(it) }
?.count { it is Role.Admin } ?.count { it == Role.Admin }
?: 0 ?: 0
val isLastAdmin = isAdmin && otherAdminCount == 0 val isLastAdmin = isAdmin && otherAdminCount == 0

View File

@@ -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.CreateRoomPreset
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset 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.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.CreateSpaceParams
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@@ -65,7 +66,7 @@ class CreateSpaceViewModelTask @Inject constructor(
if (params.isPublic) { if (params.isPublic) {
this.roomAliasName = params.spaceAlias this.roomAliasName = params.spaceAlias
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
invite = Role.Default.value invite = UserPowerLevel.User.value
) )
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
@@ -79,7 +80,7 @@ class CreateSpaceViewModelTask @Inject constructor(
} }
) )
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
invite = Role.Moderator.value invite = UserPowerLevel.Moderator.value
) )
} }
}) })

View File

@@ -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.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType 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.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.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import timber.log.Timber import timber.log.Timber
@@ -96,16 +95,14 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
private fun observePermissions() { private fun observePermissions() {
val room = session.getRoom(initialState.spaceId) ?: return val room = session.getRoom(initialState.spaceId) ?: return
val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() val powerLevelsFlow = PowerLevelsFlowFactory(room).createFlow()
powerLevelsContentLive powerLevelsFlow
.onEach { .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(it)
setState { setState {
copy( copy(
canAddRooms = powerLevelsHelper.isUserAllowedToSend( canAddRooms = roomPowerLevels.isUserAllowedToSend(
session.myUserId, true, userId = session.myUserId, isState = true, eventType = EventType.STATE_SPACE_CHILD
EventType.STATE_SPACE_CHILD
) )
) )
} }

View File

@@ -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.EventType
import org.matrix.android.sdk.api.session.events.model.toModel 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.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.getStateEvent
import org.matrix.android.sdk.api.session.room.model.Membership 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.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.powerlevels.Role
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
@@ -50,14 +51,12 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
init { init {
val space = session.getRoom(initialState.spaceId) val space = session.getRoom(initialState.spaceId)
val spaceSummary = space?.roomSummary() val spaceSummary = space?.roomSummary()
val roomPowerLevels = space?.getRoomPowerLevels()
val powerLevelsEvent = space?.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) roomPowerLevels?.let {
powerLevelsEvent?.content?.toModel<PowerLevelsContent>()?.let { powerLevelsContent -> val isAdmin = roomPowerLevels.getUserRole(session.myUserId) == Role.Admin
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin
val otherAdminCount = spaceSummary?.otherMemberIds val otherAdminCount = spaceSummary?.otherMemberIds
?.map { powerLevelsHelper.getUserRole(it) } ?.map { roomPowerLevels.getUserRole(it) }
?.count { it is Role.Admin } ?.count { it == Role.Admin }
?: 0 ?: 0
val isLastAdmin = isAdmin && otherAdminCount == 0 val isLastAdmin = isAdmin && otherAdminCount == 0
setState { setState {

View File

@@ -54,7 +54,7 @@ class SpacePeopleListController @Inject constructor(
memberSummaries.forEach { memberEntry -> memberSummaries.forEach { memberEntry ->
val filtered = memberEntry.second val filtered = memberEntry.second
.filter { roomMemberSummaryFilter.test(it) } .filter { roomMemberSummaryFilter.test(it.summary) }
if (filtered.isNotEmpty()) { if (filtered.isNotEmpty()) {
dividerItem { dividerItem {
id("divider_type_${memberEntry.first.titleRes}") id("divider_type_${memberEntry.first.titleRes}")
@@ -65,10 +65,10 @@ class SpacePeopleListController @Inject constructor(
.join( .join(
each = { _, roomMember -> each = { _, roomMember ->
profileMatrixItemWithPowerLevel { profileMatrixItemWithPowerLevel {
id(roomMember.userId) id(roomMember.summary.userId)
matrixItem(roomMember.toMatrixItem()) matrixItem(roomMember.summary.toMatrixItem())
avatarRenderer(host.avatarRenderer) avatarRenderer(host.avatarRenderer)
userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.summary.userId))
.apply { .apply {
val pl = host.toPowerLevelLabel(memberEntry.first) val pl = host.toPowerLevelLabel(memberEntry.first)
if (memberEntry.first == RoomMemberListCategories.INVITE) { if (memberEntry.first == RoomMemberListCategories.INVITE) {
@@ -106,13 +106,13 @@ class SpacePeopleListController @Inject constructor(
} }
clickListener { clickListener {
host.listener?.onSpaceMemberClicked(roomMember) host.listener?.onSpaceMemberClicked(roomMember.summary)
} }
} }
}, },
between = { _, roomMemberBefore -> between = { _, roomMemberBefore ->
dividerItem { dividerItem {
id("divider_${roomMemberBefore.userId}") id("divider_${roomMemberBefore.summary.userId}")
} }
} }
) )

View File

@@ -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.Session
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary 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( class ShareSpaceViewModel @AssistedInject constructor(
@Assisted private val initialState: ShareSpaceViewState, @Assisted private val initialState: ShareSpaceViewState,
@@ -52,11 +52,10 @@ class ShareSpaceViewModel @AssistedInject constructor(
val room = session.getRoom(initialState.spaceId) ?: return val room = session.getRoom(initialState.spaceId) ?: return
PowerLevelsFlowFactory(room) PowerLevelsFlowFactory(room)
.createFlow() .createFlow()
.onEach { powerLevelContent -> .onEach { roomPowerLevels ->
val powerLevelsHelper = PowerLevelsHelper(powerLevelContent)
setState { setState {
copy( copy(
canInviteByMxId = powerLevelsHelper.isUserAbleToInvite(session.myUserId) canInviteByMxId = roomPowerLevels.isUserAbleToInvite(session.myUserId)
) )
} }
} }

View File

@@ -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.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom 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.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.getStateEvent
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent 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.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.flow
import org.matrix.android.sdk.flow.unwrap import org.matrix.android.sdk.flow.unwrap
import timber.log.Timber import timber.log.Timber
@@ -139,12 +140,8 @@ class StartVoiceBroadcastUseCase @Inject constructor(
@VisibleForTesting @VisibleForTesting
fun assertHasEnoughPowerLevels(room: Room) { fun assertHasEnoughPowerLevels(room: Room) {
val powerLevelsHelper = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) val roomPowerLevels = room.getRoomPowerLevels()
?.content if (!roomPowerLevels.isUserAllowedToSend(session.myUserId, true, VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO)) {
?.toModel<PowerLevelsContent>()
?.let { PowerLevelsHelper(it) }
if (powerLevelsHelper?.isUserAllowedToSend(session.myUserId, true, VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO) != true) {
Timber.d("## StartVoiceBroadcastUseCase: Cannot start voice broadcast: no permission") Timber.d("## StartVoiceBroadcastUseCase: Cannot start voice broadcast: no permission")
throw VoiceBroadcastFailure.RecordingError.NoPermission throw VoiceBroadcastFailure.RecordingError.NoPermission
} }

View File

@@ -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.toContent
import org.matrix.android.sdk.api.session.events.model.toModel 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.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.getStateEvent
import org.matrix.android.sdk.api.session.room.model.Membership 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.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.session.widgets.WidgetPostAPIMediator
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import timber.log.Timber import timber.log.Timber
@@ -146,13 +147,8 @@ class WidgetPostAPIHandler @AssistedInject constructor(
Timber.d("## canSendEvent() : eventType $eventType isState $isState") Timber.d("## canSendEvent() : eventType $eventType isState $isState")
val powerLevelsEvent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) val roomPowerLevels = room.getRoomPowerLevels()
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>() val canSend = roomPowerLevels.isUserAllowedToSend(session.myUserId, isState, eventType)
val canSend = if (powerLevelsContent == null) {
false
} else {
PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(session.myUserId, isState, eventType)
}
if (canSend) { if (canSend) {
Timber.d("## canSendEvent() returns true") Timber.d("## canSendEvent() returns true")
widgetPostAPIMediator.sendBoolResponse(true, eventData) widgetPostAPIMediator.sendBoolResponse(true, eventData)

View File

@@ -19,9 +19,11 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session 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.getRoom
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService 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.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.api.session.widgets.WidgetManagementFailure
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.mapOptional
@@ -102,11 +104,10 @@ class WidgetViewModel @AssistedInject constructor(
if (room == null) { if (room == null) {
return return
} }
room.flow().liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.IsEmpty) PowerLevelsFlowFactory(room)
.mapOptional { it.content.toModel<PowerLevelsContent>() } .createFlow()
.unwrap() .map { roomPowerLevels ->
.map { roomPowerLevels.isUserAllowedToSend(session.myUserId, true, null)
PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, null)
} }
.setOnEach { .setOnEach {
copy(canManageWidgets = it) copy(canManageWidgets = it)

View File

@@ -13,7 +13,7 @@ import com.airbnb.mvrx.test.MavericksTestRule
import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.RoomProfileArgs
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
import im.vector.app.features.roomprofile.members.RoomMemberListViewState 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.test
import im.vector.app.test.testCoroutineDispatchers import im.vector.app.test.testCoroutineDispatchers
import io.mockk.coEvery import io.mockk.coEvery
@@ -266,7 +266,7 @@ class MemberListViewModelTest {
private fun createViewModel(): RoomMemberListViewModel { private fun createViewModel(): RoomMemberListViewModel {
return RoomMemberListViewModel( return RoomMemberListViewModel(
RoomMemberListViewState(args), RoomMemberListViewState(args),
RoomMemberSummaryComparator(), RoomMemberListComparator(),
fakeSession, fakeSession,
) )
} }