From a50e39efc376d78fb1ad6412e4690c25968424c4 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 22 Aug 2022 10:39:29 -0700 Subject: [PATCH] move login fields code out of main activity and into email view --- .../app/omnivore/omnivore/MainActivity.kt | 192 ------------------ .../main/java/app/omnivore/omnivore/Routes.kt | 4 +- .../omnivore/omnivore/ui/auth/EmailLogin.kt | 129 +++++++++++- .../auth/{SplashPage.kt => WelcomeScreen.kt} | 34 ++-- .../app/omnivore/omnivore/ui/home/HomeView.kt | 33 +++ .../omnivore/omnivore/ui/main/ScreenMain.kt | 35 +++- 6 files changed, 204 insertions(+), 223 deletions(-) rename android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/{SplashPage.kt => WelcomeScreen.kt} (68%) create mode 100644 android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt index b17bcfde6..5ddc0927d 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt @@ -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() -// } -//} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt index c6ca342c8..338a099e9 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt @@ -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") } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt index ef4c30c78..5b8f00d3e 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt @@ -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") + } + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/SplashPage.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt similarity index 68% rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/SplashPage.kt rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt index cb275fc52..a072d9669 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/SplashPage.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt @@ -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) } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt new file mode 100644 index 000000000..7aa3772f4 --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt @@ -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") + } + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/main/ScreenMain.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/main/ScreenMain.kt index cef82c262..a59b6b93d 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/main/ScreenMain.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/main/ScreenMain.kt @@ -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) + } +}