Start filling MatrixRoom class and use it in MessagesScreen
This commit is contained in:
parent
d04d847521
commit
11b74c0279
11 changed files with 202 additions and 74 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
package io.element.android.x.features.messages
|
||||
|
||||
import Avatar
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.mavericksViewModel
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun MessagesScreen(roomId: String) {
|
||||
val viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId })
|
||||
LogCompositions(tag = "MessagesScreen", msg = "Root")
|
||||
val roomTitle by viewModel.collectAsState(prop1 = MessagesViewState::roomTitle)
|
||||
MessagesContent(roomTitle)
|
||||
val roomTitle by viewModel.collectAsState(MessagesViewState::roomName)
|
||||
val roomAvatar by viewModel.collectAsState(MessagesViewState::roomAvatar)
|
||||
MessagesContent(roomTitle, roomAvatar)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessagesContent(roomTitle: String) {
|
||||
fun MessagesContent(
|
||||
roomTitle: String?,
|
||||
roomAvatar: AvatarData?
|
||||
) {
|
||||
val appBarState = rememberTopAppBarState()
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
||||
LogCompositions(tag = "RoomListScreen", msg = "Content")
|
||||
|
|
@ -31,7 +37,14 @@ fun MessagesContent(roomTitle: String) {
|
|||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(text = roomTitle) }
|
||||
navigationIcon = {
|
||||
if (roomAvatar != null) {
|
||||
IconButton(onClick = {}) {
|
||||
Avatar(roomAvatar)
|
||||
}
|
||||
}
|
||||
},
|
||||
title = { Text(text = roomTitle ?: "") }
|
||||
)
|
||||
},
|
||||
content = { padding ->
|
||||
|
|
|
|||
|
|
@ -1,48 +1,69 @@
|
|||
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.Success
|
||||
import io.element.android.x.core.data.parallelMap
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.x.features.messages.model.MessagesViewState
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import io.element.android.x.matrix.room.RoomSummary
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import io.element.android.x.matrix.room.MatrixRoom
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
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) {
|
||||
|
||||
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 {
|
||||
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(
|
||||
client: MatrixClient,
|
||||
name: String,
|
||||
url: String?,
|
||||
size: AvatarSize = AvatarSize.MEDIUM
|
||||
): AvatarData {
|
||||
val mediaContent = url?.let {
|
||||
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(
|
||||
{ it },
|
||||
|
|
@ -52,10 +73,6 @@ class MessagesViewModel(initialState: MessagesViewState) :
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getClient(): MatrixClient {
|
||||
return matrix.matrixClient().first().get()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,14 @@ package io.element.android.x.features.messages.model
|
|||
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.matrix.core.RoomId
|
||||
|
||||
data class MessagesViewState(
|
||||
val roomId: String,
|
||||
val roomTitle: String = "",
|
||||
val roomName: String? = null,
|
||||
val roomAvatar: AvatarData? = null
|
||||
) : MavericksState {
|
||||
|
||||
@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 {
|
||||
return matrix.matrixClient().first().get()
|
||||
return matrix.client().first().get()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import org.matrix.rustcomponents.sdk.AuthenticationService
|
|||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
import java.io.File
|
||||
import java.util.Optional
|
||||
import java.util.*
|
||||
|
||||
class Matrix(
|
||||
coroutineScope: CoroutineScope,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
context: Context,
|
||||
) {
|
||||
private val coroutineDispatchers = CoroutineDispatchers(
|
||||
|
|
@ -44,10 +44,14 @@ class Matrix(
|
|||
return isLoggedIn
|
||||
}
|
||||
|
||||
fun matrixClient(): Flow<Optional<MatrixClient>> {
|
||||
fun client(): Flow<Optional<MatrixClient>> {
|
||||
return matrixClient
|
||||
}
|
||||
|
||||
fun activeClient(): MatrixClient {
|
||||
return matrixClient.value.get()
|
||||
}
|
||||
|
||||
suspend fun restoreSession() = withContext(coroutineDispatchers.io) {
|
||||
sessionStore.getStoredData()
|
||||
?.let { sessionData ->
|
||||
|
|
@ -80,6 +84,7 @@ class Matrix(
|
|||
return MatrixClient(
|
||||
client = client,
|
||||
sessionStore = sessionStore,
|
||||
coroutineScope = coroutineScope,
|
||||
dispatchers = coroutineDispatchers
|
||||
).also {
|
||||
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.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.RoomSummaryDetailsFactory
|
||||
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.sync.SlidingSyncObserverProxy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.*
|
||||
import timber.log.Timber
|
||||
|
|
@ -13,6 +18,7 @@ import java.io.Closeable
|
|||
class MatrixClient internal constructor(
|
||||
private val client: Client,
|
||||
private val sessionStore: SessionStore,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) : 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()
|
||||
.timelineLimit(limit = 1u)
|
||||
.requiredState(requiredState = listOf(
|
||||
RequiredState(key = "m.room.avatar", value = ""),
|
||||
RequiredState(key = "m.room.encryption", value = ""),
|
||||
))
|
||||
.requiredState(
|
||||
requiredState = listOf(
|
||||
RequiredState(key = "m.room.avatar", value = ""),
|
||||
RequiredState(key = "m.room.name", value = ""),
|
||||
RequiredState(key = "m.room.encryption", value = ""),
|
||||
)
|
||||
)
|
||||
.name(name = "HomeScreenView")
|
||||
.syncMode(mode = SlidingSyncMode.FULL_SYNC)
|
||||
.build()
|
||||
|
|
@ -54,15 +56,28 @@ class MatrixClient internal constructor(
|
|||
.addView(slidingSyncView)
|
||||
.build()
|
||||
|
||||
private val slidingSyncObserverProxy = SlidingSyncObserverProxy(coroutineScope)
|
||||
private val roomSummaryDataSource: RustRoomSummaryDataSource =
|
||||
RustRoomSummaryDataSource(slidingSync, slidingSyncView, dispatchers)
|
||||
RustRoomSummaryDataSource(slidingSyncObserverProxy.updateSummaryFlow, slidingSync, slidingSyncView, dispatchers)
|
||||
private var slidingSyncObserverToken: StoppableSpawn? = null
|
||||
|
||||
init {
|
||||
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() {
|
||||
slidingSync.setObserver(slidingSyncObserver)
|
||||
roomSummaryDataSource.startSync()
|
||||
slidingSync.setObserver(slidingSyncObserverProxy)
|
||||
slidingSyncObserverToken = slidingSync.sync()
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +128,8 @@ class MatrixClient internal constructor(
|
|||
): Result<ByteArray> =
|
||||
withContext(dispatchers.io) {
|
||||
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
|
||||
|
||||
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.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 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
|
||||
|
||||
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.sync.roomListDiff
|
||||
import io.element.android.x.matrix.sync.state
|
||||
|
|
@ -20,10 +19,11 @@ interface RoomSummaryDataSource {
|
|||
}
|
||||
|
||||
internal class RustRoomSummaryDataSource(
|
||||
private val slidingSyncUpdateFlow: Flow<UpdateSummary>,
|
||||
private val slidingSync: SlidingSync,
|
||||
private val slidingSyncView: SlidingSyncView,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory(),
|
||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory()
|
||||
) : RoomSummaryDataSource, Closeable {
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + coroutineDispatchers.io)
|
||||
|
|
@ -31,7 +31,8 @@ internal class RustRoomSummaryDataSource(
|
|||
private val roomSummaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
private val state = MutableStateFlow(SlidingSyncState.COLD)
|
||||
|
||||
init {
|
||||
|
||||
fun startSync(){
|
||||
slidingSyncView.roomListDiff()
|
||||
.onEach { diff ->
|
||||
updateRoomSummaries {
|
||||
|
|
@ -44,6 +45,11 @@ internal class RustRoomSummaryDataSource(
|
|||
Timber.v("New sliding sync state: $slidingSyncState")
|
||||
state.value = slidingSyncState
|
||||
}.launchIn(coroutineScope)
|
||||
|
||||
slidingSyncUpdateFlow
|
||||
.onEach {
|
||||
didReceiveSyncUpdate(it)
|
||||
}.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
fun stopSync() {
|
||||
|
|
@ -58,13 +64,13 @@ internal class RustRoomSummaryDataSource(
|
|||
return roomSummaries.sample(100)
|
||||
}
|
||||
|
||||
internal fun updateRoomsWithIdentifiers(identifiers: List<String>) {
|
||||
Timber.v("UpdateRooms with identifiers: $identifiers")
|
||||
private fun didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||
Timber.v("UpdateRooms with identifiers: ${summary.rooms}")
|
||||
if (state.value != SlidingSyncState.LIVE) {
|
||||
return
|
||||
}
|
||||
updateRoomSummaries {
|
||||
for (identifier in identifiers) {
|
||||
for (identifier in summary.rooms) {
|
||||
val index = indexOfFirst { it.identifier() == identifier }
|
||||
if (index == -1) {
|
||||
continue
|
||||
|
|
@ -78,7 +84,7 @@ internal class RustRoomSummaryDataSource(
|
|||
private fun MutableList<RoomSummary>.applyDiff(diff: SlidingSyncViewRoomsListDiff) {
|
||||
|
||||
fun MutableList<RoomSummary>.fillUntil(untilIndex: Int) {
|
||||
repeat((size-1 until untilIndex).count()) {
|
||||
repeat((size - 1 until untilIndex).count()) {
|
||||
add(buildEmptyRoomSummary())
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +95,7 @@ internal class RustRoomSummaryDataSource(
|
|||
add(roomSummary)
|
||||
}
|
||||
is SlidingSyncViewRoomsListDiff.UpdateAt -> {
|
||||
fillUntil(diff.index.toInt())
|
||||
//fillUntil(diff.index.toInt())
|
||||
val roomSummary = buildSummaryForRoomListEntry(diff.value)
|
||||
set(diff.index.toInt(), roomSummary)
|
||||
}
|
||||
|
|
@ -124,24 +130,8 @@ internal class RustRoomSummaryDataSource(
|
|||
|
||||
private fun buildRoomSummaryForIdentifier(identifier: String): RoomSummary {
|
||||
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(
|
||||
details = RoomSummaryDetails(
|
||||
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
|
||||
)
|
||||
details = roomSummaryDetailsFactory.create(room, room.fullRoom())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
class RoomMessageFactory {
|
||||
|
||||
fun create(eventTimelineItem: EventTimelineItem?): RoomMessage? {
|
||||
eventTimelineItem ?: return null
|
||||
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