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,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
}
}
*/