Browse Source

Upgrade to rust sdk 0.1.71 (#1905)

https://github.com/matrix-org/matrix-rust-components-kotlin/releases/tag/sdk-v0.1.71

There are breaking changes as specified in: https://github.com/vector-im/element-x-android/issues/1898 plus the one related to the poll history feature.
pull/1906/head
Marco Romano 10 months ago committed by GitHub
parent
commit
6ea26dd6c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt
  2. 8
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt
  3. 2
      gradle/libs.versions.toml
  4. 5
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupUploadState.kt
  5. 5
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EnableRecoveryProgress.kt
  6. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
  7. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt
  8. 1
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
  9. 5
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupUploadStateMapper.kt
  10. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EnableRecoveryProgressMapper.kt
  11. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt
  12. 2
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/SteadyStateExceptionMapper.kt
  13. 14
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt
  14. 61
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
  15. 19
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt
  16. 22
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
  17. 2
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt
  18. 3
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
  19. 2
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt

3
features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt

@ -140,8 +140,7 @@ private fun BackupUploadState.isBackingUp(): Boolean {
return when (this) { return when (this) {
BackupUploadState.Unknown, BackupUploadState.Unknown,
BackupUploadState.Waiting, BackupUploadState.Waiting,
is BackupUploadState.Uploading, is BackupUploadState.Uploading -> true
is BackupUploadState.CheckingIfUploadNeeded -> true
is BackupUploadState.SteadyException -> exception is SteadyStateException.Connection is BackupUploadState.SteadyException -> exception is SteadyStateException.Connection
BackupUploadState.Done, BackupUploadState.Done,
BackupUploadState.Error -> false BackupUploadState.Error -> false

8
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt

@ -129,11 +129,11 @@ class SecureBackupSetupPresenter @AssistedInject constructor(
encryptionService.enableRecoveryProgressStateFlow.collect { enableRecoveryProgress -> encryptionService.enableRecoveryProgressStateFlow.collect { enableRecoveryProgress ->
Timber.tag(loggerTagSetup.value).d("New enableRecoveryProgress: ${enableRecoveryProgress.javaClass.simpleName}") Timber.tag(loggerTagSetup.value).d("New enableRecoveryProgress: ${enableRecoveryProgress.javaClass.simpleName}")
when (enableRecoveryProgress) { when (enableRecoveryProgress) {
EnableRecoveryProgress.Unknown, is EnableRecoveryProgress.Starting,
is EnableRecoveryProgress.CreatingBackup,
is EnableRecoveryProgress.CreatingRecoveryKey,
is EnableRecoveryProgress.BackingUp, is EnableRecoveryProgress.BackingUp,
EnableRecoveryProgress.CreatingBackup, is EnableRecoveryProgress.RoomKeyUploadError -> Unit
EnableRecoveryProgress.CreatingRecoveryKey ->
Unit
is EnableRecoveryProgress.Done -> is EnableRecoveryProgress.Done ->
stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkHasCreatedKey(enableRecoveryProgress.recoveryKey)) stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkHasCreatedKey(enableRecoveryProgress.recoveryKey))
} }

2
gradle/libs.versions.toml

@ -147,7 +147,7 @@ jsoup = "org.jsoup:jsoup:1.17.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:1.3.1" molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.1"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.70" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.71"
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" }

5
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupUploadState.kt

@ -19,11 +19,6 @@ package io.element.android.libraries.matrix.api.encryption
sealed interface BackupUploadState { sealed interface BackupUploadState {
data object Unknown : BackupUploadState data object Unknown : BackupUploadState
data class CheckingIfUploadNeeded(
val backedUpCount: Int,
val totalCount: Int,
) : BackupUploadState
data object Waiting : BackupUploadState data object Waiting : BackupUploadState
data class Uploading( data class Uploading(

5
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EnableRecoveryProgress.kt

@ -17,9 +17,10 @@
package io.element.android.libraries.matrix.api.encryption package io.element.android.libraries.matrix.api.encryption
sealed interface EnableRecoveryProgress { sealed interface EnableRecoveryProgress {
data object Unknown : EnableRecoveryProgress data object Starting : EnableRecoveryProgress
data object CreatingRecoveryKey : EnableRecoveryProgress
data object CreatingBackup : EnableRecoveryProgress data object CreatingBackup : EnableRecoveryProgress
data object CreatingRecoveryKey : EnableRecoveryProgress
data class BackingUp(val backedUpCount: Int, val totalCount: Int) : EnableRecoveryProgress data class BackingUp(val backedUpCount: Int, val totalCount: Int) : EnableRecoveryProgress
data object RoomKeyUploadError : EnableRecoveryProgress
data class Done(val recoveryKey: String) : EnableRecoveryProgress data class Done(val recoveryKey: String) : EnableRecoveryProgress
} }

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

@ -238,5 +238,7 @@ interface MatrixRoom : Closeable {
*/ */
fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver>
suspend fun pollHistory(): MatrixTimeline
override fun close() = destroy() override fun close() = destroy()
} }

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt

@ -20,7 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
interface MatrixTimeline { interface MatrixTimeline: AutoCloseable {
data class PaginationState( data class PaginationState(
val isBackPaginating: Boolean, val isBackPaginating: Boolean,

1
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

@ -214,6 +214,7 @@ class RustMatrixClient constructor(
isKeyBackupEnabled = client.encryption().backupState() == BackupState.ENABLED, isKeyBackupEnabled = client.encryption().backupState() == BackupState.ENABLED,
roomListItem = roomListItem, roomListItem = roomListItem,
innerRoom = fullRoom, innerRoom = fullRoom,
innerTimeline = fullRoom.timeline(),
roomNotificationSettingsService = notificationSettingsService, roomNotificationSettingsService = notificationSettingsService,
sessionCoroutineScope = sessionCoroutineScope, sessionCoroutineScope = sessionCoroutineScope,
coroutineDispatchers = dispatchers, coroutineDispatchers = dispatchers,

5
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupUploadStateMapper.kt

@ -22,11 +22,6 @@ import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState
class BackupUploadStateMapper { class BackupUploadStateMapper {
fun map(rustEnableProgress: RustBackupUploadState): BackupUploadState { fun map(rustEnableProgress: RustBackupUploadState): BackupUploadState {
return when (rustEnableProgress) { return when (rustEnableProgress) {
is RustBackupUploadState.CheckingIfUploadNeeded ->
BackupUploadState.CheckingIfUploadNeeded(
backedUpCount = rustEnableProgress.backedUpCount.toInt(),
totalCount = rustEnableProgress.totalCount.toInt(),
)
RustBackupUploadState.Done -> RustBackupUploadState.Done ->
BackupUploadState.Done BackupUploadState.Done
is RustBackupUploadState.Uploading -> is RustBackupUploadState.Uploading ->

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EnableRecoveryProgressMapper.kt

@ -22,12 +22,14 @@ import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecover
class EnableRecoveryProgressMapper { class EnableRecoveryProgressMapper {
fun map(rustEnableProgress: RustEnableRecoveryProgress): EnableRecoveryProgress { fun map(rustEnableProgress: RustEnableRecoveryProgress): EnableRecoveryProgress {
return when (rustEnableProgress) { return when (rustEnableProgress) {
is RustEnableRecoveryProgress.CreatingRecoveryKey -> EnableRecoveryProgress.CreatingRecoveryKey is RustEnableRecoveryProgress.Starting -> EnableRecoveryProgress.Starting
is RustEnableRecoveryProgress.CreatingBackup -> EnableRecoveryProgress.CreatingBackup is RustEnableRecoveryProgress.CreatingBackup -> EnableRecoveryProgress.CreatingBackup
is RustEnableRecoveryProgress.CreatingRecoveryKey -> EnableRecoveryProgress.CreatingRecoveryKey
is RustEnableRecoveryProgress.BackingUp -> EnableRecoveryProgress.BackingUp( is RustEnableRecoveryProgress.BackingUp -> EnableRecoveryProgress.BackingUp(
backedUpCount = rustEnableProgress.backedUpCount.toInt(), backedUpCount = rustEnableProgress.backedUpCount.toInt(),
totalCount = rustEnableProgress.totalCount.toInt(), totalCount = rustEnableProgress.totalCount.toInt(),
) )
is RustEnableRecoveryProgress.RoomKeyUploadError -> EnableRecoveryProgress.RoomKeyUploadError
is RustEnableRecoveryProgress.Done -> EnableRecoveryProgress.Done( is RustEnableRecoveryProgress.Done -> EnableRecoveryProgress.Done(
recoveryKey = rustEnableProgress.recoveryKey recoveryKey = rustEnableProgress.recoveryKey
) )

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt

@ -86,7 +86,7 @@ internal class RustEncryptionService(
} }
}.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RecoveryState.WAITING_FOR_SYNC) }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RecoveryState.WAITING_FOR_SYNC)
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Unknown) override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
fun start() { fun start() {
service.backupStateListener(object : BackupStateListener { service.backupStateListener(object : BackupStateListener {
@ -181,7 +181,7 @@ internal class RustEncryptionService(
override suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) { override suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
runCatching { runCatching {
service.fixRecoveryIssues(recoveryKey) service.recover(recoveryKey)
} }
} }
} }

2
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/SteadyStateExceptionMapper.kt

@ -28,7 +28,7 @@ class SteadyStateExceptionMapper {
is RustSteadyStateException.Connection -> SteadyStateException.Connection( is RustSteadyStateException.Connection -> SteadyStateException.Connection(
message = data.message message = data.message
) )
is RustSteadyStateException.Laged -> SteadyStateException.Lagged( is RustSteadyStateException.Lagged -> SteadyStateException.Lagged(
message = data.message message = data.message
) )
} }

14
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt

@ -24,8 +24,8 @@ 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.Room
import org.matrix.rustcomponents.sdk.RoomListService import org.matrix.rustcomponents.sdk.RoomListService
import org.matrix.rustcomponents.sdk.Timeline
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
/** /**
@ -37,19 +37,19 @@ class RoomContentForwarder(
) { ) {
/** /**
* Forwards the event with the given [eventId] from the [fromRoom] to the given [toRoomIds]. * Forwards the event with the given [eventId] from the [fromTimeline] to the given [toRoomIds].
* @param fromRoom the room to forward the event from * @param fromTimeline the room to forward the event from
* @param eventId the id of the event to forward * @param eventId the id of the event to forward
* @param toRoomIds the ids of the rooms to forward the event to * @param toRoomIds the ids of the rooms to forward the event to
* @param timeoutMs the maximum time in milliseconds to wait for the event to be sent to a room * @param timeoutMs the maximum time in milliseconds to wait for the event to be sent to a room
*/ */
suspend fun forward( suspend fun forward(
fromRoom: Room, fromTimeline: Timeline,
eventId: EventId, eventId: EventId,
toRoomIds: List<RoomId>, toRoomIds: List<RoomId>,
timeoutMs: Long = 5000L timeoutMs: Long = 5000L
) { ) {
val content = fromRoom.getTimelineEventContentByEventId(eventId.value) val content = fromTimeline.getTimelineEventContentByEventId(eventId.value)
val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) }
val targetRooms = targetSlidingSyncRooms.mapNotNull { slidingSyncRoom -> slidingSyncRoom.use { it.fullRoom() } } val targetRooms = targetSlidingSyncRooms.mapNotNull { slidingSyncRoom -> slidingSyncRoom.use { it.fullRoom() } }
val failedForwardingTo = mutableSetOf<RoomId>() val failedForwardingTo = mutableSetOf<RoomId>()
@ -57,9 +57,9 @@ class RoomContentForwarder(
room.use { targetRoom -> room.use { targetRoom ->
runCatching { runCatching {
// Sending a message requires a registered timeline listener // Sending a message requires a registered timeline listener
targetRoom.runWithTimelineListenerRegistered { targetRoom.timeline().runWithTimelineListenerRegistered {
withTimeout(timeoutMs.milliseconds) { withTimeout(timeoutMs.milliseconds) {
targetRoom.send(content) targetRoom.timeline().send(content)
} }
} }
} }

61
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

@ -76,6 +76,7 @@ import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomMember import org.matrix.rustcomponents.sdk.RoomMember
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
import org.matrix.rustcomponents.sdk.Timeline
import org.matrix.rustcomponents.sdk.WidgetCapabilities import org.matrix.rustcomponents.sdk.WidgetCapabilities
import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
@ -87,9 +88,10 @@ import java.io.File
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class RustMatrixRoom( class RustMatrixRoom(
override val sessionId: SessionId, override val sessionId: SessionId,
isKeyBackupEnabled: Boolean, private val isKeyBackupEnabled: Boolean,
private val roomListItem: RoomListItem, private val roomListItem: RoomListItem,
private val innerRoom: Room, private val innerRoom: Room,
private val innerTimeline: Timeline,
private val roomNotificationSettingsService: RustNotificationSettingsService, private val roomNotificationSettingsService: RustNotificationSettingsService,
sessionCoroutineScope: CoroutineScope, sessionCoroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
@ -130,7 +132,7 @@ class RustMatrixRoom(
override val timeline = RustMatrixTimeline( override val timeline = RustMatrixTimeline(
isKeyBackupEnabled = isKeyBackupEnabled, isKeyBackupEnabled = isKeyBackupEnabled,
matrixRoom = this, matrixRoom = this,
innerRoom = innerRoom, innerTimeline = innerTimeline,
roomCoroutineScope = roomCoroutineScope, roomCoroutineScope = roomCoroutineScope,
dispatcher = roomDispatcher, dispatcher = roomDispatcher,
lastLoginTimestamp = sessionData.loginTimestamp, lastLoginTimestamp = sessionData.loginTimestamp,
@ -147,6 +149,7 @@ class RustMatrixRoom(
override fun destroy() { override fun destroy() {
roomCoroutineScope.cancel() roomCoroutineScope.cancel()
innerTimeline.destroy()
innerRoom.destroy() innerRoom.destroy()
roomListItem.destroy() roomListItem.destroy()
specialModeEventTimelineItem?.destroy() specialModeEventTimelineItem?.destroy()
@ -254,7 +257,7 @@ class RustMatrixRoom(
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) { override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content -> messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content ->
runCatching { runCatching {
innerRoom.send(content) innerTimeline.send(content)
} }
} }
} }
@ -269,9 +272,9 @@ class RustMatrixRoom(
withContext(roomDispatcher) { withContext(roomDispatcher) {
if (originalEventId != null) { if (originalEventId != null) {
runCatching { runCatching {
val editedEvent = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(originalEventId.value) val editedEvent = specialModeEventTimelineItem ?: innerTimeline.getEventTimelineItemByEventId(originalEventId.value)
editedEvent.use { editedEvent.use {
innerRoom.edit( innerTimeline.edit(
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
editItem = it, editItem = it,
) )
@ -281,7 +284,7 @@ class RustMatrixRoom(
} else { } else {
runCatching { runCatching {
transactionId?.let { cancelSend(it) } transactionId?.let { cancelSend(it) }
innerRoom.send(messageEventContentFromParts(body, htmlBody)) innerTimeline.send(messageEventContentFromParts(body, htmlBody))
} }
} }
} }
@ -292,15 +295,15 @@ class RustMatrixRoom(
runCatching { runCatching {
specialModeEventTimelineItem?.destroy() specialModeEventTimelineItem?.destroy()
specialModeEventTimelineItem = null specialModeEventTimelineItem = null
specialModeEventTimelineItem = eventId?.let { innerRoom.getEventTimelineItemByEventId(it.value) } specialModeEventTimelineItem = eventId?.let { innerTimeline.getEventTimelineItemByEventId(it.value) }
} }
} }
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) { override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
val inReplyTo = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(eventId.value) val inReplyTo = specialModeEventTimelineItem ?: innerTimeline.getEventTimelineItemByEventId(eventId.value)
inReplyTo.use { eventTimelineItem -> inReplyTo.use { eventTimelineItem ->
innerRoom.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem) innerTimeline.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem)
} }
specialModeEventTimelineItem = null specialModeEventTimelineItem = null
} }
@ -362,37 +365,37 @@ class RustMatrixRoom(
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> { override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
return sendAttachment(listOf(file, thumbnailFile)) { return sendAttachment(listOf(file, thumbnailFile)) {
innerRoom.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher()) innerTimeline.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher())
} }
} }
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> { override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
return sendAttachment(listOf(file, thumbnailFile)) { return sendAttachment(listOf(file, thumbnailFile)) {
innerRoom.sendVideo(file.path, thumbnailFile.path, videoInfo.map(), progressCallback?.toProgressWatcher()) innerTimeline.sendVideo(file.path, thumbnailFile.path, videoInfo.map(), progressCallback?.toProgressWatcher())
} }
} }
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> { override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
return sendAttachment(listOf(file)) { return sendAttachment(listOf(file)) {
innerRoom.sendAudio(file.path, audioInfo.map(), progressCallback?.toProgressWatcher()) innerTimeline.sendAudio(file.path, audioInfo.map(), progressCallback?.toProgressWatcher())
} }
} }
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> { override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
return sendAttachment(listOf(file)) { return sendAttachment(listOf(file)) {
innerRoom.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher()) innerTimeline.sendFile(file.path, fileInfo.map(), progressCallback?.toProgressWatcher())
} }
} }
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> = withContext(roomDispatcher) { override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.toggleReaction(key = emoji, eventId = eventId.value) innerTimeline.toggleReaction(key = emoji, eventId = eventId.value)
} }
} }
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = withContext(roomDispatcher) { override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
roomContentForwarder.forward(fromRoom = innerRoom, eventId = eventId, toRoomIds = roomIds) roomContentForwarder.forward(fromTimeline = innerTimeline, eventId = eventId, toRoomIds = roomIds)
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
} }
@ -400,13 +403,13 @@ class RustMatrixRoom(
override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> = withContext(roomDispatcher) { override suspend fun retrySendMessage(transactionId: TransactionId): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.retrySend(transactionId.value) innerTimeline.retrySend(transactionId.value)
} }
} }
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = withContext(roomDispatcher) { override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.cancelSend(transactionId.value) innerTimeline.cancelSend(transactionId.value)
} }
} }
@ -451,7 +454,7 @@ class RustMatrixRoom(
assetType: AssetType?, assetType: AssetType?,
): Result<Unit> = withContext(roomDispatcher) { ): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.sendLocation( innerTimeline.sendLocation(
body = body, body = body,
geoUri = geoUri, geoUri = geoUri,
description = description, description = description,
@ -468,7 +471,7 @@ class RustMatrixRoom(
pollKind: PollKind, pollKind: PollKind,
): Result<Unit> = withContext(roomDispatcher) { ): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.createPoll( innerTimeline.createPoll(
question = question, question = question,
answers = answers, answers = answers,
maxSelections = maxSelections.toUByte(), maxSelections = maxSelections.toUByte(),
@ -486,11 +489,11 @@ class RustMatrixRoom(
): Result<Unit> = withContext(roomDispatcher) { ): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
val pollStartEvent = val pollStartEvent =
innerRoom.getEventTimelineItemByEventId( innerTimeline.getEventTimelineItemByEventId(
eventId = pollStartId.value eventId = pollStartId.value
) )
pollStartEvent.use { pollStartEvent.use {
innerRoom.editPoll( innerTimeline.editPoll(
question = question, question = question,
answers = answers, answers = answers,
maxSelections = maxSelections.toUByte(), maxSelections = maxSelections.toUByte(),
@ -506,7 +509,7 @@ class RustMatrixRoom(
answers: List<String> answers: List<String>
): Result<Unit> = withContext(roomDispatcher) { ): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.sendPollResponse( innerTimeline.sendPollResponse(
pollStartId = pollStartId.value, pollStartId = pollStartId.value,
answers = answers, answers = answers,
) )
@ -518,7 +521,7 @@ class RustMatrixRoom(
text: String text: String
): Result<Unit> = withContext(roomDispatcher) { ): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.endPoll( innerTimeline.endPoll(
pollStartId = pollStartId.value, pollStartId = pollStartId.value,
text = text, text = text,
) )
@ -531,7 +534,7 @@ class RustMatrixRoom(
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback?, progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendAttachment(listOf(file)) { ): Result<MediaUploadHandler> = sendAttachment(listOf(file)) {
innerRoom.sendVoiceMessage( innerTimeline.sendVoiceMessage(
url = file.path, url = file.path,
audioInfo = audioInfo.map(), audioInfo = audioInfo.map(),
waveform = waveform.toMSC3246range(), waveform = waveform.toMSC3246range(),
@ -560,6 +563,16 @@ class RustMatrixRoom(
) )
} }
override suspend fun pollHistory() = RustMatrixTimeline(
isKeyBackupEnabled = isKeyBackupEnabled,
matrixRoom = this,
innerTimeline = innerRoom.pollHistory(),
roomCoroutineScope = roomCoroutineScope,
dispatcher = roomDispatcher,
lastLoginTimestamp = sessionData.loginTimestamp,
onNewSyncedEvent = { _syncUpdateFlow.value = systemClock.epochMillis() }
)
private suspend fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> { private suspend fun sendAttachment(files: List<File>, handle: () -> SendAttachmentJoinHandle): Result<MediaUploadHandler> {
return runCatching { return runCatching {
MediaUploadHandlerImpl(files, handle()) MediaUploadHandlerImpl(files, handle())

19
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt

@ -29,29 +29,28 @@ import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import org.matrix.rustcomponents.sdk.BackPaginationStatus import org.matrix.rustcomponents.sdk.BackPaginationStatus
import org.matrix.rustcomponents.sdk.BackPaginationStatusListener import org.matrix.rustcomponents.sdk.BackPaginationStatusListener
import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.Timeline
import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineItem
import org.matrix.rustcomponents.sdk.TimelineListener import org.matrix.rustcomponents.sdk.TimelineListener
import timber.log.Timber import timber.log.Timber
internal fun Room.timelineDiffFlow(onInitialList: suspend (List<TimelineItem>) -> Unit): Flow<List<TimelineDiff>> = internal fun Timeline.timelineDiffFlow(onInitialList: suspend (List<TimelineItem>) -> Unit): Flow<List<TimelineDiff>> =
callbackFlow { callbackFlow {
val listener = object : TimelineListener { val listener = object : TimelineListener {
override fun onUpdate(diff: List<TimelineDiff>) { override fun onUpdate(diff: List<TimelineDiff>) {
trySendBlocking(diff) trySendBlocking(diff)
} }
} }
val roomId = id() Timber.d("Open timelineDiffFlow for TimelineInterface ${this@timelineDiffFlow}")
Timber.d("Open timelineDiffFlow for room $roomId") val result = addListener(listener)
val result = addTimelineListener(listener)
try { try {
onInitialList(result.items) onInitialList(result.items)
} catch (exception: Exception) { } catch (exception: Exception) {
Timber.d(exception, "Catch failure in timelineDiffFlow of room $roomId") Timber.d(exception, "Catch failure in timelineDiffFlow of TimelineInterface ${this@timelineDiffFlow}")
} }
awaitClose { awaitClose {
Timber.d("Close timelineDiffFlow for room $roomId") Timber.d("Close timelineDiffFlow for TimelineInterface ${this@timelineDiffFlow}")
result.itemsStream.cancelAndDestroy() result.itemsStream.cancelAndDestroy()
result.items.destroyAll() result.items.destroyAll()
} }
@ -59,7 +58,7 @@ internal fun Room.timelineDiffFlow(onInitialList: suspend (List<TimelineItem>) -
Timber.d(it, "timelineDiffFlow() failed") Timber.d(it, "timelineDiffFlow() failed")
}.buffer(Channel.UNLIMITED) }.buffer(Channel.UNLIMITED)
internal fun Room.backPaginationStatusFlow(): Flow<BackPaginationStatus> = internal fun Timeline.backPaginationStatusFlow(): Flow<BackPaginationStatus> =
mxCallbackFlow { mxCallbackFlow {
val listener = object : BackPaginationStatusListener { val listener = object : BackPaginationStatusListener {
override fun onUpdate(status: BackPaginationStatus) { override fun onUpdate(status: BackPaginationStatus) {
@ -71,8 +70,8 @@ internal fun Room.backPaginationStatusFlow(): Flow<BackPaginationStatus> =
} }
}.buffer(Channel.UNLIMITED) }.buffer(Channel.UNLIMITED)
internal suspend fun Room.runWithTimelineListenerRegistered(action: suspend () -> Unit) { internal suspend fun Timeline.runWithTimelineListenerRegistered(action: suspend () -> Unit) {
val result = addTimelineListener(NoOpTimelineListener) val result = addListener(NoOpTimelineListener)
try { try {
action() action()
} finally { } finally {

22
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt

@ -46,7 +46,7 @@ import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.BackPaginationStatus import org.matrix.rustcomponents.sdk.BackPaginationStatus
import org.matrix.rustcomponents.sdk.EventItemOrigin import org.matrix.rustcomponents.sdk.EventItemOrigin
import org.matrix.rustcomponents.sdk.PaginationOptions import org.matrix.rustcomponents.sdk.PaginationOptions
import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.Timeline
import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineItem
import timber.log.Timber import timber.log.Timber
@ -59,9 +59,9 @@ class RustMatrixTimeline(
roomCoroutineScope: CoroutineScope, roomCoroutineScope: CoroutineScope,
isKeyBackupEnabled: Boolean, isKeyBackupEnabled: Boolean,
private val matrixRoom: MatrixRoom, private val matrixRoom: MatrixRoom,
private val innerRoom: Room, private val innerTimeline: Timeline,
private val dispatcher: CoroutineDispatcher, private val dispatcher: CoroutineDispatcher,
private val lastLoginTimestamp: Date?, lastLoginTimestamp: Date?,
private val onNewSyncedEvent: () -> Unit, private val onNewSyncedEvent: () -> Unit,
) : MatrixTimeline { ) : MatrixTimeline {
@ -109,7 +109,7 @@ class RustMatrixTimeline(
Timber.d("Initialize timeline for room ${matrixRoom.roomId}") Timber.d("Initialize timeline for room ${matrixRoom.roomId}")
roomCoroutineScope.launch(dispatcher) { roomCoroutineScope.launch(dispatcher) {
innerRoom.timelineDiffFlow { initialList -> innerTimeline.timelineDiffFlow { initialList ->
postItems(initialList) postItems(initialList)
}.onEach { diffs -> }.onEach { diffs ->
if (diffs.any { diff -> diff.eventOrigin() == EventItemOrigin.SYNC }) { if (diffs.any { diff -> diff.eventOrigin() == EventItemOrigin.SYNC }) {
@ -118,7 +118,7 @@ class RustMatrixTimeline(
postDiffs(diffs) postDiffs(diffs)
}.launchIn(this) }.launchIn(this)
innerRoom.backPaginationStatusFlow() innerTimeline.backPaginationStatusFlow()
.onEach { .onEach {
postPaginationStatus(it) postPaginationStatus(it)
} }
@ -130,7 +130,7 @@ class RustMatrixTimeline(
private suspend fun fetchMembers() = withContext(dispatcher) { private suspend fun fetchMembers() = withContext(dispatcher) {
initLatch.await() initLatch.await()
innerRoom.fetchMembers() innerTimeline.fetchMembers()
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@ -188,7 +188,7 @@ class RustMatrixTimeline(
override suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> = withContext(dispatcher) { override suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> = withContext(dispatcher) {
runCatching { runCatching {
innerRoom.fetchDetailsForEvent(eventId.value) innerTimeline.fetchDetailsForEvent(eventId.value)
} }
} }
@ -201,7 +201,7 @@ class RustMatrixTimeline(
items = untilNumberOfItems.toUShort(), items = untilNumberOfItems.toUShort(),
waitForToken = true, waitForToken = true,
) )
innerRoom.paginateBackwards(paginationOptions) innerTimeline.paginateBackwards(paginationOptions)
}.onFailure { error -> }.onFailure { error ->
if (error is TimelineException.CannotPaginate) { if (error is TimelineException.CannotPaginate) {
Timber.d("Can't paginate backwards on room ${matrixRoom.roomId}, we're already at the start") Timber.d("Can't paginate backwards on room ${matrixRoom.roomId}, we're already at the start")
@ -219,10 +219,14 @@ class RustMatrixTimeline(
override suspend fun sendReadReceipt(eventId: EventId) = withContext(dispatcher) { override suspend fun sendReadReceipt(eventId: EventId) = withContext(dispatcher) {
runCatching { runCatching {
innerRoom.sendReadReceipt(eventId = eventId.value) innerTimeline.sendReadReceipt(eventId = eventId.value)
} }
} }
override fun close() {
innerTimeline.close()
}
fun getItemById(eventId: EventId): MatrixTimelineItem.Event? { fun getItemById(eventId: EventId): MatrixTimelineItem.Event? {
return _timelineItems.value.firstOrNull { (it as? MatrixTimelineItem.Event)?.eventId == eventId } as? MatrixTimelineItem.Event return _timelineItems.value.firstOrNull { (it as? MatrixTimelineItem.Event)?.eventId == eventId } as? MatrixTimelineItem.Event
} }

2
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt

@ -30,7 +30,7 @@ class FakeEncryptionService : EncryptionService {
private var disableRecoveryFailure: Exception? = null private var disableRecoveryFailure: Exception? = null
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN) override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN)
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(RecoveryState.UNKNOWN) override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(RecoveryState.UNKNOWN)
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Unknown) override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf() private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
private var fixRecoveryIssuesFailure: Exception? = null private var fixRecoveryIssuesFailure: Exception? = null

3
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -425,6 +425,9 @@ class FakeMatrixRoom(
): Result<String> = generateWidgetWebViewUrlResult ): Result<String> = generateWidgetWebViewUrlResult
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> = getWidgetDriverResult override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> = getWidgetDriverResult
override suspend fun pollHistory(): MatrixTimeline {
return FakeMatrixTimeline()
}
fun givenLeaveRoomError(throwable: Throwable?) { fun givenLeaveRoomError(throwable: Throwable?) {
this.leaveRoomError = throwable this.leaveRoomError = throwable

2
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt

@ -79,4 +79,6 @@ class FakeMatrixTimeline(
sendReadReceiptLatch?.complete(Unit) sendReadReceiptLatch?.complete(Unit)
Result.success(Unit) Result.success(Unit)
} }
override fun close() = Unit
} }

Loading…
Cancel
Save