Power level: handle timeline rendering

This commit is contained in:
ganfra 2020-06-04 17:18:39 +02:00
parent 9075371145
commit 73eca2407b
17 changed files with 158 additions and 106 deletions

View File

@ -39,6 +39,6 @@ class SenderNotificationPermissionCondition(
fun isSatisfied(event: Event, powerLevels: PowerLevelsContent): Boolean {
val powerLevelsHelper = PowerLevelsHelper(powerLevels)
return event.senderId != null && powerLevelsHelper.getUserPowerLevel(event.senderId) >= powerLevelsHelper.notificationLevel(key)
return event.senderId != null && powerLevelsHelper.getUserPowerLevelValue(event.senderId) >= powerLevelsHelper.notificationLevel(key)
}
}

View File

@ -18,21 +18,21 @@ package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants
import im.vector.matrix.android.api.session.room.powerlevels.Role
/**
* Class representing the EventType.EVENT_TYPE_STATE_ROOM_POWER_LEVELS state event content.
*/
@JsonClass(generateAdapter = true)
data class PowerLevelsContent(
@Json(name = "ban") val ban: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "kick") val kick: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "invite") val invite: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "redact") val redact: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "events_default") val eventsDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL,
@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: MutableMap<String, Int> = HashMap(),
@Json(name = "users_default") val usersDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL,
@Json(name = "users_default") val usersDefault: Int = Role.Default.value,
@Json(name = "users") val users: MutableMap<String, Int> = HashMap(),
@Json(name = "state_default") val stateDefault: Int = PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL,
@Json(name = "state_default") val stateDefault: Int = Role.Moderator.value,
@Json(name = "notifications") val notifications: Map<String, Any> = HashMap()
)

View File

@ -1,27 +0,0 @@
/*
* Copyright 2020 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.matrix.android.api.session.room.powerlevels
object PowerLevelsConstants {
const val DEFAULT_ROOM_ADMIN_LEVEL = 100
const val DEFAULT_ROOM_MODERATOR_LEVEL = 50
const val DEFAULT_ROOM_USER_LEVEL = 0
val DEFAULT_DEFINED_LEVELS = listOf(DEFAULT_ROOM_ADMIN_LEVEL, DEFAULT_ROOM_MODERATOR_LEVEL, DEFAULT_ROOM_USER_LEVEL)
}

View File

@ -30,12 +30,24 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
* @param userId the user id
* @return the power level
*/
fun getUserPowerLevel(userId: String): Int {
fun getUserPowerLevelValue(userId: String): Int {
return powerLevelsContent.users.getOrElse(userId) {
powerLevelsContent.usersDefault
}
}
/**
* Returns the user power level of a dedicated user Id
*
* @param userId the user id
* @return the power level
*/
fun getUserRole(userId: String): Role {
return getUserPowerLevelValue(userId).let {
Role.fromValue(it, powerLevelsContent.eventsDefault)
}
}
/**
* Tell if an user can send an event of a certain type
*
@ -45,7 +57,7 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
*/
fun isAllowedToSend(isState: Boolean, eventType: String?, userId: String): Boolean {
return if (userId.isNotEmpty()) {
val powerLevel = getUserPowerLevel(userId)
val powerLevel = getUserPowerLevelValue(userId)
val minimumPowerLevel = powerLevelsContent.events[eventType]
?: if (isState) {
powerLevelsContent.stateDefault
@ -67,7 +79,7 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
// the first implementation was a string value
is String -> value.toInt()
is Int -> value
else -> PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL
else -> Role.Moderator.value
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2020 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.matrix.android.api.session.room.powerlevels
import androidx.annotation.StringRes
import im.vector.matrix.android.R
sealed class Role(open val value: Int, @StringRes val res: Int) : Comparable<Role> {
object Admin : Role(100, R.string.power_level_admin)
object Moderator : Role(50, R.string.power_level_moderator)
object Default : Role(0, R.string.power_level_default)
data class Custom(override val value: Int) : Role(value, R.string.power_level_custom)
override fun compareTo(other: Role): Int {
return value.compareTo(other.value)
}
companion object {
fun fromValue(value: Int, default: Int): Role {
return when (value) {
default, Default.value -> Default
Moderator.value -> Moderator
Admin.value -> Admin
else -> Custom(value)
}
}
}
}

View File

@ -89,6 +89,16 @@
<string name="notice_widget_modified">%1$s modified %2$s widget</string>
<string name="notice_widget_modified_by_you">You modified %1$s widget</string>
<string name="power_level_admin">Admin</string>
<string name="power_level_moderator">Moderator</string>
<string name="power_level_default">Default</string>
<string name="power_level_custom">Custom (%1$d)</string>
<string name="power_level_custom_no_value">Custom</string>
<string name="notice_power_level_changed_by_you">You changed the power level of %1$s.</string>
<string name="notice_power_level_changed">%1$s changed the power level of %2$s.</string>
<string name="notice_power_level_diff">%1$s from %2$s to %3$s</string>
<string name="notice_crypto_unable_to_decrypt">** Unable to decrypt: %s **</string>
<string name="notice_crypto_error_unkwown_inbound_session_id">The sender\'s device has not sent us the keys for this message.</string>

View File

@ -59,6 +59,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER,
EventType.STATE_ROOM_POWER_LEVELS,
EventType.REACTION,
EventType.REDACTION -> noticeItemFactory.create(event, highlight, callback)
EventType.STATE_ROOM_ENCRYPTION -> {

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.GuestAccess
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomGuestAccessContent
@ -34,6 +35,7 @@ import im.vector.matrix.android.api.session.room.model.RoomNameContent
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.widgets.model.WidgetContent
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
@ -64,6 +66,7 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
EventType.STATE_ROOM_WIDGET,
EventType.STATE_ROOM_WIDGET_LEGACY -> formatWidgetEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName)
EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName)
EventType.STATE_ROOM_POWER_LEVELS -> formatRoomPowerLevels(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName)
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER -> formatCallEvent(type, timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName)
@ -84,6 +87,34 @@ class NoticeEventFormatter @Inject constructor(private val sessionHolder: Active
}
}
private fun formatRoomPowerLevels(event: Event, disambiguatedDisplayName: String): CharSequence? {
val powerLevelsContent: PowerLevelsContent = event.getClearContent().toModel() ?: return null
val previousPowerLevelsContent: PowerLevelsContent = event.prevContent.toModel() ?: return null
val userIds = HashSet<String>()
userIds.addAll(powerLevelsContent.users.keys)
userIds.addAll(previousPowerLevelsContent.users.keys)
val diffs = ArrayList<String>()
userIds.forEach { userId ->
val from = PowerLevelsHelper(previousPowerLevelsContent).getUserRole(userId)
val to = PowerLevelsHelper(powerLevelsContent).getUserRole(userId)
if (from != to) {
val fromStr = sp.getString(from.res, from.value)
val toStr = sp.getString(to.res, to.value)
val diff = sp.getString(R.string.notice_power_level_diff, userId, fromStr, toStr)
diffs.add(diff)
}
}
if (diffs.isEmpty()) {
return null
}
val diffStr = diffs.joinToString(separator = ", ")
return if (event.isSentByCurrentUser()) {
sp.getString(R.string.notice_power_level_changed_by_you, diffStr)
} else {
sp.getString(R.string.notice_power_level_changed, disambiguatedDisplayName, diffStr)
}
}
private fun formatWidgetEvent(event: Event, disambiguatedDisplayName: String): CharSequence? {
val widgetContent: WidgetContent = event.getClearContent().toModel() ?: return null
val previousWidgetContent: WidgetContent? = event.prevContent.toModel()

View File

@ -32,6 +32,7 @@ object TimelineDisplayableEvents {
EventType.STATE_ROOM_ALIASES,
EventType.STATE_ROOM_CANONICAL_ALIAS,
EventType.STATE_ROOM_HISTORY_VISIBILITY,
EventType.STATE_ROOM_POWER_LEVELS,
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER,

View File

@ -19,7 +19,7 @@ package im.vector.riotx.features.roommemberprofile
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants
import im.vector.matrix.android.api.session.room.powerlevels.Role
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.profiles.buildProfileAction
@ -46,7 +46,7 @@ class RoomMemberProfileController @Inject constructor(
fun onShowDeviceListNoCrossSigning()
fun onJumpToReadReceiptClicked()
fun onMentionClicked()
fun onSetPowerLevel(userPowerLevel: Int)
fun onSetPowerLevel(userRole: Role)
}
override fun buildModels(data: RoomMemberProfileViewState?) {
@ -85,10 +85,10 @@ class RoomMemberProfileController @Inject constructor(
val powerLevelsContent = state.powerLevelsContent() ?: return
val powerLevelsStr = state.userPowerLevelString() ?: return
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
val userPowerLevel = powerLevelsHelper.getUserPowerLevel(state.userId)
val myPowerLevel = powerLevelsHelper.getUserPowerLevel(session.myUserId)
val userPowerLevel = powerLevelsHelper.getUserRole(state.userId)
val myPowerLevel = powerLevelsHelper.getUserRole(session.myUserId)
if ((!state.isMine && myPowerLevel <= userPowerLevel)
|| myPowerLevel != PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL) {
|| myPowerLevel != Role.Admin) {
return
}
buildProfileSection("Admin Actions")

View File

@ -29,6 +29,7 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.room.powerlevels.Role
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R
import im.vector.riotx.core.animations.AppBarStateChangeListener
@ -248,9 +249,9 @@ class RoomMemberProfileFragment @Inject constructor(
navigator.openBigImageViewer(requireActivity(), view, userMatrixItem)
}
override fun onSetPowerLevel(userPowerLevel: Int) {
SetPowerLevelDialogs.showChoice(requireActivity(), userPowerLevel) { newPowerLevel ->
viewModel.handle(RoomMemberProfileAction.SetPowerLevel(userPowerLevel, newPowerLevel, true))
override fun onSetPowerLevel(userRole: Role) {
SetPowerLevelDialogs.showChoice(requireActivity(), userRole) { newPowerLevel ->
viewModel.handle(RoomMemberProfileAction.SetPowerLevel(userRole.value, newPowerLevel, true))
}
}
}

View File

@ -37,7 +37,6 @@ import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.matrix.android.api.util.toMatrixItem
@ -151,7 +150,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
return@withState
}
val currentPowerLevelsContent = state.powerLevelsContent() ?: return@withState
val myPowerLevel = PowerLevelsHelper(currentPowerLevelsContent).getUserPowerLevel(session.myUserId)
val myPowerLevel = PowerLevelsHelper(currentPowerLevelsContent).getUserPowerLevelValue(session.myUserId)
if (action.askForValidation && action.newValue >= myPowerLevel) {
_viewEvents.post(RoomMemberProfileViewEvents.ShowPowerLevelValidation(action.previousValue, action.newValue))
} else {
@ -229,16 +228,9 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
BiFunction<RoomSummary, PowerLevelsContent, String> { roomSummary, powerLevelsContent ->
val roomName = roomSummary.toMatrixItem().getBestName()
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
val userPowerLevel = powerLevelsHelper.getUserPowerLevel(initialState.userId)
if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL) {
stringProvider.getString(R.string.room_member_power_level_admin_in, roomName)
} else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL) {
stringProvider.getString(R.string.room_member_power_level_moderator_in, roomName)
} else if (userPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL) {
stringProvider.getString(R.string.room_member_power_level_user_in, roomName)
} else {
stringProvider.getString(R.string.room_member_power_level_custom_in, userPowerLevel, roomName)
}
val userPowerLevel = powerLevelsHelper.getUserRole(initialState.userId)
val powerLevelStr = stringProvider.getString(userPowerLevel.res, userPowerLevel.value)
stringProvider.getString(R.string.room_member_power_level_in, powerLevelStr, roomName)
}
).execute {
copy(userPowerLevelString = it)

View File

@ -22,21 +22,21 @@ import android.view.KeyEvent
import android.widget.SeekBar
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants
import im.vector.matrix.android.api.session.room.powerlevels.Role
import im.vector.riotx.R
import im.vector.riotx.core.extensions.hideKeyboard
import kotlinx.android.synthetic.main.dialog_set_power_level.view.*
object SetPowerLevelDialogs {
fun showChoice(activity: Activity, currentValue: Int, listener: (Int) -> Unit) {
fun showChoice(activity: Activity, currentRole: Role, listener: (Int) -> Unit) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_set_power_level, null)
dialogLayout.powerLevelRadioGroup.setOnCheckedChangeListener { _, checkedId ->
dialogLayout.powerLevelCustomLayout.isVisible = checkedId == R.id.powerLevelCustomRadio
}
dialogLayout.powerLevelCustomSlider.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
dialogLayout.powerLevelCustomValue.text = progress.toString()
dialogLayout.powerLevelCustomTitle.text = activity.getString(R.string.power_level_custom, progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
@ -47,12 +47,12 @@ object SetPowerLevelDialogs {
//NOOP
}
})
dialogLayout.powerLevelCustomSlider.progress = currentValue
when (currentValue) {
PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL -> dialogLayout.powerLevelAdminRadio.isChecked = true
PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL -> dialogLayout.powerLevelModeratorRadio.isChecked = true
PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL -> dialogLayout.powerLevelDefaultRadio.isChecked = true
else -> dialogLayout.powerLevelCustomRadio.isChecked = true
dialogLayout.powerLevelCustomSlider.progress = currentRole.value
when (currentRole) {
Role.Admin -> dialogLayout.powerLevelAdminRadio.isChecked = true
Role.Moderator -> dialogLayout.powerLevelModeratorRadio.isChecked = true
Role.Default -> dialogLayout.powerLevelDefaultRadio.isChecked = true
else -> dialogLayout.powerLevelCustomRadio.isChecked = true
}
AlertDialog.Builder(activity)
@ -61,9 +61,9 @@ object SetPowerLevelDialogs {
.setPositiveButton(R.string.action_change)
{ _, _ ->
val newValue = when (dialogLayout.powerLevelRadioGroup.checkedRadioButtonId) {
R.id.powerLevelAdminRadio -> PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL
R.id.powerLevelModeratorRadio -> PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL
R.id.powerLevelDefaultRadio -> PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL
R.id.powerLevelAdminRadio -> Role.Admin.value
R.id.powerLevelModeratorRadio -> Role.Moderator.value
R.id.powerLevelDefaultRadio -> Role.Default.value
else -> dialogLayout.powerLevelCustomSlider.progress
}
listener(newValue)

View File

@ -31,8 +31,8 @@ import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsConstants
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.android.api.session.room.powerlevels.Role
import im.vector.matrix.rx.asObservable
import im.vector.matrix.rx.mapOptional
import im.vector.matrix.rx.rx
@ -135,22 +135,22 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
roomMembers
.forEach { roomMember ->
val memberPowerLevel = powerLevelsHelper.getUserPowerLevel(roomMember.userId)
val userRole = powerLevelsHelper.getUserRole(roomMember.userId)
when {
roomMember.membership == Membership.INVITE -> invites.add(roomMember)
memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_ADMIN_LEVEL -> admins.add(roomMember)
memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_MODERATOR_LEVEL -> moderators.add(roomMember)
memberPowerLevel == PowerLevelsConstants.DEFAULT_ROOM_USER_LEVEL -> users.add(roomMember)
else -> customs.add(roomMember)
roomMember.membership == Membership.INVITE -> invites.add(roomMember)
userRole == Role.Admin -> admins.add(roomMember)
userRole == Role.Moderator -> moderators.add(roomMember)
userRole == Role.Default -> users.add(roomMember)
else -> customs.add(roomMember)
}
}
return listOf(
PowerLevelCategory.ADMIN to admins.sortedWith(roomMemberSummaryComparator),
PowerLevelCategory.MODERATOR to moderators.sortedWith(roomMemberSummaryComparator),
PowerLevelCategory.CUSTOM to customs.sortedWith(roomMemberSummaryComparator),
PowerLevelCategory.INVITE to invites.sortedWith(roomMemberSummaryComparator),
PowerLevelCategory.USER to users.sortedWith(roomMemberSummaryComparator)
RoomMemberListCategories.ADMIN to admins.sortedWith(roomMemberSummaryComparator),
RoomMemberListCategories.MODERATOR to moderators.sortedWith(roomMemberSummaryComparator),
RoomMemberListCategories.CUSTOM to customs.sortedWith(roomMemberSummaryComparator),
RoomMemberListCategories.INVITE to invites.sortedWith(roomMemberSummaryComparator),
RoomMemberListCategories.USER to users.sortedWith(roomMemberSummaryComparator)
)
}

View File

@ -36,9 +36,9 @@ data class RoomMemberListViewState(
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
}
typealias RoomMemberSummaries = List<Pair<PowerLevelCategory, List<RoomMemberSummary>>>
typealias RoomMemberSummaries = List<Pair<RoomMemberListCategories, List<RoomMemberSummary>>>
enum class PowerLevelCategory(@StringRes val titleRes: Int) {
enum class RoomMemberListCategories(@StringRes val titleRes: Int) {
ADMIN(R.string.room_member_power_level_admins),
MODERATOR(R.string.room_member_power_level_moderators),
CUSTOM(R.string.room_member_power_level_custom),

View File

@ -3,6 +3,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:paddingStart="?dialogPreferredPadding"
android:paddingLeft="?dialogPreferredPadding"
@ -20,28 +21,28 @@
android:id="@+id/powerLevelAdminRadio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Admin"
android:text="@string/power_level_admin"
android:textColor="?riotx_text_primary" />
<RadioButton
android:id="@+id/powerLevelModeratorRadio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Moderator"
android:text="@string/power_level_moderator"
android:textColor="?riotx_text_primary" />
<RadioButton
android:id="@+id/powerLevelDefaultRadio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Default"
android:text="@string/power_level_default"
android:textColor="?riotx_text_primary" />
<RadioButton
android:id="@+id/powerLevelCustomRadio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Custom"
android:text="@string/power_level_custom_no_value"
android:textColor="?riotx_text_primary" />
</RadioGroup>
@ -50,28 +51,18 @@
android:id="@+id/powerLevelCustomLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
android:layout_marginTop="8dp">
<TextView
android:id="@+id/powerLevelCustomTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:text="Custom power level"
tools:text="Custom (20)"
android:layout_marginStart="16dp"
android:textColor="?riotx_text_primary"
android:textStyle="bold" />
<TextView
android:id="@+id/powerLevelCustomValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:text="20"
android:textColor="?riotx_text_secondary" />
<SeekBar
android:id="@+id/powerLevelCustomSlider"
android:layout_width="match_parent"

View File

@ -2084,11 +2084,7 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
<string name="room_member_power_level_invites">Invites</string>
<string name="room_member_power_level_users">Users</string>
<string name="room_member_power_level_admin_in">Admin in %1$s</string>
<string name="room_member_power_level_moderator_in">Moderator in %1$s</string>
<string name="room_member_power_level_user_in">Default in %1$s</string>
<string name="room_member_power_level_custom_in">Custom (%1$d) in %2$s</string>
<string name="room_member_power_level_in">%1$s in %2$s</string>
<string name="room_member_jump_to_read_receipt">Jump to read receipt</string>
<string name="rendering_event_error_type_of_event_not_handled">"RiotX does not handle events of type '%1$s'"</string>