Start filling MatrixRoom class and use it in MessagesScreen
This commit is contained in:
parent
823cef8b7c
commit
f176ad6105
11 changed files with 202 additions and 74 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
package io.element.android.x.features.messages
|
package io.element.android.x.features.messages
|
||||||
|
|
||||||
|
import Avatar
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
|
@ -12,18 +13,23 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import com.airbnb.mvrx.compose.collectAsState
|
import com.airbnb.mvrx.compose.collectAsState
|
||||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||||
import io.element.android.x.core.data.LogCompositions
|
import io.element.android.x.core.data.LogCompositions
|
||||||
|
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.x.features.messages.model.MessagesViewState
|
import io.element.android.x.features.messages.model.MessagesViewState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MessagesScreen(roomId: String) {
|
fun MessagesScreen(roomId: String) {
|
||||||
val viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId })
|
val viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId })
|
||||||
LogCompositions(tag = "MessagesScreen", msg = "Root")
|
LogCompositions(tag = "MessagesScreen", msg = "Root")
|
||||||
val roomTitle by viewModel.collectAsState(prop1 = MessagesViewState::roomTitle)
|
val roomTitle by viewModel.collectAsState(MessagesViewState::roomName)
|
||||||
MessagesContent(roomTitle)
|
val roomAvatar by viewModel.collectAsState(MessagesViewState::roomAvatar)
|
||||||
|
MessagesContent(roomTitle, roomAvatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MessagesContent(roomTitle: String) {
|
fun MessagesContent(
|
||||||
|
roomTitle: String?,
|
||||||
|
roomAvatar: AvatarData?
|
||||||
|
) {
|
||||||
val appBarState = rememberTopAppBarState()
|
val appBarState = rememberTopAppBarState()
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
||||||
LogCompositions(tag = "RoomListScreen", msg = "Content")
|
LogCompositions(tag = "RoomListScreen", msg = "Content")
|
||||||
|
|
@ -31,7 +37,14 @@ fun MessagesContent(roomTitle: String) {
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(text = roomTitle) }
|
navigationIcon = {
|
||||||
|
if (roomAvatar != null) {
|
||||||
|
IconButton(onClick = {}) {
|
||||||
|
Avatar(roomAvatar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = { Text(text = roomTitle ?: "") }
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
content = { padding ->
|
content = { padding ->
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,69 @@
|
||||||
package io.element.android.x.features.messages
|
package io.element.android.x.features.messages
|
||||||
|
|
||||||
import com.airbnb.mvrx.Fail
|
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.MavericksViewModel
|
import com.airbnb.mvrx.MavericksViewModel
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import io.element.android.x.core.data.parallelMap
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.x.features.messages.model.MessagesViewState
|
import io.element.android.x.features.messages.model.MessagesViewState
|
||||||
import io.element.android.x.matrix.MatrixClient
|
import io.element.android.x.matrix.MatrixClient
|
||||||
import io.element.android.x.matrix.MatrixInstance
|
import io.element.android.x.matrix.MatrixInstance
|
||||||
import io.element.android.x.matrix.room.RoomSummary
|
import io.element.android.x.matrix.room.MatrixRoom
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
||||||
|
|
||||||
class MessagesViewModel(initialState: MessagesViewState) :
|
class MessagesViewModel(
|
||||||
|
private val client: MatrixClient,
|
||||||
|
private val room: MatrixRoom,
|
||||||
|
private val initialState: MessagesViewState
|
||||||
|
) :
|
||||||
MavericksViewModel<MessagesViewState>(initialState) {
|
MavericksViewModel<MessagesViewState>(initialState) {
|
||||||
|
|
||||||
private val matrix = MatrixInstance.getInstance()
|
companion object : MavericksViewModelFactory<MessagesViewModel, MessagesViewState> {
|
||||||
|
|
||||||
|
override fun create(
|
||||||
|
viewModelContext: ViewModelContext,
|
||||||
|
state: MessagesViewState
|
||||||
|
): MessagesViewModel? {
|
||||||
|
val matrix = MatrixInstance.getInstance()
|
||||||
|
val client = matrix.activeClient()
|
||||||
|
val room = client.getRoom(state.roomId) ?: return null
|
||||||
|
return MessagesViewModel(client, room, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
handleInit()
|
handleInit()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleInit() {
|
private fun handleInit() {
|
||||||
viewModelScope.launch {
|
room.syncUpdateFlow()
|
||||||
|
.onEach {
|
||||||
|
val avatarData =
|
||||||
}
|
loadAvatarData(room.name ?: room.roomId.value, room.avatarUrl, AvatarSize.SMALL)
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
roomName = room.name, roomAvatar = avatarData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadAvatarData(
|
private suspend fun loadAvatarData(
|
||||||
client: MatrixClient,
|
|
||||||
name: String,
|
name: String,
|
||||||
url: String?,
|
url: String?,
|
||||||
size: AvatarSize = AvatarSize.MEDIUM
|
size: AvatarSize = AvatarSize.MEDIUM
|
||||||
): AvatarData {
|
): AvatarData {
|
||||||
val mediaContent = url?.let {
|
val mediaContent = url?.let {
|
||||||
val mediaSource = mediaSourceFromUrl(it)
|
val mediaSource = mediaSourceFromUrl(it)
|
||||||
client.loadMediaThumbnailForSource(mediaSource, size.value.toLong(), size.value.toLong())
|
client.loadMediaThumbnailForSource(
|
||||||
|
mediaSource,
|
||||||
|
size.value.toLong(),
|
||||||
|
size.value.toLong()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return mediaContent?.fold(
|
return mediaContent?.fold(
|
||||||
{ it },
|
{ it },
|
||||||
|
|
@ -52,10 +73,6 @@ class MessagesViewModel(initialState: MessagesViewState) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getClient(): MatrixClient {
|
|
||||||
return matrix.matrixClient().first().get()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@ package io.element.android.x.features.messages.model
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.x.matrix.core.RoomId
|
|
||||||
|
|
||||||
data class MessagesViewState(
|
data class MessagesViewState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val roomTitle: String = "",
|
val roomName: String? = null,
|
||||||
val roomAvatar: AvatarData? = null
|
val roomAvatar: AvatarData? = null
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
constructor(roomId: String) : this(roomId = roomId, roomTitle = "", roomAvatar = null)
|
constructor(roomId: String) : this(roomId = roomId, roomName = null, roomAvatar = null)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ class RoomListViewModel(initialState: RoomListViewState) :
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getClient(): MatrixClient {
|
private suspend fun getClient(): MatrixClient {
|
||||||
return matrix.matrixClient().first().get()
|
return matrix.client().first().get()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import org.matrix.rustcomponents.sdk.AuthenticationService
|
||||||
import org.matrix.rustcomponents.sdk.Client
|
import org.matrix.rustcomponents.sdk.Client
|
||||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Optional
|
import java.util.*
|
||||||
|
|
||||||
class Matrix(
|
class Matrix(
|
||||||
coroutineScope: CoroutineScope,
|
private val coroutineScope: CoroutineScope,
|
||||||
context: Context,
|
context: Context,
|
||||||
) {
|
) {
|
||||||
private val coroutineDispatchers = CoroutineDispatchers(
|
private val coroutineDispatchers = CoroutineDispatchers(
|
||||||
|
|
@ -44,10 +44,14 @@ class Matrix(
|
||||||
return isLoggedIn
|
return isLoggedIn
|
||||||
}
|
}
|
||||||
|
|
||||||
fun matrixClient(): Flow<Optional<MatrixClient>> {
|
fun client(): Flow<Optional<MatrixClient>> {
|
||||||
return matrixClient
|
return matrixClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun activeClient(): MatrixClient {
|
||||||
|
return matrixClient.value.get()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
||||||
sessionStore.getStoredData()
|
sessionStore.getStoredData()
|
||||||
?.let { sessionData ->
|
?.let { sessionData ->
|
||||||
|
|
@ -80,6 +84,7 @@ class Matrix(
|
||||||
return MatrixClient(
|
return MatrixClient(
|
||||||
client = client,
|
client = client,
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
|
coroutineScope = coroutineScope,
|
||||||
dispatchers = coroutineDispatchers
|
dispatchers = coroutineDispatchers
|
||||||
).also {
|
).also {
|
||||||
matrixClient.value = Optional.of(it)
|
matrixClient.value = Optional.of(it)
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,14 @@ package io.element.android.x.matrix
|
||||||
|
|
||||||
import io.element.android.x.core.data.CoroutineDispatchers
|
import io.element.android.x.core.data.CoroutineDispatchers
|
||||||
import io.element.android.x.matrix.core.UserId
|
import io.element.android.x.matrix.core.UserId
|
||||||
|
import io.element.android.x.matrix.room.MatrixRoom
|
||||||
import io.element.android.x.matrix.room.RoomSummaryDataSource
|
import io.element.android.x.matrix.room.RoomSummaryDataSource
|
||||||
|
import io.element.android.x.matrix.room.RoomSummaryDetailsFactory
|
||||||
import io.element.android.x.matrix.room.RustRoomSummaryDataSource
|
import io.element.android.x.matrix.room.RustRoomSummaryDataSource
|
||||||
|
import io.element.android.x.matrix.room.message.RoomMessageFactory
|
||||||
import io.element.android.x.matrix.session.SessionStore
|
import io.element.android.x.matrix.session.SessionStore
|
||||||
|
import io.element.android.x.matrix.sync.SlidingSyncObserverProxy
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.*
|
import org.matrix.rustcomponents.sdk.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -13,6 +18,7 @@ import java.io.Closeable
|
||||||
class MatrixClient internal constructor(
|
class MatrixClient internal constructor(
|
||||||
private val client: Client,
|
private val client: Client,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
|
private val coroutineScope: CoroutineScope,
|
||||||
private val dispatchers: CoroutineDispatchers,
|
private val dispatchers: CoroutineDispatchers,
|
||||||
) : Closeable {
|
) : Closeable {
|
||||||
|
|
||||||
|
|
@ -30,19 +36,15 @@ class MatrixClient internal constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val slidingSyncObserver = object : SlidingSyncObserver {
|
|
||||||
override fun didReceiveSyncUpdate(summary: UpdateSummary) {
|
|
||||||
Timber.v("didReceiveSyncUpdate=$summary on Thread: ${Thread.currentThread()}")
|
|
||||||
roomSummaryDataSource.updateRoomsWithIdentifiers(summary.rooms)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val slidingSyncView = SlidingSyncViewBuilder()
|
private val slidingSyncView = SlidingSyncViewBuilder()
|
||||||
.timelineLimit(limit = 1u)
|
.timelineLimit(limit = 1u)
|
||||||
.requiredState(requiredState = listOf(
|
.requiredState(
|
||||||
RequiredState(key = "m.room.avatar", value = ""),
|
requiredState = listOf(
|
||||||
RequiredState(key = "m.room.encryption", value = ""),
|
RequiredState(key = "m.room.avatar", value = ""),
|
||||||
))
|
RequiredState(key = "m.room.name", value = ""),
|
||||||
|
RequiredState(key = "m.room.encryption", value = ""),
|
||||||
|
)
|
||||||
|
)
|
||||||
.name(name = "HomeScreenView")
|
.name(name = "HomeScreenView")
|
||||||
.syncMode(mode = SlidingSyncMode.FULL_SYNC)
|
.syncMode(mode = SlidingSyncMode.FULL_SYNC)
|
||||||
.build()
|
.build()
|
||||||
|
|
@ -54,15 +56,28 @@ class MatrixClient internal constructor(
|
||||||
.addView(slidingSyncView)
|
.addView(slidingSyncView)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private val slidingSyncObserverProxy = SlidingSyncObserverProxy(coroutineScope)
|
||||||
private val roomSummaryDataSource: RustRoomSummaryDataSource =
|
private val roomSummaryDataSource: RustRoomSummaryDataSource =
|
||||||
RustRoomSummaryDataSource(slidingSync, slidingSyncView, dispatchers)
|
RustRoomSummaryDataSource(slidingSyncObserverProxy.updateSummaryFlow, slidingSync, slidingSyncView, dispatchers)
|
||||||
private var slidingSyncObserverToken: StoppableSpawn? = null
|
private var slidingSyncObserverToken: StoppableSpawn? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
client.setDelegate(clientDelegate)
|
client.setDelegate(clientDelegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRoom(roomId: String): MatrixRoom? {
|
||||||
|
val slidingSyncRoom = slidingSync.getRoom(roomId) ?: return null
|
||||||
|
val room = slidingSyncRoom.fullRoom() ?: return null
|
||||||
|
return MatrixRoom(
|
||||||
|
slidingSyncUpdateFlow = slidingSyncObserverProxy.updateSummaryFlow,
|
||||||
|
slidingSyncRoom = slidingSyncRoom,
|
||||||
|
room = room
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun startSync() {
|
fun startSync() {
|
||||||
slidingSync.setObserver(slidingSyncObserver)
|
roomSummaryDataSource.startSync()
|
||||||
|
slidingSync.setObserver(slidingSyncObserverProxy)
|
||||||
slidingSyncObserverToken = slidingSync.sync()
|
slidingSyncObserverToken = slidingSync.sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +128,8 @@ class MatrixClient internal constructor(
|
||||||
): Result<ByteArray> =
|
): Result<ByteArray> =
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
client.getMediaThumbnail(source, width.toULong(), height.toULong()).toUByteArray().toByteArray()
|
client.getMediaThumbnail(source, width.toULong(), height.toULong()).toUByteArray()
|
||||||
|
.toByteArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,50 @@
|
||||||
package io.element.android.x.matrix.room
|
package io.element.android.x.matrix.room
|
||||||
|
|
||||||
import io.element.android.x.matrix.core.RoomId
|
import io.element.android.x.matrix.core.RoomId
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
import org.matrix.rustcomponents.sdk.Room
|
import org.matrix.rustcomponents.sdk.Room
|
||||||
|
import org.matrix.rustcomponents.sdk.SlidingSyncRoom
|
||||||
|
import org.matrix.rustcomponents.sdk.UpdateSummary
|
||||||
|
|
||||||
class MatrixRoom(private val room: Room) {
|
class MatrixRoom(
|
||||||
|
private val slidingSyncUpdateFlow: Flow<UpdateSummary>,
|
||||||
|
private val slidingSyncRoom: SlidingSyncRoom,
|
||||||
|
private val room: Room,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun syncUpdateFlow(): Flow<Unit> {
|
||||||
|
return slidingSyncUpdateFlow
|
||||||
|
.filter {
|
||||||
|
it.rooms.contains(room.id())
|
||||||
|
}
|
||||||
|
.map { }
|
||||||
|
.onStart { emit(Unit) }
|
||||||
|
}
|
||||||
|
|
||||||
val roomId = RoomId(room.id())
|
val roomId = RoomId(room.id())
|
||||||
|
|
||||||
|
val name: String?
|
||||||
|
get() {
|
||||||
|
return slidingSyncRoom.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayName: String
|
||||||
|
get() {
|
||||||
|
return room.displayName()
|
||||||
|
}
|
||||||
|
|
||||||
|
val topic: String?
|
||||||
|
get() {
|
||||||
|
return room.topic()
|
||||||
|
}
|
||||||
|
|
||||||
|
val avatarUrl: String?
|
||||||
|
get() {
|
||||||
|
return room.avatarUrl()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package io.element.android.x.matrix.room
|
package io.element.android.x.matrix.room
|
||||||
|
|
||||||
import io.element.android.x.core.data.CoroutineDispatchers
|
import io.element.android.x.core.data.CoroutineDispatchers
|
||||||
import io.element.android.x.matrix.core.RoomId
|
|
||||||
import io.element.android.x.matrix.room.message.RoomMessageFactory
|
import io.element.android.x.matrix.room.message.RoomMessageFactory
|
||||||
import io.element.android.x.matrix.sync.roomListDiff
|
import io.element.android.x.matrix.sync.roomListDiff
|
||||||
import io.element.android.x.matrix.sync.state
|
import io.element.android.x.matrix.sync.state
|
||||||
|
|
@ -20,10 +19,11 @@ interface RoomSummaryDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class RustRoomSummaryDataSource(
|
internal class RustRoomSummaryDataSource(
|
||||||
|
private val slidingSyncUpdateFlow: Flow<UpdateSummary>,
|
||||||
private val slidingSync: SlidingSync,
|
private val slidingSync: SlidingSync,
|
||||||
private val slidingSyncView: SlidingSyncView,
|
private val slidingSyncView: SlidingSyncView,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory(),
|
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory()
|
||||||
) : RoomSummaryDataSource, Closeable {
|
) : RoomSummaryDataSource, Closeable {
|
||||||
|
|
||||||
private val coroutineScope = CoroutineScope(SupervisorJob() + coroutineDispatchers.io)
|
private val coroutineScope = CoroutineScope(SupervisorJob() + coroutineDispatchers.io)
|
||||||
|
|
@ -31,7 +31,8 @@ internal class RustRoomSummaryDataSource(
|
||||||
private val roomSummaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
private val roomSummaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||||
private val state = MutableStateFlow(SlidingSyncState.COLD)
|
private val state = MutableStateFlow(SlidingSyncState.COLD)
|
||||||
|
|
||||||
init {
|
|
||||||
|
fun startSync(){
|
||||||
slidingSyncView.roomListDiff()
|
slidingSyncView.roomListDiff()
|
||||||
.onEach { diff ->
|
.onEach { diff ->
|
||||||
updateRoomSummaries {
|
updateRoomSummaries {
|
||||||
|
|
@ -44,6 +45,11 @@ internal class RustRoomSummaryDataSource(
|
||||||
Timber.v("New sliding sync state: $slidingSyncState")
|
Timber.v("New sliding sync state: $slidingSyncState")
|
||||||
state.value = slidingSyncState
|
state.value = slidingSyncState
|
||||||
}.launchIn(coroutineScope)
|
}.launchIn(coroutineScope)
|
||||||
|
|
||||||
|
slidingSyncUpdateFlow
|
||||||
|
.onEach {
|
||||||
|
didReceiveSyncUpdate(it)
|
||||||
|
}.launchIn(coroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopSync() {
|
fun stopSync() {
|
||||||
|
|
@ -58,13 +64,13 @@ internal class RustRoomSummaryDataSource(
|
||||||
return roomSummaries.sample(100)
|
return roomSummaries.sample(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun updateRoomsWithIdentifiers(identifiers: List<String>) {
|
private fun didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||||
Timber.v("UpdateRooms with identifiers: $identifiers")
|
Timber.v("UpdateRooms with identifiers: ${summary.rooms}")
|
||||||
if (state.value != SlidingSyncState.LIVE) {
|
if (state.value != SlidingSyncState.LIVE) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateRoomSummaries {
|
updateRoomSummaries {
|
||||||
for (identifier in identifiers) {
|
for (identifier in summary.rooms) {
|
||||||
val index = indexOfFirst { it.identifier() == identifier }
|
val index = indexOfFirst { it.identifier() == identifier }
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
continue
|
continue
|
||||||
|
|
@ -78,7 +84,7 @@ internal class RustRoomSummaryDataSource(
|
||||||
private fun MutableList<RoomSummary>.applyDiff(diff: SlidingSyncViewRoomsListDiff) {
|
private fun MutableList<RoomSummary>.applyDiff(diff: SlidingSyncViewRoomsListDiff) {
|
||||||
|
|
||||||
fun MutableList<RoomSummary>.fillUntil(untilIndex: Int) {
|
fun MutableList<RoomSummary>.fillUntil(untilIndex: Int) {
|
||||||
repeat((size-1 until untilIndex).count()) {
|
repeat((size - 1 until untilIndex).count()) {
|
||||||
add(buildEmptyRoomSummary())
|
add(buildEmptyRoomSummary())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +95,7 @@ internal class RustRoomSummaryDataSource(
|
||||||
add(roomSummary)
|
add(roomSummary)
|
||||||
}
|
}
|
||||||
is SlidingSyncViewRoomsListDiff.UpdateAt -> {
|
is SlidingSyncViewRoomsListDiff.UpdateAt -> {
|
||||||
fillUntil(diff.index.toInt())
|
//fillUntil(diff.index.toInt())
|
||||||
val roomSummary = buildSummaryForRoomListEntry(diff.value)
|
val roomSummary = buildSummaryForRoomListEntry(diff.value)
|
||||||
set(diff.index.toInt(), roomSummary)
|
set(diff.index.toInt(), roomSummary)
|
||||||
}
|
}
|
||||||
|
|
@ -124,24 +130,8 @@ internal class RustRoomSummaryDataSource(
|
||||||
|
|
||||||
private fun buildRoomSummaryForIdentifier(identifier: String): RoomSummary {
|
private fun buildRoomSummaryForIdentifier(identifier: String): RoomSummary {
|
||||||
val room = slidingSync.getRoom(identifier) ?: return RoomSummary.Empty(identifier)
|
val room = slidingSync.getRoom(identifier) ?: return RoomSummary.Empty(identifier)
|
||||||
val latestRoomMessage = room.latestRoomMessage()?.let {
|
|
||||||
roomMessageFactory.create(it)
|
|
||||||
}
|
|
||||||
val computedLastMessage = when {
|
|
||||||
latestRoomMessage == null -> null
|
|
||||||
room.isDm() == true -> latestRoomMessage.body
|
|
||||||
else -> "${latestRoomMessage.sender.value}: ${latestRoomMessage.body}"
|
|
||||||
}
|
|
||||||
return RoomSummary.Filled(
|
return RoomSummary.Filled(
|
||||||
details = RoomSummaryDetails(
|
details = roomSummaryDetailsFactory.create(room, room.fullRoom())
|
||||||
roomId = RoomId(identifier),
|
|
||||||
name = room.name() ?: identifier,
|
|
||||||
isDirect = room.isDm() ?: false,
|
|
||||||
avatarURLString = room.fullRoom()?.avatarUrl(),
|
|
||||||
unreadNotificationCount = room.unreadNotifications().notificationCount().toInt(),
|
|
||||||
lastMessage = computedLastMessage,
|
|
||||||
lastMessageTimestamp = latestRoomMessage?.originServerTs
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package io.element.android.x.matrix.room
|
||||||
|
|
||||||
|
import io.element.android.x.matrix.core.RoomId
|
||||||
|
import io.element.android.x.matrix.room.message.RoomMessageFactory
|
||||||
|
import org.matrix.rustcomponents.sdk.Room
|
||||||
|
import org.matrix.rustcomponents.sdk.SlidingSyncRoom
|
||||||
|
|
||||||
|
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
|
||||||
|
|
||||||
|
fun create(slidingSyncRoom: SlidingSyncRoom, room: Room?): RoomSummaryDetails{
|
||||||
|
val latestRoomMessage = slidingSyncRoom.latestRoomMessage()?.let {
|
||||||
|
roomMessageFactory.create(it)
|
||||||
|
}
|
||||||
|
val computedLastMessage = when {
|
||||||
|
latestRoomMessage == null -> null
|
||||||
|
slidingSyncRoom.isDm() == true -> latestRoomMessage.body
|
||||||
|
else -> "${latestRoomMessage.sender.value}: ${latestRoomMessage.body}"
|
||||||
|
}
|
||||||
|
return RoomSummaryDetails(
|
||||||
|
roomId = RoomId(slidingSyncRoom.roomId()),
|
||||||
|
name = slidingSyncRoom.name() ?: slidingSyncRoom.roomId(),
|
||||||
|
isDirect = slidingSyncRoom.isDm() ?: false,
|
||||||
|
avatarURLString = room?.avatarUrl(),
|
||||||
|
unreadNotificationCount = slidingSyncRoom.unreadNotifications().notificationCount().toInt(),
|
||||||
|
lastMessage = computedLastMessage,
|
||||||
|
lastMessageTimestamp = latestRoomMessage?.originServerTs
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ import io.element.android.x.matrix.core.UserId
|
||||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||||
|
|
||||||
class RoomMessageFactory {
|
class RoomMessageFactory {
|
||||||
|
|
||||||
fun create(eventTimelineItem: EventTimelineItem?): RoomMessage? {
|
fun create(eventTimelineItem: EventTimelineItem?): RoomMessage? {
|
||||||
eventTimelineItem ?: return null
|
eventTimelineItem ?: return null
|
||||||
return RoomMessage(
|
return RoomMessage(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.element.android.x.matrix.sync
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.rustcomponents.sdk.SlidingSyncObserver
|
||||||
|
import org.matrix.rustcomponents.sdk.UpdateSummary
|
||||||
|
|
||||||
|
class SlidingSyncObserverProxy(private val coroutineScope: CoroutineScope) : SlidingSyncObserver {
|
||||||
|
|
||||||
|
private val updateSummaryMutableFlow = MutableSharedFlow<UpdateSummary>()
|
||||||
|
val updateSummaryFlow: Flow<UpdateSummary> = updateSummaryMutableFlow
|
||||||
|
|
||||||
|
override fun didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
updateSummaryMutableFlow.emit(summary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue