diff --git a/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientCategory.kt b/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientCategory.kt index 724c58d..2079629 100644 --- a/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientCategory.kt +++ b/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientCategory.kt @@ -14,8 +14,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -25,7 +27,6 @@ import androidx.compose.ui.window.Popup import androidx.hilt.navigation.compose.hiltViewModel import com.android.pocketalchemy.R import com.android.pocketalchemy.model.IngredientCategory -import com.android.pocketalchemy.model.displayLabelRes import com.android.pocketalchemy.ui.common.PaTopAppBar /** @@ -55,28 +56,29 @@ fun SelectIngredientCategory( .fillMaxSize(1f) .padding(scaffoldPadding) ) { - val selectedCategory by selectIngredientViewModel.selectedCategory.collectAsState() + var showSelectIngredient by remember { mutableStateOf(false) } LazyColumn( modifier = Modifier.padding(8.dp), ) { items(IngredientCategory.entries) { - IngredientCategoryCard(category = it) { categoryName -> - selectIngredientViewModel.updateCategory(categoryName) + IngredientCategoryCard(category = it) { category -> + selectIngredientViewModel.updateCategory(category) + showSelectIngredient = true } } } - if (selectedCategory != null) { + if (showSelectIngredient) { SelectIngredient(selectIngredientViewModel) { - selectIngredientViewModel.updateCategory(null) + showSelectIngredient = false } } BackHandler( - enabled = (selectedCategory != null) + enabled = (showSelectIngredient) ) { - selectIngredientViewModel.updateCategory(null) + showSelectIngredient = false } } } @@ -93,10 +95,10 @@ fun SelectIngredientCategory( @Composable fun IngredientCategoryCard( category: IngredientCategory, - onClickCategoryCard: (String) -> Unit, + onClickCategoryCard: (IngredientCategory) -> Unit, ) { Card( - onClick = { onClickCategoryCard(category.categoryName) }, + onClick = { onClickCategoryCard(category) }, elevation = CardDefaults.cardElevation(4.dp), modifier = Modifier .fillMaxWidth(1f) @@ -109,7 +111,7 @@ fun IngredientCategoryCard( .padding(8.dp) ) { Text( - text = stringResource(id = category.displayLabelRes()), + text = stringResource(id = category.labelResId), modifier = Modifier .padding(8.dp) .fillMaxWidth(1f), diff --git a/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientViewModel.kt b/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientViewModel.kt index ad995a2..703f9ae 100644 --- a/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientViewModel.kt +++ b/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectIngredientViewModel.kt @@ -6,8 +6,9 @@ import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData -import com.android.pocketalchemy.model.ALL_CATEGORIES_NAME +import com.android.pocketalchemy.firebase.FirestoreIngredientCategory import com.android.pocketalchemy.model.Ingredient +import com.android.pocketalchemy.model.IngredientCategory import com.android.pocketalchemy.paging.IngredientPagingSource import com.android.pocketalchemy.paging.PageSizes.INGREDIENT_PAGE_SIZE import com.android.pocketalchemy.paging.PageSizes.INGREDIENT_PREFETCH_DISTANCE @@ -30,6 +31,7 @@ class SelectIngredientViewModel @Inject constructor( private val ingredientRepository: IngredientRepository, ): ViewModel() { private val defaultDispatcher = Dispatchers.Default + private var _selectedCategory: IngredientCategory = IngredientCategory.ALL private val defaultQuery get() = ingredientRepository.getIngredientCollectionRef() @@ -45,12 +47,6 @@ class SelectIngredientViewModel @Inject constructor( val ingredientPagingState: StateFlow>> get () = _ingredientPagingState.asStateFlow() - private val _selectedCategory: MutableStateFlow - = MutableStateFlow(null) - - val selectedCategory: StateFlow - get() = _selectedCategory.asStateFlow() - /** * Updates paging flow with new query * @param query new query for paging data @@ -81,19 +77,16 @@ class SelectIngredientViewModel @Inject constructor( viewModelScope.launch( defaultDispatcher ) { - val categoryName = selectedCategory.value + val category = _selectedCategory var query = defaultQuery + val firestoreCategory = FirestoreIngredientCategory.getCategoryName(category) // set category name - when (categoryName) { - ALL_CATEGORIES_NAME, - null -> {} // no specific category selected - else -> { - query = query.whereEqualTo( - Ingredient.CATEGORY_KEY, - categoryName - ) - } + if (firestoreCategory != null) { + query = query.whereEqualTo( + Ingredient.CATEGORY_KEY, + firestoreCategory + ) } keyword?.let { keyword -> @@ -111,12 +104,10 @@ class SelectIngredientViewModel @Inject constructor( /** * Updates selected category and sets query - * @param categoryName + * @param category */ - fun updateCategory(categoryName: String?) { - _selectedCategory.update { - categoryName - } + fun updateCategory(category: IngredientCategory) { + _selectedCategory = category setQuery() } } diff --git a/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectQuantityDialog.kt b/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectQuantityDialog.kt index 32ea3f6..42a53ce 100644 --- a/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectQuantityDialog.kt +++ b/app/src/main/java/com/android/pocketalchemy/editrecipe/selectingredient/SelectQuantityDialog.kt @@ -1,13 +1,40 @@ package com.android.pocketalchemy.editrecipe.selectingredient import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import com.android.pocketalchemy.R import com.android.pocketalchemy.model.Ingredient +import com.android.pocketalchemy.model.MeasureUnitDefaults /** @@ -25,13 +52,129 @@ fun SelectIngredientQuantityDialog( onAddIngredient: (Ingredient, Float) -> Unit, ) { Dialog(onDismissRequest = onDismiss) { - Box(modifier = Modifier.fillMaxSize(.5f)) { - Text( - text = ingredient.fancyDescription, - modifier = Modifier.align(Alignment.Center) - ) + var surfaceSize by remember { mutableStateOf(IntSize.Zero) } + + Surface( + modifier = Modifier + .fillMaxWidth(0.75f) + .fillMaxHeight(0.4f) + .onGloballyPositioned { + surfaceSize = it.size + }, + color = MaterialTheme.colorScheme.primaryContainer, + ) { + + Box( + modifier = Modifier + .fillMaxSize(1f) + .padding(8.dp) + ) { + var quantity by remember { mutableFloatStateOf(0.0f) } + var unitMenuExpanded by remember { mutableStateOf(false) } + var unit by remember { mutableStateOf(MeasureUnitDefaults.defaultUnit) } + var buttonSize by remember { mutableStateOf(IntSize.Zero) } + + Text( + text = ingredient.fancyDescription, + modifier = Modifier.align(Alignment.TopCenter), + textAlign = TextAlign.Center + ) - /* TODO: Add quantity selection UI */ + Column( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth(1f) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + QuantityField( + quantity = quantity, + onValueChange = { + val newQuantity = it.toFloatOrNull() + if (newQuantity != null) { + quantity = newQuantity + } + }, + ) + + Button( + onClick = { unitMenuExpanded = !unitMenuExpanded }, + modifier = Modifier + .fillMaxWidth(0.8f) + .padding(8.dp) + .onGloballyPositioned { + buttonSize = it.size + } + ) { + Text( + text = stringResource(id = unit.labelResId), + style = MaterialTheme.typography.bodyLarge, + maxLines = 1 + ) + + //val unitMenuOffset = (100) + //Log.d("Test", "$surfaceSize") + + DropdownMenu( + expanded = unitMenuExpanded, + onDismissRequest = { unitMenuExpanded = false }, + //offset = DpOffset(x = unitMenuOffset.dp, y=0.dp) + ) { + DropdownMenuItem( + text = { /*TODO*/ }, + onClick = { /*TODO*/ }, + ) + } + } + } + } } + } } + +@Composable +private fun QuantityField( + quantity: Float, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, +) { + TextField( + value = quantity.toString(), + onValueChange = { onValueChange(it) }, + modifier = modifier.fillMaxWidth(0.5f), + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Decimal + ), + singleLine = true, + label = { + Text( + stringResource(id = R.string.quantity_field_label), + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + textAlign = TextAlign.Center + ) + }, + textStyle = MaterialTheme.typography.bodyLarge + ) +} + +@Preview +@Composable +private fun PreviewDialog( + ingredient: Ingredient = Ingredient(description = "Preview Ingredient Description"), + onDismiss: () -> Unit = {}, + onAddIngredient: (Ingredient, Float) -> Unit = { _: Ingredient, _: Float -> }, +) { + Surface( + modifier = Modifier + .height(750.dp) + .width(300.dp) + ) { + SelectIngredientQuantityDialog( + ingredient = ingredient, + onDismiss = onDismiss, + onAddIngredient = onAddIngredient + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/pocketalchemy/firebase/FirestoreIngredientCategory.kt b/app/src/main/java/com/android/pocketalchemy/firebase/FirestoreIngredientCategory.kt new file mode 100644 index 0000000..46e2fee --- /dev/null +++ b/app/src/main/java/com/android/pocketalchemy/firebase/FirestoreIngredientCategory.kt @@ -0,0 +1,73 @@ +package com.android.pocketalchemy.firebase + +import com.android.pocketalchemy.model.IngredientCategory +import com.android.pocketalchemy.model.IngredientCategory.ALL +import com.android.pocketalchemy.model.IngredientCategory.BAKED_GOODS +import com.android.pocketalchemy.model.IngredientCategory.BEEF +import com.android.pocketalchemy.model.IngredientCategory.BEVERAGES +import com.android.pocketalchemy.model.IngredientCategory.CEREAL +import com.android.pocketalchemy.model.IngredientCategory.DAIRY_N_EGGS +import com.android.pocketalchemy.model.IngredientCategory.FATS_N_OILS +import com.android.pocketalchemy.model.IngredientCategory.FISH_N_SHELLFISH +import com.android.pocketalchemy.model.IngredientCategory.FRUITS +import com.android.pocketalchemy.model.IngredientCategory.GRAINS_N_PASTA +import com.android.pocketalchemy.model.IngredientCategory.LAMB_N_GAME_PRODUCTS +import com.android.pocketalchemy.model.IngredientCategory.LEGUMES +import com.android.pocketalchemy.model.IngredientCategory.NUTS_N_SEEDS +import com.android.pocketalchemy.model.IngredientCategory.PORK +import com.android.pocketalchemy.model.IngredientCategory.POULTRY +import com.android.pocketalchemy.model.IngredientCategory.SAUSAGES_N_LUNCH_MEATS +import com.android.pocketalchemy.model.IngredientCategory.SNACKS +import com.android.pocketalchemy.model.IngredientCategory.SOUPS_N_SAUCES +import com.android.pocketalchemy.model.IngredientCategory.SPICES_N_HERBS +import com.android.pocketalchemy.model.IngredientCategory.SWEETS +import com.android.pocketalchemy.model.IngredientCategory.VEGETABLES + +object FirestoreIngredientCategory { + private const val BAKED_CATEGORY_NAME = "Baked Products" + private const val BEEF_CATEGORY_NAME = "Beef Products" + private const val BEVERAGES_CATEGORY_NAME = "Beverages" + private const val CEREAL_CATEGORY_NAME = "Breakfast Cereals" + private const val DAIRY_N_EGGS_CATEGORY_NAME = "Dairy and Egg Products" + private const val FATS_N_OILS_CATEGORY_NAME = "Fats and Oils" + private const val FISH_N_SHELLFISH_CATEGORY_NAME = "Finfish and Shellfish Products" + private const val FRUITS_CATEGORY_NAME = "Fruits and Fruit Juices" + private const val GRAINS_AND_PASTA_CATEGORY_NAME = "Cereal Grains and Pasta" + private const val LAMB_N_GAME_CATEGORY_NAME = "Lamb, Veal, and Game Products" + private const val LEGUME_CATEGORY_NAME = "Legumes and Legume Products" + private const val NUTS_N_SEEDS_CATEGORY_NAME = "Nut and Seed Products" + private const val POULTRY_CATEGORY_NAME = "Poultry Products" + private const val PORK_CATEGORY_NAME = "Pork Products" + private const val SAUSAGES_N_LUNCH_MEATS_CATEGORY_NAME = "Sausages and Luncheon Meats" + private const val SNACKS_CATEGORY_NAME = "Snacks" + private const val SOUPS_N_SAUCES_CATEGORY_NAME = "Soups, Sauces, and Gravies" + private const val SPICES_N_HERBS_CATEGORY_NAME = "Spices and Herbs" + private const val SWEETS_CATEGORY_NAME = "Sweets" + private const val VEGETABLES_CATEGORY_NAME = "Vegetables and Vegetable Products" + + fun getCategoryName(category: IngredientCategory): String? { + return when(category) { + ALL -> null + BAKED_GOODS -> BAKED_CATEGORY_NAME + BEEF -> BEEF_CATEGORY_NAME + BEVERAGES -> BEVERAGES_CATEGORY_NAME + CEREAL -> CEREAL_CATEGORY_NAME + DAIRY_N_EGGS -> DAIRY_N_EGGS_CATEGORY_NAME + FATS_N_OILS -> FATS_N_OILS_CATEGORY_NAME + FISH_N_SHELLFISH -> FISH_N_SHELLFISH_CATEGORY_NAME + FRUITS -> FRUITS_CATEGORY_NAME + GRAINS_N_PASTA -> GRAINS_AND_PASTA_CATEGORY_NAME + LAMB_N_GAME_PRODUCTS -> LAMB_N_GAME_CATEGORY_NAME + LEGUMES -> LEGUME_CATEGORY_NAME + NUTS_N_SEEDS -> NUTS_N_SEEDS_CATEGORY_NAME + POULTRY -> POULTRY_CATEGORY_NAME + PORK -> PORK_CATEGORY_NAME + SAUSAGES_N_LUNCH_MEATS -> SAUSAGES_N_LUNCH_MEATS_CATEGORY_NAME + SNACKS -> SNACKS_CATEGORY_NAME + SOUPS_N_SAUCES -> SOUPS_N_SAUCES_CATEGORY_NAME + SPICES_N_HERBS -> SPICES_N_HERBS_CATEGORY_NAME + SWEETS -> SWEETS_CATEGORY_NAME + VEGETABLES -> VEGETABLES_CATEGORY_NAME + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/pocketalchemy/model/IngredientCategory.kt b/app/src/main/java/com/android/pocketalchemy/model/IngredientCategory.kt index f3adf18..8f9f7dc 100644 --- a/app/src/main/java/com/android/pocketalchemy/model/IngredientCategory.kt +++ b/app/src/main/java/com/android/pocketalchemy/model/IngredientCategory.kt @@ -1,99 +1,27 @@ package com.android.pocketalchemy.model import com.android.pocketalchemy.R -import com.android.pocketalchemy.model.IngredientCategory.ALL -import com.android.pocketalchemy.model.IngredientCategory.BAKED_GOODS -import com.android.pocketalchemy.model.IngredientCategory.BEEF -import com.android.pocketalchemy.model.IngredientCategory.BEVERAGES -import com.android.pocketalchemy.model.IngredientCategory.CEREAL -import com.android.pocketalchemy.model.IngredientCategory.DAIRY_N_EGGS -import com.android.pocketalchemy.model.IngredientCategory.FATS_N_OILS -import com.android.pocketalchemy.model.IngredientCategory.FISH_N_SHELLFISH -import com.android.pocketalchemy.model.IngredientCategory.FRUITS -import com.android.pocketalchemy.model.IngredientCategory.GRAINS_N_PASTA -import com.android.pocketalchemy.model.IngredientCategory.LAMB_N_GAME_PRODUCTS -import com.android.pocketalchemy.model.IngredientCategory.LEGUMES -import com.android.pocketalchemy.model.IngredientCategory.NUTS_N_SEEDS -import com.android.pocketalchemy.model.IngredientCategory.PORK -import com.android.pocketalchemy.model.IngredientCategory.POULTRY -import com.android.pocketalchemy.model.IngredientCategory.SAUSAGES_N_LUNCH_MEATS -import com.android.pocketalchemy.model.IngredientCategory.SNACKS -import com.android.pocketalchemy.model.IngredientCategory.SOUPS_N_SAUCES -import com.android.pocketalchemy.model.IngredientCategory.SPICES_N_HERBS -import com.android.pocketalchemy.model.IngredientCategory.SWEETS -import com.android.pocketalchemy.model.IngredientCategory.VEGETABLES -const val ALL_CATEGORIES_NAME = "All" -private const val BAKED_CATEGORY_NAME = "Baked Products" -private const val BEEF_CATEGORY_NAME = "Beef Products" -private const val BEVERAGES_CATEGORY_NAME = "Beverages" -private const val CEREAL_CATEGORY_NAME = "Breakfast Cereals" -private const val DAIRY_N_EGGS_CATEGORY_NAME = "Dairy and Egg Products" -private const val FATS_N_OILS_CATEGORY_NAME = "Fats and Oils" -private const val FISH_N_SHELLFISH_CATEGORY_NAME = "Finfish and Shellfish Products" -private const val FRUITS_CATEGORY_NAME = "Fruits and Fruit Juices" -private const val GRAINS_AND_PASTA_CATEGORY_NAME = "Cereal Grains and Pasta" -private const val LAMB_N_GAME_CATEGORY_NAME = "Lamb, Veal, and Game Products" -private const val LEGUME_CATEGORY_NAME = "Legumes and Legume Products" -private const val NUTS_N_SEEDS_CATEGORY_NAME = "Nut and Seed Products" -private const val POULTRY_CATEGORY_NAME = "Poultry Products" -private const val PORK_CATEGORY_NAME = "Pork Products" -private const val SAUSAGES_N_LUNCH_MEATS_CATEGORY_NAME = "Sausages and Luncheon Meats" -private const val SNACKS_CATEGORY_NAME = "Snacks" -private const val SOUPS_N_SAUCES_CATEGORY_NAME = "Soups, Sauces, and Gravies" -private const val SPICES_N_HERBS_CATEGORY_NAME = "Spices and Herbs" -private const val SWEETS_CATEGORY_NAME = "Sweets" -private const val VEGETABLES_CATEGORY_NAME = "Vegetables and Vegetable Products" - -enum class IngredientCategory(val categoryName: String) { - ALL(ALL_CATEGORIES_NAME), - BAKED_GOODS(BAKED_CATEGORY_NAME), - BEEF(BEEF_CATEGORY_NAME), - BEVERAGES(BEVERAGES_CATEGORY_NAME), - CEREAL(CEREAL_CATEGORY_NAME), - DAIRY_N_EGGS(DAIRY_N_EGGS_CATEGORY_NAME), - FATS_N_OILS(FATS_N_OILS_CATEGORY_NAME), - FISH_N_SHELLFISH(FISH_N_SHELLFISH_CATEGORY_NAME), - FRUITS(FRUITS_CATEGORY_NAME), - GRAINS_N_PASTA(GRAINS_AND_PASTA_CATEGORY_NAME), - LAMB_N_GAME_PRODUCTS(LAMB_N_GAME_CATEGORY_NAME), - LEGUMES(LEGUME_CATEGORY_NAME), - NUTS_N_SEEDS(NUTS_N_SEEDS_CATEGORY_NAME), - POULTRY(POULTRY_CATEGORY_NAME), - PORK(PORK_CATEGORY_NAME), - SAUSAGES_N_LUNCH_MEATS(SAUSAGES_N_LUNCH_MEATS_CATEGORY_NAME), - SNACKS(SNACKS_CATEGORY_NAME), - SOUPS_N_SAUCES(SOUPS_N_SAUCES_CATEGORY_NAME), - SPICES_N_HERBS(SPICES_N_HERBS_CATEGORY_NAME), - SWEETS(SWEETS_CATEGORY_NAME), - VEGETABLES(VEGETABLES_CATEGORY_NAME); -} - -/** - * Gets string resource ID for ingredient category label - */ -fun IngredientCategory.displayLabelRes(): Int { - return when(this) { - ALL -> R.string.all_categories_label - BAKED_GOODS -> R.string.baked_goods_label - BEEF -> R.string.beef_label - BEVERAGES -> R.string.beverages_label - CEREAL -> R.string.cereal_label - DAIRY_N_EGGS -> R.string.dairy_n_eggs_label - FATS_N_OILS -> R.string.fats_n_oils_label - FISH_N_SHELLFISH -> R.string.fish_n_shellfish_label - FRUITS -> R.string.fruits_label - GRAINS_N_PASTA -> R.string.grains_n_pasta_label - LAMB_N_GAME_PRODUCTS -> R.string.lamb_n_game_label - LEGUMES -> R.string.legumes_label - NUTS_N_SEEDS -> R.string.nuts_and_seeds_label - POULTRY -> R.string.poultry_label - PORK -> R.string.pork_label - SAUSAGES_N_LUNCH_MEATS -> R.string.sausage_n_lunch_meats_label - SNACKS -> R.string.snacks_label - SOUPS_N_SAUCES -> R.string.soups_n_sauces_label - SPICES_N_HERBS -> R.string.spices_n_herbs_label - SWEETS -> R.string.sweets_label - VEGETABLES -> R.string.vegetables_label - } +enum class IngredientCategory(val labelResId: Int) { + ALL(R.string.all_categories_label), + BAKED_GOODS(R.string.baked_goods_label), + BEEF(R.string.beef_label), + BEVERAGES(R.string.beverages_label), + CEREAL(R.string.cereal_label), + DAIRY_N_EGGS(R.string.dairy_n_eggs_label), + FATS_N_OILS(R.string.fats_n_oils_label), + FISH_N_SHELLFISH(R.string.fish_n_shellfish_label), + FRUITS(R.string.fruits_label), + GRAINS_N_PASTA(R.string.grains_n_pasta_label), + LAMB_N_GAME_PRODUCTS(R.string.lamb_n_game_label), + LEGUMES(R.string.legumes_label), + NUTS_N_SEEDS(R.string.nuts_and_seeds_label), + POULTRY(R.string.poultry_label), + PORK(R.string.pork_label), + SAUSAGES_N_LUNCH_MEATS(R.string.sausage_n_lunch_meats_label), + SNACKS(R.string.snacks_label), + SOUPS_N_SAUCES(R.string.soups_n_sauces_label), + SPICES_N_HERBS(R.string.spices_n_herbs_label), + SWEETS(R.string.sweets_label), + VEGETABLES(R.string.vegetables_label); } \ No newline at end of file diff --git a/app/src/main/java/com/android/pocketalchemy/model/MeasureUnit.kt b/app/src/main/java/com/android/pocketalchemy/model/MeasureUnit.kt new file mode 100644 index 0000000..a8c9cb4 --- /dev/null +++ b/app/src/main/java/com/android/pocketalchemy/model/MeasureUnit.kt @@ -0,0 +1,13 @@ +package com.android.pocketalchemy.model + +import com.android.pocketalchemy.R + +enum class MeasureUnit(val labelResId: Int) { + Kilograms(R.string.kilogram_unit_label), + Grams(R.string.gram_unit_label), + Milligrams(R.string.milligram_unit_label), +} + +object MeasureUnitDefaults { + val defaultUnit = MeasureUnit.Grams +} \ 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 fbc164d..52aca79 100644 --- a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeList.kt +++ b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeList.kt @@ -25,10 +25,11 @@ fun RecipeList( onNavigateToEditRecipe: (String?) -> Unit, recipeListViewModel: RecipeListViewModel = hiltViewModel() ) { - val recipeList: List - by recipeListViewModel.recipeList.collectAsState() + val recipeListUiState by recipeListViewModel.recipeListUiState.collectAsState() + val isLoading = recipeListUiState.isLoading + val recipeList by recipeListUiState.recipeList.collectAsState(initial = emptyList()) - if (!recipeListViewModel.isLoading) { + if (!isLoading) { RecipeListColumn(recipeList, onNavigateToEditRecipe) } else { LoadingIndicator() diff --git a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListUiState.kt b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListUiState.kt index e4d0080..3efce7f 100644 --- a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListUiState.kt +++ b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListUiState.kt @@ -1,6 +1,7 @@ package com.android.pocketalchemy.recipelist import com.android.pocketalchemy.model.Recipe +import kotlinx.coroutines.flow.Flow /** * Wrapper for recipe list UI state @@ -8,6 +9,6 @@ import com.android.pocketalchemy.model.Recipe * @param isLoading true if fetching recipe list data */ data class RecipeListUiState( - val recipeList: List, + val recipeList: Flow>, val isLoading: Boolean ) \ No newline at end of file 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 a9545ea..1c007b8 100644 --- a/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListViewModel.kt +++ b/app/src/main/java/com/android/pocketalchemy/recipelist/RecipeListViewModel.kt @@ -2,12 +2,13 @@ package com.android.pocketalchemy.recipelist import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.android.pocketalchemy.model.Recipe import com.android.pocketalchemy.repository.RecipeRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -20,29 +21,35 @@ class RecipeListViewModel @Inject constructor( private val recipeRepository: RecipeRepository, ) : ViewModel() { - private var _isLoading = false + // UI State + private val _recipeListUiState = MutableStateFlow( + RecipeListUiState( + isLoading = false, + recipeList = emptyFlow() + ) + ) - val isLoading - get() = _isLoading - - private var _recipeList: StateFlow> - = MutableStateFlow(emptyList()) - - val recipeList: StateFlow> - get() { - return _recipeList - } + // Public accessor for UI state + val recipeListUiState: StateFlow + get() = _recipeListUiState.asStateFlow() init { - _isLoading = true + setIsLoading(true) getRecipeList() + setIsLoading(false) + } + + private fun setIsLoading(value: Boolean) { + _recipeListUiState.update { + it.copy(isLoading = value) + } } private fun getRecipeList() { viewModelScope.launch { - _recipeList = recipeRepository.getUserRecipeList() - .stateIn(this) - _isLoading = false + _recipeListUiState.update { + it.copy(recipeList = recipeRepository.getUserRecipeList()) + } } } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2bf002c..22ffb7f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,6 +25,7 @@ // Select ingredient labels Back Add Ingredient + Quantity // Ingredient category labels All Categories @@ -62,6 +63,11 @@ Shopping Lists Ingredients + // MeasureUnit labels. + kilograms + grams + milligrams + // Content descriptions for icons. Image of pencil. Image of list.