Fix navigation issue.

Ensure that the timeout has effect only in Idle state.
This commit is contained in:
Benoit Marty 2026-04-13 15:25:37 +02:00
parent 7a10ce9e54
commit 21512bbe33
2 changed files with 59 additions and 57 deletions

View file

@ -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))
}
}
}
}
}
}
}