Upgrade Rust bindings to v25.04.11 (#4580)

* Upgrade Rust bindings to `v25.04.11`

* Rename `mapMessageKind` back to `mapMessageType`

* Fix tests and fixtures
This commit is contained in:
Jorge Martin Espinosa 2025-04-11 18:22:16 +02:00 committed by GitHub
parent 4dda027ed4
commit 73b4ac8c12
14 changed files with 90 additions and 64 deletions

View file

@ -47,7 +47,7 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
), ),
aJoinRoomState( aJoinRoomState(
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin), contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin),
joinAction = AsyncAction.Failure(ClientException.Generic("Something went wrong")) joinAction = AsyncAction.Failure(ClientException.Generic("Something went wrong", null))
), ),
aJoinRoomState( aJoinRoomState(
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null)) contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null))

View file

@ -250,7 +250,7 @@ class JoinRoomPresenterTest {
val presenter = createJoinRoomPresenter( val presenter = createJoinRoomPresenter(
roomDescription = Optional.of(roomDescription), roomDescription = Optional.of(roomDescription),
joinRoomLambda = { _, _, _ -> joinRoomLambda = { _, _, _ ->
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden")) Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden", null))
}, },
) )
presenter.test { presenter.test {
@ -742,7 +742,7 @@ class JoinRoomPresenterTest {
fun `present - when room is not known RoomPreview is loaded with error Forbidden`() = runTest { fun `present - when room is not known RoomPreview is loaded with error Forbidden`() = runTest {
val client = FakeMatrixClient( val client = FakeMatrixClient(
getRoomPreviewResult = { _, _ -> getRoomPreviewResult = { _, _ ->
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden")) Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden", null))
} }
) )
val presenter = createJoinRoomPresenter( val presenter = createJoinRoomPresenter(

View file

@ -18,7 +18,7 @@ open class RoomAliasResolverStateProvider : PreviewParameterProvider<RoomAliasRe
get() = sequenceOf( get() = sequenceOf(
aRoomAliasResolverState(), aRoomAliasResolverState(),
aRoomAliasResolverState( aRoomAliasResolverState(
resolveState = AsyncData.Failure(ClientException.Generic("Something went wrong")), resolveState = AsyncData.Failure(ClientException.Generic("Something went wrong", null)),
), ),
aRoomAliasResolverState( aRoomAliasResolverState(
resolveState = AsyncData.Failure(RoomAliasResolverFailures.UnknownAlias), resolveState = AsyncData.Failure(RoomAliasResolverFailures.UnknownAlias),

View file

@ -174,7 +174,7 @@ jsoup = "org.jsoup:jsoup:1.19.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.4.8" matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.4.11"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }

View file

@ -7,10 +7,10 @@
package io.element.android.libraries.matrix.api.exception package io.element.android.libraries.matrix.api.exception
sealed class ClientException(message: String) : Exception(message) { sealed class ClientException(message: String, val details: String?) : Exception(message) {
class Generic(message: String) : ClientException(message) class Generic(message: String, details: String?) : ClientException(message, details)
class MatrixApi(val kind: ErrorKind, val code: String, message: String) : ClientException(message) class MatrixApi(val kind: ErrorKind, val code: String, message: String, details: String?) : ClientException(message, details)
class Other(message: String) : ClientException(message) class Other(message: String) : ClientException(message, null)
} }
fun ClientException.isNetworkError(): Boolean { fun ClientException.isNetworkError(): Boolean {

View file

@ -14,11 +14,12 @@ fun Throwable.mapClientException(): ClientException {
return when (this) { return when (this) {
is RustClientException -> { is RustClientException -> {
when (this) { when (this) {
is RustClientException.Generic -> ClientException.Generic(msg) is RustClientException.Generic -> ClientException.Generic(msg, details)
is RustClientException.MatrixApi -> ClientException.MatrixApi( is RustClientException.MatrixApi -> ClientException.MatrixApi(
kind = kind.map(), kind = kind.map(),
code = code, code = code,
message = msg message = msg,
details = details,
) )
} }
} }

View file

@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.MsgLikeKind
import org.matrix.rustcomponents.sdk.RoomListService import org.matrix.rustcomponents.sdk.RoomListService
import org.matrix.rustcomponents.sdk.Timeline import org.matrix.rustcomponents.sdk.Timeline
import org.matrix.rustcomponents.sdk.TimelineItemContent import org.matrix.rustcomponents.sdk.TimelineItemContent
@ -42,7 +43,10 @@ class RoomContentForwarder(
toRoomIds: List<RoomId>, toRoomIds: List<RoomId>,
timeoutMs: Long = 5000L timeoutMs: Long = 5000L
) { ) {
val content = (fromTimeline.getEventTimelineItemByEventId(eventId.value).content as? TimelineItemContent.Message)?.content val messageLikeContent = (fromTimeline.getEventTimelineItemByEventId(eventId.value).content as? TimelineItemContent.MsgLike)?.content
?: throw ForwardEventException(toRoomIds)
val content = (messageLikeContent.kind as? MsgLikeKind.Message)?.content
?: throw ForwardEventException(toRoomIds) ?: throw ForwardEventException(toRoomIds)
val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) }

View file

@ -48,8 +48,8 @@ class RustRoomPreview(
override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = runCatching { override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = runCatching {
val details = inner.ownMembershipDetails() ?: return@runCatching null val details = inner.ownMembershipDetails() ?: return@runCatching null
RoomMembershipDetails( RoomMembershipDetails(
currentUserMember = RoomMemberMapper.map(details.ownRoomMember), currentUserMember = RoomMemberMapper.map(details.roomMember),
senderMember = details.senderRoomMember?.let { RoomMemberMapper.map(it) }, senderMember = details.senderInfo?.let { RoomMemberMapper.map(it) },
) )
} }

View file

@ -23,24 +23,25 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageT
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper
import org.matrix.rustcomponents.sdk.InReplyToDetails
import org.matrix.rustcomponents.sdk.MessageType import org.matrix.rustcomponents.sdk.MessageType
import org.matrix.rustcomponents.sdk.MsgLikeKind
import org.matrix.rustcomponents.sdk.use import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody
import org.matrix.rustcomponents.sdk.MessageContent as Message
import org.matrix.rustcomponents.sdk.MessageFormat as RustMessageFormat import org.matrix.rustcomponents.sdk.MessageFormat as RustMessageFormat
import org.matrix.rustcomponents.sdk.MessageType as RustMessageType import org.matrix.rustcomponents.sdk.MessageType as RustMessageType
class EventMessageMapper { class EventMessageMapper {
private val inReplyToMapper by lazy { InReplyToMapper(TimelineEventContentMapper()) } private val inReplyToMapper by lazy { InReplyToMapper(TimelineEventContentMapper()) }
fun map(message: Message): MessageContent = message.use { fun map(message: MsgLikeKind.Message, inReplyTo: InReplyToDetails?, isThreaded: Boolean): MessageContent = message.use {
val type = it.msgType.use(this::mapMessageType) val type = it.content.msgType.use(this::mapMessageType)
val inReplyToEvent: InReplyTo? = it.inReplyTo?.use(inReplyToMapper::map) val inReplyToEvent: InReplyTo? = inReplyTo?.use(inReplyToMapper::map)
MessageContent( MessageContent(
body = it.body, body = it.content.body,
inReplyTo = inReplyToEvent, inReplyTo = inReplyToEvent,
isEdited = it.isEdited, isEdited = it.content.isEdited,
isThreaded = it.threadRoot != null, isThreaded = isThreaded,
type = type type = type
) )
} }

View file

@ -28,6 +28,7 @@ import org.matrix.rustcomponents.sdk.EventOrTransactionId
import org.matrix.rustcomponents.sdk.QueueWedgeError import org.matrix.rustcomponents.sdk.QueueWedgeError
import org.matrix.rustcomponents.sdk.Reaction import org.matrix.rustcomponents.sdk.Reaction
import org.matrix.rustcomponents.sdk.ShieldState import org.matrix.rustcomponents.sdk.ShieldState
import org.matrix.rustcomponents.sdk.TimelineItemContent
import uniffi.matrix_sdk_common.ShieldStateCode import uniffi.matrix_sdk_common.ShieldStateCode
import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState
import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem
@ -48,7 +49,7 @@ class EventTimelineItemMapper(
isOwn = isOwn, isOwn = isOwn,
isRemote = isRemote, isRemote = isRemote,
localSendState = localSendState?.map(), localSendState = localSendState?.map(),
reactions = reactions.map(), reactions = (content as? TimelineItemContent.MsgLike)?.content?.reactions.map(),
receipts = readReceipts.map(), receipts = readReceipts.map(),
sender = UserId(sender), sender = UserId(sender),
senderProfile = senderProfile.map(), senderProfile = senderProfile.map(),

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.poll.map import io.element.android.libraries.matrix.impl.poll.map
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import org.matrix.rustcomponents.sdk.MsgLikeKind
import org.matrix.rustcomponents.sdk.TimelineItemContent import org.matrix.rustcomponents.sdk.TimelineItemContent
import org.matrix.rustcomponents.sdk.use import org.matrix.rustcomponents.sdk.use
import uniffi.matrix_sdk_ui.RoomPinnedEventsChange import uniffi.matrix_sdk_ui.RoomPinnedEventsChange
@ -54,8 +55,43 @@ class TimelineEventContentMapper(
error = it.error error = it.error
) )
} }
is TimelineItemContent.Message -> { is TimelineItemContent.MsgLike -> {
eventMessageMapper.map(it.content) when (val kind = it.content.kind) {
is MsgLikeKind.Message -> {
val inReplyTo = it.content.inReplyTo
val isThreaded = it.content.threadRoot != null
eventMessageMapper.map(kind, inReplyTo, isThreaded)
}
is MsgLikeKind.Redacted -> {
RedactedContent
}
is MsgLikeKind.Poll -> {
PollContent(
question = kind.question,
kind = kind.kind.map(),
maxSelections = kind.maxSelections,
answers = kind.answers.map { answer -> answer.map() }.toImmutableList(),
votes = kind.votes.mapValues { vote ->
vote.value.map { userId -> UserId(userId) }.toImmutableList()
}.toImmutableMap(),
endTime = kind.endTime,
isEdited = kind.hasBeenEdited,
)
}
is MsgLikeKind.UnableToDecrypt -> {
UnableToDecryptContent(
data = kind.msg.map()
)
}
is MsgLikeKind.Sticker -> {
StickerContent(
filename = kind.body,
body = null,
info = kind.info.map(),
source = kind.source.map(),
)
}
}
} }
is TimelineItemContent.ProfileChange -> { is TimelineItemContent.ProfileChange -> {
ProfileChangeContent( ProfileChangeContent(
@ -65,9 +101,6 @@ class TimelineEventContentMapper(
prevAvatarUrl = it.prevAvatarUrl prevAvatarUrl = it.prevAvatarUrl
) )
} }
TimelineItemContent.RedactedMessage -> {
RedactedContent
}
is TimelineItemContent.RoomMembership -> { is TimelineItemContent.RoomMembership -> {
RoomMembershipContent( RoomMembershipContent(
userId = UserId(it.userId), userId = UserId(it.userId),
@ -81,32 +114,6 @@ class TimelineEventContentMapper(
content = it.content.map() content = it.content.map()
) )
} }
is TimelineItemContent.Sticker -> {
StickerContent(
filename = it.body,
body = null,
info = it.info.map(),
source = it.source.map(),
)
}
is TimelineItemContent.Poll -> {
PollContent(
question = it.question,
kind = it.kind.map(),
maxSelections = it.maxSelections,
answers = it.answers.map { answer -> answer.map() }.toImmutableList(),
votes = it.votes.mapValues { vote ->
vote.value.map { userId -> UserId(userId) }.toImmutableList()
}.toImmutableMap(),
endTime = it.endTime,
isEdited = it.hasBeenEdited,
)
}
is TimelineItemContent.UnableToDecrypt -> {
UnableToDecryptContent(
data = it.msg.map()
)
}
is TimelineItemContent.CallInvite -> LegacyCallInviteContent is TimelineItemContent.CallInvite -> LegacyCallInviteContent
is TimelineItemContent.CallNotify -> CallNotifyContent is TimelineItemContent.CallNotify -> CallNotifyContent
} }

View file

@ -15,7 +15,6 @@ import org.matrix.rustcomponents.sdk.EventSendState
import org.matrix.rustcomponents.sdk.EventTimelineItem import org.matrix.rustcomponents.sdk.EventTimelineItem
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo
import org.matrix.rustcomponents.sdk.ProfileDetails import org.matrix.rustcomponents.sdk.ProfileDetails
import org.matrix.rustcomponents.sdk.Reaction
import org.matrix.rustcomponents.sdk.Receipt import org.matrix.rustcomponents.sdk.Receipt
import org.matrix.rustcomponents.sdk.ShieldState import org.matrix.rustcomponents.sdk.ShieldState
import org.matrix.rustcomponents.sdk.TimelineItemContent import org.matrix.rustcomponents.sdk.TimelineItemContent
@ -30,7 +29,6 @@ fun aRustEventTimelineItem(
isEditable: Boolean = true, isEditable: Boolean = true,
content: TimelineItemContent = aRustTimelineItemMessageContent(), content: TimelineItemContent = aRustTimelineItemMessageContent(),
timestamp: ULong = 0uL, timestamp: ULong = 0uL,
reactions: List<Reaction> = emptyList(),
debugInfo: EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo(), debugInfo: EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo(),
localSendState: EventSendState? = null, localSendState: EventSendState? = null,
readReceipts: Map<String, Receipt> = emptyMap(), readReceipts: Map<String, Receipt> = emptyMap(),
@ -49,7 +47,6 @@ fun aRustEventTimelineItem(
canBeRepliedTo = canBeRepliedTo, canBeRepliedTo = canBeRepliedTo,
content = content, content = content,
localSendState = localSendState, localSendState = localSendState,
reactions = reactions,
readReceipts = readReceipts, readReceipts = readReceipts,
origin = origin, origin = origin,
localCreatedAt = localCreatedAt, localCreatedAt = localCreatedAt,

View file

@ -9,16 +9,23 @@ package io.element.android.libraries.matrix.impl.fixtures.factories
import org.matrix.rustcomponents.sdk.MessageContent import org.matrix.rustcomponents.sdk.MessageContent
import org.matrix.rustcomponents.sdk.MessageType import org.matrix.rustcomponents.sdk.MessageType
import org.matrix.rustcomponents.sdk.MsgLikeContent
import org.matrix.rustcomponents.sdk.MsgLikeKind
import org.matrix.rustcomponents.sdk.TextMessageContent import org.matrix.rustcomponents.sdk.TextMessageContent
import org.matrix.rustcomponents.sdk.TimelineItemContent import org.matrix.rustcomponents.sdk.TimelineItemContent
fun aRustTimelineItemMessageContent(body: String = "Hello") = TimelineItemContent.Message( fun aRustTimelineItemMessageContent(body: String = "Hello") = TimelineItemContent.MsgLike(
content = MessageContent( content = MsgLikeContent(
msgType = MessageType.Text(content = TextMessageContent(body = body, formatted = null)), kind = MsgLikeKind.Message(
body = body, content = MessageContent(
inReplyTo = null, msgType = MessageType.Text(content = TextMessageContent(body = body, formatted = null)),
body = body,
isEdited = false,
mentions = null,
)
),
reactions = emptyList(),
threadRoot = null, threadRoot = null,
isEdited = false, inReplyTo = null,
mentions = null, ),
)
) )

View file

@ -7,9 +7,11 @@
package io.element.android.libraries.matrix.impl.fixtures.factories package io.element.android.libraries.matrix.impl.fixtures.factories
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineEvent import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineEvent
import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_ROOM_NAME
import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.A_USER_NAME
import org.matrix.rustcomponents.sdk.JoinRule
import org.matrix.rustcomponents.sdk.NotificationEvent import org.matrix.rustcomponents.sdk.NotificationEvent
import org.matrix.rustcomponents.sdk.NotificationItem import org.matrix.rustcomponents.sdk.NotificationItem
import org.matrix.rustcomponents.sdk.NotificationRoomInfo import org.matrix.rustcomponents.sdk.NotificationRoomInfo
@ -22,12 +24,14 @@ fun aRustNotificationItem(
roomInfo: NotificationRoomInfo = aRustNotificationRoomInfo(), roomInfo: NotificationRoomInfo = aRustNotificationRoomInfo(),
isNoisy: Boolean? = false, isNoisy: Boolean? = false,
hasMention: Boolean? = false, hasMention: Boolean? = false,
threadId: ThreadId? = null,
) = NotificationItem( ) = NotificationItem(
event = event, event = event,
senderInfo = senderInfo, senderInfo = senderInfo,
roomInfo = roomInfo, roomInfo = roomInfo,
isNoisy = isNoisy, isNoisy = isNoisy,
hasMention = hasMention, hasMention = hasMention,
threadId = threadId?.value,
) )
fun aRustNotificationSenderInfo( fun aRustNotificationSenderInfo(
@ -47,6 +51,8 @@ fun aRustNotificationRoomInfo(
joinedMembersCount: ULong = 2u, joinedMembersCount: ULong = 2u,
isEncrypted: Boolean? = true, isEncrypted: Boolean? = true,
isDirect: Boolean = false, isDirect: Boolean = false,
joinRule: JoinRule? = null,
isPublic: Boolean = true,
) = NotificationRoomInfo( ) = NotificationRoomInfo(
displayName = displayName, displayName = displayName,
avatarUrl = avatarUrl, avatarUrl = avatarUrl,
@ -54,6 +60,8 @@ fun aRustNotificationRoomInfo(
joinedMembersCount = joinedMembersCount, joinedMembersCount = joinedMembersCount,
isEncrypted = isEncrypted, isEncrypted = isEncrypted,
isDirect = isDirect, isDirect = isDirect,
joinRule = joinRule,
isPublic = isPublic,
) )
fun aRustNotificationEventTimeline( fun aRustNotificationEventTimeline(