From 2ba9ea111fa6c09bdacc5b19001926f347d5c2ad Mon Sep 17 00:00:00 2001 From: Cody Weaver Date: Thu, 4 Jan 2024 17:32:18 -0700 Subject: [PATCH] add ErrOnLoginScreen and empty recipe list placeholder surface --- app/build.gradle.kts | 2 +- .../pocketalchemy/PaInstrumentedTest.kt | 2 +- .../com/android/pocketalchemy/MainActivity.kt | 2 +- .../com/android/pocketalchemy/PaNavHost.kt | 22 ++++++- .../editrecipe/EditRecipeViewModel.kt | 13 ++-- .../pocketalchemy/login/ErrOnLoginScreen.kt | 62 +++++++++++++++++++ .../pocketalchemy/recipelist/RecipeList.kt | 45 +++++++++++--- .../recipelist/RecipeListScreen.kt | 2 +- .../recipelist/RecipeListViewModel.kt | 3 +- .../AuthRepository.kt | 6 +- .../{model => repository}/RecipeRepository.kt | 14 +++-- app/src/main/res/values/strings.xml | 7 ++- 12 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/android/pocketalchemy/login/ErrOnLoginScreen.kt rename app/src/main/java/com/android/pocketalchemy/{firebase => repository}/AuthRepository.kt (91%) rename app/src/main/java/com/android/pocketalchemy/{model => repository}/RecipeRepository.kt (88%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8216da..63f921a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,7 +35,7 @@ android { isMinifyEnabled = false isDebuggable = true isDefault = true - buildConfigField("Boolean", "DEBUG", "false") + buildConfigField("Boolean", "DEBUG", "true") } release { diff --git a/app/src/androidTest/java/com/android/pocketalchemy/PaInstrumentedTest.kt b/app/src/androidTest/java/com/android/pocketalchemy/PaInstrumentedTest.kt index 6ec1afd..ee17f54 100644 --- a/app/src/androidTest/java/com/android/pocketalchemy/PaInstrumentedTest.kt +++ b/app/src/androidTest/java/com/android/pocketalchemy/PaInstrumentedTest.kt @@ -5,7 +5,7 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.lifecycle.Lifecycle -import com.android.pocketalchemy.firebase.AuthRepository +import com.android.pocketalchemy.repository.AuthRepository import com.android.pocketalchemy.ui.theme.PocketAlchemyTheme import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest diff --git a/app/src/main/java/com/android/pocketalchemy/MainActivity.kt b/app/src/main/java/com/android/pocketalchemy/MainActivity.kt index ac3c8ee..98707f8 100644 --- a/app/src/main/java/com/android/pocketalchemy/MainActivity.kt +++ b/app/src/main/java/com/android/pocketalchemy/MainActivity.kt @@ -3,7 +3,7 @@ package com.android.pocketalchemy import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import com.android.pocketalchemy.firebase.AuthRepository +import com.android.pocketalchemy.repository.AuthRepository import com.android.pocketalchemy.ui.theme.PocketAlchemyTheme import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject diff --git a/app/src/main/java/com/android/pocketalchemy/PaNavHost.kt b/app/src/main/java/com/android/pocketalchemy/PaNavHost.kt index c3a503e..734c3ca 100644 --- a/app/src/main/java/com/android/pocketalchemy/PaNavHost.kt +++ b/app/src/main/java/com/android/pocketalchemy/PaNavHost.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -11,9 +12,10 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.android.pocketalchemy.editrecipe.EditRecipeScreen import com.android.pocketalchemy.editrecipe.EditRecipeViewModel -import com.android.pocketalchemy.firebase.AuthRepository +import com.android.pocketalchemy.login.ErrOnLoginScreen import com.android.pocketalchemy.login.LoginScreen import com.android.pocketalchemy.recipelist.RecipeListScreen +import com.android.pocketalchemy.repository.AuthRepository /** * Launches navigation host. @@ -29,14 +31,19 @@ fun PaNavHost( LoginScreen { authRepository.signInAnonymousUser( onSuccess = { - navController.navigate("recipeListScreen") + navController.navigateAndPopAll("recipeListScreen") }, onFailure = { - // TODO: Navigate to error on login screen + navController.navigateAndPopAll("errOnLoginScreen") } ) } } + composable("errOnLoginScreen") { + ErrOnLoginScreen { + navController.navigateAndPopAll("loginScreen") + } + } composable("recipeListScreen") { RecipeListScreen( navController, @@ -66,4 +73,13 @@ fun PaNavHost( ) } } +} + +/** + * Extension function to navigate to dest and clear back stack + */ +fun NavController.navigateAndPopAll(route: String) { + this.navigate(route) { + popUpTo(0) + } } \ No newline at end of file diff --git a/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeViewModel.kt b/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeViewModel.kt index f7cfb8b..26c380f 100644 --- a/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeViewModel.kt +++ b/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeViewModel.kt @@ -6,11 +6,13 @@ import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import com.android.pocketalchemy.firebase.AuthRepository +import androidx.lifecycle.viewModelScope +import com.android.pocketalchemy.repository.AuthRepository import com.android.pocketalchemy.model.Recipe -import com.android.pocketalchemy.model.RecipeRepository +import com.android.pocketalchemy.repository.RecipeRepository import com.google.firebase.firestore.toObject import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject /** @@ -97,9 +99,10 @@ class EditRecipeViewModel @Inject constructor( */ fun saveRecipe() { // TODO: Check no required fields are empty!!! - recipeRepository.insertRecipe(this.recipeState.value) - - this.clearRecipeId() + viewModelScope.launch { + recipeRepository.insertRecipe(recipeState.value) + clearRecipeId() + } } companion object { diff --git a/app/src/main/java/com/android/pocketalchemy/login/ErrOnLoginScreen.kt b/app/src/main/java/com/android/pocketalchemy/login/ErrOnLoginScreen.kt new file mode 100644 index 0000000..51c9bc9 --- /dev/null +++ b/app/src/main/java/com/android/pocketalchemy/login/ErrOnLoginScreen.kt @@ -0,0 +1,62 @@ +package com.android.pocketalchemy.login + +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.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.android.pocketalchemy.R + +@Composable +fun ErrOnLoginScreen( + onNavigateToLoginScreen: () -> Unit +) { + Surface( + color = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + modifier = Modifier.fillMaxSize(1f) + ) { + + Box ( + modifier = Modifier.padding(16.dp) + ) { + Column( + modifier = Modifier.align(Alignment.Center) + .padding(16.dp) + ) { + Text( + text = stringResource(id = R.string.app_name), + modifier = Modifier.align(Alignment.CenterHorizontally), + style = MaterialTheme.typography.headlineLarge, + fontWeight = FontWeight.ExtraBold, + textAlign = TextAlign.Center, + ) + Text( + text = stringResource(R.string.err_on_login_msg), + modifier = Modifier.align(Alignment.CenterHorizontally) + .fillMaxWidth(.75f), + textAlign = TextAlign.Center + ) + TextButton( + onClick = onNavigateToLoginScreen, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + Text( + text = stringResource(R.string.back_to_login_label) + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeList.kt b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeList.kt index 7dc1e54..9140d0c 100644 --- a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeList.kt +++ b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeList.kt @@ -6,12 +6,20 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +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.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.android.pocketalchemy.R import com.android.pocketalchemy.model.Recipe /** @@ -26,16 +34,14 @@ fun RecipeList( ) { val recipes: State> = recipeListViewModel.recipes.collectAsState(initial = emptyList()) - - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(8.dp), - contentPadding = PaddingValues(8.dp) - ) { - if (recipes.value.isEmpty()) { - // TODO: Show help card when list is empty - } else { + + if (recipes.value.isNotEmpty()) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(8.dp), + contentPadding = PaddingValues(8.dp) + ) { items(recipes.value) { Box( modifier = Modifier.padding(vertical = 8.dp) @@ -44,5 +50,24 @@ fun RecipeList( } } } + } else { + Surface( + color = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + onClick = { onNavigateToEditRecipe(null) }, // navigates to new recipe + modifier = Modifier.fillMaxSize(1f) + ) { + Box { + Text( + text = stringResource(id = R.string.empty_recipe_list_prompt), + modifier = Modifier.align(Alignment.Center), + style = MaterialTheme.typography.headlineLarge, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + ) + } + } } + + } \ No newline at end of file diff --git a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListScreen.kt b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListScreen.kt index d6b64d5..6e0d24e 100644 --- a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListScreen.kt +++ b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListScreen.kt @@ -33,7 +33,7 @@ fun RecipeListScreen( PaTopAppBar(titleId = R.string.app_name) }, floatingActionButton = { - NewRecipeFAB() { onNavigateToEditRecipe(null) } + NewRecipeFAB { onNavigateToEditRecipe(null) } }, bottomBar = { PaNavBar(navController, isRecipeListSelected = true) diff --git a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListViewModel.kt b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListViewModel.kt index d4373b3..64addc8 100644 --- a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListViewModel.kt +++ b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListViewModel.kt @@ -2,7 +2,7 @@ package com.android.pocketalchemy.recipelist import androidx.lifecycle.ViewModel import com.android.pocketalchemy.model.Recipe -import com.android.pocketalchemy.model.RecipeRepository +import com.android.pocketalchemy.repository.RecipeRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -21,6 +21,7 @@ class RecipeListViewModel @Inject constructor( */ val recipes: Flow> get() = recipeRepository.getUserRecipeList() + } diff --git a/app/src/main/java/com/android/pocketalchemy/firebase/AuthRepository.kt b/app/src/main/java/com/android/pocketalchemy/repository/AuthRepository.kt similarity index 91% rename from app/src/main/java/com/android/pocketalchemy/firebase/AuthRepository.kt rename to app/src/main/java/com/android/pocketalchemy/repository/AuthRepository.kt index 795a796..07205ea 100644 --- a/app/src/main/java/com/android/pocketalchemy/firebase/AuthRepository.kt +++ b/app/src/main/java/com/android/pocketalchemy/repository/AuthRepository.kt @@ -1,4 +1,4 @@ -package com.android.pocketalchemy.firebase +package com.android.pocketalchemy.repository import android.util.Log import com.google.firebase.auth.FirebaseAuth @@ -49,10 +49,6 @@ class AuthRepository @Inject constructor( onFailure() } } - .addOnFailureListener { - // DEBUG USE - throw it - } } companion object { diff --git a/app/src/main/java/com/android/pocketalchemy/model/RecipeRepository.kt b/app/src/main/java/com/android/pocketalchemy/repository/RecipeRepository.kt similarity index 88% rename from app/src/main/java/com/android/pocketalchemy/model/RecipeRepository.kt rename to app/src/main/java/com/android/pocketalchemy/repository/RecipeRepository.kt index 499fe3a..618fa7b 100644 --- a/app/src/main/java/com/android/pocketalchemy/model/RecipeRepository.kt +++ b/app/src/main/java/com/android/pocketalchemy/repository/RecipeRepository.kt @@ -1,8 +1,8 @@ -package com.android.pocketalchemy.model +package com.android.pocketalchemy.repository import android.util.Log -import com.android.pocketalchemy.firebase.AuthRepository import com.android.pocketalchemy.firebase.FirestoreCollections.RECIPE_COLLECTION +import com.android.pocketalchemy.model.Recipe import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.dataObjects @@ -10,8 +10,6 @@ import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.flow.Flow import javax.inject.Inject -private const val TAG = "RecipeRepository" - /** * Repository for accessing recipe collection. * @param firestore firestore instance @@ -29,8 +27,8 @@ class RecipeRepository @Inject constructor( fun getUserRecipeList(): Flow> { val user = authRepository.getUser() return firestore.collection(RECIPE_COLLECTION) - .whereEqualTo(Recipe.USER_ID_FIELD, user.uid) - .dataObjects() + .whereEqualTo(Recipe.USER_ID_FIELD, user.uid) + .dataObjects() } @@ -66,4 +64,8 @@ class RecipeRepository @Inject constructor( } } } + + companion object { + private const val TAG = "RecipeRepository" + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4fd7eaa..d3c9067 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,7 +5,12 @@ Edit Recipe // Login screen labels. - continue as guest + continue as guest\ + Sorry, we could not authenticate your profile right now … + go back to login screen + + // Recipe list screen labels. + Create a new recipe to get started. // Edit recipe screen labels. Title