AddTempTargetForm.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import Foundation
  2. import SwiftUI
  3. struct AddTempTargetForm: View {
  4. @StateObject var state: OverrideConfig.StateModel
  5. @Environment(\.presentationMode) var presentationMode
  6. @Environment(\.colorScheme) var colorScheme
  7. @Environment(\.dismiss) var dismiss
  8. @State private var showAlert = false
  9. @State private var showPresetAlert = false
  10. @State private var alertString = ""
  11. var color: LinearGradient {
  12. colorScheme == .dark ? LinearGradient(
  13. gradient: Gradient(colors: [
  14. Color.bgDarkBlue,
  15. Color.bgDarkerDarkBlue
  16. ]),
  17. startPoint: .top,
  18. endPoint: .bottom
  19. )
  20. :
  21. LinearGradient(
  22. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  23. startPoint: .top,
  24. endPoint: .bottom
  25. )
  26. }
  27. private var formatter: NumberFormatter {
  28. let formatter = NumberFormatter()
  29. formatter.numberStyle = .decimal
  30. formatter.maximumFractionDigits = 0
  31. return formatter
  32. }
  33. private var glucoseFormatter: NumberFormatter {
  34. let formatter = NumberFormatter()
  35. formatter.numberStyle = .decimal
  36. formatter.maximumFractionDigits = 0
  37. if state.units == .mmolL {
  38. formatter.maximumFractionDigits = 1
  39. }
  40. formatter.roundingMode = .halfUp
  41. return formatter
  42. }
  43. var body: some View {
  44. NavigationView {
  45. Form {
  46. addTempTarget()
  47. }.scrollContentBackground(.hidden).background(color)
  48. .navigationTitle("Add Override")
  49. .navigationBarItems(trailing: Button("Cancel") {
  50. presentationMode.wrappedValue.dismiss()
  51. })
  52. .alert(
  53. "Start Override",
  54. isPresented: $showAlert,
  55. actions: {
  56. Button("Cancel", role: .cancel) { state.isTempTargetEnabled = false }
  57. Button("Start Temp Target", role: .destructive) {
  58. Task {
  59. await setupAlertString()
  60. state.isTempTargetEnabled.toggle()
  61. await state.saveCustomTempTarget()
  62. await state.resetTempTargetState()
  63. dismiss()
  64. }
  65. }
  66. },
  67. message: {
  68. Text(alertString)
  69. }
  70. )
  71. }
  72. }
  73. @ViewBuilder private func addTempTarget() -> some View {
  74. Section {
  75. VStack {
  76. TextField("Name", text: $state.overrideName)
  77. }
  78. } header: {
  79. Text("Name")
  80. }.listRowBackground(Color.chart)
  81. Section {
  82. HStack {
  83. Text("Target")
  84. Spacer()
  85. TextFieldWithToolBar(text: $state.tempTargetTarget, placeholder: "0", numberFormatter: glucoseFormatter)
  86. Text(state.units.rawValue).foregroundColor(.secondary)
  87. }
  88. HStack {
  89. Text("Duration")
  90. Spacer()
  91. TextFieldWithToolBar(text: $state.tempTargetDuration, placeholder: "0", numberFormatter: formatter)
  92. Text("minutes").foregroundColor(.secondary)
  93. }
  94. DatePicker("Date", selection: $state.date)
  95. HStack {
  96. Button {
  97. showAlert.toggle()
  98. }
  99. label: { Text("Enact") }
  100. .disabled(state.tempTargetDuration == 0)
  101. .buttonStyle(BorderlessButtonStyle())
  102. .font(.callout)
  103. .controlSize(.mini)
  104. Button {
  105. Task {
  106. await state.saveTempTargetPreset()
  107. }
  108. }
  109. label: { Text("Save as preset") }
  110. .disabled(state.tempTargetDuration == 0)
  111. .tint(.orange)
  112. .frame(maxWidth: .infinity, alignment: .trailing)
  113. .buttonStyle(BorderlessButtonStyle())
  114. .controlSize(.mini)
  115. }
  116. } header: {
  117. Text("Add Custom Temp Target")
  118. }.listRowBackground(Color.chart)
  119. }
  120. private func setupAlertString() async {
  121. alertString = "\(state.overrideSliderPercentage.formatted(.number)) %, " +
  122. (
  123. state.overrideDuration > 0 || !state
  124. .indefinite ?
  125. (
  126. state
  127. .overrideDuration
  128. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) +
  129. " min."
  130. ) :
  131. NSLocalizedString(" infinite duration.", comment: "")
  132. ) +
  133. (
  134. (state.target == 0 || !state.shouldOverrideTarget) ? "" :
  135. (" Target: " + state.target.formatted() + " " + state.units.rawValue + ".")
  136. )
  137. +
  138. (
  139. state
  140. .smbIsOff ?
  141. NSLocalizedString(
  142. " SMBs are disabled either by schedule or during the entire duration.",
  143. comment: ""
  144. ) : ""
  145. )
  146. +
  147. "\n\n"
  148. +
  149. NSLocalizedString(
  150. "Starting this override will change your profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping ”Start Override” will start your new Override or edit your current active Override.",
  151. comment: ""
  152. )
  153. }
  154. }