From 24f230632380f38c41eae7815bd6b1a880a8a98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Karpi=C5=84ski?= Date: Mon, 15 Jul 2024 10:40:48 +0200 Subject: [PATCH] Fix signup screen --- .../librepass/android/ui/auth/SignupScreen.kt | 167 ++++++++---------- .../android/ui/auth/SignupViewModel.kt | 24 ++- 2 files changed, 94 insertions(+), 97 deletions(-) diff --git a/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupScreen.kt b/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupScreen.kt index 85174ce3..86ba1647 100644 --- a/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupScreen.kt +++ b/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupScreen.kt @@ -2,26 +2,20 @@ package dev.medzik.librepass.android.ui.auth import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.input.InputTransformation.Companion.keyboardOptions import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Password import androidx.compose.material.icons.filled.QuestionMark import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -40,8 +34,6 @@ import dev.medzik.android.compose.ui.textfield.AnimatedTextField import dev.medzik.android.compose.ui.textfield.PasswordAnimatedTextField import dev.medzik.android.compose.ui.textfield.TextFieldValue import dev.medzik.librepass.android.ui.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @Serializable @@ -49,7 +41,12 @@ object Signup @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SignupScreen(navController: NavController) { +fun SignupScreen( + navController: NavController, + viewModel: SignupViewModel = hiltViewModel() +) { + val context = LocalContext.current + Scaffold( topBar = { TopAppBar( @@ -62,98 +59,87 @@ fun SignupScreen(navController: NavController) { ) } ) { innerPadding -> - SignupScreenContent(navController, innerPadding) - } -} - -@Composable -fun SignupScreenContent( - navController: NavController, - innerPadding: PaddingValues, - viewModel: SignupViewModel = hiltViewModel() -) { - val context = LocalContext.current - - LazyColumn( - modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), - verticalArrangement = Arrangement.SpaceBetween - ) { - item { - Column( - modifier = Modifier.regularHorizontalPadding(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - EmailTextField( - value = TextFieldValue.fromMutableState( - state = viewModel.email + LazyColumn( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize(), + verticalArrangement = Arrangement.SpaceBetween + ) { + item { + Column( + modifier = Modifier.regularHorizontalPadding(), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + EmailTextField( + value = TextFieldValue.fromMutableState( + state = viewModel.email + ) ) - ) - PasswordAnimatedTextField( - label = stringResource(R.string.Password), - value = TextFieldValue.fromMutableState( - state = viewModel.password, - valueLabel = TextFieldValue.ValueLabel( - type = TextFieldValue.ValueLabel.Type.WARNING, - text = context.getString(R.string.PasswordLabel_WontBeRecover) + PasswordAnimatedTextField( + label = stringResource(R.string.Password), + value = TextFieldValue.fromMutableState( + state = viewModel.password, + valueLabel = TextFieldValue.ValueLabel( + type = TextFieldValue.ValueLabel.Type.WARNING, + text = context.getString(R.string.PasswordLabel_WontBeRecover) + ) + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email ) - ), - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Email ) - ) - PasswordAnimatedTextField( - label = stringResource(R.string.RetypePassword), - value = TextFieldValue.fromMutableState( - state = viewModel.retypePassword, - error = if (!viewModel.retypePasswordIsValid) { - "Passwords mismatch" - } else null - ), - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Email + PasswordAnimatedTextField( + label = stringResource(R.string.RetypePassword), + value = TextFieldValue.fromMutableState( + state = viewModel.retypePassword, + error = if (viewModel.password.value.isNotEmpty() && viewModel.retypePasswordError) { + "Passwords mismatch" + } else null + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email + ) ) - ) - AnimatedTextField( - label = stringResource(R.string.PasswordHint), - value = TextFieldValue.fromMutableState( - state = viewModel.passwordHint, - valueLabel = TextFieldValue.ValueLabel( - type = TextFieldValue.ValueLabel.Type.INFO, - text = context.getString(R.string.PasswordHintLabel) + AnimatedTextField( + label = stringResource(R.string.PasswordHint), + value = TextFieldValue.fromMutableState( + state = viewModel.passwordHint, + valueLabel = TextFieldValue.ValueLabel( + type = TextFieldValue.ValueLabel.Type.INFO, + text = context.getString(R.string.PasswordHintLabel) + ) + ), + leading = { IconBox(Icons.Default.QuestionMark) }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email ) - ), - leading = { IconBox(Icons.Default.QuestionMark) }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Email ) - ) - ChoiceServer(viewModel.server) + ChoiceServer(viewModel.server) + } } - } - item { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - LoadingButton( - modifier = Modifier - .padding(vertical = 12.dp) - .height(50.dp) - .fillMaxWidth(0.85f), - onClick = { viewModel.register(context, navController) }, - enabled = viewModel.canLogin + item { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = stringResource(R.string.Signup), - style = MaterialTheme.typography.titleMedium - ) + LoadingButton( + modifier = Modifier + .padding(vertical = 12.dp) + .height(50.dp) + .fillMaxWidth(0.85f), + onClick = { viewModel.register(navController) }, + enabled = viewModel.canSignup + ) { + Text( + text = stringResource(R.string.Signup), + style = MaterialTheme.typography.titleMedium + ) + } } } } @@ -164,6 +150,9 @@ fun SignupScreenContent( @Composable fun SignupPreview() { MaterialTheme { - SignupScreen(rememberNavController()) + SignupScreen( + navController = rememberNavController(), + viewModel = SignupViewModel(context = LocalContext.current) + ) } } diff --git a/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupViewModel.kt b/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupViewModel.kt index 758fd459..49df0315 100644 --- a/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupViewModel.kt +++ b/ui-logic/src/main/java/dev/medzik/librepass/android/ui/auth/SignupViewModel.kt @@ -4,28 +4,36 @@ import android.content.Context import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.navigation.NavController +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import dev.medzik.android.utils.showToast import dev.medzik.librepass.android.common.haveNetworkConnection import dev.medzik.librepass.android.ui.R import dev.medzik.librepass.client.Server import dev.medzik.librepass.client.api.AuthClient +import javax.inject.Inject -class SignupViewModel : ViewModel() { +@HiltViewModel +class SignupViewModel @Inject constructor( + @ApplicationContext private val context: Context +) : ViewModel() { val email = mutableStateOf("") val password = mutableStateOf("") val retypePassword = mutableStateOf("") - val retypePasswordIsValid = password.value.isNotEmpty() && - retypePassword.value == password.value val passwordHint = mutableStateOf("") - val canLogin = email.value.isNotEmpty() && - password.value.isNotEmpty() && - retypePassword.value == password.value + + val retypePasswordError: Boolean + get() = retypePassword.value != password.value + val canSignup: Boolean + get() = email.value.isNotEmpty() && + password.value.isNotEmpty() && + !retypePasswordError val server = mutableStateOf(Server.PRODUCTION) private val authClient = AuthClient(apiUrl = server.value) - fun register(context: Context, navController: NavController) { + fun register(navController: NavController) { if (!context.haveNetworkConnection()) { context.showToast(R.string.NoInternetConnection) return @@ -40,7 +48,7 @@ class SignupViewModel : ViewModel() { // TODO: disable back navController.navigate(Login) } catch (e: Exception) { - + // TODO } } }