Nextcloud Login refactoring

This commit is contained in:
VincentMeilinger
2025-05-31 11:12:14 +02:00
parent 5acf3b9c4f
commit 48b31a7997
29 changed files with 1277 additions and 720 deletions

View File

@@ -8,10 +8,10 @@
import Foundation
import SwiftUI
/*
struct RecipeCardView: View {
@EnvironmentObject var appState: AppState
@State var recipe: CookbookApiRecipeV1
//@EnvironmentObject var appState: AppState
@State var recipe: Recipe
@State var recipeThumb: UIImage?
@State var isDownloaded: Bool? = nil
@@ -50,6 +50,7 @@ struct RecipeCardView: View {
.background(Color.backgroundHighlight)
.clipShape(RoundedRectangle(cornerRadius: 17))
.task {
/*
recipeThumb = await appState.getImage(
id: recipe.recipe_id,
size: .THUMB,
@@ -59,18 +60,20 @@ struct RecipeCardView: View {
recipe.storedLocally = appState.recipeDetailExists(recipeId: recipe.recipe_id)
}
isDownloaded = recipe.storedLocally
*/
}
.refreshable {
/*
recipeThumb = await appState.getImage(
id: recipe.recipe_id,
size: .THUMB,
fetchMode: UserSettings.shared.storeThumb ? .preferServer : .onlyServer
)
)*/
}
.frame(height: 80)
}
}
*/
/*
struct RecipeCardView: View {
@State var state: AccountState

View File

@@ -7,6 +7,53 @@
import Foundation
import SwiftUI
import SwiftData
struct RecipeListView: View {
@Environment(\.modelContext) var modelContext
@Query var recipes: [Recipe]
@Binding var selectedRecipe: Recipe?
@Binding var selectedCategory: String?
init(selectedCategory: Binding<String?>, selectedRecipe: Binding<Recipe?>) {
var predicate: Predicate<Recipe>? = nil
if let category = selectedCategory.wrappedValue, category != "*" {
predicate = #Predicate<Recipe> {
$0.category == category
}
}
_recipes = Query(filter: predicate, sort: \.name)
_selectedRecipe = selectedRecipe
_selectedCategory = selectedCategory
}
var body: some View {
List(selection: $selectedRecipe) {
ForEach(recipes) { recipe in
RecipeCardView(recipe: recipe)
.shadow(radius: 2)
.background(
NavigationLink(value: recipe) {
EmptyView()
}
.buttonStyle(.plain)
.opacity(0)
)
.frame(height: 85)
.listRowInsets(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
.listRowSeparatorTint(.clear)
}
}
.listStyle(.plain)
.navigationTitle("Recipes")
.toolbar {
}
}
}
/*

View File

@@ -8,15 +8,15 @@
import Foundation
import SwiftUI
/*
struct RecipeView: View {
@EnvironmentObject var appState: AppState
@Bindable var recipe: Recipe
@Environment(\.dismiss) private var dismiss
@StateObject var viewModel: ViewModel
@GestureState private var dragOffset = CGSize.zero
var imageHeight: CGFloat {
if let image = viewModel.recipeImage {
if let recipeImage = recipe.image, let image = recipeImage.image {
return image.size.height < 350 ? image.size.height : 350
}
return 200
@@ -33,8 +33,8 @@ struct RecipeView: View {
coordinateSpace: CoordinateSpaces.scrollView,
defaultHeight: imageHeight
) {
if let recipeImage = viewModel.recipeImage {
Image(uiImage: recipeImage)
if let recipeImage = recipe.image, let image = recipeImage.image {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(maxHeight: imageHeight + 200)
@@ -54,15 +54,12 @@ struct RecipeView: View {
VStack(alignment: .leading) {
if viewModel.editMode {
RecipeImportSection(viewModel: viewModel, importRecipe: importRecipe)
}
if viewModel.editMode {
RecipeMetadataSection(viewModel: viewModel)
//RecipeImportSection(viewModel: viewModel, importRecipe: importRecipe)
//RecipeMetadataSection(viewModel: viewModel)
}
HStack {
EditableText(text: $viewModel.observableRecipeDetail.name, editMode: $viewModel.editMode, titleKey: "Recipe Name")
EditableText(text: $recipe.name, editMode: $viewModel.editMode, titleKey: "Recipe Name")
.font(.title)
.bold()
@@ -74,36 +71,37 @@ struct RecipeView: View {
}
}.padding([.top, .horizontal])
if viewModel.observableRecipeDetail.description != "" || viewModel.editMode {
EditableText(text: $viewModel.observableRecipeDetail.description, editMode: $viewModel.editMode, titleKey: "Description", lineLimit: 0...5, axis: .vertical)
if recipe.recipeDescription != "" || viewModel.editMode {
EditableText(text: $recipe.recipeDescription, editMode: $viewModel.editMode, titleKey: "Description", lineLimit: 0...5, axis: .vertical)
.fontWeight(.medium)
.padding(.horizontal)
.padding(.top, 2)
}
// Recipe Body Section
RecipeDurationSection(viewModel: viewModel)
RecipeDurationSection(recipe: recipe, editMode: $viewModel.editMode)
Divider()
LazyVGrid(columns: [GridItem(.adaptive(minimum: 400), alignment: .top)]) {
if(!viewModel.observableRecipeDetail.recipeIngredient.isEmpty || viewModel.editMode) {
RecipeIngredientSection(viewModel: viewModel)
if(!recipe.ingredients.isEmpty || viewModel.editMode) {
RecipeIngredientSection(recipe: recipe, editMode: $viewModel.editMode, presentIngredientEditView: $viewModel.presentIngredientEditView)
}
if(!viewModel.observableRecipeDetail.recipeInstructions.isEmpty || viewModel.editMode) {
RecipeInstructionSection(viewModel: viewModel)
if(!recipe.instructions.isEmpty || viewModel.editMode) {
RecipeInstructionSection(recipe: recipe, editMode: $viewModel.editMode, presentInstructionEditView: $viewModel.presentInstructionEditView)
}
if(!viewModel.observableRecipeDetail.tool.isEmpty || viewModel.editMode) {
RecipeToolSection(viewModel: viewModel)
if(!recipe.tools.isEmpty || viewModel.editMode) {
RecipeToolSection(recipe: recipe, editMode: $viewModel.editMode, presentToolEditView: $viewModel.presentToolEditView)
}
RecipeNutritionSection(viewModel: viewModel)
RecipeNutritionSection(recipe: recipe, editMode: $viewModel.editMode)
}
if !viewModel.editMode {
Divider()
LazyVGrid(columns: [GridItem(.adaptive(minimum: 400), alignment: .top)]) {
RecipeKeywordSection(viewModel: viewModel)
MoreInformationSection(viewModel: viewModel)
//RecipeKeywordSection(viewModel: viewModel)
MoreInformationSection(recipe: recipe)
}
}
}
@@ -115,21 +113,21 @@ struct RecipeView: View {
.ignoresSafeArea(.container, edges: .top)
.navigationBarTitleDisplayMode(.inline)
.toolbar(.visible, for: .navigationBar)
//.toolbarTitleDisplayMode(.inline)
.navigationTitle(viewModel.showTitle ? viewModel.recipe.name : "")
.toolbar {
RecipeViewToolBar(viewModel: viewModel)
}
.sheet(isPresented: $viewModel.presentShareSheet) {
ShareView(recipeDetail: viewModel.observableRecipeDetail.toRecipeDetail(),
/*ShareView(recipeDetail: viewModel.observableRecipeDetail.toRecipeDetail(),
recipeImage: viewModel.recipeImage,
presentShareSheet: $viewModel.presentShareSheet)
presentShareSheet: $viewModel.presentShareSheet)*/
}
.sheet(isPresented: $viewModel.presentInstructionEditView) {
EditableListView(
isPresented: $viewModel.presentInstructionEditView,
items: $viewModel.observableRecipeDetail.recipeInstructions,
items: $recipe.instructions,
title: "Instructions",
emptyListText: "Add cooking steps for fellow chefs to follow.",
titleKey: "Instruction",
@@ -139,7 +137,7 @@ struct RecipeView: View {
.sheet(isPresented: $viewModel.presentIngredientEditView) {
EditableListView(
isPresented: $viewModel.presentIngredientEditView,
items: $viewModel.observableRecipeDetail.recipeIngredient,
items: $recipe.ingredients,
title: "Ingredients",
emptyListText: "Start by adding your first ingredient! 🥬",
titleKey: "Ingredient",
@@ -149,7 +147,7 @@ struct RecipeView: View {
.sheet(isPresented: $viewModel.presentToolEditView) {
EditableListView(
isPresented: $viewModel.presentToolEditView,
items: $viewModel.observableRecipeDetail.tool,
items: $recipe.tools,
title: "Tools",
emptyListText: "List your tools here. 🍴",
titleKey: "Tool",
@@ -158,6 +156,7 @@ struct RecipeView: View {
}
.task {
/*
// Load recipe detail
if !viewModel.newRecipe {
// For existing recipes, load the recipeDetail and image
@@ -185,7 +184,7 @@ struct RecipeView: View {
viewModel.setupView(recipeDetail: CookbookApiRecipeDetailV1())
viewModel.editMode = true
viewModel.isDownloaded = false
}
}*/
}
.alert(viewModel.alertType.localizedTitle, isPresented: $viewModel.presentAlert) {
ForEach(viewModel.alertType.alertButtons) { buttonType in
@@ -217,13 +216,14 @@ struct RecipeView: View {
UIApplication.shared.isIdleTimerDisabled = false
}
.onChange(of: viewModel.editMode) { newValue in
/*
if newValue && appState.allKeywords.isEmpty {
Task {
appState.allKeywords = await appState.getKeywords(fetchMode: .preferServer).sorted(by: { a, b in
a.recipe_count > b.recipe_count
})
}
}
}*/
}
}
@@ -231,9 +231,8 @@ struct RecipeView: View {
// MARK: - RecipeView ViewModel
class ViewModel: ObservableObject {
@Published var observableRecipeDetail: Recipe = Recipe()
@Published var recipeDetail: CookbookApiRecipeDetailV1 = CookbookApiRecipeDetailV1.error
@Published var recipeImage: UIImage? = nil
@Published var recipe: Recipe
@Published var editMode: Bool = false
@Published var showTitle: Bool = false
@Published var isDownloaded: Bool? = nil
@@ -244,7 +243,6 @@ struct RecipeView: View {
@Published var presentIngredientEditView: Bool = false
@Published var presentToolEditView: Bool = false
var recipe: CookbookApiRecipeV1
var sharedURL: URL? = nil
var newRecipe: Bool = false
@@ -254,26 +252,13 @@ struct RecipeView: View {
var alertAction: () async -> () = { }
// Initializers
init(recipe: CookbookApiRecipeV1) {
init(recipe: Recipe) {
self.recipe = recipe
}
init() {
self.newRecipe = true
self.recipe = CookbookApiRecipeV1(
name: String(localized: "New Recipe"),
keywords: "",
dateCreated: "",
dateModified: "",
imageUrl: "",
imagePlaceholderUrl: "",
recipe_id: 0)
}
// View setup
func setupView(recipeDetail: CookbookApiRecipeDetailV1) {
self.recipeDetail = recipeDetail
self.observableRecipeDetail = Recipe(recipeDetail)
self.recipe = Recipe()
}
func presentAlert(_ type: UserAlert, action: @escaping () async -> () = {}) {
@@ -285,7 +270,7 @@ struct RecipeView: View {
}
/*
extension RecipeView {
func importRecipe(from url: String) async -> UserAlert? {
let (scrapedRecipe, error) = await appState.importRecipe(url: url)
@@ -309,13 +294,12 @@ extension RecipeView {
return nil
}
}
*/
// MARK: - Tool Bar
struct RecipeViewToolBar: ToolbarContent {
@EnvironmentObject var appState: AppState
@Environment(\.dismiss) private var dismiss
@ObservedObject var viewModel: RecipeView.ViewModel
@@ -385,6 +369,7 @@ struct RecipeViewToolBar: ToolbarContent {
}
func handleUpload() async {
/*
if viewModel.newRecipe {
print("Uploading new recipe.")
if let recipeValidationError = recipeValid() {
@@ -416,9 +401,11 @@ struct RecipeViewToolBar: ToolbarContent {
}
viewModel.editMode = false
viewModel.presentAlert(RecipeAlert.UPLOAD_SUCCESS)
*/
}
func handleDelete() async {
/*
let category = viewModel.observableRecipeDetail.recipeCategory
guard let id = Int(viewModel.observableRecipeDetail.id) else {
viewModel.presentAlert(RequestAlert.REQUEST_DROPPED)
@@ -432,11 +419,13 @@ struct RecipeViewToolBar: ToolbarContent {
await appState.getCategory(named: category, fetchMode: .preferServer)
viewModel.presentAlert(RecipeAlert.DELETE_SUCCESS)
dismiss()
*/
}
func recipeValid() -> RecipeAlert? {
/*
// Check if the recipe has a name
if viewModel.observableRecipeDetail.name.replacingOccurrences(of: " ", with: "") == "" {
if viewModel.recipe.name.replacingOccurrences(of: " ", with: "") == "" {
return RecipeAlert.NO_TITLE
}
@@ -454,12 +443,11 @@ struct RecipeViewToolBar: ToolbarContent {
}
}
}
*/
return nil
}
}
*/

View File

@@ -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 {
}
}
*/

View File

@@ -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 {
}
}
*/

View File

@@ -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
}
}

View File

@@ -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()
}
}
*/

View File

@@ -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 {
}
}
*/

View File

@@ -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 {
}
*/