diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate index 6493ce8..c37af79 100644 Binary files a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate and b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift b/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift index 848e59a..76a5d48 100644 --- a/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift +++ b/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift @@ -367,14 +367,20 @@ fileprivate struct RecipeIngredientSection: View { } Spacer() Button { - groceryList.addItems(recipeDetail.recipeIngredient, toRecipe: recipeDetail.id, recipeName: recipeDetail.name) + withAnimation { + if groceryList.containsRecipe(recipeDetail.id) { + groceryList.deleteGroceryRecipe(recipeDetail.id) + } else { + groceryList.addItems(recipeDetail.recipeIngredient, toRecipe: recipeDetail.id, recipeName: recipeDetail.name) + } + } } label: { Image(systemName: "storefront") } } ForEach(recipeDetail.recipeIngredient, id: \.self) { ingredient in - IngredientListItem(ingredient: ingredient) { + IngredientListItem(ingredient: ingredient, recipeId: recipeDetail.id) { groceryList.addItem(ingredient, toRecipe: recipeDetail.id, recipeName: recipeDetail.name) } .padding(4) @@ -403,6 +409,7 @@ fileprivate struct RecipeToolSection: View { fileprivate struct IngredientListItem: View { @EnvironmentObject var groceryList: GroceryList @State var ingredient: String + @State var recipeId: String let addToGroceryListAction: () -> Void @State var isSelected: Bool = false @State private var dragOffset: CGFloat = 0 @@ -410,6 +417,10 @@ fileprivate struct IngredientListItem: View { var body: some View { HStack(alignment: .top) { + if groceryList.containsItem(at: recipeId, item: ingredient) { + Image(systemName: "storefront") + .foregroundStyle(Color.green) + } if isSelected { Image(systemName: "checkmark.circle") } else { @@ -435,8 +446,14 @@ fileprivate struct IngredientListItem: View { self.dragOffset = max(0, min(dragAmount, maxDragDistance + pow(dragAmount - maxDragDistance, 0.7))) } .onEnded { gesture in - if gesture.translation.width > maxDragDistance * 0.8 { // Swipe right threshold - addToGroceryListAction() + if gesture.translation.width > maxDragDistance * 0.8 { // Swipe threshold + withAnimation { + if groceryList.containsItem(at: recipeId, item: ingredient) { + groceryList.deleteItem(ingredient, fromRecipe: recipeId) + } else { + addToGroceryListAction() + } + } } // Animate back to original position withAnimation { @@ -444,17 +461,6 @@ fileprivate struct IngredientListItem: View { } } ) - .background { - if dragOffset > 0 { - HStack { - Image(systemName: "storefront") - .foregroundStyle(Color.green) - .opacity((dragOffset - 10)/(maxDragDistance-10)) - - Spacer() - } - } - } } } diff --git a/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift b/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift index 6ab5302..0f0c458 100644 --- a/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift @@ -180,12 +180,14 @@ class GroceryList: ObservableObject { groceryDict.removeValue(forKey: recipeId) } save() + objectWillChange.send() } func deleteGroceryRecipe(_ recipeId: String) { print("Deleting grocery recipe with id \(recipeId)") groceryDict.removeValue(forKey: recipeId) save() + objectWillChange.send() } func deleteAll() { @@ -200,6 +202,18 @@ class GroceryList: ObservableObject { save() } + func containsItem(at recipeId: String, item: String) -> Bool { + guard let recipe = groceryDict[recipeId] else { return false } + if recipe.items.contains(where: { $0.name == item }) { + return true + } + return false + } + + func containsRecipe(_ recipeId: String) -> Bool { + return groceryDict[recipeId] != nil + } + func save() { Task { await dataStore.save(data: groceryDict, toPath: "grocery_list.data")