From 22c10f5ada6d2014af0a7743d2b2bb49c3a6e9be Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 12:24:47 +0100 Subject: [PATCH 01/10] Prefer immutable type --- .../session/room/model/PowerLevelsContent.kt | 24 +++++++++++-------- .../home/room/detail/RoomDetailViewModel.kt | 10 ++++---- .../RoomMemberProfileViewModel.kt | 6 +++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index 696b612389..c466486ec0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -30,23 +30,27 @@ data class PowerLevelsContent( @Json(name = "invite") val invite: Int = Role.Moderator.value, @Json(name = "redact") val redact: Int = Role.Moderator.value, @Json(name = "events_default") val eventsDefault: Int = Role.Default.value, - @Json(name = "events") val events: MutableMap = HashMap(), + @Json(name = "events") val events: Map = emptyMap(), @Json(name = "users_default") val usersDefault: Int = Role.Default.value, - @Json(name = "users") val users: MutableMap = HashMap(), + @Json(name = "users") val users: Map = emptyMap(), @Json(name = "state_default") val stateDefault: Int = Role.Moderator.value, - @Json(name = "notifications") val notifications: Map = HashMap() + @Json(name = "notifications") val notifications: Map = emptyMap() ) { /** - * Alter this content with a new power level for the specified user + * Return a copy of this content with a new power level for the specified user * * @param userId the userId to alter the power level of * @param powerLevel the new power level, or null to set the default value. */ - fun setUserPowerLevel(userId: String, powerLevel: Int?) { - if (powerLevel == null || powerLevel == usersDefault) { - users.remove(userId) - } else { - users[userId] = powerLevel - } + fun setUserPowerLevel(userId: String, powerLevel: Int?): PowerLevelsContent { + return copy( + users = users.toMutableMap().apply { + if (powerLevel == null || powerLevel == usersDefault) { + remove(userId) + } else { + put(userId, powerLevel) + } + } + ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 1e6e7c9d14..975eda5d2f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -887,13 +887,15 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleSetUserPowerLevel(setUserPowerLevel: ParsedCommand.SetUserPowerLevel) { - val currentPowerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + val newPowerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) ?.content - ?.toModel() ?: return + ?.toModel() + ?.setUserPowerLevel(setUserPowerLevel.userId, setUserPowerLevel.powerLevel) + ?.toContent() + ?: return launchSlashCommandFlowSuspendable { - currentPowerLevelsContent.setUserPowerLevel(setUserPowerLevel.userId, setUserPowerLevel.powerLevel) - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, currentPowerLevelsContent.toContent()) + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 39b5884308..8211a05127 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -162,11 +162,13 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } else if (action.askForValidation && state.isMine) { _viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelDemoteWarning(action.previousValue, action.newValue)) } else { - currentPowerLevelsContent.setUserPowerLevel(state.userId, action.newValue) + val newPowerLevelsContent = currentPowerLevelsContent + .setUserPowerLevel(state.userId, action.newValue) + .toContent() viewModelScope.launch { _viewEvents.post(RoomMemberProfileViewEvents.Loading()) try { - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, currentPowerLevelsContent.toContent()) + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent) _viewEvents.post(RoomMemberProfileViewEvents.OnSetPowerLevelSuccess) } catch (failure: Throwable) { _viewEvents.post(RoomMemberProfileViewEvents.Failure(failure)) From c4a019f0d36344be339a6411c72b7a86a53d221d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 16:15:01 +0100 Subject: [PATCH 02/10] Edit room permissions (#2471) --- CHANGES.md | 1 + .../im/vector/app/core/di/FragmentModule.kt | 6 + .../RoomMemberProfileFragment.kt | 2 +- .../powerlevel/EditPowerLevelDialogs.kt | 8 +- .../roomprofile/RoomProfileActivity.kt | 19 ++- .../roomprofile/RoomProfileSharedAction.kt | 1 + .../permissions/EditablePermission.kt | 104 +++++++++++++ .../permissions/RoomPermissionsAction.kt | 23 +++ .../permissions/RoomPermissionsController.kt | 144 ++++++++++++++++++ .../permissions/RoomPermissionsFragment.kt | 102 +++++++++++++ .../permissions/RoomPermissionsViewEvents.kt | 28 ++++ .../permissions/RoomPermissionsViewModel.kt | 144 ++++++++++++++++++ .../permissions/RoomPermissionsViewState.kt | 40 +++++ .../settings/RoomSettingsController.kt | 11 ++ .../settings/RoomSettingsFragment.kt | 4 + vector/src/main/res/values/strings.xml | 26 ++++ 16 files changed, 654 insertions(+), 9 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/EditablePermission.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt diff --git a/CHANGES.md b/CHANGES.md index 7d4678fe40..ff457cb539 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ Changes in Element 1.0.14 (2020-XX-XX) Features ✨: - Enable url previews for notices (#2562) + - Edit room permissions (#2471) Improvements 🙌: - Add System theme option and set as default (#904) (#2387) diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 87ab875746..407aa2fc73 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -84,6 +84,7 @@ import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment import im.vector.app.features.roomprofile.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.alias.RoomAliasFragment +import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment @@ -364,6 +365,11 @@ interface FragmentModule { @FragmentKey(RoomAliasFragment::class) fun bindRoomAliasFragment(fragment: RoomAliasFragment): Fragment + @Binds + @IntoMap + @FragmentKey(RoomPermissionsFragment::class) + fun bindRoomPermissionsFragment(fragment: RoomPermissionsFragment): Fragment + @Binds @IntoMap @FragmentKey(RoomMemberProfileFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 08a251834e..688f74ba5d 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -324,7 +324,7 @@ class RoomMemberProfileFragment @Inject constructor( } override fun onEditPowerLevel(currentRole: Role) { - EditPowerLevelDialogs.showChoice(requireActivity(), currentRole) { newPowerLevel -> + EditPowerLevelDialogs.showChoice(requireActivity(), R.string.power_level_edit_title, currentRole) { newPowerLevel -> viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true)) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt index 4316a4bd0d..764271f7ce 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt @@ -19,6 +19,7 @@ package im.vector.app.features.roommemberprofile.powerlevel import android.app.Activity import android.content.DialogInterface import android.view.KeyEvent +import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import im.vector.app.R @@ -29,7 +30,10 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role object EditPowerLevelDialogs { - fun showChoice(activity: Activity, currentRole: Role, listener: (Int) -> Unit) { + fun showChoice(activity: Activity, + @StringRes titleRes: Int, + currentRole: Role, + listener: (Int) -> Unit) { val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null) val views = DialogEditPowerLevelBinding.bind(dialogLayout) views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId -> @@ -45,7 +49,7 @@ object EditPowerLevelDialogs { } AlertDialog.Builder(activity) - .setTitle(R.string.power_level_edit_title) + .setTitle(titleRes) .setView(dialogLayout) .setPositiveButton(R.string.edit) { _, _ -> val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index 76649d53b3..7cb713d378 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -27,6 +27,7 @@ import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding @@ -38,6 +39,7 @@ import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment import im.vector.app.features.roomprofile.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.alias.RoomAliasFragment +import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import javax.inject.Inject @@ -102,12 +104,13 @@ class RoomProfileActivity : .observe() .subscribe { sharedAction -> when (sharedAction) { - is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() - is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() - is RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias() - is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() - is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() - } + RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() + RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() + RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias() + RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions() + RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() + RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() + }.exhaustive } .disposeOnDestroy() @@ -144,6 +147,10 @@ class RoomProfileActivity : addFragmentToBackstack(R.id.simpleFragmentContainer, RoomAliasFragment::class.java, roomProfileArgs) } + private fun openRoomPermissions() { + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomPermissionsFragment::class.java, roomProfileArgs) + } + private fun openRoomMembers() { addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt index 83a610cf1b..2a5775d1af 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt @@ -24,6 +24,7 @@ import im.vector.app.core.platform.VectorSharedAction sealed class RoomProfileSharedAction : VectorSharedAction { object OpenRoomSettings : RoomProfileSharedAction() object OpenRoomAliasesSettings : RoomProfileSharedAction() + object OpenRoomPermissionsSettings : RoomProfileSharedAction() object OpenRoomUploads : RoomProfileSharedAction() object OpenRoomMembers : RoomProfileSharedAction() object OpenBannedRoomMembers : RoomProfileSharedAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/EditablePermission.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/EditablePermission.kt new file mode 100644 index 0000000000..bb1054b704 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/EditablePermission.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.permissions + +import androidx.annotation.StringRes +import im.vector.app.R +import org.matrix.android.sdk.api.session.events.model.EventType + +/** + * Change on each permission has an effect on the power level event. Try to sort the effect by category. + */ +sealed class EditablePermission(@StringRes val labelResId: Int) { + // Updates `content.events.[eventType]` + open class EventTypeEditablePermission(val eventType: String, @StringRes labelResId: Int) : EditablePermission(labelResId) + + class ModifyWidgets : EventTypeEditablePermission( + // Note: Element Web still use legacy value + EventType.STATE_ROOM_WIDGET_LEGACY, + R.string.room_permissions_modify_widgets + ) + + class ChangeRoomAvatar : EventTypeEditablePermission( + EventType.STATE_ROOM_AVATAR, + R.string.room_permissions_change_room_avatar + ) + + class ChangeMainAddressForTheRoom : EventTypeEditablePermission( + EventType.STATE_ROOM_CANONICAL_ALIAS, + R.string.room_permissions_change_main_address_for_the_room + ) + + class EnableRoomEncryption : EventTypeEditablePermission( + EventType.STATE_ROOM_ENCRYPTION, + R.string.room_permissions_enable_room_encryption + ) + + class ChangeHistoryVisibility : EventTypeEditablePermission( + EventType.STATE_ROOM_HISTORY_VISIBILITY, + R.string.room_permissions_change_history_visibility + ) + + class ChangeRoomName : EventTypeEditablePermission( + EventType.STATE_ROOM_NAME, + R.string.room_permissions_change_room_name + ) + + class ChangePermissions : EventTypeEditablePermission( + EventType.STATE_ROOM_POWER_LEVELS, + R.string.room_permissions_change_permissions + ) + + class SendRoomServerAclEvents : EventTypeEditablePermission( + EventType.STATE_ROOM_SERVER_ACL, + R.string.room_permissions_send_m_room_server_acl_events + ) + + class UpgradeTheRoom : EventTypeEditablePermission( + EventType.STATE_ROOM_TOMBSTONE, + R.string.room_permissions_upgrade_the_room + ) + + class ChangeTopic : EventTypeEditablePermission( + EventType.STATE_ROOM_TOPIC, + R.string.room_permissions_change_topic + ) + + // Updates `content.users_default` + class DefaultRole : EditablePermission(R.string.room_permissions_default_role) + + // Updates `content.events_default` + class SendMessages : EditablePermission(R.string.room_permissions_send_messages) + + // Updates `content.invites` + class InviteUsers : EditablePermission(R.string.room_permissions_invite_users) + + // Updates `content.state_default` + class ChangeSettings : EditablePermission(R.string.room_permissions_change_settings) + + // Updates `content.kick` + class KickUsers : EditablePermission(R.string.room_permissions_kick_users) + + // Updates `content.ban` + class BanUsers : EditablePermission(R.string.room_permissions_ban_users) + + // Updates `content.redact` + class RemoveMessagesSentByOthers : EditablePermission(R.string.room_permissions_remove_messages_sent_by_others) + + // Updates `content.notification.room` + class NotifyEveryone : EditablePermission(R.string.room_permissions_notify_everyone) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt new file mode 100644 index 0000000000..32d9236fb6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.permissions + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class RoomPermissionsAction : VectorViewModelAction { + data class UpdatePermission(val editablePermission: EditablePermission, val powerLevel: Int) : RoomPermissionsAction() +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt new file mode 100644 index 0000000000..058f5a9ce7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.permissions + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Success +import im.vector.app.R +import im.vector.app.core.epoxy.loadingItem +import im.vector.app.core.epoxy.profiles.buildProfileAction +import im.vector.app.core.epoxy.profiles.buildProfileSection +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.discovery.settingsInfoItem +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.powerlevels.Role +import javax.inject.Inject + +class RoomPermissionsController @Inject constructor( + private val stringProvider: StringProvider, + colorProvider: ColorProvider +) : TypedEpoxyController() { + + interface Callback { + fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) + } + + var callback: Callback? = null + + private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) + + // Order is the order applied in the UI + private val allEditablePermissions = listOf( + EditablePermission.DefaultRole(), + EditablePermission.SendMessages(), + EditablePermission.InviteUsers(), + EditablePermission.ChangeSettings(), + EditablePermission.KickUsers(), + EditablePermission.BanUsers(), + EditablePermission.RemoveMessagesSentByOthers(), + EditablePermission.NotifyEveryone(), + EditablePermission.ModifyWidgets(), + EditablePermission.ChangeRoomAvatar(), + EditablePermission.ChangeMainAddressForTheRoom(), + EditablePermission.EnableRoomEncryption(), + EditablePermission.ChangeHistoryVisibility(), + EditablePermission.ChangeRoomName(), + EditablePermission.ChangePermissions(), + EditablePermission.SendRoomServerAclEvents(), + EditablePermission.UpgradeTheRoom(), + EditablePermission.ChangeTopic() + ) + + init { + setData(null) + } + + override fun buildModels(data: RoomPermissionsViewState?) { + buildProfileSection( + stringProvider.getString(R.string.room_permissions_title) + ) + + settingsInfoItem { + id("notice") + helperText(stringProvider.getString(R.string.room_permissions_notice)) + } + + when (val content = data?.currentPowerLevelsContent) { + is Success -> buildPermissions(data, content()) + else -> { + loadingItem { + id("loading") + loadingText(stringProvider.getString(R.string.loading)) + } + } + } + } + + private fun buildPermissions(data: RoomPermissionsViewState, content: PowerLevelsContent) { + allEditablePermissions.forEach { editablePermission -> + val currentRole = getCurrentRole(editablePermission, content) + buildProfileAction( + id = editablePermission.labelResId.toString(), + title = stringProvider.getString(editablePermission.labelResId), + subtitle = getSubtitle(currentRole), + dividerColor = dividerColor, + divider = true, + editable = data.actionPermissions.canChangePowerLevels, + action = { callback?.onEditPermission(editablePermission, currentRole) } + ) + } + } + + private fun getSubtitle(currentRole: Role): String { + return when (currentRole) { + Role.Admin, + Role.Moderator, + Role.Default -> stringProvider.getString(currentRole.res) + is Role.Custom -> stringProvider.getString(currentRole.res, currentRole.value) + } + } + + private fun getCurrentRole(editablePermission: EditablePermission, content: PowerLevelsContent): Role { + val value = when (editablePermission) { + is EditablePermission.EventTypeEditablePermission -> content.events[editablePermission.eventType] ?: content.stateDefault + is EditablePermission.DefaultRole -> content.usersDefault + is EditablePermission.SendMessages -> content.eventsDefault + is EditablePermission.InviteUsers -> content.invite + is EditablePermission.ChangeSettings -> content.stateDefault + is EditablePermission.KickUsers -> content.kick + is EditablePermission.BanUsers -> content.ban + is EditablePermission.RemoveMessagesSentByOthers -> content.redact + is EditablePermission.NotifyEveryone -> (content.notifications["room"] as? Int) ?: Role.Moderator.value + } + + return Role.fromValue( + value, + when (editablePermission) { + is EditablePermission.EventTypeEditablePermission -> content.stateDefault + 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 + } + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt new file mode 100644 index 0000000000..73e0b00de9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.permissions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.toast +import im.vector.app.databinding.FragmentRoomSettingGenericBinding +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs +import im.vector.app.features.roomprofile.RoomProfileArgs +import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +class RoomPermissionsFragment @Inject constructor( + val viewModelFactory: RoomPermissionsViewModel.Factory, + private val controller: RoomPermissionsController, + private val avatarRenderer: AvatarRenderer +) : + VectorBaseFragment(), + RoomPermissionsController.Callback { + + private val viewModel: RoomPermissionsViewModel by fragmentViewModel() + + private val roomProfileArgs: RoomProfileArgs by args() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomSettingGenericBinding { + return FragmentRoomSettingGenericBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + controller.callback = this + setupToolbar(views.roomSettingsToolbar) + views.roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) + views.waitingView.waitingStatusText.setText(R.string.please_wait) + views.waitingView.waitingStatusText.isVisible = true + + viewModel.observeViewEvents { + when (it) { + is RoomPermissionsViewEvents.Failure -> showFailure(it.throwable) + RoomPermissionsViewEvents.Success -> showSuccess() + }.exhaustive + } + } + + private fun showSuccess() { + activity?.toast(R.string.room_settings_save_success) + } + + override fun onDestroyView() { + controller.callback = null + views.roomSettingsRecyclerView.cleanup() + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { state -> + views.waitingView.root.isVisible = state.isLoading + controller.setData(state) + renderRoomSummary(state) + } + + private fun renderRoomSummary(state: RoomPermissionsViewState) { + state.roomSummary()?.let { + views.roomSettingsToolbarTitleView.text = it.displayName + avatarRenderer.render(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView) + } + } + + override fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) { + EditPowerLevelDialogs.showChoice(requireActivity(), editablePermission.labelResId, currentRole) { newPowerLevel -> + viewModel.handle(RoomPermissionsAction.UpdatePermission(editablePermission, newPowerLevel)) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewEvents.kt new file mode 100644 index 0000000000..8994398cf3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewEvents.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.app.features.roomprofile.permissions + +import im.vector.app.core.platform.VectorViewEvents + +/** + * Transient events for room settings screen + */ +sealed class RoomPermissionsViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : RoomPermissionsViewEvents() + object Success : RoomPermissionsViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt new file mode 100644 index 0000000000..95217c1885 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.permissions + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import kotlinx.coroutines.launch +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.toContent +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.rx.unwrap + +class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialState: RoomPermissionsViewState, + private val session: Session) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomPermissionsViewState): RoomPermissionsViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomPermissionsViewState): RoomPermissionsViewModel? { + val fragment: RoomPermissionsFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + private val room = session.getRoom(initialState.roomId)!! + + init { + observeRoomSummary() + observePowerLevel() + } + + private fun observeRoomSummary() { + room.rx().liveRoomSummary() + .unwrap() + .execute { async -> + copy( + roomSummary = async + ) + } + } + + private fun observePowerLevel() { + PowerLevelsObservableFactory(room) + .createObservable() + .subscribe { powerLevelContent -> + val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) + val permissions = RoomPermissionsViewState.ActionPermissions( + canChangePowerLevels = powerLevelsHelper.isUserAllowedToSend( + userId = session.myUserId, + isState = true, + eventType = EventType.STATE_ROOM_POWER_LEVELS + ) + ) + setState { + copy( + actionPermissions = permissions, + currentPowerLevelsContent = Success(powerLevelContent) + ) + } + } + .disposeOnClear() + } + + override fun handle(action: RoomPermissionsAction) { + when (action) { + is RoomPermissionsAction.UpdatePermission -> updatePermission(action) + }.exhaustive + } + + private fun updatePermission(action: RoomPermissionsAction.UpdatePermission) { + withState { state -> + val currentPowerLevel = state.currentPowerLevelsContent.invoke() ?: return@withState + postLoading(true) + viewModelScope.launch { + try { + val newPowerLevelsContent = when(action.editablePermission) { + is EditablePermission.EventTypeEditablePermission -> currentPowerLevel.copy( + events = currentPowerLevel.events.toMutableMap().apply { + put(action.editablePermission.eventType, action.powerLevel) + } + ) + is EditablePermission.DefaultRole -> currentPowerLevel.copy(usersDefault = action.powerLevel) + is EditablePermission.SendMessages -> currentPowerLevel.copy(eventsDefault = action.powerLevel) + is EditablePermission.InviteUsers -> currentPowerLevel.copy(invite = action.powerLevel) + is EditablePermission.ChangeSettings -> currentPowerLevel.copy(stateDefault = action.powerLevel) + is EditablePermission.KickUsers -> currentPowerLevel.copy(kick = action.powerLevel) + is EditablePermission.BanUsers -> currentPowerLevel.copy(ban = action.powerLevel) + is EditablePermission.RemoveMessagesSentByOthers -> currentPowerLevel.copy(redact = action.powerLevel) + is EditablePermission.NotifyEveryone -> currentPowerLevel.copy( + notifications = currentPowerLevel.notifications.toMutableMap().apply { + put("room", action.powerLevel) + } + ) + } + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent.toContent()) + setState { + copy( + isLoading = false + ) + } + } catch (failure: Throwable) { + postLoading(false) + _viewEvents.post(RoomPermissionsViewEvents.Failure(failure)) + } + } + } + } + + private fun postLoading(isLoading: Boolean) { + setState { + copy(isLoading = isLoading) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt new file mode 100644 index 0000000000..556c44b730 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.permissions + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomprofile.RoomProfileArgs +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +data class RoomPermissionsViewState( + val roomId: String, + val roomSummary: Async = Uninitialized, + val actionPermissions: ActionPermissions = ActionPermissions(), + val currentPowerLevelsContent: Async = Uninitialized, + val newPowerLevelsContent: PowerLevelsContent? = null, + val isLoading: Boolean = false +) : MvRxState { + + constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + + data class ActionPermissions( + val canChangePowerLevels: Boolean = false + ) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index bf3c1f87f8..1984be078d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -46,6 +46,7 @@ class RoomSettingsController @Inject constructor( fun onTopicChanged(topic: String) fun onHistoryVisibilityClicked() fun onRoomAliasesClicked() + fun onRoomPermissionsClicked() fun onJoinRuleClicked() } @@ -115,6 +116,16 @@ class RoomSettingsController @Inject constructor( action = { callback?.onRoomAliasesClicked() } ) + buildProfileAction( + id = "permissions", + title = stringProvider.getString(R.string.room_settings_permissions_title), + subtitle = stringProvider.getString(R.string.room_settings_permissions_subtitle), + dividerColor = dividerColor, + divider = true, + editable = true, + action = { callback?.onRoomPermissionsClicked() } + ) + buildProfileAction( id = "historyReadability", title = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_title), diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 52e273f3d4..1ca539ea7e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -178,6 +178,10 @@ class RoomSettingsFragment @Inject constructor( roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAliasesSettings) } + override fun onRoomPermissionsClicked() { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomPermissionsSettings) + } + override fun onJoinRuleClicked() = withState(viewModel) { state -> val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules val currentGuestAccess = state.newRoomJoinRules.newGuestAccess ?: state.currentGuestAccess diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 355ac4d6d6..467270b58e 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -609,6 +609,32 @@ The certificate has changed from a previously trusted one to one that is not trusted. The server may have renewed its certificate. Contact the server administrator for the expected fingerprint. Only accept the certificate if the server administrator has published a fingerprint that matches the one above. + + Room permissions + View and update the roles required to change various parts of the room. + + "Permissions" + "Select the roles required to change various parts of the room" + + Default role + Send messages + Invite users + Change settings + Kick users + Ban users + Remove messages sent by others + Notify everyone + Modify widgets + Change room avatar + Change main address for the room + Enable room encryption + Change history visibility + Change room name + Change permissions + Send m.room.server_acl events + Upgrade the room + Change topic + Room Details People From 0702eee179489a592c20fa227508c6c0e8fb2ad8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 18:38:06 +0100 Subject: [PATCH 03/10] Improve code --- .../SenderNotificationPermissionCondition.kt | 2 +- .../session/room/model/PowerLevelsContent.kt | 19 +++++++++++++++++++ .../room/powerlevels/PowerLevelsHelper.kt | 15 --------------- .../permissions/RoomPermissionsController.kt | 2 +- .../permissions/RoomPermissionsViewModel.kt | 3 ++- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/SenderNotificationPermissionCondition.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/SenderNotificationPermissionCondition.kt index 4f9c84a47c..6675fb0ff5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/SenderNotificationPermissionCondition.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/pushrules/SenderNotificationPermissionCondition.kt @@ -37,6 +37,6 @@ class SenderNotificationPermissionCondition( fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean { val powerLevelsHelper = PowerLevelsHelper(powerLevels) - return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevelsHelper.notificationLevel(key) + return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevels.notificationLevel(key) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index c466486ec0..317948388d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -53,4 +53,23 @@ data class PowerLevelsContent( } ) } + + /** + * Get the notification level for a dedicated key. + * + * @param key the notification key + * @return the level, default to Moderator if the key is not found + */ + fun notificationLevel(key: String): Int { + return when (val value = notifications[key]) { + // the first implementation was a string value + is String -> value.toInt() + is Int -> value + else -> Role.Moderator.value + } + } + + companion object { + const val NOTIFICATIONS_ROOM_KEY = "room" + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt index 47922f6968..4f1253c6df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt @@ -108,19 +108,4 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) { val powerLevel = getUserPowerLevelValue(userId) return powerLevel >= powerLevelsContent.redact } - - /** - * Get the notification level for a dedicated key. - * - * @param key the notification key - * @return the level - */ - fun notificationLevel(key: String): Int { - return when (val value = powerLevelsContent.notifications[key]) { - // the first implementation was a string value - is String -> value.toInt() - is Int -> value - else -> Role.Moderator.value - } - } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt index 058f5a9ce7..399ada62f6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt @@ -123,7 +123,7 @@ class RoomPermissionsController @Inject constructor( is EditablePermission.KickUsers -> content.kick is EditablePermission.BanUsers -> content.ban is EditablePermission.RemoveMessagesSentByOthers -> content.redact - is EditablePermission.NotifyEveryone -> (content.notifications["room"] as? Int) ?: Role.Moderator.value + is EditablePermission.NotifyEveryone -> content.notificationLevel(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY) } return Role.fromValue( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 95217c1885..af0a392d88 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.launch 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.toContent +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.rx.rx import org.matrix.android.sdk.rx.unwrap @@ -118,7 +119,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat is EditablePermission.RemoveMessagesSentByOthers -> currentPowerLevel.copy(redact = action.powerLevel) is EditablePermission.NotifyEveryone -> currentPowerLevel.copy( notifications = currentPowerLevel.notifications.toMutableMap().apply { - put("room", action.powerLevel) + put(PowerLevelsContent.NOTIFICATIONS_ROOM_KEY, action.powerLevel) } ) } From f253aa6b3721355f8b1293edccc95aabef2ea4f9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 18:45:24 +0100 Subject: [PATCH 04/10] Remove unused field --- .../features/roomprofile/permissions/RoomPermissionsViewState.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt index 556c44b730..47c82bde2a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt @@ -28,7 +28,6 @@ data class RoomPermissionsViewState( val roomSummary: Async = Uninitialized, val actionPermissions: ActionPermissions = ActionPermissions(), val currentPowerLevelsContent: Async = Uninitialized, - val newPowerLevelsContent: PowerLevelsContent? = null, val isLoading: Boolean = false ) : MvRxState { From 81bdf506bcdcee1a12120e5b6dd866652accb7d6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 22:45:40 +0100 Subject: [PATCH 05/10] Fix Int type issue when sending PowerLevelsContent to the server --- .../session/room/model/PowerLevelsContent.kt | 1 + .../session/room/state/DefaultStateService.kt | 14 +++-- .../room/state/SafePowerLevelContent.kt | 60 +++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index 317948388d..e18cb7f05f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -64,6 +64,7 @@ data class PowerLevelsContent( return when (val value = notifications[key]) { // the first implementation was a string value is String -> value.toInt() + is Double -> value.toInt() is Int -> value else -> Role.Moderator.value } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index b546584450..804968bac0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -35,13 +35,11 @@ import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.MimeTypes import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.session.content.FileUploader -import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask internal class DefaultStateService @AssistedInject constructor(@Assisted private val roomId: String, private val stateEventDataSource: StateEventDataSource, private val sendStateTask: SendStateTask, - private val fileUploader: FileUploader, - private val addRoomAliasTask: AddRoomAliasTask + private val fileUploader: FileUploader ) : StateService { @AssistedInject.Factory @@ -74,11 +72,19 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private roomId = roomId, stateKey = stateKey, eventType = eventType, - body = body + body = body.toSafeJson(eventType) ) sendStateTask.execute(params) } + private fun JsonDict.toSafeJson(eventType: String): JsonDict { + // Safe treatment for PowerLevelContent + return when (eventType) { + EventType.STATE_ROOM_POWER_LEVELS -> toSafePowerLevelsContentDict() + else -> this + } + } + override suspend fun updateTopic(topic: String) { sendStateEvent( eventType = EventType.STATE_ROOM_TOPIC, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt new file mode 100644 index 0000000000..9f9a63de84 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.state + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.util.JsonDict + +@JsonClass(generateAdapter = true) +internal data class SerializablePowerLevelsContent( + @Json(name = "ban") val ban: Int = Role.Moderator.value, + @Json(name = "kick") val kick: Int = Role.Moderator.value, + @Json(name = "invite") val invite: Int = Role.Moderator.value, + @Json(name = "redact") val redact: Int = Role.Moderator.value, + @Json(name = "events_default") val eventsDefault: Int = Role.Default.value, + @Json(name = "events") val events: Map = emptyMap(), + @Json(name = "users_default") val usersDefault: Int = Role.Default.value, + @Json(name = "users") val users: Map = emptyMap(), + @Json(name = "state_default") val stateDefault: Int = Role.Moderator.value, + // `Int` is the diff here (instead of `Any`) + @Json(name = "notifications") val notifications: Map = emptyMap() +) + +internal fun JsonDict.toSafePowerLevelsContentDict(): JsonDict { + return toModel() + ?.let { content -> + SerializablePowerLevelsContent( + ban = content.ban, + kick = content.kick, + invite = content.invite, + redact = content.redact, + eventsDefault = content.eventsDefault, + events = content.events, + usersDefault = content.usersDefault, + users = content.users, + stateDefault = content.stateDefault, + notifications = content.notifications.mapValues { content.notificationLevel(it.key) } + ) + } + ?.toContent() + ?: emptyMap() +} From a28dfdc48ea66f21d6e731a22c72d3172e4d1e35 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 23:03:09 +0100 Subject: [PATCH 06/10] Improve UI and UX when permission are not editable --- .../permissions/RoomPermissionsController.kt | 17 +++++++++++------ vector/src/main/res/values/strings.xml | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt index 399ada62f6..313782bf28 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt @@ -73,11 +73,6 @@ class RoomPermissionsController @Inject constructor( stringProvider.getString(R.string.room_permissions_title) ) - settingsInfoItem { - id("notice") - helperText(stringProvider.getString(R.string.room_permissions_notice)) - } - when (val content = data?.currentPowerLevelsContent) { is Success -> buildPermissions(data, content()) else -> { @@ -90,6 +85,12 @@ class RoomPermissionsController @Inject constructor( } private fun buildPermissions(data: RoomPermissionsViewState, content: PowerLevelsContent) { + val editable = data.actionPermissions.canChangePowerLevels + settingsInfoItem { + id("notice") + helperText(stringProvider.getString(if (editable) R.string.room_permissions_notice else R.string.room_permissions_notice_read_only)) + } + allEditablePermissions.forEach { editablePermission -> val currentRole = getCurrentRole(editablePermission, content) buildProfileAction( @@ -99,7 +100,11 @@ class RoomPermissionsController @Inject constructor( dividerColor = dividerColor, divider = true, editable = data.actionPermissions.canChangePowerLevels, - action = { callback?.onEditPermission(editablePermission, currentRole) } + action = { + callback + ?.takeIf { editable } + ?.onEditPermission(editablePermission, currentRole) + } ) } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 467270b58e..b352d67490 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -615,6 +615,7 @@ "Permissions" "Select the roles required to change various parts of the room" + "You don't have permission to update the roles required to change various parts of the room" Default role Send messages From 037e53f385d7c66ef85f52181f26baf5c2290c5c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Jan 2021 23:24:25 +0100 Subject: [PATCH 07/10] Split permissions between useful ones and the others --- .../permissions/RoomPermissionsAction.kt | 2 + .../permissions/RoomPermissionsController.kt | 75 +++++++++++++------ .../permissions/RoomPermissionsFragment.kt | 4 + .../permissions/RoomPermissionsViewModel.kt | 9 ++- .../permissions/RoomPermissionsViewState.kt | 1 + 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt index 32d9236fb6..b853dda160 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsAction.kt @@ -19,5 +19,7 @@ package im.vector.app.features.roomprofile.permissions import im.vector.app.core.platform.VectorViewModelAction sealed class RoomPermissionsAction : VectorViewModelAction { + object ToggleShowAllPermissions : RoomPermissionsAction() + data class UpdatePermission(val editablePermission: EditablePermission, val powerLevel: Int) : RoomPermissionsAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt index 313782bf28..92bdac7ae0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsController.kt @@ -25,6 +25,7 @@ import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.settingsInfoItem +import im.vector.app.features.form.formAdvancedToggleItem import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.Role import javax.inject.Inject @@ -36,6 +37,7 @@ class RoomPermissionsController @Inject constructor( interface Callback { fun onEditPermission(editablePermission: EditablePermission, currentRole: Role) + fun toggleShowAllPermissions() } var callback: Callback? = null @@ -43,25 +45,34 @@ class RoomPermissionsController @Inject constructor( private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) // Order is the order applied in the UI - private val allEditablePermissions = listOf( + // Element Web order is not really nice, try to put the settings which are more likely to be updated first + // And a second section, hidden by default + private val usefulEditablePermissions = listOf( + EditablePermission.ChangeRoomAvatar(), + EditablePermission.ChangeRoomName(), + EditablePermission.ChangeTopic() + ) + + private val advancedEditablePermissions = listOf( + EditablePermission.ChangeMainAddressForTheRoom(), + EditablePermission.DefaultRole(), - EditablePermission.SendMessages(), EditablePermission.InviteUsers(), - EditablePermission.ChangeSettings(), EditablePermission.KickUsers(), EditablePermission.BanUsers(), + + EditablePermission.SendMessages(), + EditablePermission.RemoveMessagesSentByOthers(), EditablePermission.NotifyEveryone(), + + EditablePermission.ChangeSettings(), EditablePermission.ModifyWidgets(), - EditablePermission.ChangeRoomAvatar(), - EditablePermission.ChangeMainAddressForTheRoom(), - EditablePermission.EnableRoomEncryption(), EditablePermission.ChangeHistoryVisibility(), - EditablePermission.ChangeRoomName(), EditablePermission.ChangePermissions(), EditablePermission.SendRoomServerAclEvents(), - EditablePermission.UpgradeTheRoom(), - EditablePermission.ChangeTopic() + EditablePermission.EnableRoomEncryption(), + EditablePermission.UpgradeTheRoom() ) init { @@ -91,22 +102,38 @@ class RoomPermissionsController @Inject constructor( helperText(stringProvider.getString(if (editable) R.string.room_permissions_notice else R.string.room_permissions_notice_read_only)) } - allEditablePermissions.forEach { editablePermission -> - val currentRole = getCurrentRole(editablePermission, content) - buildProfileAction( - id = editablePermission.labelResId.toString(), - title = stringProvider.getString(editablePermission.labelResId), - subtitle = getSubtitle(currentRole), - dividerColor = dividerColor, - divider = true, - editable = data.actionPermissions.canChangePowerLevels, - action = { - callback - ?.takeIf { editable } - ?.onEditPermission(editablePermission, currentRole) - } - ) + // Useful permissions + usefulEditablePermissions.forEach { buildPermission(it, content, editable) } + + // Toggle + formAdvancedToggleItem { + id("showAdvanced") + title(stringProvider.getString(if (data.showAdvancedPermissions) R.string.hide_advanced else R.string.show_advanced)) + expanded(!data.showAdvancedPermissions) + listener { callback?.toggleShowAllPermissions() } } + + // Advanced permissions + if (data.showAdvancedPermissions) { + advancedEditablePermissions.forEach { buildPermission(it, content, editable) } + } + } + + private fun buildPermission(editablePermission: EditablePermission, content: PowerLevelsContent, editable: Boolean) { + val currentRole = getCurrentRole(editablePermission, content) + buildProfileAction( + id = editablePermission.labelResId.toString(), + title = stringProvider.getString(editablePermission.labelResId), + subtitle = getSubtitle(currentRole), + dividerColor = dividerColor, + divider = true, + editable = editable, + action = { + callback + ?.takeIf { editable } + ?.onEditPermission(editablePermission, currentRole) + } + ) } private fun getSubtitle(currentRole: Role): String { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt index 73e0b00de9..61635c9b31 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt @@ -99,4 +99,8 @@ class RoomPermissionsFragment @Inject constructor( viewModel.handle(RoomPermissionsAction.UpdatePermission(editablePermission, newPowerLevel)) } } + + override fun toggleShowAllPermissions() { + viewModel.handle(RoomPermissionsAction.ToggleShowAllPermissions) + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index af0a392d88..12f2467247 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -94,10 +94,17 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat override fun handle(action: RoomPermissionsAction) { when (action) { - is RoomPermissionsAction.UpdatePermission -> updatePermission(action) + is RoomPermissionsAction.UpdatePermission -> updatePermission(action) + RoomPermissionsAction.ToggleShowAllPermissions -> toggleShowAllPermissions() }.exhaustive } + private fun toggleShowAllPermissions() { + setState { + copy(showAdvancedPermissions = !showAdvancedPermissions) + } + } + private fun updatePermission(action: RoomPermissionsAction.UpdatePermission) { withState { state -> val currentPowerLevel = state.currentPowerLevelsContent.invoke() ?: return@withState diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt index 47c82bde2a..ce38ab87e5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewState.kt @@ -27,6 +27,7 @@ data class RoomPermissionsViewState( val roomId: String, val roomSummary: Async = Uninitialized, val actionPermissions: ActionPermissions = ActionPermissions(), + val showAdvancedPermissions: Boolean = false, val currentPowerLevelsContent: Async = Uninitialized, val isLoading: Boolean = false ) : MvRxState { From ae55ee82a7b6a634749466244073c345cac8decd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Jan 2021 00:07:40 +0100 Subject: [PATCH 08/10] Cleanup --- .../sdk/internal/session/room/state/SafePowerLevelContent.kt | 2 +- .../roomprofile/permissions/RoomPermissionsViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt index 9f9a63de84..a97709e38b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/SafePowerLevelContent.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index 12f2467247..183488a5e0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -111,7 +111,7 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat postLoading(true) viewModelScope.launch { try { - val newPowerLevelsContent = when(action.editablePermission) { + val newPowerLevelsContent = when (action.editablePermission) { is EditablePermission.EventTypeEditablePermission -> currentPowerLevel.copy( events = currentPowerLevel.events.toMutableMap().apply { put(action.editablePermission.eventType, action.powerLevel) From a6f909b942538f2549da847492b2e9b5818133ab Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Jan 2021 11:04:11 +0100 Subject: [PATCH 09/10] Add Javadoc --- .../session/room/model/PowerLevelsContent.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt index e18cb7f05f..e778f5740d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/PowerLevelsContent.kt @@ -25,15 +25,45 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role */ @JsonClass(generateAdapter = true) data class PowerLevelsContent( + /** + * The level required to ban a user. Defaults to 50 if unspecified. + */ @Json(name = "ban") val ban: Int = Role.Moderator.value, + /** + * The level required to kick a user. Defaults to 50 if unspecified. + */ @Json(name = "kick") val kick: Int = Role.Moderator.value, + /** + * The level required to invite a user. Defaults to 50 if unspecified. + */ @Json(name = "invite") val invite: Int = Role.Moderator.value, + /** + * The level required to redact an event. Defaults to 50 if unspecified. + */ @Json(name = "redact") val redact: Int = Role.Moderator.value, + /** + * The default level required to send message events. Can be overridden by the events key. Defaults to 0 if unspecified. + */ @Json(name = "events_default") val eventsDefault: Int = Role.Default.value, + /** + * The level required to send specific event types. This is a mapping from event type to power level required. + */ @Json(name = "events") val events: Map = emptyMap(), + /** + * The default power level for every user in the room, unless their user_id is mentioned in the users key. Defaults to 0 if unspecified. + */ @Json(name = "users_default") val usersDefault: Int = Role.Default.value, + /** + * The power levels for specific users. This is a mapping from user_id to power level for that user. + */ @Json(name = "users") val users: Map = emptyMap(), + /** + * The default level required to send state events. Can be overridden by the events key. Defaults to 50 if unspecified. + */ @Json(name = "state_default") val stateDefault: Int = Role.Moderator.value, + /** + * The power level requirements for specific notification types. This is a mapping from key to power level for that notifications key. + */ @Json(name = "notifications") val notifications: Map = emptyMap() ) { /** @@ -71,6 +101,9 @@ data class PowerLevelsContent( } companion object { + /** + * Key to use for content.notifications and get the level required to trigger an @room notification. Defaults to 50 if unspecified. + */ const val NOTIFICATIONS_ROOM_KEY = "room" } } From 75e06d43c565fe8676e656f2d8eac79aa0afa35e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Jan 2021 12:29:34 +0100 Subject: [PATCH 10/10] Cover room settings screens by the sanity unit test --- .../vector/app/ui/UiAllScreensSanityTest.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index cc4724e8f3..58b596b05f 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -247,6 +247,7 @@ class UiAllScreensSanityTest { // Room settings clickListItem(R.id.matrixProfileRecyclerView, 3) + navigateToRoomParameters() pressBack() // Notifications @@ -285,6 +286,31 @@ class UiAllScreensSanityTest { pressBack() } + private fun navigateToRoomParameters() { + // Room addresses + clickListItem(R.id.roomSettingsRecyclerView, 4) + onView(isRoot()).perform(waitForView(withText(R.string.room_alias_published_alias_title))) + pressBack() + + // Room permissions + clickListItem(R.id.roomSettingsRecyclerView, 6) + onView(isRoot()).perform(waitForView(withText(R.string.room_permissions_title))) + clickOn(R.string.room_permissions_change_room_avatar) + clickDialogNegativeButton() + // Toggle + clickOn(R.string.show_advanced) + clickOn(R.string.hide_advanced) + pressBack() + + // Room history readability + clickListItem(R.id.roomSettingsRecyclerView, 8) + pressBack() + + // Room access + clickListItem(R.id.roomSettingsRecyclerView, 10) + pressBack() + } + private fun navigateToInvite() { assertDisplayed(R.id.inviteUsersButton) clickOn(R.id.inviteUsersButton)