AlgorithmAdvancedSettingsRootView.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import SwiftUI
  2. import Swinject
  3. extension AlgorithmAdvancedSettings {
  4. struct RootView: BaseView {
  5. let resolver: Resolver
  6. @State 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. private 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. var body: some View {
  32. List {
  33. Section(
  34. header: Text("DISCLAIMER"),
  35. content: {
  36. VStack(alignment: .leading) {
  37. Text(
  38. "The settings in this section are designed for advanced expert users and typically do not require ANY modifications."
  39. ).bold()
  40. }
  41. }
  42. ).listRowBackground(Color.tabBar)
  43. SettingInputSection(
  44. decimalValue: $state.maxDailySafetyMultiplier,
  45. booleanValue: $booleanPlaceholder,
  46. shouldDisplayHint: $shouldDisplayHint,
  47. selectedVerboseHint: Binding(
  48. get: { selectedVerboseHint },
  49. set: {
  50. selectedVerboseHint = $0.map { AnyView($0) }
  51. hintLabel = NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier")
  52. }
  53. ),
  54. units: state.units,
  55. type: .decimal("maxDailySafetyMultiplier"),
  56. label: NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier"),
  57. miniHint: """
  58. Temporary basal rates cannot be set higher than this percentage of your LARGEST profile basal rate
  59. Default setting: 300%
  60. """,
  61. verboseHint: VStack {
  62. Text("Default: 300%").bold()
  63. Text("""
  64. This limits the automatic adjustment of the temporary basal rate to this value times the highest scheduled basal rate in your basal profile.
  65. """)
  66. Text("If Autotune is enabled, Trio uses Autotune basals instead of scheduled basals.").italic()
  67. Text("""
  68. Increasing this setting is not advised.
  69. """).bold().italic()
  70. }
  71. )
  72. SettingInputSection(
  73. decimalValue: $state.currentBasalSafetyMultiplier,
  74. booleanValue: $booleanPlaceholder,
  75. shouldDisplayHint: $shouldDisplayHint,
  76. selectedVerboseHint: Binding(
  77. get: { selectedVerboseHint },
  78. set: {
  79. selectedVerboseHint = $0.map { AnyView($0) }
  80. hintLabel = NSLocalizedString(
  81. "Current Basal Safety Multiplier",
  82. comment: "Current Basal Safety Multiplier"
  83. )
  84. }
  85. ),
  86. units: state.units,
  87. type: .decimal("currentBasalSafetyMultiplier"),
  88. label: NSLocalizedString("Current Basal Safety Multiplier", comment: "Current Basal Safety Multiplier"),
  89. miniHint: """
  90. Temporary basal rates cannot be set higher than this percentage of the profile basal rate at the time of the loop cycle
  91. Default: 400%
  92. """,
  93. verboseHint: VStack {
  94. Text("Default: 400%").bold()
  95. Text("""
  96. This limits the automatic adjustment of the temporary basal rate to this percentage of the current hourly basal rate at the time of the loop cycle.
  97. """)
  98. Text("If Autotune is enabled, Trio uses Autotune basals instead of scheduled basals.").italic()
  99. Text("""
  100. Increasing this setting is not advised.
  101. """).bold().italic()
  102. }
  103. )
  104. SettingInputSection(
  105. decimalValue: $state.insulinActionCurve,
  106. booleanValue: $booleanPlaceholder,
  107. shouldDisplayHint: $shouldDisplayHint,
  108. selectedVerboseHint: Binding(
  109. get: { selectedVerboseHint },
  110. set: {
  111. selectedVerboseHint = $0.map { AnyView($0) }
  112. hintLabel = "Duration of Insulin Action"
  113. }
  114. ),
  115. units: state.units,
  116. type: .decimal("dia"),
  117. label: "Duration of Insulin Action",
  118. miniHint: """
  119. Number of hours insulin will be active in your body
  120. Default: 6 hours
  121. """,
  122. verboseHint: VStack {
  123. Text("Default: 6 hours").bold()
  124. Text("""
  125. Number of hours insulin will contribute to IOB after dosing.
  126. """)
  127. Text("It is better to use Custom Peak Timing rather than adjust your Duration of Insulin Action (DIA)").italic()
  128. }
  129. )
  130. SettingInputSection(
  131. decimalValue: $state.insulinPeakTime,
  132. booleanValue: $state.useCustomPeakTime,
  133. shouldDisplayHint: $shouldDisplayHint,
  134. selectedVerboseHint: Binding(
  135. get: { selectedVerboseHint },
  136. set: {
  137. selectedVerboseHint = $0.map { AnyView($0) }
  138. hintLabel = NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time")
  139. }
  140. ),
  141. units: state.units,
  142. type: .conditionalDecimal("insulinPeakTime"),
  143. label: NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time"),
  144. conditionalLabel: NSLocalizedString("Insulin Peak Time", comment: "Insulin Peak Time"),
  145. miniHint: """
  146. Time that insulin effect is at it’s highest. Set in minutes since injection.
  147. Default: (Set by Insulin Type)
  148. """,
  149. verboseHint: VStack {
  150. Text("Default: Set by Insulin Type").bold()
  151. Text("""
  152. Time of maximum glucose lowering effect of insulin. Set in minutes since insulin administration.
  153. System-Determined Defaults:
  154. Ultra-Rapid: 55 minutes (permitted range 35-100 minutes)
  155. Rapid-Acting: 75 minutes (permitted range 50-120 minutes)
  156. """)
  157. }
  158. )
  159. SettingInputSection(
  160. decimalValue: $decimalPlaceholder,
  161. booleanValue: $state.skipNeutralTemps,
  162. shouldDisplayHint: $shouldDisplayHint,
  163. selectedVerboseHint: Binding(
  164. get: { selectedVerboseHint },
  165. set: {
  166. selectedVerboseHint = $0.map { AnyView($0) }
  167. hintLabel = NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps")
  168. }
  169. ),
  170. units: state.units,
  171. type: .boolean,
  172. label: NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps"),
  173. miniHint: """
  174. When on, Trio will not send a temp basal command to the pump if the determined basal rate is the same as the scheduled basal
  175. Default: OFF
  176. """,
  177. verboseHint: VStack {
  178. Text("Default: OFF").bold()
  179. Text("""
  180. When enabled, Trio will skip neutral temp basals (those that are the same as your default basal), if no adjustments are needed.
  181. When off, Trio will set temps whenever it can, so it will be easier to see if the system is working.
  182. """)
  183. }
  184. )
  185. SettingInputSection(
  186. decimalValue: $decimalPlaceholder,
  187. booleanValue: $state.unsuspendIfNoTemp,
  188. shouldDisplayHint: $shouldDisplayHint,
  189. selectedVerboseHint: Binding(
  190. get: { selectedVerboseHint },
  191. set: {
  192. selectedVerboseHint = $0.map { AnyView($0) }
  193. hintLabel = NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp")
  194. }
  195. ),
  196. units: state.units,
  197. type: .boolean,
  198. label: NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp"),
  199. miniHint: """
  200. Automatically resume your insulin pump if you forget to unsuspend it after a zero temp basal expires
  201. Default: OFF
  202. """,
  203. verboseHint: VStack {
  204. Text("Default: OFF").bold()
  205. Text("""
  206. Many people occasionally forget to resume / unsuspend their pump after reconnecting it. If you’re one of them, and you are willing to reliably set a zero temp basal whenever suspending and disconnecting your pump, this feature has your back. If enabled, it will automatically resume / unsuspend the pump if you forget to do so before your zero temp expires. As long as the zero temp is still running, it will leave the pump suspended.
  207. """)
  208. Text("Applies only to pumps with manual suspend options").italic()
  209. }
  210. )
  211. SettingInputSection(
  212. decimalValue: $decimalPlaceholder,
  213. booleanValue: $state.suspendZerosIOB,
  214. shouldDisplayHint: $shouldDisplayHint,
  215. selectedVerboseHint: Binding(
  216. get: { selectedVerboseHint },
  217. set: {
  218. selectedVerboseHint = $0.map { AnyView($0) }
  219. hintLabel = NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB")
  220. }
  221. ),
  222. units: state.units,
  223. type: .boolean,
  224. label: NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB"),
  225. miniHint: """
  226. Replaces any enacted temp basals prior to a pump suspend with a zero temp basal
  227. Default: OFF
  228. """,
  229. verboseHint: VStack {
  230. Text("Default: OFF").bold()
  231. Text("""
  232. Any existing temp basals during times the pump was suspended will be deleted and zero temp basals to negate the profile basal rates during times pump is suspended will be added.
  233. """)
  234. Text("Applies to only to pumps with manual suspend options").italic()
  235. }
  236. )
  237. SettingInputSection(
  238. decimalValue: $state.autotuneISFAdjustmentFraction,
  239. booleanValue: $booleanPlaceholder,
  240. shouldDisplayHint: $shouldDisplayHint,
  241. selectedVerboseHint: Binding(
  242. get: { selectedVerboseHint },
  243. set: {
  244. selectedVerboseHint = $0.map { AnyView($0) }
  245. hintLabel = NSLocalizedString(
  246. "Autotune ISF Adjustment Fraction",
  247. comment: "Autotune ISF Adjustment Fraction"
  248. )
  249. }
  250. ),
  251. units: state.units,
  252. type: .decimal("autotuneISFAdjustmentFraction"),
  253. label: NSLocalizedString("Autotune ISF Adjustment Fraction", comment: "Autotune ISF Adjustment Fraction"),
  254. miniHint: "Using Autotune is not advised",
  255. verboseHint: Text(
  256. NSLocalizedString(
  257. "The default of 0.5 for this value keeps autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF.",
  258. comment: "Autotune ISF Adjustment Fraction"
  259. )
  260. )
  261. )
  262. SettingInputSection(
  263. decimalValue: $state.min5mCarbimpact,
  264. booleanValue: $booleanPlaceholder,
  265. shouldDisplayHint: $shouldDisplayHint,
  266. selectedVerboseHint: Binding(
  267. get: { selectedVerboseHint },
  268. set: {
  269. selectedVerboseHint = $0.map { AnyView($0) }
  270. hintLabel = NSLocalizedString("Min 5m Carbimpact", comment: "Min 5m Carbimpact")
  271. }
  272. ),
  273. units: state.units,
  274. type: .decimal("min5mCarbimpact"),
  275. label: NSLocalizedString("Min 5m Carbimpact", comment: "Min 5m Carbimpact"),
  276. miniHint: """
  277. Set the default rate of carb absorption when no clear impact on blood glucose is visible
  278. Default: 8 mg/dL/5min
  279. """,
  280. verboseHint: VStack {
  281. Text("Default: 8 mg/dL/5 min").bold()
  282. Text("""
  283. The Min 5m Carbimpact setting determines the default expected glucose rise (in mg/dL) over a 5-minute period from carbs when the system cannot detect clear absorption from your blood glucose levels.
  284. The default value of 8 mg/dL per 5 minutes corresponds to an absorption rate of 24g of carbs per hour.
  285. This setting helps the system estimate how much glucose your body is absorbing, even when it’s not immediately visible in your glucose data, ensuring more accurate insulin dosing during carb absorption.
  286. """)
  287. }
  288. )
  289. SettingInputSection(
  290. decimalValue: $state.remainingCarbsFraction,
  291. booleanValue: $booleanPlaceholder,
  292. shouldDisplayHint: $shouldDisplayHint,
  293. selectedVerboseHint: Binding(
  294. get: { selectedVerboseHint },
  295. set: {
  296. selectedVerboseHint = $0.map { AnyView($0) }
  297. hintLabel = NSLocalizedString("Remaining Carbs Fraction", comment: "Remaining Carbs Fraction")
  298. }
  299. ),
  300. units: state.units,
  301. type: .decimal("remainingCarbsFraction"),
  302. label: NSLocalizedString("Remaining Carbs Fraction", comment: "Remaining Carbs Fraction"),
  303. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  304. verboseHint: Text(
  305. NSLocalizedString(
  306. "This is the fraction of carbs we’ll assume will absorb over 4h if we don’t yet see carb absorption.",
  307. comment: "Remaining Carbs Fraction"
  308. )
  309. )
  310. )
  311. SettingInputSection(
  312. decimalValue: $state.remainingCarbsCap,
  313. booleanValue: $booleanPlaceholder,
  314. shouldDisplayHint: $shouldDisplayHint,
  315. selectedVerboseHint: Binding(
  316. get: { selectedVerboseHint },
  317. set: {
  318. selectedVerboseHint = $0.map { AnyView($0) }
  319. hintLabel = NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap")
  320. }
  321. ),
  322. units: state.units,
  323. type: .decimal("remainingCarbsCap"),
  324. label: NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap"),
  325. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  326. verboseHint: Text(
  327. NSLocalizedString(
  328. "This is the amount of the maximum number of carbs we’ll assume will absorb over 4h if we don’t yet see carb absorption.",
  329. comment: "Remaining Carbs Cap"
  330. )
  331. )
  332. )
  333. SettingInputSection(
  334. decimalValue: $state.noisyCGMTargetMultiplier,
  335. booleanValue: $booleanPlaceholder,
  336. shouldDisplayHint: $shouldDisplayHint,
  337. selectedVerboseHint: Binding(
  338. get: { selectedVerboseHint },
  339. set: {
  340. selectedVerboseHint = $0.map { AnyView($0) }
  341. hintLabel = NSLocalizedString("Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier")
  342. }
  343. ),
  344. units: state.units,
  345. type: .decimal("noisyCGMTargetMultiplier"),
  346. label: NSLocalizedString("Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier"),
  347. miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
  348. verboseHint: Text(
  349. NSLocalizedString(
  350. "Defaults to 1.3. Increase target by this amount when looping off raw/noisy CGM data",
  351. comment: "Noisy CGM Target Multiplier"
  352. )
  353. )
  354. )
  355. }
  356. .sheet(isPresented: $shouldDisplayHint) {
  357. SettingInputHintView(
  358. hintDetent: $hintDetent,
  359. shouldDisplayHint: $shouldDisplayHint,
  360. hintLabel: hintLabel ?? "",
  361. hintText: selectedVerboseHint ?? AnyView(EmptyView()),
  362. sheetTitle: "Help"
  363. )
  364. }
  365. .scrollContentBackground(.hidden).background(color)
  366. .onAppear(perform: configureView)
  367. .navigationTitle("Additionals")
  368. .navigationBarTitleDisplayMode(.automatic)
  369. .onDisappear {
  370. state.saveIfChanged()
  371. }
  372. }
  373. }
  374. }