Settling in on an auth screen design that I like...
This commit is contained in:
parent
01dbcbcc4a
commit
4c752b4a0f
6 changed files with 138 additions and 232 deletions
|
@ -31,7 +31,7 @@ android {
|
|||
)
|
||||
}
|
||||
debug {
|
||||
buildConfigField("String", "API_BASE_URL", "\"http://localhost:42069\"")
|
||||
buildConfigField("String", "API_BASE_URL", "\"http://10.0.2.2:42069\"")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
|
|
|
@ -8,16 +8,33 @@ 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.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Key
|
||||
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.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
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.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import software.patchwork.debtpirate.R
|
||||
|
||||
|
@ -28,48 +45,143 @@ fun AuthScreen(
|
|||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { innerPadding ->
|
||||
) {
|
||||
Column {
|
||||
AuthScreenTopAppBar(
|
||||
innerPadding,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
)
|
||||
|
||||
val displayName = viewModel.displayName.collectAsState()
|
||||
val emailAddress = viewModel.emailAddress.collectAsState()
|
||||
SignUpScreen(
|
||||
displayName,
|
||||
onDisplayNameUpdate = viewModel::updateDisplayName,
|
||||
emailAddress,
|
||||
onEmailAddressUpdate = viewModel::updateEmailAddress,
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(3f)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
val displayName = viewModel.displayName.collectAsState()
|
||||
val emailAddress = viewModel.emailAddress.collectAsState()
|
||||
RegistrationScreen(
|
||||
displayName,
|
||||
onDisplayNameUpdate = viewModel::updateDisplayName,
|
||||
emailAddress,
|
||||
onEmailAddressUpdate = viewModel::updateEmailAddress
|
||||
)
|
||||
|
||||
Separator(modifier = Modifier.padding(PaddingValues(top = 24.dp, bottom = 24.dp)))
|
||||
|
||||
PassKeyButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AuthScreenTopAppBar(innerPadding: PaddingValues, modifier: Modifier = Modifier) {
|
||||
private fun AuthScreenTopAppBar(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)
|
||||
)
|
||||
{
|
||||
OutlinedButton(onClick = { /*TODO*/ }) {
|
||||
Text(stringResource(id = R.string.login_screen__sign_in))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Separator(modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(2.dp)
|
||||
.weight(4.5f)
|
||||
.background(Color.LightGray)
|
||||
) {}
|
||||
|
||||
Text(
|
||||
text = "OR",
|
||||
style = TextStyle(
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color.Gray
|
||||
),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(2.dp)
|
||||
.weight(4.5f)
|
||||
.background(Color.LightGray)
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RegistrationScreen(
|
||||
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.auth_screen__display_name)) },
|
||||
placeholder = { Text(stringResource(id = R.string.auth_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.auth_screen__email)) },
|
||||
placeholder = { Text(stringResource(id = R.string.auth_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 = 16.dp))
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(id = R.string.auth_screen__register))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PassKeyButton() {
|
||||
OutlinedButton(
|
||||
onClick = { /*TODO*/ },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(Icons.Outlined.Key, "passkey")
|
||||
Text(
|
||||
stringResource(id = R.string.auth_screen__sign_in),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
package software.patchwork.debtpirate.screens.auth
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.interaction.FocusInteraction
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
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.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.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextLinkStyles
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
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.text.withLink
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import software.patchwork.debtpirate.R
|
||||
|
||||
@Composable
|
||||
internal fun SignInScreen(
|
||||
usernameState: StateFlow<String>,
|
||||
passwordState: StateFlow<String>,
|
||||
onUsernameUpdate: (String) -> Unit,
|
||||
onPasswordUpdate: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
val username = usernameState.collectAsState()
|
||||
OutlinedTextField(
|
||||
value = username.value,
|
||||
label = { Text(stringResource(id = R.string.login_screen__username)) },
|
||||
placeholder = { Text(stringResource(id = R.string.login_screen__username)) },
|
||||
leadingIcon = { Icon(Icons.Outlined.Person, "person") },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.Unspecified,
|
||||
keyboardType = KeyboardType.Email,
|
||||
imeAction = ImeAction.Next
|
||||
),
|
||||
onValueChange = onUsernameUpdate,
|
||||
interactionSource = remember { MutableInteractionSource() }.also { source ->
|
||||
LaunchedEffect(source) {
|
||||
source.interactions.collect { interaction ->
|
||||
when (interaction) {
|
||||
is FocusInteraction -> Log.v(
|
||||
"LoginScreen::LoginForm::usernameTextField",
|
||||
"Clicked!"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
|
||||
val password = passwordState.collectAsState()
|
||||
OutlinedTextField(
|
||||
value = password.value,
|
||||
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.Send
|
||||
),
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
onValueChange = onPasswordUpdate,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(PaddingValues(top = 8.dp))
|
||||
)
|
||||
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
withLink(
|
||||
LinkAnnotation.Url(
|
||||
"", TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
color = Color.Gray
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
append(stringResource(id = R.string.login_screen__forgot_password))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(PaddingValues(top = 16.dp))
|
||||
)
|
||||
Button(
|
||||
onClick = { /*TODO*/ },
|
||||
modifier = Modifier
|
||||
.padding(PaddingValues(top = 32.dp))
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(id = R.string.login_screen__sign_in))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package software.patchwork.debtpirate.screens.auth
|
||||
|
||||
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.patchwork.debtpirate.R
|
||||
|
||||
@Composable
|
||||
internal fun SignUpScreen(
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,10 @@
|
|||
<resources>
|
||||
<string name="app_name">Debt Pirate</string>
|
||||
|
||||
<!-- Login Screen -->
|
||||
<string name="login_screen__sign_in">Sign In</string>
|
||||
<string name="login_screen__signup">Sign Up</string>
|
||||
<string name="login_screen__username">Username</string>
|
||||
<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__display_name">Display Name</string>
|
||||
<string name="login_screen__forgot_password">Forgot Password?</string>
|
||||
<!-- Auth Screen -->
|
||||
<string name="auth_screen__register">Register</string>
|
||||
<string name="auth_screen__sign_in">Sign in with a Passkey</string>
|
||||
<string name="auth_screen__email">Email Address</string>
|
||||
<string name="auth_screen__display_name">Display Name</string>
|
||||
|
||||
</resources>
|
|
@ -30,7 +30,7 @@ androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-co
|
|||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
|
||||
androidx-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "iconsExtended" }
|
||||
androidx-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "iconsExtended" }
|
||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
||||
androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "navigation" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
|
|
Loading…
Add table
Reference in a new issue