mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-21 13:48:46 +03:00
Edit room permissions (#2471)
This commit is contained in:
parent
22c10f5ada
commit
c4a019f0d3
16 changed files with 654 additions and 9 deletions
|
@ -3,6 +3,7 @@ Changes in Element 1.0.14 (2020-XX-XX)
|
||||||
|
|
||||||
Features ✨:
|
Features ✨:
|
||||||
- Enable url previews for notices (#2562)
|
- Enable url previews for notices (#2562)
|
||||||
|
- Edit room permissions (#2471)
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Add System theme option and set as default (#904) (#2387)
|
- Add System theme option and set as default (#904) (#2387)
|
||||||
|
|
|
@ -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.members.RoomMemberListFragment
|
||||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||||
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
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.RoomUploadsFragment
|
||||||
import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment
|
import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment
|
||||||
import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment
|
import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment
|
||||||
|
@ -364,6 +365,11 @@ interface FragmentModule {
|
||||||
@FragmentKey(RoomAliasFragment::class)
|
@FragmentKey(RoomAliasFragment::class)
|
||||||
fun bindRoomAliasFragment(fragment: RoomAliasFragment): Fragment
|
fun bindRoomAliasFragment(fragment: RoomAliasFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(RoomPermissionsFragment::class)
|
||||||
|
fun bindRoomPermissionsFragment(fragment: RoomPermissionsFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(RoomMemberProfileFragment::class)
|
@FragmentKey(RoomMemberProfileFragment::class)
|
||||||
|
|
|
@ -324,7 +324,7 @@ class RoomMemberProfileFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEditPowerLevel(currentRole: Role) {
|
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))
|
viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.features.roommemberprofile.powerlevel
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -29,7 +30,10 @@ import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||||
|
|
||||||
object EditPowerLevelDialogs {
|
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 dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_edit_power_level, null)
|
||||||
val views = DialogEditPowerLevelBinding.bind(dialogLayout)
|
val views = DialogEditPowerLevelBinding.bind(dialogLayout)
|
||||||
views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
views.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||||
|
@ -45,7 +49,7 @@ object EditPowerLevelDialogs {
|
||||||
}
|
}
|
||||||
|
|
||||||
AlertDialog.Builder(activity)
|
AlertDialog.Builder(activity)
|
||||||
.setTitle(R.string.power_level_edit_title)
|
.setTitle(titleRes)
|
||||||
.setView(dialogLayout)
|
.setView(dialogLayout)
|
||||||
.setPositiveButton(R.string.edit) { _, _ ->
|
.setPositiveButton(R.string.edit) { _, _ ->
|
||||||
val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) {
|
val newValue = when (views.powerLevelRadioGroup.checkedRadioButtonId) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.di.ScreenComponent
|
import im.vector.app.core.di.ScreenComponent
|
||||||
import im.vector.app.core.extensions.addFragment
|
import im.vector.app.core.extensions.addFragment
|
||||||
import im.vector.app.core.extensions.addFragmentToBackstack
|
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.ToolbarConfigurable
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.databinding.ActivitySimpleBinding
|
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.members.RoomMemberListFragment
|
||||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||||
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
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.RoomUploadsFragment
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -102,12 +104,13 @@ class RoomProfileActivity :
|
||||||
.observe()
|
.observe()
|
||||||
.subscribe { sharedAction ->
|
.subscribe { sharedAction ->
|
||||||
when (sharedAction) {
|
when (sharedAction) {
|
||||||
is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
|
RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
|
||||||
is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
|
RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
|
||||||
is RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias()
|
RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias()
|
||||||
is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
|
RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions()
|
||||||
is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
|
RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
|
||||||
}
|
RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
|
||||||
|
}.exhaustive
|
||||||
}
|
}
|
||||||
.disposeOnDestroy()
|
.disposeOnDestroy()
|
||||||
|
|
||||||
|
@ -144,6 +147,10 @@ class RoomProfileActivity :
|
||||||
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomAliasFragment::class.java, roomProfileArgs)
|
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomAliasFragment::class.java, roomProfileArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openRoomPermissions() {
|
||||||
|
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomPermissionsFragment::class.java, roomProfileArgs)
|
||||||
|
}
|
||||||
|
|
||||||
private fun openRoomMembers() {
|
private fun openRoomMembers() {
|
||||||
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs)
|
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import im.vector.app.core.platform.VectorSharedAction
|
||||||
sealed class RoomProfileSharedAction : VectorSharedAction {
|
sealed class RoomProfileSharedAction : VectorSharedAction {
|
||||||
object OpenRoomSettings : RoomProfileSharedAction()
|
object OpenRoomSettings : RoomProfileSharedAction()
|
||||||
object OpenRoomAliasesSettings : RoomProfileSharedAction()
|
object OpenRoomAliasesSettings : RoomProfileSharedAction()
|
||||||
|
object OpenRoomPermissionsSettings : RoomProfileSharedAction()
|
||||||
object OpenRoomUploads : RoomProfileSharedAction()
|
object OpenRoomUploads : RoomProfileSharedAction()
|
||||||
object OpenRoomMembers : RoomProfileSharedAction()
|
object OpenRoomMembers : RoomProfileSharedAction()
|
||||||
object OpenBannedRoomMembers : RoomProfileSharedAction()
|
object OpenBannedRoomMembers : RoomProfileSharedAction()
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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<RoomPermissionsViewState>() {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<FragmentRoomSettingGenericBinding>(),
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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<RoomPermissionsViewState, RoomPermissionsAction, RoomPermissionsViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedInject.Factory
|
||||||
|
interface Factory {
|
||||||
|
fun create(initialState: RoomPermissionsViewState): RoomPermissionsViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<RoomPermissionsViewModel, RoomPermissionsViewState> {
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<RoomSummary> = Uninitialized,
|
||||||
|
val actionPermissions: ActionPermissions = ActionPermissions(),
|
||||||
|
val currentPowerLevelsContent: Async<PowerLevelsContent> = Uninitialized,
|
||||||
|
val newPowerLevelsContent: PowerLevelsContent? = null,
|
||||||
|
val isLoading: Boolean = false
|
||||||
|
) : MvRxState {
|
||||||
|
|
||||||
|
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||||
|
|
||||||
|
data class ActionPermissions(
|
||||||
|
val canChangePowerLevels: Boolean = false
|
||||||
|
)
|
||||||
|
}
|
|
@ -46,6 +46,7 @@ class RoomSettingsController @Inject constructor(
|
||||||
fun onTopicChanged(topic: String)
|
fun onTopicChanged(topic: String)
|
||||||
fun onHistoryVisibilityClicked()
|
fun onHistoryVisibilityClicked()
|
||||||
fun onRoomAliasesClicked()
|
fun onRoomAliasesClicked()
|
||||||
|
fun onRoomPermissionsClicked()
|
||||||
fun onJoinRuleClicked()
|
fun onJoinRuleClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +116,16 @@ class RoomSettingsController @Inject constructor(
|
||||||
action = { callback?.onRoomAliasesClicked() }
|
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(
|
buildProfileAction(
|
||||||
id = "historyReadability",
|
id = "historyReadability",
|
||||||
title = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_title),
|
title = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_title),
|
||||||
|
|
|
@ -178,6 +178,10 @@ class RoomSettingsFragment @Inject constructor(
|
||||||
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAliasesSettings)
|
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAliasesSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRoomPermissionsClicked() {
|
||||||
|
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomPermissionsSettings)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onJoinRuleClicked() = withState(viewModel) { state ->
|
override fun onJoinRuleClicked() = withState(viewModel) { state ->
|
||||||
val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules
|
val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules
|
||||||
val currentGuestAccess = state.newRoomJoinRules.newGuestAccess ?: state.currentGuestAccess
|
val currentGuestAccess = state.newRoomJoinRules.newGuestAccess ?: state.currentGuestAccess
|
||||||
|
|
|
@ -609,6 +609,32 @@
|
||||||
<string name="ssl_expected_existing_expl">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.</string>
|
<string name="ssl_expected_existing_expl">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.</string>
|
||||||
<string name="ssl_only_accept">Only accept the certificate if the server administrator has published a fingerprint that matches the one above.</string>
|
<string name="ssl_only_accept">Only accept the certificate if the server administrator has published a fingerprint that matches the one above.</string>
|
||||||
|
|
||||||
|
<!-- Room Permissions -->
|
||||||
|
<string name="room_settings_permissions_title">Room permissions</string>
|
||||||
|
<string name="room_settings_permissions_subtitle">View and update the roles required to change various parts of the room.</string>
|
||||||
|
|
||||||
|
<string name="room_permissions_title">"Permissions"</string>
|
||||||
|
<string name="room_permissions_notice">"Select the roles required to change various parts of the room"</string>
|
||||||
|
|
||||||
|
<string name="room_permissions_default_role">Default role</string>
|
||||||
|
<string name="room_permissions_send_messages">Send messages</string>
|
||||||
|
<string name="room_permissions_invite_users">Invite users</string>
|
||||||
|
<string name="room_permissions_change_settings">Change settings</string>
|
||||||
|
<string name="room_permissions_kick_users">Kick users</string>
|
||||||
|
<string name="room_permissions_ban_users">Ban users</string>
|
||||||
|
<string name="room_permissions_remove_messages_sent_by_others">Remove messages sent by others</string>
|
||||||
|
<string name="room_permissions_notify_everyone">Notify everyone</string>
|
||||||
|
<string name="room_permissions_modify_widgets">Modify widgets</string>
|
||||||
|
<string name="room_permissions_change_room_avatar">Change room avatar</string>
|
||||||
|
<string name="room_permissions_change_main_address_for_the_room">Change main address for the room</string>
|
||||||
|
<string name="room_permissions_enable_room_encryption">Enable room encryption</string>
|
||||||
|
<string name="room_permissions_change_history_visibility">Change history visibility</string>
|
||||||
|
<string name="room_permissions_change_room_name">Change room name</string>
|
||||||
|
<string name="room_permissions_change_permissions">Change permissions</string>
|
||||||
|
<string name="room_permissions_send_m_room_server_acl_events">Send m.room.server_acl events</string>
|
||||||
|
<string name="room_permissions_upgrade_the_room">Upgrade the room</string>
|
||||||
|
<string name="room_permissions_change_topic">Change topic</string>
|
||||||
|
|
||||||
<!-- Room Details -->
|
<!-- Room Details -->
|
||||||
<string name="room_details_title">Room Details</string>
|
<string name="room_details_title">Room Details</string>
|
||||||
<string name="room_details_people">People</string>
|
<string name="room_details_people">People</string>
|
||||||
|
|
Loading…
Add table
Reference in a new issue