feat: add provider login flow #4

Merged
KremeCN merged 26 commits from feat/provider-login into main 2026-08-25 01:59:00 +08:00
3 changed files with 18 additions and 3 deletions
Showing only changes of commit 8788fe6ec5 - Show all commits
@@ -21,13 +21,17 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -47,6 +51,9 @@ fun LoginRoute(
viewModel: LoginViewModel = hiltViewModel(),
) {
LaunchedEffect(profile.id) { viewModel.loadProfile(profile) }
DisposableEffect(profile.id) {
onDispose(viewModel::clearSensitiveState)
}
val state = viewModel.uiState.collectAsStateWithLifecycle().value
LoginScreen(
profile = profile,
@@ -146,6 +153,11 @@ fun LoginScreen(
onValueChange = onPasswordChange,
label = { Text(stringResource(R.string.login_password)) },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done,
autoCorrect = false,
),
enabled = formEnabled,
singleLine = true,
modifier = Modifier.fillMaxWidth(),
@@ -43,12 +43,12 @@ class ProviderProfileStoreImpl(
override fun observeLoginPreferences(profileId: ProviderProfileId): Flow<LoginPreferences> =
safeData.map { preferencesFrom(it, profileId) }
override suspend fun profiles(): List<ProviderProfile> = profilesFrom(dataStore.data.first())
override suspend fun profiles(): List<ProviderProfile> = profilesFrom(safeData.first())
override suspend fun activeProfile(): ProviderProfile? = activeProfileFrom(dataStore.data.first())
override suspend fun activeProfile(): ProviderProfile? = activeProfileFrom(safeData.first())
override suspend fun loginPreferences(profileId: ProviderProfileId): LoginPreferences =
preferencesFrom(dataStore.data.first(), profileId)
preferencesFrom(safeData.first(), profileId)
override suspend fun saveProfile(profile: ProviderProfile): DomainResult<Unit> = operation {
dataStore.edit { preferences ->
@@ -19,6 +19,9 @@ class ProviderProfileStoreFailureTest {
assertEquals(emptyList<Any>(), store.observeProfiles().first())
assertEquals(null, store.observeActiveProfile().first())
assertEquals(emptyList<Any>(), store.profiles())
assertEquals(null, store.activeProfile())
assertEquals(false, store.loginPreferences(com.flagship.abox.manager.domain.ProviderProfileId("profile")).autoLoginEnabled)
}
private class FailingReadDataStore : DataStore<Preferences> {