PowerLevel : clean after Benoit's review

This commit is contained in:
ganfra 2020-06-10 17:19:33 +02:00
parent bf5ad2cf18
commit 60b91d4d50
15 changed files with 93 additions and 40 deletions

View File

@ -43,20 +43,19 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @return the power level
*/
fun getUserRole(userId: String): Role {
return getUserPowerLevelValue(userId).let {
Role.fromValue(it, powerLevelsContent.eventsDefault)
}
val value = getUserPowerLevelValue(userId)
return Role.fromValue(value, powerLevelsContent.eventsDefault)
}
/**
* Tell if an user can send an event of a certain type
*
* @param userId the id of the user to check for.
* @param isState true if the event is a state event (ie. state key is not null)
* @param eventType the event type to check for
* @param userId the user id
* @return true if the user can send this type of event
*/
fun isAllowedToSend(isState: Boolean, eventType: String?, userId: String): Boolean {
fun isUserAllowedToSend(userId: String, isState: Boolean, eventType: String?): Boolean {
return if (userId.isNotEmpty()) {
val powerLevel = getUserPowerLevelValue(userId)
val minimumPowerLevel = powerLevelsContent.events[eventType]
@ -69,22 +68,42 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
} else false
}
fun canInvite(userId: String): Boolean {
/**
* Check if the user have the necessary power level to invite
* @param userId the id of the user to check for.
* @return true if able to invite
*/
fun isUserAbleToInvite(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
return powerLevel >= powerLevelsContent.invite
}
fun canBan(userId: String): Boolean {
/**
* Check if the user have the necessary power level to ban
* @param userId the id of the user to check for.
* @return true if able to ban
*/
fun isUserAbleToBan(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
return powerLevel >= powerLevelsContent.ban
}
fun canKick(userId: String): Boolean {
/**
* Check if the user have the necessary power level to kick
* @param userId the id of the user to check for.
* @return true if able to kick
*/
fun isUserAbleToKick(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
return powerLevel >= powerLevelsContent.kick
}
fun canRedact(userId: String): Boolean {
/**
* Check if the user have the necessary power level to redact
* @param userId the id of the user to check for.
* @return true if able to redact
*/
fun isUserAbleToRedact(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
return powerLevel >= powerLevelsContent.redact
}

View File

@ -34,10 +34,11 @@ sealed class Role(open val value: Int, @StringRes val res: Int) : Comparable<Rol
fun fromValue(value: Int, default: Int): Role {
return when (value) {
default, Default.value -> Default
Moderator.value -> Moderator
Admin.value -> Admin
else -> Custom(value)
default,
Default.value -> Default
Moderator.value -> Moderator
Admin.value -> Admin
else -> Custom(value)
}
}
}

View File

@ -251,7 +251,7 @@ internal interface RoomAPI {
* @param userIdAndReason the banned user object (userId and reason for ban)
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/ban")
fun ban(@Path("roomId") roomId: String?, @Body userIdAndReason: UserIdAndReason): Call<Unit>
fun ban(@Path("roomId") roomId: String, @Body userIdAndReason: UserIdAndReason): Call<Unit>
/**
* unban a user from the given room.
@ -260,7 +260,7 @@ internal interface RoomAPI {
* @param userIdAndReason the unbanned user object (userId and reason for unban)
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/unban")
fun unban(@Path("roomId") roomId: String?, @Body userIdAndReason: UserIdAndReason): Call<Unit>
fun unban(@Path("roomId") roomId: String, @Body userIdAndReason: UserIdAndReason): Call<Unit>
/**
* Kick a user from the given room.
@ -269,7 +269,7 @@ internal interface RoomAPI {
* @param userIdAndReason the kicked user object (userId and reason for kicking)
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/kick")
fun kick(@Path("roomId") roomId: String?, @Body userIdAndReason: UserIdAndReason): Call<Unit>
fun kick(@Path("roomId") roomId: String, @Body userIdAndReason: UserIdAndReason): Call<Unit>
/**
* Strips all information out of an event which isn't critical to the integrity of the server-side representation of the room.

View File

@ -198,6 +198,6 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
stateKey = QueryStringValue.NoCondition
)
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>() ?: return false
return PowerLevelsHelper(powerLevelsContent).isAllowedToSend(true, null, userId)
return PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(userId, true, null)
}
}

View File

@ -95,8 +95,11 @@
<string name="power_level_custom">Custom (%1$d)</string>
<string name="power_level_custom_no_value">Custom</string>
<!-- parameter will be a comma separated list of values of notice_power_level_diff -->
<string name="notice_power_level_changed_by_you">You changed the power level of %1$s.</string>
<!-- parameter will be a comma separated list of values of notice_power_level_diff -->
<string name="notice_power_level_changed">%1$s changed the power level of %2$s.</string>
<!-- First parameter will be a userId or display name, the two last ones will be value of power_level_* -->
<string name="notice_power_level_diff">%1$s from %2$s to %3$s</string>
<string name="notice_crypto_unable_to_decrypt">** Unable to decrypt: %s **</string>

View File

@ -18,11 +18,15 @@ package im.vector.riotx.core.ui.views
import android.content.Context
import android.graphics.Color
import android.text.method.LinkMovementMethod
import android.util.AttributeSet
import android.view.View
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.riotx.R
import im.vector.riotx.core.error.ResourceLimitErrorFormatter
import im.vector.riotx.core.utils.DimensionConverter
import im.vector.riotx.features.themes.ThemeUtils
import kotlinx.android.synthetic.main.view_notification_area.view.*
@ -61,10 +65,11 @@ class NotificationAreaView @JvmOverloads constructor(
cleanUp()
state = newState
when (newState) {
is State.Default -> renderDefault()
is State.Hidden -> renderHidden()
is State.NoPermissionToPost -> renderNoPermissionToPost()
is State.Tombstone -> renderTombstone(newState)
is State.Default -> renderDefault()
is State.Hidden -> renderHidden()
is State.NoPermissionToPost -> renderNoPermissionToPost()
is State.Tombstone -> renderTombstone(newState)
is State.ResourceLimitExceededError -> renderResourceLimitExceededError(newState)
}
}
@ -93,6 +98,26 @@ class NotificationAreaView @JvmOverloads constructor(
roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))
}
private fun renderResourceLimitExceededError(state: State.ResourceLimitExceededError) {
visibility = View.VISIBLE
val resourceLimitErrorFormatter = ResourceLimitErrorFormatter(context)
val formatterMode: ResourceLimitErrorFormatter.Mode
val backgroundColor: Int
if (state.isSoft) {
backgroundColor = R.color.soft_resource_limit_exceeded
formatterMode = ResourceLimitErrorFormatter.Mode.Soft
} else {
backgroundColor = R.color.hard_resource_limit_exceeded
formatterMode = ResourceLimitErrorFormatter.Mode.Hard
}
val message = resourceLimitErrorFormatter.format(state.matrixError, formatterMode, clickable = true)
roomNotificationMessage.setTextColor(Color.WHITE)
roomNotificationMessage.text = message
roomNotificationMessage.movementMethod = LinkMovementMethod.getInstance()
roomNotificationMessage.setLinkTextColor(Color.WHITE)
setBackgroundColor(ContextCompat.getColor(context, backgroundColor))
}
private fun renderTombstone(state: State.Tombstone) {
visibility = View.VISIBLE
roomNotificationIcon.setImageResource(R.drawable.error)
@ -129,13 +154,16 @@ class NotificationAreaView @JvmOverloads constructor(
object Default : State()
// User can't post messages to room because his power level doesn't allow it.
object NoPermissionToPost: State()
object NoPermissionToPost : State()
// View will be Gone
object Hidden : State()
// The room is dead
data class Tombstone(val tombstoneEvent: Event) : State()
// Resource limit exceeded error will be displayed (only hard for the moment)
data class ResourceLimitExceededError(val isSoft: Boolean, val matrixError: MatrixError) : State()
}
/**

View File

@ -72,8 +72,8 @@ import im.vector.riotx.features.crypto.verification.SupportedVerificationMethods
import im.vector.riotx.features.home.room.detail.composer.rainbow.RainbowGenerator
import im.vector.riotx.features.home.room.detail.sticker.StickerPickerActionHandler
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
import im.vector.riotx.features.powerlevel.PowerLevelsObservableFactory
import im.vector.riotx.features.home.room.typing.TypingHelper
import im.vector.riotx.features.powerlevel.PowerLevelsObservableFactory
import im.vector.riotx.features.settings.VectorPreferences
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
@ -176,11 +176,12 @@ class RoomDetailViewModel @AssistedInject constructor(
private fun observePowerLevel() {
PowerLevelsObservableFactory(room).createObservable()
.subscribe {
val canSendMessage = PowerLevelsHelper(it).isAllowedToSend(false, EventType.MESSAGE, session.myUserId)
val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
setState {
copy(canSendMessage = canSendMessage)
}
}.disposeOnClear()
}
.disposeOnClear()
}
private fun observeActiveRoomWidgets() {

View File

@ -109,9 +109,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
PowerLevelsObservableFactory(room).createObservable()
.subscribe {
val powerLevelsHelper = PowerLevelsHelper(it)
val canReact = powerLevelsHelper.isAllowedToSend(false, EventType.REACTION, session.myUserId)
val canRedact = powerLevelsHelper.canRedact(session.myUserId)
val canSendMessage = powerLevelsHelper.isAllowedToSend(false, EventType.MESSAGE, session.myUserId)
val canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION)
val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId)
val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact)
setState {
copy(actionPermissions = permissions)

View File

@ -22,7 +22,7 @@ import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomMemberProfileAction : VectorViewModelAction {
object RetryFetchingInfo : RoomMemberProfileAction()
object IgnoreUser : RoomMemberProfileAction()
data class BanUser(val reason: String?) : RoomMemberProfileAction()
data class BanOrUnbanUser(val reason: String?) : RoomMemberProfileAction()
data class KickUser(val reason: String?) : RoomMemberProfileAction()
object InviteUser : RoomMemberProfileAction()
object VerifyUser : RoomMemberProfileAction()

View File

@ -325,7 +325,7 @@ class RoomMemberProfileFragment @Inject constructor(
reasonHintRes = R.string.room_participants_ban_reason,
titleRes = titleRes
) { reason ->
viewModel.handle(RoomMemberProfileAction.BanUser(reason))
viewModel.handle(RoomMemberProfileAction.BanOrUnbanUser(reason))
}
}

View File

@ -144,7 +144,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
is RoomMemberProfileAction.VerifyUser -> prepareVerification()
is RoomMemberProfileAction.ShareRoomMemberProfile -> handleShareRoomMemberProfile()
is RoomMemberProfileAction.SetPowerLevel -> handleSetPowerLevel(action)
is RoomMemberProfileAction.BanUser -> handleBanAction(action)
is RoomMemberProfileAction.BanOrUnbanUser -> handleBanOrUnbanAction(action)
is RoomMemberProfileAction.KickUser -> handleKickAction(action)
RoomMemberProfileAction.InviteUser -> handleInviteAction()
}
@ -223,7 +223,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
}
}
private fun handleBanAction(action: RoomMemberProfileAction.BanUser) = withState { state ->
private fun handleBanOrUnbanAction(action: RoomMemberProfileAction.BanOrUnbanUser) = withState { state ->
if (room == null) {
return@withState
}
@ -286,10 +286,10 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
powerLevelsContentLive.subscribe {
val powerLevelsHelper = PowerLevelsHelper(it)
val permissions = ActionPermissions(
canKick = powerLevelsHelper.canKick(session.myUserId),
canBan = powerLevelsHelper.canBan(session.myUserId),
canInvite = powerLevelsHelper.canInvite(session.myUserId),
canEditPowerLevel = powerLevelsHelper.isAllowedToSend(true, EventType.STATE_ROOM_POWER_LEVELS, session.myUserId)
canKick = powerLevelsHelper.isUserAbleToKick(session.myUserId),
canBan = powerLevelsHelper.isUserAbleToBan(session.myUserId),
canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId),
canEditPowerLevel = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_POWER_LEVELS)
)
setState { copy(powerLevelsContent = it, actionPermissions = permissions) }
}.disposeOnClear()

View File

@ -73,7 +73,7 @@ object EditPowerLevelDialogs {
}
fun showValidation(activity: Activity, onValidate: () -> Unit) {
// ask to the user to confirmation thu upgrade.
// Ask to the user the confirmation to upgrade.
AlertDialog.Builder(activity)
.setMessage(R.string.room_participants_power_level_prompt)
.setPositiveButton(R.string.yes) { _, _ ->
@ -84,6 +84,7 @@ object EditPowerLevelDialogs {
}
fun showDemoteWarning(activity: Activity, onValidate: () -> Unit) {
// Ask to the user the confirmation to downgrade his own role.
AlertDialog.Builder(activity)
.setTitle(R.string.room_participants_power_level_demote_warning_title)
.setMessage(R.string.room_participants_power_level_demote_warning_prompt)

View File

@ -124,7 +124,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
PowerLevelsObservableFactory(room).createObservable()
.subscribe {
val permissions = ActionPermissions(
canInvite = PowerLevelsHelper(it).canInvite(session.myUserId)
canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId)
)
setState {
copy(actionsPermissions = permissions)

View File

@ -154,7 +154,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
val canSend = if (powerLevelsContent == null) {
false
} else {
PowerLevelsHelper(powerLevelsContent).isAllowedToSend(isState, eventType, session.myUserId)
PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(session.myUserId, isState, eventType)
}
if (canSend) {
Timber.d("## canSendEvent() returns true")

View File

@ -112,7 +112,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi
.mapOptional { it.content.toModel<PowerLevelsContent>() }
.unwrap()
.map {
PowerLevelsHelper(it).isAllowedToSend(true, null, session.myUserId)
PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, null)
}.subscribe {
setState { copy(canManageWidgets = it) }
}.disposeOnClear()