Nextcloud Login refactoring
This commit is contained in:
@@ -9,19 +9,20 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - RecipeView Duration Section
|
||||
/*
|
||||
|
||||
struct RecipeDurationSection: View {
|
||||
@State var viewModel: RecipeView.ViewModel
|
||||
@Bindable var recipe: Recipe
|
||||
@Binding var editMode: Bool
|
||||
@State var presentPopover: Bool = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200, maximum: .infinity), alignment: .leading)]) {
|
||||
DurationView(time: viewModel.recipe.prepTime, title: LocalizedStringKey("Preparation"))
|
||||
DurationView(time: viewModel.recipe.cookTime, title: LocalizedStringKey("Cooking"))
|
||||
DurationView(time: viewModel.recipe.totalTime, title: LocalizedStringKey("Total time"))
|
||||
DurationView(time: recipe.prepTimeDurationComponent, title: LocalizedStringKey("Preparation"))
|
||||
DurationView(time: recipe.cookTimeDurationComponent, title: LocalizedStringKey("Cooking"))
|
||||
DurationView(time: recipe.totalTimeDurationComponent, title: LocalizedStringKey("Total time"))
|
||||
}
|
||||
if viewModel.editMode {
|
||||
if editMode {
|
||||
Button {
|
||||
presentPopover.toggle()
|
||||
} label: {
|
||||
@@ -34,9 +35,9 @@ struct RecipeDurationSection: View {
|
||||
.padding()
|
||||
.popover(isPresented: $presentPopover) {
|
||||
EditableDurationView(
|
||||
prepTime: viewModel.recipe.prepTime,
|
||||
cookTime: viewModel.recipe.cookTime,
|
||||
totalTime: viewModel.recipe.totalTime
|
||||
prepTime: recipe.prepTimeDurationComponent,
|
||||
cookTime: recipe.cookTimeDurationComponent,
|
||||
totalTime: recipe.totalTimeDurationComponent
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -143,4 +144,4 @@ fileprivate struct TimePickerView: View {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@@ -7,18 +7,24 @@
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
// MARK: - RecipeView Ingredients Section
|
||||
/*
|
||||
|
||||
struct RecipeIngredientSection: View {
|
||||
@Environment(CookbookState.self) var cookbookState
|
||||
@State var viewModel: RecipeView.ViewModel
|
||||
@Environment(\.modelContext) var modelContext
|
||||
@Bindable var recipe: Recipe
|
||||
@Binding var editMode: Bool
|
||||
@Binding var presentIngredientEditView: Bool
|
||||
@State var recipeGroceries: RecipeGroceries? = nil
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
/*
|
||||
if cookbookState.groceryList.containsRecipe(viewModel.recipe.id) {
|
||||
cookbookState.groceryList.deleteGroceryRecipe(viewModel.recipe.id)
|
||||
} else {
|
||||
@@ -28,6 +34,7 @@ struct RecipeIngredientSection: View {
|
||||
recipeName: viewModel.recipe.name
|
||||
)
|
||||
}
|
||||
*/
|
||||
}
|
||||
} label: {
|
||||
if #available(iOS 17.0, *) {
|
||||
@@ -35,7 +42,7 @@ struct RecipeIngredientSection: View {
|
||||
} else {
|
||||
Image(systemName: "heart.text.square")
|
||||
}
|
||||
}.disabled(viewModel.editMode)
|
||||
}.disabled(editMode)
|
||||
|
||||
SecondaryLabel(text: LocalizedStringKey("Ingredients"))
|
||||
|
||||
@@ -45,26 +52,30 @@ struct RecipeIngredientSection: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.bold()
|
||||
|
||||
ServingPickerView(selectedServingSize: $viewModel.recipe.ingredientMultiplier)
|
||||
ServingPickerView(selectedServingSize: $recipe.ingredientMultiplier)
|
||||
}
|
||||
|
||||
ForEach(0..<viewModel.recipe.recipeIngredient.count, id: \.self) { ix in
|
||||
IngredientListItem(
|
||||
ingredient: $viewModel.recipe.recipeIngredient[ix],
|
||||
servings: $viewModel.recipe.ingredientMultiplier,
|
||||
recipeYield: Double(viewModel.recipe.recipeYield),
|
||||
recipeId: viewModel.recipe.id
|
||||
|
||||
|
||||
ForEach(0..<recipe.ingredients.count, id: \.self) { ix in
|
||||
/*IngredientListItem(
|
||||
ingredient: $recipe.recipeIngredient[ix],
|
||||
servings: $recipe.ingredientMultiplier,
|
||||
recipeYield: Double(recipe.recipeYield),
|
||||
recipeId: recipe.id
|
||||
) {
|
||||
/*
|
||||
cookbookState.groceryList.addItem(
|
||||
viewModel.recipe.recipeIngredient[ix],
|
||||
toRecipe: viewModel.recipe.id,
|
||||
recipeName: viewModel.recipe.name
|
||||
)
|
||||
recipe.recipeIngredient[ix],
|
||||
toRecipe: recipe.id,
|
||||
recipeName: recipe.name
|
||||
)*/
|
||||
}
|
||||
.padding(4)
|
||||
.padding(4)*/
|
||||
Text(recipe.ingredients[ix])
|
||||
}
|
||||
|
||||
if viewModel.recipe.ingredientMultiplier != Double(viewModel.recipe.recipeYield) {
|
||||
if recipe.ingredientMultiplier != Double(recipe.yield) {
|
||||
HStack() {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundStyle(.secondary)
|
||||
@@ -73,9 +84,9 @@ struct RecipeIngredientSection: View {
|
||||
}.padding(.top)
|
||||
}
|
||||
|
||||
if viewModel.editMode {
|
||||
if editMode {
|
||||
Button {
|
||||
viewModel.presentIngredientEditView.toggle()
|
||||
presentIngredientEditView.toggle()
|
||||
} label: {
|
||||
Text("Edit")
|
||||
}
|
||||
@@ -83,19 +94,80 @@ struct RecipeIngredientSection: View {
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.animation(.easeInOut, value: viewModel.recipe.ingredientMultiplier)
|
||||
.animation(.easeInOut, value: recipe.ingredientMultiplier)
|
||||
}
|
||||
|
||||
func toggleAllGroceryItems(_ itemNames: [String], inCategory categoryId: String, named name: String) {
|
||||
do {
|
||||
// Find or create the target category
|
||||
let categoryPredicate = #Predicate<RecipeGroceries> { $0.id == categoryId }
|
||||
let fetchDescriptor = FetchDescriptor<RecipeGroceries>(predicate: categoryPredicate)
|
||||
|
||||
if let existingCategory = try modelContext.fetch(fetchDescriptor).first {
|
||||
// Delete category if it exists
|
||||
modelContext.delete(existingCategory)
|
||||
} else {
|
||||
// Create the category if it doesn't exist
|
||||
let newCategory = RecipeGroceries(id: categoryId, name: name)
|
||||
modelContext.insert(newCategory)
|
||||
|
||||
// Add new GroceryItems to the category
|
||||
for itemName in itemNames {
|
||||
let newItem = GroceryItem(name: itemName, isChecked: false)
|
||||
newCategory.items.append(newItem)
|
||||
}
|
||||
|
||||
try modelContext.save()
|
||||
}
|
||||
} catch {
|
||||
print("Error adding grocery items: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func toggleGroceryItem(_ itemName: String, inCategory categoryId: String, named name: String) {
|
||||
do {
|
||||
// Find or create the target category
|
||||
let categoryPredicate = #Predicate<RecipeGroceries> { $0.id == categoryId }
|
||||
let fetchDescriptor = FetchDescriptor<RecipeGroceries>(predicate: categoryPredicate)
|
||||
|
||||
if let existingCategory = try modelContext.fetch(fetchDescriptor).first {
|
||||
// Delete item if it exists
|
||||
if existingCategory.items.contains(where: { $0.name == itemName }) {
|
||||
existingCategory.items.removeAll { $0.name == itemName }
|
||||
|
||||
// Delete category if empty
|
||||
if existingCategory.items.isEmpty {
|
||||
modelContext.delete(existingCategory)
|
||||
}
|
||||
} else {
|
||||
existingCategory.items.append(GroceryItem(name: itemName, isChecked: false))
|
||||
}
|
||||
} else {
|
||||
// Add the category if it doesn't exist
|
||||
let newCategory = RecipeGroceries(id: categoryId, name: name)
|
||||
modelContext.insert(newCategory)
|
||||
|
||||
// Add the item to the new category
|
||||
newCategory.items.append(GroceryItem(name: itemName, isChecked: false))
|
||||
}
|
||||
|
||||
try modelContext.save()
|
||||
} catch {
|
||||
print("Error adding grocery items: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RecipeIngredientSection List Item
|
||||
|
||||
/*
|
||||
fileprivate struct IngredientListItem: View {
|
||||
@Environment(CookbookState.self) var cookbookState
|
||||
@Environment(\.modelContext) var modelContext
|
||||
@Bindable var recipeGroceries: RecipeGroceries
|
||||
@Binding var ingredient: String
|
||||
@Binding var servings: Double
|
||||
@State var recipeYield: Double
|
||||
@State var recipeId: String
|
||||
let addToGroceryListAction: () -> Void
|
||||
|
||||
|
||||
@State var modifiedIngredient: AttributedString = ""
|
||||
@State var isSelected: Bool = false
|
||||
@@ -110,7 +182,7 @@ fileprivate struct IngredientListItem: View {
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top) {
|
||||
if cookbookState.groceryList.containsItem(at: recipeId, item: ingredient) {
|
||||
if recipeGroceries.items.contains(ingredient) {
|
||||
if #available(iOS 17.0, *) {
|
||||
Image(systemName: "storefront")
|
||||
.foregroundStyle(Color.green)
|
||||
@@ -168,7 +240,7 @@ fileprivate struct IngredientListItem: View {
|
||||
.onEnded { gesture in
|
||||
withAnimation {
|
||||
if dragOffset > maxDragDistance * 0.3 { // Swipe threshold
|
||||
if cookbookState.groceryList.containsItem(at: recipeId, item: ingredient) {
|
||||
if recipeGroceries.items.contains(ingredient) {
|
||||
cookbookState.groceryList.deleteItem(ingredient, fromRecipe: recipeId)
|
||||
} else {
|
||||
addToGroceryListAction()
|
||||
@@ -182,7 +254,7 @@ fileprivate struct IngredientListItem: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
struct ServingPickerView: View {
|
||||
@@ -217,4 +289,4 @@ struct ServingPickerView: View {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@@ -9,22 +9,26 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - RecipeView Instructions Section
|
||||
/*
|
||||
struct RecipeInstructionSection: View {
|
||||
@State var viewModel: RecipeView.ViewModel
|
||||
|
||||
struct RecipeInstructionSection: View {
|
||||
@Bindable var recipe: Recipe
|
||||
@Binding var editMode: Bool
|
||||
@Binding var presentInstructionEditView: Bool
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
SecondaryLabel(text: LocalizedStringKey("Instructions"))
|
||||
Spacer()
|
||||
}
|
||||
ForEach(viewModel.recipe.recipeInstructions.indices, id: \.self) { ix in
|
||||
RecipeInstructionListItem(instruction: $viewModel.recipe.recipeInstructions[ix], index: ix+1)
|
||||
ForEach(recipe.instructions.indices, id: \.self) { ix in
|
||||
RecipeInstructionListItem(instruction: $recipe.instructions[ix], index: ix+1)
|
||||
}
|
||||
if viewModel.editMode {
|
||||
if editMode {
|
||||
Button {
|
||||
viewModel.presentInstructionEditView.toggle()
|
||||
presentInstructionEditView.toggle()
|
||||
} label: {
|
||||
Text("Edit")
|
||||
}
|
||||
@@ -32,11 +36,10 @@ struct RecipeInstructionSection: View {
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Preview
|
||||
|
||||
fileprivate struct RecipeInstructionListItem: View {
|
||||
@Binding var instruction: String
|
||||
@@ -56,4 +59,45 @@ fileprivate struct RecipeInstructionListItem: View {
|
||||
.animation(.easeInOut, value: isSelected)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
struct RecipeInstructionSection_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
// Create a mock recipe
|
||||
@State var mockRecipe = createRecipe()
|
||||
|
||||
// Create mock state variables for the @Binding properties
|
||||
@State var mockEditMode = true
|
||||
@State var mockPresentInstructionEditView = false
|
||||
|
||||
// Provide the mock data to the view
|
||||
RecipeInstructionSection(
|
||||
recipe: mockRecipe,
|
||||
editMode: $mockEditMode,
|
||||
presentInstructionEditView: $mockPresentInstructionEditView
|
||||
)
|
||||
.previewDisplayName("Instructions - Edit Mode")
|
||||
|
||||
RecipeInstructionSection(
|
||||
recipe: mockRecipe,
|
||||
editMode: $mockEditMode,
|
||||
presentInstructionEditView: $mockPresentInstructionEditView
|
||||
)
|
||||
.previewDisplayName("Instructions - Read Only")
|
||||
.environment(\.editMode, .constant(.inactive))
|
||||
}
|
||||
|
||||
static func createRecipe() -> Recipe {
|
||||
let recipe = Recipe()
|
||||
recipe.name = "Mock Recipe"
|
||||
recipe.instructions = [
|
||||
"Step 1: Gather all ingredients and equipment.",
|
||||
"Step 2: Preheat oven to 180°C (350°F) and prepare baking dish.",
|
||||
"Step 3: Combine dry ingredients in a large bowl and mix thoroughly.",
|
||||
"Step 4: In a separate bowl, whisk wet ingredients until smooth.",
|
||||
"Step 5: Gradually add wet ingredients to dry ingredients, mixing until just combined. Do not overmix.",
|
||||
"Step 6: Pour the mixture into the prepared baking dish and bake for 30-35 minutes, or until golden brown and a toothpick inserted into the center comes out clean.",
|
||||
"Step 7: Let cool before serving. Enjoy!"
|
||||
]
|
||||
return recipe
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,27 +121,27 @@ fileprivate struct PickerPopoverView<Item: Hashable & CustomStringConvertible, C
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// MARK: - RecipeView More Information Section
|
||||
|
||||
struct MoreInformationSection: View {
|
||||
@State var viewModel: RecipeView.ViewModel
|
||||
@Bindable var recipe: Recipe
|
||||
|
||||
var body: some View {
|
||||
CollapsibleView(titleColor: .secondary, isCollapsed: !UserSettings.shared.expandInfoSection) {
|
||||
VStack(alignment: .leading) {
|
||||
if let dateCreated = viewModel.recipe.dateCreated {
|
||||
if let dateCreated = recipe.dateCreated {
|
||||
Text("Created: \(Date.convertISOStringToLocalString(isoDateString: dateCreated) ?? "")")
|
||||
}
|
||||
if let dateModified = viewModel.recipe.dateModified {
|
||||
if let dateModified = recipe.dateModified {
|
||||
Text("Last modified: \(Date.convertISOStringToLocalString(isoDateString: dateModified) ?? "")")
|
||||
}
|
||||
if viewModel.recipe.url != "", let url = URL(string: viewModel.recipe.url ?? "") {
|
||||
if recipe.url != "", let url = URL(string: recipe.url ?? "") {
|
||||
HStack(alignment: .top) {
|
||||
Text("URL:")
|
||||
Link(destination: url) {
|
||||
Text(viewModel.recipe.url ?? "")
|
||||
Text(recipe.url ?? "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,5 +157,3 @@ struct MoreInformationSection: View {
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -9,14 +9,15 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - RecipeView Nutrition Section
|
||||
/*
|
||||
|
||||
struct RecipeNutritionSection: View {
|
||||
@State var viewModel: RecipeView.ViewModel
|
||||
@Bindable var recipe: Recipe
|
||||
@Binding var editMode: Bool
|
||||
|
||||
var body: some View {
|
||||
CollapsibleView(titleColor: .secondary, isCollapsed: !UserSettings.shared.expandNutritionSection) {
|
||||
VStack(alignment: .leading) {
|
||||
if viewModel.editMode {
|
||||
if editMode {
|
||||
ForEach(Nutrition.allCases, id: \.self) { nutrition in
|
||||
HStack {
|
||||
Text(nutrition.localizedDescription)
|
||||
@@ -28,7 +29,7 @@ struct RecipeNutritionSection: View {
|
||||
} else if !nutritionEmpty() {
|
||||
VStack(alignment: .leading) {
|
||||
ForEach(Nutrition.allCases, id: \.self) { nutrition in
|
||||
if let value = viewModel.recipe.nutrition[nutrition.dictKey], nutrition.dictKey != Nutrition.servingSize.dictKey {
|
||||
if let value = recipe.nutrition[nutrition.dictKey], nutrition.dictKey != Nutrition.servingSize.dictKey {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(nutrition.localizedDescription): \(value)")
|
||||
.multilineTextAlignment(.leading)
|
||||
@@ -43,7 +44,7 @@ struct RecipeNutritionSection: View {
|
||||
}
|
||||
} title: {
|
||||
HStack {
|
||||
if let servingSize = viewModel.recipe.nutrition["servingSize"] {
|
||||
if let servingSize = recipe.nutrition["servingSize"] {
|
||||
SecondaryLabel(text: "Nutrition (\(servingSize))")
|
||||
} else {
|
||||
SecondaryLabel(text: LocalizedStringKey("Nutrition"))
|
||||
@@ -56,14 +57,14 @@ struct RecipeNutritionSection: View {
|
||||
|
||||
func binding(for key: String) -> Binding<String> {
|
||||
Binding(
|
||||
get: { viewModel.recipe.nutrition[key, default: ""] },
|
||||
set: { viewModel.recipe.nutrition[key] = $0 }
|
||||
get: { recipe.nutrition[key, default: ""] },
|
||||
set: { recipe.nutrition[key] = $0 }
|
||||
)
|
||||
}
|
||||
|
||||
func nutritionEmpty() -> Bool {
|
||||
for nutrition in Nutrition.allCases {
|
||||
if let value = viewModel.recipe.nutrition[nutrition.dictKey] {
|
||||
if let value = recipe.nutrition[nutrition.dictKey] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -71,4 +72,3 @@ struct RecipeNutritionSection: View {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -9,9 +9,11 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - RecipeView Tool Section
|
||||
/*
|
||||
|
||||
struct RecipeToolSection: View {
|
||||
@State var viewModel: RecipeView.ViewModel
|
||||
@Bindable var recipe: Recipe
|
||||
@Binding var editMode: Bool
|
||||
@Binding var presentToolEditView: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
@@ -20,11 +22,11 @@ struct RecipeToolSection: View {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
RecipeListSection(list: $viewModel.recipe.tool)
|
||||
RecipeListSection(list: $recipe.tools)
|
||||
|
||||
if viewModel.editMode {
|
||||
if editMode {
|
||||
Button {
|
||||
viewModel.presentToolEditView.toggle()
|
||||
presentToolEditView.toggle()
|
||||
} label: {
|
||||
Text("Edit")
|
||||
}
|
||||
@@ -36,4 +38,4 @@ struct RecipeToolSection: View {
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user