diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index 619c7c5feb..b656532075 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -19,8 +19,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import im.vector.app.features.analytics.plan.CryptoSessionStateChange import im.vector.app.features.analytics.plan.UserProperties -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.log.logger.LoggerTag @@ -29,6 +27,8 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion +import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.matrix.api.sync.isConnected import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase @@ -46,7 +46,7 @@ private val pusherTag = LoggerTag("Pusher", LoggerTag.PushLoggerTag) class LoggedInPresenter @Inject constructor( private val matrixClient: MatrixClient, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, private val pushService: PushService, private val sessionVerificationService: SessionVerificationService, private val analyticsService: AnalyticsService, @@ -76,10 +76,10 @@ class LoggedInPresenter @Inject constructor( .launchIn(this) } val syncIndicator by matrixClient.roomListService.syncIndicator.collectAsState() - val networkStatus by networkMonitor.connectivity.collectAsState() + val syncState by syncService.syncState.collectAsState() val showSyncSpinner by remember { derivedStateOf { - networkStatus == NetworkStatus.Online && syncIndicator == RoomListService.SyncIndicator.Show + syncState.isConnected() && syncIndicator == RoomListService.SyncIndicator.Show } } var forceNativeSlidingSyncMigration by remember { mutableStateOf(false) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SendQueues.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SendQueues.kt index c494737f8f..458e6e5c49 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SendQueues.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SendQueues.kt @@ -8,11 +8,12 @@ package io.element.android.appnav.loggedin import androidx.annotation.VisibleForTesting -import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.matrix.api.sync.SyncState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine @@ -27,7 +28,7 @@ const val SEND_QUEUES_RETRY_DELAY_MILLIS = 500L @SingleIn(SessionScope::class) class SendQueues @Inject constructor( private val matrixClient: MatrixClient, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, ) { /** * Launches the send queues retry mechanism in the given [coroutineScope]. @@ -36,12 +37,12 @@ class SendQueues @Inject constructor( @OptIn(FlowPreview::class) fun launchIn(coroutineScope: CoroutineScope) { combine( - networkMonitor.connectivity, + syncService.syncState, matrixClient.sendQueueDisabledFlow(), - ) { networkStatus, _ -> networkStatus } + ) { syncState, _ -> syncState } .debounce(SEND_QUEUES_RETRY_DELAY_MILLIS) - .onEach { networkStatus -> - if (networkStatus == NetworkStatus.Online) { + .onEach { syncState -> + if (syncState == SyncState.Running) { matrixClient.setAllSendQueuesEnabled(enabled = true) } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index b7ce9a08c0..3358af877a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -30,8 +30,6 @@ import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode import io.element.android.appnav.room.joined.LoadingRoomNodeView import io.element.android.appnav.room.joined.LoadingRoomState import io.element.android.features.joinroom.api.JoinRoomEntryPoint -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.BackstackView @@ -50,6 +48,8 @@ import io.element.android.libraries.matrix.api.getRoomInfoFlow import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias +import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.matrix.api.sync.isConnected import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first @@ -68,7 +68,7 @@ class RoomFlowNode @AssistedInject constructor( private val client: MatrixClient, private val joinRoomEntryPoint: JoinRoomEntryPoint, private val roomAliasResolverEntryPoint: RoomAliasResolverEntryPoint, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, private val membershipObserver: RoomMembershipObserver, ) : BaseFlowNode( backstack = BackStack( @@ -211,10 +211,10 @@ class RoomFlowNode @AssistedInject constructor( } private fun loadingNode(buildContext: BuildContext) = node(buildContext) { modifier -> - val networkStatus by networkMonitor.connectivity.collectAsState() + val syncState by syncService.syncState.collectAsState() LoadingRoomNodeView( state = LoadingRoomState.Loading, - hasNetworkConnection = networkStatus == NetworkStatus.Online, + hasNetworkConnection = syncState.isConnected(), onBackClick = { navigateUp() }, modifier = modifier, ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt index e271f15c61..3ea2c8997e 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomFlowNode.kt @@ -28,8 +28,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.room.RoomNavigationTarget -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs @@ -37,6 +35,8 @@ import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.matrix.api.sync.isConnected import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -48,7 +48,7 @@ class JoinedRoomFlowNode @AssistedInject constructor( @Assisted val buildContext: BuildContext, @Assisted plugins: List, loadingRoomStateFlowFactory: LoadingRoomStateFlowFactory, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, ) : BaseFlowNode( backstack = BackStack( @@ -114,10 +114,10 @@ class JoinedRoomFlowNode @AssistedInject constructor( private fun loadingNode(buildContext: BuildContext, onBackClick: () -> Unit) = node(buildContext) { modifier -> val loadingRoomState by loadingRoomStateStateFlow.collectAsState() - val networkStatus by networkMonitor.connectivity.collectAsState() + val syncState by syncService.syncState.collectAsState() LoadingRoomNodeView( state = loadingRoomState, - hasNetworkConnection = networkStatus == NetworkStatus.Online, + hasNetworkConnection = syncState.isConnected(), modifier = modifier, onBackClick = onBackClick ) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index 2f10298480..3f0713cd99 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -14,14 +14,13 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.CryptoSessionStateChange import im.vector.app.features.analytics.plan.UserProperties -import io.element.android.features.networkmonitor.api.NetworkStatus -import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion +import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -29,6 +28,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService +import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.preferences.api.store.EnableNativeSlidingSyncUseCase import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore @@ -73,7 +73,7 @@ class LoggedInPresenterTest { @Test fun `present - show sync spinner`() = runTest { val roomListService = FakeRoomListService() - val presenter = createLoggedInPresenter(roomListService, NetworkStatus.Online) + val presenter = createLoggedInPresenter(roomListService, SyncState.Running) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -94,7 +94,7 @@ class LoggedInPresenterTest { val encryptionService = FakeEncryptionService() val presenter = LoggedInPresenter( matrixClient = FakeMatrixClient(roomListService = roomListService, encryptionService = encryptionService), - networkMonitor = FakeNetworkMonitor(NetworkStatus.Online), + syncService = FakeSyncService(initialSyncState = SyncState.Running), pushService = FakePushService(), sessionVerificationService = verificationService, analyticsService = analyticsService, @@ -574,7 +574,7 @@ class LoggedInPresenterTest { private fun TestScope.createLoggedInPresenter( roomListService: RoomListService = FakeRoomListService(), - networkStatus: NetworkStatus = NetworkStatus.Offline, + syncState: SyncState = SyncState.Running, analyticsService: AnalyticsService = FakeAnalyticsService(), sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(), encryptionService: EncryptionService = FakeEncryptionService(), @@ -584,7 +584,7 @@ class LoggedInPresenterTest { ): LoggedInPresenter { return LoggedInPresenter( matrixClient = matrixClient, - networkMonitor = FakeNetworkMonitor(networkStatus), + syncService = FakeSyncService(initialSyncState = syncState), pushService = pushService, sessionVerificationService = sessionVerificationService, analyticsService = analyticsService, diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/SendQueuesTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/SendQueuesTest.kt index f33490ec51..597139c5e9 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/SendQueuesTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/SendQueuesTest.kt @@ -7,11 +7,11 @@ package io.element.android.appnav.loggedin -import io.element.android.features.networkmonitor.api.NetworkStatus -import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -25,8 +25,8 @@ import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) class SendQueuesTest { private val matrixClient = FakeMatrixClient() - private val networkMonitor = FakeNetworkMonitor() - private val sut = SendQueues(matrixClient, networkMonitor) + private val syncService = FakeSyncService(initialSyncState = SyncState.Running) + private val sut = SendQueues(matrixClient, syncService) @Test fun `test network status online and sending queue failed`() = runTest { @@ -53,13 +53,13 @@ class SendQueuesTest { } @Test - fun `test network status offline and sending queue failed`() = runTest { + fun `test sync state offline and sending queue failed`() = runTest { val sendQueueDisabledFlow = MutableSharedFlow(replay = 1) val setAllSendQueuesEnabledLambda = lambdaRecorder { _: Boolean -> } matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda - networkMonitor.connectivity.value = NetworkStatus.Offline + syncService.emitSyncState(SyncState.Offline) val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> } val room = FakeMatrixRoom( setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt index ec371de814..2ab7156878 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt @@ -41,7 +41,6 @@ import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancelAndJoin -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.test.TestScope @@ -82,7 +81,7 @@ class CallScreenPresenterTest { @Test fun `present - with CallType RoomCall sets call as active, loads URL, runs WidgetDriver and notifies the other clients a call started`() = runTest { val sendCallNotificationIfNeededLambda = lambdaRecorder> { Result.success(Unit) } - val syncService = FakeSyncService(MutableStateFlow(SyncState.Running)) + val syncService = FakeSyncService(SyncState.Running) val fakeRoom = FakeMatrixRoom(sendCallNotificationIfNeededResult = sendCallNotificationIfNeededLambda) val client = FakeMatrixClient(syncService = syncService).apply { givenGetRoomResult(A_ROOM_ID, fakeRoom) @@ -247,9 +246,8 @@ class CallScreenPresenterTest { fun `present - automatically starts the Matrix client sync when on RoomCall`() = runTest { val navigator = FakeCallScreenNavigator() val widgetDriver = FakeMatrixWidgetDriver() - val syncStateFlow = MutableStateFlow(SyncState.Idle) val startSyncLambda = lambdaRecorder> { Result.success(Unit) } - val syncService = FakeSyncService(syncStateFlow = syncStateFlow).apply { + val syncService = FakeSyncService(SyncState.Idle).apply { this.startSyncLambda = startSyncLambda } val matrixClient = FakeMatrixClient(syncService = syncService) @@ -276,9 +274,8 @@ class CallScreenPresenterTest { fun `present - automatically stops the Matrix client sync on dispose`() = runTest { val navigator = FakeCallScreenNavigator() val widgetDriver = FakeMatrixWidgetDriver() - val syncStateFlow = MutableStateFlow(SyncState.Running) val stopSyncLambda = lambdaRecorder> { Result.success(Unit) } - val syncService = FakeSyncService(syncStateFlow = syncStateFlow).apply { + val syncService = FakeSyncService(SyncState.Running).apply { this.stopSyncLambda = stopSyncLambda } val matrixClient = FakeMatrixClient(syncService = syncService) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index a01a3ed3c8..92c6d82912 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -47,8 +47,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.architecture.AsyncData @@ -72,6 +70,8 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage +import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.matrix.api.sync.isConnected import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.model.getAvatarData @@ -98,7 +98,7 @@ class MessagesPresenter @AssistedInject constructor( private val readReceiptBottomSheetPresenter: Presenter, private val pinnedMessagesBannerPresenter: Presenter, private val roomCallStatePresenter: Presenter, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, private val snackbarDispatcher: SnackbarDispatcher, private val dispatchers: CoroutineDispatchers, private val clipboardHelper: ClipboardHelper, @@ -170,7 +170,7 @@ class MessagesPresenter @AssistedInject constructor( showReinvitePrompt = !hasDismissedInviteDialog && composerState.textEditorState.hasFocus() && room.isDm && room.activeMemberCount == 1L } } - val networkConnectionStatus by networkMonitor.connectivity.collectAsState() + val syncState by syncService.syncState.collectAsState() val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() @@ -220,7 +220,7 @@ class MessagesPresenter @AssistedInject constructor( customReactionState = customReactionState, reactionSummaryState = reactionSummaryState, readReceiptBottomSheetState = readReceiptBottomSheetState, - hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, + hasNetworkConnection = syncState.isConnected(), snackbarMessage = snackbarMessage, showReinvitePrompt = showReinvitePrompt, inviteProgress = inviteProgress.value, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt index eb0dab7588..8840411a24 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt @@ -7,7 +7,6 @@ package io.element.android.features.messages.impl.pinned -import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.di.RoomScope @@ -15,6 +14,7 @@ import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.TimelineProvider import kotlinx.coroutines.CoroutineScope @@ -31,7 +31,7 @@ import javax.inject.Inject @SingleIn(RoomScope::class) class PinnedEventsTimelineProvider @Inject constructor( private val room: MatrixRoom, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, private val featureFlagService: FeatureFlagService, ) : TimelineProvider { private val _timelineStateFlow: MutableStateFlow> = @@ -63,9 +63,9 @@ class PinnedEventsTimelineProvider @Inject constructor( private suspend fun onActive() = coroutineScope { combine( featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents), - networkMonitor.connectivity + syncService.syncState, ) { isEnabled, _ -> - // do not use connectivity here as data can be loaded from cache, it's just to trigger retry if needed + // do not use syncState here as data can be loaded from cache, it's just to trigger retry if needed isEnabled } .onEach { isFeatureEnabled -> diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 458c573ed1..20b57fa343 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -35,7 +35,6 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider -import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.AsyncData @@ -72,6 +71,7 @@ import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember +import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.aTimelineItemDebugInfo import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @@ -1187,7 +1187,7 @@ class MessagesPresenterTest { identityChangeStatePresenter = { anIdentityChangeState() }, pinnedMessagesBannerPresenter = { aLoadedPinnedMessagesBannerState() }, roomCallStatePresenter = { aStandByCallState() }, - networkMonitor = FakeNetworkMonitor(), + syncService = FakeSyncService(), snackbarDispatcher = SnackbarDispatcher(), navigator = navigator, clipboardHelper = clipboardHelper, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt index 7a01d90464..fdd01f5ad1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt @@ -9,12 +9,11 @@ package io.element.android.features.messages.impl.pinned.banner import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.eventformatter.test.FakePinnedMessagesBannerFormatter import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 @@ -22,6 +21,7 @@ import io.element.android.libraries.matrix.test.A_UNIQUE_ID import io.element.android.libraries.matrix.test.A_UNIQUE_ID_2 import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.aMessageContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem @@ -186,12 +186,12 @@ class PinnedMessagesBannerPresenterTest { formatLambda = { event -> "${event.content}" } ) ), - networkMonitor: NetworkMonitor = FakeNetworkMonitor(), + syncService: SyncService = FakeSyncService(), isFeatureEnabled: Boolean = true, ): PinnedMessagesBannerPresenter { val timelineProvider = PinnedEventsTimelineProvider( room = room, - networkMonitor = networkMonitor, + syncService = syncService, featureFlagService = FakeFeatureFlagService( initialState = mapOf(FeatureFlags.PinnedEvents.key to isFeatureEnabled) ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt index acdb039ac1..3799eb2c74 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt @@ -15,13 +15,12 @@ import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryC import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -29,6 +28,7 @@ import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.A_UNIQUE_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo +import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.aMessageContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem @@ -293,13 +293,13 @@ class PinnedMessagesListPresenterTest { private fun TestScope.createPinnedMessagesListPresenter( navigator: PinnedMessagesListNavigator = FakePinnedMessagesListNavigator(), room: MatrixRoom = FakeMatrixRoom(), - networkMonitor: NetworkMonitor = FakeNetworkMonitor(), + syncService: SyncService = FakeSyncService(), isFeatureEnabled: Boolean = true, analyticsService: AnalyticsService = FakeAnalyticsService(), ): PinnedMessagesListPresenter { val timelineProvider = PinnedEventsTimelineProvider( room = room, - networkMonitor = networkMonitor, + syncService = syncService, featureFlagService = FakeFeatureFlagService( initialState = mapOf(FeatureFlags.PinnedEvents.key to isFeatureEnabled) ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index e1144b35e7..ed63b94ea1 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -30,8 +30,6 @@ import io.element.android.features.invite.api.response.InviteData import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.logout.api.direct.DirectLogoutState -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.filters.RoomListFiltersState import io.element.android.features.roomlist.impl.model.RoomListRoomSummary @@ -51,6 +49,8 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion +import io.element.android.libraries.matrix.api.sync.SyncService +import io.element.android.libraries.matrix.api.sync.isConnected import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.push.api.notifications.NotificationCleaner @@ -76,7 +76,7 @@ private const val SUBSCRIBE_TO_VISIBLE_ROOMS_DEBOUNCE_IN_MILLIS = 300L class RoomListPresenter @Inject constructor( private val client: MatrixClient, - private val networkMonitor: NetworkMonitor, + private val syncService: SyncService, private val snackbarDispatcher: SnackbarDispatcher, private val leaveRoomPresenter: Presenter, private val roomListDataSource: RoomListDataSource, @@ -98,7 +98,7 @@ class RoomListPresenter @Inject constructor( val coroutineScope = rememberCoroutineScope() val leaveRoomState = leaveRoomPresenter.present() val matrixUser = client.userProfile.collectAsState() - val networkConnectionStatus by networkMonitor.connectivity.collectAsState() + val syncState by syncService.syncState.collectAsState() val filtersState = filtersPresenter.present() val searchState = searchPresenter.present() val acceptDeclineInviteState = acceptDeclineInvitePresenter.present() @@ -158,7 +158,7 @@ class RoomListPresenter @Inject constructor( matrixUser = matrixUser.value, showAvatarIndicator = showAvatarIndicator, snackbarMessage = snackbarMessage, - hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, + hasNetworkConnection = syncState.isConnected(), contextMenu = contextMenu.value, leaveRoomState = leaveRoomState, filtersState = filtersState, diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt index 646fa5c87a..a49e020d8b 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt @@ -19,8 +19,6 @@ import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState import io.element.android.features.logout.api.direct.aDirectLogoutState -import io.element.android.features.networkmonitor.api.NetworkMonitor -import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.aRoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.filters.RoomListFiltersState @@ -48,6 +46,7 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.user.MatrixUser @@ -84,7 +83,6 @@ import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest @@ -202,7 +200,7 @@ class RoomListPresenterTest { val encryptionService = FakeEncryptionService().apply { emitRecoveryState(RecoveryState.INCOMPLETE) } - val syncService = FakeSyncService(MutableStateFlow(SyncState.Running)) + val syncService = FakeSyncService(initialSyncState = SyncState.Running) val presenter = createRoomListPresenter( client = FakeMatrixClient(roomListService = roomListService, encryptionService = encryptionService, syncService = syncService), ) @@ -233,7 +231,7 @@ class RoomListPresenterTest { sessionVerificationService = FakeSessionVerificationService().apply { emitNeedsSessionVerification(false) }, - syncService = FakeSyncService(MutableStateFlow(SyncState.Running)), + syncService = FakeSyncService(initialSyncState = SyncState.Running), ) val presenter = createRoomListPresenter( client = matrixClient, @@ -633,7 +631,7 @@ class RoomListPresenterTest { private fun TestScope.createRoomListPresenter( client: MatrixClient = FakeMatrixClient(), - networkMonitor: NetworkMonitor = FakeNetworkMonitor(), + syncService: SyncService = FakeSyncService(), snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(), leaveRoomState: LeaveRoomState = aLeaveRoomState(), dateFormatter: DateFormatter = FakeDateFormatter(), @@ -647,7 +645,7 @@ class RoomListPresenterTest { notificationCleaner: NotificationCleaner = FakeNotificationCleaner(), ) = RoomListPresenter( client = client, - networkMonitor = networkMonitor, + syncService = syncService, snackbarDispatcher = snackbarDispatcher, leaveRoomPresenter = { leaveRoomState }, roomListDataSource = RoomListDataSource( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncState.kt index 6d253c8126..35c2dc6c04 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncState.kt @@ -14,3 +14,11 @@ enum class SyncState { Terminated, Offline, } + +fun SyncState.isConnected() = when (this) { + SyncState.Idle, + SyncState.Running, + SyncState.Error, + SyncState.Terminated -> true + SyncState.Offline -> false +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index bdfaba67e2..48e1296792 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -74,6 +74,7 @@ class RustMatrixClientFactory @Inject constructor( val syncService = client.syncService() .withUtdHook(UtdTracker(analyticsService)) + .withOfflineMode() .finish() return RustMatrixClient( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt index acdcf31877..361d1efd97 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.verification.SessionVerificationService import kotlinx.coroutines.CoroutineScope @@ -45,6 +46,11 @@ object SessionMatrixModule { return matrixClient.roomListService } + @Provides + fun providesSyncService(matrixClient: MatrixClient): SyncService { + return matrixClient.syncService() + } + @Provides fun providesEncryptionService(matrixClient: MatrixClient): EncryptionService { return matrixClient.encryptionService() diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustSyncServiceBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustSyncServiceBuilder.kt index 6e592d4b47..0ddfe16eea 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustSyncServiceBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustSyncServiceBuilder.kt @@ -14,5 +14,6 @@ import org.matrix.rustcomponents.sdk.UnableToDecryptDelegate class FakeRustSyncServiceBuilder : SyncServiceBuilder(NoPointer) { override suspend fun withUtdHook(delegate: UnableToDecryptDelegate): SyncServiceBuilder = this + override fun withOfflineMode(): SyncServiceBuilder = this override suspend fun finish(): SyncService = FakeRustSyncService() } 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 67cc4b4293..9019eea646 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 @@ -13,8 +13,10 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow class FakeSyncService( - syncStateFlow: MutableStateFlow = MutableStateFlow(SyncState.Idle) + initialSyncState: SyncState = SyncState.Idle, ) : SyncService { + private val syncStateFlow: MutableStateFlow = MutableStateFlow(initialSyncState) + var startSyncLambda: () -> Result = { Result.success(Unit) } override suspend fun startSync(): Result { return startSyncLambda() @@ -26,4 +28,8 @@ class FakeSyncService( } override val syncState: StateFlow = syncStateFlow + + suspend fun emitSyncState(syncState: SyncState) { + syncStateFlow.emit(syncState) + } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt index 4dc45781cd..08b14e5680 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt @@ -36,7 +36,6 @@ import org.junit.Test class SyncOnNotifiableEventTest { private val timelineItems = MutableStateFlow>(emptyList()) - private val syncStateFlow = MutableStateFlow(SyncState.Idle) private val startSyncLambda = lambdaRecorder> { Result.success(Unit) } private val stopSyncLambda = lambdaRecorder> { Result.success(Unit) } private val subscribeToSyncLambda = lambdaRecorder { } @@ -49,7 +48,7 @@ class SyncOnNotifiableEventTest { liveTimeline = liveTimeline, subscribeToSyncLambda = subscribeToSyncLambda ) - private val syncService = FakeSyncService(syncStateFlow).also { + private val syncService = FakeSyncService(SyncState.Idle).also { it.startSyncLambda = startSyncLambda it.stopSyncLambda = stopSyncLambda } @@ -115,7 +114,7 @@ class SyncOnNotifiableEventTest { timelineItems.emit( listOf(MatrixTimelineItem.Event(A_UNIQUE_ID, anEventTimelineItem())) ) - syncStateFlow.emit(SyncState.Running) + syncService.emitSyncState(SyncState.Running) sut(notifiableEvent) assert(startSyncLambda).isCalledOnce()