From e2cb0bbe07f454a9c6e1a7fd25651f17bbcce880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Rodr=C3=ADguez?= Date: Wed, 16 Aug 2023 15:06:45 -0300 Subject: [PATCH 1/2] Sign Up UI added --- .../androidcomposebase/ui/MainActivity.kt | 2 + .../ui/pages/sign_up/SignUpScreen.kt | 84 +++++++++++++++++++ .../ui/pages/sign_up/SignUpUiState.kt | 20 +++++ .../ui/pages/sign_up/SignUpViewModel.kt | 48 +++++++++++ example/app/src/main/res/values/strings.xml | 7 ++ .../com/rootstrap/example/data/Patterns.kt | 1 + 6 files changed, 162 insertions(+) create mode 100644 example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpScreen.kt create mode 100644 example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpUiState.kt create mode 100644 example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt diff --git a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/MainActivity.kt b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/MainActivity.kt index 847a444..d8a683e 100644 --- a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/MainActivity.kt +++ b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/MainActivity.kt @@ -17,6 +17,8 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.rootstrap.androidcomposebase.ui.pages.login.LogInScreen import com.rootstrap.androidcomposebase.ui.pages.login.LogInViewModel +import com.rootstrap.androidcomposebase.ui.pages.sign_up.SignUpScreen +import com.rootstrap.androidcomposebase.ui.pages.sign_up.SignUpViewModel import com.rootstrap.androidcomposebase.ui.theme.AppTheme import org.koin.androidx.viewmodel.ext.android.viewModel diff --git a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpScreen.kt b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpScreen.kt new file mode 100644 index 0000000..fad7ef4 --- /dev/null +++ b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpScreen.kt @@ -0,0 +1,84 @@ +package com.rootstrap.androidcomposebase.ui.pages.sign_up + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.rootstrap.androidcomposebase.ui.common.AppButton +import com.rootstrap.androidcomposebase.ui.common.AppTextField +import com.rootstrap.androidcomposebase.ui.theme.Padding +import com.rootstrap.example.app.R + +@Preview +@Composable +fun SignUpScreen( + viewModel: SignUpViewModel = SignUpViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + Column( + verticalArrangement = Arrangement.SpaceEvenly, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .background(Color.Black) + .padding(horizontal = Padding.huge) + ) { + Icon( + painter = painterResource(id = R.drawable.rootstrap_logo), + contentDescription = null, + tint = Color.White + ) + Column( + verticalArrangement = Arrangement.Center + ) { + AppTextField( + value = uiState.name, + onValueChange = { viewModel.onNameChanged(it) }, + label = stringResource(id = R.string.sign_up_name_label), + showError = uiState.showNameError, + errorMessage = stringResource(id = R.string.sign_up_name_error) + ) + + AppTextField( + value = uiState.email, + onValueChange = { viewModel.onEmailChanged(it) }, + label = stringResource(id = R.string.log_in_email_label), + showError = uiState.showEmailError, + errorMessage = stringResource(id = R.string.log_in_email_error) + ) + + AppTextField( + value = uiState.password, + onValueChange = { viewModel.onPasswordChanged(it) }, + label = stringResource(id = R.string.log_in_password_label), + showError = uiState.showPasswordError, + errorMessage = stringResource(id = R.string.log_in_password_error), + isPasswordField = true + ) + + AppTextField( + value = uiState.confirmPassword, + onValueChange = { viewModel.onConfirmPasswordChanged(it) }, + label = stringResource(id = R.string.sign_up_confirm_password_label), + showError = uiState.showConfirmPasswordError, + errorMessage = stringResource(id = R.string.sign_up_confirm_password_error), + isPasswordField = true + ) + } + + AppButton( + label = R.string.sign_up_button, enabled = uiState.isButtonEnabled + ) { + viewModel.onSignUpButtonClicked() + } + } +} diff --git a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpUiState.kt b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpUiState.kt new file mode 100644 index 0000000..a9d3886 --- /dev/null +++ b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpUiState.kt @@ -0,0 +1,20 @@ +package com.rootstrap.androidcomposebase.ui.pages.sign_up + +data class SignUpUiState( + val name: String = "", + val showNameError: Boolean = false, + val email: String = "", + val showEmailError: Boolean = false, + val password: String = "", + val showPasswordError: Boolean = false, + val confirmPassword: String = "", + val showConfirmPasswordError: Boolean = false, +) { + + val isButtonEnabled: Boolean + get() { + return name.isNotEmpty() && !showNameError && email.isNotEmpty() && !showEmailError + && password.isNotEmpty() && !showPasswordError && + confirmPassword.isNotEmpty() && !showConfirmPasswordError + } +} \ No newline at end of file diff --git a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt new file mode 100644 index 0000000..031e563 --- /dev/null +++ b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt @@ -0,0 +1,48 @@ +package com.rootstrap.androidcomposebase.ui.pages.sign_up + +import android.util.Patterns +import androidx.lifecycle.ViewModel +import com.rootstrap.androidcomposebase.ui.pages.login.LoginUiState +import com.rootstrap.example.data.PatternsUtil +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import java.util.regex.Pattern + +class SignUpViewModel : ViewModel() { + + private var _uiState: MutableStateFlow = MutableStateFlow(SignUpUiState()) + val uiState: StateFlow + get() = _uiState + + fun onNameChanged(name: String) { + val isNameValid = + Pattern.compile(PatternsUtil.NAME_REGEX).matcher(name).matches() || name.isEmpty() + _uiState.update { it.copy(name = name, showNameError = !isNameValid) } + } + + fun onEmailChanged(email: String) { + val isEmailValid = Patterns.EMAIL_ADDRESS.toRegex().matches(email) || email.isEmpty() + _uiState.update { it.copy(email = email, showEmailError = !isEmailValid) } + } + + fun onPasswordChanged(password: String) { + val isPasswordValid = Pattern.compile(PatternsUtil.PASSWORD_REGEX).matcher(password) + .matches() || password.isEmpty() + _uiState.update { it.copy(password = password, showPasswordError = !isPasswordValid) } + } + + fun onConfirmPasswordChanged(confirmPassword: String) { + val isConfirmPasswordValid = _uiState.value.password == confirmPassword + _uiState.update { + it.copy( + confirmPassword = confirmPassword, + showConfirmPasswordError = !isConfirmPasswordValid + ) + } + } + + fun onSignUpButtonClicked() { + // TODO: Do log in request + } +} \ No newline at end of file diff --git a/example/app/src/main/res/values/strings.xml b/example/app/src/main/res/values/strings.xml index 3f8c4df..3c8cbb7 100644 --- a/example/app/src/main/res/values/strings.xml +++ b/example/app/src/main/res/values/strings.xml @@ -8,4 +8,11 @@ Password must contain a lower case, upper case, a number and a special character LOG IN + + Name + Name not valid + Confirm Password + Passwords don\'t match + SIGN UP + diff --git a/example/core/data/src/main/java/com/rootstrap/example/data/Patterns.kt b/example/core/data/src/main/java/com/rootstrap/example/data/Patterns.kt index afce284..5e06ecc 100644 --- a/example/core/data/src/main/java/com/rootstrap/example/data/Patterns.kt +++ b/example/core/data/src/main/java/com/rootstrap/example/data/Patterns.kt @@ -3,4 +3,5 @@ package com.rootstrap.example.data object PatternsUtil { const val PASSWORD_REGEX = "^(?=.*[0-9])(?=.*[A-Z])(?=.*[@#\$%^&+=!]).{4,}\$" + const val NAME_REGEX = "^[a-zA-Z]{4,}(?: [a-zA-Z]+){0,2}\$" } From 4d2fecd9445ac0d0efbcbed952540637b8486015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Rodr=C3=ADguez?= Date: Wed, 16 Aug 2023 15:16:01 -0300 Subject: [PATCH 2/2] Refactor --- .../androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt index 031e563..d65de2c 100644 --- a/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt +++ b/example/app/src/main/java/com/rootstrap/androidcomposebase/ui/pages/sign_up/SignUpViewModel.kt @@ -43,6 +43,6 @@ class SignUpViewModel : ViewModel() { } fun onSignUpButtonClicked() { - // TODO: Do log in request + // TODO: Do Sign Up request } -} \ No newline at end of file +}