Change a room's permissions power levels (#2525)

* Change a room's permissions power levels

* Make `currentPermissions` use a `MatrixRoomPowerLevels?` instance instead.

* Update strings

* Update screenshots

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
Jorge Martin Espinosa 2024-03-12 15:45:06 +01:00 committed by GitHub
parent 3453738344
commit 59a682b407
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
71 changed files with 1556 additions and 58 deletions

View file

@ -170,7 +170,7 @@ class StateContentFormatter @Inject constructor(
"RoomPinnedEvents"
}
}
is OtherState.RoomPowerLevels -> when (renderingMode) {
is OtherState.RoomUserPowerLevels -> when (renderingMode) {
RenderingMode.RoomList -> {
Timber.v("Filtering timeline item for room state change: $content")
null

View file

@ -3,12 +3,16 @@
<string name="state_event_avatar_changed_too">"(avatar was changed too)"</string>
<string name="state_event_avatar_url_changed">"%1$s changed their avatar"</string>
<string name="state_event_avatar_url_changed_by_you">"You changed your avatar"</string>
<string name="state_event_demoted_to_member">"%1$s was demoted to member"</string>
<string name="state_event_demoted_to_moderator">"%1$s was demoted to moderator"</string>
<string name="state_event_display_name_changed_from">"%1$s changed their display name from %2$s to %3$s"</string>
<string name="state_event_display_name_changed_from_by_you">"You changed your display name from %1$s to %2$s"</string>
<string name="state_event_display_name_removed">"%1$s removed their display name (it was %2$s)"</string>
<string name="state_event_display_name_removed_by_you">"You removed your display name (it was %1$s)"</string>
<string name="state_event_display_name_set">"%1$s set their display name to %2$s"</string>
<string name="state_event_display_name_set_by_you">"You set your display name to %1$s"</string>
<string name="state_event_promoted_to_administrator">"%1$s was promoted to admin"</string>
<string name="state_event_promoted_to_moderator">"%1$s was promoted to moderator"</string>
<string name="state_event_room_avatar_changed">"%1$s changed the room avatar"</string>
<string name="state_event_room_avatar_changed_by_you">"You changed the room avatar"</string>
<string name="state_event_room_avatar_removed">"%1$s removed the room avatar"</string>

View file

@ -650,7 +650,7 @@ class DefaultRoomLastMessageFormatterTest {
OtherState.RoomHistoryVisibility,
OtherState.RoomJoinRules,
OtherState.RoomPinnedEvents,
OtherState.RoomPowerLevels(emptyMap()),
OtherState.RoomUserPowerLevels(emptyMap()),
OtherState.RoomServerAcl,
OtherState.RoomTombstone,
OtherState.SpaceChild,

View file

@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.ReceiptType
@ -97,6 +98,12 @@ interface MatrixRoom : Closeable {
suspend fun unsubscribeFromSync()
suspend fun powerLevels(): Result<MatrixRoomPowerLevels>
suspend fun updatePowerLevels(matrixRoomPowerLevels: MatrixRoomPowerLevels): Result<Unit>
suspend fun resetPowerLevels(): Result<MatrixRoomPowerLevels>
suspend fun userRole(userId: UserId): Result<RoomMember.Role>
suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit>

View file

@ -20,6 +20,17 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.StateEventType
data class MatrixRoomPowerLevels(
val ban: Long,
val invite: Long,
val kick: Long,
val sendEvents: Long,
val redactEvents: Long,
val roomName: Long,
val roomAvatar: Long,
val roomTopic: Long,
)
/**
* Shortcut for calling [MatrixRoom.canUserInvite] with our own user.
*/

View file

@ -33,7 +33,7 @@ sealed interface OtherState {
data object RoomJoinRules : OtherState
data class RoomName(val name: String?) : OtherState
data object RoomPinnedEvents : OtherState
data class RoomPowerLevels(val users: Map<String, Long>) : OtherState
data class RoomUserPowerLevels(val users: Map<String, Long>) : OtherState
data object RoomServerAcl : OtherState
data class RoomThirdPartyInvite(val displayName: String?) : OtherState
data object RoomTombstone : OtherState

View file

@ -39,6 +39,7 @@ import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
@ -54,6 +55,7 @@ import io.element.android.libraries.matrix.impl.poll.toInner
import io.element.android.libraries.matrix.impl.room.location.toInner
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
@ -86,6 +88,7 @@ import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
import org.matrix.rustcomponents.sdk.use
import timber.log.Timber
import uniffi.matrix_sdk.RoomPowerLevelChanges
import java.io.File
import org.matrix.rustcomponents.sdk.Room as InnerRoom
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
@ -253,6 +256,34 @@ class RustMatrixRoom(
}
}
override suspend fun powerLevels(): Result<MatrixRoomPowerLevels> = withContext(roomDispatcher) {
runCatching {
RoomPowerLevelsMapper.map(innerRoom.getPowerLevels())
}
}
override suspend fun updatePowerLevels(matrixRoomPowerLevels: MatrixRoomPowerLevels): Result<Unit> = withContext(roomDispatcher) {
runCatching {
val changes = RoomPowerLevelChanges(
ban = matrixRoomPowerLevels.ban,
invite = matrixRoomPowerLevels.invite,
kick = matrixRoomPowerLevels.kick,
redact = matrixRoomPowerLevels.redactEvents,
eventsDefault = matrixRoomPowerLevels.sendEvents,
roomName = matrixRoomPowerLevels.roomName,
roomAvatar = matrixRoomPowerLevels.roomAvatar,
roomTopic = matrixRoomPowerLevels.roomTopic,
)
innerRoom.applyPowerLevelChanges(changes)
}
}
override suspend fun resetPowerLevels(): Result<MatrixRoomPowerLevels> = withContext(roomDispatcher) {
runCatching {
RoomPowerLevelsMapper.map(innerRoom.resetPowerLevels())
}
}
override suspend fun userAvatarUrl(userId: UserId): Result<String?> = withContext(roomDispatcher) {
runCatching {
innerRoom.memberAvatarUrl(userId.value)

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.room.powerlevels
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
import org.matrix.rustcomponents.sdk.RoomPowerLevels as RustRoomPowerLevels
object RoomPowerLevelsMapper {
fun map(roomPowerLevels: RustRoomPowerLevels): MatrixRoomPowerLevels {
return MatrixRoomPowerLevels(
ban = roomPowerLevels.ban,
invite = roomPowerLevels.invite,
kick = roomPowerLevels.kick,
sendEvents = roomPowerLevels.eventsDefault,
redactEvents = roomPowerLevels.redact,
roomName = roomPowerLevels.roomName,
roomAvatar = roomPowerLevels.roomAvatar,
roomTopic = roomPowerLevels.roomTopic
)
}
}

View file

@ -163,7 +163,7 @@ private fun RustOtherState.map(): OtherState {
RustOtherState.RoomJoinRules -> OtherState.RoomJoinRules
is RustOtherState.RoomName -> OtherState.RoomName(name)
RustOtherState.RoomPinnedEvents -> OtherState.RoomPinnedEvents
is RustOtherState.RoomPowerLevels -> OtherState.RoomPowerLevels(users)
is RustOtherState.RoomPowerLevels -> OtherState.RoomUserPowerLevels(users)
RustOtherState.RoomServerAcl -> OtherState.RoomServerAcl
is RustOtherState.RoomThirdPartyInvite -> OtherState.RoomThirdPartyInvite(displayName)
RustOtherState.RoomTombstone -> OtherState.RoomTombstone

View file

@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.ReceiptType
@ -125,6 +126,9 @@ class FakeMatrixRoom(
private var canUserTriggerRoomNotificationResult: Result<Boolean> = Result.success(true)
private var canUserJoinCallResult: Result<Boolean> = Result.success(true)
private var setIsFavoriteResult = Result.success(Unit)
private var powerLevelsResult = Result.success(defaultRoomPowerLevels())
private var updatePowerLevelsResult = Result.success(Unit)
private var resetPowerLevelsResult = Result.success(defaultRoomPowerLevels())
var sendMessageMentions = emptyList<Mention>()
val editMessageCalls = mutableListOf<Pair<String, String?>>()
private val _typingRecord = mutableListOf<Boolean>()
@ -204,6 +208,17 @@ class FakeMatrixRoom(
override suspend fun subscribeToSync() = Unit
override suspend fun unsubscribeFromSync() = Unit
override suspend fun powerLevels(): Result<MatrixRoomPowerLevels> {
return powerLevelsResult
}
override suspend fun updatePowerLevels(matrixRoomPowerLevels: MatrixRoomPowerLevels): Result<Unit> = simulateLongTask {
updatePowerLevelsResult
}
override suspend fun resetPowerLevels(): Result<MatrixRoomPowerLevels> = simulateLongTask {
resetPowerLevelsResult
}
override fun destroy() = Unit
@ -676,6 +691,18 @@ class FakeMatrixRoom(
fun givenRoomTypingMembers(typingMembers: List<UserId>) {
_roomTypingMembersFlow.tryEmit(typingMembers)
}
fun givenPowerLevelsResult(result: Result<MatrixRoomPowerLevels>) {
powerLevelsResult = result
}
fun givenUpdatePowerLevelsResult(result: Result<Unit>) {
updatePowerLevelsResult = result
}
fun givenResetPowerLevelsResult(result: Result<MatrixRoomPowerLevels>) {
resetPowerLevelsResult = result
}
}
data class SendLocationInvocation(
@ -752,3 +779,14 @@ fun aRoomInfo(
userPowerLevels = userPowerLevels,
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
)
fun defaultRoomPowerLevels() = MatrixRoomPowerLevels(
ban = 50,
invite = 0,
kick = 50,
sendEvents = 0,
redactEvents = 50,
roomName = 100,
roomAvatar = 100,
roomTopic = 100
)

View file

@ -49,6 +49,7 @@
<string name="action_decline">"Decline"</string>
<string name="action_delete_poll">"Delete Poll"</string>
<string name="action_disable">"Disable"</string>
<string name="action_discard">"Discard"</string>
<string name="action_done">"Done"</string>
<string name="action_edit">"Edit"</string>
<string name="action_edit_poll">"Edit poll"</string>