Create SyncOrchestrator (#4176)

* Create `SyncOrchestrator` to centralise the sync start/stop flow through the whole app: the decision is based on several inputs: sync state, network available, app in foreground, app in call, app needing to sync an event for a notification.

* Make network monitor return network connectivity status, not internet connectivity

* Don't stop the `SyncService` when network connection is lost, let it fail instead. This prevents an issue when using the offline mode of the SDK, which made the wrong UI states to be shown when the `SyncState` is `Idle` (that is, after the service being manually stopped).

* Rename `NetworkStatus.Online/Offline` to `Connected/Disconnected` so they're not easily mistaken with internet connectivity instead
This commit is contained in:
Jorge Martin Espinosa 2025-02-06 16:36:57 +01:00 committed by GitHub
parent ce1c01e626
commit 3c87fb05b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 851 additions and 344 deletions

View file

@ -9,6 +9,7 @@ package io.element.android.features.messages.impl
import android.app.Activity
import android.content.Context
import androidx.activity.compose.LocalActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@ -17,7 +18,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.Lifecycle
import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
@ -223,7 +223,7 @@ class MessagesNode @AssistedInject constructor(
@Composable
override fun View(modifier: Modifier) {
val activity = LocalContext.current as Activity
val activity = requireNotNull(LocalActivity.current)
val isDark = ElementTheme.isLightTheme.not()
CompositionLocalProvider(
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,

View file

@ -8,6 +8,7 @@
package io.element.android.features.messages.impl.pinned
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.mapState
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn
@ -26,6 +27,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import javax.inject.Inject
@SingleIn(RoomScope::class)
@ -33,6 +35,7 @@ class PinnedEventsTimelineProvider @Inject constructor(
private val room: MatrixRoom,
private val syncService: SyncService,
private val featureFlagService: FeatureFlagService,
private val dispatchers: CoroutineDispatchers,
) : TimelineProvider {
private val _timelineStateFlow: MutableStateFlow<AsyncData<Timeline>> =
MutableStateFlow(AsyncData.Uninitialized)
@ -100,7 +103,9 @@ class PinnedEventsTimelineProvider @Inject constructor(
when (timelineStateFlow.value) {
is AsyncData.Uninitialized, is AsyncData.Failure -> {
timelineStateFlow.emit(AsyncData.Loading())
room.pinnedEventsTimeline()
withContext(dispatchers.io) {
room.pinnedEventsTimeline()
}
.fold(
{ timelineStateFlow.emit(AsyncData.Success(it)) },
{ timelineStateFlow.emit(AsyncData.Failure(it)) }

View file

@ -194,7 +194,8 @@ class PinnedMessagesBannerPresenterTest {
syncService = syncService,
featureFlagService = FakeFeatureFlagService(
initialState = mapOf(FeatureFlags.PinnedEvents.key to isFeatureEnabled)
)
),
dispatchers = testCoroutineDispatchers(),
)
timelineProvider.launchIn(backgroundScope)

View file

@ -38,6 +38,7 @@ import io.element.android.tests.testutils.lambda.assert
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.test
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
@ -302,7 +303,8 @@ class PinnedMessagesListPresenterTest {
syncService = syncService,
featureFlagService = FakeFeatureFlagService(
initialState = mapOf(FeatureFlags.PinnedEvents.key to isFeatureEnabled)
)
),
dispatchers = testCoroutineDispatchers(),
)
timelineProvider.launchIn(backgroundScope)
return PinnedMessagesListPresenter(