// // RecipeTabView.swift // Nextcloud Cookbook iOS Client // // Created by Vincent Meilinger on 23.01.24. // import Foundation import SwiftUI import SwiftData struct RecipeTabView: View { //@State var cookbookState: CookbookState = CookbookState() @Environment(\.modelContext) var modelContext @Query var recipes: [Recipe] @State var categories: [(String, Int)] = [] @State private var selectedRecipe: Recipe? @State private var selectedCategory: String? = "*" var body: some View { NavigationSplitView { List(selection: $selectedCategory) { CategoryListItem(category: "All Recipes", count: recipes.count, isSelected: selectedCategory == "*") .tag("*") // Tag nil to select all recipes Section("Categories") { ForEach(categories, id: \.0.self) { category in CategoryListItem(category: category.0, count: category.1, isSelected: selectedCategory == category.0) .tag(category.0) } } } .navigationTitle("Categories") } content: { RecipeListView(selectedCategory: $selectedCategory, selectedRecipe: $selectedRecipe) } detail: { // Use a conditional view based on selection if let selectedRecipe { //RecipeDetailView(recipe: recipe) // Create a dedicated detail view RecipeView(recipe: selectedRecipe, viewModel: RecipeView.ViewModel(recipe: selectedRecipe)) } else { ContentUnavailableView("Select a Recipe", systemImage: "fork.knife.circle") } } .task { initCategories() return do { try modelContext.delete(model: Recipe.self) } catch { print("Failed to delete recipes and categories.") } guard let categories = await CookbookApiV1.getCategories(auth: UserSettings.shared.authString).0 else { return } for category in categories { guard let recipeStubs = await CookbookApiV1.getCategory(auth: UserSettings.shared.authString, named: category.name).0 else { return } for recipeStub in recipeStubs { guard let recipe = await CookbookApiV1.getRecipe(auth: UserSettings.shared.authString, id: recipeStub.id).0 else { return } modelContext.insert(recipe) } } }/* .toolbar { ToolbarItem(placement: .topBarLeading) { Button(action: { //cookbookState.showSettings = true }) { Label("Settings", systemImage: "gearshape") } } }*/ } func initCategories() { // Load Categories var categoryDict: [String: Int] = [:] for recipe in recipes { // Ensure "Uncategorized" is a valid category if used if !recipe.category.isEmpty { categoryDict[recipe.category, default: 0] += 1 } else { categoryDict["Other", default: 0] += 1 } } categories = categoryDict.map { ($0.key, $0.value) }.sorted { $0.0 < $1.0 } } class ViewModel: ObservableObject { @Published var presentEditView: Bool = false @Published var presentSettingsView: Bool = false @Published var presentLoadingIndicator: Bool = false @Published var presentConnectionPopover: Bool = false @Published var serverConnection: Bool = false } } fileprivate struct CategoryListItem: View { var category: String var count: Int var isSelected: Bool var body: some View { HStack(alignment: .center) { if isSelected { Image(systemName: "book") } else { Image(systemName: "book.closed.fill") } Text(category) .font(.system(size: 20, weight: .medium, design: .default)) Spacer() Text("\(count)") .font(.system(size: 15, weight: .bold, design: .default)) .foregroundStyle(Color.background) .frame(width: 25, height: 25, alignment: .center) .minimumScaleFactor(0.5) .background { Circle() .foregroundStyle(Color.secondary) } }.padding(7) } } /* fileprivate struct RecipeTabViewToolBar: ToolbarContent { @EnvironmentObject var appState: AppState @EnvironmentObject var viewModel: RecipeTabView.ViewModel var body: some ToolbarContent { // Top left menu toolbar item ToolbarItem(placement: .topBarLeading) { Menu { Button { Task { viewModel.presentLoadingIndicator = true UserSettings.shared.lastUpdate = Date.distantPast await appState.getCategories() for category in appState.categories { await appState.getCategory(named: category.name, fetchMode: .preferServer) } await appState.updateAllRecipeDetails() viewModel.presentLoadingIndicator = false } } label: { Text("Refresh all") Image(systemName: "icloud.and.arrow.down") } Button { viewModel.presentSettingsView = true } label: { Text("Settings") Image(systemName: "gearshape") } } label: { Image(systemName: "ellipsis.circle") } } // Server connection indicator ToolbarItem(placement: .topBarTrailing) { Button { print("Check server connection") viewModel.presentConnectionPopover = true } label: { if viewModel.presentLoadingIndicator { ProgressView() } else if viewModel.serverConnection { Image(systemName: "checkmark.icloud") } else { Image(systemName: "xmark.icloud") } }.popover(isPresented: $viewModel.presentConnectionPopover) { VStack(alignment: .leading) { Text(viewModel.serverConnection ? LocalizedStringKey("Connected to server.") : LocalizedStringKey("Unable to connect to server.")) .bold() Text("Last updated: \(DateFormatter.utcToString(date: UserSettings.shared.lastUpdate))") .font(.caption) .foregroundStyle(Color.secondary) } .padding() .presentationCompactAdaptation(.popover) } } // Create new recipes ToolbarItem(placement: .topBarTrailing) { Button { print("Add new recipe") viewModel.presentEditView = true } label: { Image(systemName: "plus.circle.fill") } } } } */