diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelper.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelper.kt index f719fe5083..cae4f834d0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelper.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelper.kt @@ -12,67 +12,60 @@ import io.element.android.features.login.impl.classic.ElementClassicConnection import io.element.android.features.login.impl.classic.ElementClassicConnectionState import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.toUserListFlow +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.flowOf @Inject class ClassicFlowNodeHelper( private val elementClassicConnection: ElementClassicConnection, private val sessionStore: SessionStore, ) { - // Ensure user is not stuck on the loading screen. - // If Element Classic is taking too long to communicate (or crashes), unblock the user after a few seconds. - private val timeoutFLow = flow { - emit(false) - delay(5_000) - emit(true) - } - + @OptIn(ExperimentalCoroutinesApi::class) fun navigationEventFlow(): Flow { - return combine( - timeoutFLow, - elementClassicConnection.stateFlow - .distinctUntilChangedBy { - // Ignore change on ElementClassicConnectionState.ElementClassicReady.avatar - if (it is ElementClassicConnectionState.ElementClassicReady) { - it.copy(avatar = null) - } else { - it - } - }, - sessionStore.sessionsFlow().toUserListFlow() - // Take only 1 emission of the sessions, else when the user actually logged in it will trigger a navigation to OnBoarding. - .take(1), - ) { timeout, elementClassicConnectionState, existingSessions -> - when (elementClassicConnectionState) { - ElementClassicConnectionState.Idle -> { - if (timeout) { - NavigationEvent.NavigateToOnBoarding - } else { - NavigationEvent.Idle - } + return elementClassicConnection.stateFlow + .distinctUntilChangedBy { + // Ignore change on ElementClassicConnectionState.ElementClassicReady.avatar + if (it is ElementClassicConnectionState.ElementClassicReady) { + it.copy(avatar = null) + } else { + it } - ElementClassicConnectionState.ElementClassicNotFound, - ElementClassicConnectionState.ElementClassicReadyNoSession, - is ElementClassicConnectionState.Error -> { - NavigationEvent.NavigateToOnBoarding - } - is ElementClassicConnectionState.ElementClassicReady -> { - if (elementClassicConnectionState.elementClassicSession.userId.value in existingSessions) { - NavigationEvent.NavigateToOnBoarding - } else { - // 2 cases when this can be run: - // First time this screen will be displayed - // Missing key backup screen was displayed, but the data has changed (user set up the key backup on Classic), - // and the app is resuming. - NavigationEvent.NavigateToLoginWithClassic(elementClassicConnectionState.elementClassicSession.userId) + } + .flatMapLatest { elementClassicConnectionState -> + when (elementClassicConnectionState) { + ElementClassicConnectionState.Idle -> { + // Ensure user is not stuck on the loading screen. + // If Element Classic is taking too long to communicate (or crashes), unblock the user after a few seconds. + flow { + emit(NavigationEvent.Idle) + delay(5_000) + emit(NavigationEvent.NavigateToOnBoarding) + } + } + ElementClassicConnectionState.ElementClassicNotFound, + ElementClassicConnectionState.ElementClassicReadyNoSession, + is ElementClassicConnectionState.Error -> { + flowOf(NavigationEvent.NavigateToOnBoarding) + } + is ElementClassicConnectionState.ElementClassicReady -> { + val existingSessions = sessionStore.sessionsFlow().toUserListFlow().first() + if (elementClassicConnectionState.elementClassicSession.userId.value in existingSessions) { + flowOf(NavigationEvent.NavigateToOnBoarding) + } else { + // 2 cases when this can be run: + // First time this screen will be displayed + // Missing key backup screen was displayed, but the data has changed (user set up the key backup on Classic), + // and the app is resuming. + flowOf(NavigationEvent.NavigateToLoginWithClassic(elementClassicConnectionState.elementClassicSession.userId)) + } } } } - } } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelperTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelperTest.kt index 1184bb91af..017fd1b633 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelperTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNodeHelperTest.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -38,16 +39,6 @@ class ClassicFlowNodeHelperTest { @get:Rule val warmUpRule = WarmUpRule() - @Test - fun `initial state`() = runTest { - createHelper() - .navigationEventFlow() - .test { - val initialState = awaitItem() - assertThat(initialState).isEqualTo(NavigationEvent.Idle) - } - } - @Test fun `after a few seconds in Idle, NavigateToOnBoarding is emitted`() = runTest { createHelper() @@ -57,6 +48,8 @@ class ClassicFlowNodeHelperTest { assertThat(initialState).isEqualTo(NavigationEvent.Idle) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToOnBoarding) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -82,6 +75,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToOnBoarding) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -100,6 +95,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToOnBoarding) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -118,6 +115,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToOnBoarding) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -136,6 +135,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToOnBoarding) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -154,6 +155,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToLoginWithClassic(A_USER_ID)) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -178,6 +181,7 @@ class ClassicFlowNodeHelperTest { avatar = createBitmap(1, 1) ) ) + advanceTimeBy(10_000) expectNoEvents() } } @@ -211,6 +215,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToLoginWithClassic(A_USER_ID)) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -236,6 +242,8 @@ class ClassicFlowNodeHelperTest { ) val finalState = awaitItem() assertThat(finalState).isEqualTo(NavigationEvent.NavigateToLoginWithClassic(A_USER_ID)) + advanceTimeBy(10_000) + expectNoEvents() } } @@ -264,6 +272,7 @@ class ClassicFlowNodeHelperTest { sessionId = A_USER_ID.value, ) ) + advanceTimeBy(10_000) expectNoEvents() } }