Add fetchMembers and use it when init timeline (import again rust-sdk with fix on poisoning)

This commit is contained in:
ganfra 2023-02-14 20:13:47 +01:00
parent 2176aa8e5a
commit 9526eac81c
13 changed files with 106 additions and 107 deletions

View file

@ -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.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.utils.PairCombinedPreviewParameter 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.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber
@Composable @Composable
fun TimelineView( fun TimelineView(
@ -117,14 +117,6 @@ fun TimelineView(
onLongClick = onMessageLongClicked onLongClick = onMessageLongClicked
) )
} }
/*
if (state.hasMoreToLoad) {
item {
TimelineLoadingMoreIndicator()
}
}
*/
} }
fun onReachedLoadMore() { fun onReachedLoadMore() {
@ -165,12 +157,23 @@ fun TimelineItemRow(
is TimelineItem.Virtual -> TimelineItemVirtualRow( is TimelineItem.Virtual -> TimelineItemVirtualRow(
virtual = timelineItem virtual = timelineItem
) )
is TimelineItem.Event -> TimelineItemEventRow( is TimelineItem.Event -> {
event = timelineItem,
isHighlighted = isHighlighted, fun onClick() {
onClick = { onClick(timelineItem) }, onClick(timelineItem)
onLongClick = { onLongClick(timelineItem) } }
)
fun onLongClick() {
onLongClick(timelineItem)
}
TimelineItemEventRow(
event = timelineItem,
isHighlighted = isHighlighted,
onClick = ::onClick,
onLongClick = ::onLongClick
)
}
} }
} }

View file

@ -33,7 +33,7 @@ class TimelineItemEventFactory @Inject constructor(
private val contentFactory: TimelineItemContentFactory, private val contentFactory: TimelineItemContentFactory,
) { ) {
suspend fun create( fun create(
currentTimelineItem: MatrixTimelineItem.Event, currentTimelineItem: MatrixTimelineItem.Event,
index: Int, index: Int,
timelineItems: List<MatrixTimelineItem>, timelineItems: List<MatrixTimelineItem>,

View file

@ -29,7 +29,7 @@ class TimelineItemVirtualFactory @Inject constructor(
private val daySeparatorFactory: TimelineItemDaySeparatorFactory, private val daySeparatorFactory: TimelineItemDaySeparatorFactory,
) { ) {
suspend fun create( fun create(
currentTimelineItem: MatrixTimelineItem.Virtual, currentTimelineItem: MatrixTimelineItem.Virtual,
index: Int, index: Int,
timelineItems: List<MatrixTimelineItem>, timelineItems: List<MatrixTimelineItem>,

View file

@ -24,11 +24,19 @@ import io.element.android.libraries.matrix.core.EventId
@Immutable @Immutable
sealed interface TimelineItem { sealed interface TimelineItem {
fun identifier(): String = when(this){
is Event -> id
is Virtual -> id
}
@Immutable
data class Virtual( data class Virtual(
val id: String, val id: String,
val model: TimelineItemVirtualModel val model: TimelineItemVirtualModel
) : TimelineItem ) : TimelineItem
@Immutable
data class Event( data class Event(
val id: String, val id: String,
val eventId: EventId? = null, val eventId: EventId? = null,

View file

@ -43,7 +43,7 @@ import timber.log.Timber
import java.io.File import java.io.File
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
internal class RustMatrixClient internal constructor( class RustMatrixClient constructor(
private val client: Client, private val client: Client,
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
@ -132,11 +132,11 @@ internal class RustMatrixClient internal constructor(
override fun getRoom(roomId: RoomId): MatrixRoom? { override fun getRoom(roomId: RoomId): MatrixRoom? {
val slidingSyncRoom = slidingSync.getRoom(roomId.value) ?: return null val slidingSyncRoom = slidingSync.getRoom(roomId.value) ?: return null
val room = slidingSyncRoom.fullRoom() ?: return null val fullRoom = slidingSyncRoom.fullRoom() ?: return null
return RustMatrixRoom( return RustMatrixRoom(
slidingSyncUpdateFlow = slidingSyncObserverProxy.updateSummaryFlow, slidingSyncUpdateFlow = slidingSyncObserverProxy.updateSummaryFlow,
slidingSyncRoom = slidingSyncRoom, slidingSyncRoom = slidingSyncRoom,
room = room, innerRoom = fullRoom,
coroutineScope = coroutineScope, coroutineScope = coroutineScope,
coroutineDispatchers = dispatchers coroutineDispatchers = dispatchers
) )

View file

@ -31,6 +31,6 @@ object MatrixModule {
@Provides @Provides
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
fun providesRustAuthenticationService(baseDirectory: File): AuthenticationService { fun providesRustAuthenticationService(baseDirectory: File): AuthenticationService {
return AuthenticationService(baseDirectory.absolutePath, null) return AuthenticationService(baseDirectory.absolutePath, null, null)
} }
} }

View file

@ -33,6 +33,8 @@ interface MatrixRoom {
fun timeline(): MatrixTimeline fun timeline(): MatrixTimeline
suspend fun fetchMembers(): Result<Unit>
suspend fun userDisplayName(userId: String): Result<String?> suspend fun userDisplayName(userId: String): Result<String?>
suspend fun userAvatarUrl(userId: String): Result<String?> suspend fun userAvatarUrl(userId: String): Result<String?>
@ -44,4 +46,5 @@ interface MatrixRoom {
suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> suspend fun replyMessage(eventId: EventId, message: String): Result<Unit>
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit> suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
} }

View file

@ -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<TimelineDiff> = callbackFlow {
val listener = object : TimelineListener {
override fun onUpdate(update: TimelineDiff) {
scope.launch {
send(update)
}
}
}
addTimelineListener(listener)
awaitClose {
removeTimeline()
}
}

View file

@ -32,11 +32,12 @@ import org.matrix.rustcomponents.sdk.SlidingSyncRoom
import org.matrix.rustcomponents.sdk.UpdateSummary import org.matrix.rustcomponents.sdk.UpdateSummary
import org.matrix.rustcomponents.sdk.genTransactionId import org.matrix.rustcomponents.sdk.genTransactionId
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
import timber.log.Timber
class RustMatrixRoom( class RustMatrixRoom(
private val slidingSyncUpdateFlow: Flow<UpdateSummary>, private val slidingSyncUpdateFlow: Flow<UpdateSummary>,
private val slidingSyncRoom: SlidingSyncRoom, private val slidingSyncRoom: SlidingSyncRoom,
private val room: Room, private val innerRoom: Room,
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
) : MatrixRoom { ) : MatrixRoom {
@ -44,7 +45,7 @@ class RustMatrixRoom(
override fun syncUpdateFlow(): Flow<Long> { override fun syncUpdateFlow(): Flow<Long> {
return slidingSyncUpdateFlow return slidingSyncUpdateFlow
.filter { .filter {
it.rooms.contains(room.id()) it.rooms.contains(innerRoom.id())
} }
.map { .map {
System.currentTimeMillis() System.currentTimeMillis()
@ -55,14 +56,14 @@ class RustMatrixRoom(
override fun timeline(): MatrixTimeline { override fun timeline(): MatrixTimeline {
return RustMatrixTimeline( return RustMatrixTimeline(
matrixRoom = this, matrixRoom = this,
room = room, innerRoom = innerRoom,
slidingSyncRoom = slidingSyncRoom, slidingSyncRoom = slidingSyncRoom,
coroutineScope = coroutineScope, coroutineScope = coroutineScope,
coroutineDispatchers = coroutineDispatchers coroutineDispatchers = coroutineDispatchers
) )
} }
override val roomId = RoomId(room.id()) override val roomId = RoomId(innerRoom.id())
override val name: String? override val name: String?
get() { get() {
@ -71,35 +72,41 @@ class RustMatrixRoom(
override val bestName: String override val bestName: String
get() { get() {
return name?.takeIf { it.isNotEmpty() } ?: room.id() return name?.takeIf { it.isNotEmpty() } ?: innerRoom.id()
} }
override val displayName: String override val displayName: String
get() { get() {
return room.displayName() return innerRoom.displayName()
} }
override val topic: String? override val topic: String?
get() { get() {
return room.topic() return innerRoom.topic()
} }
override val avatarUrl: String? override val avatarUrl: String?
get() { get() {
return room.avatarUrl() return innerRoom.avatarUrl()
} }
override suspend fun fetchMembers(): Result<Unit> = withContext(coroutineDispatchers.io) {
runCatching {
innerRoom.fetchMembers()
}
}
override suspend fun userDisplayName(userId: String): Result<String?> = override suspend fun userDisplayName(userId: String): Result<String?> =
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
runCatching { runCatching {
room.memberDisplayName(userId) innerRoom.memberDisplayName(userId)
} }
} }
override suspend fun userAvatarUrl(userId: String): Result<String?> = override suspend fun userAvatarUrl(userId: String): Result<String?> =
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
runCatching { runCatching {
room.memberAvatarUrl(userId) innerRoom.memberAvatarUrl(userId)
} }
} }
@ -107,7 +114,7 @@ class RustMatrixRoom(
val transactionId = genTransactionId() val transactionId = genTransactionId()
val content = messageEventContentFromMarkdown(message) val content = messageEventContentFromMarkdown(message)
runCatching { runCatching {
room.send(content, transactionId) innerRoom.send(content, transactionId)
} }
} }
@ -115,7 +122,7 @@ class RustMatrixRoom(
val transactionId = genTransactionId() val transactionId = genTransactionId()
// val content = messageEventContentFromMarkdown(message) // val content = messageEventContentFromMarkdown(message)
runCatching { 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 transactionId = genTransactionId()
// val content = messageEventContentFromMarkdown(message) // val content = messageEventContentFromMarkdown(message)
runCatching { 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) { override suspend fun redactEvent(eventId: EventId, reason: String?) = withContext(coroutineDispatchers.io) {
val transactionId = genTransactionId() val transactionId = genTransactionId()
runCatching { runCatching {
room.redact(eventId.value, reason, transactionId) innerRoom.redact(eventId.value, reason, transactionId)
} }
} }
} }

View file

@ -54,7 +54,8 @@ class PreferencesSessionStore @Inject constructor(
val homeserverUrl: String, val homeserverUrl: String,
val isSoftLogout: Boolean, val isSoftLogout: Boolean,
val refreshToken: String?, val refreshToken: String?,
val userId: String val userId: String,
val slidingSyncProxy: String?
) )
private val store = context.dataStore private val store = context.dataStore
@ -73,7 +74,8 @@ class PreferencesSessionStore @Inject constructor(
homeserverUrl = session.homeserverUrl, homeserverUrl = session.homeserverUrl,
isSoftLogout = session.isSoftLogout, isSoftLogout = session.isSoftLogout,
refreshToken = session.refreshToken, refreshToken = session.refreshToken,
userId = session.userId userId = session.userId,
slidingSyncProxy = session.slidingSyncProxy
) )
val encodedSession = Json.encodeToString(sessionData) val encodedSession = Json.encodeToString(sessionData)
prefs[sessionKey] = encodedSession prefs[sessionKey] = encodedSession
@ -90,7 +92,8 @@ class PreferencesSessionStore @Inject constructor(
homeserverUrl = sessionData.homeserverUrl, homeserverUrl = sessionData.homeserverUrl,
isSoftLogout = sessionData.isSoftLogout, isSoftLogout = sessionData.isSoftLogout,
refreshToken = sessionData.refreshToken, refreshToken = sessionData.refreshToken,
userId = sessionData.userId userId = sessionData.userId,
slidingSyncProxy = sessionData.slidingSyncProxy
) )
} }
} }

View file

@ -34,15 +34,25 @@ import org.matrix.rustcomponents.sdk.TimelineChange
import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineDiff
import org.matrix.rustcomponents.sdk.TimelineListener import org.matrix.rustcomponents.sdk.TimelineListener
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.Collections
class RustMatrixTimeline( class RustMatrixTimeline(
private val matrixRoom: RustMatrixRoom, private val matrixRoom: RustMatrixRoom,
private val room: Room, private val innerRoom: Room,
private val slidingSyncRoom: SlidingSyncRoom, private val slidingSyncRoom: SlidingSyncRoom,
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers, 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 override var callback: MatrixTimeline.Callback? = null
@ -59,46 +69,38 @@ class RustMatrixTimeline(
private fun MutableList<MatrixTimelineItem>.applyDiff(diff: TimelineDiff) { private fun MutableList<MatrixTimelineItem>.applyDiff(diff: TimelineDiff) {
when (diff.change()) { when (diff.change()) {
TimelineChange.PUSH -> { TimelineChange.PUSH -> {
Timber.v("Apply push on list with size: $size")
val item = diff.push()?.asMatrixTimelineItem() ?: return val item = diff.push()?.asMatrixTimelineItem() ?: return
callback?.onPushedTimelineItem(item) callback?.onPushedTimelineItem(item)
add(item) add(item)
} }
TimelineChange.UPDATE_AT -> { TimelineChange.UPDATE_AT -> {
val updateAtData = diff.updateAt() ?: return val updateAtData = diff.updateAt() ?: return
Timber.v("Apply $updateAtData on list with size: $size")
val item = updateAtData.item.asMatrixTimelineItem() val item = updateAtData.item.asMatrixTimelineItem()
callback?.onUpdatedTimelineItem(item) callback?.onUpdatedTimelineItem(item)
set(updateAtData.index.toInt(), item) set(updateAtData.index.toInt(), item)
} }
TimelineChange.INSERT_AT -> { TimelineChange.INSERT_AT -> {
val insertAtData = diff.insertAt() ?: return val insertAtData = diff.insertAt() ?: return
Timber.v("Apply $insertAtData on list with size: $size")
val item = insertAtData.item.asMatrixTimelineItem() val item = insertAtData.item.asMatrixTimelineItem()
add(insertAtData.index.toInt(), item) add(insertAtData.index.toInt(), item)
} }
TimelineChange.MOVE -> { TimelineChange.MOVE -> {
val moveData = diff.move() ?: return val moveData = diff.move() ?: return
Timber.v("Apply $moveData on list with size: $size")
Collections.swap(this, moveData.oldIndex.toInt(), moveData.newIndex.toInt()) Collections.swap(this, moveData.oldIndex.toInt(), moveData.newIndex.toInt())
} }
TimelineChange.REMOVE_AT -> { TimelineChange.REMOVE_AT -> {
val removeAtData = diff.removeAt() ?: return val removeAtData = diff.removeAt() ?: return
Timber.v("Apply $removeAtData on list with size: $size")
removeAt(removeAtData.toInt()) removeAt(removeAtData.toInt())
} }
TimelineChange.REPLACE -> { TimelineChange.REPLACE -> {
Timber.v("Apply REPLACE on list with size: $size")
clear() clear()
val items = diff.replace()?.map { it.asMatrixTimelineItem() } ?: return val items = diff.replace()?.map { it.asMatrixTimelineItem() } ?: return
addAll(items) addAll(items)
} }
TimelineChange.POP -> { TimelineChange.POP -> {
Timber.v("Apply POP on list with size: $size")
removeLast() removeLast()
} }
TimelineChange.CLEAR -> { TimelineChange.CLEAR -> {
Timber.v("Apply CLEAR on list with size: $size")
clear() clear()
} }
} }
@ -106,11 +108,16 @@ class RustMatrixTimeline(
override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> = withContext(coroutineDispatchers.io) { override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> = withContext(coroutineDispatchers.io) {
runCatching { runCatching {
Timber.v("Start back paginating for room ${slidingSyncRoom.roomId()} ")
val paginationOptions = PaginationOptions.UntilNumItems( val paginationOptions = PaginationOptions.UntilNumItems(
eventLimit = requestSize.toUShort(), eventLimit = requestSize.toUShort(),
items = untilNumberOfItems.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) { override fun addListener(timelineListener: TimelineListener) {
listenerTokens += slidingSyncRoom.subscribeAndAddTimelineListener(timelineListener, settings = null) listenerTokens += slidingSyncRoom.subscribeAndAddTimelineListener(timelineListener, null)
} }
override fun initialize() { 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() { override fun dispose() {
Timber.v("Dispose timeline for room ${slidingSyncRoom.roomId()}")
listenerTokens.dispose() listenerTokens.dispose()
} }
@ -147,12 +164,4 @@ class RustMatrixTimeline(
override suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result<Unit> { override suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result<Unit> {
return matrixRoom.replyMessage(inReplyToEventId, message) return matrixRoom.replyMessage(inReplyToEventId, message)
} }
override fun onUpdate(update: TimelineDiff) {
coroutineScope.launch {
updateTimelineItems {
applyDiff(update)
}
}
}
} }

View file

@ -23,15 +23,16 @@ data class TracingConfiguration(
) { ) {
// Order should matters // Order should matters
private val targets = mutableMapOf( private val targets: MutableMap<Target, LogLevel> = mutableMapOf(
Target.MatrixSdk.HttpClient to LogLevel.Trace, Target.Common to LogLevel.Warn,
Target.MatrixSdk.SlidingSync to LogLevel.Trace,
Target.MatrixSdk.BaseSlidingSync to LogLevel.Trace,
Target.MatrixSdk.Root to LogLevel.Warn,
Target.MatrixSdk.Sled to LogLevel.Warn,
Target.Hyper to LogLevel.Warn, Target.Hyper to LogLevel.Warn,
Target.Sled 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 val filter: String
@ -56,6 +57,7 @@ sealed class Target(open val filter: String) {
sealed class MatrixSdk(override val filter: String) : Target(filter) { sealed class MatrixSdk(override val filter: String) : Target(filter) {
object Root : MatrixSdk("matrix_sdk") object Root : MatrixSdk("matrix_sdk")
object Sled : MatrixSdk("matrix_sdk_sled") object Sled : MatrixSdk("matrix_sdk_sled")
object Crypto: MatrixSdk("matrix_sdk_crypto")
object FFI : MatrixSdk("matrix_sdk_ffi") object FFI : MatrixSdk("matrix_sdk_ffi")
object HttpClient : MatrixSdk("matrix_sdk::http_client") object HttpClient : MatrixSdk("matrix_sdk::http_client")
object UniffiAPI : MatrixSdk("matrix_sdk_ffi::uniffi_api") object UniffiAPI : MatrixSdk("matrix_sdk_ffi::uniffi_api")

View file

@ -41,6 +41,10 @@ class FakeMatrixRoom(
return FakeMatrixTimeline() return FakeMatrixTimeline()
} }
override suspend fun fetchMembers(): Result<Unit> {
return Result.success(Unit)
}
override suspend fun userDisplayName(userId: String): Result<String?> { override suspend fun userDisplayName(userId: String): Result<String?> {
return Result.success("") return Result.success("")
} }