Navigate to registration screen when button is clicked
This commit is contained in:
parent
fac5a55251
commit
40ff448d59
10 changed files with 143 additions and 57 deletions
|
@ -9,11 +9,15 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import ing.bikeshedengineer.debtpirate.theme.DebtPirateTheme
|
||||
import ing.bikeshedengineer.debtpirate.auth.presentation.AuthScreen
|
||||
import ing.bikeshedengineer.debtpirate.auth.presentation.login.LoginScreen
|
||||
import ing.bikeshedengineer.debtpirate.auth.presentation.register.RegistrationScreen
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
object AuthRoute
|
||||
object LoginRoute
|
||||
|
||||
@Serializable
|
||||
object RegistrationRoute
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
@ -25,8 +29,9 @@ class MainActivity : ComponentActivity() {
|
|||
val navController = rememberNavController()
|
||||
|
||||
DebtPirateTheme {
|
||||
NavHost(navController = navController, startDestination = AuthRoute) {
|
||||
composable<AuthRoute>() { AuthScreen() }
|
||||
NavHost(navController = navController, startDestination = LoginRoute) {
|
||||
composable<LoginRoute>() { LoginScreen(navController = navController) }
|
||||
composable<RegistrationRoute>() { RegistrationScreen() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.data.remote.model
|
||||
|
||||
data class AuthLoginRequest(val username: String, val password: String)
|
||||
data class AuthLoginRequest(val emailAddress: String, val password: String)
|
||||
|
|
|
@ -8,8 +8,8 @@ import ing.bikeshedengineer.debtpirate.auth.data.remote.repository.AuthRepositor
|
|||
class SubmitLoginCredentialsUseCase(
|
||||
private val authRepository: AuthRepository
|
||||
) {
|
||||
suspend operator fun invoke(username: String, password: String): AuthLoginResponse {
|
||||
val credentials = AuthLoginRequest(username, password)
|
||||
suspend operator fun invoke(emailAddress: String, password: String): AuthLoginResponse {
|
||||
val credentials = AuthLoginRequest(emailAddress, password)
|
||||
|
||||
try {
|
||||
val response = authRepository.submitLoginRequest(credentials)
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.domain.usecase
|
||||
|
||||
import android.util.Log
|
||||
|
||||
sealed class LoginCredentialsValidationResult {
|
||||
object EmptyCredentials : LoginCredentialsValidationResult()
|
||||
object EmptyUsernameField : LoginCredentialsValidationResult()
|
||||
object EmptyEmailAddressField : LoginCredentialsValidationResult()
|
||||
object EmptyPasswordField : LoginCredentialsValidationResult()
|
||||
object ValidCredentials : LoginCredentialsValidationResult()
|
||||
}
|
||||
|
||||
class ValidateLoginCredentialsUseCase {
|
||||
operator fun invoke(username: String, password: String): LoginCredentialsValidationResult {
|
||||
return if (username.isEmpty() && password.isEmpty()) {
|
||||
operator fun invoke(email: String, password: String): LoginCredentialsValidationResult {
|
||||
return if (email.isEmpty() && password.isEmpty()) {
|
||||
LoginCredentialsValidationResult.EmptyCredentials;
|
||||
} else if (username.isEmpty() && password.isNotEmpty()) {
|
||||
LoginCredentialsValidationResult.EmptyUsernameField;
|
||||
} else if (username.isNotEmpty() && password.isEmpty()) {
|
||||
} else if (email.isEmpty() && password.isNotEmpty()) {
|
||||
LoginCredentialsValidationResult.EmptyEmailAddressField;
|
||||
} else if (email.isNotEmpty() && password.isEmpty()) {
|
||||
LoginCredentialsValidationResult.EmptyPasswordField
|
||||
} else {
|
||||
LoginCredentialsValidationResult.ValidCredentials
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.navigation
|
||||
|
||||
sealed class AuthNavigationEvent {
|
||||
object NavigateToRegistrationScreen : AuthNavigationEvent()
|
||||
object NavigateToLoginScreen : AuthNavigationEvent()
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.presentation
|
||||
package ing.bikeshedengineer.debtpirate.auth.presentation.login
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
|
@ -15,8 +15,8 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
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.Password
|
||||
import androidx.compose.material.icons.outlined.Person
|
||||
import androidx.compose.material.icons.outlined.PersonAdd
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
|
@ -25,11 +25,13 @@ import androidx.compose.material3.OutlinedTextField
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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
|
||||
|
@ -38,17 +40,34 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import ing.bikeshedengineer.debtpirate.R
|
||||
import ing.bikeshedengineer.debtpirate.app.host.RegistrationRoute
|
||||
import ing.bikeshedengineer.debtpirate.auth.navigation.AuthNavigationEvent
|
||||
|
||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||
@Composable
|
||||
fun AuthScreen(
|
||||
viewModel: AuthScreenViewModel = hiltViewModel<AuthScreenViewModel>()
|
||||
fun LoginScreen(
|
||||
navController: NavController,
|
||||
viewModel: LoginScreenViewModel = hiltViewModel<LoginScreenViewModel>()
|
||||
) {
|
||||
LaunchedEffect(viewModel.navigationEvent) {
|
||||
viewModel.navigationEvent.collect { event ->
|
||||
when (event) {
|
||||
is AuthNavigationEvent.NavigateToRegistrationScreen -> {
|
||||
navController.navigate(RegistrationRoute)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Column {
|
||||
AuthScreenTopAppBar(
|
||||
LoginScreenTopAppBar(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
|
@ -59,11 +78,11 @@ fun AuthScreen(
|
|||
.weight(3f)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
val username = viewModel.username.collectAsState()
|
||||
val emailAddress = viewModel.emailAddress.collectAsState()
|
||||
val password = viewModel.password.collectAsState()
|
||||
LoginComponent(
|
||||
username = username,
|
||||
onUpdateUsername = viewModel::updateUsername,
|
||||
emailAddress = emailAddress,
|
||||
onUpdateEmailAddress = viewModel::updateUsername,
|
||||
password = password,
|
||||
onUpdatePassword = viewModel::updatePassword,
|
||||
submitLoginRequest = viewModel::submitLoginRequest
|
||||
|
@ -71,16 +90,26 @@ fun AuthScreen(
|
|||
|
||||
Separator(modifier = Modifier.padding(PaddingValues(top = 24.dp, bottom = 24.dp)))
|
||||
|
||||
RegisterButton()
|
||||
RegisterButton(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoginScreenTopAppBar(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.background(Color.Green)) {
|
||||
Text(
|
||||
text = "Hello from Login! I'm in a box!",
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoginComponent(
|
||||
username: State<String>,
|
||||
onUpdateUsername: (String) -> Unit,
|
||||
emailAddress: State<String>,
|
||||
onUpdateEmailAddress: (String) -> Unit,
|
||||
password: State<String>,
|
||||
onUpdatePassword: (String) -> Unit,
|
||||
submitLoginRequest: () -> Unit,
|
||||
|
@ -88,25 +117,30 @@ private fun LoginComponent(
|
|||
) {
|
||||
Column(modifier.verticalScroll(rememberScrollState())) {
|
||||
OutlinedTextField(
|
||||
value = username.value,
|
||||
label = { Text("Username") },
|
||||
placeholder = { Text("Username") },
|
||||
leadingIcon = { Icon(Icons.Outlined.Person, "person") },
|
||||
value = emailAddress.value,
|
||||
label = { Text(text = stringResource(R.string.auth__email)) },
|
||||
placeholder = { Text(text = stringResource(R.string.auth__email)) },
|
||||
leadingIcon = { Icon(Icons.Outlined.Mail, stringResource(R.string.auth__email)) },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
keyboardType = KeyboardType.Text,
|
||||
keyboardType = KeyboardType.Email,
|
||||
imeAction = ImeAction.Next
|
||||
),
|
||||
onValueChange = onUpdateUsername,
|
||||
onValueChange = onUpdateEmailAddress,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password.value,
|
||||
label = { Text("Password") },
|
||||
placeholder = { Text("Password") },
|
||||
leadingIcon = { Icon(Icons.Outlined.Password, "password") },
|
||||
label = { Text(text = stringResource(R.string.auth__password)) },
|
||||
placeholder = { Text(text = stringResource(R.string.auth__password)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Outlined.Password,
|
||||
stringResource(R.string.auth__password)
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
|
@ -133,16 +167,6 @@ private fun LoginComponent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Separator(modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
|
@ -172,9 +196,9 @@ private fun Separator(modifier: Modifier = Modifier) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun RegisterButton() {
|
||||
private fun RegisterButton(viewModel: LoginScreenViewModel) {
|
||||
OutlinedButton(
|
||||
onClick = { /*TODO*/ },
|
||||
onClick = { viewModel.onRegisterButtonClick() },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
|
@ -1,16 +1,16 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.presentation
|
||||
package ing.bikeshedengineer.debtpirate.auth.presentation.login
|
||||
|
||||
import android.util.Log
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import ing.bikeshedengineer.debtpirate.PrefsDataStore
|
||||
import ing.bikeshedengineer.debtpirate.auth.data.remote.model.AuthLoginRequest
|
||||
import ing.bikeshedengineer.debtpirate.app.host.RegistrationRoute
|
||||
import ing.bikeshedengineer.debtpirate.auth.data.remote.repository.AuthRepository
|
||||
import ing.bikeshedengineer.debtpirate.auth.domain.usecase.LoginCredentialsValidationResult
|
||||
import ing.bikeshedengineer.debtpirate.auth.domain.usecase.SubmitLoginCredentialsUseCase
|
||||
import ing.bikeshedengineer.debtpirate.auth.domain.usecase.ValidateLoginCredentialsUseCase
|
||||
import ing.bikeshedengineer.debtpirate.auth.navigation.AuthNavigationEvent
|
||||
import ing.bikeshedengineer.debtpirate.domain.model.Token
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
@ -34,8 +34,9 @@ enum class InvalidReason {
|
|||
PasswordTooShort
|
||||
}
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class AuthScreenViewModel @Inject constructor(
|
||||
class LoginScreenViewModel @Inject constructor(
|
||||
private val authRepository: AuthRepository,
|
||||
private val prefsStore: DataStore<PrefsDataStore>,
|
||||
private val submitLoginCredentials: SubmitLoginCredentialsUseCase,
|
||||
|
@ -43,14 +44,17 @@ class AuthScreenViewModel @Inject constructor(
|
|||
) : ViewModel() {
|
||||
// private val storeLoginData = StoreLoginDataUseCase(dataStore)
|
||||
|
||||
private val _username = MutableStateFlow("")
|
||||
val username = _username.asStateFlow()
|
||||
private val _navigationEvent = MutableStateFlow<AuthNavigationEvent?>(null)
|
||||
val navigationEvent = _navigationEvent.asStateFlow()
|
||||
|
||||
private val _emailAddress = MutableStateFlow("")
|
||||
val emailAddress = _emailAddress.asStateFlow()
|
||||
|
||||
private val _password = MutableStateFlow("")
|
||||
val password = _password.asStateFlow()
|
||||
|
||||
fun updateUsername(username: String) {
|
||||
_username.value = username
|
||||
_emailAddress.value = username
|
||||
}
|
||||
|
||||
fun updatePassword(password: String) {
|
||||
|
@ -58,16 +62,17 @@ class AuthScreenViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun submitLoginRequest() {
|
||||
when (validateLoginCredentials(username.value, password.value)) {
|
||||
when (validateLoginCredentials(emailAddress.value, password.value)) {
|
||||
is LoginCredentialsValidationResult.ValidCredentials -> {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = submitLoginCredentials(username.value, password.value)
|
||||
val result = submitLoginCredentials(emailAddress.value, password.value)
|
||||
} catch (err: Throwable) {
|
||||
// TODO...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// TODO...
|
||||
}
|
||||
|
@ -94,4 +99,8 @@ class AuthScreenViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onRegisterButtonClick() {
|
||||
_navigationEvent.value = AuthNavigationEvent.NavigateToRegistrationScreen
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.presentation.register
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||
@Composable()
|
||||
fun RegistrationScreen() {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Column {
|
||||
RegistrationScreenTopAppBar(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RegistrationScreenTopAppBar(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.background(Color.Green)) {
|
||||
Text(
|
||||
text = "Hello from Registration! I'm in a box!",
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package ing.bikeshedengineer.debtpirate.auth.presentation.register
|
||||
|
||||
class RegistrationScreenViewModel {
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<resources>
|
||||
<string name="app_name">Debt Pirate</string>
|
||||
<string name="title_activity_main">MainActivity</string>
|
||||
<string name="auth__email">Email</string>
|
||||
<string name="auth__password">Password</string>
|
||||
</resources>
|
Loading…
Add table
Reference in a new issue