Fix navigation issue.
Ensure that the timeout has effect only in Idle state.
This commit is contained in:
parent
73e1a092d2
commit
f5e1cbef38
2 changed files with 59 additions and 57 deletions
|
|
@ -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<NavigationEvent> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue