mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
move login fields code out of main activity and into email view
This commit is contained in:
parent
53b8f7eeac
commit
a50e39efc3
6 changed files with 204 additions and 223 deletions
|
|
@ -1,38 +1,17 @@
|
|||
package app.omnivore.omnivore
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import app.omnivore.omnivore.ui.auth.GoogleAuthButton
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
import app.omnivore.omnivore.ui.main.ScreenMain
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
|
@ -43,11 +22,7 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
setContent {
|
||||
OmnivoreTheme {
|
||||
// A surface container using the 'background' color from the theme
|
||||
ScreenMain(viewModel = viewModel)
|
||||
// Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
// WelcomeView(viewModel)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,170 +36,3 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WelcomeView(viewModel: LoginViewModel) {
|
||||
val hasAuthToken: Boolean by viewModel.hasAuthTokenLiveData.observeAsState(false)
|
||||
|
||||
if (hasAuthToken) {
|
||||
LoggedInView(viewModel)
|
||||
} else {
|
||||
LoginView(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoginView(viewModel: LoginViewModel) {
|
||||
val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
|
||||
.getInstance()
|
||||
.isGooglePlayServicesAvailable(LocalContext.current) == 0
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
) {
|
||||
if (isGoogleAuthAvailable) {
|
||||
GoogleAuthButton(viewModel)
|
||||
}
|
||||
EmailLoginView(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoggedInView(viewModel: LoginViewModel) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Text("You have a valid auth token. Nice. Go save something in Chrome!")
|
||||
|
||||
Button(onClick = {
|
||||
viewModel.logout()
|
||||
}) {
|
||||
Text(text = "Logout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CoroutineCreationDuringComposition")
|
||||
@Composable
|
||||
fun EmailLoginView(viewModel: LoginViewModel) {
|
||||
var email by rememberSaveable { mutableStateOf("") }
|
||||
var password by rememberSaveable { mutableStateOf("") }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
.clickable { focusManager.clearFocus() }
|
||||
) {
|
||||
LoginFields(
|
||||
email,
|
||||
password,
|
||||
onEmailChange = { email = it },
|
||||
onPasswordChange = { password = it },
|
||||
onLoginClick = { viewModel.login(email, password) }
|
||||
)
|
||||
|
||||
// TODO: add a activity indicator (maybe after a delay?)
|
||||
if (viewModel.isLoading) {
|
||||
Text("Loading...")
|
||||
}
|
||||
|
||||
if (viewModel.errorMessage != null) {
|
||||
coroutineScope.launch {
|
||||
val result = snackBarHostState
|
||||
.showSnackbar(
|
||||
viewModel.errorMessage!!,
|
||||
actionLabel = "Dismiss",
|
||||
duration = SnackbarDuration.Indefinite
|
||||
)
|
||||
when (result) {
|
||||
SnackbarResult.ActionPerformed -> viewModel.resetErrorMessage()
|
||||
}
|
||||
}
|
||||
|
||||
SnackbarHost(hostState = snackBarHostState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LoginFields(
|
||||
email: String,
|
||||
password: String,
|
||||
onEmailChange: (String) -> Unit,
|
||||
onPasswordChange: (String) -> Unit,
|
||||
onLoginClick: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(300.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(25.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("Never miss a great read")
|
||||
|
||||
OutlinedTextField(
|
||||
value = email,
|
||||
placeholder = { Text(text = "user@email.com") },
|
||||
label = { Text(text = "email") },
|
||||
onValueChange = onEmailChange,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
placeholder = { Text(text = "password") },
|
||||
label = { Text(text = "password") },
|
||||
onValueChange = onPasswordChange,
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
|
||||
)
|
||||
|
||||
Button(onClick = {
|
||||
if (email.isNotBlank() && password.isNotBlank()) {
|
||||
onLoginClick()
|
||||
focusManager.clearFocus()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Please enter an email address and password.",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}) {
|
||||
Text(text = "Login")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Preview(
|
||||
// uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
// showBackground = true,
|
||||
// name = "Dark Mode"
|
||||
//)
|
||||
//@Preview(showBackground = true)
|
||||
//@Composable
|
||||
//fun DefaultPreview() {
|
||||
// OmnivoreTheme {
|
||||
// LoginView()
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package app.omnivore.omnivore
|
||||
|
||||
sealed class Routes(val route: String) {
|
||||
object Splash : Routes("Splash")
|
||||
object Root : Routes("Root")
|
||||
object Home : Routes("Home")
|
||||
object Welcome : Routes("Welcome")
|
||||
object EmailLogin : Routes("EmailLogin")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
package app.omnivore.omnivore.ui.auth
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.EmailLoginView
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun EmailLoginPage(viewModel: LoginViewModel, navController: NavHostController) {
|
||||
fun EmailLoginViewContainer(viewModel: LoginViewModel, navController: NavHostController) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
|
@ -25,3 +33,108 @@ fun EmailLoginPage(viewModel: LoginViewModel, navController: NavHostController)
|
|||
EmailLoginView(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CoroutineCreationDuringComposition")
|
||||
@Composable
|
||||
fun EmailLoginView(viewModel: LoginViewModel) {
|
||||
var email by rememberSaveable { mutableStateOf("") }
|
||||
var password by rememberSaveable { mutableStateOf("") }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
.clickable { focusManager.clearFocus() }
|
||||
) {
|
||||
LoginFields(
|
||||
email,
|
||||
password,
|
||||
onEmailChange = { email = it },
|
||||
onPasswordChange = { password = it },
|
||||
onLoginClick = { viewModel.login(email, password) }
|
||||
)
|
||||
|
||||
// TODO: add a activity indicator (maybe after a delay?)
|
||||
if (viewModel.isLoading) {
|
||||
Text("Loading...")
|
||||
}
|
||||
|
||||
if (viewModel.errorMessage != null) {
|
||||
coroutineScope.launch {
|
||||
val result = snackBarHostState
|
||||
.showSnackbar(
|
||||
viewModel.errorMessage!!,
|
||||
actionLabel = "Dismiss",
|
||||
duration = SnackbarDuration.Indefinite
|
||||
)
|
||||
when (result) {
|
||||
SnackbarResult.ActionPerformed -> viewModel.resetErrorMessage()
|
||||
}
|
||||
}
|
||||
|
||||
SnackbarHost(hostState = snackBarHostState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LoginFields(
|
||||
email: String,
|
||||
password: String,
|
||||
onEmailChange: (String) -> Unit,
|
||||
onPasswordChange: (String) -> Unit,
|
||||
onLoginClick: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(300.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(25.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("Never miss a great read")
|
||||
|
||||
OutlinedTextField(
|
||||
value = email,
|
||||
placeholder = { Text(text = "user@email.com") },
|
||||
label = { Text(text = "email") },
|
||||
onValueChange = onEmailChange,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
placeholder = { Text(text = "password") },
|
||||
label = { Text(text = "password") },
|
||||
onValueChange = onPasswordChange,
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
|
||||
)
|
||||
|
||||
Button(onClick = {
|
||||
if (email.isNotBlank() && password.isNotBlank()) {
|
||||
onLoginClick()
|
||||
focusManager.clearFocus()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Please enter an email address and password.",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}) {
|
||||
Text(text = "Login")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -21,27 +22,30 @@ import app.omnivore.omnivore.Routes
|
|||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
|
||||
@Composable
|
||||
fun SplashPage(viewModel: LoginViewModel, navController: NavHostController) {
|
||||
fun WelcomeScreen(viewModel: LoginViewModel, navController: NavHostController) {
|
||||
val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
|
||||
.getInstance()
|
||||
.isGooglePlayServicesAvailable(LocalContext.current) == 0
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
Text("Never miss a great read")
|
||||
MoreInfoButton()
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
||||
if (isGoogleAuthAvailable) {
|
||||
GoogleAuthButton(viewModel)
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
Text("Never miss a great read")
|
||||
MoreInfoButton()
|
||||
|
||||
if (isGoogleAuthAvailable) {
|
||||
GoogleAuthButton(viewModel)
|
||||
}
|
||||
|
||||
ContinueWithEmailButton(navController)
|
||||
}
|
||||
|
||||
ContinueWithEmailButton(navController)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package app.omnivore.omnivore.ui.home
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
|
||||
@Composable
|
||||
fun HomeView(viewModel: LoginViewModel, navController: NavHostController) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Text("You have a valid auth token. Nice. Go save something in Chrome!")
|
||||
|
||||
Button(onClick = {
|
||||
viewModel.logout()
|
||||
}) {
|
||||
Text(text = "Logout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,45 @@
|
|||
package app.omnivore.omnivore.ui.main
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import app.omnivore.omnivore.Routes
|
||||
import app.omnivore.omnivore.ui.auth.EmailLoginPage
|
||||
import app.omnivore.omnivore.ui.auth.SplashPage
|
||||
import app.omnivore.omnivore.ui.auth.EmailLoginViewContainer
|
||||
import app.omnivore.omnivore.ui.auth.LoginViewModel
|
||||
import app.omnivore.omnivore.ui.auth.WelcomeScreen
|
||||
import app.omnivore.omnivore.ui.home.HomeView
|
||||
|
||||
@Composable
|
||||
fun ScreenMain(viewModel: LoginViewModel){
|
||||
fun ScreenMain(viewModel: LoginViewModel) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(navController = navController, startDestination = Routes.Splash.route) {
|
||||
composable(Routes.Splash.route) {
|
||||
SplashPage(viewModel = viewModel, navController = navController)
|
||||
NavHost(navController = navController, startDestination = Routes.Root.route) {
|
||||
composable(Routes.Root.route) {
|
||||
RootView(viewModel = viewModel, navController = navController)
|
||||
}
|
||||
composable(Routes.Welcome.route) {
|
||||
WelcomeScreen(viewModel = viewModel, navController = navController)
|
||||
}
|
||||
composable(Routes.Home.route) {
|
||||
HomeView(viewModel = viewModel, navController = navController)
|
||||
}
|
||||
composable(Routes.EmailLogin.route) {
|
||||
EmailLoginPage(viewModel = viewModel, navController = navController)
|
||||
EmailLoginViewContainer(viewModel = viewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RootView(viewModel: LoginViewModel, navController: NavHostController) {
|
||||
val hasAuthToken: Boolean by viewModel.hasAuthTokenLiveData.observeAsState(false)
|
||||
|
||||
if (hasAuthToken) {
|
||||
HomeView(viewModel = viewModel, navController = navController)
|
||||
} else {
|
||||
WelcomeScreen(viewModel = viewModel, navController = navController)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue