EditTempTargetForm.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import Foundation
  2. import SwiftUI
  3. struct EditTempTargetForm: View {
  4. @ObservedObject var tempTarget: TempTargetStored
  5. @Environment(\.presentationMode) var presentationMode
  6. @Environment(\.colorScheme) var colorScheme
  7. @StateObject var state: OverrideConfig.StateModel
  8. @State private var name: String
  9. @State private var target: Decimal
  10. @State private var duration: Decimal
  11. @State private var date: Date
  12. @State private var hasChanges = false
  13. @State private var showAlert = false
  14. init(tempTargetToEdit: TempTargetStored, state: OverrideConfig.StateModel) {
  15. tempTarget = tempTargetToEdit
  16. _state = StateObject(wrappedValue: state)
  17. _name = State(initialValue: tempTargetToEdit.name ?? "")
  18. _target = State(initialValue: tempTargetToEdit.target?.decimalValue ?? 0)
  19. _duration = State(initialValue: tempTargetToEdit.duration?.decimalValue ?? 0)
  20. _date = State(initialValue: tempTargetToEdit.date ?? Date())
  21. }
  22. var color: LinearGradient {
  23. colorScheme == .dark ? LinearGradient(
  24. gradient: Gradient(colors: [
  25. Color.bgDarkBlue,
  26. Color.bgDarkerDarkBlue
  27. ]),
  28. startPoint: .top,
  29. endPoint: .bottom
  30. ) :
  31. LinearGradient(
  32. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  33. startPoint: .top,
  34. endPoint: .bottom
  35. )
  36. }
  37. private var formatter: NumberFormatter {
  38. let formatter = NumberFormatter()
  39. formatter.numberStyle = .decimal
  40. formatter.maximumFractionDigits = 0
  41. return formatter
  42. }
  43. private var glucoseFormatter: NumberFormatter {
  44. let formatter = NumberFormatter()
  45. formatter.numberStyle = .decimal
  46. if state.units == .mmolL {
  47. formatter.maximumFractionDigits = 1
  48. } else {
  49. formatter.maximumFractionDigits = 0
  50. }
  51. formatter.roundingMode = .halfUp
  52. return formatter
  53. }
  54. var body: some View {
  55. NavigationView {
  56. Form {
  57. editTempTarget()
  58. saveButton
  59. }.scrollContentBackground(.hidden)
  60. .background(color)
  61. .navigationTitle("Edit Temp Target")
  62. .navigationBarTitleDisplayMode(.inline)
  63. .navigationBarItems(leading: Button("Close") {
  64. presentationMode.wrappedValue.dismiss()
  65. })
  66. .onDisappear {
  67. if !hasChanges {
  68. // Reset UI changes
  69. resetValues()
  70. }
  71. }
  72. .alert(isPresented: $state.showInvalidTargetAlert) {
  73. Alert(
  74. title: Text("Invalid Input"),
  75. message: Text("\(state.alertMessage)"),
  76. dismissButton: .default(Text("OK")) { state.showInvalidTargetAlert = false }
  77. )
  78. }
  79. }
  80. }
  81. @ViewBuilder private func editTempTarget() -> some View {
  82. Section {
  83. VStack {
  84. TextField("Name", text: $name)
  85. .onChange(of: name) { _ in hasChanges = true }
  86. }
  87. } header: {
  88. Text("Name")
  89. }.listRowBackground(Color.chart)
  90. Section {
  91. HStack {
  92. Text("Target")
  93. Spacer()
  94. TextFieldWithToolBar(
  95. text: Binding(
  96. get: { target },
  97. set: {
  98. target = $0
  99. hasChanges = true
  100. }
  101. ),
  102. placeholder: "0",
  103. numberFormatter: glucoseFormatter
  104. )
  105. Text(state.units.rawValue).foregroundColor(.secondary)
  106. }
  107. HStack {
  108. Text("Duration")
  109. Spacer()
  110. TextFieldWithToolBar(
  111. text: Binding(
  112. get: { duration },
  113. set: {
  114. duration = $0
  115. hasChanges = true
  116. }
  117. ),
  118. placeholder: "0",
  119. numberFormatter: formatter
  120. )
  121. Text("minutes").foregroundColor(.secondary)
  122. }
  123. DatePicker("Date", selection: $date)
  124. .onChange(of: date) { _ in hasChanges = true }
  125. }.listRowBackground(Color.chart)
  126. }
  127. private var saveButton: some View {
  128. HStack {
  129. Spacer()
  130. Button(action: {
  131. if !state.isInputInvalid(target: target) {
  132. saveChanges()
  133. do {
  134. guard let moc = tempTarget.managedObjectContext else { return }
  135. guard moc.hasChanges else { return }
  136. try moc.save()
  137. // Disable previous active Temp Target and update View
  138. if let currentActiveTempTarget = state.currentActiveTempTarget {
  139. Task {
  140. await state.disableAllActiveOverrides(
  141. except: currentActiveTempTarget.objectID,
  142. createOverrideRunEntry: false
  143. )
  144. state.updateLatestTempTargetConfiguration()
  145. }
  146. }
  147. hasChanges = false
  148. presentationMode.wrappedValue.dismiss()
  149. } catch {
  150. debugPrint("Failed to edit Temp Target")
  151. }
  152. }
  153. }, label: {
  154. Text("Save")
  155. })
  156. .disabled(!hasChanges)
  157. .frame(maxWidth: .infinity, alignment: .center)
  158. .tint(.white)
  159. Spacer()
  160. }.listRowBackground(hasChanges ? Color(.systemBlue) : Color(.systemGray4))
  161. }
  162. private func saveChanges() {
  163. tempTarget.name = name
  164. tempTarget.target = NSDecimalNumber(decimal: target)
  165. tempTarget.duration = NSDecimalNumber(decimal: duration)
  166. tempTarget.date = date
  167. tempTarget.isUploadedToNS = false
  168. }
  169. private func resetValues() {
  170. name = tempTarget.name ?? ""
  171. target = tempTarget.target?.decimalValue ?? 0
  172. duration = tempTarget.duration?.decimalValue ?? 0
  173. date = tempTarget.date ?? Date()
  174. }
  175. }