diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 83465bfcd4..9d6db782ef 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -22,7 +22,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import coil.Coil import com.bumble.appyx.core.composable.Children 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.createroom.api.CreateRoomEntryPoint 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.roomlist.api.RoomListEntryPoint 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.core.MAIN_SPACE 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.services.analytics.api.AnalyticsService import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) @@ -81,6 +89,7 @@ class LoggedInFlowNode @AssistedInject constructor( private val inviteListEntryPoint: InviteListEntryPoint, private val analyticsService: AnalyticsService, private val coroutineScope: CoroutineScope, + private val networkMonitor: NetworkMonitor, snackbarDispatcher: SnackbarDispatcher, ) : BackstackNode( backstack = BackStack( @@ -162,6 +171,27 @@ class LoggedInFlowNode @AssistedInject constructor( 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 { diff --git a/features/networkmonitor/impl/src/main/kotlin/io/element/android/features/networkmonitor/impl/NetworkMonitorImpl.kt b/features/networkmonitor/impl/src/main/kotlin/io/element/android/features/networkmonitor/impl/NetworkMonitorImpl.kt index 69b53f9083..3901ec4120 100644 --- a/features/networkmonitor/impl/src/main/kotlin/io/element/android/features/networkmonitor/impl/NetworkMonitorImpl.kt +++ b/features/networkmonitor/impl/src/main/kotlin/io/element/android/features/networkmonitor/impl/NetworkMonitorImpl.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.stateIn import timber.log.Timber import javax.inject.Inject @@ -81,6 +82,7 @@ class NetworkMonitorImpl @Inject constructor( connectivityManager.unregisterNetworkCallback(callback) } } + .distinctUntilChanged() .debounce(300) .stateIn(appCoroutineScope, SharingStarted.WhileSubscribed(), connectivityManager.activeNetworkStatus()) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt index 616f4fd859..b47c463dd8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt @@ -30,7 +30,7 @@ interface SyncService { fun stopSync() /** - * + * Flow of [SyncState]. Will be updated as soon as the current [SyncState] changes. */ - fun syncState(): StateFlow + val syncState: StateFlow } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 0c7f9c75de..7122617284 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -114,7 +114,7 @@ class RustMatrixClient constructor( init { client.setDelegate(clientDelegate) - syncService.syncState() + syncService.syncState .onEach { syncState -> if (syncState == SyncState.Syncing) { onSlidingSyncUpdate() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt index 97e3f4c865..26b5789d70 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.impl.room.roomListStateFlow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn @@ -46,13 +47,10 @@ class RustSyncService( } } - override fun syncState(): StateFlow { - return roomListService + override val syncState: StateFlow = + roomListService .roomListStateFlow() .map(RoomListState::toSyncState) - .onEach { syncState -> - Timber.d("OnSyncState updated = $syncState") - } - .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, SyncState.Idle) - } + .distinctUntilChanged() + .stateIn(sessionCoroutineScope, SharingStarted.WhileSubscribed(), SyncState.Idle) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt index 04837efc90..cd452ce591 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt @@ -23,21 +23,19 @@ import kotlinx.coroutines.flow.StateFlow class FakeSyncService : SyncService { - private val syncState = MutableStateFlow(SyncState.Idle) + private val syncStateFlow = MutableStateFlow(SyncState.Idle) fun simulateError() { - syncState.value = SyncState.InError + syncStateFlow.value = SyncState.InError } override fun startSync() { - syncState.value = SyncState.Syncing + syncStateFlow.value = SyncState.Syncing } override fun stopSync() { - syncState.value = SyncState.Terminated + syncStateFlow.value = SyncState.Terminated } - override fun syncState(): StateFlow { - return syncState - } + override val syncState: StateFlow = syncStateFlow }