Initialize recipe removal feature
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,4 +19,6 @@ interface RecipeRepo {
|
|||||||
suspend fun refreshRecipes()
|
suspend fun refreshRecipes()
|
||||||
|
|
||||||
suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean): Result<Unit>
|
suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun deleteRecipe(recipeSlug: String): Result<Unit>
|
||||||
}
|
}
|
||||||
@@ -83,6 +83,14 @@ class RecipeRepoImpl @Inject constructor(
|
|||||||
logger.e(it) { "Can't update recipe's is favorite status" }
|
logger.e(it) { "Can't update recipe's is favorite status" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteRecipe(recipeSlug: String): Result<Unit> = 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 {
|
companion object {
|
||||||
private const val LOAD_PAGE_SIZE = 50
|
private const val LOAD_PAGE_SIZE = 50
|
||||||
private const val INITIAL_LOAD_PAGE_SIZE = LOAD_PAGE_SIZE * 3
|
private const val INITIAL_LOAD_PAGE_SIZE = LOAD_PAGE_SIZE * 3
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ interface RecipeDataSource {
|
|||||||
suspend fun getFavoriteRecipes(): List<String>
|
suspend fun getFavoriteRecipes(): List<String>
|
||||||
|
|
||||||
suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean)
|
suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean)
|
||||||
|
|
||||||
|
suspend fun deleteRecipe(recipeSlug: String)
|
||||||
}
|
}
|
||||||
@@ -47,6 +47,10 @@ class RecipeViewHolder @AssistedInject constructor(
|
|||||||
override val recipeSummaryEntity: RecipeSummaryEntity
|
override val recipeSummaryEntity: RecipeSummaryEntity
|
||||||
) : ClickEvent()
|
) : ClickEvent()
|
||||||
|
|
||||||
|
data class DeleteClick(
|
||||||
|
override val recipeSummaryEntity: RecipeSummaryEntity
|
||||||
|
) : ClickEvent()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loadingPlaceholder by lazy {
|
private val loadingPlaceholder by lazy {
|
||||||
@@ -62,6 +66,7 @@ class RecipeViewHolder @AssistedInject constructor(
|
|||||||
logger.d { "bind: item clicked $entity" }
|
logger.d { "bind: item clicked $entity" }
|
||||||
clickListener(ClickEvent.RecipeClick(entity))
|
clickListener(ClickEvent.RecipeClick(entity))
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.favoriteIcon.isVisible = showFavoriteIcon
|
binding.favoriteIcon.isVisible = showFavoriteIcon
|
||||||
binding.favoriteIcon.setOnClickListener {
|
binding.favoriteIcon.setOnClickListener {
|
||||||
clickListener(ClickEvent.FavoriteClick(entity))
|
clickListener(ClickEvent.FavoriteClick(entity))
|
||||||
@@ -80,6 +85,10 @@ class RecipeViewHolder @AssistedInject constructor(
|
|||||||
R.string.view_holder_recipe_non_favorite_content_description
|
R.string.view_holder_recipe_non_favorite_content_description
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binding.deleteIcon.setOnClickListener {
|
||||||
|
clickListener(ClickEvent.DeleteClick(item))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ class RecipesListFragment : Fragment(R.layout.fragment_recipes_list) {
|
|||||||
is RecipeViewHolder.ClickEvent.RecipeClick -> {
|
is RecipeViewHolder.ClickEvent.RecipeClick -> {
|
||||||
onRecipeClicked(it.recipeSummaryEntity)
|
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) {
|
private fun onFavoriteClick(event: RecipeViewHolder.ClickEvent) {
|
||||||
logger.v { "onFavoriteClick() called with: event = $event" }
|
logger.v { "onFavoriteClick() called with: event = $event" }
|
||||||
viewModel.onFavoriteIconClick(event.recipeSummaryEntity).observe(viewLifecycleOwner) {
|
viewModel.onFavoriteIconClick(event.recipeSummaryEntity).observe(viewLifecycleOwner) {
|
||||||
|
|||||||
@@ -49,4 +49,9 @@ class RecipesListViewModel @Inject constructor(
|
|||||||
isFavorite = recipeSummaryEntity.isFavorite.not(),
|
isFavorite = recipeSummaryEntity.isFavorite.not(),
|
||||||
).also { emit(it) }
|
).also { emit(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onDeleteConfirm(recipeSummaryEntity: RecipeSummaryEntity) = liveData {
|
||||||
|
logger.v { "onDeleteConfirm() called with: recipeSummaryEntity = $recipeSummaryEntity" }
|
||||||
|
recipeRepo.deleteRecipe(recipeSummaryEntity.slug).also { emit(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
10
app/src/main/res/drawable/ic_delete.xml
Normal file
10
app/src/main/res/drawable/ic_delete.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:tint="?attr/colorPrimary"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M7,21Q6.175,21 5.588,20.413Q5,19.825 5,19V6H4V4H9V3H15V4H20V6H19V19Q19,19.825 18.413,20.413Q17.825,21 17,21ZM17,6H7V19Q7,19 7,19Q7,19 7,19H17Q17,19 17,19Q17,19 17,19ZM9,17H11V8H9ZM13,17H15V8H13ZM7,6V19Q7,19 7,19Q7,19 7,19Q7,19 7,19Q7,19 7,19Z" />
|
||||||
|
</vector>
|
||||||
@@ -38,9 +38,8 @@
|
|||||||
app:layout_constraintDimensionRatio="2:1"
|
app:layout_constraintDimensionRatio="2:1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="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_constraintVertical_chainStyle="packed"
|
||||||
app:layout_goneMarginTop="@dimen/margin_medium"
|
|
||||||
app:shapeAppearance="?shapeAppearanceCornerMedium"
|
app:shapeAppearance="?shapeAppearanceCornerMedium"
|
||||||
tools:srcCompat="@drawable/placeholder_recipe" />
|
tools:srcCompat="@drawable/placeholder_recipe" />
|
||||||
|
|
||||||
@@ -54,6 +53,18 @@
|
|||||||
app:layout_constraintEnd_toEndOf="@id/image"
|
app:layout_constraintEnd_toEndOf="@id/image"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:srcCompat="@drawable/ic_favorite_unfilled"
|
tools:srcCompat="@drawable/ic_favorite_unfilled"
|
||||||
tools:visibility="gone" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/delete_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/margin_medium"
|
||||||
|
android:layout_marginVertical="@dimen/margin_small"
|
||||||
|
android:contentDescription="@string/view_holder_recipe_delete_content_description"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/image"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/favorite_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_delete" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
<string name="fragment_recipes_load_failure_toast_unexpected_response">unexpected response</string>
|
<string name="fragment_recipes_load_failure_toast_unexpected_response">unexpected response</string>
|
||||||
<string name="fragment_recipes_load_failure_toast_no_connection">no connection</string>
|
<string name="fragment_recipes_load_failure_toast_no_connection">no connection</string>
|
||||||
<string name="fragment_recipes_favorite_update_failed">Favorite status update failed</string>
|
<string name="fragment_recipes_favorite_update_failed">Favorite status update failed</string>
|
||||||
|
<string name="fragment_recipes_delete_recipe_failed">Recipe removal failed</string>
|
||||||
<string name="menu_navigation_drawer_change_url">Change URL</string>
|
<string name="menu_navigation_drawer_change_url">Change URL</string>
|
||||||
<string name="search_recipes_hint">Search recipes</string>
|
<string name="search_recipes_hint">Search recipes</string>
|
||||||
<string name="menu_navigation_drawer_header" translatable="false">@string/app_name</string>
|
<string name="menu_navigation_drawer_header" translatable="false">@string/app_name</string>
|
||||||
@@ -60,4 +61,5 @@
|
|||||||
<string name="content_description_activity_share_recipe_progress">Progress indicator</string>
|
<string name="content_description_activity_share_recipe_progress">Progress indicator</string>
|
||||||
<string name="view_holder_recipe_favorite_content_description">Item is favorite</string>
|
<string name="view_holder_recipe_favorite_content_description">Item is favorite</string>
|
||||||
<string name="view_holder_recipe_non_favorite_content_description">Item is not favorite</string>
|
<string name="view_holder_recipe_non_favorite_content_description">Item is not favorite</string>
|
||||||
|
<string name="view_holder_recipe_delete_content_description">Delete recipe</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -46,4 +46,6 @@ interface MealieDataSourceV0 {
|
|||||||
suspend fun removeFavoriteRecipe(userId: Int, recipeSlug: String)
|
suspend fun removeFavoriteRecipe(userId: Int, recipeSlug: String)
|
||||||
|
|
||||||
suspend fun addFavoriteRecipe(userId: Int, recipeSlug: String)
|
suspend fun addFavoriteRecipe(userId: Int, recipeSlug: String)
|
||||||
|
|
||||||
|
suspend fun deleteRecipe(slug: String)
|
||||||
}
|
}
|
||||||
@@ -115,4 +115,12 @@ class MealieDataSourceV0Impl @Inject constructor(
|
|||||||
logMethod = { "addFavoriteRecipe" },
|
logMethod = { "addFavoriteRecipe" },
|
||||||
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
|
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override suspend fun deleteRecipe(
|
||||||
|
slug: String
|
||||||
|
): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||||
|
block = { service.deleteRecipe(slug) },
|
||||||
|
logMethod = { "deleteRecipe" },
|
||||||
|
logParameters = { "slug = $slug" }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,4 +55,9 @@ interface MealieServiceV0 {
|
|||||||
@Path("userId") userId: Int,
|
@Path("userId") userId: Int,
|
||||||
@Path("recipeSlug") recipeSlug: String
|
@Path("recipeSlug") recipeSlug: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@DELETE("/api/recipes/{slug}")
|
||||||
|
suspend fun deleteRecipe(
|
||||||
|
@Path("slug") slug: String
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -54,4 +54,5 @@ interface MealieDataSourceV1 {
|
|||||||
suspend fun removeFavoriteRecipe(userId: String, recipeSlug: String)
|
suspend fun removeFavoriteRecipe(userId: String, recipeSlug: String)
|
||||||
|
|
||||||
suspend fun addFavoriteRecipe(userId: String, recipeSlug: String)
|
suspend fun addFavoriteRecipe(userId: String, recipeSlug: String)
|
||||||
|
suspend fun deleteRecipe(slug: String)
|
||||||
}
|
}
|
||||||
@@ -126,5 +126,13 @@ class MealieDataSourceV1Impl @Inject constructor(
|
|||||||
logMethod = { "addFavoriteRecipe" },
|
logMethod = { "addFavoriteRecipe" },
|
||||||
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
|
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override suspend fun deleteRecipe(
|
||||||
|
slug: String
|
||||||
|
): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||||
|
block = { service.deleteRecipe(slug) },
|
||||||
|
logMethod = { "deleteRecipe" },
|
||||||
|
logParameters = { "slug = $slug" }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,4 +61,9 @@ interface MealieServiceV1 {
|
|||||||
@Path("userId") userId: String,
|
@Path("userId") userId: String,
|
||||||
@Path("recipeSlug") recipeSlug: String
|
@Path("recipeSlug") recipeSlug: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@DELETE("/api/recipes/{slug}")
|
||||||
|
suspend fun deleteRecipe(
|
||||||
|
@Path("slug") slug: String
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user