New Recipe Edit View
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct Category: Codable {
|
||||
let name: String
|
||||
let recipe_count: Int
|
||||
@@ -17,170 +18,17 @@ struct Category: Codable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Category: Identifiable, Hashable {
|
||||
var id: String { name }
|
||||
}
|
||||
|
||||
struct Recipe: Codable {
|
||||
let name: String
|
||||
let keywords: String?
|
||||
let dateCreated: String
|
||||
let dateModified: String
|
||||
let imageUrl: String
|
||||
let imagePlaceholderUrl: String
|
||||
let recipe_id: Int
|
||||
|
||||
// Properties excluded from Codable
|
||||
var storedLocally: Bool? = nil
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case name, keywords, dateCreated, dateModified, imageUrl, imagePlaceholderUrl, recipe_id
|
||||
}
|
||||
}
|
||||
|
||||
extension Recipe: Identifiable, Hashable {
|
||||
var id: String { name }
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct RecipeDetail: Codable {
|
||||
var name: String
|
||||
var keywords: String
|
||||
var dateCreated: String
|
||||
var dateModified: String
|
||||
var imageUrl: String
|
||||
var id: String
|
||||
var prepTime: String?
|
||||
var cookTime: String?
|
||||
var totalTime: String?
|
||||
var description: String
|
||||
var url: String
|
||||
var recipeYield: Int
|
||||
var recipeCategory: String
|
||||
var tool: [String]
|
||||
var recipeIngredient: [String]
|
||||
var recipeInstructions: [String]
|
||||
var nutrition: [String:String]
|
||||
|
||||
init(name: String, keywords: String, dateCreated: String, dateModified: String, imageUrl: String, id: String, prepTime: String? = nil, cookTime: String? = nil, totalTime: String? = nil, description: String, url: String, recipeYield: Int, recipeCategory: String, tool: [String], recipeIngredient: [String], recipeInstructions: [String], nutrition: [String:String]) {
|
||||
self.name = name
|
||||
self.keywords = keywords
|
||||
self.dateCreated = dateCreated
|
||||
self.dateModified = dateModified
|
||||
self.imageUrl = imageUrl
|
||||
self.id = id
|
||||
self.prepTime = prepTime
|
||||
self.cookTime = cookTime
|
||||
self.totalTime = totalTime
|
||||
self.description = description
|
||||
self.url = url
|
||||
self.recipeYield = recipeYield
|
||||
self.recipeCategory = recipeCategory
|
||||
self.tool = tool
|
||||
self.recipeIngredient = recipeIngredient
|
||||
self.recipeInstructions = recipeInstructions
|
||||
self.nutrition = nutrition
|
||||
}
|
||||
|
||||
init() {
|
||||
name = ""
|
||||
keywords = ""
|
||||
dateCreated = ""
|
||||
dateModified = ""
|
||||
imageUrl = ""
|
||||
id = ""
|
||||
prepTime = ""
|
||||
cookTime = ""
|
||||
totalTime = ""
|
||||
description = ""
|
||||
url = ""
|
||||
recipeYield = 0
|
||||
recipeCategory = ""
|
||||
tool = []
|
||||
recipeIngredient = []
|
||||
recipeInstructions = []
|
||||
nutrition = [:]
|
||||
}
|
||||
}
|
||||
|
||||
extension RecipeDetail {
|
||||
static var error: RecipeDetail {
|
||||
return RecipeDetail(
|
||||
name: "Error: Unable to load recipe.",
|
||||
keywords: "",
|
||||
dateCreated: "",
|
||||
dateModified: "",
|
||||
imageUrl: "",
|
||||
id: "",
|
||||
prepTime: "",
|
||||
cookTime: "",
|
||||
totalTime: "",
|
||||
description: "",
|
||||
url: "",
|
||||
recipeYield: 0,
|
||||
recipeCategory: "",
|
||||
tool: [],
|
||||
recipeIngredient: [],
|
||||
recipeInstructions: [],
|
||||
nutrition: [:]
|
||||
)
|
||||
}
|
||||
|
||||
func getKeywordsArray() -> [String] {
|
||||
if keywords == "" { return [] }
|
||||
return keywords.components(separatedBy: ",")
|
||||
}
|
||||
|
||||
mutating func setKeywordsFromArray(_ keywordsArray: [String]) {
|
||||
if !keywordsArray.isEmpty {
|
||||
self.keywords = keywordsArray.joined(separator: ",")
|
||||
}
|
||||
}
|
||||
|
||||
func getNutritionList() -> [String]? {
|
||||
var stringList: [String] = []
|
||||
if let value = nutrition["calories"] { stringList.append("Calories: \(value)") }
|
||||
if let value = nutrition["carbohydrateContent"] { stringList.append("Carbohydrates: \(value)") }
|
||||
if let value = nutrition["cholesterolContent"] { stringList.append("Cholesterol: \(value)") }
|
||||
if let value = nutrition["fatContent"] { stringList.append("Fat: \(value)") }
|
||||
if let value = nutrition["saturatedFatContent"] { stringList.append("Saturated fat: \(value)") }
|
||||
if let value = nutrition["unsaturatedFatContent"] { stringList.append("Unsaturated fat: \(value)") }
|
||||
if let value = nutrition["transFatContent"] { stringList.append("Trans fat: \(value)") }
|
||||
if let value = nutrition["fiberContent"] { stringList.append("Fibers: \(value)") }
|
||||
if let value = nutrition["proteinContent"] { stringList.append("Protein: \(value)") }
|
||||
if let value = nutrition["sodiumContent"] { stringList.append("Sodium: \(value)") }
|
||||
if let value = nutrition["sugarContent"] { stringList.append("Sugar: \(value)") }
|
||||
return stringList.isEmpty ? nil : stringList
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct RecipeImage {
|
||||
enum RecipeImageSize: String {
|
||||
case THUMB="thumb", FULL="full"
|
||||
}
|
||||
var imageExists: Bool = true
|
||||
var thumb: UIImage?
|
||||
var full: UIImage?
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct RecipeKeyword: Codable {
|
||||
let name: String
|
||||
let recipe_count: Int
|
||||
}
|
||||
|
||||
struct RecipeImportRequest: Codable {
|
||||
let url: String
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Login flow
|
||||
|
||||
// MARK: - Login flow
|
||||
|
||||
struct LoginV2Request: Codable {
|
||||
let poll: LoginV2Poll
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
//
|
||||
// Duration.swift
|
||||
// Nextcloud Cookbook iOS Client
|
||||
//
|
||||
// Created by Vincent Meilinger on 11.11.23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
class DurationComponents: ObservableObject {
|
||||
@Published var secondComponent: String = "00" {
|
||||
didSet {
|
||||
if secondComponent.count > 2 {
|
||||
secondComponent = oldValue
|
||||
} else if secondComponent.count == 1 {
|
||||
secondComponent = "0\(secondComponent)"
|
||||
} else if secondComponent.count == 0 {
|
||||
secondComponent = "00"
|
||||
}
|
||||
let filtered = secondComponent.filter { $0.isNumber }
|
||||
if secondComponent != filtered {
|
||||
secondComponent = filtered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var minuteComponent: String = "00" {
|
||||
didSet {
|
||||
if minuteComponent.count > 2 {
|
||||
minuteComponent = oldValue
|
||||
} else if minuteComponent.count == 1 {
|
||||
minuteComponent = "0\(minuteComponent)"
|
||||
} else if minuteComponent.count == 0 {
|
||||
minuteComponent = "00"
|
||||
}
|
||||
let filtered = minuteComponent.filter { $0.isNumber }
|
||||
if minuteComponent != filtered {
|
||||
minuteComponent = filtered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var hourComponent: String = "00" {
|
||||
didSet {
|
||||
if hourComponent.count > 2 {
|
||||
hourComponent = oldValue
|
||||
} else if hourComponent.count == 1 {
|
||||
hourComponent = "0\(hourComponent)"
|
||||
} else if hourComponent.count == 0 {
|
||||
hourComponent = "00"
|
||||
}
|
||||
let filtered = hourComponent.filter { $0.isNumber }
|
||||
if hourComponent != filtered {
|
||||
hourComponent = filtered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func fromPTString(_ PTRepresentation: String) -> DurationComponents {
|
||||
let duration = DurationComponents()
|
||||
let hourRegex = /([0-9]{1,2})H/
|
||||
let minuteRegex = /([0-9]{1,2})M/
|
||||
if let match = PTRepresentation.firstMatch(of: hourRegex) {
|
||||
duration.hourComponent = String(match.1)
|
||||
}
|
||||
if let match = PTRepresentation.firstMatch(of: minuteRegex) {
|
||||
duration.minuteComponent = String(match.1)
|
||||
}
|
||||
return duration
|
||||
}
|
||||
|
||||
func fromPTString(_ PTRepresentation: String) {
|
||||
let hourRegex = /([0-9]{1,2})H/
|
||||
let minuteRegex = /([0-9]{1,2})M/
|
||||
if let match = PTRepresentation.firstMatch(of: hourRegex) {
|
||||
self.hourComponent = String(match.1)
|
||||
}
|
||||
if let match = PTRepresentation.firstMatch(of: minuteRegex) {
|
||||
self.minuteComponent = String(match.1)
|
||||
}
|
||||
}
|
||||
|
||||
func toPTString() -> String {
|
||||
return "PT\(hourComponent)H\(minuteComponent)M00S"
|
||||
}
|
||||
|
||||
func toText() -> LocalizedStringKey {
|
||||
let intHour = Int(hourComponent) ?? 0
|
||||
let intMinute = Int(minuteComponent) ?? 0
|
||||
|
||||
if intHour != 0 && intMinute != 0 {
|
||||
return "\(intHour) h, \(intMinute) min"
|
||||
} else if intHour == 0 && intMinute != 0 {
|
||||
return "\(intMinute) min"
|
||||
} else if intHour != 0 && intMinute == 0 {
|
||||
return "\(intHour) h"
|
||||
} else {
|
||||
return "-"
|
||||
}
|
||||
}
|
||||
|
||||
func toTimerText() -> String {
|
||||
var timeString = ""
|
||||
if hourComponent != "00" {
|
||||
timeString.append("\(hourComponent):")
|
||||
}
|
||||
timeString.append("\(minuteComponent):")
|
||||
timeString.append("\(secondComponent)")
|
||||
return timeString
|
||||
}
|
||||
|
||||
func toSeconds() -> Double {
|
||||
guard let hours = Double(hourComponent) else { return 0 }
|
||||
guard let minutes = Double(minuteComponent) else { return 0 }
|
||||
guard let seconds = Double(secondComponent) else { return 0 }
|
||||
return hours * 3600 + minutes * 60 + seconds
|
||||
}
|
||||
|
||||
func fromSeconds(_ totalSeconds: Int) {
|
||||
let hours = totalSeconds / 3600
|
||||
let minutes = (totalSeconds % 3600) / 60
|
||||
let seconds = totalSeconds % 60
|
||||
self.hourComponent = String(hours)
|
||||
self.minuteComponent = String(minutes)
|
||||
self.secondComponent = String(seconds)
|
||||
}
|
||||
|
||||
static func ptToText(_ ptString: String) -> String? {
|
||||
let hourRegex = /([0-9]{1,2})H/
|
||||
let minuteRegex = /([0-9]{1,2})M/
|
||||
|
||||
var intHour = 0
|
||||
var intMinute = 0
|
||||
if let match = ptString.firstMatch(of: hourRegex) {
|
||||
let hourComponent = String(match.1)
|
||||
intHour = Int(hourComponent) ?? 0
|
||||
}
|
||||
if let match = ptString.firstMatch(of: minuteRegex) {
|
||||
let minuteComponent = String(match.1)
|
||||
intMinute = Int(minuteComponent) ?? 0
|
||||
}
|
||||
|
||||
if intHour != 0 && intMinute != 0 {
|
||||
return "\(intHour) h, \(intMinute) min"
|
||||
} else if intHour == 0 && intMinute != 0 {
|
||||
return "\(intMinute) min"
|
||||
} else if intHour != 0 && intMinute == 0 {
|
||||
return "\(intHour) h"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
214
Nextcloud Cookbook iOS Client/Data/RecipeModels.swift
Normal file
214
Nextcloud Cookbook iOS Client/Data/RecipeModels.swift
Normal file
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// RecipeModels.swift
|
||||
// Nextcloud Cookbook iOS Client
|
||||
//
|
||||
// Created by Vincent Meilinger on 17.02.24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct Recipe: Codable {
|
||||
let name: String
|
||||
let keywords: String?
|
||||
let dateCreated: String
|
||||
let dateModified: String
|
||||
let imageUrl: String
|
||||
let imagePlaceholderUrl: String
|
||||
let recipe_id: Int
|
||||
|
||||
// Properties excluded from Codable
|
||||
var storedLocally: Bool? = nil
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case name, keywords, dateCreated, dateModified, imageUrl, imagePlaceholderUrl, recipe_id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Recipe: Identifiable, Hashable {
|
||||
var id: String { name }
|
||||
}
|
||||
|
||||
|
||||
struct RecipeDetail: Codable {
|
||||
var name: String
|
||||
var keywords: String
|
||||
var dateCreated: String
|
||||
var dateModified: String
|
||||
var imageUrl: String
|
||||
var id: String
|
||||
var prepTime: String?
|
||||
var cookTime: String?
|
||||
var totalTime: String?
|
||||
var description: String
|
||||
var url: String
|
||||
var recipeYield: Int
|
||||
var recipeCategory: String
|
||||
var tool: [String]
|
||||
var recipeIngredient: [String]
|
||||
var recipeInstructions: [String]
|
||||
var nutrition: [String:String]
|
||||
|
||||
init(name: String, keywords: String, dateCreated: String, dateModified: String, imageUrl: String, id: String, prepTime: String? = nil, cookTime: String? = nil, totalTime: String? = nil, description: String, url: String, recipeYield: Int, recipeCategory: String, tool: [String], recipeIngredient: [String], recipeInstructions: [String], nutrition: [String:String]) {
|
||||
self.name = name
|
||||
self.keywords = keywords
|
||||
self.dateCreated = dateCreated
|
||||
self.dateModified = dateModified
|
||||
self.imageUrl = imageUrl
|
||||
self.id = id
|
||||
self.prepTime = prepTime
|
||||
self.cookTime = cookTime
|
||||
self.totalTime = totalTime
|
||||
self.description = description
|
||||
self.url = url
|
||||
self.recipeYield = recipeYield
|
||||
self.recipeCategory = recipeCategory
|
||||
self.tool = tool
|
||||
self.recipeIngredient = recipeIngredient
|
||||
self.recipeInstructions = recipeInstructions
|
||||
self.nutrition = nutrition
|
||||
}
|
||||
|
||||
init() {
|
||||
name = ""
|
||||
keywords = ""
|
||||
dateCreated = ""
|
||||
dateModified = ""
|
||||
imageUrl = ""
|
||||
id = ""
|
||||
prepTime = ""
|
||||
cookTime = ""
|
||||
totalTime = ""
|
||||
description = ""
|
||||
url = ""
|
||||
recipeYield = 0
|
||||
recipeCategory = ""
|
||||
tool = []
|
||||
recipeIngredient = []
|
||||
recipeInstructions = []
|
||||
nutrition = [:]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension RecipeDetail {
|
||||
static var error: RecipeDetail {
|
||||
return RecipeDetail(
|
||||
name: "Error: Unable to load recipe.",
|
||||
keywords: "",
|
||||
dateCreated: "",
|
||||
dateModified: "",
|
||||
imageUrl: "",
|
||||
id: "",
|
||||
prepTime: "",
|
||||
cookTime: "",
|
||||
totalTime: "",
|
||||
description: "",
|
||||
url: "",
|
||||
recipeYield: 0,
|
||||
recipeCategory: "",
|
||||
tool: [],
|
||||
recipeIngredient: [],
|
||||
recipeInstructions: [],
|
||||
nutrition: [:]
|
||||
)
|
||||
}
|
||||
|
||||
func getKeywordsArray() -> [String] {
|
||||
if keywords == "" { return [] }
|
||||
return keywords.components(separatedBy: ",")
|
||||
}
|
||||
|
||||
mutating func setKeywordsFromArray(_ keywordsArray: [String]) {
|
||||
if !keywordsArray.isEmpty {
|
||||
self.keywords = keywordsArray.joined(separator: ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct RecipeImage {
|
||||
enum RecipeImageSize: String {
|
||||
case THUMB="thumb", FULL="full"
|
||||
}
|
||||
var imageExists: Bool = true
|
||||
var thumb: UIImage?
|
||||
var full: UIImage?
|
||||
}
|
||||
|
||||
|
||||
struct RecipeKeyword: Codable {
|
||||
let name: String
|
||||
let recipe_count: Int
|
||||
}
|
||||
|
||||
|
||||
enum Nutrition: CaseIterable {
|
||||
case calories,
|
||||
carbohydrateContent,
|
||||
cholesterolContent,
|
||||
fatContent,
|
||||
saturatedFatContent,
|
||||
unsaturatedFatContent,
|
||||
transFatContent,
|
||||
fiberContent,
|
||||
proteinContent,
|
||||
sodiumContent,
|
||||
sugarContent
|
||||
|
||||
var localizedDescription: LocalizedStringKey {
|
||||
switch self {
|
||||
case .calories:
|
||||
"Calories"
|
||||
case .carbohydrateContent:
|
||||
"Carbohydrate content"
|
||||
case .cholesterolContent:
|
||||
"Cholesterol content"
|
||||
case .fatContent:
|
||||
"Fat content"
|
||||
case .saturatedFatContent:
|
||||
"Saturated fat content"
|
||||
case .unsaturatedFatContent:
|
||||
"Unsaturated fat content"
|
||||
case .transFatContent:
|
||||
"Trans fat content"
|
||||
case .fiberContent:
|
||||
"Fiber content"
|
||||
case .proteinContent:
|
||||
"Protein content"
|
||||
case .sodiumContent:
|
||||
"Sodium content"
|
||||
case .sugarContent:
|
||||
"Sugar content"
|
||||
}
|
||||
}
|
||||
|
||||
var dictKey: String {
|
||||
switch self {
|
||||
case .calories:
|
||||
"calories"
|
||||
case .carbohydrateContent:
|
||||
"carbohydrateContent"
|
||||
case .cholesterolContent:
|
||||
"cholesterolContent"
|
||||
case .fatContent:
|
||||
"fatContent"
|
||||
case .saturatedFatContent:
|
||||
"saturatedFatContent"
|
||||
case .unsaturatedFatContent:
|
||||
"unsaturatedFatContent"
|
||||
case .transFatContent:
|
||||
"transFatContent"
|
||||
case .fiberContent:
|
||||
"fiberContent"
|
||||
case .proteinContent:
|
||||
"proteinContent"
|
||||
case .sodiumContent:
|
||||
"sodiumContent"
|
||||
case .sugarContent:
|
||||
"sugarContent"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user