AutotuneConfigRootView.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import SwiftUI
  2. import Swinject
  3. extension AutotuneConfig {
  4. struct RootView: BaseView {
  5. let resolver: Resolver
  6. @StateObject var state = StateModel()
  7. @State private var shouldDisplayHint: Bool = false
  8. @State var hintDetent = PresentationDetent.large
  9. @State var selectedVerboseHint: AnyView?
  10. @State var hintLabel: String?
  11. @State private var decimalPlaceholder: Decimal = 0.0
  12. @State private var booleanPlaceholder: Bool = false
  13. @State var replaceAlert = false
  14. @Environment(\.colorScheme) var colorScheme
  15. var color: LinearGradient {
  16. colorScheme == .dark ? LinearGradient(
  17. gradient: Gradient(colors: [
  18. Color.bgDarkBlue,
  19. Color.bgDarkerDarkBlue
  20. ]),
  21. startPoint: .top,
  22. endPoint: .bottom
  23. )
  24. :
  25. LinearGradient(
  26. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  27. startPoint: .top,
  28. endPoint: .bottom
  29. )
  30. }
  31. private var isfFormatter: NumberFormatter {
  32. let formatter = NumberFormatter()
  33. formatter.numberStyle = .decimal
  34. formatter.maximumFractionDigits = 2
  35. return formatter
  36. }
  37. private var rateFormatter: NumberFormatter {
  38. let formatter = NumberFormatter()
  39. formatter.numberStyle = .decimal
  40. formatter.maximumFractionDigits = 2
  41. return formatter
  42. }
  43. private var dateFormatter: DateFormatter {
  44. let formatter = DateFormatter()
  45. formatter.dateStyle = .medium
  46. formatter.timeStyle = .short
  47. return formatter
  48. }
  49. var body: some View {
  50. Form {
  51. SettingInputSection(
  52. decimalValue: $decimalPlaceholder,
  53. booleanValue: $state.useAutotune,
  54. shouldDisplayHint: $shouldDisplayHint,
  55. selectedVerboseHint: Binding(
  56. get: { selectedVerboseHint },
  57. set: {
  58. selectedVerboseHint = $0.map { AnyView($0) }
  59. hintLabel = "Use Autotune"
  60. }
  61. ),
  62. units: state.units,
  63. type: .boolean,
  64. label: "Use Autotune",
  65. miniHint: """
  66. It is not advised to use Autotune with Trio
  67. Default: OFF
  68. """,
  69. verboseHint: VStack(spacing: 10) {
  70. Text("Default: OFF").bold()
  71. Text("It is not advised to use Autotune with Trio").bold()
  72. Text("Autotune is not designed to work with Trio. It is best to keep Autotune off and do not use it.")
  73. },
  74. headerText: "Data-driven Adjustments"
  75. )
  76. if state.useAutotune {
  77. SettingInputSection(
  78. decimalValue: $decimalPlaceholder,
  79. booleanValue: $state.onlyAutotuneBasals,
  80. shouldDisplayHint: $shouldDisplayHint,
  81. selectedVerboseHint: Binding(
  82. get: { selectedVerboseHint },
  83. set: {
  84. selectedVerboseHint = $0.map { AnyView($0) }
  85. hintLabel = "Only Autotune Basal Insulin"
  86. }
  87. ),
  88. units: state.units,
  89. type: .boolean,
  90. label: "Only Autotune Basal Insulin",
  91. miniHint: """
  92. Restricts Autotune adjustments to only basal settings
  93. Default: OFF
  94. """,
  95. verboseHint: Text("Restricts Autotune adjustments to only basal settings.")
  96. )
  97. }
  98. Section(
  99. header: HStack {
  100. Text("Last run")
  101. Spacer()
  102. Text(dateFormatter.string(from: state.publishedDate))
  103. },
  104. content: {
  105. Button {
  106. state.run()
  107. } label: {
  108. Text("Run now")
  109. }
  110. .frame(maxWidth: .infinity, alignment: .center)
  111. .listRowBackground(Color(.systemBlue))
  112. .tint(.white)
  113. }
  114. )
  115. if let autotune = state.autotune {
  116. if !state.onlyAutotuneBasals {
  117. Section {
  118. HStack {
  119. Text("Carb ratio")
  120. Spacer()
  121. Text(isfFormatter.string(from: autotune.carbRatio as NSNumber) ?? "0")
  122. Text("g/U").foregroundColor(.secondary)
  123. }
  124. HStack {
  125. Text("Sensitivity")
  126. Spacer()
  127. if state.units == .mmolL {
  128. Text(isfFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
  129. } else {
  130. Text(isfFormatter.string(from: autotune.sensitivity as NSNumber) ?? "0")
  131. }
  132. Text(state.units.rawValue + "/U").foregroundColor(.secondary)
  133. }
  134. }
  135. .listRowBackground(Color.chart)
  136. }
  137. Section(header: Text("Basal profile")) {
  138. ForEach(0 ..< autotune.basalProfile.count, id: \.self) { index in
  139. HStack {
  140. Text(autotune.basalProfile[index].start).foregroundColor(.secondary)
  141. Spacer()
  142. Text(rateFormatter.string(from: autotune.basalProfile[index].rate as NSNumber) ?? "0")
  143. Text("U/hr").foregroundColor(.secondary)
  144. }
  145. }
  146. HStack {
  147. Text("Total")
  148. .bold()
  149. .foregroundColor(.primary)
  150. Spacer()
  151. Text(rateFormatter.string(from: autotune.basalProfile.reduce(0) { $0 + $1.rate } as NSNumber) ?? "0")
  152. .foregroundColor(.primary) +
  153. Text(" U/day")
  154. .foregroundColor(.secondary)
  155. }
  156. }
  157. .listRowBackground(Color.chart)
  158. Section {
  159. Button {
  160. Task {
  161. await state.delete()
  162. }
  163. } label: {
  164. Text("Delete Autotune Data")
  165. }
  166. .frame(maxWidth: .infinity, alignment: .center)
  167. .listRowBackground(Color(.loopRed))
  168. .tint(.white)
  169. }
  170. // Section {
  171. // Button {
  172. // replaceAlert = true
  173. // } label: {
  174. // Text("Save as Normal Basal Rates")
  175. // }
  176. // .frame(maxWidth: .infinity, alignment: .center)
  177. // .listRowBackground(Color(.systemGray4))
  178. // .tint(.white)
  179. // }
  180. }
  181. }
  182. .sheet(isPresented: $shouldDisplayHint) {
  183. SettingInputHintView(
  184. hintDetent: $hintDetent,
  185. shouldDisplayHint: $shouldDisplayHint,
  186. hintLabel: hintLabel ?? "",
  187. hintText: selectedVerboseHint ?? AnyView(EmptyView()),
  188. sheetTitle: "Help"
  189. )
  190. }
  191. .scrollContentBackground(.hidden).background(color)
  192. .onAppear(perform: configureView)
  193. .navigationTitle("Autotune")
  194. .navigationBarTitleDisplayMode(.automatic)
  195. .alert(Text("Are you sure?"), isPresented: $replaceAlert) {
  196. Button("Yes", action: {
  197. state.replace()
  198. replaceAlert.toggle()
  199. })
  200. Button("No", action: { replaceAlert.toggle() })
  201. }
  202. }
  203. }
  204. }