fix: isolate login credentials across providers

This commit is contained in:
2026-08-23 22:25:21 +08:00
parent cc8eeebdc4
commit 3764acb7ec
6 changed files with 76 additions and 4 deletions
@@ -53,6 +53,29 @@ class LoginScreenTest {
assertEquals(2, switches)
}
@Test
fun systemBackIsConsumedWhileSubmitting() {
var switches = 0
composeRule.setContent {
ABoxTheme {
LoginScreen(
profile = profile,
state = LoginUiState(isSubmitting = true),
onUsernameChange = {},
onPasswordChange = {},
onRememberUsernameChange = {},
onAutoLoginChange = {},
onSubmit = {},
onSwitchProvider = { switches++ },
)
}
}
composeRule.activity.onBackPressedDispatcher.onBackPressed()
composeRule.waitForIdle()
assertEquals(0, switches)
}
@Test
fun backControlsAreDisabledWhileSubmitting() {
composeRule.setContent {
@@ -48,6 +48,10 @@ fun LoginRoute(
) {
LaunchedEffect(profile.id) { viewModel.loadProfile(profile) }
val state = viewModel.uiState.collectAsStateWithLifecycle().value
val switchProvider = {
viewModel.clearSensitiveState()
onSwitchProvider()
}
LoginScreen(
profile = profile,
state = state.copy(
@@ -58,7 +62,7 @@ fun LoginRoute(
onRememberUsernameChange = viewModel::setRememberUsername,
onAutoLoginChange = viewModel::setAutoLogin,
onSubmit = viewModel::submit,
onSwitchProvider = onSwitchProvider,
onSwitchProvider = switchProvider,
switchingProvider = actionInProgress,
)
}
@@ -77,7 +81,9 @@ fun LoginScreen(
switchingProvider: Boolean = false,
) {
val navigationEnabled = !state.isSubmitting && !switchingProvider
BackHandler(enabled = navigationEnabled, onBack = onSwitchProvider)
BackHandler {
if (navigationEnabled) onSwitchProvider()
}
Scaffold(
topBar = {
@@ -29,11 +29,15 @@ class LoginViewModel @Inject constructor(
private var loadingJob: Job? = null
fun loadProfile(value: ProviderProfile) {
if (profile?.id == value.id && loadingJob != null) return
val profileChanged = profile?.id != value.id
if (!profileChanged && loadingJob != null) return
loadingJob?.cancel()
profile = value
_uiState.value = LoginUiState()
loadingJob = viewModelScope.launch {
val preferences = profileStore.loginPreferences(value.id)
_uiState.value = _uiState.value.copy(
if (profile?.id != value.id) return@launch
_uiState.value = LoginUiState(
username = preferences.rememberedUsername?.value.orEmpty(),
rememberUsername = preferences.rememberUsername,
autoLogin = preferences.autoLoginEnabled,
@@ -41,6 +45,13 @@ class LoginViewModel @Inject constructor(
}
}
fun clearSensitiveState() {
loadingJob?.cancel()
loadingJob = null
profile = null
_uiState.value = LoginUiState()
}
fun setUsername(value: String) {
_uiState.value = _uiState.value.copy(username = value, errorMessage = null)
}