Redesign recipe creation and edit view with Form-based layout and URL import
Replace the single "+" button with a 2-option menu (Create New Recipe / Import from URL) across RecipeTabView, RecipeListView, and AllRecipesListView. Add ImportURLSheet for server-side recipe import with loading and error states. Completely redesign edit mode to use a native Form layout with inline editing for all sections (metadata, duration, ingredients, instructions, tools, nutrition) instead of the previous sheet-based EditableListView approach. Move delete action from edit toolbar to view mode context menu. Add recipe image display to the edit form. Refactor RecipeListView and AllRecipesListView to use closure-based callbacks instead of Binding<Bool> for the create/import actions. Add preloadedRecipeDetail support to RecipeView.ViewModel for imported recipes. Add DE/ES/FR translations for all new UI strings. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// ImportURLSheet.swift
|
||||
// Nextcloud Cookbook iOS Client
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ImportURLSheet: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var onImport: (RecipeDetail) -> Void
|
||||
|
||||
@State private var url: String = ""
|
||||
@State private var isLoading: Bool = false
|
||||
@State private var presentAlert: Bool = false
|
||||
@State private var alertMessage: String = ""
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Recipe URL", text: $url)
|
||||
.keyboardType(.URL)
|
||||
.textContentType(.URL)
|
||||
.autocorrectionDisabled()
|
||||
.textInputAutocapitalization(.never)
|
||||
} footer: {
|
||||
Text("Paste the URL of a recipe you would like to import.")
|
||||
}
|
||||
|
||||
Section {
|
||||
Button {
|
||||
Task {
|
||||
await importRecipe()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Spacer()
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Text("Import")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.disabled(url.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || isLoading)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Import Recipe")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Import Failed", isPresented: $presentAlert) {
|
||||
Button("OK", role: .cancel) { }
|
||||
} message: {
|
||||
Text(alertMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func importRecipe() async {
|
||||
isLoading = true
|
||||
let (recipeDetail, error) = await appState.importRecipe(url: url)
|
||||
isLoading = false
|
||||
|
||||
if let recipeDetail {
|
||||
dismiss()
|
||||
onImport(recipeDetail)
|
||||
} else {
|
||||
alertMessage = error?.localizedDescription ?? String(localized: "The recipe could not be imported. Please check the URL and try again.")
|
||||
presentAlert = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user