DynamicRootView.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import SwiftUI
  2. import Swinject
  3. extension Dynamic {
  4. struct RootView: BaseView {
  5. let resolver: Resolver
  6. @StateObject var state = StateModel()
  7. @State private var showDynamicHint: Bool = false
  8. @State var hintDetent = PresentationDetent.large
  9. private var conversionFormatter: NumberFormatter {
  10. let formatter = NumberFormatter()
  11. formatter.numberStyle = .decimal
  12. formatter.maximumFractionDigits = 1
  13. return formatter
  14. }
  15. @Environment(\.colorScheme) var colorScheme
  16. var color: LinearGradient {
  17. colorScheme == .dark ? LinearGradient(
  18. gradient: Gradient(colors: [
  19. Color.bgDarkBlue,
  20. Color.bgDarkerDarkBlue
  21. ]),
  22. startPoint: .top,
  23. endPoint: .bottom
  24. )
  25. :
  26. LinearGradient(
  27. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  28. startPoint: .top,
  29. endPoint: .bottom
  30. )
  31. }
  32. private var formatter: NumberFormatter {
  33. let formatter = NumberFormatter()
  34. formatter.numberStyle = .decimal
  35. return formatter
  36. }
  37. private var glucoseFormatter: NumberFormatter {
  38. let formatter = NumberFormatter()
  39. formatter.numberStyle = .decimal
  40. if state.unit == .mmolL {
  41. formatter.maximumFractionDigits = 1
  42. } else { formatter.maximumFractionDigits = 0 }
  43. formatter.roundingMode = .halfUp
  44. return formatter
  45. }
  46. var body: some View {
  47. List {
  48. Section {
  49. VStack {
  50. Toggle("Activate Dynamic Sensitivity (ISF)", isOn: $state.useNewFormula)
  51. HStack {
  52. Text(
  53. "Trio calculates insulin sensitivity (ISF) each loop cycle based on current blood sugar, daily insulin use, and an adjustment factor, within set limits."
  54. ).font(.footnote).foregroundColor(.secondary)
  55. Spacer()
  56. Button(
  57. action: { showDynamicHint.toggle() },
  58. label: {
  59. HStack {
  60. Image(systemName: "questionmark.circle")
  61. }
  62. }
  63. )
  64. }
  65. }
  66. }
  67. if state.useNewFormula {
  68. Section {
  69. VStack {
  70. Toggle("Activate Dynamic Carb Ratio (CR)", isOn: $state.enableDynamicCR)
  71. HStack {
  72. Text(
  73. "Similar to Dynamic Sensitivity, Trio calculates a dynamic carb ratio every loop cycle."
  74. ).font(.footnote).foregroundColor(.secondary)
  75. Spacer()
  76. Button(
  77. action: { showDynamicHint.toggle() },
  78. label: {
  79. HStack {
  80. Image(systemName: "questionmark.circle")
  81. }
  82. }
  83. )
  84. }
  85. }
  86. }
  87. }
  88. if state.useNewFormula {
  89. Section {
  90. HStack {
  91. Toggle("Use Sigmoid Formula", isOn: $state.sigmoid)
  92. }
  93. } header: { Text("Formula") }
  94. Section {
  95. if !state.sigmoid {
  96. HStack {
  97. Text("Adjustment Factor")
  98. Spacer()
  99. TextFieldWithToolBar(text: $state.adjustmentFactor, placeholder: "0", numberFormatter: formatter)
  100. }
  101. } else {
  102. HStack {
  103. Text("Sigmoid Adjustment Factor")
  104. Spacer()
  105. TextFieldWithToolBar(
  106. text: $state.adjustmentFactorSigmoid,
  107. placeholder: "0",
  108. numberFormatter: formatter
  109. )
  110. }
  111. }
  112. HStack {
  113. Text("Weighted Average of TDD. Weight of past 24 hours:")
  114. Spacer()
  115. TextFieldWithToolBar(text: $state.weightPercentage, placeholder: "0", numberFormatter: formatter)
  116. }
  117. HStack {
  118. Toggle("Adjust basal", isOn: $state.tddAdjBasal)
  119. }
  120. } header: { Text("Settings") }
  121. Section {
  122. HStack {
  123. Text("Threshold Setting")
  124. Spacer()
  125. TextFieldWithToolBar(
  126. text: $state.threshold_setting,
  127. placeholder: "0",
  128. numberFormatter: glucoseFormatter
  129. )
  130. Text(state.unit.rawValue)
  131. }
  132. } header: { Text("Safety") }
  133. }
  134. }
  135. .scrollContentBackground(.hidden).background(color)
  136. .onAppear(perform: configureView)
  137. .navigationBarTitle("Dynamic ISF")
  138. .navigationBarTitleDisplayMode(.automatic)
  139. .toolbar {
  140. ToolbarItem(placement: .topBarTrailing) {
  141. Button(
  142. action: { showDynamicHint.toggle() },
  143. label: {
  144. HStack {
  145. Text("Hints")
  146. Image(systemName: "questionmark.circle").font(.system(size: 20))
  147. }
  148. }
  149. )
  150. }
  151. }
  152. .sheet(isPresented: $showDynamicHint) {
  153. NavigationStack {
  154. List {
  155. DefinitionRow(
  156. term: NSLocalizedString("Enable Dynamic ISF", comment: "Enable Dynamic ISF"),
  157. definition: NSLocalizedString(
  158. "Calculate a new ISF with every loop cycle. New ISF will be based on current BG, TDD of insulin (past 24 hours or a weighted average) and an Adjustment Factor (default is 1).\n\nDynamic ISF and CR ratios will be limited by your autosens.min/max limits.\n\nDynamic ratio replaces the autosens.ratio:\n\nNew ISF = Static ISF / Dynamic ratio,\n\nDynamic ratio = profile.sens * adjustmentFactor * tdd * Math.log(BG/insulinFactor+1) / 1800,\n\ninsulinFactor = 120 - InsulinPeakTimeInMinutes",
  159. comment: "Enable Dynamic ISF"
  160. )
  161. )
  162. // DefinitionRow(
  163. // term: NSLocalizedString("Enable Dynamic CR", comment: "Use Dynamic CR together with Dynamic ISF"),
  164. // definition: NSLocalizedString(
  165. // "Use Dynamic CR. The dynamic ratio will be used for CR as follows:\n\n When ratio > 1: dynCR = (newRatio - 1) / 2 + 1.\nWhen ratio < 1: dynCR = CR/dynCR.\n\nDon't use toghether with a high Insulin Fraction (> 2)",
  166. // comment: "Use Dynamic CR together with Dynamic ISF"
  167. // )
  168. // )
  169. // DefinitionRow(
  170. // term: NSLocalizedString("Use Sigmoid Function", comment: "Use Sigmoid Function"),
  171. // definition: NSLocalizedString(
  172. // "Use a sigmoid function for ISF (and for CR, when enabled), instead of the default Logarithmic formula. Requires the Dynamic ISF setting to be enabled in settings\n\nThe Adjustment setting adjusts the slope of the curve (Y: Dynamic ratio, X: Blood Glucose). A lower value ==> less steep == less aggressive.\n\nThe autosens.min/max settings determines both the max/min limits for the dynamic ratio AND how much the dynamic ratio is adjusted. If AF is the slope of the curve, the autosens.min/max is the height of the graph, the Y-interval, where Y: dynamic ratio. The curve will always have a sigmoid shape, no matter which autosens.min/max settings are used, meaning these settings have big consequences for the outcome of the computed dynamic ISF. Please be careful setting a too high autosens.max value. With a proper profile ISF setting, you will probably never need it to be higher than 1.5\n\nAn Autosens.max limit > 1.5 is not advisable when using the sigmoid function.",
  173. // comment: "Use Sigmoid Function"
  174. // )
  175. // )
  176. }
  177. .padding(.trailing, 10)
  178. .navigationBarTitle("Dynamic ISF Hints", displayMode: .inline)
  179. Button { showDynamicHint.toggle() }
  180. label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
  181. .buttonStyle(.bordered)
  182. .padding(.top)
  183. }
  184. .padding()
  185. .presentationDetents(
  186. [.fraction(0.9), .large],
  187. selection: $hintDetent
  188. )
  189. }
  190. .onDisappear {
  191. state.saveIfChanged()
  192. }
  193. }
  194. }
  195. }