Improved date handling (DurationComponents)
This commit is contained in:
@@ -18,7 +18,6 @@
|
|||||||
A70171AF2AB2116B00064C43 /* NetworkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171AE2AB2116B00064C43 /* NetworkHandler.swift */; };
|
A70171AF2AB2116B00064C43 /* NetworkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171AE2AB2116B00064C43 /* NetworkHandler.swift */; };
|
||||||
A70171B12AB211DF00064C43 /* CustomError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171B02AB211DF00064C43 /* CustomError.swift */; };
|
A70171B12AB211DF00064C43 /* CustomError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171B02AB211DF00064C43 /* CustomError.swift */; };
|
||||||
A70171B42AB2122900064C43 /* NetworkRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171B32AB2122900064C43 /* NetworkRequests.swift */; };
|
A70171B42AB2122900064C43 /* NetworkRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171B32AB2122900064C43 /* NetworkRequests.swift */; };
|
||||||
A70171B92AB399FB00064C43 /* DateFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171B82AB399FB00064C43 /* DateFormatterExtension.swift */; };
|
|
||||||
A70171BE2AB4987900064C43 /* CategoryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171BD2AB4987900064C43 /* CategoryDetailView.swift */; };
|
A70171BE2AB4987900064C43 /* CategoryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171BD2AB4987900064C43 /* CategoryDetailView.swift */; };
|
||||||
A70171C02AB498A900064C43 /* RecipeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171BF2AB498A900064C43 /* RecipeDetailView.swift */; };
|
A70171C02AB498A900064C43 /* RecipeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171BF2AB498A900064C43 /* RecipeDetailView.swift */; };
|
||||||
A70171C22AB498C600064C43 /* RecipeCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171C12AB498C600064C43 /* RecipeCardView.swift */; };
|
A70171C22AB498C600064C43 /* RecipeCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70171C12AB498C600064C43 /* RecipeCardView.swift */; };
|
||||||
@@ -35,6 +34,7 @@
|
|||||||
A74D33C32AFCD1C300D06555 /* RecipeScraper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74D33C22AFCD1C300D06555 /* RecipeScraper.swift */; };
|
A74D33C32AFCD1C300D06555 /* RecipeScraper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74D33C22AFCD1C300D06555 /* RecipeScraper.swift */; };
|
||||||
A76B8A6F2ADFFA8800096CEC /* SupportedLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76B8A6E2ADFFA8800096CEC /* SupportedLanguage.swift */; };
|
A76B8A6F2ADFFA8800096CEC /* SupportedLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76B8A6E2ADFFA8800096CEC /* SupportedLanguage.swift */; };
|
||||||
A76B8A712AE002AE00096CEC /* Alerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76B8A702AE002AE00096CEC /* Alerts.swift */; };
|
A76B8A712AE002AE00096CEC /* Alerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = A76B8A702AE002AE00096CEC /* Alerts.swift */; };
|
||||||
|
A79AA8E02AFF80E3007D25F2 /* DurationComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79AA8DF2AFF80E3007D25F2 /* DurationComponents.swift */; };
|
||||||
A7AEAE642AD5521400135378 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A7AEAE632AD5521400135378 /* Localizable.xcstrings */; };
|
A7AEAE642AD5521400135378 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A7AEAE632AD5521400135378 /* Localizable.xcstrings */; };
|
||||||
A7F3F8E82ACBFC760076C227 /* KeywordPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F3F8E72ACBFC760076C227 /* KeywordPickerView.swift */; };
|
A7F3F8E82ACBFC760076C227 /* KeywordPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F3F8E72ACBFC760076C227 /* KeywordPickerView.swift */; };
|
||||||
A7F3F8EA2ACC221C0076C227 /* CategoryPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F3F8E92ACC221C0076C227 /* CategoryPickerView.swift */; };
|
A7F3F8EA2ACC221C0076C227 /* CategoryPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7F3F8E92ACC221C0076C227 /* CategoryPickerView.swift */; };
|
||||||
@@ -73,7 +73,6 @@
|
|||||||
A70171AE2AB2116B00064C43 /* NetworkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkHandler.swift; sourceTree = "<group>"; };
|
A70171AE2AB2116B00064C43 /* NetworkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkHandler.swift; sourceTree = "<group>"; };
|
||||||
A70171B02AB211DF00064C43 /* CustomError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomError.swift; sourceTree = "<group>"; };
|
A70171B02AB211DF00064C43 /* CustomError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomError.swift; sourceTree = "<group>"; };
|
||||||
A70171B32AB2122900064C43 /* NetworkRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequests.swift; sourceTree = "<group>"; };
|
A70171B32AB2122900064C43 /* NetworkRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequests.swift; sourceTree = "<group>"; };
|
||||||
A70171B82AB399FB00064C43 /* DateFormatterExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatterExtension.swift; sourceTree = "<group>"; };
|
|
||||||
A70171BD2AB4987900064C43 /* CategoryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryDetailView.swift; sourceTree = "<group>"; };
|
A70171BD2AB4987900064C43 /* CategoryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryDetailView.swift; sourceTree = "<group>"; };
|
||||||
A70171BF2AB498A900064C43 /* RecipeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeDetailView.swift; sourceTree = "<group>"; };
|
A70171BF2AB498A900064C43 /* RecipeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeDetailView.swift; sourceTree = "<group>"; };
|
||||||
A70171C12AB498C600064C43 /* RecipeCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeCardView.swift; sourceTree = "<group>"; };
|
A70171C12AB498C600064C43 /* RecipeCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeCardView.swift; sourceTree = "<group>"; };
|
||||||
@@ -90,6 +89,7 @@
|
|||||||
A74D33C22AFCD1C300D06555 /* RecipeScraper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeScraper.swift; sourceTree = "<group>"; };
|
A74D33C22AFCD1C300D06555 /* RecipeScraper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeScraper.swift; sourceTree = "<group>"; };
|
||||||
A76B8A6E2ADFFA8800096CEC /* SupportedLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportedLanguage.swift; sourceTree = "<group>"; };
|
A76B8A6E2ADFFA8800096CEC /* SupportedLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportedLanguage.swift; sourceTree = "<group>"; };
|
||||||
A76B8A702AE002AE00096CEC /* Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alerts.swift; sourceTree = "<group>"; };
|
A76B8A702AE002AE00096CEC /* Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alerts.swift; sourceTree = "<group>"; };
|
||||||
|
A79AA8DF2AFF80E3007D25F2 /* DurationComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationComponents.swift; sourceTree = "<group>"; };
|
||||||
A7AEAE632AD5521400135378 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
A7AEAE632AD5521400135378 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||||
A7F3F8E72ACBFC760076C227 /* KeywordPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeywordPickerView.swift; sourceTree = "<group>"; };
|
A7F3F8E72ACBFC760076C227 /* KeywordPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeywordPickerView.swift; sourceTree = "<group>"; };
|
||||||
A7F3F8E92ACC221C0076C227 /* CategoryPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerView.swift; sourceTree = "<group>"; };
|
A7F3F8E92ACC221C0076C227 /* CategoryPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryPickerView.swift; sourceTree = "<group>"; };
|
||||||
@@ -228,6 +228,7 @@
|
|||||||
A70171C32AB4A31200064C43 /* DataStore.swift */,
|
A70171C32AB4A31200064C43 /* DataStore.swift */,
|
||||||
A70171C52AB4C43A00064C43 /* DataModels.swift */,
|
A70171C52AB4C43A00064C43 /* DataModels.swift */,
|
||||||
A70171CA2AB4CD1700064C43 /* UserSettings.swift */,
|
A70171CA2AB4CD1700064C43 /* UserSettings.swift */,
|
||||||
|
A79AA8DF2AFF80E3007D25F2 /* DurationComponents.swift */,
|
||||||
);
|
);
|
||||||
path = Data;
|
path = Data;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -235,7 +236,6 @@
|
|||||||
A703226B2ABAF60D00D7C4ED /* Extensions */ = {
|
A703226B2ABAF60D00D7C4ED /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A70171B82AB399FB00064C43 /* DateFormatterExtension.swift */,
|
|
||||||
A70322692ABAF49800D7C4ED /* JSONCoderExtension.swift */,
|
A70322692ABAF49800D7C4ED /* JSONCoderExtension.swift */,
|
||||||
A703226E2ABB1DD700D7C4ED /* ColorExtension.swift */,
|
A703226E2ABB1DD700D7C4ED /* ColorExtension.swift */,
|
||||||
);
|
);
|
||||||
@@ -404,10 +404,10 @@
|
|||||||
A70171C42AB4A31200064C43 /* DataStore.swift in Sources */,
|
A70171C42AB4A31200064C43 /* DataStore.swift in Sources */,
|
||||||
A70171AF2AB2116B00064C43 /* NetworkHandler.swift in Sources */,
|
A70171AF2AB2116B00064C43 /* NetworkHandler.swift in Sources */,
|
||||||
A70171B42AB2122900064C43 /* NetworkRequests.swift in Sources */,
|
A70171B42AB2122900064C43 /* NetworkRequests.swift in Sources */,
|
||||||
A70171B92AB399FB00064C43 /* DateFormatterExtension.swift in Sources */,
|
|
||||||
A70171BE2AB4987900064C43 /* CategoryDetailView.swift in Sources */,
|
A70171BE2AB4987900064C43 /* CategoryDetailView.swift in Sources */,
|
||||||
A70171C62AB4C43A00064C43 /* DataModels.swift in Sources */,
|
A70171C62AB4C43A00064C43 /* DataModels.swift in Sources */,
|
||||||
A7F3F8E82ACBFC760076C227 /* KeywordPickerView.swift in Sources */,
|
A7F3F8E82ACBFC760076C227 /* KeywordPickerView.swift in Sources */,
|
||||||
|
A79AA8E02AFF80E3007D25F2 /* DurationComponents.swift in Sources */,
|
||||||
A70171C02AB498A900064C43 /* RecipeDetailView.swift in Sources */,
|
A70171C02AB498A900064C43 /* RecipeDetailView.swift in Sources */,
|
||||||
A7F3F8EA2ACC221C0076C227 /* CategoryPickerView.swift in Sources */,
|
A7F3F8EA2ACC221C0076C227 /* CategoryPickerView.swift in Sources */,
|
||||||
A70171CD2AB501B100064C43 /* SettingsView.swift in Sources */,
|
A70171CD2AB501B100064C43 /* SettingsView.swift in Sources */,
|
||||||
|
|||||||
@@ -91,6 +91,16 @@ struct RecipeDetail: Codable {
|
|||||||
recipeInstructions = []
|
recipeInstructions = []
|
||||||
nutrition = [:]
|
nutrition = [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getKeywordsArray() -> [String] {
|
||||||
|
return keywords.components(separatedBy: ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func setKeywordsFromArray(_ keywordsArray: [String]) {
|
||||||
|
if !self.keywords.isEmpty {
|
||||||
|
self.keywords = keywordsArray.joined(separator: ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RecipeDetail {
|
extension RecipeDetail {
|
||||||
|
|||||||
99
Nextcloud Cookbook iOS Client/Data/DurationComponents.swift
Normal file
99
Nextcloud Cookbook iOS Client/Data/DurationComponents.swift
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// Duration.swift
|
||||||
|
// Nextcloud Cookbook iOS Client
|
||||||
|
//
|
||||||
|
// Created by Vincent Meilinger on 11.11.23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
|
class DurationComponents: ObservableObject {
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// DateFormatterExtension.swift
|
|
||||||
// Nextcloud Cookbook iOS Client
|
|
||||||
//
|
|
||||||
// Created by Vincent Meilinger on 14.09.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension Formatter {
|
|
||||||
static let positional: DateComponentsFormatter = {
|
|
||||||
let formatter = DateComponentsFormatter()
|
|
||||||
formatter.unitsStyle = .positional
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
static func formatDate(duration: String) -> String {
|
|
||||||
var duration = duration
|
|
||||||
if duration.hasPrefix("PT") { duration.removeFirst(2) }
|
|
||||||
var hour: Int = 0, minute: Int = 0
|
|
||||||
if let index = duration.firstIndex(of: "H") {
|
|
||||||
hour = Int(duration[..<index]) ?? 0
|
|
||||||
duration.removeSubrange(...index)
|
|
||||||
}
|
|
||||||
if let index = duration.firstIndex(of: "M") {
|
|
||||||
minute = Int(duration[..<index]) ?? 0
|
|
||||||
duration.removeSubrange(...index)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hour == 0 && minute != 0 {
|
|
||||||
return "\(minute)min"
|
|
||||||
}
|
|
||||||
if hour != 0 && minute == 0 {
|
|
||||||
return "\(hour)h"
|
|
||||||
}
|
|
||||||
if hour != 0 && minute != 0 {
|
|
||||||
return "\(hour)h \(minute)"
|
|
||||||
}
|
|
||||||
return "--"
|
|
||||||
}
|
|
||||||
|
|
||||||
static func stringToComponents(duration: String) -> (Int, Int) {
|
|
||||||
var duration = duration
|
|
||||||
if duration.hasPrefix("PT") { duration.removeFirst(2) }
|
|
||||||
var hour: Int = 0, minute: Int = 0
|
|
||||||
if let index = duration.firstIndex(of: "H") {
|
|
||||||
hour = Int(duration[..<index]) ?? 0
|
|
||||||
duration.removeSubrange(...index)
|
|
||||||
}
|
|
||||||
if let index = duration.firstIndex(of: "M") {
|
|
||||||
minute = Int(duration[..<index]) ?? 0
|
|
||||||
duration.removeSubrange(...index)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (hour, minute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,6 +22,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"-" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
":" : {
|
":" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -88,6 +91,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"%lld h" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"%lld h, %lld min" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "%1$lld h, %2$lld min"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"%lld min" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"%lld." : {
|
"%lld." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ fileprivate struct RecipeDurationSection: View {
|
|||||||
if let prepTime = recipeDetail.prepTime {
|
if let prepTime = recipeDetail.prepTime {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
SecondaryLabel(text: LocalizedStringKey("Preparation"))
|
SecondaryLabel(text: LocalizedStringKey("Preparation"))
|
||||||
Text(DateFormatter.formatDate(duration: prepTime))
|
Text(DurationComponents.ptToText(prepTime))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ fileprivate struct RecipeDurationSection: View {
|
|||||||
if let cookTime = recipeDetail.cookTime {
|
if let cookTime = recipeDetail.cookTime {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
SecondaryLabel(text: LocalizedStringKey("Cooking"))
|
SecondaryLabel(text: LocalizedStringKey("Cooking"))
|
||||||
Text(DateFormatter.formatDate(duration: cookTime))
|
Text(DurationComponents.ptToText(cookTime))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ fileprivate struct RecipeDurationSection: View {
|
|||||||
if let totalTime = recipeDetail.totalTime {
|
if let totalTime = recipeDetail.totalTime {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
SecondaryLabel(text: LocalizedStringKey("Total time"))
|
SecondaryLabel(text: LocalizedStringKey("Total time"))
|
||||||
Text(DateFormatter.formatDate(duration: totalTime))
|
Text(DurationComponents.ptToText(totalTime))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ import PhotosUI
|
|||||||
|
|
||||||
struct RecipeEditView: View {
|
struct RecipeEditView: View {
|
||||||
@ObservedObject var viewModel: MainViewModel
|
@ObservedObject var viewModel: MainViewModel
|
||||||
@State var recipe: RecipeDetail = RecipeDetail() {
|
@State var recipe: RecipeDetail = RecipeDetail()
|
||||||
didSet {
|
|
||||||
prepareView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Binding var isPresented: Bool
|
@Binding var isPresented: Bool
|
||||||
@State var uploadNew: Bool = true
|
@State var uploadNew: Bool = true
|
||||||
|
|
||||||
@@ -25,9 +21,9 @@ struct RecipeEditView: View {
|
|||||||
@State private var alertType: UserAlert = RecipeCreationError.GENERIC
|
@State private var alertType: UserAlert = RecipeCreationError.GENERIC
|
||||||
@State private var alertAction: () -> () = {}
|
@State private var alertAction: () -> () = {}
|
||||||
|
|
||||||
@StateObject private var prepDuration: Duration = Duration()
|
@StateObject private var prepDuration: DurationComponents = DurationComponents()
|
||||||
@StateObject private var cookDuration: Duration = Duration()
|
@StateObject private var cookDuration: DurationComponents = DurationComponents()
|
||||||
@StateObject private var totalDuration: Duration = Duration()
|
@StateObject private var totalDuration: DurationComponents = DurationComponents()
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@State private var keywords: [String] = []
|
@State private var keywords: [String] = []
|
||||||
@State private var keywordSuggestions: [String] = []
|
@State private var keywordSuggestions: [String] = []
|
||||||
@@ -94,6 +90,7 @@ struct RecipeEditView: View {
|
|||||||
let (scrapedRecipe, error) = try await RecipeScraper().scrape(url: importURL)
|
let (scrapedRecipe, error) = try await RecipeScraper().scrape(url: importURL)
|
||||||
if let scrapedRecipe = scrapedRecipe {
|
if let scrapedRecipe = scrapedRecipe {
|
||||||
self.recipe = scrapedRecipe
|
self.recipe = scrapedRecipe
|
||||||
|
prepareView()
|
||||||
}
|
}
|
||||||
if let error = error {
|
if let error = error {
|
||||||
self.alertType = error
|
self.alertType = error
|
||||||
@@ -200,13 +197,10 @@ struct RecipeEditView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createRecipe() {
|
func createRecipe() {
|
||||||
self.recipe.prepTime = prepDuration.format()
|
self.recipe.prepTime = prepDuration.toPTString()
|
||||||
self.recipe.cookTime = cookDuration.format()
|
self.recipe.cookTime = cookDuration.toPTString()
|
||||||
self.recipe.totalTime = totalDuration.format()
|
self.recipe.totalTime = totalDuration.toPTString()
|
||||||
|
self.recipe.setKeywordsFromArray(keywords)
|
||||||
if !self.keywords.isEmpty {
|
|
||||||
self.recipe.keywords = self.keywords.joined(separator: ",")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func recipeValid() -> Bool {
|
func recipeValid() -> Bool {
|
||||||
@@ -316,19 +310,16 @@ struct RecipeEditView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func prepareView() {
|
func prepareView() {
|
||||||
if uploadNew { return }
|
|
||||||
if let prepTime = recipe.prepTime {
|
if let prepTime = recipe.prepTime {
|
||||||
prepDuration.initFromPT(prepTime)
|
prepDuration.fromPTString(prepTime)
|
||||||
}
|
}
|
||||||
if let cookTime = recipe.cookTime {
|
if let cookTime = recipe.cookTime {
|
||||||
cookDuration.initFromPT(cookTime)
|
cookDuration.fromPTString(cookTime)
|
||||||
}
|
}
|
||||||
if let totalTime = recipe.totalTime {
|
if let totalTime = recipe.totalTime {
|
||||||
totalDuration.initFromPT(totalTime)
|
totalDuration.fromPTString(totalTime)
|
||||||
}
|
|
||||||
for keyword in self.recipe.keywords.components(separatedBy: ",") {
|
|
||||||
keywords.append(keyword)
|
|
||||||
}
|
}
|
||||||
|
self.keywords = recipe.getKeywordsArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +372,7 @@ fileprivate struct EditableListSection: View {
|
|||||||
|
|
||||||
fileprivate struct DurationPicker: View {
|
fileprivate struct DurationPicker: View {
|
||||||
@State var title: LocalizedStringKey
|
@State var title: LocalizedStringKey
|
||||||
@ObservedObject var duration: Duration
|
@ObservedObject var duration: DurationComponents
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
@@ -405,54 +396,7 @@ fileprivate struct DurationPicker: View {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
fileprivate class Duration: ObservableObject {
|
|
||||||
@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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initFromPT(_ 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 format() -> String {
|
|
||||||
return "PT\(hourComponent)H\(minuteComponent)M00S"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user