fix: preserve login input during preference load

This commit is contained in:
2026-08-23 23:02:01 +08:00
parent 3764acb7ec
commit 344fa6dafa
4 changed files with 99 additions and 15 deletions
@@ -80,7 +80,8 @@ fun LoginScreen(
onSwitchProvider: () -> Unit,
switchingProvider: Boolean = false,
) {
val navigationEnabled = !state.isSubmitting && !switchingProvider
val formEnabled = !state.isSubmitting && !state.isLoadingPreferences
val navigationEnabled = formEnabled && !switchingProvider
BackHandler {
if (navigationEnabled) onSwitchProvider()
}
@@ -140,7 +141,7 @@ fun LoginScreen(
value = state.username,
onValueChange = onUsernameChange,
label = { Text(stringResource(R.string.login_username)) },
enabled = !state.isSubmitting,
enabled = formEnabled,
singleLine = true,
modifier = Modifier.fillMaxWidth(),
)
@@ -149,29 +150,31 @@ fun LoginScreen(
onValueChange = onPasswordChange,
label = { Text(stringResource(R.string.login_password)) },
visualTransformation = PasswordVisualTransformation(),
enabled = !state.isSubmitting,
enabled = formEnabled,
singleLine = true,
modifier = Modifier.fillMaxWidth(),
)
ToggleRow(
label = stringResource(R.string.login_remember_username),
checked = state.rememberUsername,
enabled = !state.isSubmitting,
enabled = formEnabled,
onCheckedChange = onRememberUsernameChange,
)
ToggleRow(
label = stringResource(R.string.login_auto_login),
checked = state.autoLogin,
enabled = !state.isSubmitting,
enabled = formEnabled,
onCheckedChange = onAutoLoginChange,
)
state.errorMessage?.let { Text(it, color = MaterialTheme.colorScheme.error) }
ABoxButton(
onClick = onSubmit,
enabled = !state.isSubmitting && !switchingProvider,
enabled = formEnabled && !switchingProvider,
modifier = Modifier.fillMaxWidth(),
) {
if (state.isSubmitting || switchingProvider) ABoxLoadingIndicator()
if (state.isSubmitting || state.isLoadingPreferences || switchingProvider) {
ABoxLoadingIndicator()
}
else Text(stringResource(R.string.action_login))
}
ABoxButton(
@@ -27,20 +27,39 @@ class LoginViewModel @Inject constructor(
val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()
private var profile: ProviderProfile? = null
private var loadingJob: Job? = null
private var usernameEdited = false
private var rememberUsernameEdited = false
private var autoLoginEdited = false
fun loadProfile(value: ProviderProfile) {
val profileChanged = profile?.id != value.id
if (!profileChanged && loadingJob != null) return
loadingJob?.cancel()
profile = value
_uiState.value = LoginUiState()
usernameEdited = false
rememberUsernameEdited = false
autoLoginEdited = false
_uiState.value = LoginUiState(isLoadingPreferences = true)
loadingJob = viewModelScope.launch {
val preferences = profileStore.loginPreferences(value.id)
if (profile?.id != value.id) return@launch
_uiState.value = LoginUiState(
username = preferences.rememberedUsername?.value.orEmpty(),
rememberUsername = preferences.rememberUsername,
autoLogin = preferences.autoLoginEnabled,
_uiState.value = _uiState.value.copy(
username = if (usernameEdited) {
_uiState.value.username
} else {
preferences.rememberedUsername?.value.orEmpty()
},
rememberUsername = if (rememberUsernameEdited) {
_uiState.value.rememberUsername
} else {
preferences.rememberUsername
},
autoLogin = if (autoLoginEdited) {
_uiState.value.autoLogin
} else {
preferences.autoLoginEnabled
},
isLoadingPreferences = false,
)
}
}
@@ -49,10 +68,14 @@ class LoginViewModel @Inject constructor(
loadingJob?.cancel()
loadingJob = null
profile = null
usernameEdited = false
rememberUsernameEdited = false
autoLoginEdited = false
_uiState.value = LoginUiState()
}
fun setUsername(value: String) {
usernameEdited = true
_uiState.value = _uiState.value.copy(username = value, errorMessage = null)
}
@@ -61,17 +84,23 @@ class LoginViewModel @Inject constructor(
}
fun setRememberUsername(value: Boolean) {
rememberUsernameEdited = true
_uiState.value = _uiState.value.copy(rememberUsername = value)
}
fun setAutoLogin(value: Boolean) {
autoLoginEdited = true
_uiState.value = _uiState.value.copy(autoLogin = value)
}
fun submit() {
val activeProfile = profile ?: return
val state = _uiState.value
if (state.username.isBlank() || state.password.isEmpty() || state.isSubmitting) {
if (state.username.isBlank() ||
state.password.isEmpty() ||
state.isSubmitting ||
state.isLoadingPreferences
) {
_uiState.value = state.copy(errorMessage = "Username and password are required")
return
}
@@ -104,6 +133,7 @@ data class LoginUiState(
val password: String = "",
val rememberUsername: Boolean = false,
val autoLogin: Boolean = false,
val isLoadingPreferences: Boolean = false,
val isSubmitting: Boolean = false,
val errorMessage: String? = null,
)