From 9e4045960fb9af74847821d1c4f73e9f0b55a183 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 17 Jan 2022 12:10:18 -0600 Subject: [PATCH 1/8] Do not show workout mode settings in therapy settings --- Loop/Views/SettingsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Loop/Views/SettingsView.swift b/Loop/Views/SettingsView.swift index 62e73fc8fb..7bffcf91dc 100644 --- a/Loop/Views/SettingsView.swift +++ b/Loop/Views/SettingsView.swift @@ -148,6 +148,7 @@ extension SettingsView { .sheet(isPresented: $therapySettingsIsPresented) { TherapySettingsView(mode: .settings, viewModel: TherapySettingsViewModel(therapySettings: self.viewModel.therapySettings(), + sensitivityOverridesEnabled: FeatureFlags.sensitivityOverridesEnabled, delegate: self.viewModel.therapySettingsViewModelDelegate)) .environmentObject(displayGlucoseUnitObservable) .environment(\.dismissAction, self.dismiss) From 3c0f9030bc282245469d8aeab73a74faa7a07a36 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Tue, 18 Jan 2022 17:37:28 -0600 Subject: [PATCH 2/8] Fix crash when adding carbs from carb list when in open loop mode --- Loop/View Controllers/CarbAbsorptionViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop/View Controllers/CarbAbsorptionViewController.swift b/Loop/View Controllers/CarbAbsorptionViewController.swift index 5baf9dc4be..441114f5b5 100644 --- a/Loop/View Controllers/CarbAbsorptionViewController.swift +++ b/Loop/View Controllers/CarbAbsorptionViewController.swift @@ -513,7 +513,7 @@ final class CarbAbsorptionViewController: LoopChartsTableViewController, Identif navigationWrapper = UINavigationController(rootViewController: carbEntryViewController) } else { let viewModel = SimpleBolusViewModel(delegate: deviceManager, displayMealEntry: true) - let bolusEntryView = SimpleBolusView(viewModel: viewModel) + let bolusEntryView = SimpleBolusView(viewModel: viewModel).environmentObject(DisplayGlucoseUnitObservable(displayGlucoseUnit: .milligramsPerDeciliter)) let hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) navigationWrapper = UINavigationController(rootViewController: hostingController) hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation)) From 4426103d596896204e086f3dc24d634ca3a855c7 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Fri, 21 Jan 2022 16:36:16 -0600 Subject: [PATCH 3/8] Add handling for 45mm and 41mm watches (#1589) * Add handling for 45mm watches * Add 45mm cases * Adding enum for 41mm --- .../Views/Carb Entry & Bolus/CarbAndBolusFlow.swift | 8 ++++---- .../Views/Extensions/Environment+SizeClass.swift | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/WatchApp Extension/Views/Carb Entry & Bolus/CarbAndBolusFlow.swift b/WatchApp Extension/Views/Carb Entry & Bolus/CarbAndBolusFlow.swift index bb776a0cca..7528e71d9c 100644 --- a/WatchApp Extension/Views/Carb Entry & Bolus/CarbAndBolusFlow.swift +++ b/WatchApp Extension/Views/Carb Entry & Bolus/CarbAndBolusFlow.swift @@ -158,9 +158,9 @@ extension CarbAndBolusFlow { return 2 case .size42mm: return 0 - case .size40mm: + case .size40mm, .size41mm: return configuration == .carbEntry ? 7 : 19 - case .size44mm: + case .size44mm, .size45mm: return 5 } } @@ -228,9 +228,9 @@ extension CarbAndBolusFlow { switch sizeClass { case .size38mm, .size42mm: return 0 - case .size40mm: + case .size40mm, .size41mm: return 20 - case .size44mm: + case .size44mm, .size45mm: return 27 } } diff --git a/WatchApp Extension/Views/Extensions/Environment+SizeClass.swift b/WatchApp Extension/Views/Extensions/Environment+SizeClass.swift index 5b5258a06e..e558008a6a 100644 --- a/WatchApp Extension/Views/Extensions/Environment+SizeClass.swift +++ b/WatchApp Extension/Views/Extensions/Environment+SizeClass.swift @@ -28,16 +28,20 @@ extension WKInterfaceDevice { case size38mm case size42mm - // Apple Watch Series 4 and later + // Apple Watch Series 4 - 6 case size40mm case size44mm + + // Apple Watch Series 7 + case size41mm + case size45mm } var sizeClass: SizeClass { if let sizeClass = SizeClass(screenSize: screenBounds.size) { return sizeClass } else { - assertionFailure("Unrecognized Watch size \(screenBounds.size)") + //assertionFailure("Unrecognized Watch size \(screenBounds.size)") return .size40mm } } @@ -61,8 +65,12 @@ extension WKInterfaceDevice.SizeClass { return CGSize(width: 156, height: 195) case .size40mm: return CGSize(width: 162, height: 197) + case .size41mm: + return CGSize(width: 176, height: 215) case .size44mm: return CGSize(width: 184, height: 224) + case .size45mm: + return CGSize(width: 198, height: 242) } } From 21d0e2f4d72411fbaba92f619f2bf50a9d814a2a Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 23 Jan 2022 13:13:22 -0600 Subject: [PATCH 4/8] Increase local cache duration to 7 days --- Loop.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop.xcconfig b/Loop.xcconfig index eb37ad0db1..5306494a96 100644 --- a/Loop.xcconfig +++ b/Loop.xcconfig @@ -23,7 +23,7 @@ APP_STORE_URL = SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) // General [DEFAULT] -LOOP_LOCAL_CACHE_DURATION_DAYS = 1 +LOOP_LOCAL_CACHE_DURATION_DAYS = 7 // Entitlements [DEFAULT] LOOP_ENTITLEMENTS = Loop/Loop.entitlements From c4c78569b1d348b1aa86a0318965a0aef0d33516 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Tue, 25 Jan 2022 11:49:10 -0600 Subject: [PATCH 5/8] Put simple bolus calculator behind feature flag (#1594) * Put simple bolus calculator behind feature flag * Re-enable carb editing in open-loop when simple bolus calc turned off --- Common/FeatureFlags.swift | 8 +++++ .../CarbAbsorptionViewController.swift | 20 ++++++------- .../StatusTableViewController.swift | 30 +++++++++---------- Loop/Views/SimpleBolusView.swift | 2 +- LoopUI/Views/LoopCompletionHUDView.swift | 2 +- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Common/FeatureFlags.swift b/Common/FeatureFlags.swift index 5cf208a94d..1603066ef2 100644 --- a/Common/FeatureFlags.swift +++ b/Common/FeatureFlags.swift @@ -28,6 +28,7 @@ struct FeatureFlagConfiguration: Decodable { let sensitivityOverridesEnabled: Bool let simulatedCoreDataEnabled: Bool let siriEnabled: Bool + let simpleBolusCalculatorEnabled: Bool fileprivate init() { // Swift compiler config is inverse, since the default state is enabled. @@ -143,6 +144,12 @@ struct FeatureFlagConfiguration: Decodable { #else self.siriEnabled = true #endif + + #if SIMPLE_BOLUS_CALCULATOR_ENABLED + self.simpleBolusCalculatorEnabled = true + #else + self.simpleBolusCalculatorEnabled = false + #endif } } @@ -167,6 +174,7 @@ extension FeatureFlagConfiguration : CustomDebugStringConvertible { "* automaticBolusEnabled: \(automaticBolusEnabled)", "* manualDoseEntryEnabled: \(manualDoseEntryEnabled)", "* allowDebugFeatures: \(allowDebugFeatures)", + "* simpleBolusCalculatorEnabled: \(simpleBolusCalculatorEnabled)", ].joined(separator: "\n") } } diff --git a/Loop/View Controllers/CarbAbsorptionViewController.swift b/Loop/View Controllers/CarbAbsorptionViewController.swift index 441114f5b5..b1bb0fe2d6 100644 --- a/Loop/View Controllers/CarbAbsorptionViewController.swift +++ b/Loop/View Controllers/CarbAbsorptionViewController.swift @@ -63,11 +63,9 @@ final class CarbAbsorptionViewController: LoopChartsTableViewController, Identif } navigationItem.rightBarButtonItem?.isEnabled = isOnboardingComplete - - if !closedLoopStatus.isClosedLoop { - allowEditing = false - } + allowEditing = closedLoopStatus.isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled + if allowEditing { navigationItem.rightBarButtonItems?.append(editButtonItem) } @@ -504,19 +502,19 @@ final class CarbAbsorptionViewController: LoopChartsTableViewController, Identif @IBAction func presentCarbEntryScreen() { let navigationWrapper: UINavigationController - if closedLoopStatus.isClosedLoop { + if FeatureFlags.simpleBolusCalculatorEnabled && !closedLoopStatus.isClosedLoop { + let viewModel = SimpleBolusViewModel(delegate: deviceManager, displayMealEntry: true) + let bolusEntryView = SimpleBolusView(viewModel: viewModel).environmentObject(DisplayGlucoseUnitObservable(displayGlucoseUnit: .milligramsPerDeciliter)) + let hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) + navigationWrapper = UINavigationController(rootViewController: hostingController) + hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation)) + } else { let carbEntryViewController = UIStoryboard(name: "Main", bundle: Bundle(for: AppDelegate.self)).instantiateViewController(withIdentifier: "CarbEntryViewController") as! CarbEntryViewController carbEntryViewController.deviceManager = deviceManager carbEntryViewController.defaultAbsorptionTimes = deviceManager.carbStore.defaultAbsorptionTimes carbEntryViewController.preferredCarbUnit = deviceManager.carbStore.preferredUnit navigationWrapper = UINavigationController(rootViewController: carbEntryViewController) - } else { - let viewModel = SimpleBolusViewModel(delegate: deviceManager, displayMealEntry: true) - let bolusEntryView = SimpleBolusView(viewModel: viewModel).environmentObject(DisplayGlucoseUnitObservable(displayGlucoseUnit: .milligramsPerDeciliter)) - let hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) - navigationWrapper = UINavigationController(rootViewController: hostingController) - hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation)) } self.present(navigationWrapper, animated: true) } diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 2d78efc8ef..1a7578bff5 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -1211,7 +1211,16 @@ final class StatusTableViewController: LoopChartsTableViewController { func presentCarbEntryScreen(_ activity: NSUserActivity?) { let navigationWrapper: UINavigationController - if closedLoopStatus.isClosedLoop { + if FeatureFlags.simpleBolusCalculatorEnabled && !closedLoopStatus.isClosedLoop { + let viewModel = SimpleBolusViewModel(delegate: deviceManager, displayMealEntry: true) + if let activity = activity { + viewModel.restoreUserActivityState(activity) + } + let bolusEntryView = SimpleBolusView(viewModel: viewModel).environmentObject(deviceManager.displayGlucoseUnitObservable) + let hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) + navigationWrapper = UINavigationController(rootViewController: hostingController) + hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation)) + } else { let carbEntryViewController = UIStoryboard(name: "Main", bundle: Bundle(for: AppDelegate.self)).instantiateViewController(withIdentifier: "CarbEntryViewController") as! CarbEntryViewController carbEntryViewController.deviceManager = deviceManager @@ -1221,15 +1230,6 @@ final class StatusTableViewController: LoopChartsTableViewController { carbEntryViewController.restoreUserActivityState(activity) } navigationWrapper = UINavigationController(rootViewController: carbEntryViewController) - } else { - let viewModel = SimpleBolusViewModel(delegate: deviceManager, displayMealEntry: true) - if let activity = activity { - viewModel.restoreUserActivityState(activity) - } - let bolusEntryView = SimpleBolusView(viewModel: viewModel).environmentObject(deviceManager.displayGlucoseUnitObservable) - let hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) - navigationWrapper = UINavigationController(rootViewController: hostingController) - hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation)) } present(navigationWrapper, animated: true) } @@ -1240,14 +1240,14 @@ final class StatusTableViewController: LoopChartsTableViewController { func presentBolusEntryView(enableManualGlucoseEntry: Bool = false) { let hostingController: DismissibleHostingController - if closedLoopStatus.isClosedLoop { - let viewModel = BolusEntryViewModel(delegate: deviceManager, isManualGlucoseEntryEnabled: enableManualGlucoseEntry) - let bolusEntryView = BolusEntryView(viewModel: viewModel).environmentObject(deviceManager.displayGlucoseUnitObservable) - hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) - } else { + if FeatureFlags.simpleBolusCalculatorEnabled && !closedLoopStatus.isClosedLoop { let viewModel = SimpleBolusViewModel(delegate: deviceManager, displayMealEntry: false) let bolusEntryView = SimpleBolusView(viewModel: viewModel).environmentObject(deviceManager.displayGlucoseUnitObservable) hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) + } else { + let viewModel = BolusEntryViewModel(delegate: deviceManager, isManualGlucoseEntryEnabled: enableManualGlucoseEntry) + let bolusEntryView = BolusEntryView(viewModel: viewModel).environmentObject(deviceManager.displayGlucoseUnitObservable) + hostingController = DismissibleHostingController(rootView: bolusEntryView, isModalInPresentation: false) } let navigationWrapper = UINavigationController(rootViewController: hostingController) hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation)) diff --git a/Loop/Views/SimpleBolusView.swift b/Loop/Views/SimpleBolusView.swift index 45ac55ba2a..50583db6cd 100644 --- a/Loop/Views/SimpleBolusView.swift +++ b/Loop/Views/SimpleBolusView.swift @@ -355,7 +355,7 @@ struct SimpleBolusView: View { private func closedLoopOffInformationalModal() -> SwiftUI.Alert { return SwiftUI.Alert( title: Text("Closed Loop OFF", comment: "Alert title for closed loop off informational modal"), - message: Text(String(format: NSLocalizedString("%1$@ is operating with Closed Loop in the OFF position. Your pump and CGM will continue operating, but your basal insulin will not adjust automatically.", comment: "Alert message for closed loop off informational modal. (1: app name)"), Bundle.main.bundleDisplayName)) + message: Text(String(format: NSLocalizedString("%1$@ is operating with Closed Loop in the OFF position. Your pump and CGM will continue operating, but the app will not adjust dosing automatically.", comment: "Alert message for closed loop off informational modal. (1: app name)"), Bundle.main.bundleDisplayName)) ) } diff --git a/LoopUI/Views/LoopCompletionHUDView.swift b/LoopUI/Views/LoopCompletionHUDView.swift index 95a554deb9..fb0460bbe0 100644 --- a/LoopUI/Views/LoopCompletionHUDView.swift +++ b/LoopUI/Views/LoopCompletionHUDView.swift @@ -165,7 +165,7 @@ extension LoopCompletionHUDView { case .fresh: if loopStateView.open { return (title: LocalizedString("Closed Loop OFF", comment: "Title of green open loop OFF message"), - message: String(format: NSLocalizedString("\n%1$@ is operating with Closed Loop in the OFF position. Your pump and CGM will continue operating, but your basal insulin will not adjust automatically.\n\nTap Settings to toggle Closed Loop ON if you wish for the app to automate your insulin.", comment: "Green closed loop OFF message (1: app name)"), Bundle.main.bundleDisplayName)) + message: String(format: NSLocalizedString("\n%1$@ is operating with Closed Loop in the OFF position. Your pump and CGM will continue operating, but the app will not adjust dosing automatically.\n\nTap Settings to toggle Closed Loop ON if you wish for the app to automate your insulin.", comment: "Green closed loop OFF message (1: app name)"), Bundle.main.bundleDisplayName)) } else { return (title: LocalizedString("Closed Loop ON", comment: "Title of green closed loop ON message"), message: String(format: LocalizedString("\n%1$@ is operating with Closed Loop in the ON position. Your last loop was successful within the last 5 minutes.", comment: "Green closed loop ON message (1: app name)"), Bundle.main.bundleDisplayName)) From 8c5c688963a015716496bb37616395b005313b08 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 26 Jan 2022 00:27:44 -0600 Subject: [PATCH 6/8] Allow debug menu --- Loop/View Controllers/StatusTableViewController.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 1a7578bff5..470041cf58 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -1662,9 +1662,6 @@ final class StatusTableViewController: LoopChartsTableViewController { guard FeatureFlags.allowDebugFeatures else { return } - guard FeatureFlags.scenariosEnabled || FeatureFlags.simulatedCoreDataEnabled || FeatureFlags.mockTherapySettingsEnabled else { - fatalError("\(#function) should be invoked only when scenarios, simulated core data, or mock therapy settings are enabled") - } let actionSheet = UIAlertController(title: "Debug", message: nil, preferredStyle: .actionSheet) if FeatureFlags.scenariosEnabled { From a4bce1c0639476858f2711cf1feeeee1bdfc0363 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 26 Jan 2022 15:07:07 -0600 Subject: [PATCH 7/8] Enable watch control during open loop if simple bolus calculator not enabled --- WatchApp Extension/Controllers/ActionHUDController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WatchApp Extension/Controllers/ActionHUDController.swift b/WatchApp Extension/Controllers/ActionHUDController.swift index 3de20b9b89..bdd75f3015 100644 --- a/WatchApp Extension/Controllers/ActionHUDController.swift +++ b/WatchApp Extension/Controllers/ActionHUDController.swift @@ -72,7 +72,7 @@ final class ActionHUDController: HUDInterfaceController { let isClosedLoop = loopManager.activeContext?.isClosedLoop ?? false - if !isClosedLoop { + if !isClosedLoop && FeatureFlags.simpleBolusCalculatorEnabled { preMealButtonGroup.state = .disabled overrideButtonGroup.state = .disabled carbsButtonGroup.state = .disabled From d5cbbe4e96f98dbe54f45a81123c4f11bdeb4813 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Thu, 27 Jan 2022 10:18:27 -0600 Subject: [PATCH 8/8] Show forecast and forecast details in open loop --- .../View Controllers/StatusTableViewController.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 470041cf58..76cdc74163 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -247,7 +247,7 @@ final class StatusTableViewController: LoopChartsTableViewController { let isClosedLoop = closedLoopStatus.isClosedLoop toolbarItems![0].isEnabled = isPumpOnboarded - toolbarItems![2].isEnabled = isPumpOnboarded && isClosedLoop + toolbarItems![2].isEnabled = isPumpOnboarded && (isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled) toolbarItems![4].isEnabled = isPumpOnboarded toolbarItems![6].isEnabled = isPumpOnboarded toolbarItems![8].isEnabled = true @@ -493,7 +493,7 @@ final class StatusTableViewController: LoopChartsTableViewController { if let glucoseSamples = glucoseSamples { self.statusCharts.setGlucoseValues(glucoseSamples) } - if isClosedLoop, let predictedGlucoseValues = predictedGlucoseValues { + if (isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled), let predictedGlucoseValues = predictedGlucoseValues { self.statusCharts.setPredictedGlucoseValues(predictedGlucoseValues) } else { self.statusCharts.setPredictedGlucoseValues([]) @@ -781,7 +781,7 @@ final class StatusTableViewController: LoopChartsTableViewController { } private func updatePreMealModeAvailability(isClosedLoop: Bool) { - let allowed = onboardingManager.isComplete && isClosedLoop + let allowed = onboardingManager.isComplete && (isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled) toolbarItems![2] = createPreMealButtonItem(selected: preMealMode ?? false && allowed, isEnabled: allowed) } @@ -858,7 +858,7 @@ final class StatusTableViewController: LoopChartsTableViewController { return self?.statusCharts.glucoseChart(withFrame: frame)?.view }) cell.setTitleLabelText(label: NSLocalizedString("Glucose", comment: "The title of the glucose and prediction graph")) - cell.doesNavigate = closedLoopStatus.isClosedLoop + cell.doesNavigate = closedLoopStatus.isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled case .iob: cell.setChartGenerator(generator: { [weak self] (frame) in return self?.statusCharts.iobChart(withFrame: frame)?.view @@ -1013,7 +1013,7 @@ final class StatusTableViewController: LoopChartsTableViewController { } else { cell.setSubtitleLabel(label: nil) } - cell.doesNavigate = closedLoopStatus.isClosedLoop + cell.doesNavigate = closedLoopStatus.isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled case .iob: if let currentIOB = currentIOBDescription { cell.setSubtitleLabel(label: currentIOB) @@ -1133,7 +1133,7 @@ final class StatusTableViewController: LoopChartsTableViewController { case .charts: switch ChartRow(rawValue: indexPath.row)! { case .glucose: - if closedLoopStatus.isClosedLoop { + if closedLoopStatus.isClosedLoop || !FeatureFlags.simpleBolusCalculatorEnabled { performSegue(withIdentifier: PredictionTableViewController.className, sender: indexPath) } case .iob, .dose: