From 2ed664857bba68cf0c3416e51c64467f9cd7c7b7 Mon Sep 17 00:00:00 2001 From: Vincent Meilinger Date: Sun, 7 Jan 2024 17:10:47 +0100 Subject: [PATCH] users can now choose between http and https --- .../UserInterfaceState.xcuserstate | Bin 26049 -> 29375 bytes .../Data/UserSettings.swift | 7 ++ .../Localizable.xcstrings | 17 ++- .../Network/CookbookApi/ApiRequest.swift | 6 +- .../Network/CookbookApi/CookbookApi.swift | 33 +----- .../Network/CookbookApi/CookbookApiV1.swift | 44 +++---- .../ViewModels/MainViewModel.swift | 11 -- .../Views/Onboarding/OnboardingView.swift | 46 +++----- .../Views/Onboarding/TokenLoginView.swift | 9 +- .../Views/Onboarding/V2LoginView.swift | 108 ++++++++---------- 10 files changed, 105 insertions(+), 176 deletions(-) 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 1f00a6cca4a5daec45d9f15ccae82ca5578f9c4d..cbd1a85cfdc8661d5e1aa11bbd25b08c4552432b 100644 GIT binary patch delta 14996 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 HUZVd8&+iKv diff --git a/Nextcloud Cookbook iOS Client/Data/UserSettings.swift b/Nextcloud Cookbook iOS Client/Data/UserSettings.swift index 7aac9cd..e5b9245 100644 --- a/Nextcloud Cookbook iOS Client/Data/UserSettings.swift +++ b/Nextcloud Cookbook iOS Client/Data/UserSettings.swift @@ -37,6 +37,12 @@ class UserSettings: ObservableObject { } } + @Published var serverProtocol: String { + didSet { + UserDefaults.standard.set(serverProtocol, forKey: "serverProtocol") + } + } + @Published var onboarding: Bool { didSet { UserDefaults.standard.set(onboarding, forKey: "onboarding") @@ -102,6 +108,7 @@ class UserSettings: ObservableObject { self.token = UserDefaults.standard.object(forKey: "token") as? String ?? "" 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.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 diff --git a/Nextcloud Cookbook iOS Client/Localizable.xcstrings b/Nextcloud Cookbook iOS Client/Localizable.xcstrings index 8a68a9a..3e2e5fb 100644 --- a/Nextcloud Cookbook iOS Client/Localizable.xcstrings +++ b/Nextcloud Cookbook iOS Client/Localizable.xcstrings @@ -710,6 +710,9 @@ } } } + }, + "Copy Link" : { + }, "Could not establish a connection to the server. The action will be retried upon reconnection." : { "localizations" : { @@ -952,6 +955,9 @@ } } } + }, + "e.g.: example.com" : { + }, "Edit" : { "localizations" : { @@ -1152,6 +1158,7 @@ } }, "If the login button does not open your browser, copy the following link and paste it in your browser manually:" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -1172,6 +1179,9 @@ } } } + }, + "If the login button does not open your browser, use the 'Copy Link' button and paste the link in your browser manually." : { + }, "If you are interested in contributing to this project or simply wish to review its source code, we encourage you to visit the GitHub repository for this application." : { "localizations" : { @@ -1574,8 +1584,12 @@ } } } + }, + "Make sure to enter the server address in the form 'example.com', or \n':'\n when a non-standard port is used." : { + }, "Make sure to enter the server address in the form 'example.com'. Currently, only servers using the 'https' protocol are supported." : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2828,9 +2842,6 @@ } } } - }, - "Use a non-standard port" : { - }, "Validate" : { "localizations" : { diff --git a/Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift b/Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift index 67498f5..8056637 100644 --- a/Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift +++ b/Nextcloud Cookbook iOS Client/Network/CookbookApi/ApiRequest.swift @@ -9,8 +9,6 @@ import Foundation import OSLog struct ApiRequest { - /// The server address, e.g. https://example.com - let serverAddress: String let path: String let method: RequestMethod let authString: String? @@ -21,14 +19,12 @@ struct ApiRequest { let cookbookPath = "/index.php/apps/cookbook" init( - serverAdress: String, path: String, method: RequestMethod, authString: String? = nil, headerFields: [HeaderField] = [], body: Data? = nil ) { - self.serverAddress = serverAdress self.method = method self.path = path self.headerFields = headerFields @@ -40,7 +36,7 @@ struct ApiRequest { Logger.network.debug("\(method.rawValue) \(path) sending ...") // Prepare URL - let urlString = "https://" + serverAddress + cookbookPath + path + let urlString = UserSettings.shared.serverProtocol + UserSettings.shared.serverAddress + cookbookPath + path print("Full path: \(urlString)") //Logger.network.debug("Full path: \(urlString)") guard let urlStringSanitized = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, .unknownError) } diff --git a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift index deb6c99..7baed46 100644 --- a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift +++ b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApi.swift @@ -13,20 +13,17 @@ import UIKit protocol CookbookApi { /// Not implemented yet. static func importRecipe( - from serverAdress: String, auth: String, data: Data ) async -> (RecipeDetail?, NetworkError?) /// Get either the full image or a thumbnail sized version. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - id: The according recipe id. /// - size: The size of the image. /// - Returns: The image of the recipe with the specified id. A NetworkError if the request fails, otherwise nil. static func getImage( - from serverAdress: String, auth: String, id: Int, size: RecipeImage.RecipeImageSize @@ -34,91 +31,75 @@ protocol CookbookApi { /// Get all recipes. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - Returns: A list of all recipes. static func getRecipes( - from serverAdress: String, auth: String ) async -> ([Recipe]?, NetworkError?) /// Create a new recipe. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - Returns: A NetworkError if the request fails. Nil otherwise. static func createRecipe( - from serverAdress: String, auth: String, recipe: RecipeDetail ) async -> (NetworkError?) /// Get the recipe with the specified id. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - id: The recipe id. /// - Returns: The recipe if it exists. A NetworkError if the request fails. static func getRecipe( - from serverAdress: String, auth: String, id: Int ) async -> (RecipeDetail?, NetworkError?) /// Update an existing recipe with new entries. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - id: The recipe id. /// - Returns: A NetworkError if the request fails. Nil otherwise. static func updateRecipe( - from serverAdress: String, - auth: String, + auth: String, recipe: RecipeDetail ) async -> (NetworkError?) /// Delete the recipe with the specified id. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - id: The recipe id. /// - Returns: A NetworkError if the request fails. Nil otherwise. static func deleteRecipe( - from serverAdress: String, auth: String, id: Int ) async -> (NetworkError?) /// Get all categories. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - Returns: A list of categories. A NetworkError if the request fails. static func getCategories( - from serverAdress: String, auth: String ) async -> ([Category]?, NetworkError?) /// Get all recipes of a specified category. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - categoryName: The category name. /// - Returns: A list of recipes. A NetworkError if the request fails. static func getCategory( - from serverAdress: String, auth: String, named categoryName: String ) async -> ([Recipe]?, NetworkError?) /// Rename an existing category. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - categoryName: The name of the category to be renamed. /// - newName: The new category name. /// - Returns: A NetworkError if the request fails. static func renameCategory( - from serverAdress: String, auth: String, named categoryName: String, newName: String @@ -126,63 +107,51 @@ protocol CookbookApi { /// Get all keywords/tags. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - Returns: A list of tag strings. A NetworkError if the request fails. static func getTags( - from serverAdress: String, auth: String ) async -> ([RecipeKeyword]?, NetworkError?) /// Get all recipes tagged with the specified keyword. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - keyword: The keyword. /// - Returns: A list of recipes tagged with the specified keyword. A NetworkError if the request fails. static func getRecipesTagged( - from serverAdress: String, auth: String, keyword: String ) async -> ([Recipe]?, NetworkError?) /// Get the servers api version. /// - Parameters: - /// - serverAdress: Server address in the format https://example.com. /// - auth: Server authentication string. /// - Returns: A NetworkError if the request fails. static func getApiVersion( - from serverAdress: String, auth: String ) async -> (NetworkError?) /// Trigger a reindexing action on the server. /// - Parameters: - /// - serverAdress: Server address in the format. https://example.com /// - auth: Server authentication string /// - Returns: A NetworkError if the request fails. static func postReindex( - from serverAdress: String, auth: String ) async -> (NetworkError?) /// Get the current configuration of the Cookbook server application. /// - Parameters: - /// - serverAdress: Server address in the format. https://example.com /// - auth: Server authentication string /// - Returns: A NetworkError if the request fails. static func getConfig( - from serverAdress: String, auth: String ) async -> (NetworkError?) /// Set the current configuration of the Cookbook server application. /// - Parameters: - /// - serverAdress: Server address in the format. https://example.com /// - auth: Server authentication string /// - Returns: A NetworkError if the request fails. static func postConfig( - from serverAdress: String, auth: String ) async -> (NetworkError?) } diff --git a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift index 2f8c7ec..e4f4a0e 100644 --- a/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift +++ b/Nextcloud Cookbook iOS Client/Network/CookbookApi/CookbookApiV1.swift @@ -10,9 +10,8 @@ import UIKit class CookbookApiV1: CookbookApi { - static func importRecipe(from serverAdress: String, auth: String, data: Data) async -> (RecipeDetail?, NetworkError?) { + static func importRecipe(auth: String, data: Data) async -> (RecipeDetail?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/import", method: .POST, authString: auth, @@ -24,10 +23,9 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func getImage(from serverAdress: String, auth: String, id: Int, size: RecipeImage.RecipeImageSize) async -> (UIImage?, NetworkError?) { + static func getImage(auth: String, id: Int, size: RecipeImage.RecipeImageSize) async -> (UIImage?, NetworkError?) { let imageSize = (size == .FULL ? "full" : "thumb") let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/recipes/\(id)/image?size=\(imageSize)", method: .GET, authString: auth, @@ -39,9 +37,8 @@ class CookbookApiV1: CookbookApi { return (UIImage(data: data), error) } - static func getRecipes(from serverAdress: String, auth: String) async -> ([Recipe]?, NetworkError?) { + static func getRecipes(auth: String) async -> ([Recipe]?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/recipes", method: .GET, authString: auth, @@ -53,13 +50,12 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func createRecipe(from serverAdress: String, auth: String, recipe: RecipeDetail) async -> (NetworkError?) { + static func createRecipe(auth: String, recipe: RecipeDetail) async -> (NetworkError?) { guard let recipeData = JSONEncoder.safeEncode(recipe) else { return .dataError } let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/recipes", method: .POST, authString: auth, @@ -82,9 +78,8 @@ class CookbookApiV1: CookbookApi { return nil } - static func getRecipe(from serverAdress: String, auth: String, id: Int) async -> (RecipeDetail?, NetworkError?) { + static func getRecipe(auth: String, id: Int) async -> (RecipeDetail?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/recipes/\(id)", method: .GET, authString: auth, @@ -96,12 +91,11 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func updateRecipe(from serverAdress: String, auth: String, recipe: RecipeDetail) async -> (NetworkError?) { + static func updateRecipe(auth: String, recipe: RecipeDetail) async -> (NetworkError?) { guard let recipeData = JSONEncoder.safeEncode(recipe) else { return .dataError } let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/recipes/\(recipe.id)", method: .PUT, authString: auth, @@ -124,9 +118,8 @@ class CookbookApiV1: CookbookApi { return nil } - static func deleteRecipe(from serverAdress: String, auth: String, id: Int) async -> (NetworkError?) { + static func deleteRecipe(auth: String, id: Int) async -> (NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/recipes/\(id)", method: .DELETE, authString: auth, @@ -138,9 +131,8 @@ class CookbookApiV1: CookbookApi { return nil } - static func getCategories(from serverAdress: String, auth: String) async -> ([Category]?, NetworkError?) { + static func getCategories(auth: String) async -> ([Category]?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/categories", method: .GET, authString: auth, @@ -152,9 +144,8 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func getCategory(from serverAdress: String, auth: String, named categoryName: String) async -> ([Recipe]?, NetworkError?) { + static func getCategory(auth: String, named categoryName: String) async -> ([Recipe]?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/category/\(categoryName)", method: .GET, authString: auth, @@ -166,9 +157,8 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func renameCategory(from serverAdress: String, auth: String, named categoryName: String, newName: String) async -> (NetworkError?) { + static func renameCategory(auth: String, named categoryName: String, newName: String) async -> (NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/category/\(categoryName)", method: .PUT, authString: auth, @@ -180,9 +170,8 @@ class CookbookApiV1: CookbookApi { return nil } - static func getTags(from serverAdress: String, auth: String) async -> ([RecipeKeyword]?, NetworkError?) { + static func getTags(auth: String) async -> ([RecipeKeyword]?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/keywords", method: .GET, authString: auth, @@ -194,9 +183,8 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func getRecipesTagged(from serverAdress: String, auth: String, keyword: String) async -> ([Recipe]?, NetworkError?) { + static func getRecipesTagged(auth: String, keyword: String) async -> ([Recipe]?, NetworkError?) { let request = ApiRequest( - serverAdress: serverAdress, path: "/api/v1/tags/\(keyword)", method: .GET, authString: auth, @@ -208,19 +196,19 @@ class CookbookApiV1: CookbookApi { return (JSONDecoder.safeDecode(data), nil) } - static func getApiVersion(from serverAdress: String, auth: String) async -> (NetworkError?) { + static func getApiVersion(auth: String) async -> (NetworkError?) { return .none } - static func postReindex(from serverAdress: String, auth: String) async -> (NetworkError?) { + static func postReindex(auth: String) async -> (NetworkError?) { return .none } - static func getConfig(from serverAdress: String, auth: String) async -> (NetworkError?) { + static func getConfig(auth: String) async -> (NetworkError?) { return .none } - static func postConfig(from serverAdress: String, auth: String) async -> (NetworkError?) { + static func postConfig(auth: String) async -> (NetworkError?) { return .none } } diff --git a/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift b/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift index da72632..87b5555 100644 --- a/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift +++ b/Nextcloud Cookbook iOS Client/ViewModels/MainViewModel.swift @@ -49,7 +49,6 @@ import UIKit */ func getCategories() async { let (categories, _) = await api.getCategories( - from: userSettings.serverAddress, auth: userSettings.authString ) if let categories = categories { @@ -93,7 +92,6 @@ import UIKit func getServer(store: Bool = false) async -> Bool { let (recipes, _) = await api.getCategory( - from: userSettings.serverAddress, auth: userSettings.authString, named: categoryString ) @@ -156,7 +154,6 @@ import UIKit */ func getRecipes() async -> [Recipe] { let (recipes, error) = await api.getRecipes( - from: userSettings.serverAddress, auth: userSettings.authString ) if let recipes = recipes { @@ -197,7 +194,6 @@ import UIKit func getServer() async -> RecipeDetail? { let (recipe, error) = await api.getRecipe( - from: userSettings.serverAddress, auth: userSettings.authString, id: id ) @@ -291,7 +287,6 @@ import UIKit func getServer() async -> UIImage? { let (image, _) = await api.getImage( - from: userSettings.serverAddress, auth: userSettings.authString, id: id, size: size @@ -368,7 +363,6 @@ import UIKit func getServer() async -> [RecipeKeyword]? { let (tags, _) = await api.getTags( - from: userSettings.serverAddress, auth: userSettings.authString ) return tags @@ -423,7 +417,6 @@ import UIKit */ func deleteRecipe(withId id: Int, categoryName: String) async -> RequestAlert? { let (error) = await api.deleteRecipe( - from: userSettings.serverAddress, auth: userSettings.authString, id: id ) @@ -455,7 +448,6 @@ import UIKit */ func checkServerConnection() async -> Bool { let (categories, _) = await api.getCategories( - from: userSettings.serverAddress, auth: userSettings.authString ) if let categories = categories { @@ -485,13 +477,11 @@ import UIKit var error: NetworkError? = nil if createNew { error = await api.createRecipe( - from: userSettings.serverAddress, auth: userSettings.authString, recipe: recipeDetail ) } else { error = await api.updateRecipe( - from: userSettings.serverAddress, auth: userSettings.authString, recipe: recipeDetail ) @@ -505,7 +495,6 @@ 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( - from: userSettings.serverAddress, auth: userSettings.authString, data: data ) diff --git a/Nextcloud Cookbook iOS Client/Views/Onboarding/OnboardingView.swift b/Nextcloud Cookbook iOS Client/Views/Onboarding/OnboardingView.swift index 02aff48..98a73e1 100644 --- a/Nextcloud Cookbook iOS Client/Views/Onboarding/OnboardingView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Onboarding/OnboardingView.swift @@ -181,11 +181,8 @@ struct LoginTextField: View { struct ServerAddressField: View { - @Binding var addressString: String - @State var serverAddress: String = "" + @ObservedObject var userSettings = UserSettings.shared @State var serverProtocol: ServerProtocol = .https - @State var serverPort: String = "" - @State var useNonStandardPort: Bool = false enum ServerProtocol: String { case https="https://", http="http://" @@ -205,22 +202,26 @@ struct ServerAddressField: View { }.pickerStyle(.menu) .tint(.white) .font(.headline) + .onChange(of: serverProtocol) { color in + userSettings.serverProtocol = color.rawValue + } + + TextField("e.g.: example.com", text: $userSettings.serverAddress) + .textFieldStyle(.plain) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .foregroundStyle(.white) + .padding() + .background( + RoundedRectangle(cornerRadius: 10) + .foregroundColor(Color.white.opacity(0.2)) + ) - LoginTextField(example: "e.g.: example.com", text: $serverAddress) - } - - Toggle("Use a non-standard port", isOn: $useNonStandardPort) - .tint(.white.opacity(0.2)) - .foregroundStyle(.white) - .font(.headline) - .padding(.top) - if useNonStandardPort { - LoginTextField(example: "e.g.: 80", text: $serverPort) } LoginLabel(text: "Full server address") .padding(.top) - Text(createServerAddressString()) + Text(userSettings.serverProtocol + userSettings.serverAddress) .foregroundColor(.white) .padding(.vertical, 5) } @@ -230,26 +231,13 @@ struct ServerAddressField: View { .stroke(.white, lineWidth: 2) .foregroundColor(.clear) ) - }.animation(.easeInOut, value: useNonStandardPort) - } - - func createServerAddressString() -> String { - if useNonStandardPort && serverPort != "" { - addressString = serverProtocol.rawValue + serverAddress + ":" + serverPort - } else { - addressString = serverProtocol.rawValue + serverAddress } - return addressString } } struct ServerAddressField_Preview: PreviewProvider { static var previews: some View { - ServerAddressField(addressString: .constant(""), - serverAddress: "example.com", - serverProtocol: .https, - serverPort: "80", - useNonStandardPort: true) + ServerAddressField() .previewLayout(.sizeThatFits) .padding() .background(Color.nextcloudBlue) diff --git a/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift b/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift index d112349..89efc58 100644 --- a/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Onboarding/TokenLoginView.swift @@ -26,14 +26,9 @@ struct TokenLoginView: View { var body: some View { VStack(alignment: .leading) { - /*LoginLabel(text: "Server address") - LoginTextField(example: "e.g.: example.com", text: $userSettings.serverAddress) - .focused($focusedField, equals: .server) - .textContentType(.URL) - .submitLabel(.next) + ServerAddressField() .padding(.bottom) - */ - ServerAddressField(addressString: $userSettings.serverAddress) + LoginLabel(text: "User name") BorderedLoginTextField(example: "username", text: $userSettings.username) .focused($focusedField, equals: .username) diff --git a/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift b/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift index f974a7d..e3133d3 100644 --- a/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift +++ b/Nextcloud Cookbook iOS Client/Views/Onboarding/V2LoginView.swift @@ -9,11 +9,10 @@ import Foundation import SwiftUI enum V2LoginStage: LoginStage { - case serverAddress, login, validate + case login, validate func next() -> V2LoginStage { switch self { - case .serverAddress: return .login case .login: return .validate case .validate: return .validate } @@ -21,8 +20,7 @@ enum V2LoginStage: LoginStage { func previous() -> V2LoginStage { switch self { - case .serverAddress: return .serverAddress - case .login: return .serverAddress + case .login: return .login case .validate: return .login } } @@ -34,9 +32,8 @@ struct V2LoginView: View { @Binding var showAlert: Bool @Binding var alertMessage: String - @State var loginStage: V2LoginStage = .serverAddress + @State var loginStage: V2LoginStage = .login @State var loginRequest: LoginV2Request? = nil - @FocusState private var focusedField: Field? @State var userSettings = UserSettings.shared @@ -50,27 +47,14 @@ struct V2LoginView: View { var body: some View { ScrollView { VStack(alignment: .leading) { - /*LoginLabel(text: "Server address") - .padding() - LoginTextField(example: "e.g.: example.com", text: $userSettings.serverAddress, color: loginStage == .serverAddress ? .white : .secondary) - .focused($focusedField, equals: .server) - .textContentType(.URL) - .submitLabel(.done) - .padding([.bottom, .horizontal]) - .onSubmit { - withAnimation(.easeInOut) { - loginStage = loginStage.next() - } - } - */ - ServerAddressField(addressString: $userSettings.serverAddress) + ServerAddressField() CollapsibleView { VStack(alignment: .leading) { - Text("Make sure to enter the server address in the form 'example.com'. Currently, only servers using the 'https' protocol are supported.") - if let loginRequest = loginRequest { - Text("If the login button does not open your browser, copy the following link and paste it in your browser manually:") - Text(loginRequest.login) - } + Text("Make sure to enter the server address in the form 'example.com', or \n':'\n when a non-standard port is used.") + .padding(.bottom) + Text("The 'Login' button will open a web browser. Please follow the login instructions provided there.\nAfter a successful login, return to this application and press 'Validate'.") + .padding(.bottom) + Text("If the login button does not open your browser, use the 'Copy Link' button and paste the link in your browser manually.") } } title: { Text("Show help") @@ -78,43 +62,45 @@ struct V2LoginView: View { .font(.headline) }.padding() - if loginStage == .login || loginStage == .validate { - Text("The 'Login' button will open a web browser. Please follow the login instructions provided there.\nAfter a successful login, return to this application and press 'Validate'.") - .font(.subheadline) - .foregroundStyle(.white) - .padding() - } - HStack { - if loginStage == .login || loginStage == .validate { - Button { - if userSettings.serverAddress == "" { - alertMessage = "Please enter a valid server address." - showAlert = true - return - } - - Task { - await sendLoginV2Request() - if let loginRequest = loginRequest { - await UIApplication.shared.open(URL(string: loginRequest.login)!) - } else { - alertMessage = "Unable to reach server. Please check your server address and internet connection." - showAlert = true - } - } - loginStage = loginStage.next() - } label: { - Text("Login") - .foregroundColor(.white) - .font(.headline) - .padding() - .background( - RoundedRectangle(cornerRadius: 10) - .stroke(Color.white, lineWidth: 2) - .foregroundColor(.clear) - ) - }.padding() + if loginRequest != nil { + Button("Copy Link") { + UIPasteboard.general.string = loginRequest!.login } + .font(.headline) + .foregroundStyle(.white) + .padding() + } + + HStack { + Button { + if userSettings.serverAddress == "" { + alertMessage = "Please enter a valid server address." + showAlert = true + return + } + + Task { + await sendLoginV2Request() + if let loginRequest = loginRequest { + await UIApplication.shared.open(URL(string: loginRequest.login)!) + } else { + alertMessage = "Unable to reach server. Please check your server address and internet connection." + showAlert = true + } + } + loginStage = loginStage.next() + } label: { + Text("Login") + .foregroundColor(.white) + .font(.headline) + .padding() + .background( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.white, lineWidth: 2) + .foregroundColor(.clear) + ) + }.padding() + if loginStage == .validate { Spacer()