InsulinSensitivityScheduleEditor.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. //
  2. // InsulinSensitivityScheduleEditor.swift
  3. // LoopKitUI
  4. //
  5. // Created by Michael Pangburn on 4/20/20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. import HealthKit
  10. import LoopKit
  11. public struct InsulinSensitivityScheduleEditor: View {
  12. @EnvironmentObject private var displayGlucoseUnitObservable: DisplayGlucoseUnitObservable
  13. @Environment(\.appName) private var appName
  14. let mode: SettingsPresentationMode
  15. let viewModel: InsulinSensitivityScheduleEditorViewModel
  16. var displayGlucoseUnit: HKUnit {
  17. displayGlucoseUnitObservable.displayGlucoseUnit
  18. }
  19. public init(
  20. mode: SettingsPresentationMode,
  21. therapySettingsViewModel: TherapySettingsViewModel,
  22. didSave: (() -> Void)? = nil
  23. ) {
  24. self.mode = mode
  25. self.viewModel = InsulinSensitivityScheduleEditorViewModel(
  26. therapySettingsViewModel: therapySettingsViewModel,
  27. didSave: didSave)
  28. }
  29. public var body: some View {
  30. QuantityScheduleEditor(
  31. title: Text(TherapySetting.insulinSensitivity.title),
  32. description: description,
  33. schedule: viewModel.insulinSensitivitySchedule?.schedule(for: displayGlucoseUnit),
  34. unit: sensitivityUnit,
  35. guardrail: .insulinSensitivity,
  36. selectableValueStride: stride,
  37. defaultFirstScheduleItemValue: Guardrail.insulinSensitivity.startingSuggestion ?? Guardrail.insulinSensitivity.recommendedBounds.upperBound,
  38. confirmationAlertContent: confirmationAlertContent,
  39. guardrailWarning: InsulinSensitivityGuardrailWarning.init(crossedThresholds:),
  40. onSave: { insulinSensitivitySchedulePerU in
  41. // the sensitivity units are passed as the units to display `/U`
  42. // need to go back to displayGlucoseUnit. This does not affect the value
  43. // force unwrapping since dailyItems are already validated
  44. let insulinSensitivitySchedule = InsulinSensitivitySchedule(unit: displayGlucoseUnit,
  45. dailyItems: insulinSensitivitySchedulePerU.items,
  46. timeZone: insulinSensitivitySchedulePerU.timeZone)!
  47. viewModel.saveInsulinSensitivitySchedule(insulinSensitivitySchedule)
  48. },
  49. mode: mode,
  50. settingType: .insulinSensitivity
  51. )
  52. }
  53. private var description: Text {
  54. Text(TherapySetting.insulinSensitivity.descriptiveText(appName: appName))
  55. }
  56. private var sensitivityUnit: HKUnit {
  57. displayGlucoseUnit.unitDivided(by: .internationalUnit())
  58. }
  59. private var stride: HKQuantity {
  60. let doubleValue: Double
  61. switch displayGlucoseUnit {
  62. case .milligramsPerDeciliter:
  63. doubleValue = 1
  64. case .millimolesPerLiter:
  65. doubleValue = 0.1
  66. case let otherUnit:
  67. fatalError("Unsupported glucose unit \(otherUnit)")
  68. }
  69. return HKQuantity(unit: sensitivityUnit, doubleValue: doubleValue)
  70. }
  71. private var confirmationAlertContent: AlertContent {
  72. AlertContent(
  73. title: Text(LocalizedString("Save Insulin Sensitivities?", comment: "Alert title for confirming insulin sensitivities outside the recommended range")),
  74. message: Text(TherapySetting.insulinSensitivity.guardrailSaveWarningCaption)
  75. )
  76. }
  77. }
  78. private struct InsulinSensitivityGuardrailWarning: View {
  79. var crossedThresholds: [SafetyClassification.Threshold]
  80. var body: some View {
  81. assert(!crossedThresholds.isEmpty)
  82. return GuardrailWarning(
  83. therapySetting: .insulinSensitivity,
  84. title: crossedThresholds.count == 1 ? singularWarningTitle(for: crossedThresholds.first!) : multipleWarningTitle,
  85. thresholds: crossedThresholds
  86. )
  87. }
  88. private func singularWarningTitle(for threshold: SafetyClassification.Threshold) -> Text {
  89. switch threshold {
  90. case .minimum, .belowRecommended:
  91. return Text(LocalizedString("Low Insulin Sensitivity", comment: "Title text for the low insulin sensitivity warning"))
  92. case .aboveRecommended, .maximum:
  93. return Text(LocalizedString("High Insulin Sensitivity", comment: "Title text for the high insulin sensitivity warning"))
  94. }
  95. }
  96. private var multipleWarningTitle: Text {
  97. Text(LocalizedString("Insulin Sensitivities", comment: "Title text for multi-value insulin sensitivity warning"))
  98. }
  99. }