Room/Timeline: simplify the apis
This commit is contained in:
@@ -18,7 +18,6 @@ package io.element.android.appnav
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
@@ -83,6 +82,7 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
Timber.v("OnCreate")
|
||||
inputs.room.open()
|
||||
plugins<LifecycleCallback>().forEach { it.onFlowCreated(id, inputs.room) }
|
||||
appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId)
|
||||
fetchRoomMembers()
|
||||
@@ -149,18 +149,8 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
data class RoomMemberDetails(val userId: UserId) : NavTarget
|
||||
}
|
||||
|
||||
private val timeline = inputs.room.timeline()
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
timeline.initialize()
|
||||
onDispose {
|
||||
timeline.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
Children(
|
||||
navModel = backstack,
|
||||
modifier = modifier,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.core.coroutine
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.plus
|
||||
|
||||
fun childScopeOf(
|
||||
parentScope: CoroutineScope,
|
||||
dispatcher: CoroutineDispatcher,
|
||||
name: String,
|
||||
): CoroutineScope = run {
|
||||
val supervisorJob = SupervisorJob(parent = parentScope.coroutineContext.job)
|
||||
parentScope + dispatcher + supervisorJob + CoroutineName(name)
|
||||
}
|
||||
@@ -61,6 +61,8 @@ interface MatrixRoom : Closeable {
|
||||
|
||||
fun timeline(): MatrixTimeline
|
||||
|
||||
fun open(): Result<Unit>
|
||||
|
||||
suspend fun userDisplayName(userId: UserId): Result<String?>
|
||||
|
||||
suspend fun userAvatarUrl(userId: UserId): Result<String?>
|
||||
|
||||
@@ -28,20 +28,8 @@ interface MatrixTimeline {
|
||||
)
|
||||
|
||||
fun paginationState(): StateFlow<PaginationState>
|
||||
|
||||
fun timelineItems(): Flow<List<MatrixTimelineItem>>
|
||||
|
||||
suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit>
|
||||
fun initialize()
|
||||
fun dispose()
|
||||
|
||||
/**
|
||||
* @param message markdown message
|
||||
*/
|
||||
suspend fun sendMessage(message: String): Result<Unit>
|
||||
|
||||
suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit>
|
||||
|
||||
suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result<Unit>
|
||||
|
||||
suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit>
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScopeOf
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
@@ -39,6 +40,7 @@ import io.element.android.libraries.matrix.impl.notification.RustNotificationSer
|
||||
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomSummaryDataSource
|
||||
import io.element.android.libraries.matrix.impl.room.roomOrNull
|
||||
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
|
||||
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
||||
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
|
||||
@@ -47,7 +49,7 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -66,7 +68,7 @@ import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility
|
||||
class RustMatrixClient constructor(
|
||||
private val client: Client,
|
||||
private val sessionStore: SessionStore,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val baseDirectory: File,
|
||||
private val baseCacheDirectory: File,
|
||||
@@ -75,13 +77,13 @@ class RustMatrixClient constructor(
|
||||
|
||||
override val sessionId: UserId = UserId(client.userId())
|
||||
|
||||
private val sessionCoroutineScope = childScopeOf(appCoroutineScope, dispatchers.main, "Session-${sessionId}")
|
||||
private val verificationService = RustSessionVerificationService()
|
||||
private val pushersService = RustPushersService(
|
||||
client = client,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
private val notificationService = RustNotificationService(client)
|
||||
private var slidingSyncUpdateJob: Job? = null
|
||||
|
||||
private val clientDelegate = object : ClientDelegate {
|
||||
override fun didReceiveAuthError(isSoftLogout: Boolean) {
|
||||
@@ -95,6 +97,7 @@ class RustMatrixClient constructor(
|
||||
private val rustRoomSummaryDataSource: RustRoomSummaryDataSource =
|
||||
RustRoomSummaryDataSource(
|
||||
roomList,
|
||||
sessionCoroutineScope,
|
||||
dispatchers,
|
||||
)
|
||||
|
||||
@@ -104,6 +107,7 @@ class RustMatrixClient constructor(
|
||||
private val rustInvitesDataSource: RustRoomSummaryDataSource =
|
||||
RustRoomSummaryDataSource(
|
||||
roomList,
|
||||
sessionCoroutineScope,
|
||||
dispatchers,
|
||||
)
|
||||
|
||||
@@ -127,14 +131,15 @@ class RustMatrixClient constructor(
|
||||
}
|
||||
|
||||
override fun getRoom(roomId: RoomId): MatrixRoom? {
|
||||
val roomListItem = roomList.room(roomId.value)
|
||||
val roomListItem = roomList.roomOrNull(roomId.value) ?: return null
|
||||
val fullRoom = roomListItem.fullRoom()
|
||||
return RustMatrixRoom(
|
||||
sessionId = sessionId,
|
||||
roomListItem = roomListItem,
|
||||
innerRoom = fullRoom,
|
||||
coroutineScope = coroutineScope,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
coroutineDispatchers = dispatchers,
|
||||
systemClock = clock
|
||||
)
|
||||
}
|
||||
|
||||
@@ -231,10 +236,8 @@ class RustMatrixClient constructor(
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
slidingSyncUpdateJob?.cancel()
|
||||
stopSync()
|
||||
rustRoomSummaryDataSource.close()
|
||||
rustInvitesDataSource.close()
|
||||
sessionCoroutineScope.cancel()
|
||||
client.setDelegate(null)
|
||||
verificationService.destroy()
|
||||
roomList.destroy()
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.matrix.rustcomponents.sdk.AuthenticationService as RustAuthentication
|
||||
class RustMatrixAuthenticationService @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val baseDirectory: File,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val sessionStore: SessionStore,
|
||||
private val clock: SystemClock,
|
||||
@@ -179,7 +179,7 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||
return RustMatrixClient(
|
||||
client = client,
|
||||
sessionStore = sessionStore,
|
||||
coroutineScope = coroutineScope,
|
||||
appCoroutineScope = appCoroutineScope,
|
||||
dispatchers = coroutineDispatchers,
|
||||
baseDirectory = baseDirectory,
|
||||
baseCacheDirectory = context.cacheDir,
|
||||
|
||||
@@ -7,10 +7,12 @@ import org.matrix.rustcomponents.sdk.RoomList
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomListState
|
||||
import org.matrix.rustcomponents.sdk.RoomListStateListener
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncListLoadingState
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncListStateObserver
|
||||
import timber.log.Timber
|
||||
|
||||
fun RoomList.stateFlow(): Flow<RoomListState> =
|
||||
mxCallbackFlow {
|
||||
@@ -46,3 +48,11 @@ fun RoomList.roomListEntriesUpdateFlow(onInitialList: suspend (List<RoomListEntr
|
||||
result.entriesStream
|
||||
}
|
||||
|
||||
fun RoomList.roomOrNull(roomId: String): RoomListItem? {
|
||||
return try {
|
||||
room(roomId)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "Failed finding room with id=$roomId")
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScopeOf
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
@@ -30,22 +31,25 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.roomMembers
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
import io.element.android.libraries.matrix.impl.media.map
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.timeline.timelineDiffFlow
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.RequiredState
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.genTransactionId
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||
import java.io.File
|
||||
@@ -54,40 +58,73 @@ class RustMatrixRoom(
|
||||
override val sessionId: SessionId,
|
||||
private val roomListItem: RoomListItem,
|
||||
private val innerRoom: Room,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val systemClock: SystemClock,
|
||||
) : MatrixRoom {
|
||||
|
||||
override val roomId = RoomId(innerRoom.id())
|
||||
|
||||
private val roomCoroutineScope = childScopeOf(sessionCoroutineScope, coroutineDispatchers.main, "RoomScope-$roomId")
|
||||
|
||||
override val membersStateFlow: StateFlow<MatrixRoomMembersState>
|
||||
get() = _membersStateFlow
|
||||
|
||||
private var _membersStateFlow = MutableStateFlow<MatrixRoomMembersState>(MatrixRoomMembersState.Unknown)
|
||||
private val isInit = MutableStateFlow(false)
|
||||
private val syncUpdateFlow = MutableStateFlow(systemClock.epochMillis())
|
||||
|
||||
private val timeline by lazy {
|
||||
RustMatrixTimeline(
|
||||
matrixRoom = this,
|
||||
innerRoom = innerRoom,
|
||||
roomListItem = roomListItem,
|
||||
coroutineScope = coroutineScope,
|
||||
coroutineScope = roomCoroutineScope,
|
||||
coroutineDispatchers = coroutineDispatchers
|
||||
)
|
||||
}
|
||||
|
||||
override fun syncUpdateFlow(): Flow<Long> {
|
||||
//TODO branch this somehow...
|
||||
return emptyFlow()
|
||||
return syncUpdateFlow
|
||||
}
|
||||
|
||||
override fun timeline(): MatrixTimeline {
|
||||
return timeline
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
innerRoom.destroy()
|
||||
roomListItem.destroy()
|
||||
override fun open(): Result<Unit> {
|
||||
if (isInit.value) return Result.failure(IllegalStateException("Listener already registered"))
|
||||
val settings = RoomSubscription(
|
||||
requiredState = listOf(
|
||||
RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""),
|
||||
RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""),
|
||||
RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""),
|
||||
RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""),
|
||||
),
|
||||
timelineLimit = null
|
||||
)
|
||||
roomListItem.subscribe(settings)
|
||||
innerRoom.timelineDiffFlow { initialList ->
|
||||
timeline.postItems(initialList)
|
||||
}.onEach {
|
||||
syncUpdateFlow.value = systemClock.epochMillis()
|
||||
timeline.postDiff(it)
|
||||
}.launchIn(roomCoroutineScope)
|
||||
roomCoroutineScope.launch {
|
||||
fetchMembers()
|
||||
}
|
||||
isInit.value = true
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override val roomId = RoomId(innerRoom.id())
|
||||
override fun close() {
|
||||
if(isInit.value) {
|
||||
isInit.value = false
|
||||
roomCoroutineScope.cancel()
|
||||
roomListItem.unsubscribe()
|
||||
innerRoom.destroy()
|
||||
roomListItem.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
override val name: String?
|
||||
get() {
|
||||
@@ -264,7 +301,7 @@ class RustMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: String): Result<Unit> =
|
||||
override suspend fun cancelSend(transactionId: String): Result<Unit> =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.cancelSend(transactionId)
|
||||
@@ -299,4 +336,10 @@ class RustMatrixRoom(
|
||||
innerRoom.setTopic(topic)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchMembers() = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.fetchMembers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -33,24 +31,21 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
||||
import org.matrix.rustcomponents.sdk.RoomListInput
|
||||
import org.matrix.rustcomponents.sdk.RoomListRange
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncListLoadingState
|
||||
import timber.log.Timber
|
||||
import java.io.Closeable
|
||||
import java.util.UUID
|
||||
|
||||
internal class RustRoomSummaryDataSource(
|
||||
private val roomList: RoomList,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||
) : RoomSummaryDataSource, Closeable {
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + coroutineDispatchers.io)
|
||||
) : RoomSummaryDataSource {
|
||||
|
||||
private val roomSummaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
private val loadingState = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded)
|
||||
|
||||
fun subscribeIfNeeded() {
|
||||
coroutineScope.launch {
|
||||
sessionCoroutineScope.launch {
|
||||
roomList.roomListEntriesUpdateFlow { roomListEntries ->
|
||||
val summaries = roomListEntries.map(::buildSummaryForRoomListEntry)
|
||||
updateRoomSummaries {
|
||||
@@ -64,10 +59,6 @@ internal class RustRoomSummaryDataSource(
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
coroutineScope.cancel()
|
||||
}
|
||||
|
||||
override fun roomSummaries(): StateFlow<List<RoomSummary>> {
|
||||
return roomSummaries
|
||||
}
|
||||
@@ -78,7 +69,7 @@ internal class RustRoomSummaryDataSource(
|
||||
|
||||
override fun setSlidingSyncRange(range: IntRange) {
|
||||
Timber.v("setVisibleRange=$range")
|
||||
coroutineScope.launch {
|
||||
sessionCoroutineScope.launch {
|
||||
val ranges = listOf(RoomListRange(range.first.toUInt(), range.last.toUInt()))
|
||||
roomList.applyInput(
|
||||
RoomListInput.Viewport(ranges)
|
||||
@@ -148,7 +139,8 @@ internal class RustRoomSummaryDataSource(
|
||||
}
|
||||
|
||||
private fun buildRoomSummaryForIdentifier(identifier: String): RoomSummary {
|
||||
return roomList.room(identifier).use { roomListItem ->
|
||||
val roomListItem = roomList.roomOrNull(identifier) ?: return RoomSummary.Empty(identifier)
|
||||
return roomListItem.use {
|
||||
roomListItem.fullRoom().use { fullRoom ->
|
||||
RoomSummary.Filled(
|
||||
details = roomSummaryDetailsFactory.create(roomListItem, fullRoom)
|
||||
|
||||
@@ -36,7 +36,7 @@ internal class MatrixTimelineDiffProcessor(
|
||||
private val timelineItemFactory: MatrixTimelineItemMapper,
|
||||
) {
|
||||
|
||||
fun onUpdate(diff: TimelineDiff) {
|
||||
fun postDiff(diff: TimelineDiff) {
|
||||
coroutineScope.launch {
|
||||
updateTimelineItems {
|
||||
applyDiff(diff)
|
||||
@@ -122,4 +122,5 @@ internal class MatrixTimelineDiffProcessor(
|
||||
private fun TimelineItem.asMatrixTimelineItem(): MatrixTimelineItem {
|
||||
return timelineItemFactory.map(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,40 +21,30 @@ import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.util.TaskHandleBag
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.PaginationOptions
|
||||
import org.matrix.rustcomponents.sdk.RequiredState
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.TimelineDiff
|
||||
import org.matrix.rustcomponents.sdk.TimelineItem
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class RustMatrixTimeline(
|
||||
coroutineScope: CoroutineScope,
|
||||
private val matrixRoom: MatrixRoom,
|
||||
private val innerRoom: Room,
|
||||
private val roomListItem: RoomListItem,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
) : MatrixTimeline {
|
||||
|
||||
private val isInit = AtomicBoolean(false)
|
||||
|
||||
private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>> =
|
||||
MutableStateFlow(emptyList())
|
||||
|
||||
@@ -81,7 +71,6 @@ class RustMatrixTimeline(
|
||||
timelineItemFactory = timelineItemFactory,
|
||||
)
|
||||
|
||||
private val listenerTokens = TaskHandleBag()
|
||||
override fun paginationState(): StateFlow<MatrixTimeline.PaginationState> {
|
||||
return paginationState
|
||||
}
|
||||
@@ -91,38 +80,12 @@ class RustMatrixTimeline(
|
||||
return timelineItems.sample(50)
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
Timber.v("Init timeline for room ${matrixRoom.roomId}")
|
||||
coroutineScope.launch {
|
||||
subscribeAndAddListener(this)
|
||||
.onSuccess {
|
||||
isInit.set(true)
|
||||
}
|
||||
.onFailure {
|
||||
Timber.e("Failed adding timeline listener on room with identifier: ${matrixRoom.roomId})")
|
||||
}
|
||||
}
|
||||
internal fun postItems(items: List<TimelineItem>) {
|
||||
timelineItems.value = items.map(timelineItemFactory::map)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
Timber.v("Dispose timeline for room ${matrixRoom.roomId}")
|
||||
listenerTokens.dispose()
|
||||
isInit.set(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message markdown message
|
||||
*/
|
||||
override suspend fun sendMessage(message: String): Result<Unit> {
|
||||
return matrixRoom.sendMessage(message)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> {
|
||||
return matrixRoom.editMessage(originalEventId, message = message)
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result<Unit> {
|
||||
return matrixRoom.replyMessage(inReplyToEventId, message)
|
||||
internal fun postDiff(timelineDiff: TimelineDiff) {
|
||||
timelineDiffProcessor.postDiff(timelineDiff)
|
||||
}
|
||||
|
||||
override suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
@@ -134,9 +97,6 @@ class RustMatrixTimeline(
|
||||
override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
Timber.v("Start back paginating for room ${matrixRoom.roomId} ")
|
||||
if (!isInit.get()) {
|
||||
throw IllegalStateException("Timeline is not init yet")
|
||||
}
|
||||
val paginationOptions = PaginationOptions.UntilNumItems(
|
||||
eventLimit = requestSize.toUShort(),
|
||||
items = untilNumberOfItems.toUShort(),
|
||||
@@ -149,30 +109,4 @@ class RustMatrixTimeline(
|
||||
Timber.v("Success back paginating for room ${matrixRoom.roomId}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeAndAddListener(coroutineScope: CoroutineScope): Result<Unit> {
|
||||
return runCatching {
|
||||
val settings = RoomSubscription(
|
||||
requiredState = listOf(
|
||||
RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""),
|
||||
RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""),
|
||||
RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""),
|
||||
RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""),
|
||||
),
|
||||
timelineLimit = null
|
||||
)
|
||||
roomListItem.subscribe(settings)
|
||||
innerRoom.timelineDiffFlow { initialList ->
|
||||
timelineItems.value = initialList.map(timelineItemFactory::map)
|
||||
}.onEach {
|
||||
timelineDiffProcessor.onUpdate(it)
|
||||
}.launchIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchMembers() = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.fetchMembers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,18 +71,6 @@ class FakeMatrixTimeline(
|
||||
isInitialized = false
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class MainActivity : ComponentActivity() {
|
||||
RustMatrixAuthenticationService(
|
||||
context = applicationContext,
|
||||
baseDirectory = baseDirectory,
|
||||
coroutineScope = Singleton.appScope,
|
||||
appCoroutineScope = Singleton.appScope,
|
||||
coroutineDispatchers = Singleton.coroutineDispatchers,
|
||||
sessionStore = InMemorySessionStore(),
|
||||
clock = DefaultSystemClock()
|
||||
|
||||
Reference in New Issue
Block a user