Separate the registration and sign in screens

This commit is contained in:
Z. Charles Dziura 2024-08-20 21:06:07 -04:00
parent ffd58cac7a
commit bab607a898
7 changed files with 158 additions and 235 deletions

View file

@ -9,9 +9,9 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.serialization.Serializable
import software.makeshift.debtpirate.screens.login.`LoginFormViewModel.kt`
import software.makeshift.debtpirate.screens.login.LoginFormViewModel
import software.makeshift.debtpirate.screens.login.LoginScreen
import software.makeshift.debtpirate.screens.login.SignUpFormViewModel
import software.makeshift.debtpirate.screens.login.signup.SignUpFormViewModel
import software.makeshift.debtpirate.ui.theme.DebtPirateTheme
@Serializable
@ -28,8 +28,8 @@ class MainActivity : ComponentActivity() {
NavHost(navController = navController, startDestination = LoginRoute) {
composable<LoginRoute> {
LoginScreen(
`loginFormViewModel.kt` = viewModel<`LoginFormViewModel.kt`>(),
viewModel = viewModel<SignUpFormViewModel>()
loginFormViewModel = viewModel<LoginFormViewModel>(),
signUpFormViewModel = viewModel<SignUpFormViewModel>()
)
}
}

View file

@ -7,6 +7,8 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -21,6 +23,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
@ -29,65 +32,77 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import software.makeshift.debtpirate.R
import software.makeshift.debtpirate.screens.login.signup.SignUpForm
import software.makeshift.debtpirate.screens.login.signup.SignUpFormViewModel
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun LoginScreen(
`loginFormViewModel.kt`: `LoginFormViewModel.kt`,
viewModel: SignUpFormViewModel
loginFormViewModel: LoginFormViewModel,
signUpFormViewModel: SignUpFormViewModel
) {
Scaffold(modifier = Modifier.fillMaxSize()) {
Scaffold(
modifier = Modifier.fillMaxSize()
) { innerPadding ->
Column {
Banner(
LoginTopBar(
innerPadding,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
LoginAndSignUpForms(
`loginFormViewModel.kt`,
viewModel,
val displayName = signUpFormViewModel.displayName.collectAsState()
val emailAddress = signUpFormViewModel.emailAddress.collectAsState()
SignUpForm(
displayName,
onDisplayNameUpdate = signUpFormViewModel::updateDisplayName,
emailAddress,
onEmailAddressUpdate = signUpFormViewModel::updateEmailAddress,
modifier = Modifier
.weight(3f)
.padding(16.dp)
)
}
}
}
@Composable
private fun Banner(modifier: Modifier = Modifier) {
private fun LoginTopBar(innerPadding: PaddingValues, modifier: Modifier = Modifier) {
Box(modifier = modifier.background(Color.Green)) {
Text(
text = "Hello from Login! I'm in a box!",
modifier = Modifier.align(Alignment.Center)
)
Row(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(innerPadding)
.padding(16.dp)
)
{
TextButton(onClick = { /*TODO*/ }) {
Text("Sign In")
}
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun LoginAndSignUpForms(
`loginFormViewModel.kt`: `LoginFormViewModel.kt`,
viewModel: SignUpFormViewModel,
loginFormViewModel: LoginFormViewModel,
signUpFormViewModel: SignUpFormViewModel,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
val pagerState = rememberPagerState(initialPage = 0) { 2 }
val pagerState = rememberPagerState(initialPage = 0) { 1 }
SecondaryTabRow(
selectedTabIndex = pagerState.currentPage,
modifier = Modifier.height(48.dp)
) {
Tab(
selected = pagerState.currentPage == 0,
onClick = { pagerState.requestScrollToPage(0) },
text = {
Text(
text = stringResource(id = R.string.login_screen__login)
)
},
modifier = Modifier.fillMaxHeight()
)
Tab(
selected = pagerState.currentPage == 1,
onClick = { pagerState.requestScrollToPage(1) },
@ -98,6 +113,16 @@ private fun LoginAndSignUpForms(
},
modifier = Modifier.fillMaxHeight()
)
// Tab(
// selected = pagerState.currentPage == 0,
// onClick = { pagerState.requestScrollToPage(0) },
// text = {
// Text(
// text = stringResource(id = R.string.login_screen__login)
// )
// },
// modifier = Modifier.fillMaxHeight()
// )
}
HorizontalPager(
@ -114,24 +139,11 @@ private fun LoginAndSignUpForms(
when (page) {
0 -> {
LoginForm(
`loginFormViewModel.kt`.username,
`loginFormViewModel.kt`.password,
`loginFormViewModel.kt`::updateUsername,
`loginFormViewModel.kt`::updateLoginPassword,
formContainerModifier
)
}
1 -> {
val signUpState = viewModel.signUpState.collectAsState()
SignUpForm(
state = signUpState,
viewModel::updateFirstName,
viewModel::updateLastName,
viewModel::updateEmailAddress,
viewModel::updatePassword,
viewModel::updateConfirmPassword,
signUpFormViewModel.displayName.collectAsState(),
signUpFormViewModel::updateDisplayName,
signUpFormViewModel.emailAddress.collectAsState(),
signUpFormViewModel::updateEmailAddress,
formContainerModifier
)
}

View file

@ -1,136 +0,0 @@
package software.makeshift.debtpirate.screens.login
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material.icons.outlined.Mail
import androidx.compose.material.icons.outlined.Password
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import software.makeshift.debtpirate.R
@Composable
internal fun SignUpForm(
state: State<SignUpState>,
onFirstNameUpdate: (String) -> Unit,
onLastNameUpdate: (String) -> Unit,
onEmailUpdate: (String) -> Unit,
onPasswordUpdate: (String) -> Unit,
onPasswordConfirmedUpdate: (String) -> Unit,
modifier: Modifier = Modifier
) {
Column(modifier.verticalScroll(rememberScrollState()))
{
OutlinedTextField(
value = state.value.firstName,
label = { Text(stringResource(id = R.string.login_screen__first_name)) },
placeholder = { Text(stringResource(id = R.string.login_screen__first_name)) },
leadingIcon = { Icon(Icons.Outlined.Person, "person") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Words,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
onValueChange = onFirstNameUpdate,
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = state.value.lastName,
label = { Text(stringResource(id = R.string.login_screen__last_name)) },
placeholder = { Text(stringResource(id = R.string.login_screen__last_name)) },
leadingIcon = { Icon(Icons.Outlined.Person, "person") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Words,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
onValueChange = onLastNameUpdate,
modifier = Modifier
.fillMaxWidth()
.padding(PaddingValues(top = 8.dp))
)
OutlinedTextField(
value = state.value.email,
label = { Text(stringResource(id = R.string.login_screen__email)) },
placeholder = { Text(stringResource(id = R.string.login_screen__email)) },
leadingIcon = { Icon(Icons.Outlined.Mail, "email") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Unspecified,
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
),
onValueChange = onEmailUpdate,
modifier = Modifier
.fillMaxWidth()
.padding(PaddingValues(top = 8.dp))
)
OutlinedTextField(
value = state.value.password,
label = { Text(stringResource(id = R.string.login_screen__password)) },
placeholder = { Text(stringResource(id = R.string.login_screen__password)) },
leadingIcon = { Icon(Icons.Outlined.Lock, "password") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Unspecified,
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Next
),
visualTransformation = PasswordVisualTransformation(),
onValueChange = onPasswordUpdate,
modifier = Modifier
.fillMaxWidth()
.padding(PaddingValues(top = 8.dp))
)
OutlinedTextField(
value = state.value.passwordConfirmed,
label = { Text(stringResource(id = R.string.login_screen__confirm_password)) },
placeholder = { Text(stringResource(id = R.string.login_screen__confirm_password)) },
leadingIcon = { Icon(Icons.Outlined.Password, "confirm password") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Unspecified,
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Send
),
visualTransformation = PasswordVisualTransformation(),
onValueChange = onPasswordConfirmedUpdate,
modifier = Modifier
.fillMaxWidth()
.padding(PaddingValues(top = 8.dp))
)
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.padding(PaddingValues(top = 32.dp))
.fillMaxWidth()
) {
Text(stringResource(id = R.string.login_screen__signup))
}
}
}

View file

@ -1,55 +0,0 @@
package software.makeshift.debtpirate.screens.login
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
data class SignUpState(
val firstName: String = "",
val lastName: String = "",
val email: String = "",
val password: String = "",
val passwordConfirmed: String = ""
)
class SignUpFormViewModel : ViewModel() {
private val _firstName = MutableStateFlow("")
val firstName = _firstName.asStateFlow()
private val _lastName = MutableStateFlow("")
val lastName = _lastName.asStateFlow()
private val _emailAddress = MutableStateFlow("")
val emailAddress = _emailAddress.asStateFlow()
private val _password = MutableStateFlow("")
val password = _password.asStateFlow()
private val _confirmPassword = MutableStateFlow("")
val confirmPassword = _confirmPassword.asStateFlow()
fun updateFirstName(firstName: String) {
_firstName.value = firstName
}
fun updateLastName(lastName: String) {
_lastName.value = lastName
}
fun updateEmailAddress(emailAddress: String) {
_emailAddress.value = emailAddress
}
fun updatePassword(password: String) {
_password.value = password
}
fun updateConfirmPassword(confirmPassword: String) {
_confirmPassword.value = confirmPassword
}
fun isValidPassword() {
}
}

View file

@ -0,0 +1,78 @@
package software.makeshift.debtpirate.screens.login.signup
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Mail
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import software.makeshift.debtpirate.R
@Composable
internal fun SignUpForm(
displayName: State<String>,
onDisplayNameUpdate: (String) -> Unit,
emailAddress: State<String>,
onEmailAddressUpdate: (String) -> Unit,
modifier: Modifier = Modifier
) {
Column(modifier.verticalScroll(rememberScrollState()))
{
OutlinedTextField(
value = displayName.value,
label = { Text(stringResource(id = R.string.login_screen__display_name)) },
placeholder = { Text(stringResource(id = R.string.login_screen__display_name)) },
leadingIcon = { Icon(Icons.Outlined.Person, "person") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Words,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
onValueChange = onDisplayNameUpdate,
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = emailAddress.value,
label = { Text(stringResource(id = R.string.login_screen__email)) },
placeholder = { Text(stringResource(id = R.string.login_screen__email)) },
leadingIcon = { Icon(Icons.Outlined.Mail, "email") },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Unspecified,
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
),
onValueChange = onEmailAddressUpdate,
modifier = Modifier
.fillMaxWidth()
.padding(PaddingValues(top = 8.dp))
)
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.padding(PaddingValues(top = 32.dp))
.fillMaxWidth()
) {
Text(stringResource(id = R.string.login_screen__signup))
}
}
}

View file

@ -0,0 +1,25 @@
package software.makeshift.debtpirate.screens.login.signup
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class SignUpFormViewModel : ViewModel() {
private val _displayName = MutableStateFlow("")
val displayName = _displayName.asStateFlow()
private val _emailAddress = MutableStateFlow("")
val emailAddress = _emailAddress.asStateFlow()
fun updateDisplayName(firstName: String) {
_displayName.value = firstName
}
fun updateEmailAddress(emailAddress: String) {
_emailAddress.value = emailAddress
}
fun isValidPassword() {
}
}

View file

@ -8,8 +8,7 @@
<string name="login_screen__email">Email Address</string>
<string name="login_screen__password">Password</string>
<string name="login_screen__confirm_password">Confirm Password</string>
<string name="login_screen__first_name">First Name</string>
<string name="login_screen__last_name">Last Name</string>
<string name="login_screen__display_name">Display Name</string>
<string name="login_screen__forgot_password">Forgot Password?</string>
</resources>