New Recipe Edit View

This commit is contained in:
VincentMeilinger
2024-02-18 12:25:18 +01:00
parent e88f1d329d
commit d3e0366ce6
23 changed files with 1261 additions and 1086 deletions

View File

@@ -0,0 +1,135 @@
//
// AlertHandler.swift
// Nextcloud Cookbook iOS Client
//
// Created by Vincent Meilinger on 18.10.23.
//
import Foundation
import SwiftUI
protocol UserAlert: Error {
var localizedTitle: LocalizedStringKey { get }
var localizedDescription: LocalizedStringKey { get }
var alertButtons: [AlertButton] { get }
}
enum AlertButton: LocalizedStringKey, Identifiable {
var id: Self {
return self
}
case OK = "Ok", DELETE = "Delete", CANCEL = "Cancel"
}
enum RecipeAlert: UserAlert {
case NO_TITLE,
DUPLICATE,
UPLOAD_ERROR,
CONFIRM_DELETE,
LOGIN_FAILED,
GENERIC,
CUSTOM(title: LocalizedStringKey, description: LocalizedStringKey)
var localizedDescription: LocalizedStringKey {
switch self {
case .NO_TITLE:
return "Please enter a recipe name."
case .DUPLICATE:
return "A recipe with that name already exists."
case .UPLOAD_ERROR:
return "Unable to upload your recipe. Please check your internet connection."
case .CONFIRM_DELETE:
return "This action is not reversible!"
case .LOGIN_FAILED:
return "Please check your credentials and internet connection."
case .CUSTOM(title: _, description: let description):
return description
default:
return "An unknown error occured."
}
}
var localizedTitle: LocalizedStringKey {
switch self {
case .NO_TITLE:
return "Missing recipe name."
case .DUPLICATE:
return "Duplicate recipe."
case .UPLOAD_ERROR:
return "Network error."
case .CONFIRM_DELETE:
return "Delete recipe?"
case .LOGIN_FAILED:
return "Login failed."
case .CUSTOM(title: let title, description: _):
return title
default:
return "Error."
}
}
var alertButtons: [AlertButton] {
switch self {
case .CONFIRM_DELETE:
return [.CANCEL, .DELETE]
default:
return [.OK]
}
}
}
enum RecipeImportAlert: UserAlert {
case BAD_URL,
CHECK_CONNECTION,
WEBSITE_NOT_SUPPORTED
var localizedDescription: LocalizedStringKey {
switch self {
case .BAD_URL: return "Please check the entered URL."
case .CHECK_CONNECTION: return "Unable to load website content. Please check your internet connection."
case .WEBSITE_NOT_SUPPORTED: return "This website might not be currently supported. If this appears incorrect, you can use the support options in the app settings to raise awareness about this issue."
}
}
var localizedTitle: LocalizedStringKey {
switch self {
case .BAD_URL: return "Bad URL"
case .CHECK_CONNECTION: return "Connection error"
case .WEBSITE_NOT_SUPPORTED: return "Parsing error"
}
}
var alertButtons: [AlertButton] {
return [.OK]
}
}
enum RequestAlert: UserAlert {
case REQUEST_DELAYED,
REQUEST_DROPPED
var localizedDescription: LocalizedStringKey {
switch self {
case .REQUEST_DELAYED: return "Could not establish a connection to the server. The action will be retried upon reconnection."
case .REQUEST_DROPPED: return "Unable to complete action."
}
}
var localizedTitle: LocalizedStringKey {
switch self {
case .REQUEST_DELAYED: return "Action delayed"
case .REQUEST_DROPPED: return "Error"
}
}
var alertButtons: [AlertButton] {
return [.OK]
}
}

View File

@@ -0,0 +1,155 @@
//
// 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
}
}
}

View File

@@ -0,0 +1,34 @@
//
// SupportedLanguage.swift
// Nextcloud Cookbook iOS Client
//
// Created by Vincent Meilinger on 18.10.23.
//
import Foundation
enum SupportedLanguage: String, Codable {
case DEVICE = "device",
EN = "en",
DE = "de",
ES = "es",
FR = "fr"
func descriptor() -> String {
switch self {
case .DEVICE:
return String(localized: "Same as Device")
case .EN:
return "English"
case .DE:
return "Deutsch"
case .ES:
return "Español"
case .FR:
return "Français"
}
}
static let allValues = [DEVICE, EN, DE, ES, FR]
}