Split MatrixRoom into BaseRoom and JoinedRoom (#4561)

`JoinedRoom` will now contain both a mandatory live timeline reference and all the functionality associated to it.

`BaseRoom` on the other hand will contain only functionality that's shared for both joined and not joined rooms.

`NotJoinedRoom` is a wrapper around `RoomPreviewInfo` data and a possible local `BaseRoom`, if it exists.

The `RustRoomFactory` cache is now gone since the persistent event cache should have the same effect.
This commit is contained in:
Jorge Martin Espinosa 2025-04-23 15:53:40 +02:00 committed by GitHub
parent 91cb84ce8d
commit 619aa6f2de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
193 changed files with 2921 additions and 2567 deletions

View file

@ -23,10 +23,11 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.NotJoinedRoom
import io.element.android.libraries.matrix.api.room.RoomInfo
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.room.RoomPreview
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
import io.element.android.libraries.matrix.api.roomlist.RoomListService
@ -52,8 +53,8 @@ interface MatrixClient {
val mediaLoader: MatrixMediaLoader
val sessionCoroutineScope: CoroutineScope
val ignoredUsersFlow: StateFlow<ImmutableList<UserId>>
suspend fun getRoom(roomId: RoomId): MatrixRoom?
suspend fun getPendingRoom(roomId: RoomId): RoomPreview?
suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom?
suspend fun getRoom(roomId: RoomId): BaseRoom?
suspend fun findDM(userId: UserId): RoomId?
suspend fun ignoreUser(userId: UserId): Result<Unit>
suspend fun unignoreUser(userId: UserId): Result<Unit>
@ -147,7 +148,7 @@ interface MatrixClient {
/**
* 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.
*/
suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview>
suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<NotJoinedRoom>
/**
* Returns the currently used sliding sync version.
@ -168,7 +169,7 @@ interface MatrixClient {
* The flow will emit a new value whenever the room info is updated.
* The flow will emit Optional.empty item if the room is not found.
*/
fun MatrixClient.getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow<Optional<MatrixRoomInfo>> {
fun MatrixClient.getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow<Optional<RoomInfo>> {
return getRoomSummaryFlow(roomIdOrAlias)
.map { roomSummary -> roomSummary.map { it.info } }
.distinctUntilChanged()

View file

@ -8,11 +8,11 @@
package io.element.android.libraries.matrix.api.analytics
import im.vector.app.features.analytics.plan.ViewRoom
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.BaseRoom
fun MatrixRoom.toAnalyticsViewRoom(
fun BaseRoom.toAnalyticsViewRoom(
trigger: ViewRoom.Trigger? = null,
selectedSpace: MatrixRoom? = null,
selectedSpace: BaseRoom? = null,
viaKeyboard: Boolean? = null,
): ViewRoom {
val activeSpace = selectedSpace?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home
@ -26,6 +26,6 @@ fun MatrixRoom.toAnalyticsViewRoom(
)
}
private fun MatrixRoom.toActiveSpace(): ViewRoom.ActiveSpace {
private fun BaseRoom.toActiveSpace(): ViewRoom.ActiveSpace {
return if (info().isPublic) ViewRoom.ActiveSpace.Public else ViewRoom.ActiveSpace.Private
}

View file

@ -8,14 +8,14 @@
package io.element.android.libraries.matrix.api.notificationsettings
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsState
import kotlinx.coroutines.flow.SharedFlow
interface NotificationSettingsService {
/**
* State of the current room notification settings flow ([MatrixRoomNotificationSettingsState.Unknown] if not started).
* State of the current room notification settings flow ([RoomNotificationSettingsState.Unknown] if not started).
*/
val notificationSettingsChangeFlow: SharedFlow<Unit>
suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings>

View file

@ -0,0 +1,231 @@
/*
* Copyright 2023, 2024 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
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow
import java.io.Closeable
/**
* This interface represents the common functionality for a local room, whether it's joined, invited, knocked, or left.
*/
interface BaseRoom : Closeable {
/**
* The session id of the current user.
*/
val sessionId: SessionId
/**
* The id of the room.
*/
val roomId: RoomId
/**
* The coroutine scope that will handle all jobs related to this room.
*/
val roomCoroutineScope: CoroutineScope
/**
* The current loaded members as a StateFlow.
* Initial value is [RoomMembersState.Unknown].
* To update them you should call [updateMembers].
*/
val membersStateFlow: StateFlow<RoomMembersState>
/**
* A flow that emits the current [RoomInfo] state.
*/
val roomInfoFlow: StateFlow<RoomInfo>
/**
* Get the latest room info we have received from the SDK stream.
*/
fun info(): RoomInfo = roomInfoFlow.value
/**
* Try to load the room members and update the membersFlow.
*/
suspend fun updateMembers()
/**
* Get the members of the room. Note: generally this should not be used, please use
* [membersStateFlow] and [updateMembers] instead.
*/
suspend fun getMembers(limit: Int = 5): Result<List<RoomMember>>
/**
* Will return an updated member or an error.
*/
suspend fun getUpdatedMember(userId: UserId): Result<RoomMember>
/**
* Adds the room to the sync subscription list.
*/
suspend fun subscribeToSync()
/**
* Gets the power levels of the room.
*/
suspend fun powerLevels(): Result<RoomPowerLevels>
/**
* Gets the role of the user with the provided [userId] in the room.
*/
suspend fun userRole(userId: UserId): Result<RoomMember.Role>
/**
* Gets the display name of the user with the provided [userId] in the room.
*/
suspend fun userDisplayName(userId: UserId): Result<String?>
/**
* Gets the avatar of the user with the provided [userId] in the room.
*/
suspend fun userAvatarUrl(userId: UserId): Result<String?>
/**
* Leaves and forgets the room. Only joined, invited or knocked rooms can be left.
*/
suspend fun leave(): Result<Unit>
/**
* Joins the room. Only invited rooms can be joined.
*/
suspend fun join(): Result<Unit>
/**
* Forgets about the room, removing it from the server and the local cache. Only left and banned rooms can be forgotten.
*/
suspend fun forget(): Result<Unit>
/**
* Returns `true` if the user with the provided [userId] can invite other users to the room.
*/
suspend fun canUserInvite(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can kick other users from the room.
*/
suspend fun canUserKick(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can ban other users from the room.
*/
suspend fun canUserBan(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can redact their own messages.
*/
suspend fun canUserRedactOwn(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can redact messages from other users.
*/
suspend fun canUserRedactOther(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can send state events.
*/
suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can send messages.
*/
suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can trigger an `@room` notification.
*/
suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can pin or unpin messages.
*/
suspend fun canUserPinUnpin(userId: UserId): Result<Boolean>
/**
* Returns `true` if the user with the provided [userId] can join or starts calls.
*/
suspend fun canUserJoinCall(userId: UserId): Result<Boolean> =
canUserSendState(userId, StateEventType.CALL_MEMBER)
/**
* Sets the room as favorite or not, based on the [isFavorite] parameter.
*/
suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit>
/**
* Mark the room as read by trying to attach an unthreaded read receipt to the latest room event.
* @param receiptType The type of receipt to send.
*/
suspend fun markAsRead(receiptType: ReceiptType): Result<Unit>
/**
* Sets a flag on the room to indicate that the user has explicitly marked it as unread, or reverts the flag.
* @param isUnread true to mark the room as unread, false to remove the flag.
*/
suspend fun setUnreadFlag(isUnread: Boolean): Result<Unit>
/**
* Clear the event cache storage for the current room.
*/
suspend fun clearEventCacheStorage(): Result<Unit>
/**
* Get the permalink for the room.
*/
suspend fun getPermalink(): Result<String>
/**
* Get the permalink for the provided [eventId].
* @param eventId The event id to get the permalink for.
* @return The permalink, or a failure.
*/
suspend fun getPermalinkFor(eventId: EventId): Result<String>
/**
* Returns the visibility for this room in the room directory.
* If the room is not published, the result will be [RoomVisibility.Private].
*/
suspend fun getRoomVisibility(): Result<RoomVisibility>
/**
* Returns the visibility for this room in the room directory, fetching it from the homeserver if needed.
*/
suspend fun getUpdatedIsEncrypted(): Result<Boolean>
/**
* Store the given `ComposerDraft` in the state store of this room.
*/
suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit>
/**
* Retrieve the `ComposerDraft` stored in the state store for this room.
*/
suspend fun loadComposerDraft(): Result<ComposerDraft?>
/**
* Clear the `ComposerDraft` stored in the state store for this room.
*/
suspend fun clearComposerDraft(): Result<Unit>
/**
* Destroy the room and release all resources associated to it.
*/
fun destroy()
override fun close() = destroy()
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* 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.
@ -13,7 +13,6 @@ import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SendHandle
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
@ -23,35 +22,28 @@ import io.element.android.libraries.matrix.api.media.ImageInfo
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.draft.ComposerDraft
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
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.room.powerlevels.MatrixRoomPowerLevels
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import java.io.Closeable
import java.io.File
interface MatrixRoom : Closeable {
val sessionId: SessionId
val roomId: RoomId
interface JoinedRoom : BaseRoom {
val syncUpdateFlow: StateFlow<Long>
val roomCoroutineScope: CoroutineScope
val roomInfoFlow: StateFlow<MatrixRoomInfo>
val roomTypingMembersFlow: Flow<List<UserId>>
val identityStateChangesFlow: Flow<List<IdentityStateChange>>
val roomNotificationSettingsStateFlow: StateFlow<RoomNotificationSettingsState>
/**
* The current knock requests in the room as a Flow.
@ -64,40 +56,6 @@ interface MatrixRoom : Closeable {
*/
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
/**
* The current loaded members as a StateFlow.
* Initial value is [MatrixRoomMembersState.Unknown].
* To update them you should call [updateMembers].
*/
val membersStateFlow: StateFlow<MatrixRoomMembersState>
val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState>
/**
* Get the latest room info we have received from the SDK stream.
*/
fun info(): MatrixRoomInfo = roomInfoFlow.value
/**
* Try to load the room members and update the membersFlow.
*/
suspend fun updateMembers()
/**
* Get the members of the room. Note: generally this should not be used, please use
* [membersStateFlow] and [updateMembers] instead.
*/
suspend fun getMembers(limit: Int = 5): Result<List<RoomMember>>
/**
* Will return an updated member or an error.
*/
suspend fun getUpdatedMember(userId: UserId): Result<RoomMember>
suspend fun updateRoomNotificationSettings(): Result<Unit>
val syncUpdateFlow: StateFlow<Long>
/**
* The live timeline of the room. Must be used to send Event to a room.
*/
@ -111,24 +69,6 @@ interface MatrixRoom : Closeable {
createTimelineParams: CreateTimelineParams,
): Result<Timeline>
fun destroy()
suspend fun subscribeToSync()
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>
suspend fun userDisplayName(userId: UserId): Result<String?>
suspend fun userAvatarUrl(userId: UserId): Result<String?>
suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
@ -198,75 +138,6 @@ interface MatrixRoom : Closeable {
assetType: AssetType? = null,
): Result<Unit>
suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit>
suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>
suspend fun cancelSend(transactionId: TransactionId): Result<Unit>
suspend fun leave(): Result<Unit>
suspend fun join(): Result<Unit>
suspend fun inviteUserById(id: UserId): Result<Unit>
suspend fun canUserInvite(userId: UserId): Result<Boolean>
suspend fun canUserKick(userId: UserId): Result<Boolean>
suspend fun canUserBan(userId: UserId): Result<Boolean>
suspend fun canUserRedactOwn(userId: UserId): Result<Boolean>
suspend fun canUserRedactOther(userId: UserId): Result<Boolean>
suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean>
suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean>
suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean>
suspend fun canUserPinUnpin(userId: UserId): Result<Boolean>
suspend fun canUserJoinCall(userId: UserId): Result<Boolean> =
canUserSendState(userId, StateEventType.CALL_MEMBER)
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>
suspend fun removeAvatar(): Result<Unit>
suspend fun setName(name: String): Result<Unit>
suspend fun setTopic(topic: String): Result<Unit>
suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit>
suspend fun kickUser(userId: UserId, reason: String? = null): Result<Unit>
suspend fun banUser(userId: UserId, reason: String? = null): Result<Unit>
suspend fun unbanUser(userId: UserId, reason: String? = null): Result<Unit>
suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit>
/**
* Mark the room as read by trying to attach an unthreaded read receipt to the latest room event.
* @param receiptType The type of receipt to send.
*/
suspend fun markAsRead(receiptType: ReceiptType): Result<Unit>
/**
* Sets a flag on the room to indicate that the user has explicitly marked it as unread, or reverts the flag.
* @param isUnread true to mark the room as unread, false to remove the flag.
*
*/
suspend fun setUnreadFlag(isUnread: Boolean): Result<Unit>
/**
* Clear the event cache storage for the current room.
*/
suspend fun clearEventCacheStorage(): Result<Unit>
/**
* Create a poll in the room.
*
@ -321,82 +192,19 @@ interface MatrixRoom : Closeable {
*/
suspend fun typingNotice(isTyping: Boolean): Result<Unit>
/**
* Generates a Widget url to display in a [android.webkit.WebView] given the provided parameters.
* @param widgetSettings The widget settings to use.
* @param clientId The client id to use. It should be unique per app install.
* @param languageTag The language tag to use. If null, the default language will be used.
* @param theme The theme to use. If null, the default theme will be used.
* @return The resulting url, or a failure.
*/
suspend fun generateWidgetWebViewUrl(
widgetSettings: MatrixWidgetSettings,
clientId: String,
languageTag: String?,
theme: String?,
): Result<String>
suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit>
/**
* Get a [MatrixWidgetDriver] for the provided [widgetSettings].
* @param widgetSettings The widget settings to use.
* @return The resulting [MatrixWidgetDriver], or a failure.
*/
fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver>
suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>
/**
* Get the permalink for the room.
*/
suspend fun getPermalink(): Result<String>
suspend fun cancelSend(transactionId: TransactionId): Result<Unit>
/**
* Get the permalink for the provided [eventId].
* @param eventId The event id to get the permalink for.
* @return The permalink, or a failure.
*/
suspend fun getPermalinkFor(eventId: EventId): Result<String>
suspend fun inviteUserById(id: UserId): Result<Unit>
/**
* Send an Element Call started notification if needed.
*/
suspend fun sendCallNotificationIfNeeded(): Result<Unit>
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>
suspend fun setSendQueueEnabled(enabled: Boolean)
suspend fun removeAvatar(): Result<Unit>
/**
* Store the given `ComposerDraft` in the state store of this room.
*/
suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit>
/**
* Retrieve the `ComposerDraft` stored in the state store for this room.
*/
suspend fun loadComposerDraft(): Result<ComposerDraft?>
/**
* Clear the `ComposerDraft` stored in the state store for this room.
*/
suspend fun clearComposerDraft(): Result<Unit>
/**
* Ignore the local trust for the given devices and resend messages that failed to send because said devices are unverified.
*
* @param devices The map of users identifiers to device identifiers received in the error
* @param sendHandle The send queue handle of the local echo the send error applies to. It can be used to retry the upload.
*
*/
suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle): Result<Unit>
/**
* Remove verification requirements for the given users and
* resend messages that failed to send because their identities were no longer verified.
*
* @param userIds The list of users identifiers received in the error.
* @param sendHandle The send queue handle of the local echo the send error applies to. It can be used to retry the upload.
*
*/
suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle): Result<Unit>
override fun close() = destroy()
suspend fun updateRoomNotificationSettings(): Result<Unit>
/**
* Update the canonical alias of the room.
@ -418,12 +226,6 @@ interface MatrixRoom : Closeable {
*/
suspend fun updateHistoryVisibility(historyVisibility: RoomHistoryVisibility): Result<Unit>
/**
* Returns the visibility for this room in the room directory.
* If the room is not published, the result will be [RoomVisibility.Private].
*/
suspend fun getRoomVisibility(): Result<RoomVisibility>
/**
* Publish a new room alias for this room in the room directory.
*
@ -454,5 +256,69 @@ interface MatrixRoom : Closeable {
*/
suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit>
suspend fun getUpdatedIsEncrypted(): Result<Boolean>
suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit>
suspend fun updatePowerLevels(roomPowerLevels: RoomPowerLevels): Result<Unit>
suspend fun resetPowerLevels(): Result<RoomPowerLevels>
suspend fun setName(name: String): Result<Unit>
suspend fun setTopic(topic: String): Result<Unit>
suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit>
suspend fun kickUser(userId: UserId, reason: String? = null): Result<Unit>
suspend fun banUser(userId: UserId, reason: String? = null): Result<Unit>
suspend fun unbanUser(userId: UserId, reason: String? = null): Result<Unit>
/**
* Generates a Widget url to display in a [android.webkit.WebView] given the provided parameters.
* @param widgetSettings The widget settings to use.
* @param clientId The client id to use. It should be unique per app install.
* @param languageTag The language tag to use. If null, the default language will be used.
* @param theme The theme to use. If null, the default theme will be used.
* @return The resulting url, or a failure.
*/
suspend fun generateWidgetWebViewUrl(
widgetSettings: MatrixWidgetSettings,
clientId: String,
languageTag: String?,
theme: String?,
): Result<String>
/**
* Get a [MatrixWidgetDriver] for the provided [widgetSettings].
* @param widgetSettings The widget settings to use.
* @return The resulting [MatrixWidgetDriver], or a failure.
*/
fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver>
/**
* Send an Element Call started notification if needed.
*/
suspend fun sendCallNotificationIfNeeded(): Result<Unit>
suspend fun setSendQueueEnabled(enabled: Boolean)
/**
* Ignore the local trust for the given devices and resend messages that failed to send because said devices are unverified.
*
* @param devices The map of users identifiers to device identifiers received in the error
* @param sendHandle The send queue handle of the local echo the send error applies to. It can be used to retry the upload.
*
*/
suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle): Result<Unit>
/**
* Remove verification requirements for the given users and
* resend messages that failed to send because their identities were no longer verified.
*
* @param userIds The list of users identifiers received in the error.
* @param sendHandle The send queue handle of the local echo the send error applies to. It can be used to retry the upload.
*
*/
suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle): Result<Unit>
}

View file

@ -1,24 +0,0 @@
/*
* Copyright 2023, 2024 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
sealed interface MatrixRoomNotificationSettingsState {
data object Unknown : MatrixRoomNotificationSettingsState
data class Pending(val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
data class Error(val failure: Throwable, val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
data class Ready(val roomNotificationSettings: RoomNotificationSettings) : MatrixRoomNotificationSettingsState
}
fun MatrixRoomNotificationSettingsState.roomNotificationSettings(): RoomNotificationSettings? {
return when (this) {
is MatrixRoomNotificationSettingsState.Ready -> roomNotificationSettings
is MatrixRoomNotificationSettingsState.Pending -> prevRoomNotificationSettings
is MatrixRoomNotificationSettingsState.Error -> prevRoomNotificationSettings
else -> null
}
}

View file

@ -7,21 +7,12 @@
package io.element.android.libraries.matrix.api.room
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
/** A reference to a room either invited, knocked or banned. */
interface RoomPreview : AutoCloseable {
val sessionId: SessionId
val info: RoomPreviewInfo
/** Leave the room ie.decline invite or cancel knock. */
suspend fun leave(): Result<Unit>
/**
* Forget the room if we had access to it, and it was left or banned.
*/
suspend fun forget(): Result<Unit>
interface NotJoinedRoom : AutoCloseable {
val previewInfo: RoomPreviewInfo
val localRoom: BaseRoom?
/**
* Get the membership details of the user in the room, as well as from the user who sent the `m.room.member` event.

View file

@ -19,7 +19,7 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
@Immutable
data class MatrixRoomInfo(
data class RoomInfo(
val id: RoomId,
/** The room's name from the room state event if received from sync, or one that's been computed otherwise. */
val name: String?,

View file

@ -21,11 +21,11 @@ fun isDm(isDirect: Boolean, activeMembersCount: Int): Boolean {
}
/**
* Returns whether the [MatrixRoom] is a DM, with an updated state from the latest [MatrixRoomInfo].
* Returns whether the [BaseRoom] is a DM, with an updated state from the latest [RoomInfo].
*/
suspend fun MatrixRoom.isDm() = roomInfoFlow.first().isDm
suspend fun BaseRoom.isDm() = roomInfoFlow.first().isDm
/**
* Returns whether the [MatrixRoomInfo] is from a DM.
* Returns whether the [RoomInfo] is from a DM.
*/
val MatrixRoomInfo.isDm get() = isDm(isDirect, activeMembersCount.toInt())
val RoomInfo.isDm get() = isDm(isDirect, activeMembersCount.toInt())

View file

@ -11,26 +11,26 @@ import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
@Immutable
sealed interface MatrixRoomMembersState {
data object Unknown : MatrixRoomMembersState
data class Pending(val prevRoomMembers: ImmutableList<RoomMember>? = null) : MatrixRoomMembersState
data class Error(val failure: Throwable, val prevRoomMembers: ImmutableList<RoomMember>? = null) : MatrixRoomMembersState
data class Ready(val roomMembers: ImmutableList<RoomMember>) : MatrixRoomMembersState
sealed interface RoomMembersState {
data object Unknown : RoomMembersState
data class Pending(val prevRoomMembers: ImmutableList<RoomMember>? = null) : RoomMembersState
data class Error(val failure: Throwable, val prevRoomMembers: ImmutableList<RoomMember>? = null) : RoomMembersState
data class Ready(val roomMembers: ImmutableList<RoomMember>) : RoomMembersState
}
fun MatrixRoomMembersState.roomMembers(): List<RoomMember>? {
fun RoomMembersState.roomMembers(): List<RoomMember>? {
return when (this) {
is MatrixRoomMembersState.Ready -> roomMembers
is MatrixRoomMembersState.Pending -> prevRoomMembers
is MatrixRoomMembersState.Error -> prevRoomMembers
is RoomMembersState.Ready -> roomMembers
is RoomMembersState.Pending -> prevRoomMembers
is RoomMembersState.Error -> prevRoomMembers
else -> null
}
}
fun MatrixRoomMembersState.joinedRoomMembers(): List<RoomMember> {
fun RoomMembersState.joinedRoomMembers(): List<RoomMember> {
return roomMembers().orEmpty().filter { it.membership == RoomMembershipState.JOIN }
}
fun MatrixRoomMembersState.activeRoomMembers(): List<RoomMember> {
fun RoomMembersState.activeRoomMembers(): List<RoomMember> {
return roomMembers().orEmpty().filter { it.membership.isActive() }
}

View file

@ -0,0 +1,24 @@
/*
* Copyright 2023, 2024 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
sealed interface RoomNotificationSettingsState {
data object Unknown : RoomNotificationSettingsState
data class Pending(val prevRoomNotificationSettings: RoomNotificationSettings? = null) : RoomNotificationSettingsState
data class Error(val failure: Throwable, val prevRoomNotificationSettings: RoomNotificationSettings? = null) : RoomNotificationSettingsState
data class Ready(val roomNotificationSettings: RoomNotificationSettings) : RoomNotificationSettingsState
}
fun RoomNotificationSettingsState.roomNotificationSettings(): RoomNotificationSettings? {
return when (this) {
is RoomNotificationSettingsState.Ready -> roomNotificationSettings
is RoomNotificationSettingsState.Pending -> prevRoomNotificationSettings
is RoomNotificationSettingsState.Error -> prevRoomNotificationSettings
else -> null
}
}

View file

@ -8,12 +8,12 @@
package io.element.android.libraries.matrix.api.room.alias
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.BaseRoom
/**
* Return true if the given roomIdOrAlias is the same room as this room.
*/
fun MatrixRoom.matches(roomIdOrAlias: RoomIdOrAlias): Boolean {
fun BaseRoom.matches(roomIdOrAlias: RoomIdOrAlias): Boolean {
return when (roomIdOrAlias) {
is RoomIdOrAlias.Id -> {
roomIdOrAlias.roomId == roomId

View file

@ -7,7 +7,7 @@
package io.element.android.libraries.matrix.api.room.powerlevels
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.activeRoomMembers
import kotlinx.collections.immutable.ImmutableList
@ -20,7 +20,7 @@ import kotlinx.coroutines.flow.map
/**
* Return a flow of the list of active room members who have the given role.
*/
fun MatrixRoom.usersWithRole(role: RoomMember.Role): Flow<ImmutableList<RoomMember>> {
fun BaseRoom.usersWithRole(role: RoomMember.Role): Flow<ImmutableList<RoomMember>> {
return roomInfoFlow
.map { it.userPowerLevels.filter { (_, powerLevel) -> RoomMember.Role.forPowerLevel(powerLevel) == role } }
.combine(membersStateFlow) { powerLevels, membersState ->

View file

@ -1,70 +0,0 @@
/*
* Copyright 2023, 2024 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.powerlevels
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.
*/
suspend fun MatrixRoom.canInvite(): Result<Boolean> = canUserInvite(sessionId)
/**
* Shortcut for calling [MatrixRoom.canUserKick] with our own user.
*/
suspend fun MatrixRoom.canKick(): Result<Boolean> = canUserKick(sessionId)
/**
* Shortcut for calling [MatrixRoom.canUserBan] with our own user.
*/
suspend fun MatrixRoom.canBan(): Result<Boolean> = canUserBan(sessionId)
/**
* Shortcut for calling [MatrixRoom.canUserSendState] with our own user.
*/
suspend fun MatrixRoom.canSendState(type: StateEventType): Result<Boolean> = canUserSendState(sessionId, type)
/**
* Shortcut for calling [MatrixRoom.canUserSendMessage] with our own user.
*/
suspend fun MatrixRoom.canSendMessage(type: MessageEventType): Result<Boolean> = canUserSendMessage(sessionId, type)
/**
* Shortcut for calling [MatrixRoom.canUserRedactOwn] with our own user.
*/
suspend fun MatrixRoom.canRedactOwn(): Result<Boolean> = canUserRedactOwn(sessionId)
/**
* Shortcut for calling [MatrixRoom.canRedactOther] with our own user.
*/
suspend fun MatrixRoom.canRedactOther(): Result<Boolean> = canUserRedactOther(sessionId)
/**
* Shortcut for checking if current user can handle knock requests.
*/
suspend fun MatrixRoom.canHandleKnockRequests(): Result<Boolean> = runCatching {
canInvite().getOrThrow() || canBan().getOrThrow() || canKick().getOrThrow()
}
/**
* Shortcut for calling [MatrixRoom.canUserPinUnpin] with our own user.
*/
suspend fun MatrixRoom.canPinUnpin(): Result<Boolean> = canUserPinUnpin(sessionId)

View file

@ -0,0 +1,70 @@
/*
* Copyright 2023, 2024 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.powerlevels
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.StateEventType
data class RoomPowerLevels(
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 [BaseRoom.canUserInvite] with our own user.
*/
suspend fun BaseRoom.canInvite(): Result<Boolean> = canUserInvite(sessionId)
/**
* Shortcut for calling [BaseRoom.canUserKick] with our own user.
*/
suspend fun BaseRoom.canKick(): Result<Boolean> = canUserKick(sessionId)
/**
* Shortcut for calling [BaseRoom.canUserBan] with our own user.
*/
suspend fun BaseRoom.canBan(): Result<Boolean> = canUserBan(sessionId)
/**
* Shortcut for calling [BaseRoom.canUserSendState] with our own user.
*/
suspend fun BaseRoom.canSendState(type: StateEventType): Result<Boolean> = canUserSendState(sessionId, type)
/**
* Shortcut for calling [BaseRoom.canUserSendMessage] with our own user.
*/
suspend fun BaseRoom.canSendMessage(type: MessageEventType): Result<Boolean> = canUserSendMessage(sessionId, type)
/**
* Shortcut for calling [BaseRoom.canUserRedactOwn] with our own user.
*/
suspend fun BaseRoom.canRedactOwn(): Result<Boolean> = canUserRedactOwn(sessionId)
/**
* Shortcut for calling [BaseRoom.canRedactOther] with our own user.
*/
suspend fun BaseRoom.canRedactOther(): Result<Boolean> = canUserRedactOther(sessionId)
/**
* Shortcut for checking if current user can handle knock requests.
*/
suspend fun BaseRoom.canHandleKnockRequests(): Result<Boolean> = runCatching {
canInvite().getOrThrow() || canBan().getOrThrow() || canKick().getOrThrow()
}
/**
* Shortcut for calling [BaseRoom.canUserPinUnpin] with our own user.
*/
suspend fun BaseRoom.canPinUnpin(): Result<Boolean> = canUserPinUnpin(sessionId)

View file

@ -10,8 +10,8 @@ package io.element.android.libraries.matrix.api.room.recent
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.toMatrixUser
import io.element.android.libraries.matrix.api.user.MatrixUser
@ -52,6 +52,6 @@ suspend fun MatrixClient.getRecentDirectRooms(
return result
}
suspend fun MatrixRoom.isJoined(): Boolean {
suspend fun BaseRoom.isJoined(): Boolean {
return roomInfoFlow.first().currentUserMembership == CurrentUserMembership.JOINED
}

View file

@ -7,11 +7,11 @@
package io.element.android.libraries.matrix.api.roomlist
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
import io.element.android.libraries.matrix.api.room.RoomInfo
import io.element.android.libraries.matrix.api.room.message.RoomMessage
data class RoomSummary(
val info: MatrixRoomInfo,
val info: RoomInfo,
val lastMessage: RoomMessage?,
) {
val roomId = info.id