diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0a39b1b..7b0af17 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -93,6 +93,7 @@ dependencies { implementation("com.google.firebase:firebase-auth") ksp("com.google.dagger:dagger-compiler:$dagger_version") ksp("com.google.dagger:hilt-compiler:$dagger_version") + ksp("androidx.hilt:hilt-compiler:1.1.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/java/com/android/pocketalchemy/MainActivity.kt b/app/src/main/java/com/android/pocketalchemy/MainActivity.kt index 1f917b8..9c6cc68 100644 --- a/app/src/main/java/com/android/pocketalchemy/MainActivity.kt +++ b/app/src/main/java/com/android/pocketalchemy/MainActivity.kt @@ -3,14 +3,17 @@ package com.android.pocketalchemy import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.viewModels import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.android.pocketalchemy.editrecipe.EditRecipeScreen +import com.android.pocketalchemy.editrecipe.EditRecipeViewModel import com.android.pocketalchemy.firebase.AuthRepository import com.android.pocketalchemy.recipelist.RecipeListScreen import com.android.pocketalchemy.ui.theme.PocketAlchemyTheme import dagger.hilt.android.AndroidEntryPoint +import dagger.hilt.android.lifecycle.withCreationCallback import javax.inject.Inject private const val TAG = "MainActivity" @@ -36,7 +39,18 @@ class MainActivity : ComponentActivity() { } ) } - composable("CreateNewRecipe") { EditRecipeScreen() } + composable("CreateNewRecipe") { + val editRecipeViewModel by viewModels( + extrasProducer = { + defaultViewModelCreationExtras + .withCreationCallback { factory -> + factory.create(null) + } + } + ) + + EditRecipeScreen(editRecipeViewModel = editRecipeViewModel) + } } } } diff --git a/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeScreen.kt b/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeScreen.kt index 03b3b25..3ff83b6 100644 --- a/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeScreen.kt +++ b/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeScreen.kt @@ -14,14 +14,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.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import com.android.pocketalchemy.R import com.android.pocketalchemy.model.Recipe import com.android.pocketalchemy.ui.common.PaNavBar @@ -34,14 +33,13 @@ private const val TITLE_ROW_HEIGHT = 150 @Composable fun EditRecipeScreen( - recipeDocumentId: String? = null, - editRecipeViewModel: EditRecipeViewModel = hiltViewModel() + editRecipeViewModel: EditRecipeViewModel ) { - val appBarTitle = if (recipeDocumentId == null) { + val appBarTitle = if (editRecipeViewModel.recipeId == null) { R.string.create_new_recipe_title } else { R.string.app_name } - var recipe = remember { mutableStateOf(editRecipeViewModel.getRecipeObject(recipeDocumentId)) } + var recipe: State = editRecipeViewModel.getRecipe().collectAsState() Scaffold( contentColor = MaterialTheme.colorScheme.onBackground, @@ -64,7 +62,7 @@ fun EditRecipeScreen( //////////////////////////// Column( modifier = Modifier - .fillMaxWidth(.6f) + .fillMaxWidth(.75f) .height(TITLE_ROW_HEIGHT.dp) ) { // Title @@ -72,7 +70,9 @@ fun EditRecipeScreen( readOnly = false, value = recipe.value.title ?: "", onValueChange = { - recipe.value = recipe.value.copy(title = it) + editRecipeViewModel.updateRecipe { oldRecipe -> + oldRecipe.copy(title = it) + } }, label = { Text( @@ -80,7 +80,8 @@ fun EditRecipeScreen( style = MaterialTheme.typography.headlineLarge ) }, - textStyle = MaterialTheme.typography.headlineLarge + textStyle = MaterialTheme.typography.headlineMedium, + maxLines = 1, ) // Subtitle OutlinedTextField( 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 9aaf1c7..b7e18a0 100644 --- a/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeViewModel.kt +++ b/app/src/main/java/com/android/pocketalchemy/editrecipe/EditRecipeViewModel.kt @@ -5,36 +5,62 @@ import androidx.lifecycle.ViewModel import com.android.pocketalchemy.model.Recipe import com.android.pocketalchemy.model.RecipeRepository import com.google.firebase.firestore.toObject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update -@HiltViewModel -class EditRecipeViewModel @Inject constructor( +@HiltViewModel(assistedFactory = EditRecipeViewModel.EditRecipeViewModelFactory::class) +class EditRecipeViewModel @AssistedInject constructor( + @Assisted var recipeId: String?, private val recipeRepository: RecipeRepository ) : ViewModel() { + private var _recipe: MutableStateFlow - fun getRecipeObject(recipeDocumentId: String?): Recipe { - val recipeDoc = recipeRepository.getRecipe(recipeDocumentId) + init { + val recipeDoc = recipeRepository.getRecipe(recipeId) - var recipeObject = Recipe( - recipeDoc.id, + if (recipeId == null) { + // If creating a new document update id + recipeId = recipeDoc.id + } + + _recipe = MutableStateFlow( + Recipe( + recipeId = recipeId.toString() + ) ) recipeDoc.get() .addOnSuccessListener { snapshot -> Log.d(TAG, "Successfully retrieved recipe snapshot... ") snapshot.toObject()?.let { - recipeObject = it + _recipe.value = it } } .addOnFailureListener { - Log.w(TAG, "Cannot get recipe with id: $recipeDocumentId, ex: $it") + Log.w(TAG, "Cannot get recipe with id: $recipeId, ex: $it") } + } - return recipeObject + fun getRecipe(): StateFlow { + return _recipe.asStateFlow() } + fun updateRecipe(onUpdate: (Recipe) -> (Recipe)) { + _recipe.update { oldRecipe -> + onUpdate(oldRecipe) + } + } + @AssistedFactory + interface EditRecipeViewModelFactory { + fun create(recipeId: String?): EditRecipeViewModel + } companion object { private const val TAG = "EditRecipeViewModel"