AutosensSettingsRootView.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import SwiftUI
  2. import Swinject
  3. extension AutosensSettings {
  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. @Environment(\.colorScheme) var colorScheme
  14. @EnvironmentObject var appIcons: Icons
  15. @Environment(AppState.self) var appState
  16. private var rateFormatter: NumberFormatter {
  17. let formatter = NumberFormatter()
  18. formatter.numberStyle = .decimal
  19. formatter.maximumFractionDigits = 2
  20. return formatter
  21. }
  22. var autosensVerboseHint: some View {
  23. VStack(alignment: .leading, spacing: 15) {
  24. Text(
  25. "Autosens automatically adjusts insulin delivery based on how sensitive or resistant you are to insulin at the time of the current loop cycle by analyzing past data to keep blood sugar levels stable."
  26. )
  27. VStack(alignment: .leading, spacing: 5) {
  28. Text("How it Works").bold()
  29. Text(
  30. "It looks at the last 8-24 hours of data, excluding meal-related changes, and adjusts insulin settings like basal rates and targets when needed to match your sensitivity or resistance to insulin."
  31. )
  32. }
  33. VStack(alignment: .leading, spacing: 5) {
  34. Text("What it Adjusts").bold()
  35. Text(
  36. "Autosens modifies Insulin Sensitivity Factor (ISF), basal rates, and target glucose. It doesn’t account for carbs but adjusts for insulin effectiveness based on patterns in your glucose data."
  37. )
  38. }
  39. VStack(alignment: .leading, spacing: 5) {
  40. Text("Safety").bold()
  41. Text(
  42. "Autosens has safety limits determined by your Autosens Max and Autosens Min settings. These settings prevent over-adjusting."
  43. )
  44. }
  45. Text(
  46. "Autosens functions alongside certain settings, like Super Micro Bolus (SMB). Other settings, like Dynamic ISF, alter portions of the Autosens formula. Please review the in-app hints for the Algorithm Settings prior to enabling them to understand how they may influence it."
  47. )
  48. }
  49. }
  50. var AutosensView: some View {
  51. Section(
  52. header: !state.settingsManager.preferences
  53. .useNewFormula ? Text("Autosens") : Text("Dynamic Sensitivity")
  54. ) {
  55. VStack {
  56. let dynamicRatio = state.determinationsFromPersistence.first?.sensitivityRatio
  57. let dynamicISF = state.determinationsFromPersistence.first?.insulinSensitivity
  58. let newISF = state.autosensISF
  59. let decimalValue = !state.settingsManager.preferences.useNewFormula ? state
  60. .autosensRatio as NSDecimalNumber : dynamicRatio ?? 1
  61. let decimalValueText = rateFormatter
  62. .string(from: ((decimalValue as Decimal) * Decimal(100)) as NSNumber) ?? "100"
  63. HStack {
  64. Text("Sensitivity Ratio")
  65. Spacer()
  66. Text("\(decimalValueText) \(String(localized: "%", comment: "Percentage symbol"))")
  67. }.padding(.vertical)
  68. HStack {
  69. Text("Calculated Sensitivity")
  70. Spacer()
  71. if state.units == .mgdL {
  72. Text(
  73. !state.settingsManager.preferences
  74. .useNewFormula ? newISF!.description : (dynamicISF ?? 0).description
  75. )
  76. } else {
  77. Text((
  78. !state.settingsManager.preferences
  79. .useNewFormula ? newISF!.formattedAsMmolL : dynamicISF?.decimalValue.formattedAsMmolL
  80. ) ?? "0")
  81. }
  82. Text(state.units.rawValue + "/U").foregroundColor(.secondary)
  83. }
  84. HStack(alignment: .top) {
  85. Text(
  86. "This adjusted ISF is temporary, will change with the next loop cycle, and should not be directly used as your profile ISF value."
  87. )
  88. .font(.footnote)
  89. .foregroundColor(.secondary)
  90. .lineLimit(nil)
  91. Spacer()
  92. Button(
  93. action: {
  94. hintLabel = String(localized: "Autosens")
  95. selectedVerboseHint = AnyView(autosensVerboseHint)
  96. shouldDisplayHint.toggle()
  97. },
  98. label: {
  99. HStack {
  100. Image(systemName: "questionmark.circle")
  101. }
  102. }
  103. ).buttonStyle(BorderlessButtonStyle())
  104. }.padding(.top)
  105. }.padding(.bottom)
  106. }.listRowBackground(Color.chart)
  107. }
  108. var body: some View {
  109. List {
  110. if state.autosensISF != nil {
  111. AutosensView
  112. }
  113. SettingInputSection(
  114. decimalValue: $state.autosensMax,
  115. booleanValue: $booleanPlaceholder,
  116. shouldDisplayHint: $shouldDisplayHint,
  117. selectedVerboseHint: Binding(
  118. get: { selectedVerboseHint },
  119. set: {
  120. selectedVerboseHint = $0.map { AnyView($0) }
  121. hintLabel = String(localized: "Autosens Max", comment: "Autosens Max")
  122. }
  123. ),
  124. units: state.units,
  125. type: .decimal("autosensMax"),
  126. label: String(localized: "Autosens Max", comment: "Autosens Max"),
  127. miniHint: String(localized: "Upper limit of the Sensitivity Ratio."),
  128. verboseHint:
  129. VStack(alignment: .leading, spacing: 10) {
  130. Text("Default: 120%").bold()
  131. Text(
  132. "Autosens Max sets the maximum Sensitivity Ratio used by Autosens, Dynamic ISF, and Sigmoid Formula."
  133. )
  134. Text(
  135. "The Sensitivity Ratio is used to calculate the amount of adjustment needed to basal rates and ISF."
  136. )
  137. Text(
  138. "Tip: Increasing this value allows automatic adjustments of basal rates to be higher and ISF to be lower."
  139. )
  140. },
  141. headerText: String(localized: "Glucose Deviations Algorithm")
  142. )
  143. SettingInputSection(
  144. decimalValue: $state.autosensMin,
  145. booleanValue: $booleanPlaceholder,
  146. shouldDisplayHint: $shouldDisplayHint,
  147. selectedVerboseHint: Binding(
  148. get: { selectedVerboseHint },
  149. set: {
  150. selectedVerboseHint = $0.map { AnyView($0) }
  151. hintLabel = String(localized: "Autosens Min", comment: "Autosens Min")
  152. }
  153. ),
  154. units: state.units,
  155. type: .decimal("autosensMin"),
  156. label: String(localized: "Autosens Min", comment: "Autosens Min"),
  157. miniHint: String(localized: "Lower limit of the Sensitivity Ratio."),
  158. verboseHint:
  159. VStack(alignment: .leading, spacing: 10) {
  160. Text("Default: 70%").bold()
  161. Text(
  162. "Autosens Min sets the minimum Sensitivity Ratio used by Autosens, Dynamic ISF, and Sigmoid Formula."
  163. )
  164. Text(
  165. "The Sensitivity Ratio is used to calculate the amount of adjustment needed to basal rates and ISF."
  166. )
  167. Text(
  168. "Tip: Decreasing this value allows automatic adjustments of basal rates to be lower and ISF to be higher."
  169. )
  170. }
  171. )
  172. SettingInputSection(
  173. decimalValue: $decimalPlaceholder,
  174. booleanValue: $state.rewindResetsAutosens,
  175. shouldDisplayHint: $shouldDisplayHint,
  176. selectedVerboseHint: Binding(
  177. get: { selectedVerboseHint },
  178. set: {
  179. selectedVerboseHint = $0.map { AnyView($0) }
  180. hintLabel = String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens")
  181. }
  182. ),
  183. units: state.units,
  184. type: .boolean,
  185. label: String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens"),
  186. miniHint: String(localized: "Pump rewind initiates a reset in Sensitivity Ratio."),
  187. verboseHint: VStack(alignment: .leading, spacing: 5) {
  188. Text("Default: ON").bold()
  189. Text("Medtronic and Dana Users Only").bold()
  190. VStack(alignment: .leading, spacing: 10) {
  191. Text(
  192. "This feature resets the Sensitivity Ratio to neutral when you rewind your pump on the assumption that this corresponds to a site change."
  193. )
  194. Text(
  195. "Autosens will begin learning sensitivity anew from the time of the rewind, which may take up to 6 hours."
  196. )
  197. Text(
  198. "Tip: If you usually rewind your pump independently of site changes, you may want to consider disabling this feature."
  199. )
  200. }
  201. }
  202. )
  203. }
  204. .listSectionSpacing(sectionSpacing)
  205. .sheet(isPresented: $shouldDisplayHint) {
  206. SettingInputHintView(
  207. hintDetent: $hintDetent,
  208. shouldDisplayHint: $shouldDisplayHint,
  209. hintLabel: hintLabel ?? "",
  210. hintText: selectedVerboseHint ?? AnyView(EmptyView()),
  211. sheetTitle: String(localized: "Help", comment: "Help sheet title")
  212. )
  213. }
  214. .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
  215. .onAppear(perform: configureView)
  216. .navigationTitle("Autosens")
  217. .navigationBarTitleDisplayMode(.automatic)
  218. .settingsHighlightScroll()
  219. }
  220. }
  221. }