fix: scope session errors to active screen

This commit is contained in:
2026-08-24 01:02:58 +08:00
parent 547146792f
commit 40bbc2f30b
2 changed files with 43 additions and 9 deletions
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
@@ -26,11 +27,14 @@ fun ABoxManagerApp(
sessionActionsViewModel: SessionActionsViewModel = hiltViewModel(), sessionActionsViewModel: SessionActionsViewModel = hiltViewModel(),
) { ) {
val actionState = sessionActionsViewModel.uiState.collectAsStateWithLifecycle().value val actionState = sessionActionsViewModel.uiState.collectAsStateWithLifecycle().value
val sessionState = appSessionViewModel.uiState.collectAsStateWithLifecycle().value
val screenKey = sessionState.screenKey()
LaunchedEffect(screenKey) {
sessionActionsViewModel.dismissError()
}
ABoxTheme { ABoxTheme {
ABoxSurface(modifier = Modifier.fillMaxSize()) { ABoxSurface(modifier = Modifier.fillMaxSize()) {
when ( when (val state = sessionState) {
val state = appSessionViewModel.uiState.collectAsStateWithLifecycle().value
) {
AppSessionUiState.Loading -> Box( AppSessionUiState.Loading -> Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -58,3 +62,10 @@ fun ABoxManagerApp(
} }
} }
} }
private fun AppSessionUiState.screenKey(): String = when (this) {
AppSessionUiState.Loading -> "loading"
AppSessionUiState.ProviderSelection -> "providers"
is AppSessionUiState.Login -> "login:${profile.id.value}"
is AppSessionUiState.Authenticated -> "authenticated:${profile.id.value}"
}
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.flagship.abox.manager.domain.DomainResult import com.flagship.abox.manager.domain.DomainResult
import com.flagship.abox.manager.domain.ProviderProfile import com.flagship.abox.manager.domain.ProviderProfile
import com.flagship.abox.manager.domain.ProviderProfileId
import com.flagship.abox.manager.domain.ProviderProfileStore import com.flagship.abox.manager.domain.ProviderProfileStore
import com.flagship.abox.manager.domain.Session import com.flagship.abox.manager.domain.Session
import com.flagship.abox.manager.domain.SessionCoordinator import com.flagship.abox.manager.domain.SessionCoordinator
@@ -24,15 +25,19 @@ class AppSessionViewModel @Inject constructor(
private val profileStore: ProviderProfileStore, private val profileStore: ProviderProfileStore,
private val availability: ProviderAvailability, private val availability: ProviderAvailability,
) : ViewModel() { ) : ViewModel() {
private val bootstrapResult = MutableStateFlow<DomainResult<Session?>?>(null) private val bootstrapResult = MutableStateFlow<ProviderBootstrapResult?>(null)
val uiState: StateFlow<AppSessionUiState> = combine( val uiState: StateFlow<AppSessionUiState> = combine(
bootstrapResult, bootstrapResult,
profileStore.observeActiveProfile(), profileStore.observeActiveProfile(),
coordinator.observeActiveSession(), coordinator.observeActiveSession(),
) { result, profile, session -> ) { bootstrap, profile, session ->
if (result == null) AppSessionUiState.Loading if (bootstrap == null) AppSessionUiState.Loading
else stateFor(profile, session, (result as? DomainResult.Failure)?.error?.detail) else stateFor(
profile = profile,
session = session,
bootstrapError = bootstrap.errorFor(profile?.id),
)
}.stateIn( }.stateIn(
scope = viewModelScope, scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000), started = SharingStarted.WhileSubscribed(5_000),
@@ -44,9 +49,15 @@ class AppSessionViewModel @Inject constructor(
val activeProfile = profileStore.activeProfile() val activeProfile = profileStore.activeProfile()
if (activeProfile != null && availability.forType(activeProfile.type) is Availability.Disabled) { if (activeProfile != null && availability.forType(activeProfile.type) is Availability.Disabled) {
coordinator.switchProvider() coordinator.switchProvider()
bootstrapResult.value = DomainResult.Success(null) bootstrapResult.value = ProviderBootstrapResult(
profileId = null,
result = DomainResult.Success(null),
)
} else { } else {
bootstrapResult.value = coordinator.bootstrap() bootstrapResult.value = ProviderBootstrapResult(
profileId = activeProfile?.id,
result = coordinator.bootstrap(),
)
} }
} }
} }
@@ -63,6 +74,18 @@ class AppSessionViewModel @Inject constructor(
} }
} }
private data class ProviderBootstrapResult(
val profileId: ProviderProfileId?,
val result: DomainResult<Session?>,
) {
fun errorFor(currentProfileId: ProviderProfileId?): String? =
if (profileId == currentProfileId) {
(result as? DomainResult.Failure)?.error?.detail
} else {
null
}
}
sealed interface AppSessionUiState { sealed interface AppSessionUiState {
data object Loading : AppSessionUiState data object Loading : AppSessionUiState
data object ProviderSelection : AppSessionUiState data object ProviderSelection : AppSessionUiState