SettingInputSection.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import SwiftUI
  2. struct SettingInputSection: View {
  3. enum SettingInputSectionType: Equatable {
  4. case decimal(String)
  5. case boolean
  6. case conditionalDecimal(String)
  7. static func == (lhs: SettingInputSectionType, rhs: SettingInputSectionType) -> Bool {
  8. switch (lhs, rhs) {
  9. case (.boolean, .boolean):
  10. return true
  11. case let (.decimal(lhsValue), .decimal(rhsValue)):
  12. return lhsValue == rhsValue
  13. case let (.conditionalDecimal(lhsValue), .conditionalDecimal(rhsValue)):
  14. return lhsValue == rhsValue
  15. default:
  16. return false
  17. }
  18. }
  19. }
  20. @Binding var decimalValue: Decimal
  21. @Binding var booleanValue: Bool
  22. @Binding var shouldDisplayHint: Bool
  23. @Binding var selectedVerboseHint: String?
  24. var type: SettingInputSectionType
  25. var label: String
  26. var conditionalLabel: String?
  27. var miniHint: String
  28. var verboseHint: String
  29. var headerText: String?
  30. var footerText: String?
  31. // Access the shared PickerSettingsProvider instance
  32. @ObservedObject private var pickerSettingsProvider = PickerSettingsProvider.shared
  33. @State private var displayPicker: Bool = false
  34. @State private var units: GlucoseUnits = .mgdL
  35. @Injected() var settingsManager: SettingsManager!
  36. mutating func subscribe() {
  37. units = settingsManager.settings.units
  38. }
  39. var body: some View {
  40. Section(
  41. content: {
  42. VStack {
  43. switch type {
  44. case let .decimal(key):
  45. if let setting = getPickerSetting(for: key) {
  46. VStack {
  47. HStack {
  48. Text(label)
  49. Spacer()
  50. Group {
  51. Text(decimalValue.description)
  52. .foregroundColor(!displayPicker ? .primary : .accentColor)
  53. if setting.type == PickerSetting.PickerSettingType.glucose {
  54. Text(units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  55. } else if setting.type == PickerSetting.PickerSettingType.insulinUnit {
  56. Text(" U").foregroundColor(.secondary)
  57. } else if setting.type == PickerSetting.PickerSettingType.gramms {
  58. Text(" g").foregroundColor(.secondary)
  59. }
  60. }.onTapGesture {
  61. displayPicker.toggle()
  62. }
  63. }.padding(.top)
  64. if displayPicker {
  65. Picker(selection: $decimalValue, label: Text("")) {
  66. ForEach(pickerSettingsProvider.generatePickerValues(from: setting), id: \.self) { value in
  67. Text("\(value.description)").tag(value)
  68. }
  69. }
  70. .pickerStyle(WheelPickerStyle())
  71. .frame(maxWidth: .infinity)
  72. }
  73. }
  74. }
  75. case .boolean:
  76. HStack {
  77. Toggle(isOn: $booleanValue) {
  78. Text(label)
  79. }
  80. }.padding(.top)
  81. case let .conditionalDecimal(key):
  82. HStack {
  83. Toggle(isOn: $booleanValue) {
  84. Text(label)
  85. }
  86. }.padding(.vertical)
  87. if $booleanValue.wrappedValue {
  88. if let setting = getPickerSetting(for: key) {
  89. VStack {
  90. HStack {
  91. Text(conditionalLabel ?? label)
  92. Spacer()
  93. Group {
  94. Text(decimalValue.description)
  95. .foregroundColor(!displayPicker ? .primary : .accentColor)
  96. if setting.type == PickerSetting.PickerSettingType.glucose {
  97. Text(units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
  98. } else if setting.type == PickerSetting.PickerSettingType.insulinUnit {
  99. Text(" U").foregroundColor(.secondary)
  100. } else if setting.type == PickerSetting.PickerSettingType.gramms {
  101. Text(" g").foregroundColor(.secondary)
  102. }
  103. }.onTapGesture {
  104. displayPicker.toggle()
  105. }
  106. }.padding(.top)
  107. if displayPicker {
  108. Picker(selection: $decimalValue, label: Text("")) {
  109. ForEach(
  110. pickerSettingsProvider.generatePickerValues(from: setting),
  111. id: \.self
  112. ) { value in
  113. Text("\(value.description)").tag(value)
  114. }
  115. }
  116. .pickerStyle(WheelPickerStyle())
  117. .frame(maxWidth: .infinity)
  118. }
  119. }
  120. }
  121. }
  122. }
  123. HStack(alignment: .top) {
  124. Text(miniHint)
  125. .font(.footnote)
  126. .foregroundColor(.secondary)
  127. .lineLimit(nil)
  128. Spacer()
  129. Button(
  130. action: {
  131. shouldDisplayHint.toggle()
  132. selectedVerboseHint = shouldDisplayHint ? verboseHint : nil
  133. },
  134. label: {
  135. HStack {
  136. Image(systemName: "questionmark.circle")
  137. }
  138. }
  139. ).buttonStyle(BorderlessButtonStyle())
  140. }.padding(.vertical)
  141. }
  142. },
  143. header: {
  144. if let headerText = headerText {
  145. Text(headerText)
  146. }
  147. },
  148. footer: {
  149. if let footerText = footerText {
  150. Text(footerText)
  151. }
  152. }
  153. ).listRowBackground(Color.chart)
  154. }
  155. // Helper function to retrieve PickerSetting based on key
  156. private func getPickerSetting(for key: String) -> PickerSetting? {
  157. switch key {
  158. case "lowGlucose":
  159. return pickerSettingsProvider.settings.lowGlucose
  160. case "highGlucose":
  161. return pickerSettingsProvider.settings.highGlucose
  162. case "carbsRequiredThreshold":
  163. return pickerSettingsProvider.settings.carbsRequiredThreshold
  164. case "individualAdjustmentFactor":
  165. return pickerSettingsProvider.settings.individualAdjustmentFactor
  166. case "delay":
  167. return pickerSettingsProvider.settings.delay
  168. case "timeCap":
  169. return pickerSettingsProvider.settings.timeCap
  170. case "minuteInterval":
  171. return pickerSettingsProvider.settings.minuteInterval
  172. case "high":
  173. return pickerSettingsProvider.settings.high
  174. case "low":
  175. return pickerSettingsProvider.settings.low
  176. case "hours":
  177. return pickerSettingsProvider.settings.hours
  178. case "maxCarbs":
  179. return pickerSettingsProvider.settings.maxCarbs
  180. case "maxFat":
  181. return pickerSettingsProvider.settings.maxFat
  182. case "maxProtein":
  183. return pickerSettingsProvider.settings.maxProtein
  184. case "overrideFactor":
  185. return pickerSettingsProvider.settings.overrideFactor
  186. case "fattyMealFactor":
  187. return pickerSettingsProvider.settings.fattyMealFactor
  188. case "sweetMealFactor":
  189. return pickerSettingsProvider.settings.sweetMealFactor
  190. case "maxIOB":
  191. return pickerSettingsProvider.settings.maxIOB
  192. case "maxDailySafetyMultiplier":
  193. return pickerSettingsProvider.settings.maxDailySafetyMultiplier
  194. case "currentBasalSafetyMultiplier":
  195. return pickerSettingsProvider.settings.currentBasalSafetyMultiplier
  196. case "autosensMax":
  197. return pickerSettingsProvider.settings.autosensMax
  198. case "autosensMin":
  199. return pickerSettingsProvider.settings.autosensMin
  200. case "smbDeliveryRatio":
  201. return pickerSettingsProvider.settings.smbDeliveryRatio
  202. case "halfBasalExerciseTarget":
  203. return pickerSettingsProvider.settings.halfBasalExerciseTarget
  204. case "maxCOB":
  205. return pickerSettingsProvider.settings.maxCOB
  206. case "min5mCarbimpact":
  207. return pickerSettingsProvider.settings.min5mCarbimpact
  208. case "autotuneISFAdjustmentFraction":
  209. return pickerSettingsProvider.settings.autotuneISFAdjustmentFraction
  210. case "remainingCarbsFraction":
  211. return pickerSettingsProvider.settings.remainingCarbsFraction
  212. case "remainingCarbsCap":
  213. return pickerSettingsProvider.settings.remainingCarbsCap
  214. case "maxSMBBasalMinutes":
  215. return pickerSettingsProvider.settings.maxSMBBasalMinutes
  216. case "maxUAMSMBBasalMinutes":
  217. return pickerSettingsProvider.settings.maxUAMSMBBasalMinutes
  218. case "smbInterval":
  219. return pickerSettingsProvider.settings.smbInterval
  220. case "bolusIncrement":
  221. return pickerSettingsProvider.settings.bolusIncrement
  222. case "insulinPeakTime":
  223. return pickerSettingsProvider.settings.insulinPeakTime
  224. case "carbsReqThreshold":
  225. return pickerSettingsProvider.settings.carbsReqThreshold
  226. case "noisyCGMTargetMultiplier":
  227. return pickerSettingsProvider.settings.noisyCGMTargetMultiplier
  228. case "maxDeltaBGthreshold":
  229. return pickerSettingsProvider.settings.maxDeltaBGthreshold
  230. case "adjustmentFactor":
  231. return pickerSettingsProvider.settings.adjustmentFactor
  232. case "adjustmentFactorSigmoid":
  233. return pickerSettingsProvider.settings.adjustmentFactorSigmoid
  234. case "weightPercentage":
  235. return pickerSettingsProvider.settings.weightPercentage
  236. case "enableSMB_high_bg_target":
  237. return pickerSettingsProvider.settings.enableSMB_high_bg_target
  238. case "threshold_setting":
  239. return pickerSettingsProvider.settings.threshold_setting
  240. case "updateInterval":
  241. return pickerSettingsProvider.settings.updateInterval
  242. default:
  243. return nil
  244. }
  245. }
  246. }