From 7ebe89adfcd078b23e4750b0b1649772ca90cd9e Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Wed, 17 Nov 2021 20:44:14 +0300 Subject: [PATCH] Implement loading and saving full recipe info --- .../java/gq/kirmanak/mealie/data/MealieDb.kt | 3 +- .../mealie/data/recipes/RecipeRepo.kt | 2 +- .../mealie/data/recipes/db/RecipeDao.kt | 12 ++- .../mealie/data/recipes/db/RecipeStorage.kt | 6 +- .../data/recipes/db/RecipeStorageImpl.kt | 54 ++++++++++- .../recipes/db/{ => entity}/CategoryEntity.kt | 2 +- .../db/{ => entity}/CategoryRecipeEntity.kt | 4 +- .../data/recipes/db/entity/RecipeEntity.kt | 11 +++ .../db/entity/RecipeIngredientEntity.kt | 16 ++++ .../db/entity/RecipeInstructionEntity.kt | 12 +++ .../db/{ => entity}/RecipeSummaryEntity.kt | 10 +-- .../data/recipes/db/{ => entity}/TagEntity.kt | 2 +- .../db/{ => entity}/TagRecipeEntity.kt | 4 +- .../recipes/impl/RecipePagingSourceFactory.kt | 2 +- .../data/recipes/impl/RecipeRepoImpl.kt | 2 +- .../recipes/impl/RecipesRemoteMediator.kt | 2 +- .../data/recipes/network/RecipeDataSource.kt | 5 ++ .../recipes/network/RecipeDataSourceImpl.kt | 10 +++ .../data/recipes/network/RecipeService.kt | 8 ++ .../response/GetRecipeIngredientResponse.kt | 14 +++ .../response/GetRecipeInstructionResponse.kt | 10 +++ .../network/response/GetRecipeResponse.kt | 23 +++++ .../GetRecipeSummaryResponse.kt | 2 +- .../mealie/ui/recipes/RecipeViewHolder.kt | 2 +- .../mealie/ui/recipes/RecipeViewModel.kt | 2 +- .../mealie/ui/recipes/RecipesPagingAdapter.kt | 2 +- .../ui/recipes/info/RecipeInfoFragment.kt | 8 ++ .../main/res/layout/fragment_recipe_info.xml | 89 +++++++++++++++++++ .../res/layout/view_holder_ingredient.xml | 17 ++++ .../res/layout/view_holder_instruction.xml | 30 +++++++ app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 3 + .../mealie/data/recipes/RecipeImplTestData.kt | 6 +- .../data/recipes/db/RecipeStorageImplTest.kt | 16 ++-- .../recipes/impl/RecipesRemoteMediatorTest.kt | 2 +- 35 files changed, 355 insertions(+), 39 deletions(-) rename app/src/main/java/gq/kirmanak/mealie/data/recipes/db/{ => entity}/CategoryEntity.kt (88%) rename app/src/main/java/gq/kirmanak/mealie/data/recipes/db/{ => entity}/CategoryRecipeEntity.kt (90%) create mode 100644 app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeEntity.kt create mode 100644 app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeIngredientEntity.kt create mode 100644 app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeInstructionEntity.kt rename app/src/main/java/gq/kirmanak/mealie/data/recipes/db/{ => entity}/RecipeSummaryEntity.kt (61%) rename app/src/main/java/gq/kirmanak/mealie/data/recipes/db/{ => entity}/TagEntity.kt (87%) rename app/src/main/java/gq/kirmanak/mealie/data/recipes/db/{ => entity}/TagRecipeEntity.kt (89%) create mode 100644 app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeIngredientResponse.kt create mode 100644 app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeInstructionResponse.kt create mode 100644 app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeResponse.kt rename app/src/main/java/gq/kirmanak/mealie/data/recipes/network/{ => response}/GetRecipeSummaryResponse.kt (93%) create mode 100644 app/src/main/java/gq/kirmanak/mealie/ui/recipes/info/RecipeInfoFragment.kt create mode 100644 app/src/main/res/layout/fragment_recipe_info.xml create mode 100644 app/src/main/res/layout/view_holder_ingredient.xml create mode 100644 app/src/main/res/layout/view_holder_instruction.xml diff --git a/app/src/main/java/gq/kirmanak/mealie/data/MealieDb.kt b/app/src/main/java/gq/kirmanak/mealie/data/MealieDb.kt index 89f1369..37eea52 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/MealieDb.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/MealieDb.kt @@ -4,7 +4,8 @@ import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters import gq.kirmanak.mealie.data.impl.RoomTypeConverters -import gq.kirmanak.mealie.data.recipes.db.* +import gq.kirmanak.mealie.data.recipes.db.RecipeDao +import gq.kirmanak.mealie.data.recipes.db.entity.* import javax.inject.Singleton @Database( diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/RecipeRepo.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/RecipeRepo.kt index 508b612..215155f 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/RecipeRepo.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/RecipeRepo.kt @@ -1,7 +1,7 @@ package gq.kirmanak.mealie.data.recipes import androidx.paging.Pager -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity interface RecipeRepo { fun createPager(): Pager diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeDao.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeDao.kt index 4ae9c3f..e65aef1 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeDao.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeDao.kt @@ -5,6 +5,7 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import gq.kirmanak.mealie.data.recipes.db.entity.* @Dao interface RecipeDao { @@ -18,7 +19,7 @@ interface RecipeDao { fun queryRecipesByPages(): PagingSource @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertRecipe(recipeSummaryEntity: RecipeSummaryEntity): Long + suspend fun insertRecipe(recipeSummaryEntity: RecipeSummaryEntity) @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertTag(tagEntity: TagEntity): Long @@ -55,4 +56,13 @@ interface RecipeDao { @Query("SELECT * FROM tag_recipe") suspend fun queryAllTagRecipes(): List + + @Insert + suspend fun insertRecipe(recipe: RecipeEntity) + + @Insert + suspend fun insertRecipeInstructions(instructions: List) + + @Insert + suspend fun insertRecipeIngredients(ingredients: List) } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorage.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorage.kt index 2e76df1..6105d9d 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorage.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorage.kt @@ -1,7 +1,9 @@ package gq.kirmanak.mealie.data.recipes.db import androidx.paging.PagingSource -import gq.kirmanak.mealie.data.recipes.network.GetRecipeSummaryResponse +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeSummaryResponse interface RecipeStorage { suspend fun saveRecipes(recipes: List) @@ -11,4 +13,6 @@ interface RecipeStorage { suspend fun refreshAll(recipes: List) suspend fun clearAllLocalData() + + suspend fun saveRecipeInfo(recipe: GetRecipeResponse) } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImpl.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImpl.kt index f451d15..0e8413f 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImpl.kt @@ -3,7 +3,11 @@ package gq.kirmanak.mealie.data.recipes.db import androidx.paging.PagingSource import androidx.room.withTransaction import gq.kirmanak.mealie.data.MealieDb -import gq.kirmanak.mealie.data.recipes.network.GetRecipeSummaryResponse +import gq.kirmanak.mealie.data.recipes.db.entity.* +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeIngredientResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeInstructionResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeSummaryResponse import timber.log.Timber import javax.inject.Inject @@ -27,16 +31,20 @@ class RecipeStorageImpl @Inject constructor( val categoryRecipeEntities = mutableSetOf() for (recipe in recipes) { - val recipeId = recipeDao.insertRecipe(recipe.recipeEntity()) + val recipeSummaryEntity = recipe.recipeEntity() + recipeDao.insertRecipe(recipeSummaryEntity) for (tag in recipe.tags) { val tagId = getIdOrInsert(tagEntities, tag) - tagRecipeEntities += TagRecipeEntity(tagId, recipeId) + tagRecipeEntities += TagRecipeEntity(tagId, recipeSummaryEntity.remoteId) } for (category in recipe.recipeCategories) { val categoryId = getOrInsert(categoryEntities, category) - categoryRecipeEntities += CategoryRecipeEntity(categoryId, recipeId) + categoryRecipeEntities += CategoryRecipeEntity( + categoryId, + recipeSummaryEntity.remoteId + ) } } @@ -108,4 +116,42 @@ class RecipeStorageImpl @Inject constructor( recipeDao.removeAllTags() } } + + override suspend fun saveRecipeInfo(recipe: GetRecipeResponse) { + Timber.v("saveRecipeInfo() called with: recipe = $recipe") + db.withTransaction { + recipeDao.insertRecipe(recipe.toRecipeEntity()) + val ingredients = recipe.recipeIngredients.map { + it.toRecipeIngredientEntity(recipe.remoteId) + } + recipeDao.insertRecipeIngredients(ingredients) + val instructions = recipe.recipeInstructions.map { + it.toRecipeInstructionEntity(recipe.remoteId) + } + recipeDao.insertRecipeInstructions(instructions) + } + } + + private fun GetRecipeResponse.toRecipeEntity() = RecipeEntity( + remoteId = remoteId, + recipeYield = recipeYield + ) + + private fun GetRecipeIngredientResponse.toRecipeIngredientEntity(remoteId: Long) = + RecipeIngredientEntity( + recipeId = remoteId, + title = title, + note = note, + unit = unit, + food = food, + disableAmount = disableAmount, + quantity = quantity + ) + + private fun GetRecipeInstructionResponse.toRecipeInstructionEntity(remoteId: Long) = + RecipeInstructionEntity( + recipeId = remoteId, + title = title, + text = text + ) } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/CategoryEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/CategoryEntity.kt similarity index 88% rename from app/src/main/java/gq/kirmanak/mealie/data/recipes/db/CategoryEntity.kt rename to app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/CategoryEntity.kt index 204dc64..270ab18 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/CategoryEntity.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/CategoryEntity.kt @@ -1,4 +1,4 @@ -package gq.kirmanak.mealie.data.recipes.db +package gq.kirmanak.mealie.data.recipes.db.entity import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/CategoryRecipeEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/CategoryRecipeEntity.kt similarity index 90% rename from app/src/main/java/gq/kirmanak/mealie/data/recipes/db/CategoryRecipeEntity.kt rename to app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/CategoryRecipeEntity.kt index 0c5a402..14a9c2e 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/CategoryRecipeEntity.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/CategoryRecipeEntity.kt @@ -1,4 +1,4 @@ -package gq.kirmanak.mealie.data.recipes.db +package gq.kirmanak.mealie.data.recipes.db.entity import androidx.room.ColumnInfo import androidx.room.Entity @@ -17,7 +17,7 @@ import androidx.room.Index onUpdate = ForeignKey.CASCADE ), ForeignKey( entity = RecipeSummaryEntity::class, - parentColumns = ["local_id"], + parentColumns = ["remote_id"], childColumns = ["recipe_id"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeEntity.kt new file mode 100644 index 0000000..9a56321 --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeEntity.kt @@ -0,0 +1,11 @@ +package gq.kirmanak.mealie.data.recipes.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "recipe") +data class RecipeEntity( + @PrimaryKey @ColumnInfo(name = "remote_id") val remoteId: Long, + @ColumnInfo(name = "recipe_yield") val recipeYield: String, +) diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeIngredientEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeIngredientEntity.kt new file mode 100644 index 0000000..7a71b63 --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeIngredientEntity.kt @@ -0,0 +1,16 @@ +package gq.kirmanak.mealie.data.recipes.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "recipe_ingredient") +data class RecipeIngredientEntity( + @PrimaryKey @ColumnInfo(name = "recipe_id") val recipeId: Long, + @ColumnInfo(name = "title") val title: String, + @ColumnInfo(name = "note") val note: String, + @ColumnInfo(name = "unit") val unit: String, + @ColumnInfo(name = "food") val food: String, + @ColumnInfo(name = "disable_amount") val disableAmount: Boolean, + @ColumnInfo(name = "quantity") val quantity: Int, +) \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeInstructionEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeInstructionEntity.kt new file mode 100644 index 0000000..f50d56d --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeInstructionEntity.kt @@ -0,0 +1,12 @@ +package gq.kirmanak.mealie.data.recipes.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "recipe_instruction") +data class RecipeInstructionEntity( + @PrimaryKey @ColumnInfo(name = "recipe_id") val recipeId: Long, + @ColumnInfo(name = "title") val title: String, + @ColumnInfo(name = "text") val text: String, +) diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeSummaryEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeSummaryEntity.kt similarity index 61% rename from app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeSummaryEntity.kt rename to app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeSummaryEntity.kt index 9cc8891..c64a7ed 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/RecipeSummaryEntity.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/RecipeSummaryEntity.kt @@ -1,16 +1,14 @@ -package gq.kirmanak.mealie.data.recipes.db +package gq.kirmanak.mealie.data.recipes.db.entity import androidx.room.ColumnInfo import androidx.room.Entity -import androidx.room.Index import androidx.room.PrimaryKey import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime -@Entity(tableName = "recipe_summaries", indices = [Index(value = ["remote_id"], unique = true)]) +@Entity(tableName = "recipe_summaries") data class RecipeSummaryEntity( - @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "local_id") val localId: Long = 0, - @ColumnInfo(name = "remote_id") val remoteId: Long, + @PrimaryKey @ColumnInfo(name = "remote_id") val remoteId: Long, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "slug") val slug: String, @ColumnInfo(name = "image") val image: String, @@ -20,6 +18,6 @@ data class RecipeSummaryEntity( @ColumnInfo(name = "date_updated") val dateUpdated: LocalDateTime ) { override fun toString(): String { - return "RecipeEntity(localId=$localId, remoteId=$remoteId, name='$name')" + return "RecipeEntity(remoteId=$remoteId, name='$name')" } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/TagEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/TagEntity.kt similarity index 87% rename from app/src/main/java/gq/kirmanak/mealie/data/recipes/db/TagEntity.kt rename to app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/TagEntity.kt index 43617d6..61eb920 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/TagEntity.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/TagEntity.kt @@ -1,4 +1,4 @@ -package gq.kirmanak.mealie.data.recipes.db +package gq.kirmanak.mealie.data.recipes.db.entity import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/TagRecipeEntity.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/TagRecipeEntity.kt similarity index 89% rename from app/src/main/java/gq/kirmanak/mealie/data/recipes/db/TagRecipeEntity.kt rename to app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/TagRecipeEntity.kt index f5a292a..d69ecd7 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/TagRecipeEntity.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/db/entity/TagRecipeEntity.kt @@ -1,4 +1,4 @@ -package gq.kirmanak.mealie.data.recipes.db +package gq.kirmanak.mealie.data.recipes.db.entity import androidx.room.ColumnInfo import androidx.room.Entity @@ -15,7 +15,7 @@ import androidx.room.ForeignKey onUpdate = ForeignKey.CASCADE ), ForeignKey( entity = RecipeSummaryEntity::class, - parentColumns = ["local_id"], + parentColumns = ["remote_id"], childColumns = ["recipe_id"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipePagingSourceFactory.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipePagingSourceFactory.kt index 739cd45..86ede64 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipePagingSourceFactory.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipePagingSourceFactory.kt @@ -2,7 +2,7 @@ package gq.kirmanak.mealie.data.recipes.impl import androidx.paging.PagingSource import gq.kirmanak.mealie.data.recipes.db.RecipeStorage -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipeRepoImpl.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipeRepoImpl.kt index 9956f5f..7fda993 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipeRepoImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipeRepoImpl.kt @@ -5,7 +5,7 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import gq.kirmanak.mealie.data.recipes.RecipeRepo import gq.kirmanak.mealie.data.recipes.db.RecipeStorage -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import timber.log.Timber import javax.inject.Inject diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediator.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediator.kt index 4228fc7..eaeddcf 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediator.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediator.kt @@ -8,7 +8,7 @@ import androidx.paging.LoadType.REFRESH import androidx.paging.PagingState import androidx.paging.RemoteMediator import gq.kirmanak.mealie.data.recipes.db.RecipeStorage -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import gq.kirmanak.mealie.data.recipes.network.RecipeDataSource import kotlinx.coroutines.CancellationException import timber.log.Timber diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSource.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSource.kt index 90aeb78..a97bd83 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSource.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSource.kt @@ -1,5 +1,10 @@ package gq.kirmanak.mealie.data.recipes.network +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeSummaryResponse + interface RecipeDataSource { suspend fun requestRecipes(start: Int = 0, limit: Int = 9999): List + + suspend fun requestRecipeInfo(slug: String): GetRecipeResponse } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSourceImpl.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSourceImpl.kt index 5cea55d..cf5bf25 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSourceImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeDataSourceImpl.kt @@ -2,6 +2,8 @@ package gq.kirmanak.mealie.data.recipes.network import gq.kirmanak.mealie.data.auth.AuthRepo import gq.kirmanak.mealie.data.impl.RetrofitBuilder +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeSummaryResponse import kotlinx.serialization.ExperimentalSerializationApi import timber.log.Timber import javax.inject.Inject @@ -21,6 +23,14 @@ class RecipeDataSourceImpl @Inject constructor( return recipeSummary } + override suspend fun requestRecipeInfo(slug: String): GetRecipeResponse { + Timber.v("requestRecipeInfo() called with: slug = $slug") + val service: RecipeService = getRecipeService() + val recipeInfo = service.getRecipe(slug) + Timber.v("requestRecipeInfo() returned: $recipeInfo") + return recipeInfo + } + private suspend fun getRecipeService(): RecipeService { Timber.v("getRecipeService() called") val cachedService: RecipeService? = _recipeService diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeService.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeService.kt index 9628cf0..ed00fdf 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeService.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/RecipeService.kt @@ -1,6 +1,9 @@ package gq.kirmanak.mealie.data.recipes.network +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeResponse +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeSummaryResponse import retrofit2.http.GET +import retrofit2.http.Path import retrofit2.http.Query interface RecipeService { @@ -9,4 +12,9 @@ interface RecipeService { @Query("start") start: Int, @Query("limit") limit: Int ): List + + @GET("/api/recipes/:recipe_slug") + suspend fun getRecipe( + @Path("recipe_slug") recipeSlug: String + ): GetRecipeResponse } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeIngredientResponse.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeIngredientResponse.kt new file mode 100644 index 0000000..ca037d2 --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeIngredientResponse.kt @@ -0,0 +1,14 @@ +package gq.kirmanak.mealie.data.recipes.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetRecipeIngredientResponse( + @SerialName("title") val title: String = "", + @SerialName("note") val note: String = "", + @SerialName("unit") val unit: String = "", + @SerialName("food") val food: String = "", + @SerialName("disableAmount") val disableAmount: Boolean, + @SerialName("quantity") val quantity: Int, +) diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeInstructionResponse.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeInstructionResponse.kt new file mode 100644 index 0000000..372cc22 --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeInstructionResponse.kt @@ -0,0 +1,10 @@ +package gq.kirmanak.mealie.data.recipes.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetRecipeInstructionResponse( + @SerialName("title") val title: String = "", + @SerialName("text") val text: String, +) diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeResponse.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeResponse.kt new file mode 100644 index 0000000..9db0908 --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeResponse.kt @@ -0,0 +1,23 @@ +package gq.kirmanak.mealie.data.recipes.network.response + +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetRecipeResponse( + @SerialName("id") val remoteId: Long, + @SerialName("name") val name: String, + @SerialName("slug") val slug: String, + @SerialName("image") val image: String, + @SerialName("description") val description: String = "", + @SerialName("recipeCategory") val recipeCategories: List, + @SerialName("tags") val tags: List, + @SerialName("rating") val rating: Int?, + @SerialName("dateAdded") val dateAdded: LocalDate, + @SerialName("dateUpdated") val dateUpdated: LocalDateTime, + @SerialName("recipeYield") val recipeYield: String = "", + @SerialName("recipeIngredient") val recipeIngredients: List, + @SerialName("recipeInstructions") val recipeInstructions: List, +) diff --git a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/GetRecipeSummaryResponse.kt b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeSummaryResponse.kt similarity index 93% rename from app/src/main/java/gq/kirmanak/mealie/data/recipes/network/GetRecipeSummaryResponse.kt rename to app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeSummaryResponse.kt index acd61b7..c5c4875 100644 --- a/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/GetRecipeSummaryResponse.kt +++ b/app/src/main/java/gq/kirmanak/mealie/data/recipes/network/response/GetRecipeSummaryResponse.kt @@ -1,4 +1,4 @@ -package gq.kirmanak.mealie.data.recipes.network +package gq.kirmanak.mealie.data.recipes.network.response import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime diff --git a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewHolder.kt b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewHolder.kt index 2b84e23..e8d0343 100644 --- a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewHolder.kt +++ b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewHolder.kt @@ -2,7 +2,7 @@ package gq.kirmanak.mealie.ui.recipes import androidx.recyclerview.widget.RecyclerView import gq.kirmanak.mealie.R -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import gq.kirmanak.mealie.databinding.ViewHolderRecipeBinding class RecipeViewHolder( diff --git a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewModel.kt b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewModel.kt index 0e22a34..41afa39 100644 --- a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipeViewModel.kt @@ -6,7 +6,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import gq.kirmanak.mealie.data.recipes.RecipeImageLoader import gq.kirmanak.mealie.data.recipes.RecipeRepo -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipesPagingAdapter.kt b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipesPagingAdapter.kt index ab0f592..87873a7 100644 --- a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipesPagingAdapter.kt +++ b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/RecipesPagingAdapter.kt @@ -4,7 +4,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import gq.kirmanak.mealie.databinding.ViewHolderRecipeBinding import timber.log.Timber diff --git a/app/src/main/java/gq/kirmanak/mealie/ui/recipes/info/RecipeInfoFragment.kt b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/info/RecipeInfoFragment.kt new file mode 100644 index 0000000..c323e1f --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealie/ui/recipes/info/RecipeInfoFragment.kt @@ -0,0 +1,8 @@ +package gq.kirmanak.mealie.ui.recipes.info + +import androidx.fragment.app.Fragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class RecipeInfoFragment : Fragment() { +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recipe_info.xml b/app/src/main/res/layout/fragment_recipe_info.xml new file mode 100644 index 0000000..1676719 --- /dev/null +++ b/app/src/main/res/layout/fragment_recipe_info.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/view_holder_ingredient.xml b/app/src/main/res/layout/view_holder_ingredient.xml new file mode 100644 index 0000000..4b98619 --- /dev/null +++ b/app/src/main/res/layout/view_holder_ingredient.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_holder_instruction.xml b/app/src/main/res/layout/view_holder_instruction.xml new file mode 100644 index 0000000..c83507d --- /dev/null +++ b/app/src/main/res/layout/view_holder_instruction.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index ddea378..c3eabea 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,4 +3,5 @@ 8dp 360dp 180dp + @dimen/height_view_holder_recipe_image \ 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 ae4ee11..75a2662 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,4 +8,7 @@ @string/menu_main_toolbar_logout Logout Loading… + @string/content_description_view_holder_recipe_image + Ingredients + Instructions \ No newline at end of file diff --git a/app/src/test/java/gq/kirmanak/mealie/data/recipes/RecipeImplTestData.kt b/app/src/test/java/gq/kirmanak/mealie/data/recipes/RecipeImplTestData.kt index 20ada8e..8083d34 100644 --- a/app/src/test/java/gq/kirmanak/mealie/data/recipes/RecipeImplTestData.kt +++ b/app/src/test/java/gq/kirmanak/mealie/data/recipes/RecipeImplTestData.kt @@ -1,7 +1,7 @@ package gq.kirmanak.mealie.data.recipes -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity -import gq.kirmanak.mealie.data.recipes.network.GetRecipeSummaryResponse +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.network.response.GetRecipeSummaryResponse import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime import okhttp3.mockwebserver.MockResponse @@ -68,7 +68,6 @@ object RecipeImplTestData { """ val CAKE_RECIPE_ENTITY = RecipeSummaryEntity( - localId = 1, remoteId = 1, name = "Cake", slug = "cake", @@ -80,7 +79,6 @@ object RecipeImplTestData { ) val PORRIDGE_RECIPE_ENTITY = RecipeSummaryEntity( - localId = 2, remoteId = 2, name = "Porridge", slug = "porridge", diff --git a/app/src/test/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImplTest.kt b/app/src/test/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImplTest.kt index 74a0f74..2b7ed1a 100644 --- a/app/src/test/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImplTest.kt +++ b/app/src/test/java/gq/kirmanak/mealie/data/recipes/db/RecipeStorageImplTest.kt @@ -8,6 +8,10 @@ import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.CAKE_RECIPE_ENTITY import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.PORRIDGE_RECIPE_ENTITY import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.RECIPE_SUMMARY_CAKE import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.TEST_RECIPE_SUMMARIES +import gq.kirmanak.mealie.data.recipes.db.entity.CategoryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.CategoryRecipeEntity +import gq.kirmanak.mealie.data.recipes.db.entity.TagEntity +import gq.kirmanak.mealie.data.recipes.db.entity.TagRecipeEntity import kotlinx.coroutines.runBlocking import org.junit.Test import javax.inject.Inject @@ -79,9 +83,7 @@ class RecipeStorageImplTest : HiltRobolectricTest() { subject.saveRecipes(TEST_RECIPE_SUMMARIES) subject.refreshAll(listOf(RECIPE_SUMMARY_CAKE)) val actual = mealieDb.recipeDao().queryAllRecipes() - assertThat(actual).containsExactly( - CAKE_RECIPE_ENTITY.copy(localId = 3), - ) + assertThat(actual).containsExactly(CAKE_RECIPE_ENTITY) } @Test @@ -90,8 +92,8 @@ class RecipeStorageImplTest : HiltRobolectricTest() { subject.refreshAll(listOf(RECIPE_SUMMARY_CAKE)) val actual = mealieDb.recipeDao().queryAllCategoryRecipes() assertThat(actual).containsExactly( - CategoryRecipeEntity(categoryId = 1, recipeId = 3), - CategoryRecipeEntity(categoryId = 2, recipeId = 3), + CategoryRecipeEntity(categoryId = 1, recipeId = 1), + CategoryRecipeEntity(categoryId = 2, recipeId = 1), ) } @@ -101,8 +103,8 @@ class RecipeStorageImplTest : HiltRobolectricTest() { subject.refreshAll(listOf(RECIPE_SUMMARY_CAKE)) val actual = mealieDb.recipeDao().queryAllTagRecipes() assertThat(actual).containsExactly( - TagRecipeEntity(tagId = 1, recipeId = 3), - TagRecipeEntity(tagId = 2, recipeId = 3), + TagRecipeEntity(tagId = 1, recipeId = 1), + TagRecipeEntity(tagId = 2, recipeId = 1), ) } diff --git a/app/src/test/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediatorTest.kt b/app/src/test/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediatorTest.kt index 49f9a12..a6ed479 100644 --- a/app/src/test/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediatorTest.kt +++ b/app/src/test/java/gq/kirmanak/mealie/data/recipes/impl/RecipesRemoteMediatorTest.kt @@ -15,7 +15,7 @@ import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.PORRIDGE_RECIPE_ENTITY import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.TEST_RECIPE_ENTITIES import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.enqueueSuccessfulRecipeSummaryResponse import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.enqueueUnsuccessfulRecipeSummaryResponse -import gq.kirmanak.mealie.data.recipes.db.RecipeSummaryEntity +import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity import kotlinx.coroutines.runBlocking import org.junit.Before import org.junit.Test