users can now choose between http and https

This commit is contained in:
Vincent Meilinger
2024-01-07 17:10:47 +01:00
parent 5c7ff578c7
commit 2ed664857b
10 changed files with 105 additions and 176 deletions

View File

@@ -37,6 +37,12 @@ class UserSettings: ObservableObject {
} }
} }
@Published var serverProtocol: String {
didSet {
UserDefaults.standard.set(serverProtocol, forKey: "serverProtocol")
}
}
@Published var onboarding: Bool { @Published var onboarding: Bool {
didSet { didSet {
UserDefaults.standard.set(onboarding, forKey: "onboarding") UserDefaults.standard.set(onboarding, forKey: "onboarding")
@@ -102,6 +108,7 @@ class UserSettings: ObservableObject {
self.token = UserDefaults.standard.object(forKey: "token") as? String ?? "" self.token = UserDefaults.standard.object(forKey: "token") as? String ?? ""
self.authString = UserDefaults.standard.object(forKey: "authString") as? String ?? "" self.authString = UserDefaults.standard.object(forKey: "authString") as? String ?? ""
self.serverAddress = UserDefaults.standard.object(forKey: "serverAddress") 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.onboarding = UserDefaults.standard.object(forKey: "onboarding") as? Bool ?? true
self.defaultCategory = UserDefaults.standard.object(forKey: "defaultCategory") as? String ?? "" self.defaultCategory = UserDefaults.standard.object(forKey: "defaultCategory") as? String ?? ""
self.language = UserDefaults.standard.object(forKey: "language") as? String ?? SupportedLanguage.DEVICE.rawValue self.language = UserDefaults.standard.object(forKey: "language") as? String ?? SupportedLanguage.DEVICE.rawValue

View File

@@ -710,6 +710,9 @@
} }
} }
} }
},
"Copy Link" : {
}, },
"Could not establish a connection to the server. The action will be retried upon reconnection." : { "Could not establish a connection to the server. The action will be retried upon reconnection." : {
"localizations" : { "localizations" : {
@@ -952,6 +955,9 @@
} }
} }
} }
},
"e.g.: example.com" : {
}, },
"Edit" : { "Edit" : {
"localizations" : { "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:" : { "If the login button does not open your browser, copy the following link and paste it in your browser manually:" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"de" : { "de" : {
"stringUnit" : { "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." : { "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" : { "localizations" : {
@@ -1574,8 +1584,12 @@
} }
} }
} }
},
"Make sure to enter the server address in the form 'example.com', or \n'<server address>:<port>'\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." : { "Make sure to enter the server address in the form 'example.com'. Currently, only servers using the 'https' protocol are supported." : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"de" : { "de" : {
"stringUnit" : { "stringUnit" : {
@@ -2828,9 +2842,6 @@
} }
} }
} }
},
"Use a non-standard port" : {
}, },
"Validate" : { "Validate" : {
"localizations" : { "localizations" : {

View File

@@ -9,8 +9,6 @@ import Foundation
import OSLog import OSLog
struct ApiRequest { struct ApiRequest {
/// The server address, e.g. https://example.com
let serverAddress: String
let path: String let path: String
let method: RequestMethod let method: RequestMethod
let authString: String? let authString: String?
@@ -21,14 +19,12 @@ struct ApiRequest {
let cookbookPath = "/index.php/apps/cookbook" let cookbookPath = "/index.php/apps/cookbook"
init( init(
serverAdress: String,
path: String, path: String,
method: RequestMethod, method: RequestMethod,
authString: String? = nil, authString: String? = nil,
headerFields: [HeaderField] = [], headerFields: [HeaderField] = [],
body: Data? = nil body: Data? = nil
) { ) {
self.serverAddress = serverAdress
self.method = method self.method = method
self.path = path self.path = path
self.headerFields = headerFields self.headerFields = headerFields
@@ -40,7 +36,7 @@ struct ApiRequest {
Logger.network.debug("\(method.rawValue) \(path) sending ...") Logger.network.debug("\(method.rawValue) \(path) sending ...")
// Prepare URL // Prepare URL
let urlString = "https://" + serverAddress + cookbookPath + path let urlString = UserSettings.shared.serverProtocol + UserSettings.shared.serverAddress + cookbookPath + path
print("Full path: \(urlString)") print("Full path: \(urlString)")
//Logger.network.debug("Full path: \(urlString)") //Logger.network.debug("Full path: \(urlString)")
guard let urlStringSanitized = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, .unknownError) } guard let urlStringSanitized = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, .unknownError) }

View File

@@ -13,20 +13,17 @@ import UIKit
protocol CookbookApi { protocol CookbookApi {
/// Not implemented yet. /// Not implemented yet.
static func importRecipe( static func importRecipe(
from serverAdress: String,
auth: String, auth: String,
data: Data data: Data
) async -> (RecipeDetail?, NetworkError?) ) async -> (RecipeDetail?, NetworkError?)
/// Get either the full image or a thumbnail sized version. /// Get either the full image or a thumbnail sized version.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - id: The according recipe id. /// - id: The according recipe id.
/// - size: The size of the image. /// - size: The size of the image.
/// - Returns: The image of the recipe with the specified id. A NetworkError if the request fails, otherwise nil. /// - Returns: The image of the recipe with the specified id. A NetworkError if the request fails, otherwise nil.
static func getImage( static func getImage(
from serverAdress: String,
auth: String, auth: String,
id: Int, id: Int,
size: RecipeImage.RecipeImageSize size: RecipeImage.RecipeImageSize
@@ -34,91 +31,75 @@ protocol CookbookApi {
/// Get all recipes. /// Get all recipes.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - Returns: A list of all recipes. /// - Returns: A list of all recipes.
static func getRecipes( static func getRecipes(
from serverAdress: String,
auth: String auth: String
) async -> ([Recipe]?, NetworkError?) ) async -> ([Recipe]?, NetworkError?)
/// Create a new recipe. /// Create a new recipe.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - Returns: A NetworkError if the request fails. Nil otherwise. /// - Returns: A NetworkError if the request fails. Nil otherwise.
static func createRecipe( static func createRecipe(
from serverAdress: String,
auth: String, auth: String,
recipe: RecipeDetail recipe: RecipeDetail
) async -> (NetworkError?) ) async -> (NetworkError?)
/// Get the recipe with the specified id. /// Get the recipe with the specified id.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - id: The recipe id. /// - id: The recipe id.
/// - Returns: The recipe if it exists. A NetworkError if the request fails. /// - Returns: The recipe if it exists. A NetworkError if the request fails.
static func getRecipe( static func getRecipe(
from serverAdress: String,
auth: String, id: Int auth: String, id: Int
) async -> (RecipeDetail?, NetworkError?) ) async -> (RecipeDetail?, NetworkError?)
/// Update an existing recipe with new entries. /// Update an existing recipe with new entries.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - id: The recipe id. /// - id: The recipe id.
/// - Returns: A NetworkError if the request fails. Nil otherwise. /// - Returns: A NetworkError if the request fails. Nil otherwise.
static func updateRecipe( static func updateRecipe(
from serverAdress: String,
auth: String, auth: String,
recipe: RecipeDetail recipe: RecipeDetail
) async -> (NetworkError?) ) async -> (NetworkError?)
/// Delete the recipe with the specified id. /// Delete the recipe with the specified id.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - id: The recipe id. /// - id: The recipe id.
/// - Returns: A NetworkError if the request fails. Nil otherwise. /// - Returns: A NetworkError if the request fails. Nil otherwise.
static func deleteRecipe( static func deleteRecipe(
from serverAdress: String,
auth: String, auth: String,
id: Int id: Int
) async -> (NetworkError?) ) async -> (NetworkError?)
/// Get all categories. /// Get all categories.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - Returns: A list of categories. A NetworkError if the request fails. /// - Returns: A list of categories. A NetworkError if the request fails.
static func getCategories( static func getCategories(
from serverAdress: String,
auth: String auth: String
) async -> ([Category]?, NetworkError?) ) async -> ([Category]?, NetworkError?)
/// Get all recipes of a specified category. /// Get all recipes of a specified category.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - categoryName: The category name. /// - categoryName: The category name.
/// - Returns: A list of recipes. A NetworkError if the request fails. /// - Returns: A list of recipes. A NetworkError if the request fails.
static func getCategory( static func getCategory(
from serverAdress: String,
auth: String, auth: String,
named categoryName: String named categoryName: String
) async -> ([Recipe]?, NetworkError?) ) async -> ([Recipe]?, NetworkError?)
/// Rename an existing category. /// Rename an existing category.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - categoryName: The name of the category to be renamed. /// - categoryName: The name of the category to be renamed.
/// - newName: The new category name. /// - newName: The new category name.
/// - Returns: A NetworkError if the request fails. /// - Returns: A NetworkError if the request fails.
static func renameCategory( static func renameCategory(
from serverAdress: String,
auth: String, auth: String,
named categoryName: String, named categoryName: String,
newName: String newName: String
@@ -126,63 +107,51 @@ protocol CookbookApi {
/// Get all keywords/tags. /// Get all keywords/tags.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - Returns: A list of tag strings. A NetworkError if the request fails. /// - Returns: A list of tag strings. A NetworkError if the request fails.
static func getTags( static func getTags(
from serverAdress: String,
auth: String auth: String
) async -> ([RecipeKeyword]?, NetworkError?) ) async -> ([RecipeKeyword]?, NetworkError?)
/// Get all recipes tagged with the specified keyword. /// Get all recipes tagged with the specified keyword.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - keyword: The keyword. /// - keyword: The keyword.
/// - Returns: A list of recipes tagged with the specified keyword. A NetworkError if the request fails. /// - Returns: A list of recipes tagged with the specified keyword. A NetworkError if the request fails.
static func getRecipesTagged( static func getRecipesTagged(
from serverAdress: String,
auth: String, auth: String,
keyword: String keyword: String
) async -> ([Recipe]?, NetworkError?) ) async -> ([Recipe]?, NetworkError?)
/// Get the servers api version. /// Get the servers api version.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format https://example.com.
/// - auth: Server authentication string. /// - auth: Server authentication string.
/// - Returns: A NetworkError if the request fails. /// - Returns: A NetworkError if the request fails.
static func getApiVersion( static func getApiVersion(
from serverAdress: String,
auth: String auth: String
) async -> (NetworkError?) ) async -> (NetworkError?)
/// Trigger a reindexing action on the server. /// Trigger a reindexing action on the server.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format. https://example.com
/// - auth: Server authentication string /// - auth: Server authentication string
/// - Returns: A NetworkError if the request fails. /// - Returns: A NetworkError if the request fails.
static func postReindex( static func postReindex(
from serverAdress: String,
auth: String auth: String
) async -> (NetworkError?) ) async -> (NetworkError?)
/// Get the current configuration of the Cookbook server application. /// Get the current configuration of the Cookbook server application.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format. https://example.com
/// - auth: Server authentication string /// - auth: Server authentication string
/// - Returns: A NetworkError if the request fails. /// - Returns: A NetworkError if the request fails.
static func getConfig( static func getConfig(
from serverAdress: String,
auth: String auth: String
) async -> (NetworkError?) ) async -> (NetworkError?)
/// Set the current configuration of the Cookbook server application. /// Set the current configuration of the Cookbook server application.
/// - Parameters: /// - Parameters:
/// - serverAdress: Server address in the format. https://example.com
/// - auth: Server authentication string /// - auth: Server authentication string
/// - Returns: A NetworkError if the request fails. /// - Returns: A NetworkError if the request fails.
static func postConfig( static func postConfig(
from serverAdress: String,
auth: String auth: String
) async -> (NetworkError?) ) async -> (NetworkError?)
} }

View File

@@ -10,9 +10,8 @@ import UIKit
class CookbookApiV1: CookbookApi { 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/import", path: "/api/v1/import",
method: .POST, method: .POST,
authString: auth, authString: auth,
@@ -24,10 +23,9 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) 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 imageSize = (size == .FULL ? "full" : "thumb")
let request = ApiRequest( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/recipes/\(id)/image?size=\(imageSize)", path: "/api/v1/recipes/\(id)/image?size=\(imageSize)",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -39,9 +37,8 @@ class CookbookApiV1: CookbookApi {
return (UIImage(data: data), error) 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/recipes", path: "/api/v1/recipes",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -53,13 +50,12 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) 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 { guard let recipeData = JSONEncoder.safeEncode(recipe) else {
return .dataError return .dataError
} }
let request = ApiRequest( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/recipes", path: "/api/v1/recipes",
method: .POST, method: .POST,
authString: auth, authString: auth,
@@ -82,9 +78,8 @@ class CookbookApiV1: CookbookApi {
return nil 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/recipes/\(id)", path: "/api/v1/recipes/\(id)",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -96,12 +91,11 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) 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 { guard let recipeData = JSONEncoder.safeEncode(recipe) else {
return .dataError return .dataError
} }
let request = ApiRequest( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/recipes/\(recipe.id)", path: "/api/v1/recipes/\(recipe.id)",
method: .PUT, method: .PUT,
authString: auth, authString: auth,
@@ -124,9 +118,8 @@ class CookbookApiV1: CookbookApi {
return nil 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/recipes/\(id)", path: "/api/v1/recipes/\(id)",
method: .DELETE, method: .DELETE,
authString: auth, authString: auth,
@@ -138,9 +131,8 @@ class CookbookApiV1: CookbookApi {
return nil return nil
} }
static func getCategories(from serverAdress: String, auth: String) async -> ([Category]?, NetworkError?) { static func getCategories(auth: String) async -> ([Category]?, NetworkError?) {
let request = ApiRequest( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/categories", path: "/api/v1/categories",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -152,9 +144,8 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/category/\(categoryName)", path: "/api/v1/category/\(categoryName)",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -166,9 +157,8 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/category/\(categoryName)", path: "/api/v1/category/\(categoryName)",
method: .PUT, method: .PUT,
authString: auth, authString: auth,
@@ -180,9 +170,8 @@ class CookbookApiV1: CookbookApi {
return nil return nil
} }
static func getTags(from serverAdress: String, auth: String) async -> ([RecipeKeyword]?, NetworkError?) { static func getTags(auth: String) async -> ([RecipeKeyword]?, NetworkError?) {
let request = ApiRequest( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/keywords", path: "/api/v1/keywords",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -194,9 +183,8 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) 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( let request = ApiRequest(
serverAdress: serverAdress,
path: "/api/v1/tags/\(keyword)", path: "/api/v1/tags/\(keyword)",
method: .GET, method: .GET,
authString: auth, authString: auth,
@@ -208,19 +196,19 @@ class CookbookApiV1: CookbookApi {
return (JSONDecoder.safeDecode(data), nil) return (JSONDecoder.safeDecode(data), nil)
} }
static func getApiVersion(from serverAdress: String, auth: String) async -> (NetworkError?) { static func getApiVersion(auth: String) async -> (NetworkError?) {
return .none return .none
} }
static func postReindex(from serverAdress: String, auth: String) async -> (NetworkError?) { static func postReindex(auth: String) async -> (NetworkError?) {
return .none return .none
} }
static func getConfig(from serverAdress: String, auth: String) async -> (NetworkError?) { static func getConfig(auth: String) async -> (NetworkError?) {
return .none return .none
} }
static func postConfig(from serverAdress: String, auth: String) async -> (NetworkError?) { static func postConfig(auth: String) async -> (NetworkError?) {
return .none return .none
} }
} }

View File

@@ -49,7 +49,6 @@ import UIKit
*/ */
func getCategories() async { func getCategories() async {
let (categories, _) = await api.getCategories( let (categories, _) = await api.getCategories(
from: userSettings.serverAddress,
auth: userSettings.authString auth: userSettings.authString
) )
if let categories = categories { if let categories = categories {
@@ -93,7 +92,6 @@ import UIKit
func getServer(store: Bool = false) async -> Bool { func getServer(store: Bool = false) async -> Bool {
let (recipes, _) = await api.getCategory( let (recipes, _) = await api.getCategory(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
named: categoryString named: categoryString
) )
@@ -156,7 +154,6 @@ import UIKit
*/ */
func getRecipes() async -> [Recipe] { func getRecipes() async -> [Recipe] {
let (recipes, error) = await api.getRecipes( let (recipes, error) = await api.getRecipes(
from: userSettings.serverAddress,
auth: userSettings.authString auth: userSettings.authString
) )
if let recipes = recipes { if let recipes = recipes {
@@ -197,7 +194,6 @@ import UIKit
func getServer() async -> RecipeDetail? { func getServer() async -> RecipeDetail? {
let (recipe, error) = await api.getRecipe( let (recipe, error) = await api.getRecipe(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
id: id id: id
) )
@@ -291,7 +287,6 @@ import UIKit
func getServer() async -> UIImage? { func getServer() async -> UIImage? {
let (image, _) = await api.getImage( let (image, _) = await api.getImage(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
id: id, id: id,
size: size size: size
@@ -368,7 +363,6 @@ import UIKit
func getServer() async -> [RecipeKeyword]? { func getServer() async -> [RecipeKeyword]? {
let (tags, _) = await api.getTags( let (tags, _) = await api.getTags(
from: userSettings.serverAddress,
auth: userSettings.authString auth: userSettings.authString
) )
return tags return tags
@@ -423,7 +417,6 @@ import UIKit
*/ */
func deleteRecipe(withId id: Int, categoryName: String) async -> RequestAlert? { func deleteRecipe(withId id: Int, categoryName: String) async -> RequestAlert? {
let (error) = await api.deleteRecipe( let (error) = await api.deleteRecipe(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
id: id id: id
) )
@@ -455,7 +448,6 @@ import UIKit
*/ */
func checkServerConnection() async -> Bool { func checkServerConnection() async -> Bool {
let (categories, _) = await api.getCategories( let (categories, _) = await api.getCategories(
from: userSettings.serverAddress,
auth: userSettings.authString auth: userSettings.authString
) )
if let categories = categories { if let categories = categories {
@@ -485,13 +477,11 @@ import UIKit
var error: NetworkError? = nil var error: NetworkError? = nil
if createNew { if createNew {
error = await api.createRecipe( error = await api.createRecipe(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
recipe: recipeDetail recipe: recipeDetail
) )
} else { } else {
error = await api.updateRecipe( error = await api.updateRecipe(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
recipe: recipeDetail recipe: recipeDetail
) )
@@ -505,7 +495,6 @@ import UIKit
func importRecipe(url: String) async -> (RecipeDetail?, RequestAlert?) { func importRecipe(url: String) async -> (RecipeDetail?, RequestAlert?) {
guard let data = JSONEncoder.safeEncode(RecipeImportRequest(url: url)) else { return (nil, .REQUEST_DROPPED) } guard let data = JSONEncoder.safeEncode(RecipeImportRequest(url: url)) else { return (nil, .REQUEST_DROPPED) }
let (recipeDetail, error) = await api.importRecipe( let (recipeDetail, error) = await api.importRecipe(
from: userSettings.serverAddress,
auth: userSettings.authString, auth: userSettings.authString,
data: data data: data
) )

View File

@@ -181,11 +181,8 @@ struct LoginTextField: View {
struct ServerAddressField: View { struct ServerAddressField: View {
@Binding var addressString: String @ObservedObject var userSettings = UserSettings.shared
@State var serverAddress: String = ""
@State var serverProtocol: ServerProtocol = .https @State var serverProtocol: ServerProtocol = .https
@State var serverPort: String = ""
@State var useNonStandardPort: Bool = false
enum ServerProtocol: String { enum ServerProtocol: String {
case https="https://", http="http://" case https="https://", http="http://"
@@ -205,22 +202,26 @@ struct ServerAddressField: View {
}.pickerStyle(.menu) }.pickerStyle(.menu)
.tint(.white) .tint(.white)
.font(.headline) .font(.headline)
.onChange(of: serverProtocol) { color in
userSettings.serverProtocol = color.rawValue
}
LoginTextField(example: "e.g.: example.com", text: $serverAddress) 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))
)
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") LoginLabel(text: "Full server address")
.padding(.top) .padding(.top)
Text(createServerAddressString()) Text(userSettings.serverProtocol + userSettings.serverAddress)
.foregroundColor(.white) .foregroundColor(.white)
.padding(.vertical, 5) .padding(.vertical, 5)
} }
@@ -230,26 +231,13 @@ struct ServerAddressField: View {
.stroke(.white, lineWidth: 2) .stroke(.white, lineWidth: 2)
.foregroundColor(.clear) .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 { struct ServerAddressField_Preview: PreviewProvider {
static var previews: some View { static var previews: some View {
ServerAddressField(addressString: .constant(""), ServerAddressField()
serverAddress: "example.com",
serverProtocol: .https,
serverPort: "80",
useNonStandardPort: true)
.previewLayout(.sizeThatFits) .previewLayout(.sizeThatFits)
.padding() .padding()
.background(Color.nextcloudBlue) .background(Color.nextcloudBlue)

View File

@@ -26,14 +26,9 @@ struct TokenLoginView: View {
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
/*LoginLabel(text: "Server address") ServerAddressField()
LoginTextField(example: "e.g.: example.com", text: $userSettings.serverAddress)
.focused($focusedField, equals: .server)
.textContentType(.URL)
.submitLabel(.next)
.padding(.bottom) .padding(.bottom)
*/
ServerAddressField(addressString: $userSettings.serverAddress)
LoginLabel(text: "User name") LoginLabel(text: "User name")
BorderedLoginTextField(example: "username", text: $userSettings.username) BorderedLoginTextField(example: "username", text: $userSettings.username)
.focused($focusedField, equals: .username) .focused($focusedField, equals: .username)

View File

@@ -9,11 +9,10 @@ import Foundation
import SwiftUI import SwiftUI
enum V2LoginStage: LoginStage { enum V2LoginStage: LoginStage {
case serverAddress, login, validate case login, validate
func next() -> V2LoginStage { func next() -> V2LoginStage {
switch self { switch self {
case .serverAddress: return .login
case .login: return .validate case .login: return .validate
case .validate: return .validate case .validate: return .validate
} }
@@ -21,8 +20,7 @@ enum V2LoginStage: LoginStage {
func previous() -> V2LoginStage { func previous() -> V2LoginStage {
switch self { switch self {
case .serverAddress: return .serverAddress case .login: return .login
case .login: return .serverAddress
case .validate: return .login case .validate: return .login
} }
} }
@@ -34,9 +32,8 @@ struct V2LoginView: View {
@Binding var showAlert: Bool @Binding var showAlert: Bool
@Binding var alertMessage: String @Binding var alertMessage: String
@State var loginStage: V2LoginStage = .serverAddress @State var loginStage: V2LoginStage = .login
@State var loginRequest: LoginV2Request? = nil @State var loginRequest: LoginV2Request? = nil
@FocusState private var focusedField: Field?
@State var userSettings = UserSettings.shared @State var userSettings = UserSettings.shared
@@ -50,27 +47,14 @@ struct V2LoginView: View {
var body: some View { var body: some View {
ScrollView { ScrollView {
VStack(alignment: .leading) { VStack(alignment: .leading) {
/*LoginLabel(text: "Server address") ServerAddressField()
.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)
CollapsibleView { CollapsibleView {
VStack(alignment: .leading) { 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.") Text("Make sure to enter the server address in the form 'example.com', or \n'<server address>:<port>'\n when a non-standard port is used.")
if let loginRequest = loginRequest { .padding(.bottom)
Text("If the login button does not open your browser, copy the following link and paste it in your browser manually:") 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'.")
Text(loginRequest.login) .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: { } title: {
Text("Show help") Text("Show help")
@@ -78,43 +62,45 @@ struct V2LoginView: View {
.font(.headline) .font(.headline)
}.padding() }.padding()
if loginStage == .login || loginStage == .validate { if loginRequest != nil {
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'.") Button("Copy Link") {
.font(.subheadline) UIPasteboard.general.string = loginRequest!.login
.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()
} }
.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 { if loginStage == .validate {
Spacer() Spacer()