From 4c2b459683c6d28493be25cd49e72a85b5aab2da Mon Sep 17 00:00:00 2001 From: VincentMeilinger Date: Sat, 3 Feb 2024 10:07:15 +0100 Subject: [PATCH] Bug fixes --- .../project.pbxproj | 25 ++- .../xcshareddata/swiftpm/Package.resolved | 9 + .../UserInterfaceState.xcuserstate | Bin 69422 -> 112362 bytes .../Data/DataStore.swift | 23 +- .../Data/UserSettings.swift | 16 +- .../{ViewModels => Models}/AppState.swift | 1 - .../RecipeEditViewModel.swift | 0 .../{CookbookApi => }/ApiRequest.swift | 10 +- .../Network/CookbookApi/CookbookApi.swift | 19 ++ .../Network/CookbookApi/CookbookApiV1.swift | 26 ++- .../Network/NextcloudApi/NextcloudApi.swift | 66 ++++++ .../Views/MainView.swift | 209 +----------------- .../Views/Onboarding/TokenLoginView.swift | 41 +--- .../Views/Onboarding/V2LoginView.swift | 97 +++----- .../Views/Tabs/SearchTabView.swift | 30 ++- 15 files changed, 230 insertions(+), 342 deletions(-) rename Nextcloud Cookbook iOS Client/{ViewModels => Models}/AppState.swift (99%) rename Nextcloud Cookbook iOS Client/{ViewModels => Models}/RecipeEditViewModel.swift (100%) rename Nextcloud Cookbook iOS Client/Network/{CookbookApi => }/ApiRequest.swift (90%) diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj b/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj index fc88f53..f1af7c4 100644 --- a/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj +++ b/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 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 */; }; + A99DC7BC2B6411A7000118AA /* SimilaritySearchKit in Frameworks */ = {isa = PBXBuildFile; productRef = A99DC7BB2B6411A7000118AA /* SimilaritySearchKit */; }; 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 */; }; @@ -131,6 +132,7 @@ buildActionMask = 2147483647; files = ( A74D33BE2AF82AAE00D06555 /* SwiftSoup in Frameworks */, + A99DC7BC2B6411A7000118AA /* SimilaritySearchKit in Frameworks */, A9CA6CF62B4C63F200F78AB5 /* TPPDF in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -180,7 +182,7 @@ A70171812AA8E71900064C43 /* Nextcloud_Cookbook_iOS_ClientApp.swift */, A70171C72AB4C4A100064C43 /* Data */, A70171BA2AB4980100064C43 /* Views */, - A70171B72AB2445700064C43 /* ViewModels */, + A70171B72AB2445700064C43 /* Models */, A70171B22AB211F000064C43 /* Network */, A781E75F2AF8228100452F6F /* RecipeImport */, A9CA6CED2B4C084100F78AB5 /* RecipeExport */, @@ -223,6 +225,7 @@ A70171B22AB211F000064C43 /* Network */ = { isa = PBXGroup; children = ( + A79AA8EA2B062E15007D25F2 /* ApiRequest.swift */, A79AA8EE2B063B33007D25F2 /* NextcloudApi */, A79AA8E72B062DB6007D25F2 /* CookbookApi */, A70171B32AB2122900064C43 /* NetworkRequests.swift */, @@ -232,13 +235,13 @@ path = Network; sourceTree = ""; }; - A70171B72AB2445700064C43 /* ViewModels */ = { + A70171B72AB2445700064C43 /* Models */ = { isa = PBXGroup; children = ( A70171AC2AA8EF4700064C43 /* AppState.swift */, A79AA8E12AFF8C14007D25F2 /* RecipeEditViewModel.swift */, ); - path = ViewModels; + path = Models; sourceTree = ""; }; A70171BA2AB4980100064C43 /* Views */ = { @@ -295,7 +298,6 @@ A79AA8E72B062DB6007D25F2 /* CookbookApi */ = { isa = PBXGroup; children = ( - A79AA8EA2B062E15007D25F2 /* ApiRequest.swift */, A79AA8E32B02A961007D25F2 /* CookbookApi.swift */, A79AA8E82B062DD1007D25F2 /* CookbookApiV1.swift */, ); @@ -394,6 +396,7 @@ packageProductDependencies = ( A74D33BD2AF82AAE00D06555 /* SwiftSoup */, A9CA6CF52B4C63F200F78AB5 /* TPPDF */, + A99DC7BB2B6411A7000118AA /* SimilaritySearchKit */, ); productName = "Nextcloud Cookbook iOS Client"; productReference = A701717E2AA8E71900064C43 /* Nextcloud Cookbook iOS Client.app */; @@ -473,6 +476,7 @@ packageReferences = ( A74D33BC2AF82AAE00D06555 /* XCRemoteSwiftPackageReference "SwiftSoup" */, A9CA6CF42B4C63F200F78AB5 /* XCRemoteSwiftPackageReference "TPPDF" */, + A99DC7BA2B6411A7000118AA /* XCRemoteSwiftPackageReference "similarity-search-kit" */, ); productRefGroup = A701717F2AA8E71900064C43 /* Products */; projectDirPath = ""; @@ -937,6 +941,14 @@ minimumVersion = 2.6.1; }; }; + A99DC7BA2B6411A7000118AA /* XCRemoteSwiftPackageReference "similarity-search-kit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ZachNagengast/similarity-search-kit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.0.13; + }; + }; A9CA6CF42B4C63F200F78AB5 /* XCRemoteSwiftPackageReference "TPPDF" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/techprimate/TPPDF.git"; @@ -953,6 +965,11 @@ package = A74D33BC2AF82AAE00D06555 /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; + A99DC7BB2B6411A7000118AA /* SimilaritySearchKit */ = { + isa = XCSwiftPackageProductDependency; + package = A99DC7BA2B6411A7000118AA /* XCRemoteSwiftPackageReference "similarity-search-kit" */; + productName = SimilaritySearchKit; + }; A9CA6CF52B4C63F200F78AB5 /* TPPDF */ = { isa = XCSwiftPackageProductDependency; package = A9CA6CF42B4C63F200F78AB5 /* XCRemoteSwiftPackageReference "TPPDF" */; diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d488c1a..7f6ada7 100644 --- a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "similarity-search-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ZachNagengast/similarity-search-kit", + "state" : { + "revision" : "ddc8e458d0e826b2fe5dbce6f6eac96a8935e8eb", + "version" : "0.0.13" + } + }, { "identity" : "swiftsoup", "kind" : "remoteSourceControl", 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 e13864d3dabdf1a7083258ad20b43ae584f08654..564cf0fc2697a9e1fbda4faf782ccd3a766e10fc 100644 GIT binary patch literal 112362 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�nNnkNN7J1WhmcyN_;D#b%u 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 diff --git a/Nextcloud Cookbook iOS Client/Data/DataStore.swift b/Nextcloud Cookbook iOS Client/Data/DataStore.swift index 66fbd1e..26472cb 100644 --- a/Nextcloud Cookbook iOS Client/Data/DataStore.swift +++ b/Nextcloud Cookbook iOS Client/Data/DataStore.swift @@ -10,7 +10,7 @@ import SwiftUI class DataStore { let fileManager = FileManager.default - + static let shared = DataStore() private static func fileURL(appending: String) throws -> URL { try FileManager.default.url( @@ -87,7 +87,28 @@ class DataStore { return true } +} + +// SimilarityIndex loading and saving +import SimilaritySearchKit +extension DataStore { + func loadIndex() async -> [IndexItem]? { + do { + let indexItems = try await SimilarityIndex().loadIndex(fromDirectory: Self.fileURL(appending: "similarity_index")) + return indexItems + } catch { + print("Unable to load SimilarityIndex") + return nil + } + } + func saveIndex(_ index: SimilarityIndex) { + do { + try index.saveIndex(toDirectory: Self.fileURL(appending: "similarity_index")) + } catch { + print("Unable to save SimilarityIndex") + } + } } diff --git a/Nextcloud Cookbook iOS Client/Data/UserSettings.swift b/Nextcloud Cookbook iOS Client/Data/UserSettings.swift index 0543b7a..de942c4 100644 --- a/Nextcloud Cookbook iOS Client/Data/UserSettings.swift +++ b/Nextcloud Cookbook iOS Client/Data/UserSettings.swift @@ -12,7 +12,7 @@ import Combine class UserSettings: ObservableObject { static let shared = UserSettings() - + @Published var username: String { didSet { UserDefaults.standard.set(username, forKey: "username") @@ -43,6 +43,12 @@ class UserSettings: ObservableObject { } } + @Published var cookbookApiVersion: CookbookApiVersion { + didSet { + UserDefaults.standard.set(cookbookApiVersion, forKey: "cookbookApiVersion") + } + } + @Published var onboarding: Bool { didSet { UserDefaults.standard.set(onboarding, forKey: "onboarding") @@ -115,6 +121,7 @@ class UserSettings: ObservableObject { self.authString = UserDefaults.standard.object(forKey: "authString") as? String ?? "" self.serverAddress = UserDefaults.standard.object(forKey: "serverAddress") as? String ?? "" self.serverProtocol = UserDefaults.standard.object(forKey: "serverProtocol") as? String ?? "https://" + self.cookbookApiVersion = UserDefaults.standard.object(forKey: "cookbookApiVersion") as? CookbookApiVersion ?? .v1 self.onboarding = UserDefaults.standard.object(forKey: "onboarding") as? Bool ?? true self.defaultCategory = UserDefaults.standard.object(forKey: "defaultCategory") as? String ?? "" self.language = UserDefaults.standard.object(forKey: "language") as? String ?? SupportedLanguage.DEVICE.rawValue @@ -134,15 +141,14 @@ class UserSettings: ObservableObject { authString = loginData.base64EncodedString() } } + } - func setAuthString() -> String { + func setAuthString() { if token != "" && username != "" { let loginString = "\(self.username):\(self.token)" let loginData = loginString.data(using: String.Encoding.utf8)! - return loginData.base64EncodedString() - } else { - return "" + self.authString = loginData.base64EncodedString() } } } diff --git a/Nextcloud Cookbook iOS Client/ViewModels/AppState.swift b/Nextcloud Cookbook iOS Client/Models/AppState.swift similarity index 99% rename from Nextcloud Cookbook iOS Client/ViewModels/AppState.swift rename to Nextcloud Cookbook iOS Client/Models/AppState.swift index b1278c9..13d1e7b 100644 --- a/Nextcloud Cookbook iOS Client/ViewModels/AppState.swift +++ b/Nextcloud Cookbook iOS Client/Models/AppState.swift @@ -19,7 +19,6 @@ import UIKit var imagesNeedUpdate: [Int: [String: Bool]] = [:] var lastUpdates: [String: Date] = [:] - private let api: CookbookApi.Type private let dataStore: DataStore diff --git a/Nextcloud Cookbook iOS Client/ViewModels/RecipeEditViewModel.swift b/Nextcloud Cookbook iOS Client/Models/RecipeEditViewModel.swift similarity index 100% rename from Nextcloud Cookbook iOS Client/ViewModels/RecipeEditViewModel.swift rename to Nextcloud Cookbook iOS Client/Models/RecipeEditViewModel.swift diff --git a/Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift b/Nextcloud Cookbook iOS Client/Network/ApiRequest.swift similarity index 90% rename from Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift rename to Nextcloud Cookbook iOS Client/Network/ApiRequest.swift index 8056637..fd95965 100644 --- a/Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift +++ b/Nextcloud Cookbook iOS Client/Network/ApiRequest.swift @@ -16,7 +16,6 @@ struct ApiRequest { let body: Data? /// The path to the Cookbook application on the nextcloud server. - let cookbookPath = "/index.php/apps/cookbook" init( path: String, @@ -32,11 +31,11 @@ struct ApiRequest { self.body = body } - func send() async -> (Data?, NetworkError?) { + func send(pathCompletion: Bool = true) async -> (Data?, NetworkError?) { Logger.network.debug("\(method.rawValue) \(path) sending ...") // Prepare URL - let urlString = UserSettings.shared.serverProtocol + UserSettings.shared.serverAddress + cookbookPath + path + let urlString = pathCompletion ? UserSettings.shared.serverProtocol + UserSettings.shared.serverAddress + path : path print("Full path: \(urlString)") //Logger.network.debug("Full path: \(urlString)") guard let urlStringSanitized = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, .unknownError) } @@ -78,9 +77,10 @@ struct ApiRequest { return (nil, error) } if let data = data { - print(data, String(data: data, encoding: .utf8)) + print(data, String(data: data, encoding: .utf8) as Any) + return (data, nil) } - return (data!, nil) + return (nil, .unknownError) } catch { let error = decodeURLResponse(response: response as? HTTPURLResponse) Logger.network.debug("\(method.rawValue) \(path) FAILURE: \(error.debugDescription)") diff --git a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift index 7baed46..8b83fe4 100644 --- a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift +++ b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift @@ -10,7 +10,26 @@ import OSLog import UIKit +/// The Cookbook API class used for requests to the Nextcloud Cookbook service. +let cookbookApi: CookbookApi.Type = getApi() + +func getApi() -> CookbookApi.Type { + switch UserSettings.shared.cookbookApiVersion { + case .v1: + return CookbookApiV1.self + } +} + +/// The Cookbook API version. +enum CookbookApiVersion: String { + case v1 = "v1" +} + + +/// A protocol defining common API endpoints that are likely to remain the same over future Cookbook API versions. protocol CookbookApi { + static var basePath: String { get } + /// Not implemented yet. static func importRecipe( auth: String, diff --git a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift index e4f4a0e..cbe6b59 100644 --- a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift +++ b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift @@ -10,9 +10,11 @@ import UIKit class CookbookApiV1: CookbookApi { + static let basePath: String = "/index.php/apps/cookbook/api/v1" + static func importRecipe(auth: String, data: Data) async -> (RecipeDetail?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/import", + path: basePath + "/import", method: .POST, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON), HeaderField.contentType(value: .JSON)] @@ -26,7 +28,7 @@ class CookbookApiV1: CookbookApi { static func getImage(auth: String, id: Int, size: RecipeImage.RecipeImageSize) async -> (UIImage?, NetworkError?) { let imageSize = (size == .FULL ? "full" : "thumb") let request = ApiRequest( - path: "/api/v1/recipes/\(id)/image?size=\(imageSize)", + path: basePath + "/recipes/\(id)/image?size=\(imageSize)", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .IMAGE)] @@ -39,7 +41,7 @@ class CookbookApiV1: CookbookApi { static func getRecipes(auth: String) async -> ([Recipe]?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/recipes", + path: basePath + "/recipes", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -56,7 +58,7 @@ class CookbookApiV1: CookbookApi { } let request = ApiRequest( - path: "/api/v1/recipes", + path: basePath + "/recipes", method: .POST, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON), HeaderField.contentType(value: .JSON)], @@ -80,7 +82,7 @@ class CookbookApiV1: CookbookApi { static func getRecipe(auth: String, id: Int) async -> (RecipeDetail?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/recipes/\(id)", + path: basePath + "/recipes/\(id)", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -96,7 +98,7 @@ class CookbookApiV1: CookbookApi { return .dataError } let request = ApiRequest( - path: "/api/v1/recipes/\(recipe.id)", + path: basePath + "/recipes/\(recipe.id)", method: .PUT, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON), HeaderField.contentType(value: .JSON)], @@ -120,7 +122,7 @@ class CookbookApiV1: CookbookApi { static func deleteRecipe(auth: String, id: Int) async -> (NetworkError?) { let request = ApiRequest( - path: "/api/v1/recipes/\(id)", + path: basePath + "/recipes/\(id)", method: .DELETE, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -133,7 +135,7 @@ class CookbookApiV1: CookbookApi { static func getCategories(auth: String) async -> ([Category]?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/categories", + path: basePath + "/categories", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -146,7 +148,7 @@ class CookbookApiV1: CookbookApi { static func getCategory(auth: String, named categoryName: String) async -> ([Recipe]?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/category/\(categoryName)", + path: basePath + "/category/\(categoryName)", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -159,7 +161,7 @@ class CookbookApiV1: CookbookApi { static func renameCategory(auth: String, named categoryName: String, newName: String) async -> (NetworkError?) { let request = ApiRequest( - path: "/api/v1/category/\(categoryName)", + path: basePath + "/category/\(categoryName)", method: .PUT, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -172,7 +174,7 @@ class CookbookApiV1: CookbookApi { static func getTags(auth: String) async -> ([RecipeKeyword]?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/keywords", + path: basePath + "/keywords", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] @@ -185,7 +187,7 @@ class CookbookApiV1: CookbookApi { static func getRecipesTagged(auth: String, keyword: String) async -> ([Recipe]?, NetworkError?) { let request = ApiRequest( - path: "/api/v1/tags/\(keyword)", + path: basePath + "/tags/\(keyword)", method: .GET, authString: auth, headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)] diff --git a/Nextcloud Cookbook iOS Client/Network/NextcloudApi/NextcloudApi.swift b/Nextcloud Cookbook iOS Client/Network/NextcloudApi/NextcloudApi.swift index d5caf9d..93a9875 100644 --- a/Nextcloud Cookbook iOS Client/Network/NextcloudApi/NextcloudApi.swift +++ b/Nextcloud Cookbook iOS Client/Network/NextcloudApi/NextcloudApi.swift @@ -7,6 +7,72 @@ import Foundation +/// The `NextcloudApi` class provides functionalities to interact with the Nextcloud API, particularly for user authentication. class NextcloudApi { + /// Initiates the login process with Nextcloud using the Login Flow v2. + /// + /// This static function sends a POST request to the Nextcloud server to obtain a `LoginV2Request` object. + /// The object contains necessary details for the second step of the authentication process. + /// + /// - Returns: A tuple containing an optional `LoginV2Request` and an optional `NetworkError`. + /// - `LoginV2Request?`: An object containing the necessary information for the second step of the login process, if successful. + /// - `NetworkError?`: An error encountered during the network request, if any. + + static func loginV2Request() async -> (LoginV2Request?, NetworkError?) { + let path = UserSettings.shared.serverProtocol + UserSettings.shared.serverAddress + let request = ApiRequest( + path: path + "/index.php/login/v2", + method: .POST + ) + + let (data, error) = await request.send(pathCompletion: false) + + if let error = error { + return (nil, error) + } + guard let data = data else { + return (nil, NetworkError.dataError) + } + guard let loginRequest: LoginV2Request = JSONDecoder.safeDecode(data) else { + return (nil, NetworkError.decodingFailed) + } + return (loginRequest, nil) + } + + /// Completes the user authentication process with Nextcloud using the Login Flow v2. + /// + /// This static function sends a POST request to the Nextcloud server with the login token obtained from `loginV2Request`. + /// On successful validation of the token, it returns a `LoginV2Response` object, completing the user login. + /// + /// - Parameter req: A `LoginV2Request` object containing the token and endpoint information for the authentication request. + /// + /// - Returns: A tuple containing an optional `LoginV2Response` and an optional `NetworkError`. + /// - `LoginV2Response?`: An object representing the response of the login process, if successful. + /// - `NetworkError?`: An error encountered during the network request, if any. + + static func loginV2Response(req: LoginV2Request) async -> (LoginV2Response?, NetworkError?) { + let request = ApiRequest( + path: req.poll.endpoint, + method: .POST, + headerFields: [ + HeaderField.ocsRequest(value: true), + HeaderField.accept(value: .JSON), + HeaderField.contentType(value: .FORM) + ], + body: "token=\(req.poll.token)".data(using: .utf8) + ) + let (data, error) = await request.send(pathCompletion: false) + + if let error = error { + return (nil, error) + } + guard let data = data else { + return (nil, NetworkError.dataError) + } + guard let loginResponse: LoginV2Response = JSONDecoder.safeDecode(data) else { + return (nil, NetworkError.decodingFailed) + } + return (loginResponse, nil) + } } diff --git a/Nextcloud Cookbook iOS Client/Views/MainView.swift b/Nextcloud Cookbook iOS Client/Views/MainView.swift index 8d59eeb..03556d1 100644 --- a/Nextcloud Cookbook iOS Client/Views/MainView.swift +++ b/Nextcloud Cookbook iOS Client/Views/MainView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import SimilaritySearchKit struct MainView: View { @StateObject var viewModel = AppState() @@ -69,211 +70,3 @@ struct MainView: View { } } } -/*struct MainView: View { - @ObservedObject var viewModel: MainViewModel - @StateObject var userSettings: UserSettings = UserSettings.shared - - @State private var selectedCategory: Category? = nil - @State private var showEditView: Bool = false - @State private var showSearchView: Bool = false - @State private var showSettingsView: Bool = false - @State private var serverConnection: Bool = false - @State private var showLoadingIndicator: Bool = false - - - var columns: [GridItem] = [GridItem(.adaptive(minimum: 150), spacing: 0)] - - var body: some View { - NavigationSplitView { - List(selection: $selectedCategory) { - // All recipes - NavigationLink { - RecipeSearchView(viewModel: viewModel) - } label: { - HStack(alignment: .center) { - Image(systemName: "magnifyingglass") - Text("Search") - .font(.system(size: 20, weight: .medium, design: .default)) - } - .padding(7) - } - - // 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") - .navigationDestination(isPresented: $showSettingsView) { - SettingsView(viewModel: viewModel) - } - .navigationDestination(isPresented: $showSearchView) { - RecipeSearchView(viewModel: viewModel) - } - .toolbar { - MainViewToolBar( - viewModel: viewModel, - showEditView: $showEditView, - showSettingsView: $showSettingsView, - 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 { - showLoadingIndicator = true - self.serverConnection = await viewModel.checkServerConnection() - await viewModel.getCategories() - await viewModel.updateAllRecipeDetails() - - // Open detail view for default category - if userSettings.defaultCategory != "" { - if let cat = viewModel.categories.first(where: { c in - if c.name == userSettings.defaultCategory { - return true - } - return false - }) { - self.selectedCategory = cat - } - } - showLoadingIndicator = false - } - .refreshable { - self.serverConnection = await viewModel.checkServerConnection() - await viewModel.getCategories() - } - - } - } - - - - - struct MainViewToolBar: ToolbarContent { - @ObservedObject var viewModel: MainViewModel - @Binding var showEditView: Bool - @Binding var showSettingsView: 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 { - self.showSettingsView = true - } label: { - Text("Settings") - Image(systemName: "gearshape") - } - 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/Onboarding/TokenLoginView.swift b/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift index 89efc58..c47c625 100644 --- a/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift @@ -87,44 +87,21 @@ struct TokenLoginView: View { showAlert = true return false } - let headerFields = [ - HeaderField.ocsRequest(value: true), - ] - let request = RequestWrapper.customRequest( - method: .GET, - path: .CATEGORIES, - headerFields: headerFields, - authenticate: true - ) - var (data, error): (Data?, Error?) = (nil, nil) - do { - let loginString = "\(userSettings.username):\(userSettings.token)" - let loginData = loginString.data(using: String.Encoding.utf8)! - let authString = loginData.base64EncodedString() - DispatchQueue.main.async { - userSettings.authString = authString - } - (data, error) = try await NetworkHandler.sendHTTPRequest( - request, - hostPath: "https://\(userSettings.serverAddress)/index.php/apps/cookbook/api/v1/", - authString: authString - ) - - } catch { - print("Error: ", error) + UserSettings.shared.setAuthString() + let (data, error) = await cookbookApi.getCategories(auth: UserSettings.shared.authString) + + if let error = error { + alertMessage = "Login failed. Please check your inputs and internet connection." + showAlert = true + return false } + guard let data = data else { alertMessage = "Login failed. Please check your inputs." showAlert = true return false } - if let testRequest: [Category] = JSONDecoder.safeDecode(data) { - print("validationResponse: \(testRequest)") - return true - } - alertMessage = "Login failed. Please check your inputs and internet connection." - showAlert = true - return false + return true } } diff --git a/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift b/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift index e3133d3..89f3eae 100644 --- a/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift @@ -34,9 +34,7 @@ struct V2LoginView: View { @State var loginStage: V2LoginStage = .login @State var loginRequest: LoginV2Request? = nil - - @State var userSettings = UserSettings.shared - + // TextField handling enum Field { case server @@ -73,14 +71,18 @@ struct V2LoginView: View { HStack { Button { - if userSettings.serverAddress == "" { + if UserSettings.shared.serverAddress == "" { alertMessage = "Please enter a valid server address." showAlert = true return } Task { - await sendLoginV2Request() + let error = await sendLoginV2Request() + if let error = error { + alertMessage = "A network error occured (\(error.rawValue))." + showAlert = true + } if let loginRequest = loginRequest { await UIApplication.shared.open(URL(string: loginRequest.login)!) } else { @@ -107,20 +109,27 @@ struct V2LoginView: View { Button { // fetch login v2 response Task { - guard let res = await fetchLoginV2Response() else { + let (response, error) = await fetchLoginV2Response() + + if let error = error { + alertMessage = "Login failed. Please login via the browser and try again. (\(error.rawValue))" + showAlert = true + return + } + guard let response = response else { alertMessage = "Login failed. Please login via the browser and try again." showAlert = true return } - print("Login successfull for user \(res.loginName)!") - self.userSettings.username = res.loginName - self.userSettings.token = res.appPassword - let loginString = "\(userSettings.username):\(userSettings.token)" + print("Login successful for user \(response.loginName)!") + UserSettings.shared.username = response.loginName + UserSettings.shared.token = response.appPassword + let loginString = "\(UserSettings.shared.username):\(UserSettings.shared.token)" let loginData = loginString.data(using: String.Encoding.utf8)! DispatchQueue.main.async { - userSettings.authString = loginData.base64EncodedString() + UserSettings.shared.authString = loginData.base64EncodedString() } - self.userSettings.onboarding = false + UserSettings.shared.onboarding = false } } label: { Text("Validate") @@ -141,64 +150,14 @@ struct V2LoginView: View { } } - func sendLoginV2Request() async { - let hostPath = "https://\(userSettings.serverAddress)" - let headerFields: [HeaderField] = [ - //HeaderField.ocsRequest(value: true), - //HeaderField.accept(value: .JSON) - ] - let request = RequestWrapper.customRequest( - method: .POST, - path: .LOGINV2REQ, - headerFields: headerFields - ) - do { - let (data, _): (Data?, Error?) = try await NetworkHandler.sendHTTPRequest( - request, - hostPath: hostPath, - authString: nil - ) - - guard let data = data else { return } - print("Data: \(data)") - let loginReq: LoginV2Request? = JSONDecoder.safeDecode(data) - self.loginRequest = loginReq - } catch { - print("Could not establish communication with the server.") - } - + func sendLoginV2Request() async -> NetworkError? { + let (req, error) = await NextcloudApi.loginV2Request() + self.loginRequest = req + return error } - func fetchLoginV2Response() async -> LoginV2Response? { - guard let loginRequest = loginRequest else { return nil } - let headerFields = [ - HeaderField.ocsRequest(value: true), - HeaderField.accept(value: .JSON), - HeaderField.contentType(value: .FORM) - ] - let request = RequestWrapper.customRequest( - method: .POST, - path: .NONE, - headerFields: headerFields, - body: "token=\(loginRequest.poll.token)".data(using: .utf8), - authenticate: false - ) - - var (data, error): (Data?, Error?) = (nil, nil) - do { - (data, error) = try await NetworkHandler.sendHTTPRequest( - request, - hostPath: loginRequest.poll.endpoint, - authString: nil - ) - } catch { - print("Error: ", error) - } - guard let data = data else { return nil } - if let loginRes: LoginV2Response = JSONDecoder.safeDecode(data) { - return loginRes - } - print("Could not decode.") - return nil + func fetchLoginV2Response() async -> (LoginV2Response?, NetworkError?) { + guard let loginRequest = loginRequest else { return (nil, .parametersNil) } + return await NextcloudApi.loginV2Response(req: loginRequest) } } diff --git a/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift b/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift index e5fea0e..2f26d9a 100644 --- a/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Tabs/SearchTabView.swift @@ -7,7 +7,7 @@ import Foundation import SwiftUI - +import SimilaritySearchKit struct SearchTabView: View { @EnvironmentObject var viewModel: SearchTabView.ViewModel @@ -17,6 +17,13 @@ struct SearchTabView: View { NavigationStack { VStack { ScrollView(showsIndicators: false) { + /* + Picker("Topping", selection: $viewModel.searchMode) { + ForEach(ViewModel.SearchMode.allCases, id: \.self) { mode in + Text(mode.rawValue) + } + }.pickerStyle(.segmented) + */ LazyVStack { ForEach(viewModel.recipesFiltered(), id: \.recipe_id) { recipe in NavigationLink(value: recipe) { @@ -47,13 +54,26 @@ struct SearchTabView: View { class ViewModel: ObservableObject { @Published var allRecipes: [Recipe] = [] @Published var searchText: String = "" + @Published var searchMode: SearchMode = .name + + var similarityIndex: SimilarityIndex? = nil + var similaritySearchResults: [SearchResult] = [] + + enum SearchMode: String, CaseIterable { + case name = "Name & Keywords", ingredient = "Ingredients" + } 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 + if searchMode == .name { + 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 + } + } else if searchMode == .ingredient { + // TODO: Fuzzy ingredient search } + return [] } } }