Move some package on core + try some stuff around sync/roomlist

This commit is contained in:
ganfra 2022-11-10 12:39:37 +01:00
parent 8e3f600bd1
commit 2bf898582c
18 changed files with 106 additions and 43 deletions

View file

@ -66,6 +66,7 @@ android {
dependencies { dependencies {
implementation project(":libraries:designsystem") implementation project(":libraries:designsystem")
implementation project(":libraries:matrix") implementation project(":libraries:matrix")
implementation project(":libraries:core")
implementation project(":features:onboarding") implementation project(":features:onboarding")
implementation project(":features:login") implementation project(":features:login")
implementation project(":features:roomlist") implementation project(":features:roomlist")

View file

@ -11,12 +11,14 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.Lifecycle
import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.rememberNavHostEngine import com.ramcosta.composedestinations.rememberNavHostEngine
import io.element.android.x.core.compose.OnLifecycleEvent
import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.destinations.LoginScreenNavigationDestination
import io.element.android.x.destinations.OnBoardingScreenNavigationDestination import io.element.android.x.destinations.OnBoardingScreenNavigationDestination
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import timber.log.Timber
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -52,4 +54,8 @@ private fun MainScreen(viewModel: MainViewModel) {
navGraph = NavGraphs.root, navGraph = NavGraphs.root,
startRoute = startRoute startRoute = startRoute
) )
OnLifecycleEvent { _, event ->
Timber.v("OnLifecycleEvent: $event")
}
} }

View file

@ -1,8 +1,10 @@
package io.element.android.x package io.element.android.x
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.element.android.x.matrix.MatrixInstance import io.element.android.x.matrix.MatrixInstance
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
class MainViewModel : ViewModel() { class MainViewModel : ViewModel() {
private val matrix = MatrixInstance.getInstance() private val matrix = MatrixInstance.getInstance()
@ -11,7 +13,22 @@ class MainViewModel : ViewModel() {
return matrix.isLoggedIn().first() return matrix.isLoggedIn().first()
} }
fun startSyncIfLogged(){
viewModelScope.launch {
if(!isLoggedIn()) return@launch
matrix.activeClient().startSync()
}
}
fun stopSyncIfLogged(){
viewModelScope.launch {
if (!isLoggedIn()) return@launch
matrix.activeClient().stopSync()
}
}
suspend fun restoreSession() { suspend fun restoreSession() {
matrix.restoreSession() matrix.restoreSession()
matrix.activeClient().startSync()
} }
} }

View file

@ -22,7 +22,7 @@ class LoginViewModel(initialState: LoginViewState) :
viewModelScope.launch { viewModelScope.launch {
suspend { suspend {
matrix.login(state.homeserver, state.login, state.password) matrix.login(state.homeserver, state.login, state.password)
Unit matrix.activeClient().startSync()
}.execute { }.execute {
copy(isLoggedIn = it) copy(isLoggedIn = it)
} }

View file

@ -35,7 +35,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
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.compose.LogCompositions
import io.element.android.x.core.data.StableCharSequence import io.element.android.x.core.data.StableCharSequence
import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.messages.components.* import io.element.android.x.features.messages.components.*

View file

@ -18,7 +18,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
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.compose.LogCompositions
import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.designsystem.components.ProgressDialog import io.element.android.x.designsystem.components.ProgressDialog
import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.designsystem.components.avatar.AvatarData

View file

@ -42,7 +42,6 @@ class RoomListViewModel(initialState: RoomListViewState) :
private fun handleInit() { private fun handleInit() {
viewModelScope.launch { viewModelScope.launch {
val client = getClient() val client = getClient()
client.startSync()
suspend { suspend {
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull() val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
val userDisplayName = client.loadUserDisplayName().getOrNull() val userDisplayName = client.loadUserDisplayName().getOrNull()

View file

@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import io.element.android.x.core.data.LogCompositions import io.element.android.x.core.compose.LogCompositions
import io.element.android.x.features.roomlist.model.MatrixUser import io.element.android.x.features.roomlist.model.MatrixUser
@Composable @Composable

View file

@ -1,4 +1,4 @@
package io.element.android.x.core.data package io.element.android.x.core.compose
import android.util.Log import android.util.Log
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable

View file

@ -0,0 +1,27 @@
package io.element.android.x.core.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
@Composable
fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event: Lifecycle.Event) -> Unit) {
val eventHandler = rememberUpdatedState(onEvent)
val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)
DisposableEffect(lifecycleOwner.value) {
val lifecycle = lifecycleOwner.value.lifecycle
val observer = LifecycleEventObserver { owner, event ->
eventHandler.value(owner, event)
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
}

View file

@ -1,4 +1,4 @@
package io.element.android.x.core.data package io.element.android.x.core.coroutine
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher

View file

@ -1,7 +1,7 @@
package io.element.android.x.matrix package io.element.android.x.matrix
import android.content.Context import android.content.Context
import io.element.android.x.core.data.CoroutineDispatchers import io.element.android.x.core.coroutine.CoroutineDispatchers
import io.element.android.x.matrix.session.SessionStore import io.element.android.x.matrix.session.SessionStore
import io.element.android.x.matrix.util.logError import io.element.android.x.matrix.util.logError
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope

View file

@ -1,6 +1,6 @@
package io.element.android.x.matrix package io.element.android.x.matrix
import io.element.android.x.core.data.CoroutineDispatchers import io.element.android.x.core.coroutine.CoroutineDispatchers
import io.element.android.x.matrix.core.UserId import io.element.android.x.matrix.core.UserId
import io.element.android.x.matrix.media.MediaResolver import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.media.RustMediaResolver import io.element.android.x.matrix.media.RustMediaResolver
@ -14,6 +14,7 @@ import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.* import org.matrix.rustcomponents.sdk.*
import timber.log.Timber import timber.log.Timber
import java.io.Closeable import java.io.Closeable
import java.util.concurrent.atomic.AtomicBoolean
class MatrixClient internal constructor( class MatrixClient internal constructor(
private val client: Client, private val client: Client,
@ -67,6 +68,7 @@ class MatrixClient internal constructor(
private var slidingSyncObserverToken: StoppableSpawn? = null private var slidingSyncObserverToken: StoppableSpawn? = null
private val mediaResolver = RustMediaResolver(this) private val mediaResolver = RustMediaResolver(this)
private val isSyncing = AtomicBoolean(false)
init { init {
client.setDelegate(clientDelegate) client.setDelegate(clientDelegate)
@ -84,15 +86,19 @@ class MatrixClient internal constructor(
} }
fun startSync() { fun startSync() {
roomSummaryDataSource.startSync() if (isSyncing.compareAndSet(false, true)) {
slidingSync.setObserver(slidingSyncObserverProxy) roomSummaryDataSource.startSync()
slidingSyncObserverToken = slidingSync.sync() slidingSync.setObserver(slidingSyncObserverProxy)
slidingSyncObserverToken = slidingSync.sync()
}
} }
fun stopSync() { fun stopSync() {
roomSummaryDataSource.stopSync() if (isSyncing.compareAndSet(true, false)) {
slidingSync.setObserver(null) roomSummaryDataSource.stopSync()
slidingSyncObserverToken?.cancel() slidingSync.setObserver(null)
slidingSyncObserverToken?.cancel()
}
} }
fun roomSummaryDataSource(): RoomSummaryDataSource = roomSummaryDataSource fun roomSummaryDataSource(): RoomSummaryDataSource = roomSummaryDataSource

View file

@ -1,6 +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.coroutine.CoroutineDispatchers
import io.element.android.x.matrix.core.RoomId import io.element.android.x.matrix.core.RoomId
import io.element.android.x.matrix.core.UserId import io.element.android.x.matrix.core.UserId
import io.element.android.x.matrix.timeline.MatrixTimeline import io.element.android.x.matrix.timeline.MatrixTimeline

View file

@ -1,19 +1,16 @@
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.coroutine.CoroutineDispatchers
import io.element.android.x.core.data.flow.chunk import io.element.android.x.core.data.flow.chunk
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
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.matrix.rustcomponents.sdk.* import org.matrix.rustcomponents.sdk.*
import timber.log.Timber import timber.log.Timber
import java.io.Closeable import java.io.Closeable
import java.util.* import java.util.*
import java.util.concurrent.Executors
interface RoomSummaryDataSource { interface RoomSummaryDataSource {
fun roomSummaries(): Flow<List<RoomSummary>> fun roomSummaries(): Flow<List<RoomSummary>>
@ -27,33 +24,43 @@ internal class RustRoomSummaryDataSource(
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory() private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory()
) : RoomSummaryDataSource, Closeable { ) : RoomSummaryDataSource, Closeable {
private val coroutineScope = CoroutineScope(SupervisorJob() + coroutineDispatchers.io) private val singleDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val coroutineScope = CoroutineScope(SupervisorJob() + singleDispatcher)
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)
fun startSync() {
coroutineScope.launch {
updateRoomSummaries {
clear()
addAll(
slidingSyncView.currentRoomsList().map(::buildSummaryForRoomListEntry)
)
}
fun startSync(){ slidingSyncView.roomListDiff()
slidingSyncView.roomListDiff() .chunk(30)
.chunk(100) .onEach { diffs ->
.onEach { diffs -> updateRoomSummaries {
updateRoomSummaries { diffs.forEach {
diffs.forEach { applyDiff(it)
applyDiff(it) }
} }
} }.collect()
}.launchIn(coroutineScope)
slidingSyncView.state() slidingSyncView.state()
.onEach { slidingSyncState -> .onEach { slidingSyncState ->
Timber.v("New sliding sync state: $slidingSyncState") Timber.v("New sliding sync state: $slidingSyncState")
state.value = slidingSyncState state.value = slidingSyncState
}.launchIn(coroutineScope) }.collect()
slidingSyncUpdateFlow slidingSyncUpdateFlow
.onEach { .onEach {
didReceiveSyncUpdate(it) didReceiveSyncUpdate(it)
}.launchIn(coroutineScope) }.collect()
}
} }
fun stopSync() { fun stopSync() {

View file

@ -1,6 +1,6 @@
package io.element.android.x.matrix.timeline package io.element.android.x.matrix.timeline
import io.element.android.x.core.data.CoroutineDispatchers import io.element.android.x.core.coroutine.CoroutineDispatchers
import io.element.android.x.core.data.flow.chunk import io.element.android.x.core.data.flow.chunk
import io.element.android.x.matrix.core.EventId import io.element.android.x.matrix.core.EventId
import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.room.MatrixRoom