From 931364c57c9617f401911f88a042f4712d0ee055 Mon Sep 17 00:00:00 2001 From: VincentMeilinger Date: Tue, 23 Jan 2024 19:02:04 +0100 Subject: [PATCH] Added tabs and a grocery list --- .../project.pbxproj | 22 ++- .../UserInterfaceState.xcuserstate | Bin 60459 -> 68056 bytes .../Localizable.xcstrings | 9 + .../ViewModels/MainViewModel.swift | 37 ++-- .../Views/MainView.swift | 100 ++++++----- .../Views/RecipeDetailView.swift | 34 ++++ .../Views/SettingsView.swift | 2 +- .../Views/Tabs/GroceryListTabView.swift | 118 +++++++++++++ .../Views/Tabs/RecipeTabView.swift | 166 ++++++++++++++++++ .../Views/Tabs/SearchTabView.swift | 58 ++++++ 10 files changed, 485 insertions(+), 61 deletions(-) create mode 100644 Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift create mode 100644 Nextcloud Cookbook iOS Client/Views/Tabs/RecipeTabView.swift create mode 100644 Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj b/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj index c71e326..af531b4 100644 --- a/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj +++ b/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj @@ -47,6 +47,9 @@ A7FB0D7A2B25C66600A3469E /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7FB0D792B25C66600A3469E /* OnboardingView.swift */; }; A7FB0D7C2B25C68500A3469E /* TokenLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7FB0D7B2B25C68500A3469E /* TokenLoginView.swift */; }; A7FB0D7E2B25C6A200A3469E /* V2LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7FB0D7D2B25C6A200A3469E /* V2LoginView.swift */; }; + A977D0DE2B600300009783A9 /* SearchTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977D0DD2B600300009783A9 /* SearchTabView.swift */; }; + A977D0E02B600318009783A9 /* RecipeTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977D0DF2B600318009783A9 /* RecipeTabView.swift */; }; + A977D0E22B60034E009783A9 /* GroceryListTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A977D0E12B60034E009783A9 /* GroceryListTabView.swift */; }; A9CA6CEF2B4C086100F78AB5 /* RecipeExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CA6CEE2B4C086100F78AB5 /* RecipeExporter.swift */; }; A9CA6CF62B4C63F200F78AB5 /* TPPDF in Frameworks */ = {isa = PBXBuildFile; productRef = A9CA6CF52B4C63F200F78AB5 /* TPPDF */; }; A9D89AB02B4FE97800F49D92 /* TimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D89AAF2B4FE97800F49D92 /* TimerView.swift */; }; @@ -114,6 +117,9 @@ A7FB0D792B25C66600A3469E /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; A7FB0D7B2B25C68500A3469E /* TokenLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLoginView.swift; sourceTree = ""; }; A7FB0D7D2B25C6A200A3469E /* V2LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = V2LoginView.swift; sourceTree = ""; }; + A977D0DD2B600300009783A9 /* SearchTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTabView.swift; sourceTree = ""; }; + A977D0DF2B600318009783A9 /* RecipeTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeTabView.swift; sourceTree = ""; }; + A977D0E12B60034E009783A9 /* GroceryListTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroceryListTabView.swift; sourceTree = ""; }; A9CA6CEE2B4C086100F78AB5 /* RecipeExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeExporter.swift; sourceTree = ""; }; A9D89AAF2B4FE97800F49D92 /* TimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerView.swift; sourceTree = ""; }; A9FA2AB52B5079B200A43702 /* alarm_sound_0.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = alarm_sound_0.mp3; sourceTree = ""; }; @@ -237,8 +243,9 @@ A70171BA2AB4980100064C43 /* Views */ = { isa = PBXGroup; children = ( - A7FB0D782B25C65200A3469E /* Onboarding */, A70171832AA8E71900064C43 /* MainView.swift */, + A977D0DC2B6002DA009783A9 /* Tabs */, + A7FB0D782B25C65200A3469E /* Onboarding */, A70171BD2AB4987900064C43 /* CategoryDetailView.swift */, A70171C12AB498C600064C43 /* RecipeCardView.swift */, A70171BF2AB498A900064C43 /* RecipeDetailView.swift */, @@ -318,6 +325,16 @@ path = Onboarding; sourceTree = ""; }; + A977D0DC2B6002DA009783A9 /* Tabs */ = { + isa = PBXGroup; + children = ( + A977D0DD2B600300009783A9 /* SearchTabView.swift */, + A977D0DF2B600318009783A9 /* RecipeTabView.swift */, + A977D0E12B60034E009783A9 /* GroceryListTabView.swift */, + ); + path = Tabs; + sourceTree = ""; + }; A9CA6CED2B4C084100F78AB5 /* RecipeExport */ = { isa = PBXGroup; children = ( @@ -481,6 +498,7 @@ A79AA8E22AFF8C14007D25F2 /* RecipeEditViewModel.swift in Sources */, A7FB0D7C2B25C68500A3469E /* TokenLoginView.swift in Sources */, A70D7CA12AC73CA800D53DBF /* RecipeEditView.swift in Sources */, + A977D0E22B60034E009783A9 /* GroceryListTabView.swift in Sources */, A70171B12AB211DF00064C43 /* CustomError.swift in Sources */, A7FB0D7A2B25C66600A3469E /* OnboardingView.swift in Sources */, A76B8A712AE002AE00096CEC /* Alerts.swift in Sources */, @@ -505,12 +523,14 @@ A70171C22AB498C600064C43 /* RecipeCardView.swift in Sources */, A70171842AA8E71900064C43 /* MainView.swift in Sources */, A70171CB2AB4CD1700064C43 /* UserSettings.swift in Sources */, + A977D0DE2B600300009783A9 /* SearchTabView.swift in Sources */, A703226A2ABAF49800D7C4ED /* JSONCoderExtension.swift in Sources */, A79AA8ED2B063AD5007D25F2 /* NextcloudApi.swift in Sources */, A70171822AA8E71900064C43 /* Nextcloud_Cookbook_iOS_ClientApp.swift in Sources */, A74D33C32AFCD1C300D06555 /* RecipeScraper.swift in Sources */, A70171AD2AA8EF4700064C43 /* MainViewModel.swift in Sources */, A76B8A6F2ADFFA8800096CEC /* SupportedLanguage.swift in Sources */, + A977D0E02B600318009783A9 /* RecipeTabView.swift in Sources */, A703226F2ABB1DD700D7C4ED /* ColorExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate index 6cfcb9b26efb68f9eec809b50687855463c39d03..432e8fb97725d96c2b51a4c489219df6c76375e7 100644 GIT binary patch literal 68056 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
<9hw(k$N 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 diff --git a/Nextcloud Cookbook iOS Client/Localizable.xcstrings b/Nextcloud Cookbook iOS Client/Localizable.xcstrings index 4454279..635d000 100644 --- a/Nextcloud Cookbook iOS Client/Localizable.xcstrings +++ b/Nextcloud Cookbook iOS Client/Localizable.xcstrings @@ -402,6 +402,9 @@ } } } + }, + "Add groceries to this list by either using the button next to an ingredient list in a recipe, or by swiping right on individual ingredients of a recipe." : { + }, "Add new recipe" : { "localizations" : { @@ -1241,6 +1244,9 @@ } } } + }, + "Grocery List" : { + }, "If 'Same as Device' is selected and your device language is not supported yet, this option will default to english." : { "localizations" : { @@ -3161,6 +3167,9 @@ } } } + }, + "You're all set for cooking 🍓" : { + } }, "version" : "1.0" diff --git a/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift b/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift index cf25dda..1c204a7 100644 --- a/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift +++ b/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift @@ -11,8 +11,6 @@ import UIKit @MainActor class MainViewModel: ObservableObject { - @ObservedObject var userSettings = UserSettings.shared - @Published var categories: [Category] = [] @Published var recipes: [String: [Recipe]] = [:] @Published var recipeDetails: [Int: RecipeDetail] = [:] @@ -30,10 +28,10 @@ import UIKit self.api = api self.dataStore = DataStore() - if userSettings.authString == "" { - let loginString = "\(userSettings.username):\(userSettings.token)" + if UserSettings.shared.authString == "" { + let loginString = "\(UserSettings.shared.username):\(UserSettings.shared.token)" let loginData = loginString.data(using: String.Encoding.utf8)! - userSettings.authString = loginData.base64EncodedString() + UserSettings.shared.authString = loginData.base64EncodedString() } } @@ -51,7 +49,7 @@ import UIKit */ func getCategories() async { let (categories, _) = await api.getCategories( - auth: userSettings.authString + auth: UserSettings.shared.authString ) if let categories = categories { print("Successfully loaded categories") @@ -99,7 +97,7 @@ import UIKit func getServer(store: Bool = false) async -> Bool { let (recipes, _) = await api.getCategory( - auth: userSettings.authString, + auth: UserSettings.shared.authString, named: categoryString ) if let recipes = recipes { @@ -132,16 +130,16 @@ import UIKit for category in self.categories { await updateRecipeDetails(in: category.name) } - userSettings.lastUpdate = Date() + UserSettings.shared.lastUpdate = Date() } func updateRecipeDetails(in category: String) async { - guard userSettings.storeRecipes else { return } + guard UserSettings.shared.storeRecipes else { return } guard let recipes = self.recipes[category] else { return } for recipe in recipes { if needsUpdate(category: category, lastModified: recipe.dateModified) { print("\(recipe.name) needs an update. (last modified: \(recipe.dateModified)") - await updateRecipeDetail(id: recipe.recipe_id, withThumb: userSettings.storeThumb, withImage: userSettings.storeImages) + await updateRecipeDetail(id: recipe.recipe_id, withThumb: UserSettings.shared.storeThumb, withImage: UserSettings.shared.storeImages) } else { print("\(recipe.name) is up to date.") } @@ -161,7 +159,7 @@ import UIKit */ func getRecipes() async -> [Recipe] { let (recipes, error) = await api.getRecipes( - auth: userSettings.authString + auth: UserSettings.shared.authString ) if let recipes = recipes { return recipes @@ -201,7 +199,7 @@ import UIKit func getServer() async -> RecipeDetail? { let (recipe, error) = await api.getRecipe( - auth: userSettings.authString, + auth: UserSettings.shared.authString, id: id ) if let recipe = recipe { @@ -294,7 +292,7 @@ import UIKit func getServer() async -> UIImage? { let (image, _) = await api.getImage( - auth: userSettings.authString, + auth: UserSettings.shared.authString, id: id, size: size ) @@ -370,7 +368,7 @@ import UIKit func getServer() async -> [RecipeKeyword]? { let (tags, _) = await api.getTags( - auth: userSettings.authString + auth: UserSettings.shared.authString ) return tags } @@ -423,7 +421,7 @@ import UIKit */ func deleteRecipe(withId id: Int, categoryName: String) async -> RequestAlert? { let (error) = await api.deleteRecipe( - auth: userSettings.authString, + auth: UserSettings.shared.authString, id: id ) @@ -454,7 +452,7 @@ import UIKit */ func checkServerConnection() async -> Bool { let (categories, _) = await api.getCategories( - auth: userSettings.authString + auth: UserSettings.shared.authString ) if let categories = categories { self.categories = categories @@ -483,12 +481,12 @@ import UIKit var error: NetworkError? = nil if createNew { error = await api.createRecipe( - auth: userSettings.authString, + auth: UserSettings.shared.authString, recipe: recipeDetail ) } else { error = await api.updateRecipe( - auth: userSettings.authString, + auth: UserSettings.shared.authString, recipe: recipeDetail ) } @@ -501,7 +499,7 @@ import UIKit func importRecipe(url: String) async -> (RecipeDetail?, RequestAlert?) { guard let data = JSONEncoder.safeEncode(RecipeImportRequest(url: url)) else { return (nil, .REQUEST_DROPPED) } let (recipeDetail, error) = await api.importRecipe( - auth: userSettings.authString, + auth: UserSettings.shared.authString, data: data ) if error != nil { @@ -509,6 +507,7 @@ import UIKit } return (recipeDetail, nil) } + } diff --git a/Nextcloud Cookbook iOS Client/Views/MainView.swift b/Nextcloud Cookbook iOS Client/Views/MainView.swift index 17e1794..e8a8c00 100644 --- a/Nextcloud Cookbook iOS Client/Views/MainView.swift +++ b/Nextcloud Cookbook iOS Client/Views/MainView.swift @@ -7,8 +7,66 @@ import SwiftUI - struct MainView: View { + @StateObject var viewModel = MainViewModel() + @State var selectedCategory: Category? = nil + @State var showLoadingIndicator: Bool = false + + enum Tab { + case recipes, search, shoppingList, settings + } + + var body: some View { + TabView { + RecipeTabView(selectedCategory: $selectedCategory, showLoadingIndicator: $showLoadingIndicator) + .environmentObject(viewModel) + .tabItem { + Label("Recipes", systemImage: "book.closed.fill") + } + .tag(Tab.recipes) + + SearchTabView() + .environmentObject(viewModel) + .tabItem { + Label("Search", systemImage: "magnifyingglass") + } + .tag(Tab.search) + + GroceryListTabView() + .tabItem { + Label("Grocery List", systemImage: "storefront") + } + .tag(Tab.shoppingList) + + SettingsView() + .environmentObject(viewModel) + .tabItem { + Label("Settings", systemImage: "gearshape") + } + .tag(Tab.settings) + } + .task { + showLoadingIndicator = true + await viewModel.getCategories() + await viewModel.updateAllRecipeDetails() + + // Open detail view for default category + if UserSettings.shared.defaultCategory != "" { + if let cat = viewModel.categories.first(where: { c in + if c.name == UserSettings.shared.defaultCategory { + return true + } + return false + }) { + self.selectedCategory = cat + } + } + showLoadingIndicator = false + await GroceryList.shared.load() + } + } +} +/*struct MainView: View { @ObservedObject var viewModel: MainViewModel @StateObject var userSettings: UserSettings = UserSettings.shared @@ -214,43 +272,5 @@ struct MainView: View { -struct RecipeSearchView: View { - @ObservedObject var viewModel: MainViewModel - @State var searchText: String = "" - @State var allRecipes: [Recipe] = [] - - var body: some View { - NavigationStack { - VStack { - ScrollView(showsIndicators: false) { - LazyVStack { - ForEach(recipesFiltered(), id: \.recipe_id) { recipe in - NavigationLink(value: recipe) { - RecipeCardView(viewModel: viewModel, recipe: recipe) - .shadow(radius: 2) - } - .buttonStyle(.plain) - } - } - } - .navigationDestination(for: Recipe.self) { recipe in - RecipeDetailView(viewModel: viewModel, recipe: recipe) - } - .searchable(text: $searchText, prompt: "Search recipes/keywords") - } - .navigationTitle("Search recipe") - } - .task { - allRecipes = await viewModel.getRecipes() - } - } - - func recipesFiltered() -> [Recipe] { - guard searchText != "" else { return allRecipes } - return allRecipes.filter { recipe in - recipe.name.lowercased().contains(searchText.lowercased()) || // check name for occurence of search term - (recipe.keywords != nil && recipe.keywords!.lowercased().contains(searchText.lowercased())) // check keywords for search term - } - } -} +*/ diff --git a/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift b/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift index fdf2bc9..575b515 100644 --- a/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift +++ b/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift @@ -364,10 +364,17 @@ fileprivate struct RecipeIngredientSection: View { SecondaryLabel(text: LocalizedStringKey("Ingredients for \(recipeDetail.recipeYield) servings")) } Spacer() + Button { + GroceryList.shared.addItems(recipeDetail.recipeIngredient) + } label: { + Image(systemName: "storefront") + } } + ForEach(recipeDetail.recipeIngredient, id: \.self) { ingredient in IngredientListItem(ingredient: ingredient) .padding(4) + } }.padding() } @@ -392,9 +399,17 @@ fileprivate struct RecipeToolSection: View { fileprivate struct IngredientListItem: View { @State var ingredient: String @State var isSelected: Bool = false + @State private var dragOffset: CGFloat = 0 + let maxDragDistance = 30.0 var body: some View { HStack(alignment: .top) { + if dragOffset > 0 { + Image(systemName: "storefront") + .padding(2) + .background(Color.green) + .opacity((dragOffset - 10)/(maxDragDistance-10)) + } if isSelected { Image(systemName: "checkmark.circle") } else { @@ -404,12 +419,31 @@ fileprivate struct IngredientListItem: View { Text("\(ingredient)") .multilineTextAlignment(.leading) .lineLimit(5) + Spacer() } .foregroundStyle(isSelected ? Color.secondary : Color.primary) .onTapGesture { isSelected.toggle() } .animation(.easeInOut, value: isSelected) + .offset(x: dragOffset, y: 0) + .gesture( + DragGesture() + .onChanged { gesture in + // Update drag offset as the user drags + let dragAmount = gesture.translation.width + self.dragOffset = min(dragAmount, maxDragDistance + pow(dragAmount - maxDragDistance, 0.7)) + } + .onEnded { gesture in + if gesture.translation.width > maxDragDistance * 0.8 { // Swipe right threshold + GroceryList.shared.addItem(ingredient) + } + // Animate back to original position + withAnimation { + self.dragOffset = 0 + } + } + ) } } diff --git a/Nextcloud Cookbook iOS Client/Views/SettingsView.swift b/Nextcloud Cookbook iOS Client/Views/SettingsView.swift index 9314a07..902f3d4 100644 --- a/Nextcloud Cookbook iOS Client/Views/SettingsView.swift +++ b/Nextcloud Cookbook iOS Client/Views/SettingsView.swift @@ -11,7 +11,7 @@ import SwiftUI struct SettingsView: View { - @ObservedObject var viewModel: MainViewModel + @EnvironmentObject var viewModel: MainViewModel @ObservedObject var userSettings = UserSettings.shared @State fileprivate var alertType: SettingsAlert = .NONE diff --git a/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift b/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift new file mode 100644 index 0000000..0074325 --- /dev/null +++ b/Nextcloud Cookbook iOS Client/Views/Tabs/GroceryListTabView.swift @@ -0,0 +1,118 @@ +// +// GroceryListTabView.swift +// Nextcloud Cookbook iOS Client +// +// Created by Vincent Meilinger on 23.01.24. +// + +import Foundation +import SwiftUI + + +struct GroceryListTabView: View { + var body: some View { + NavigationStack { + if GroceryList.shared.listItems.isEmpty { + List { + Text("You're all set for cooking 🍓") + .font(.title2) + .foregroundStyle(.secondary) + Text("Add groceries to this list by either using the button next to an ingredient list in a recipe, or by swiping right on individual ingredients of a recipe.") + + .foregroundStyle(.secondary) + } + .padding() + .navigationTitle("Grocery List") + } else { + List(GroceryList.shared.listItems) { item in + HStack(alignment: .top) { + if item.isChecked { + Image(systemName: "checkmark.circle") + } else { + Image(systemName: "circle") + } + + Text("\(item.name)") + .multilineTextAlignment(.leading) + .lineLimit(5) + } + .foregroundStyle(item.isChecked ? Color.secondary : Color.primary) + .onTapGesture { + item.isChecked.toggle() + } + .animation(.easeInOut, value: item.isChecked) + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button { + GroceryList.shared.removeItem(item.name) + } label: { + Label("Delete", systemImage: "trash") + } + .tint(.red) + } + } + .padding() + .navigationTitle("Grocery List") + } + + } + } +} + + + +class GroceryListItem: ObservableObject, Identifiable, Codable { + var name: String + var isChecked: Bool + + init(_ name: String, isChecked: Bool = false) { + self.name = name + self.isChecked = isChecked + } +} + + + +class GroceryList: ObservableObject { + static let shared: GroceryList = GroceryList() + + let dataStore: DataStore = DataStore() + @Published var listItems: [GroceryListItem] = [] + + + func addItem(_ name: String) { + listItems.append(GroceryListItem(name)) + save() + } + + func addItems(_ items: [String]) { + for item in items { + addItem(item) + } + save() + } + + func removeItem(_ name: String) { + guard let ix = listItems.firstIndex(where: { item in + item.name == name + }) else { return } + listItems.remove(at: ix) + save() + } + + func save() { + Task { + await dataStore.save(data: listItems, toPath: "grocery_list.data") + } + } + + func load() async { + do { + guard let listItems: [GroceryListItem] = try await dataStore.load( + fromPath: "grocery_list.data" + ) else { return } + self.listItems = listItems + } catch { + print("Unable to load grocery list") + } + } +} diff --git a/Nextcloud Cookbook iOS Client/Views/Tabs/RecipeTabView.swift b/Nextcloud Cookbook iOS Client/Views/Tabs/RecipeTabView.swift new file mode 100644 index 0000000..3ee7ff7 --- /dev/null +++ b/Nextcloud Cookbook iOS Client/Views/Tabs/RecipeTabView.swift @@ -0,0 +1,166 @@ +// +// RecipeTabView.swift +// Nextcloud Cookbook iOS Client +// +// Created by Vincent Meilinger on 23.01.24. +// + +import Foundation +import SwiftUI + + +struct RecipeTabView: View { + @Binding var selectedCategory: Category? + @Binding var showLoadingIndicator: Bool + + @EnvironmentObject var viewModel: MainViewModel + @StateObject var userSettings: UserSettings = UserSettings.shared + + @State private var showEditView: Bool = false + @State private var serverConnection: Bool = false + + + var body: some View { + NavigationSplitView { + List(selection: $selectedCategory) { + // Categories + ForEach(viewModel.categories) { category in + if category.recipe_count != 0 { + NavigationLink(value: category) { + HStack(alignment: .center) { + if selectedCategory != nil && category.name == selectedCategory!.name { + Image(systemName: "book") + } else { + Image(systemName: "book.closed.fill") + } + Text(category.name == "*" ? String(localized: "Other") : category.name) + .font(.system(size: 20, weight: .medium, design: .default)) + Spacer() + Text("\(category.recipe_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) + } + } + } + } + .navigationTitle("Cookbooks") + .toolbar { + RecipeTabViewToolBar( + viewModel: viewModel, + showEditView: $showEditView, + serverConnection: $serverConnection, + showLoadingIndicator: $showLoadingIndicator + ) + } + } detail: { + NavigationStack { + if let category = selectedCategory { + CategoryDetailView( + categoryName: category.name, + viewModel: viewModel, + showEditView: $showEditView + ) + .id(category.id) // Workaround: This is needed to update the detail view when the selection changes + } + } + } + .tint(.nextcloudBlue) + .sheet(isPresented: $showEditView) { + RecipeEditView( + viewModel: + RecipeEditViewModel( + mainViewModel: viewModel, + uploadNew: true + ), + isPresented: $showEditView + ) + } + .task { + self.serverConnection = await viewModel.checkServerConnection() + } + .refreshable { + self.serverConnection = await viewModel.checkServerConnection() + await viewModel.getCategories() + } + } +} + + +fileprivate struct RecipeTabViewToolBar: ToolbarContent { + @ObservedObject var viewModel: MainViewModel + @Binding var showEditView: Bool + @Binding var serverConnection: Bool + @Binding var showLoadingIndicator: Bool + @State private var presentPopover: Bool = false + + var body: some ToolbarContent { + // Top left menu toolbar item + ToolbarItem(placement: .topBarLeading) { + Menu { + Button { + Task { + showLoadingIndicator = true + UserSettings.shared.lastUpdate = Date.distantPast + await viewModel.getCategories() + for category in viewModel.categories { + await viewModel.getCategory(named: category.name, fetchMode: .preferServer) + } + await viewModel.updateAllRecipeDetails() + showLoadingIndicator = false + } + } label: { + Text("Refresh all") + Image(systemName: "icloud.and.arrow.down") + } + + } label: { + Image(systemName: "ellipsis.circle") + } + } + + // Server connection indicator + ToolbarItem(placement: .topBarTrailing) { + Button { + print("Check server connection") + presentPopover = true + } label: { + if showLoadingIndicator { + ProgressView() + } else if serverConnection { + Image(systemName: "checkmark.icloud") + } else { + Image(systemName: "xmark.icloud") + } + }.popover(isPresented: $presentPopover) { + VStack(alignment: .leading) { + Text(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") + showEditView = true + } label: { + Image(systemName: "plus.circle.fill") + } + } + + } +} diff --git a/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift b/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift new file mode 100644 index 0000000..96add22 --- /dev/null +++ b/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift @@ -0,0 +1,58 @@ +// +// SearchTabView.swift +// Nextcloud Cookbook iOS Client +// +// Created by Vincent Meilinger on 23.01.24. +// + +import Foundation +import SwiftUI + + +struct SearchTabView: View { + @EnvironmentObject var viewModel: MainViewModel + + var body: some View { + RecipeSearchView(viewModel: viewModel) + } +} + +struct RecipeSearchView: View { + @ObservedObject var viewModel: MainViewModel + @State var searchText: String = "" + @State var allRecipes: [Recipe] = [] + + var body: some View { + NavigationStack { + VStack { + ScrollView(showsIndicators: false) { + LazyVStack { + ForEach(recipesFiltered(), id: \.recipe_id) { recipe in + NavigationLink(value: recipe) { + RecipeCardView(viewModel: viewModel, recipe: recipe) + .shadow(radius: 2) + } + .buttonStyle(.plain) + } + } + } + .navigationDestination(for: Recipe.self) { recipe in + RecipeDetailView(viewModel: viewModel, recipe: recipe) + } + .searchable(text: $searchText, prompt: "Search recipes/keywords") + } + .navigationTitle("Search recipe") + } + .task { + allRecipes = await viewModel.getRecipes() + } + } + + func recipesFiltered() -> [Recipe] { + guard searchText != "" else { return allRecipes } + return allRecipes.filter { recipe in + recipe.name.lowercased().contains(searchText.lowercased()) || // check name for occurence of search term + (recipe.keywords != nil && recipe.keywords!.lowercased().contains(searchText.lowercased())) // check keywords for search term + } + } +}