speed up message render by marking room read state async
This commit is contained in:
parent
5923e55a6b
commit
ccecfb08e0
|
@ -15,7 +15,9 @@ class MessengerModule(
|
|||
private val roomStore: RoomStore,
|
||||
) : ProvidableModule {
|
||||
|
||||
fun messengerViewModel(): MessengerViewModel {
|
||||
return MessengerViewModel(syncService, messageService, roomService, roomStore, credentialsStore)
|
||||
internal fun messengerViewModel(): MessengerViewModel {
|
||||
return MessengerViewModel(messageService, roomService, roomStore, credentialsStore, timelineUseCase())
|
||||
}
|
||||
|
||||
private fun timelineUseCase() = TimelineUseCase(syncService, messageService, roomService, MergeWithLocalEchosUseCaseImpl())
|
||||
}
|
|
@ -39,7 +39,7 @@ import app.dapk.st.navigator.Navigator
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun MessengerScreen(roomId: RoomId, viewModel: MessengerViewModel, navigator: Navigator) {
|
||||
internal fun MessengerScreen(roomId: RoomId, viewModel: MessengerViewModel, navigator: Navigator) {
|
||||
val state = viewModel.state
|
||||
|
||||
viewModel.ObserveEvents()
|
||||
|
|
|
@ -4,25 +4,25 @@ import androidx.lifecycle.viewModelScope
|
|||
import app.dapk.st.core.Lce
|
||||
import app.dapk.st.core.extensions.takeIfContent
|
||||
import app.dapk.st.matrix.common.CredentialsStore
|
||||
import app.dapk.st.matrix.common.EventId
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.matrix.common.UserId
|
||||
import app.dapk.st.matrix.message.MessageService
|
||||
import app.dapk.st.matrix.room.RoomService
|
||||
import app.dapk.st.matrix.sync.RoomEvent
|
||||
import app.dapk.st.matrix.sync.RoomStore
|
||||
import app.dapk.st.matrix.sync.SyncService
|
||||
import app.dapk.st.viewmodel.DapkViewModel
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MessengerViewModel(
|
||||
syncService: SyncService,
|
||||
internal class MessengerViewModel(
|
||||
private val messageService: MessageService,
|
||||
private val roomService: RoomService,
|
||||
private val roomStore: RoomStore,
|
||||
private val credentialsStore: CredentialsStore,
|
||||
private val useCase: TimelineUseCase,
|
||||
) : DapkViewModel<MessengerScreenState, MessengerEvent>(
|
||||
initialState = MessengerScreenState(
|
||||
roomId = null,
|
||||
|
@ -32,53 +32,58 @@ class MessengerViewModel(
|
|||
) {
|
||||
|
||||
private var syncJob: Job? = null
|
||||
private val useCase: TimelineUseCase = TimelineUseCase(syncService, messageService, roomService, MergeWithLocalEchosUseCaseImpl())
|
||||
|
||||
fun post(action: MessengerAction) {
|
||||
when (action) {
|
||||
is MessengerAction.ComposerTextUpdate -> {
|
||||
updateState { copy(composerState = ComposerState.Text(action.newValue)) }
|
||||
}
|
||||
is MessengerAction.OnMessengerVisible -> {
|
||||
updateState { copy(roomId = action.roomId) }
|
||||
is MessengerAction.OnMessengerVisible -> start(action)
|
||||
MessengerAction.OnMessengerGone -> syncJob?.cancel()
|
||||
is MessengerAction.ComposerTextUpdate -> updateState { copy(composerState = ComposerState.Text(action.newValue)) }
|
||||
MessengerAction.ComposerSendText -> sendMessage()
|
||||
}
|
||||
}
|
||||
|
||||
syncJob = viewModelScope.launch {
|
||||
useCase.startSyncing().collect()
|
||||
private fun start(action: MessengerAction.OnMessengerVisible) {
|
||||
updateState { copy(roomId = action.roomId) }
|
||||
syncJob = viewModelScope.launch {
|
||||
roomStore.markRead(action.roomId)
|
||||
|
||||
val credentials = credentialsStore.credentials()!!
|
||||
var lastKnownReadEvent: EventId? = null
|
||||
useCase.state(action.roomId, credentials.userId).distinctUntilChanged().onEach { state ->
|
||||
state.lastestMessageEventFromOthers(self = credentials.userId)?.let {
|
||||
if (lastKnownReadEvent != it) {
|
||||
updateRoomReadStateAsync(latestReadEvent = it, state)
|
||||
lastKnownReadEvent = it
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
roomStore.markRead(action.roomId)
|
||||
updateState { copy(roomState = Lce.Content(state)) }
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
|
||||
val credentials = credentialsStore.credentials()!!
|
||||
useCase.state(action.roomId, credentials.userId).distinctUntilChanged().onEach { state ->
|
||||
state.roomState.events.filterIsInstance<RoomEvent.Message>().filterNot { it.author.id == credentials.userId }.firstOrNull()?.let {
|
||||
roomService.markFullyRead(state.roomState.roomOverview.roomId, it.eventId)
|
||||
roomStore.markRead(state.roomState.roomOverview.roomId)
|
||||
}
|
||||
updateState { copy(roomState = Lce.Content(state)) }
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
MessengerAction.OnMessengerGone -> {
|
||||
syncJob?.cancel()
|
||||
}
|
||||
MessengerAction.ComposerSendText -> {
|
||||
when (val composerState = state.composerState) {
|
||||
is ComposerState.Text -> {
|
||||
val copy = composerState.copy()
|
||||
updateState { copy(composerState = composerState.copy(value = "")) }
|
||||
private fun CoroutineScope.updateRoomReadStateAsync(latestReadEvent: EventId, state: MessengerState): Deferred<Unit> {
|
||||
return async {
|
||||
roomService.markFullyRead(state.roomState.roomOverview.roomId, latestReadEvent)
|
||||
roomStore.markRead(state.roomState.roomOverview.roomId)
|
||||
}
|
||||
}
|
||||
|
||||
state.roomState.takeIfContent()?.let { content ->
|
||||
val roomState = content.roomState
|
||||
viewModelScope.launch {
|
||||
messageService.scheduleMessage(
|
||||
MessageService.Message.TextMessage(
|
||||
MessageService.Message.Content.TextContent(body = copy.value),
|
||||
roomId = roomState.roomOverview.roomId,
|
||||
sendEncrypted = roomState.roomOverview.isEncrypted,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
private fun sendMessage() {
|
||||
when (val composerState = state.composerState) {
|
||||
is ComposerState.Text -> {
|
||||
val copy = composerState.copy()
|
||||
updateState { copy(composerState = composerState.copy(value = "")) }
|
||||
|
||||
state.roomState.takeIfContent()?.let { content ->
|
||||
val roomState = content.roomState
|
||||
viewModelScope.launch {
|
||||
messageService.scheduleMessage(
|
||||
MessageService.Message.TextMessage(
|
||||
MessageService.Message.Content.TextContent(body = copy.value),
|
||||
roomId = roomState.roomOverview.roomId,
|
||||
sendEncrypted = roomState.roomOverview.isEncrypted,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +92,12 @@ class MessengerViewModel(
|
|||
|
||||
}
|
||||
|
||||
private fun MessengerState.lastestMessageEventFromOthers(self: UserId) = this.roomState.events
|
||||
.filterIsInstance<RoomEvent.Message>()
|
||||
.filterNot { it.author.id == self }
|
||||
.firstOrNull()
|
||||
?.eventId
|
||||
|
||||
sealed interface MessengerAction {
|
||||
data class ComposerTextUpdate(val newValue: String) : MessengerAction
|
||||
object ComposerSendText : MessengerAction
|
||||
|
|
|
@ -23,12 +23,13 @@ internal class TimelineUseCase(
|
|||
private val mergeWithLocalEchosUseCase: MergeWithLocalEchosUseCase
|
||||
) {
|
||||
|
||||
suspend fun startSyncing(): Flow<Unit> {
|
||||
return syncService.startSyncing()
|
||||
}
|
||||
|
||||
suspend fun state(roomId: RoomId, userId: UserId): Flow<MessengerState> {
|
||||
return combine(syncService.room(roomId), messageService.localEchos(roomId), syncService.events()) { roomState, localEchos, events ->
|
||||
return combine(
|
||||
syncService.startSyncing(),
|
||||
syncService.room(roomId),
|
||||
messageService.localEchos(roomId),
|
||||
syncService.events()
|
||||
) { _, roomState, localEchos, events ->
|
||||
MessengerState(
|
||||
roomState = when {
|
||||
localEchos.isEmpty() -> roomState
|
||||
|
|
|
@ -72,7 +72,7 @@ class NotificationsUseCase(
|
|||
}
|
||||
|
||||
if (summaryNotification == null) {
|
||||
notificationManager.cancel(101)
|
||||
notificationManager.cancel(SUMMARY_NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
notifications.forEach {
|
||||
|
|
|
@ -22,7 +22,7 @@ interface SyncService : MatrixService {
|
|||
suspend fun invites(): Flow<InviteState>
|
||||
suspend fun overview(): Flow<OverviewState>
|
||||
suspend fun room(roomId: RoomId): Flow<RoomState>
|
||||
suspend fun startSyncing(): Flow<Unit>
|
||||
fun startSyncing(): Flow<Unit>
|
||||
suspend fun events(): Flow<List<SyncEvent>>
|
||||
suspend fun observeEvent(eventId: EventId): Flow<EventId>
|
||||
suspend fun forceManualRefresh(roomIds: List<RoomId>)
|
||||
|
|
|
@ -98,7 +98,7 @@ internal class DefaultSyncService(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun startSyncing() = syncFlow
|
||||
override fun startSyncing() = syncFlow
|
||||
override suspend fun invites() = overviewStore.latestInvites()
|
||||
override suspend fun overview() = overviewStore.latest()
|
||||
override suspend fun room(roomId: RoomId) = roomStore.latest(roomId)
|
||||
|
|
Loading…
Reference in New Issue