InsulinSensitivityScheduleEditor.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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. private var schedule: DailyQuantitySchedule<Double>?
  13. private var glucoseUnit: HKUnit
  14. private var save: (InsulinSensitivitySchedule) -> Void
  15. private var mode: SettingsPresentationMode
  16. @Environment(\.appName) private var appName
  17. public init(
  18. schedule: InsulinSensitivitySchedule?,
  19. mode: SettingsPresentationMode = .settings,
  20. glucoseUnit: HKUnit,
  21. onSave save: @escaping (InsulinSensitivitySchedule) -> Void
  22. ) {
  23. // InsulinSensitivitySchedule stores only the glucose unit.
  24. // For consistency across display & computation, convert to "real" <glucose unit>/U units.
  25. self.schedule = schedule.map { schedule in
  26. DailyQuantitySchedule(
  27. unit: glucoseUnit.unitDivided(by: .internationalUnit()),
  28. dailyItems: schedule.items
  29. )!
  30. }
  31. self.glucoseUnit = glucoseUnit
  32. self.save = save
  33. self.mode = mode
  34. }
  35. public init(
  36. viewModel: TherapySettingsViewModel,
  37. didSave: (() -> Void)? = nil
  38. ) {
  39. self.init(
  40. schedule: viewModel.therapySettings.insulinSensitivitySchedule,
  41. mode: viewModel.mode,
  42. glucoseUnit: viewModel.therapySettings.glucoseUnit!,
  43. onSave: { [weak viewModel] in
  44. viewModel?.saveInsulinSensitivitySchedule(insulinSensitivitySchedule: $0)
  45. didSave?()
  46. }
  47. )
  48. }
  49. public var body: some View {
  50. QuantityScheduleEditor(
  51. title: Text(TherapySetting.insulinSensitivity.title),
  52. description: description,
  53. schedule: schedule,
  54. unit: sensitivityUnit,
  55. guardrail: .insulinSensitivity,
  56. selectableValueStride: stride,
  57. defaultFirstScheduleItemValue: Guardrail.insulinSensitivity.startingSuggestion ?? Guardrail.insulinSensitivity.recommendedBounds.upperBound,
  58. confirmationAlertContent: confirmationAlertContent,
  59. guardrailWarning: InsulinSensitivityGuardrailWarning.init(crossedThresholds:),
  60. onSave: {
  61. // Convert back to the expected glucose-unit-only schedule.
  62. self.save(DailyQuantitySchedule(unit: self.glucoseUnit, dailyItems: $0.items)!)
  63. },
  64. mode: mode,
  65. settingType: .insulinSensitivity
  66. )
  67. }
  68. private var description: Text {
  69. Text(TherapySetting.insulinSensitivity.descriptiveText(appName: appName))
  70. }
  71. private var sensitivityUnit: HKUnit {
  72. glucoseUnit.unitDivided(by: .internationalUnit())
  73. }
  74. private var stride: HKQuantity {
  75. let doubleValue: Double
  76. switch glucoseUnit {
  77. case .milligramsPerDeciliter:
  78. doubleValue = 1
  79. case .millimolesPerLiter:
  80. doubleValue = 0.1
  81. case let otherUnit:
  82. fatalError("Unsupported glucose unit \(otherUnit)")
  83. }
  84. return HKQuantity(unit: sensitivityUnit, doubleValue: doubleValue)
  85. }
  86. private var confirmationAlertContent: AlertContent {
  87. AlertContent(
  88. title: Text(LocalizedString("Save Insulin Sensitivities?", comment: "Alert title for confirming insulin sensitivities outside the recommended range")),
  89. message: Text(TherapySetting.insulinSensitivity.guardrailSaveWarningCaption)
  90. )
  91. }
  92. }
  93. private struct InsulinSensitivityGuardrailWarning: View {
  94. var crossedThresholds: [SafetyClassification.Threshold]
  95. var body: some View {
  96. assert(!crossedThresholds.isEmpty)
  97. return GuardrailWarning(
  98. title: crossedThresholds.count == 1 ? singularWarningTitle(for: crossedThresholds.first!) : multipleWarningTitle,
  99. thresholds: crossedThresholds
  100. )
  101. }
  102. private func singularWarningTitle(for threshold: SafetyClassification.Threshold) -> Text {
  103. switch threshold {
  104. case .minimum, .belowRecommended:
  105. return Text(LocalizedString("Low Insulin Sensitivity", comment: "Title text for the low insulin sensitivity warning"))
  106. case .aboveRecommended, .maximum:
  107. return Text(LocalizedString("High Insulin Sensitivity", comment: "Title text for the high insulin sensitivity warning"))
  108. }
  109. }
  110. private var multipleWarningTitle: Text {
  111. Text(LocalizedString("Insulin Sensitivities", comment: "Title text for multi-value insulin sensitivity warning"))
  112. }
  113. }