diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt index b86e62b9a0..42eb3c938b 100644 --- a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt @@ -83,11 +83,11 @@ import io.element.android.features.messages.timeline.model.virtual.TimelineItemL import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.utils.PairCombinedPreviewParameter -import io.element.android.libraries.matrix.core.EventId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch +import timber.log.Timber @Composable fun TimelineView( @@ -117,14 +117,6 @@ fun TimelineView( onLongClick = onMessageLongClicked ) } - /* - if (state.hasMoreToLoad) { - item { - TimelineLoadingMoreIndicator() - } - } - - */ } fun onReachedLoadMore() { @@ -165,12 +157,23 @@ fun TimelineItemRow( is TimelineItem.Virtual -> TimelineItemVirtualRow( virtual = timelineItem ) - is TimelineItem.Event -> TimelineItemEventRow( - event = timelineItem, - isHighlighted = isHighlighted, - onClick = { onClick(timelineItem) }, - onLongClick = { onLongClick(timelineItem) } - ) + is TimelineItem.Event -> { + + fun onClick() { + onClick(timelineItem) + } + + fun onLongClick() { + onLongClick(timelineItem) + } + + TimelineItemEventRow( + event = timelineItem, + isHighlighted = isHighlighted, + onClick = ::onClick, + onLongClick = ::onLongClick + ) + } } } diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemEventFactory.kt index fdd6bc35ab..850ec7fbd8 100644 --- a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/event/TimelineItemEventFactory.kt @@ -33,7 +33,7 @@ class TimelineItemEventFactory @Inject constructor( private val contentFactory: TimelineItemContentFactory, ) { - suspend fun create( + fun create( currentTimelineItem: MatrixTimelineItem.Event, index: Int, timelineItems: List, diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/virtual/TimelineItemVirtualFactory.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/virtual/TimelineItemVirtualFactory.kt index 1a5f094d06..e92171caa7 100644 --- a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/virtual/TimelineItemVirtualFactory.kt +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/factories/virtual/TimelineItemVirtualFactory.kt @@ -29,7 +29,7 @@ class TimelineItemVirtualFactory @Inject constructor( private val daySeparatorFactory: TimelineItemDaySeparatorFactory, ) { - suspend fun create( + fun create( currentTimelineItem: MatrixTimelineItem.Virtual, index: Int, timelineItems: List, diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/model/TimelineItem.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/model/TimelineItem.kt index 26b7caa59c..3b1a22b13c 100644 --- a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/model/TimelineItem.kt +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/model/TimelineItem.kt @@ -24,11 +24,19 @@ import io.element.android.libraries.matrix.core.EventId @Immutable sealed interface TimelineItem { + + fun identifier(): String = when(this){ + is Event -> id + is Virtual -> id + } + + @Immutable data class Virtual( val id: String, val model: TimelineItemVirtualModel ) : TimelineItem + @Immutable data class Event( val id: String, val eventId: EventId? = null, diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt index 514f818365..4e82840534 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/RustMatrixClient.kt @@ -43,7 +43,7 @@ import timber.log.Timber import java.io.File import java.util.concurrent.atomic.AtomicBoolean -internal class RustMatrixClient internal constructor( +class RustMatrixClient constructor( private val client: Client, private val sessionStore: SessionStore, private val coroutineScope: CoroutineScope, @@ -132,11 +132,11 @@ internal class RustMatrixClient internal constructor( override fun getRoom(roomId: RoomId): MatrixRoom? { val slidingSyncRoom = slidingSync.getRoom(roomId.value) ?: return null - val room = slidingSyncRoom.fullRoom() ?: return null + val fullRoom = slidingSyncRoom.fullRoom() ?: return null return RustMatrixRoom( slidingSyncUpdateFlow = slidingSyncObserverProxy.updateSummaryFlow, slidingSyncRoom = slidingSyncRoom, - room = room, + innerRoom = fullRoom, coroutineScope = coroutineScope, coroutineDispatchers = dispatchers ) diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt index eec74e9e03..5cb654faf7 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/di/MatrixModule.kt @@ -31,6 +31,6 @@ object MatrixModule { @Provides @SingleIn(AppScope::class) fun providesRustAuthenticationService(baseDirectory: File): AuthenticationService { - return AuthenticationService(baseDirectory.absolutePath, null) + return AuthenticationService(baseDirectory.absolutePath, null, null) } } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt index 06c1fa0f76..76a2a91882 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/MatrixRoom.kt @@ -33,6 +33,8 @@ interface MatrixRoom { fun timeline(): MatrixTimeline + suspend fun fetchMembers(): Result + suspend fun userDisplayName(userId: String): Result suspend fun userAvatarUrl(userId: String): Result @@ -44,4 +46,5 @@ interface MatrixRoom { suspend fun replyMessage(eventId: EventId, message: String): Result suspend fun redactEvent(eventId: EventId, reason: String? = null): Result + } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomListenerFlows.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomListenerFlows.kt deleted file mode 100644 index e307bf0dad..0000000000 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RoomListenerFlows.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.libraries.matrix.room - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.launch -import org.matrix.rustcomponents.sdk.Room -import org.matrix.rustcomponents.sdk.TimelineDiff -import org.matrix.rustcomponents.sdk.TimelineListener - -fun Room.timelineDiff(scope: CoroutineScope): Flow = callbackFlow { - val listener = object : TimelineListener { - override fun onUpdate(update: TimelineDiff) { - scope.launch { - send(update) - } - } - } - addTimelineListener(listener) - awaitClose { - removeTimeline() - } -} diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt index c481b7c3d5..99c21931c4 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/room/RustMatrixRoom.kt @@ -32,11 +32,12 @@ import org.matrix.rustcomponents.sdk.SlidingSyncRoom import org.matrix.rustcomponents.sdk.UpdateSummary import org.matrix.rustcomponents.sdk.genTransactionId import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown +import timber.log.Timber class RustMatrixRoom( private val slidingSyncUpdateFlow: Flow, private val slidingSyncRoom: SlidingSyncRoom, - private val room: Room, + private val innerRoom: Room, private val coroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, ) : MatrixRoom { @@ -44,7 +45,7 @@ class RustMatrixRoom( override fun syncUpdateFlow(): Flow { return slidingSyncUpdateFlow .filter { - it.rooms.contains(room.id()) + it.rooms.contains(innerRoom.id()) } .map { System.currentTimeMillis() @@ -55,14 +56,14 @@ class RustMatrixRoom( override fun timeline(): MatrixTimeline { return RustMatrixTimeline( matrixRoom = this, - room = room, + innerRoom = innerRoom, slidingSyncRoom = slidingSyncRoom, coroutineScope = coroutineScope, coroutineDispatchers = coroutineDispatchers ) } - override val roomId = RoomId(room.id()) + override val roomId = RoomId(innerRoom.id()) override val name: String? get() { @@ -71,35 +72,41 @@ class RustMatrixRoom( override val bestName: String get() { - return name?.takeIf { it.isNotEmpty() } ?: room.id() + return name?.takeIf { it.isNotEmpty() } ?: innerRoom.id() } override val displayName: String get() { - return room.displayName() + return innerRoom.displayName() } override val topic: String? get() { - return room.topic() + return innerRoom.topic() } override val avatarUrl: String? get() { - return room.avatarUrl() + return innerRoom.avatarUrl() } + override suspend fun fetchMembers(): Result = withContext(coroutineDispatchers.io) { + runCatching { + innerRoom.fetchMembers() + } + } + override suspend fun userDisplayName(userId: String): Result = withContext(coroutineDispatchers.io) { runCatching { - room.memberDisplayName(userId) + innerRoom.memberDisplayName(userId) } } override suspend fun userAvatarUrl(userId: String): Result = withContext(coroutineDispatchers.io) { runCatching { - room.memberAvatarUrl(userId) + innerRoom.memberAvatarUrl(userId) } } @@ -107,7 +114,7 @@ class RustMatrixRoom( val transactionId = genTransactionId() val content = messageEventContentFromMarkdown(message) runCatching { - room.send(content, transactionId) + innerRoom.send(content, transactionId) } } @@ -115,7 +122,7 @@ class RustMatrixRoom( val transactionId = genTransactionId() // val content = messageEventContentFromMarkdown(message) runCatching { - room.edit(/* TODO use content */ message, originalEventId.value, transactionId) + innerRoom.edit(/* TODO use content */ message, originalEventId.value, transactionId) } } @@ -123,14 +130,14 @@ class RustMatrixRoom( val transactionId = genTransactionId() // val content = messageEventContentFromMarkdown(message) runCatching { - room.sendReply(/* TODO use content */ message, eventId.value, transactionId) + innerRoom.sendReply(/* TODO use content */ message, eventId.value, transactionId) } } override suspend fun redactEvent(eventId: EventId, reason: String?) = withContext(coroutineDispatchers.io) { val transactionId = genTransactionId() runCatching { - room.redact(eventId.value, reason, transactionId) + innerRoom.redact(eventId.value, reason, transactionId) } } } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt index c1ad49c90a..9468937266 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/session/PreferencesSessionStore.kt @@ -54,7 +54,8 @@ class PreferencesSessionStore @Inject constructor( val homeserverUrl: String, val isSoftLogout: Boolean, val refreshToken: String?, - val userId: String + val userId: String, + val slidingSyncProxy: String? ) private val store = context.dataStore @@ -73,7 +74,8 @@ class PreferencesSessionStore @Inject constructor( homeserverUrl = session.homeserverUrl, isSoftLogout = session.isSoftLogout, refreshToken = session.refreshToken, - userId = session.userId + userId = session.userId, + slidingSyncProxy = session.slidingSyncProxy ) val encodedSession = Json.encodeToString(sessionData) prefs[sessionKey] = encodedSession @@ -90,7 +92,8 @@ class PreferencesSessionStore @Inject constructor( homeserverUrl = sessionData.homeserverUrl, isSoftLogout = sessionData.isSoftLogout, refreshToken = sessionData.refreshToken, - userId = sessionData.userId + userId = sessionData.userId, + slidingSyncProxy = sessionData.slidingSyncProxy ) } } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt index d2a7c1d8be..6bc1a7b0b9 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/timeline/RustMatrixTimeline.kt @@ -34,15 +34,25 @@ import org.matrix.rustcomponents.sdk.TimelineChange import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineListener import timber.log.Timber -import java.util.* +import java.util.Collections class RustMatrixTimeline( private val matrixRoom: RustMatrixRoom, - private val room: Room, + private val innerRoom: Room, private val slidingSyncRoom: SlidingSyncRoom, private val coroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, -) : TimelineListener, MatrixTimeline { +) : MatrixTimeline { + + private val innerTimelineListener = object : TimelineListener { + override fun onUpdate(update: TimelineDiff) { + coroutineScope.launch { + updateTimelineItems { + applyDiff(update) + } + } + } + } override var callback: MatrixTimeline.Callback? = null @@ -59,46 +69,38 @@ class RustMatrixTimeline( private fun MutableList.applyDiff(diff: TimelineDiff) { when (diff.change()) { TimelineChange.PUSH -> { - Timber.v("Apply push on list with size: $size") val item = diff.push()?.asMatrixTimelineItem() ?: return callback?.onPushedTimelineItem(item) add(item) } TimelineChange.UPDATE_AT -> { val updateAtData = diff.updateAt() ?: return - Timber.v("Apply $updateAtData on list with size: $size") val item = updateAtData.item.asMatrixTimelineItem() callback?.onUpdatedTimelineItem(item) set(updateAtData.index.toInt(), item) } TimelineChange.INSERT_AT -> { val insertAtData = diff.insertAt() ?: return - Timber.v("Apply $insertAtData on list with size: $size") val item = insertAtData.item.asMatrixTimelineItem() add(insertAtData.index.toInt(), item) } TimelineChange.MOVE -> { val moveData = diff.move() ?: return - Timber.v("Apply $moveData on list with size: $size") Collections.swap(this, moveData.oldIndex.toInt(), moveData.newIndex.toInt()) } TimelineChange.REMOVE_AT -> { val removeAtData = diff.removeAt() ?: return - Timber.v("Apply $removeAtData on list with size: $size") removeAt(removeAtData.toInt()) } TimelineChange.REPLACE -> { - Timber.v("Apply REPLACE on list with size: $size") clear() val items = diff.replace()?.map { it.asMatrixTimelineItem() } ?: return addAll(items) } TimelineChange.POP -> { - Timber.v("Apply POP on list with size: $size") removeLast() } TimelineChange.CLEAR -> { - Timber.v("Apply CLEAR on list with size: $size") clear() } } @@ -106,11 +108,16 @@ class RustMatrixTimeline( override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result = withContext(coroutineDispatchers.io) { runCatching { + Timber.v("Start back paginating for room ${slidingSyncRoom.roomId()} ") val paginationOptions = PaginationOptions.UntilNumItems( eventLimit = requestSize.toUShort(), items = untilNumberOfItems.toUShort() ) - room.paginateBackwards(paginationOptions) + innerRoom.paginateBackwards(paginationOptions) + }.onFailure { + Timber.e(it, "Fail to paginate for room ${slidingSyncRoom.roomId()}") + }.onSuccess { + Timber.v("Success back paginating for room ${slidingSyncRoom.roomId()}") } } @@ -122,14 +129,24 @@ class RustMatrixTimeline( } override fun addListener(timelineListener: TimelineListener) { - listenerTokens += slidingSyncRoom.subscribeAndAddTimelineListener(timelineListener, settings = null) + listenerTokens += slidingSyncRoom.subscribeAndAddTimelineListener(timelineListener, null) } override fun initialize() { - addListener(this) + Timber.v("Init timeline for room ${slidingSyncRoom.roomId()}") + coroutineScope.launch { + matrixRoom.fetchMembers() + .onFailure { + Timber.e(it, "Fail to fetch members for room ${slidingSyncRoom.roomId()}") + }.onSuccess { + Timber.v("Success fetching members for room ${slidingSyncRoom.roomId()}") + } + } + addListener(innerTimelineListener) } override fun dispose() { + Timber.v("Dispose timeline for room ${slidingSyncRoom.roomId()}") listenerTokens.dispose() } @@ -147,12 +164,4 @@ class RustMatrixTimeline( override suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result { return matrixRoom.replyMessage(inReplyToEventId, message) } - - override fun onUpdate(update: TimelineDiff) { - coroutineScope.launch { - updateTimelineItems { - applyDiff(update) - } - } - } } diff --git a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt index bc64e52770..747b93f287 100644 --- a/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt +++ b/libraries/matrix/src/main/kotlin/io/element/android/libraries/matrix/tracing/TracingConfiguration.kt @@ -23,15 +23,16 @@ data class TracingConfiguration( ) { // Order should matters - private val targets = mutableMapOf( - Target.MatrixSdk.HttpClient to LogLevel.Trace, - Target.MatrixSdk.SlidingSync to LogLevel.Trace, - Target.MatrixSdk.BaseSlidingSync to LogLevel.Trace, - Target.MatrixSdk.Root to LogLevel.Warn, - Target.MatrixSdk.Sled to LogLevel.Warn, + private val targets: MutableMap = mutableMapOf( + Target.Common to LogLevel.Warn, Target.Hyper to LogLevel.Warn, Target.Sled to LogLevel.Warn, - Target.Common to LogLevel.Warn, + Target.MatrixSdk.Root to LogLevel.Warn, + Target.MatrixSdk.Sled to LogLevel.Warn, + Target.MatrixSdk.Crypto to LogLevel.Debug, + Target.MatrixSdk.HttpClient to LogLevel.Debug, + Target.MatrixSdk.SlidingSync to LogLevel.Trace, + Target.MatrixSdk.BaseSlidingSync to LogLevel.Trace, ) val filter: String @@ -56,6 +57,7 @@ sealed class Target(open val filter: String) { sealed class MatrixSdk(override val filter: String) : Target(filter) { object Root : MatrixSdk("matrix_sdk") object Sled : MatrixSdk("matrix_sdk_sled") + object Crypto: MatrixSdk("matrix_sdk_crypto") object FFI : MatrixSdk("matrix_sdk_ffi") object HttpClient : MatrixSdk("matrix_sdk::http_client") object UniffiAPI : MatrixSdk("matrix_sdk_ffi::uniffi_api") diff --git a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt index 76da14418d..cb1ce7538b 100644 --- a/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt +++ b/libraries/matrixtest/src/main/kotlin/io/element/android/libraries/matrixtest/room/FakeMatrixRoom.kt @@ -41,6 +41,10 @@ class FakeMatrixRoom( return FakeMatrixTimeline() } + override suspend fun fetchMembers(): Result { + return Result.success(Unit) + } + override suspend fun userDisplayName(userId: String): Result { return Result.success("") }