feature (room upgrade) : start rendering SuccessorRoom and PredecessorRoom banners in timeline

This commit is contained in:
ganfra 2025-06-04 16:57:17 +02:00
parent 76e1ec05ba
commit 7b75a52ca2
24 changed files with 255 additions and 89 deletions

View file

@ -39,7 +39,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun ComposerAlertMolecule(
avatar: AvatarData,
avatar: AvatarData?,
content: AnnotatedString,
onSubmitClick: () -> Unit,
modifier: Modifier = Modifier,
@ -71,9 +71,9 @@ fun ComposerAlertMolecule(
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Avatar(
avatarData = avatar,
)
if(avatar != null) {
Avatar(avatarData = avatar)
}
Text(
text = content,
modifier = Modifier.weight(1f),
@ -101,7 +101,7 @@ fun ComposerAlertMolecule(
@Composable
internal fun ComposerAlertMoleculePreview(@PreviewParameter(BooleanProvider::class) isCritical: Boolean) = ElementPreview {
ComposerAlertMolecule(
avatar = anAvatarData(size = AvatarSize.ComposerAlert),
avatar = null,
content = "Alices verified identity has changed. Learn more".toAnnotatedString(),
isCritical = isCritical,
onSubmitClick = {},

View file

@ -13,6 +13,7 @@ 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.room.tombstone.PredecessorRoom
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import kotlinx.coroutines.CoroutineScope
@ -55,6 +56,8 @@ interface BaseRoom : Closeable {
*/
fun info(): RoomInfo = roomInfoFlow.value
fun predecessorRoom(): PredecessorRoom?
/**
* A one-to-one is a room with exactly 2 members.
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
@ -234,7 +237,6 @@ interface BaseRoom : Closeable {
* @param reason - The reason the room is being reported.
*/
suspend fun reportRoom(reason: String?): Result<Unit>
/**
* Destroy the room and release all resources associated to it.
*/

View file

@ -14,6 +14,7 @@ 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.history.RoomHistoryVisibility
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap

View file

@ -0,0 +1,31 @@
/*
* 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.tombstone
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
/**
*
* When a room A is tombstoned, it is replaced by a room B. The room A is the
* predecessor of B, and B is the successor of A. This type holds information
* 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,
)

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.room
package io.element.android.libraries.matrix.api.room.tombstone
import io.element.android.libraries.matrix.api.core.RoomId

View file

@ -14,11 +14,12 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.RoomInfo
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.SuccessorRoom
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.impl.room.history.map
import io.element.android.libraries.matrix.impl.room.join.map
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
import io.element.android.libraries.matrix.impl.room.tombstone.map
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentMap
@ -88,11 +89,6 @@ fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) {
RustRoomNotificationMode.MUTE -> RoomNotificationMode.MUTE
}
fun RustSuccessorRoom.map(): SuccessorRoom = SuccessorRoom(
roomId = RoomId(roomId),
reason = reason,
)
/**
* Map a RoomHero to a MatrixUser. There is not need to create a RoomHero type on the application side.
*/

View file

@ -24,12 +24,14 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.room.StateEventType
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.room.tombstone.PredecessorRoom
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.impl.room.draft.into
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.room.tombstone.map
import io.element.android.libraries.matrix.impl.roomdirectory.map
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
@ -78,6 +80,10 @@ class RustBaseRoom(
})
}.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo)
override fun predecessorRoom(): PredecessorRoom? {
return innerRoom.predecessorRoom()?.map()
}
override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
override suspend fun updateMembers() {

View file

@ -0,0 +1,20 @@
/*
* 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.impl.room.tombstone
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.room.tombstone.PredecessorRoom
import org.matrix.rustcomponents.sdk.PredecessorRoom as RustPredecessorRoom
fun RustPredecessorRoom.map(): PredecessorRoom {
return PredecessorRoom(
roomId = RoomId(roomId),
lastEventId = EventId(lastEventId),
)
}

View file

@ -0,0 +1,19 @@
/*
* 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.impl.room.tombstone
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
import org.matrix.rustcomponents.sdk.SuccessorRoom as RustSuccessorRoom
fun RustSuccessorRoom.map(): SuccessorRoom {
return SuccessorRoom(
roomId = RoomId(roomId),
reason = reason
)
}

View file

@ -27,6 +27,11 @@ import org.matrix.rustcomponents.sdk.RoomListService
import kotlin.coroutines.CoroutineContext
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
private val ROOM_LIST_RUST_FILTERS = listOf(
RoomListEntriesDynamicFilterKind.NonLeft,
RoomListEntriesDynamicFilterKind.DeduplicateVersions
)
internal class RoomListFactory(
private val innerRoomListService: RoomListService,
private val sessionCoroutineScope: CoroutineScope,
@ -55,11 +60,11 @@ internal class RoomListFactory(
coroutineScope.launch(coroutineContext) {
innerRoomList = innerProvider()
innerRoomList?.let { innerRoomList ->
innerRoomList.let { innerRoomList ->
innerRoomList.entriesFlow(
pageSize = pageSize,
roomListDynamicEvents = dynamicEvents,
initialFilterKind = RoomListEntriesDynamicFilterKind.NonLeft
initialFilterKind = RoomListEntriesDynamicFilterKind.All(ROOM_LIST_RUST_FILTERS),
).onEach { update ->
processor.postUpdate(update)
}.launchIn(this)

View file

@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.RoomInfo
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.SuccessorRoom
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
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.user.MatrixUser

View file

@ -15,7 +15,7 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.RoomInfo
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.SuccessorRoom
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
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.message.RoomMessage