Update dependency org.matrix.rustcomponents:sdk-android to v25.7.23 (#5073)
* Update dependency org.matrix.rustcomponents:sdk-android to v25.7.23
* Adapt to SDK changes:
- Add 'creator' role, adapt existing logic to it.
- Remove `ReplyParameters`, replace with `EventId` where possible.
- Fix changes in OIDC auth methods.
- Add more join rules.
* Make sure both creators and users with power level >= 150 are displayed as 'owners' in the room member list.
* Don't close the roles and permissions screen if the user is a creator
* Use `MediaPreviewValue.DEFAULT` for `MediaPreviewConfig.DEFAULT` too
* Improve APIs around checking roles and power levels:
- Ensure `RoomInfo.RoomPowerLevels.users` can't be directly used to check power levels since it can't check the power levels for creators.
- Add a few helper functions to handle actions that relied on the previous `users` property, and docs to explain their usages.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
parent
33aa7a914f
commit
040fde7f22
57 changed files with 264 additions and 227 deletions
|
|
@ -142,7 +142,7 @@ interface MatrixClient {
|
|||
/**
|
||||
* Execute generic GET requests through the SDKs internal HTTP client.
|
||||
*/
|
||||
suspend fun getUrl(url: String): Result<String>
|
||||
suspend fun getUrl(url: String): Result<ByteArray>
|
||||
|
||||
/**
|
||||
* Get a room preview for a given room ID or alias. This is especially useful for rooms that the user is not a member of, or hasn't joined yet.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ data class MediaPreviewConfig(
|
|||
* The default config if unknown (no local nor server config).
|
||||
*/
|
||||
val DEFAULT = MediaPreviewConfig(
|
||||
mediaPreviewValue = MediaPreviewValue.On,
|
||||
mediaPreviewValue = MediaPreviewValue.DEFAULT,
|
||||
hideInviteAvatar = false
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,19 @@ import io.element.android.libraries.matrix.api.room.join.JoinRule
|
|||
enum class MediaPreviewValue {
|
||||
On,
|
||||
Off,
|
||||
Private
|
||||
Private;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The default value if unknown (no local nor server config).
|
||||
*/
|
||||
val DEFAULT = On
|
||||
}
|
||||
}
|
||||
|
||||
fun MediaPreviewValue.isPreviewEnabled(joinRule: JoinRule?): Boolean {
|
||||
fun MediaPreviewValue?.isPreviewEnabled(joinRule: JoinRule?): Boolean {
|
||||
return when (this) {
|
||||
On -> true
|
||||
null, On -> true
|
||||
Off -> false
|
||||
Private -> when (joinRule) {
|
||||
is JoinRule.Private,
|
||||
|
|
|
|||
|
|
@ -72,10 +72,21 @@ data class RoomInfo(
|
|||
val numUnreadMentions: Long,
|
||||
val heroes: ImmutableList<MatrixUser>,
|
||||
val pinnedEventIds: ImmutableList<EventId>,
|
||||
val creator: UserId?,
|
||||
val creators: ImmutableList<UserId>,
|
||||
val historyVisibility: RoomHistoryVisibility,
|
||||
val successorRoom: SuccessorRoom?,
|
||||
) {
|
||||
val aliases: List<RoomAlias>
|
||||
get() = listOfNotNull(canonicalAlias) + alternativeAliases
|
||||
|
||||
/**
|
||||
* Returns the list of users with the given [role] in this room.
|
||||
*/
|
||||
fun usersWithRole(role: RoomMember.Role): List<UserId> {
|
||||
return if (role == RoomMember.Role.CREATOR) {
|
||||
this.creators
|
||||
} else {
|
||||
this.roomPowerLevels?.usersWithRole(role).orEmpty().toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,13 +26,17 @@ data class RoomMember(
|
|||
* Role of the RoomMember, based on its [powerLevel].
|
||||
*/
|
||||
enum class Role(val powerLevel: Long) {
|
||||
CREATOR(Long.MAX_VALUE),
|
||||
ADMIN(100L),
|
||||
MODERATOR(50L),
|
||||
USER(0L);
|
||||
|
||||
companion object {
|
||||
const val SUPER_ADMIN_LEVEL = 150L
|
||||
|
||||
fun forPowerLevel(powerLevel: Long): Role {
|
||||
return when {
|
||||
powerLevel > SUPER_ADMIN_LEVEL -> CREATOR
|
||||
powerLevel >= ADMIN.powerLevel -> ADMIN
|
||||
powerLevel >= MODERATOR.powerLevel -> MODERATOR
|
||||
else -> USER
|
||||
|
|
@ -83,3 +87,11 @@ fun RoomMember.toMatrixUser() = MatrixUser(
|
|||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns `true` if the [RoomMember] is an owner of the room.
|
||||
* Owners are defined as members with either the [RoomMember.Role.CREATOR] role or a power level greater than or equal to [RoomMember.Role.SUPER_ADMIN_LEVEL].
|
||||
*/
|
||||
fun RoomMember.isOwner(): Boolean {
|
||||
return role == RoomMember.Role.CREATOR || powerLevel >= RoomMember.Role.SUPER_ADMIN_LEVEL
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.api.room.message
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
|
||||
data class ReplyParameters(
|
||||
val inReplyToEventId: EventId,
|
||||
val enforceThreadReply: Boolean,
|
||||
val replyWithinThread: Boolean,
|
||||
)
|
||||
|
||||
fun replyInThread(eventId: EventId, explicitReply: Boolean = false) = ReplyParameters(
|
||||
inReplyToEventId = eventId,
|
||||
enforceThreadReply = true,
|
||||
replyWithinThread = explicitReply,
|
||||
)
|
||||
|
|
@ -22,10 +22,10 @@ import kotlinx.coroutines.flow.map
|
|||
*/
|
||||
fun BaseRoom.usersWithRole(role: RoomMember.Role): Flow<ImmutableList<RoomMember>> {
|
||||
return roomInfoFlow
|
||||
.map { it.roomPowerLevels?.users.orEmpty().filter { (_, powerLevel) -> RoomMember.Role.forPowerLevel(powerLevel) == role } }
|
||||
.map { roomInfo -> roomInfo.usersWithRole(role) }
|
||||
.combine(membersStateFlow) { powerLevels, membersState ->
|
||||
membersState.activeRoomMembers()
|
||||
.filter { powerLevels.containsKey(it.userId) }
|
||||
.filter { powerLevels.contains(it.userId) }
|
||||
.toPersistentList()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
|
|
|||
|
|
@ -8,9 +8,43 @@
|
|||
package io.element.android.libraries.matrix.api.room.powerlevels
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
|
||||
/**
|
||||
* Represents the power levels in a Matrix room, containing both the levels needed to perform actions and the custom power levels for users.
|
||||
*
|
||||
* **WARNING**: this won't contain the power level of the room creators, as it is not stored in the power levels event. The `users` property is private to
|
||||
* enforce this restriction and try to avoid using this property directly to check if a user has a certain role.
|
||||
* Use the [usersWithRole] or [roleOf] methods instead, and never for creators, that logic should be handled separately.
|
||||
*/
|
||||
data class RoomPowerLevels(
|
||||
/**
|
||||
* The power levels required to perform various actions in the room.
|
||||
*/
|
||||
val values: RoomPowerLevelsValues,
|
||||
val users: ImmutableMap<UserId, Long>,
|
||||
)
|
||||
private val users: ImmutableMap<UserId, Long>,
|
||||
) {
|
||||
/**
|
||||
* Returns the set of [UserId]s that have the given role in the room.
|
||||
*
|
||||
* **WARNING**: This method must not be used with the [RoomMember.Role.CREATOR] role. It'll result in a runtime error.
|
||||
*/
|
||||
fun usersWithRole(role: RoomMember.Role): Set<UserId> {
|
||||
return if (role == RoomMember.Role.CREATOR) {
|
||||
error("RoomPowerLevels.usersWithRole should not be used with CREATOR role, use roomInfo.creators instead")
|
||||
} else {
|
||||
users.filterValues { RoomMember.Role.forPowerLevel(it) == role }.keys
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the role of the user in the room based on their power level.
|
||||
* If the user is not found, returns null.
|
||||
*
|
||||
* **WARNING**: This method must not be used with the [RoomMember.Role.CREATOR] role, as it won't return any results.
|
||||
*/
|
||||
fun roleOf(userId: UserId): RoomMember.Role? {
|
||||
return users[userId]?.let(RoomMember.Role::forPowerLevel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.room.tombstone
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
/**
|
||||
|
|
@ -17,15 +16,10 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
* about the predecessor room.
|
||||
*
|
||||
* A room is tombstoned if it has received a m.room.tombstone state event.
|
||||
*
|
||||
*/
|
||||
data class PredecessorRoom(
|
||||
/**
|
||||
* The ID of the replaced room.
|
||||
*/
|
||||
val roomId: RoomId,
|
||||
/**
|
||||
* The event ID of the last known event in the predecessor room.
|
||||
*/
|
||||
val lastEventId: EventId,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ data class RoomDescription(
|
|||
enum class JoinRule {
|
||||
PUBLIC,
|
||||
KNOCK,
|
||||
RESTRICTED,
|
||||
KNOCK_RESTRICTED,
|
||||
INVITE,
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ 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.IntentionalMention
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
|
||||
|
|
@ -76,7 +75,7 @@ interface Timeline : AutoCloseable {
|
|||
): Result<Unit>
|
||||
|
||||
suspend fun replyMessage(
|
||||
replyParameters: ReplyParameters,
|
||||
repliedToEventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
|
|
@ -90,7 +89,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendVideo(
|
||||
|
|
@ -100,7 +99,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendAudio(
|
||||
|
|
@ -109,7 +108,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendFile(
|
||||
|
|
@ -118,7 +117,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
/**
|
||||
|
|
@ -131,7 +130,7 @@ interface Timeline : AutoCloseable {
|
|||
* @param zoomLevel Optional zoom level to display the map at.
|
||||
* @param assetType Optional type of the location asset.
|
||||
* Set to SENDER if sharing own location. Set to PIN if sharing any location.
|
||||
* @param replyParameters Optional reply parameters to use when sending the location.
|
||||
* @param inReplyToEventId Optional [EventId] for the event this message should reply to.
|
||||
*/
|
||||
suspend fun sendLocation(
|
||||
body: String,
|
||||
|
|
@ -139,7 +138,7 @@ interface Timeline : AutoCloseable {
|
|||
description: String? = null,
|
||||
zoomLevel: Int? = null,
|
||||
assetType: AssetType? = null,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<Unit>
|
||||
|
||||
suspend fun sendVoiceMessage(
|
||||
|
|
@ -147,7 +146,7 @@ interface Timeline : AutoCloseable {
|
|||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result<Unit>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue