CarbRatioScheduleEditor.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //
  2. // CarbRatioScheduleEditor.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. fileprivate extension HKUnit {
  12. static let storedCarbRatioScheduleUnit = HKUnit.gram()
  13. static let realCarbRatioScheduleUnit = HKUnit.gramsPerUnit
  14. }
  15. public struct CarbRatioScheduleEditor: View {
  16. private var schedule: DailyQuantitySchedule<Double>?
  17. private var mode: SettingsPresentationMode
  18. private var save: (CarbRatioSchedule) -> Void
  19. @Environment(\.appName) private var appName
  20. public init(
  21. schedule: CarbRatioSchedule?,
  22. onSave save: @escaping (CarbRatioSchedule) -> Void,
  23. mode: SettingsPresentationMode = .settings
  24. ) {
  25. // CarbRatioSchedule stores only the gram unit.
  26. // For consistency across display & computation, convert to "real" g/U units.
  27. self.schedule = schedule.map { schedule in
  28. DailyQuantitySchedule(
  29. unit: .realCarbRatioScheduleUnit,
  30. dailyItems: schedule.items
  31. )!
  32. }
  33. self.mode = mode
  34. self.save = save
  35. }
  36. public init(
  37. mode: SettingsPresentationMode,
  38. therapySettingsViewModel: TherapySettingsViewModel,
  39. didSave: (() -> Void)? = nil
  40. ) {
  41. self.init(
  42. schedule: therapySettingsViewModel.therapySettings.carbRatioSchedule,
  43. onSave: { [weak therapySettingsViewModel] in
  44. therapySettingsViewModel?.saveCarbRatioSchedule(carbRatioSchedule: $0)
  45. didSave?()
  46. },
  47. mode: mode
  48. )
  49. }
  50. public var body: some View {
  51. QuantityScheduleEditor(
  52. title: Text(TherapySetting.carbRatio.title),
  53. description: description,
  54. schedule: schedule,
  55. unit: .realCarbRatioScheduleUnit,
  56. guardrail: .carbRatio,
  57. selectableValueStride: HKQuantity(unit: .realCarbRatioScheduleUnit, doubleValue: 0.1),
  58. quantitySelectionMode: .fractional,
  59. defaultFirstScheduleItemValue: Guardrail.carbRatio.startingSuggestion ?? Guardrail.carbRatio.recommendedBounds.upperBound,
  60. confirmationAlertContent: confirmationAlertContent,
  61. guardrailWarning: CarbRatioGuardrailWarning.init(crossedThresholds:),
  62. onSave: {
  63. // Convert back to the expected gram-unit-only schedule.
  64. self.save(DailyQuantitySchedule(unit: .storedCarbRatioScheduleUnit, dailyItems: $0.items)!)
  65. },
  66. mode: mode,
  67. settingType: .carbRatio
  68. )
  69. }
  70. private var description: Text {
  71. Text(TherapySetting.carbRatio.descriptiveText(appName: appName))
  72. }
  73. private var confirmationAlertContent: AlertContent {
  74. AlertContent(
  75. title: Text(LocalizedString("Save Carb Ratios?", comment: "Alert title for confirming carb ratios outside the recommended range")),
  76. message: Text(TherapySetting.carbRatio.guardrailSaveWarningCaption)
  77. )
  78. }
  79. }
  80. private struct CarbRatioGuardrailWarning: View {
  81. var crossedThresholds: [SafetyClassification.Threshold]
  82. var body: some View {
  83. assert(!crossedThresholds.isEmpty)
  84. return GuardrailWarning(
  85. therapySetting: .carbRatio,
  86. title: crossedThresholds.count == 1 ? singularWarningTitle(for: crossedThresholds.first!) : multipleWarningTitle,
  87. thresholds: crossedThresholds
  88. )
  89. }
  90. private func singularWarningTitle(for threshold: SafetyClassification.Threshold) -> Text {
  91. switch threshold {
  92. case .minimum, .belowRecommended:
  93. return Text(LocalizedString("Low Carb Ratio", comment: "Title text for the low carb ratio warning"))
  94. case .aboveRecommended, .maximum:
  95. return Text(LocalizedString("High Carb Ratio", comment: "Title text for the high carb ratio warning"))
  96. }
  97. }
  98. private var multipleWarningTitle: Text {
  99. Text(LocalizedString("Carb Ratios", comment: "Title text for multi-value carb ratio warning"))
  100. }
  101. }