Task.swift 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. //
  2. // Task.swift
  3. // Todos
  4. //
  5. // Created by Sam Jaffe on 2/28/26.
  6. //
  7. import Foundation
  8. import SwiftData
  9. enum Status : String, CaseIterable, Identifiable, Codable {
  10. case Todo = " "
  11. case Complete = "V"
  12. case InProgress = "C"
  13. case Hiatus = "H"
  14. case Waiting = "R"
  15. var id: Self { self }
  16. var description: String { self.rawValue }
  17. var isStrong: Bool {
  18. self == .Complete || self == .Hiatus || self == .Waiting
  19. }
  20. var label: String {
  21. switch (self) {
  22. case .Todo: return "square.and.pencil"
  23. case .Complete: return "checkmark"
  24. case .InProgress: return "ellipsis.circle"
  25. case .Hiatus: return "clock.badge.questionmark"
  26. case .Waiting: return "airplane.circle"
  27. }
  28. }
  29. }
  30. @Model
  31. final class SubTask : Codable {
  32. var name: String
  33. var notes: String = ""
  34. var status: Status = Status.Todo
  35. init(name: String) {
  36. self.name = name
  37. }
  38. func yaml(_ indent: Int = 0) -> String {
  39. let h1 = String(repeating: " ", count: indent)
  40. let h2 = String(repeating: " ", count: indent + 1)
  41. var rval = h1 + "- [\(status.rawValue)] \(name)\n"
  42. if !notes.isEmpty {
  43. rval += h2 + "# " + notes.replacingOccurrences(of: "\n", with: "\n" + h2 + "# ") + "\n"
  44. }
  45. return rval
  46. }
  47. enum CodingKeys : CodingKey { case name, notes, status }
  48. required init(from decoder: any Decoder) throws {
  49. let container = try decoder.container(keyedBy: CodingKeys.self)
  50. name = try container.decode(String.self, forKey: .name)
  51. notes = try container.decode(String.self, forKey: .notes)
  52. status = try container.decode(Status.self, forKey: .status)
  53. }
  54. func encode(to encoder: any Encoder) throws {
  55. var container = encoder.container(keyedBy: CodingKeys.self)
  56. try container.encode(name, forKey: .name)
  57. try container.encode(notes, forKey: .notes)
  58. try container.encode(status, forKey: .status)
  59. }
  60. }
  61. @Model
  62. final class Tag : Codable {
  63. var id: String
  64. init(id: String) {
  65. self.id = id
  66. }
  67. func like(_ str: String) -> Bool {
  68. return id.caseInsensitiveCompare(str) == .orderedSame
  69. }
  70. required init(from decoder: any Decoder) throws {
  71. id = try decoder.singleValueContainer().decode(String.self)
  72. }
  73. func encode(to encoder: any Encoder) throws {
  74. var single = encoder.singleValueContainer()
  75. try single.encode(id)
  76. }
  77. }
  78. @Model
  79. final class Task : Codable {
  80. var name: String
  81. @Relationship(deleteRule: .cascade)
  82. var tags: [Tag] = []
  83. @Relationship(deleteRule: .cascade)
  84. var subtasks: [SubTask] = []
  85. var notes: String = ""
  86. var status: Status = Status.Todo
  87. init(name: String) {
  88. self.name = name
  89. }
  90. func yaml(_ indent: Int = 0) -> String {
  91. let h1 = String(repeating: " ", count: indent)
  92. let h2 = String(repeating: " ", count: indent + 1)
  93. var rval = h1 + "[\(status.rawValue)] \(name) "
  94. rval += "(\(tags.map(\.id).joined(separator: " ")))\n"
  95. if !notes.isEmpty {
  96. rval += h2 + "# " + notes.replacingOccurrences(of: "\n", with: "\n" + h2 + "# ") + "\n"
  97. }
  98. rval += subtasks.map({ $0.yaml(indent + 1) }).joined()
  99. return rval
  100. }
  101. enum CodingKeys : CodingKey { case name, tags, subtasks, notes, status }
  102. required init(from decoder: any Decoder) throws {
  103. let container = try decoder.container(keyedBy: CodingKeys.self)
  104. name = try container.decode(String.self, forKey: .name)
  105. tags = try container.decode([Tag].self, forKey: .tags)
  106. subtasks = try container.decode([SubTask].self, forKey: .subtasks)
  107. notes = try container.decode(String.self, forKey: .notes)
  108. status = try container.decode(Status.self, forKey: .status)
  109. }
  110. func encode(to encoder: any Encoder) throws {
  111. var container = encoder.container(keyedBy: CodingKeys.self)
  112. try container.encode(name, forKey: .name)
  113. try container.encode(tags, forKey: .tags)
  114. try container.encode(subtasks, forKey: .subtasks)
  115. try container.encode(notes, forKey: .notes)
  116. try container.encode(status, forKey: .status)
  117. }
  118. }