From 60b91d4d503719cbdda5eecad7670ddd007ec95b Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 10 Jun 2020 17:19:33 +0200 Subject: [PATCH] PowerLevel : clean after Benoit's review --- .../room/powerlevels/PowerLevelsHelper.kt | 37 +++++++++++++----- .../api/session/room/powerlevels/Role.kt | 9 +++-- .../android/internal/session/room/RoomAPI.kt | 6 +-- .../internal/session/widgets/WidgetManager.kt | 2 +- .../src/main/res/values/strings.xml | 3 ++ .../core/ui/views/NotificationAreaView.kt | 38 ++++++++++++++++--- .../home/room/detail/RoomDetailViewModel.kt | 7 ++-- .../action/MessageActionsViewModel.kt | 6 +-- .../RoomMemberProfileAction.kt | 2 +- .../RoomMemberProfileFragment.kt | 2 +- .../RoomMemberProfileViewModel.kt | 12 +++--- .../powerlevel/EditPowerLevelDialogs.kt | 3 +- .../members/RoomMemberListViewModel.kt | 2 +- .../features/widgets/WidgetPostAPIHandler.kt | 2 +- .../riotx/features/widgets/WidgetViewModel.kt | 2 +- 15 files changed, 93 insertions(+), 40 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt index 9b804b10ab..49141e3e86 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/PowerLevelsHelper.kt @@ -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 } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/Role.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/Role.kt index fb4f64e4f0..7f015b3d1e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/Role.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/powerlevels/Role.kt @@ -34,10 +34,11 @@ sealed class Role(open val value: Int, @StringRes val res: Int) : Comparable Default - Moderator.value -> Moderator - Admin.value -> Admin - else -> Custom(value) + default, + Default.value -> Default + Moderator.value -> Moderator + Admin.value -> Admin + else -> Custom(value) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index 50a2cf35c0..7500f0ded1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -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 + fun ban(@Path("roomId") roomId: String, @Body userIdAndReason: UserIdAndReason): Call /** * 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 + fun unban(@Path("roomId") roomId: String, @Body userIdAndReason: UserIdAndReason): Call /** * 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 + fun kick(@Path("roomId") roomId: String, @Body userIdAndReason: UserIdAndReason): Call /** * Strips all information out of an event which isn't critical to the integrity of the server-side representation of the room. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt index 3c9985de1b..8d13f53190 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/widgets/WidgetManager.kt @@ -198,6 +198,6 @@ internal class WidgetManager @Inject constructor(private val integrationManager: stateKey = QueryStringValue.NoCondition ) val powerLevelsContent = powerLevelsEvent?.content?.toModel() ?: return false - return PowerLevelsHelper(powerLevelsContent).isAllowedToSend(true, null, userId) + return PowerLevelsHelper(powerLevelsContent).isUserAllowedToSend(userId, true, null) } } diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index ef85ebbf30..12b9e726e4 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -95,8 +95,11 @@ Custom (%1$d) Custom + You changed the power level of %1$s. + %1$s changed the power level of %2$s. + %1$s from %2$s to %3$s ** Unable to decrypt: %s ** diff --git a/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt index f44f689f4c..946a5caf3f 100644 --- a/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/riotx/core/ui/views/NotificationAreaView.kt @@ -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() } /** diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 6ba91e71e2..69a6429efb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -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() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 060564f1e7..909169e7b0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt index 28c6932d3d..e60f268b22 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -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() diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index 1c9ff24b80..8864dbb328 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -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)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 3573e79352..8e0ffda1dc 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -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() diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt index 19e116a6de..5672c621c7 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt index 132413f3a9..f177d26725 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/members/RoomMemberListViewModel.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt index c47244c643..351a15aad1 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetPostAPIHandler.kt @@ -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") diff --git a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt index a503387f05..d81e1efea4 100644 --- a/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/widgets/WidgetViewModel.kt @@ -112,7 +112,7 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi .mapOptional { it.content.toModel() } .unwrap() .map { - PowerLevelsHelper(it).isAllowedToSend(true, null, session.myUserId) + PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, null) }.subscribe { setState { copy(canManageWidgets = it) } }.disposeOnClear()