RoomList: try syncing when network is back and inError state

This commit is contained in:
ganfra 2023-06-23 17:07:27 +02:00
parent 1185dbd276
commit bb4f61fe12
6 changed files with 45 additions and 17 deletions

View file

@ -22,7 +22,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import coil.Coil import coil.Coil
import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.lifecycle.subscribe
@ -43,6 +45,8 @@ import io.element.android.appnav.loggedin.LoggedInNode
import io.element.android.features.analytics.api.AnalyticsEntryPoint import io.element.android.features.analytics.api.AnalyticsEntryPoint
import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.features.createroom.api.CreateRoomEntryPoint
import io.element.android.features.invitelist.api.InviteListEntryPoint import io.element.android.features.invitelist.api.InviteListEntryPoint
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint
import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint
@ -59,13 +63,17 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.MAIN_SPACE
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ContributesNode(AppScope::class) @ContributesNode(AppScope::class)
@ -81,6 +89,7 @@ class LoggedInFlowNode @AssistedInject constructor(
private val inviteListEntryPoint: InviteListEntryPoint, private val inviteListEntryPoint: InviteListEntryPoint,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val networkMonitor: NetworkMonitor,
snackbarDispatcher: SnackbarDispatcher, snackbarDispatcher: SnackbarDispatcher,
) : BackstackNode<LoggedInFlowNode.NavTarget>( ) : BackstackNode<LoggedInFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
@ -162,6 +171,27 @@ class LoggedInFlowNode @AssistedInject constructor(
loggedInFlowProcessor.stopObserving() loggedInFlowProcessor.stopObserving()
} }
) )
observeSyncStateAndNetworkStatus()
}
private fun observeSyncStateAndNetworkStatus() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
combine(
syncService.syncState.debounce(100),
networkMonitor.connectivity
) { syncState, networkStatus ->
syncState == SyncState.InError && networkStatus == NetworkStatus.Online
}
.distinctUntilChanged()
.collect { restartSync ->
if (restartSync) {
syncService.startSync()
}
}
}
}
} }
sealed interface NavTarget : Parcelable { sealed interface NavTarget : Parcelable {

View file

@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -81,6 +82,7 @@ class NetworkMonitorImpl @Inject constructor(
connectivityManager.unregisterNetworkCallback(callback) connectivityManager.unregisterNetworkCallback(callback)
} }
} }
.distinctUntilChanged()
.debounce(300) .debounce(300)
.stateIn(appCoroutineScope, SharingStarted.WhileSubscribed(), connectivityManager.activeNetworkStatus()) .stateIn(appCoroutineScope, SharingStarted.WhileSubscribed(), connectivityManager.activeNetworkStatus())

View file

@ -30,7 +30,7 @@ interface SyncService {
fun stopSync() fun stopSync()
/** /**
* * Flow of [SyncState]. Will be updated as soon as the current [SyncState] changes.
*/ */
fun syncState(): StateFlow<SyncState> val syncState: StateFlow<SyncState>
} }

View file

@ -114,7 +114,7 @@ class RustMatrixClient constructor(
init { init {
client.setDelegate(clientDelegate) client.setDelegate(clientDelegate)
syncService.syncState() syncService.syncState
.onEach { syncState -> .onEach { syncState ->
if (syncState == SyncState.Syncing) { if (syncState == SyncState.Syncing) {
onSlidingSyncUpdate() onSlidingSyncUpdate()

View file

@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.impl.room.roomListStateFlow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -46,13 +47,10 @@ class RustSyncService(
} }
} }
override fun syncState(): StateFlow<SyncState> { override val syncState: StateFlow<SyncState> =
return roomListService roomListService
.roomListStateFlow() .roomListStateFlow()
.map(RoomListState::toSyncState) .map(RoomListState::toSyncState)
.onEach { syncState -> .distinctUntilChanged()
Timber.d("OnSyncState updated = $syncState") .stateIn(sessionCoroutineScope, SharingStarted.WhileSubscribed(), SyncState.Idle)
}
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, SyncState.Idle)
}
} }

View file

@ -23,21 +23,19 @@ import kotlinx.coroutines.flow.StateFlow
class FakeSyncService : SyncService { class FakeSyncService : SyncService {
private val syncState = MutableStateFlow(SyncState.Idle) private val syncStateFlow = MutableStateFlow(SyncState.Idle)
fun simulateError() { fun simulateError() {
syncState.value = SyncState.InError syncStateFlow.value = SyncState.InError
} }
override fun startSync() { override fun startSync() {
syncState.value = SyncState.Syncing syncStateFlow.value = SyncState.Syncing
} }
override fun stopSync() { override fun stopSync() {
syncState.value = SyncState.Terminated syncStateFlow.value = SyncState.Terminated
} }
override fun syncState(): StateFlow<SyncState> { override val syncState: StateFlow<SyncState> = syncStateFlow
return syncState
}
} }