AutotuneConfigRootView.swift 8.2 KB

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