From 9ab86e7be3f24663979c7de05be4ba04a89ff7b6 Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Wed, 14 Dec 2022 22:49:52 +0100 Subject: [PATCH] Initialize recipe removal feature --- .../data/network/MealieDataSourceWrapper.kt | 5 +++++ .../mealient/data/recipes/RecipeRepo.kt | 2 ++ .../data/recipes/impl/RecipeRepoImpl.kt | 8 ++++++++ .../data/recipes/network/RecipeDataSource.kt | 2 ++ .../mealient/ui/recipes/RecipeViewHolder.kt | 9 +++++++++ .../mealient/ui/recipes/RecipesListFragment.kt | 13 +++++++++++++ .../mealient/ui/recipes/RecipesListViewModel.kt | 5 +++++ app/src/main/res/drawable/ic_delete.xml | 10 ++++++++++ app/src/main/res/layout/view_holder_recipe.xml | 17 ++++++++++++++--- app/src/main/res/values/strings.xml | 2 ++ .../datasource/v0/MealieDataSourceV0.kt | 2 ++ .../datasource/v0/MealieDataSourceV0Impl.kt | 8 ++++++++ .../mealient/datasource/v0/MealieServiceV0.kt | 5 +++++ .../datasource/v1/MealieDataSourceV1.kt | 1 + .../datasource/v1/MealieDataSourceV1Impl.kt | 8 ++++++++ .../mealient/datasource/v1/MealieServiceV1.kt | 5 +++++ 16 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/drawable/ic_delete.xml diff --git a/app/src/main/java/gq/kirmanak/mealient/data/network/MealieDataSourceWrapper.kt b/app/src/main/java/gq/kirmanak/mealient/data/network/MealieDataSourceWrapper.kt index 5147cf0..1c75553 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/network/MealieDataSourceWrapper.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/network/MealieDataSourceWrapper.kt @@ -90,4 +90,9 @@ class MealieDataSourceWrapper @Inject constructor( } } } + + override suspend fun deleteRecipe(recipeSlug: String) = when (getVersion()) { + ServerVersion.V0 -> v0Source.deleteRecipe(recipeSlug) + ServerVersion.V1 -> v1Source.deleteRecipe(recipeSlug) + } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt index d8fdddd..77c2b07 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt @@ -19,4 +19,6 @@ interface RecipeRepo { suspend fun refreshRecipes() suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean): Result + + suspend fun deleteRecipe(recipeSlug: String): Result } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt index 8a50ce0..e61f134 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt @@ -83,6 +83,14 @@ class RecipeRepoImpl @Inject constructor( logger.e(it) { "Can't update recipe's is favorite status" } } + override suspend fun deleteRecipe(recipeSlug: String): Result = runCatchingExceptCancel { + logger.v { "deleteRecipe() called with: recipeSlug = $recipeSlug" } + dataSource.deleteRecipe(recipeSlug) + // TODO update local db + }.onFailure { + logger.e(it) { "Can't delete recipe" } + } + companion object { private const val LOAD_PAGE_SIZE = 50 private const val INITIAL_LOAD_PAGE_SIZE = LOAD_PAGE_SIZE * 3 diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSource.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSource.kt index edd9c9e..5f9b2ce 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSource.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSource.kt @@ -8,4 +8,6 @@ interface RecipeDataSource { suspend fun getFavoriteRecipes(): List suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean) + + suspend fun deleteRecipe(recipeSlug: String) } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewHolder.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewHolder.kt index 12496c1..a4a7a1d 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewHolder.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipeViewHolder.kt @@ -47,6 +47,10 @@ class RecipeViewHolder @AssistedInject constructor( override val recipeSummaryEntity: RecipeSummaryEntity ) : ClickEvent() + data class DeleteClick( + override val recipeSummaryEntity: RecipeSummaryEntity + ) : ClickEvent() + } private val loadingPlaceholder by lazy { @@ -62,6 +66,7 @@ class RecipeViewHolder @AssistedInject constructor( logger.d { "bind: item clicked $entity" } clickListener(ClickEvent.RecipeClick(entity)) } + binding.favoriteIcon.isVisible = showFavoriteIcon binding.favoriteIcon.setOnClickListener { clickListener(ClickEvent.FavoriteClick(entity)) @@ -80,6 +85,10 @@ class RecipeViewHolder @AssistedInject constructor( R.string.view_holder_recipe_non_favorite_content_description } ) + + binding.deleteIcon.setOnClickListener { + clickListener(ClickEvent.DeleteClick(item)) + } } } } diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt index 2df441c..bb1385e 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListFragment.kt @@ -100,6 +100,9 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) { is RecipeViewHolder.ClickEvent.RecipeClick -> { onRecipeClicked(it.recipeSummaryEntity) } + is RecipeViewHolder.ClickEvent.DeleteClick -> { + onDeleteClick(it) + } } } @@ -139,6 +142,16 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) { } } + private fun onDeleteClick(event: RecipeViewHolder.ClickEvent) { + logger.v { "onDeleteClick() called with: event = $event" } + viewModel.onDeleteConfirm(event.recipeSummaryEntity).observe(viewLifecycleOwner) { + logger.d { "onDeleteClick: result is $it" } + if (it.isFailure) { + showLongToast(R.string.fragment_recipes_delete_recipe_failed) + } + } + } + private fun onFavoriteClick(event: RecipeViewHolder.ClickEvent) { logger.v { "onFavoriteClick() called with: event = $event" } viewModel.onFavoriteIconClick(event.recipeSummaryEntity).observe(viewLifecycleOwner) { diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListViewModel.kt index f120965..a8a3b4a 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/RecipesListViewModel.kt @@ -49,4 +49,9 @@ class RecipesListViewModel @Inject constructor( isFavorite = recipeSummaryEntity.isFavorite.not(), ).also { emit(it) } } + + fun onDeleteConfirm(recipeSummaryEntity: RecipeSummaryEntity) = liveData { + logger.v { "onDeleteConfirm() called with: recipeSummaryEntity = $recipeSummaryEntity" } + recipeRepo.deleteRecipe(recipeSummaryEntity.slug).also { emit(it) } + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..fa259ee --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/view_holder_recipe.xml b/app/src/main/res/layout/view_holder_recipe.xml index 2121cfd..85ba963 100644 --- a/app/src/main/res/layout/view_holder_recipe.xml +++ b/app/src/main/res/layout/view_holder_recipe.xml @@ -38,9 +38,8 @@ app:layout_constraintDimensionRatio="2:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/favorite_icon" + app:layout_constraintTop_toBottomOf="@id/delete_icon" app:layout_constraintVertical_chainStyle="packed" - app:layout_goneMarginTop="@dimen/margin_medium" app:shapeAppearance="?shapeAppearanceCornerMedium" tools:srcCompat="@drawable/placeholder_recipe" /> @@ -54,6 +53,18 @@ app:layout_constraintEnd_toEndOf="@id/image" app:layout_constraintTop_toTopOf="parent" tools:srcCompat="@drawable/ic_favorite_unfilled" - tools:visibility="gone" /> + tools:visibility="visible" /> + + \ 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 bb69c91..648d79a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,6 +50,7 @@ unexpected response no connection Favorite status update failed + Recipe removal failed Change URL Search recipes @string/app_name @@ -60,4 +61,5 @@ Progress indicator Item is favorite Item is not favorite + Delete recipe \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0.kt index 2358c64..e9fff8d 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0.kt @@ -46,4 +46,6 @@ interface MealieDataSourceV0 { suspend fun removeFavoriteRecipe(userId: Int, recipeSlug: String) suspend fun addFavoriteRecipe(userId: Int, recipeSlug: String) + + suspend fun deleteRecipe(slug: String) } \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt index 146b9af..f6eea02 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt @@ -115,4 +115,12 @@ class MealieDataSourceV0Impl @Inject constructor( logMethod = { "addFavoriteRecipe" }, logParameters = { "userId = $userId, recipeSlug = $recipeSlug" } ) + + override suspend fun deleteRecipe( + slug: String + ): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized( + block = { service.deleteRecipe(slug) }, + logMethod = { "deleteRecipe" }, + logParameters = { "slug = $slug" } + ) } diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieServiceV0.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieServiceV0.kt index c3359a4..aec47d2 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieServiceV0.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieServiceV0.kt @@ -55,4 +55,9 @@ interface MealieServiceV0 { @Path("userId") userId: Int, @Path("recipeSlug") recipeSlug: String ) + + @DELETE("/api/recipes/{slug}") + suspend fun deleteRecipe( + @Path("slug") slug: String + ) } \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt index 482974f..5204405 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt @@ -54,4 +54,5 @@ interface MealieDataSourceV1 { suspend fun removeFavoriteRecipe(userId: String, recipeSlug: String) suspend fun addFavoriteRecipe(userId: String, recipeSlug: String) + suspend fun deleteRecipe(slug: String) } \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt index 6c0b094..7191cbd 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt @@ -126,5 +126,13 @@ class MealieDataSourceV1Impl @Inject constructor( logMethod = { "addFavoriteRecipe" }, logParameters = { "userId = $userId, recipeSlug = $recipeSlug" } ) + + override suspend fun deleteRecipe( + slug: String + ): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized( + block = { service.deleteRecipe(slug) }, + logMethod = { "deleteRecipe" }, + logParameters = { "slug = $slug" } + ) } diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt index d1dd7cb..6321265 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt @@ -61,4 +61,9 @@ interface MealieServiceV1 { @Path("userId") userId: String, @Path("recipeSlug") recipeSlug: String ) + + @DELETE("/api/recipes/{slug}") + suspend fun deleteRecipe( + @Path("slug") slug: String + ) } \ No newline at end of file