Przeglądaj źródła

Merge pull request #1248 from t1dude/feat/quickbolus

Add a Quick Bolus feature
trioneer-dev 3 dni temu
rodzic
commit
fc7b5c7531

+ 86 - 30
Trio.xcodeproj/project.pbxproj

@@ -136,9 +136,6 @@
 		38A504A425DD9C4000C5B9E8 /* UserDefaultsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A5049125DD9C4000C5B9E8 /* UserDefaultsExtensions.swift */; };
 		38A9260525F012D8009E3739 /* CarbRatios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A9260425F012D8009E3739 /* CarbRatios.swift */; };
 		38AAF85525FFF846004AF583 /* CurrentGlucoseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AAF85425FFF846004AF583 /* CurrentGlucoseView.swift */; };
-		BD11C001000000000000C001 /* CGMSensorDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD11C000000000000000C001 /* CGMSensorDisplayState.swift */; };
-		BD11C001000000000000C003 /* SensorLifecycleArcView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD11C000000000000000C003 /* SensorLifecycleArcView.swift */; };
-		BD11C001000000000000C004 /* SensorStatusTagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD11C000000000000000C004 /* SensorStatusTagView.swift */; };
 		38AEE73D25F0200C0013F05B /* TrioSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AEE73C25F0200C0013F05B /* TrioSettings.swift */; };
 		38AEE75225F022080013F05B /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AEE75125F022080013F05B /* SettingsManager.swift */; };
 		38AEE75725F0F18E0013F05B /* CarbsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AEE75625F0F18E0013F05B /* CarbsStorage.swift */; };
@@ -379,6 +376,7 @@
 		3E54EF2D2E476DA40006F54D /* MedtrumKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3E54EF2B2E476DA40006F54D /* MedtrumKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		3E62C7822F54CC1B00433237 /* BolusDisplayThreshold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E62C7812F54CC1600433237 /* BolusDisplayThreshold.swift */; };
 		3EF667132FE48509009FB31A /* BasalDeliveryState+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF667122FE48502009FB31A /* BasalDeliveryState+Extension.swift */; };
+		3F23E18680094E6DA98628E4 /* QuickPickBolusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A54068ABDAE4898B243DF14 /* QuickPickBolusesView.swift */; };
 		41740E936552456AAC0EDAC3 /* SettingsSearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3919BBB515547118D684CA2 /* SettingsSearchTests.swift */; };
 		45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8A87AA037BD079BA3528BA /* ConfigEditorDataFlow.swift */; };
 		45717281F743594AA9D87191 /* ConfigEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */; };
@@ -406,6 +404,7 @@
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
 		5837A5302BD2E3C700A5DC04 /* CarbEntryStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A52F2BD2E3C700A5DC04 /* CarbEntryStored+helper.swift */; };
+		584C79A91D034426A1DE60CB /* SlideToConfirmView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF96FC200503405E94334390 /* SlideToConfirmView.swift */; };
 		585E2CAE2BE7BF46006ECF1A /* PumpEvent+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E2CAD2BE7BF46006ECF1A /* PumpEvent+helper.swift */; };
 		58645B992CA2D1A4008AFCE7 /* GlucoseSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58645B982CA2D1A4008AFCE7 /* GlucoseSetup.swift */; };
 		58645B9B2CA2D24F008AFCE7 /* CarbSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58645B9A2CA2D24F008AFCE7 /* CarbSetup.swift */; };
@@ -449,8 +448,10 @@
 		711C0CB42CAABE788916BC9D /* ManualTempBasalDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */; };
 		71D44AAB2CA5F5EA0036EE9E /* AlertPermissionsChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D44AAA2CA5F5EA0036EE9E /* AlertPermissionsChecker.swift */; };
 		72F1BD388F42FCA6C52E4500 /* ConfigEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44080E4709E3AE4B73054563 /* ConfigEditorProvider.swift */; };
+		786B6E8A8E294AE883581154 /* QuickPickBolusesConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D543DE17E0054AF0821411BB /* QuickPickBolusesConfigDataFlow.swift */; };
 		7BCFACB97C821041BA43A114 /* ManualTempBasalRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C377490C77661D75E8C50649 /* ManualTempBasalRootView.swift */; };
 		7F7B756BE8543965D9FDF1A2 /* HistoryDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A401509D21F7F35D4E109EDA /* HistoryDataFlow.swift */; };
+		87F4D02B8D4D4D329BA291D2 /* QuickPickBolusesConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91711E7CA54747EFAD6540EE /* QuickPickBolusesConfigStateModel.swift */; };
 		88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB3BAE7494CB771ABAC7B8B /* ISFEditorRootView.swift */; };
 		8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A9134292D63D9A1007F8874 /* Localizable.xcstrings */; };
 		8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */; };
@@ -472,6 +473,9 @@
 		BD04ECCE2D29952A008C5FEB /* BolusProgressOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD04ECCD2D299522008C5FEB /* BolusProgressOverlay.swift */; };
 		BD0B2EF32C5998E600B3298F /* MealPresetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0B2EF22C5998E600B3298F /* MealPresetView.swift */; };
 		BD10516D2DA986E1007C6D89 /* PulsingLogoAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD10516C2DA986DC007C6D89 /* PulsingLogoAnimation.swift */; };
+		BD11C001000000000000C001 /* CGMSensorDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD11C000000000000000C001 /* CGMSensorDisplayState.swift */; };
+		BD11C001000000000000C003 /* SensorLifecycleArcView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD11C000000000000000C003 /* SensorLifecycleArcView.swift */; };
+		BD11C001000000000000C004 /* SensorStatusTagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD11C000000000000000C004 /* SensorStatusTagView.swift */; };
 		BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1661302B82ADAB00256551 /* CustomProgressView.swift */; };
 		BD175EBE0000100000000001 /* HistoryDataFlow+Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD175EFE0000100000000001 /* HistoryDataFlow+Models.swift */; };
 		BD175EBE0000100000000002 /* HistoryDeletionTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD175EFE0000100000000002 /* HistoryDeletionTarget.swift */; };
@@ -603,7 +607,9 @@
 		C2BA6B972F758E7500348E6A /* WatchNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BA6B962F758E7500348E6A /* WatchNotificationHandler.swift */; };
 		C2BA6B992F758E7600348E6A /* NotificationIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BA6B982F758E7600348E6A /* NotificationIdentifiers.swift */; };
 		C2BA6B9A2F7593C300348E6A /* NotificationIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BA6B982F758E7600348E6A /* NotificationIdentifiers.swift */; };
+		C5BAAA36C643401D97A8CE11 /* QuickPickBolusesConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3EBC8BA17C4627BD32C58F /* QuickPickBolusesConfigRootView.swift */; };
 		C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */; };
+		CA02000000000000000010C2 /* DeliveryLimitsSyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */; };
 		CA370FC152BC98B3D1832968 /* BasalProfileEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8BCB0C37DEB5EC377B9612 /* BasalProfileEditorRootView.swift */; };
 		CC6C406E2ACDD69E009B8058 /* RawFetchedProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */; };
 		CC76E9512BD4812E008BEB61 /* Forecast+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC76E9502BD4812E008BEB61 /* Forecast+helper.swift */; };
@@ -656,7 +662,6 @@
 		CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */; };
 		CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */; };
 		CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */; };
-		CA02000000000000000010C2 /* DeliveryLimitsSyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */; };
 		CEF1ED6B2D58FB5800FAF41E /* CGMOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */; };
 		D6D02515BBFBE64FEBE89856 /* HistoryRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E04BA5E0A003DE8E0A9C6 /* HistoryRootView.swift */; };
 		D6DEC113821A7F1056C4AA1E /* NightscoutConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */; };
@@ -672,6 +677,7 @@
 		DD1745132C54169400211FAC /* DevicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745122C54169400211FAC /* DevicesView.swift */; };
 		DD1745152C54388A00211FAC /* TherapySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745142C54388A00211FAC /* TherapySettingsView.swift */; };
 		DD1745172C54389F00211FAC /* FeatureSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745162C54389F00211FAC /* FeatureSettingsView.swift */; };
+		AABB00022C54389F00211FAC /* TreatmentsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABB00012C54389F00211FAC /* TreatmentsSettingsView.swift */; };
 		DD1745192C543B5700211FAC /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745182C543B5700211FAC /* NotificationsView.swift */; };
 		DD17451D2C543C5F00211FAC /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17451C2C543C5F00211FAC /* ServicesView.swift */; };
 		DD1745202C55523E00211FAC /* SMBSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17451F2C55523E00211FAC /* SMBSettingsDataFlow.swift */; };
@@ -872,6 +878,7 @@
 		DDFF204A2DB29EF500AB8A96 /* WatchLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFF20492DB29EF500AB8A96 /* WatchLogger.swift */; };
 		DDFF204E2DB2C00B00AB8A96 /* WatchStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFF204D2DB2C00B00AB8A96 /* WatchStateSnapshot.swift */; };
 		DDFF20502DB2C11900AB8A96 /* WatchStateSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFF204F2DB2C11900AB8A96 /* WatchStateSnapshot.swift */; };
+		DECF1903237041E2BBBE4033 /* QuickPickBolusesInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1526A587AD1544C495050662 /* QuickPickBolusesInfoView.swift */; };
 		E00EEC0327368630002FF094 /* ServiceAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00EEBFD27368630002FF094 /* ServiceAssembly.swift */; };
 		E00EEC0427368630002FF094 /* SecurityAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00EEBFE27368630002FF094 /* SecurityAssembly.swift */; };
 		E00EEC0527368630002FF094 /* StorageAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = E00EEBFF27368630002FF094 /* StorageAssembly.swift */; };
@@ -900,6 +907,7 @@
 		F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F90692D2274B9A130037068D /* AppleHealthKitRootView.swift */; };
 		F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F90692D5274B9A450037068D /* HealthKitStateModel.swift */; };
 		FA630397F76B582C8D8681A7 /* BasalProfileEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42369F66CF91F30624C0B3A6 /* BasalProfileEditorProvider.swift */; };
+		FA6B58486AE2496F9F8589C6 /* QuickPickBolusesConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EEC5C2927FA4C5DA61A1A40 /* QuickPickBolusesConfigProvider.swift */; };
 		FE41E4D629463EE20047FD55 /* NightscoutPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */; };
 		FE66D16B291F74F8005D6F77 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */; };
 		FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFFA7A12929FE49007B8193 /* UIDevice+Extensions.swift */; };
@@ -1024,6 +1032,7 @@
 		118DF7652C5ECBC60067FEB7 /* CancelOverrideIntent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CancelOverrideIntent.swift; sourceTree = "<group>"; };
 		118DF7672C5ECBC60067FEB7 /* OverridePresetEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverridePresetEntity.swift; sourceTree = "<group>"; };
 		118DF7682C5ECBC60067FEB7 /* OverridePresetsIntentRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverridePresetsIntentRequest.swift; sourceTree = "<group>"; };
+		1526A587AD1544C495050662 /* QuickPickBolusesInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPickBolusesInfoView.swift; sourceTree = "<group>"; };
 		19012CDB291D2CB900FB8210 /* LoopStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopStats.swift; sourceTree = "<group>"; };
 		190EBCC329FF136900BA767D /* UserInterfaceSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsDataFlow.swift; sourceTree = "<group>"; };
 		190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsProvider.swift; sourceTree = "<group>"; };
@@ -1057,6 +1066,7 @@
 		19F95FF929F1102A00314DDC /* StatRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatRootView.swift; sourceTree = "<group>"; };
 		223EC0494F55A91E3EA69EF4 /* TreatmentsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TreatmentsStateModel.swift; sourceTree = "<group>"; };
 		2AD22C985B79A2F0D2EA3D9D /* PumpConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpConfigRootView.swift; sourceTree = "<group>"; };
+		2EEC5C2927FA4C5DA61A1A40 /* QuickPickBolusesConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPickBolusesConfigProvider.swift; sourceTree = "<group>"; };
 		2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigDataFlow.swift; sourceTree = "<group>"; };
 		36F58DDD71F0E795464FA3F0 /* TargetsEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorStateModel.swift; sourceTree = "<group>"; };
 		3811DE0725C9D32E00A708ED /* BaseView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseView.swift; sourceTree = "<group>"; };
@@ -1153,9 +1163,6 @@
 		38A5049125DD9C4000C5B9E8 /* UserDefaultsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtensions.swift; sourceTree = "<group>"; };
 		38A9260425F012D8009E3739 /* CarbRatios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbRatios.swift; sourceTree = "<group>"; };
 		38AAF85425FFF846004AF583 /* CurrentGlucoseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentGlucoseView.swift; sourceTree = "<group>"; };
-		BD11C000000000000000C001 /* CGMSensorDisplayState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMSensorDisplayState.swift; sourceTree = "<group>"; };
-		BD11C000000000000000C003 /* SensorLifecycleArcView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorLifecycleArcView.swift; sourceTree = "<group>"; };
-		BD11C000000000000000C004 /* SensorStatusTagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorStatusTagView.swift; sourceTree = "<group>"; };
 		38AEE73C25F0200C0013F05B /* TrioSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrioSettings.swift; sourceTree = "<group>"; };
 		38AEE75125F022080013F05B /* SettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = "<group>"; };
 		38AEE75625F0F18E0013F05B /* CarbsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbsStorage.swift; sourceTree = "<group>"; };
@@ -1452,8 +1459,10 @@
 		7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorDataFlow.swift; sourceTree = "<group>"; };
 		8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigRootView.swift; sourceTree = "<group>"; };
 		881E04BA5E0A003DE8E0A9C6 /* HistoryRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HistoryRootView.swift; sourceTree = "<group>"; };
+		8A54068ABDAE4898B243DF14 /* QuickPickBolusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPickBolusesView.swift; sourceTree = "<group>"; };
 		8A9134292D63D9A1007F8874 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
 		8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
+		91711E7CA54747EFAD6540EE /* QuickPickBolusesConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPickBolusesConfigStateModel.swift; sourceTree = "<group>"; };
 		920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorRootView.swift; sourceTree = "<group>"; };
 		9455FA2D92E77A6C4AFED8A3 /* HistoryStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HistoryStateModel.swift; sourceTree = "<group>"; };
 		96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalDataFlow.swift; sourceTree = "<group>"; };
@@ -1473,6 +1482,9 @@
 		BD04ECCD2D299522008C5FEB /* BolusProgressOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusProgressOverlay.swift; sourceTree = "<group>"; };
 		BD0B2EF22C5998E600B3298F /* MealPresetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealPresetView.swift; sourceTree = "<group>"; };
 		BD10516C2DA986DC007C6D89 /* PulsingLogoAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PulsingLogoAnimation.swift; sourceTree = "<group>"; };
+		BD11C000000000000000C001 /* CGMSensorDisplayState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMSensorDisplayState.swift; sourceTree = "<group>"; };
+		BD11C000000000000000C003 /* SensorLifecycleArcView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorLifecycleArcView.swift; sourceTree = "<group>"; };
+		BD11C000000000000000C004 /* SensorStatusTagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorStatusTagView.swift; sourceTree = "<group>"; };
 		BD1661302B82ADAB00256551 /* CustomProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomProgressView.swift; sourceTree = "<group>"; };
 		BD175EFE0000100000000001 /* HistoryDataFlow+Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HistoryDataFlow+Models.swift"; sourceTree = "<group>"; };
 		BD175EFE0000100000000002 /* HistoryDeletionTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryDeletionTarget.swift; sourceTree = "<group>"; };
@@ -1605,6 +1617,7 @@
 		C2BA6B982F758E7600348E6A /* NotificationIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationIdentifiers.swift; sourceTree = "<group>"; };
 		C377490C77661D75E8C50649 /* ManualTempBasalRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalRootView.swift; sourceTree = "<group>"; };
 		C8D1A7CA8C10C4403D4BBFA7 /* TreatmentsDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TreatmentsDataFlow.swift; sourceTree = "<group>"; };
+		CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeliveryLimitsSyncTests.swift; sourceTree = "<group>"; };
 		CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawFetchedProfile.swift; sourceTree = "<group>"; };
 		CC76E9502BD4812E008BEB61 /* Forecast+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+helper.swift"; sourceTree = "<group>"; };
 		CE1856F42ADC4858007E39C7 /* AddCarbPresetIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCarbPresetIntent.swift; sourceTree = "<group>"; };
@@ -1658,10 +1671,12 @@
 		CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationsDataFlow.swift; sourceTree = "<group>"; };
 		CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationService.swift; sourceTree = "<group>"; };
 		CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationsTests.swift; sourceTree = "<group>"; };
-		CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeliveryLimitsSyncTests.swift; sourceTree = "<group>"; };
 		CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMOptions.swift; sourceTree = "<group>"; };
+		CF96FC200503405E94334390 /* SlideToConfirmView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideToConfirmView.swift; sourceTree = "<group>"; };
 		CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalStateModel.swift; sourceTree = "<group>"; };
 		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
+		D543DE17E0054AF0821411BB /* QuickPickBolusesConfigDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPickBolusesConfigDataFlow.swift; sourceTree = "<group>"; };
+		DB3EBC8BA17C4627BD32C58F /* QuickPickBolusesConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickPickBolusesConfigRootView.swift; sourceTree = "<group>"; };
 		DD09D47A2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsDataFlow.swift; sourceTree = "<group>"; };
 		DD09D47C2C5986DA003FEA5D /* CalendarEventSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsProvider.swift; sourceTree = "<group>"; };
 		DD09D47E2C5986E5003FEA5D /* CalendarEventSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -1675,6 +1690,7 @@
 		DD1745122C54169400211FAC /* DevicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevicesView.swift; sourceTree = "<group>"; };
 		DD1745142C54388A00211FAC /* TherapySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TherapySettingsView.swift; sourceTree = "<group>"; };
 		DD1745162C54389F00211FAC /* FeatureSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureSettingsView.swift; sourceTree = "<group>"; };
+		AABB00012C54389F00211FAC /* TreatmentsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreatmentsSettingsView.swift; sourceTree = "<group>"; };
 		DD1745182C543B5700211FAC /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
 		DD17451C2C543C5F00211FAC /* ServicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
 		DD17451F2C55523E00211FAC /* SMBSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMBSettingsDataFlow.swift; sourceTree = "<group>"; };
@@ -2006,6 +2022,17 @@
 			path = ConfigEditor;
 			sourceTree = "<group>";
 		};
+		08A79EA89BFA498F80F4D6FE /* QuickPickBolusesConfig */ = {
+			isa = PBXGroup;
+			children = (
+				D543DE17E0054AF0821411BB /* QuickPickBolusesConfigDataFlow.swift */,
+				2EEC5C2927FA4C5DA61A1A40 /* QuickPickBolusesConfigProvider.swift */,
+				91711E7CA54747EFAD6540EE /* QuickPickBolusesConfigStateModel.swift */,
+				2148032F6F184487B49B1095 /* View */,
+			);
+			path = QuickPickBolusesConfig;
+			sourceTree = "<group>";
+		};
 		0D76BBC81CEDC1A0050F45EF /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -2203,6 +2230,14 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		2148032F6F184487B49B1095 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				DB3EBC8BA17C4627BD32C58F /* QuickPickBolusesConfigRootView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		34CA4DF169B53D67EF18ED8A /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -2249,6 +2284,7 @@
 				DD9ECB6B2CA99FA400AA7C45 /* RemoteControlConfig */,
 				3811DE3825C9D4A100A708ED /* Settings */,
 				110AEDEA2C51A0AE00615CC9 /* ShortcutsConfig */,
+				08A79EA89BFA498F80F4D6FE /* QuickPickBolusesConfig */,
 				DD17451E2C55520000211FAC /* SMBSettings */,
 				19F95FF129F10F9C00314DDC /* Stat */,
 				DD17452C2C55AE3500211FAC /* TargetBehavoir */,
@@ -2340,6 +2376,7 @@
 				3811DE2E25C9D49500A708ED /* HomeRootView.swift */,
 				3833B51E260264AC003021B3 /* Chart */,
 				3833B51F260264B6003021B3 /* Header */,
+				FDB1D1005DA54C048E622761 /* QuickPickBoluses */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -2643,16 +2680,6 @@
 			path = Header;
 			sourceTree = "<group>";
 		};
-		BD11C002000000000000C000 /* SensorLifecycle */ = {
-			isa = PBXGroup;
-			children = (
-				BD11C000000000000000C001 /* CGMSensorDisplayState.swift */,
-				BD11C000000000000000C003 /* SensorLifecycleArcView.swift */,
-				BD11C000000000000000C004 /* SensorStatusTagView.swift */,
-			);
-			path = SensorLifecycle;
-			sourceTree = "<group>";
-		};
 		3856933F270B57A00002C50D /* CGM */ = {
 			isa = PBXGroup;
 			children = (
@@ -3431,6 +3458,16 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		BD11C002000000000000C000 /* SensorLifecycle */ = {
+			isa = PBXGroup;
+			children = (
+				BD11C000000000000000C001 /* CGMSensorDisplayState.swift */,
+				BD11C000000000000000C003 /* SensorLifecycleArcView.swift */,
+				BD11C000000000000000C004 /* SensorStatusTagView.swift */,
+			);
+			path = SensorLifecycle;
+			sourceTree = "<group>";
+		};
 		BD175EC00000100000000001 /* HistoryStateModel+Deletion */ = {
 			isa = PBXGroup;
 			children = (
@@ -3874,6 +3911,7 @@
 				DD1745122C54169400211FAC /* DevicesView.swift */,
 				DD1745142C54388A00211FAC /* TherapySettingsView.swift */,
 				DD1745162C54389F00211FAC /* FeatureSettingsView.swift */,
+				AABB00012C54389F00211FAC /* TreatmentsSettingsView.swift */,
 				DD1745182C543B5700211FAC /* NotificationsView.swift */,
 				DD17451C2C543C5F00211FAC /* ServicesView.swift */,
 				DD1745362C55B74200211FAC /* AlgorithmSettings.swift */,
@@ -4519,6 +4557,16 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		FDB1D1005DA54C048E622761 /* QuickPickBoluses */ = {
+			isa = PBXGroup;
+			children = (
+				CF96FC200503405E94334390 /* SlideToConfirmView.swift */,
+				1526A587AD1544C495050662 /* QuickPickBolusesInfoView.swift */,
+				8A54068ABDAE4898B243DF14 /* QuickPickBolusesView.swift */,
+			);
+			path = QuickPickBoluses;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -5133,6 +5181,13 @@
 				3B2A3BC12E2B19C600658FB9 /* DynamicISF.swift in Sources */,
 				CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */,
 				3811DE3525C9D49500A708ED /* HomeRootView.swift in Sources */,
+				584C79A91D034426A1DE60CB /* SlideToConfirmView.swift in Sources */,
+				DECF1903237041E2BBBE4033 /* QuickPickBolusesInfoView.swift in Sources */,
+				3F23E18680094E6DA98628E4 /* QuickPickBolusesView.swift in Sources */,
+				786B6E8A8E294AE883581154 /* QuickPickBolusesConfigDataFlow.swift in Sources */,
+				FA6B58486AE2496F9F8589C6 /* QuickPickBolusesConfigProvider.swift in Sources */,
+				87F4D02B8D4D4D329BA291D2 /* QuickPickBolusesConfigStateModel.swift in Sources */,
+				C5BAAA36C643401D97A8CE11 /* QuickPickBolusesConfigRootView.swift in Sources */,
 				38E98A2925F52C9300C0CED0 /* Error+Extensions.swift in Sources */,
 				3E62C7822F54CC1B00433237 /* BolusDisplayThreshold.swift in Sources */,
 				38EA05DA261F6E7C0064E39B /* SimpleLogReporter.swift in Sources */,
@@ -5340,6 +5395,7 @@
 				CE7CA3512A064973004BE681 /* ApplyTempPresetIntent.swift in Sources */,
 				FA630397F76B582C8D8681A7 /* BasalProfileEditorProvider.swift in Sources */,
 				DD1745172C54389F00211FAC /* FeatureSettingsView.swift in Sources */,
+				AABB00022C54389F00211FAC /* TreatmentsSettingsView.swift in Sources */,
 				DD3D60312F0377350021A33B /* ExportSetting.swift in Sources */,
 				DDD7C8C12F4DB45400E5CF09 /* GlucoseStored+CoreDataClass.swift in Sources */,
 				DDD7C8C22F4DB45400E5CF09 /* GlucoseStored+CoreDataProperties.swift in Sources */,
@@ -5906,7 +5962,7 @@
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPER_TEAM = "$(DEVELOPER_TEAM)";
 				DEVELOPMENT_ASSET_PATHS = "";
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = Trio/Resources/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
@@ -5951,7 +6007,7 @@
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
 				DEVELOPER_TEAM = "$(DEVELOPER_TEAM)";
 				DEVELOPMENT_ASSET_PATHS = "";
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = Trio/Resources/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
@@ -5986,7 +6042,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				INFOPLIST_FILE = TrioTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -6007,7 +6063,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				INFOPLIST_FILE = TrioTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -6032,7 +6088,7 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -6066,7 +6122,7 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -6100,7 +6156,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -6137,7 +6193,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -6174,7 +6230,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_ASSET_PATHS = "\"Trio Watch App Extension/Preview Content\"";
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_PREVIEWS = YES;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -6211,7 +6267,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_ASSET_PATHS = "\"Trio Watch App Extension/Preview Content\"";
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_PREVIEWS = YES;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -6244,7 +6300,7 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -6270,7 +6326,7 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = NY777M2L59;
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;

+ 1 - 1
Trio/Sources/Helpers/Formatters.swift

@@ -100,7 +100,7 @@ extension Formatter {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
         formatter.locale = .current
-        formatter.minimumIntegerDigits = 0
+        formatter.minimumIntegerDigits = 1
         formatter.maximumFractionDigits = 2
         return formatter
     }()

+ 40 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -27241,6 +27241,9 @@
         }
       }
     },
+    "About Quick-Pick Boluses" : {
+      "comment" : "Info sheet title for quick-pick boluses feature"
+    },
     "About the Process" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -79166,6 +79169,9 @@
         }
       }
     },
+    "Could not authenticate" : {
+      "comment" : "Alert title when biometric auth fails for quick-pick boluses"
+    },
     "Could not calculate eventual glucose. Sensitivity: %@, Deviation: %@" : {
       "localizations" : {
         "en" : {
@@ -106741,6 +106747,9 @@
         }
       }
     },
+    "Enable Quick-Pick Boluses" : {
+      "comment" : "Toggle label for the quick-pick boluses feature setting"
+    },
     "Enable Reduced Bolus Option" : {
       "localizations" : {
         "bg" : {
@@ -118607,6 +118616,9 @@
         }
       }
     },
+    "Face ID or Touch ID did not succeed. The bolus was not enacted." : {
+      "comment" : "Alert body when biometric auth fails for quick-pick boluses"
+    },
     "Factory Calibration Parameters" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -132036,6 +132048,7 @@
       }
     },
     "Got it!" : {
+      "comment" : "Dismiss button for Quick-Pick Boluses info sheet",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -158526,6 +158539,9 @@
         }
       }
     },
+    "Long-press the + button on the home screen to enact a quick-pick bolus." : {
+      "comment" : "Mini hint for the enable quick-pick boluses toggle"
+    },
     "Loop at %@ failed." : {
       "localizations" : {
         "bg" : {
@@ -180620,6 +180636,9 @@
     "No profile; cannot determine basal." : {
 
     },
+    "No bolus history yet" : {
+      "comment" : "Alert title when no quick-pick boluses history exists"
+    },
     "No recent oref algorithm determination." : {
       "localizations" : {
         "bg" : {
@@ -205161,6 +205180,15 @@
         }
       }
     },
+    "Quick-Pick Boluses" : {
+      "comment" : "Title of the quick-pick boluses sheet"
+    },
+    "Quick-Pick Boluses learns from your manual boluses over time. Once you've delivered a few boluses, it will suggest amounts based on what you typically enact at this time of day." : {
+      "comment" : "Alert body explaining that quick-pick boluses history is empty"
+    },
+    "Quick-Pick Boluses looks at your manual boluses from the past 90 days and suggests the amounts you most commonly enact at this time of day.\n\nIt gives more weight to boluses from similar times of day, and treats weekdays and weekends separately. Older entries gradually count less.\n\nTap a suggestion to select it, then slide to confirm. Your normal Face ID or Touch ID approval always applies." : {
+      "comment" : "Info sheet body explaining how quick-pick boluses scoring works"
+    },
     "Raise target glucose when Autosens Ratio is less than 1." : {
       "localizations" : {
         "bg" : {
@@ -227880,6 +227908,12 @@
         }
       }
     },
+    "Slide to confirm your selected amount. Face ID or Touch ID is always required before the bolus is enacted." : {
+      "comment" : "Verbose hint for enable quick-pick boluses toggle, part 2"
+    },
+    "Slide to Enact Bolus" : {
+      "comment" : "Slide to confirm label for quick-pick boluses"
+    },
     "Slope" : {
       "comment" : "v",
       "localizations" : {
@@ -284741,6 +284775,9 @@
         }
       }
     },
+    "When enabled, long-pressing the + button on the home screen opens a Quick-Pick Boluses sheet. It suggests up to three bolus amounts based on your bolus history at similar times of day, weighted by recency and day type (weekday vs. weekend)." : {
+      "comment" : "Verbose hint for enable quick-pick boluses toggle, part 1"
+    },
     "When enabled, Super Micro Boluses (SMBs) will always be allowed if dosing calculations determine insulin is needed via the SMB delivery method, except when a high Temp Target is set." : {
       "localizations" : {
         "bg" : {
@@ -291529,6 +291566,9 @@
         }
       }
     },
+    "Your most-used bolus amounts at similar times on similar days. Tap one to pick it." : {
+      "comment" : "Subtitle of the quick-pick boluses pill row"
+    },
     "Your phone or app is not enabled for NFC communications, which is needed to pair to libre2 sensors" : {
       "extractionState" : "manual",
       "localizations" : {

+ 5 - 0
Trio/Sources/Models/TrioSettings.swift

@@ -62,6 +62,7 @@ struct TrioSettings: JSON, Equatable, Encodable {
     var sweetMealFactor: Decimal = 1
     var displayPresets: Bool = true
     var confirmBolus: Bool = false
+    var enableQuickBolus: Bool = false
     var useLiveActivity: Bool = false
     var lockScreenView: LockScreenView = .simple
     var smartStackView: LockScreenView = .simple
@@ -294,6 +295,10 @@ extension TrioSettings: Decodable {
             settings.confirmBolus = confirmBolus
         }
 
+        if let enableQuickBolus = try? container.decode(Bool.self, forKey: .enableQuickBolus) {
+            settings.enableQuickBolus = enableQuickBolus
+        }
+
         if let useLiveActivity = try? container.decode(Bool.self, forKey: .useLiveActivity) {
             settings.useLiveActivity = useLiveActivity
         }

+ 97 - 0
Trio/Sources/Modules/Home/HomeStateModel.swift

@@ -25,6 +25,7 @@ extension Home {
         @ObservationIgnored @Injected() var overrideStorage: OverrideStorage!
         @ObservationIgnored @Injected() var bluetoothManager: BluetoothStateManager!
         @ObservationIgnored @Injected() var iobService: IOBService!
+        @ObservationIgnored @Injected() var unlockmanager: UnlockManager!
 
         var cgmStateModel: CGMSettings.StateModel {
             CGMSettings.StateModel.shared
@@ -120,6 +121,8 @@ extension Home {
         var shouldRunDeleteOnSettingsChange = true
 
         var showCarbsRequiredBadge: Bool = true
+        var enableQuickBolus: Bool = false
+        var quickBolusHistory: [Decimal] = []
         private(set) var setupPumpType: PumpConfig.PumpType = .minimed
         var minForecast: [Int] = []
         var maxForecast: [Int] = []
@@ -478,6 +481,7 @@ extension Home {
             bolusDisplayThreshold = settingsManager.settings.bolusDisplayThreshold
             thresholdLines = settingsManager.settings.rulerMarks
             showCarbsRequiredBadge = settingsManager.settings.showCarbsRequiredBadge
+            enableQuickBolus = settingsManager.settings.enableQuickBolus
             forecastDisplayType = settingsManager.settings.forecastDisplayType
             isExerciseModeActive = settingsManager.preferences.exerciseMode
             highTTraisesSens = settingsManager.preferences.highTemptargetRaisesSensitivity
@@ -534,6 +538,98 @@ extension Home {
             }
         }
 
+        func loadQuickBolusSuggestions() async {
+            guard enableQuickBolus else { return }
+            let cutoff = Calendar.current.date(byAdding: .day, value: -90, to: Date()) ?? Date()
+            let predicate = NSPredicate(
+                format: "isSMB == false AND isExternal == false AND pumpEvent.timestamp >= %@",
+                cutoff as NSDate
+            )
+            do {
+                let results: Any = try await CoreDataStack.shared.fetchEntitiesAsync(
+                    ofType: BolusStored.self,
+                    onContext: pumpHistoryFetchContext,
+                    predicate: predicate,
+                    key: "pumpEvent.timestamp",
+                    ascending: false,
+                    batchSize: 100
+                )
+
+                let suggestions: [Decimal] = await pumpHistoryFetchContext.perform {
+                    guard let boluses = results as? [BolusStored] else { return [] }
+
+                    let now = Date()
+                    let cal = Calendar.current
+                    let nowMinute = cal.component(.hour, from: now) * 60 + cal.component(.minute, from: now)
+                    let nowDOW = cal.component(.weekday, from: now)
+                    let sigma: Double = 60.0
+                    let halfLife: Double = 10.0
+
+                    var groups: [Decimal: Double] = [:]
+                    for bolus in boluses {
+                        guard let nsAmount = bolus.amount, nsAmount.doubleValue > 0,
+                              let timestamp = bolus.pumpEvent?.timestamp else { continue }
+
+                        var roundedKey = Decimal()
+                        var tempAmount = nsAmount as Decimal
+                        NSDecimalRound(&roundedKey, &tempAmount, 2, .plain)
+
+                        let entryMinute = cal.component(.hour, from: timestamp) * 60 + cal.component(.minute, from: timestamp)
+                        let entryDOW = cal.component(.weekday, from: timestamp)
+
+                        let diff = abs(entryMinute - nowMinute)
+                        let circularDiff = Double(min(diff, 1440 - diff))
+                        let t = exp(-(circularDiff * circularDiff) / (2.0 * sigma * sigma))
+
+                        let d: Double
+                        if entryDOW == nowDOW {
+                            d = 1.0
+                        } else {
+                            let nowWeekend = nowDOW == 1 || nowDOW == 7
+                            let entryWeekend = entryDOW == 1 || entryDOW == 7
+                            d = nowWeekend == entryWeekend ? 0.7 : 0.15
+                        }
+
+                        let daysAgo = now.timeIntervalSince(timestamp) / 86400.0
+                        let r = pow(0.5, daysAgo / halfLife)
+
+                        groups[roundedKey, default: 0] += t * d * r
+                    }
+
+                    return groups
+                        .filter { $0.value >= 0.1 }
+                        .sorted { $0.value > $1.value }
+                        .prefix(5)
+                        .map(\.key)
+                }
+
+                await MainActor.run {
+                    quickBolusHistory = suggestions
+                }
+            } catch {
+                debug(.default, "\(DebuggingIdentifiers.failed) failed to fetch quick bolus history: \(error)")
+            }
+        }
+
+        func enactQuickBolus(amount: Decimal) async -> Bool {
+            guard amount > 0 else { return false }
+            let delivery = min(
+                Double(truncating: amount as NSDecimalNumber),
+                pumpInitialSettings.maxBolusUnits
+            )
+            do {
+                let authenticated = try await unlockmanager.unlock()
+                if authenticated {
+                    await apsManager.enactBolus(amount: delivery, isSMB: false, callback: nil)
+                    return true
+                }
+                return false
+            } catch {
+                debug(.bolusState, "Quick bolus authentication error: \(error)")
+                return false
+            }
+        }
+
         func addPump(_ type: PumpConfig.PumpType) {
             setupPumpType = type
             shouldDisplayPumpSetupSheet = true
@@ -804,6 +900,7 @@ extension Home.StateModel:
         thresholdLines = settingsManager.settings.rulerMarks
         bolusDisplayThreshold = settingsManager.settings.bolusDisplayThreshold
         showCarbsRequiredBadge = settingsManager.settings.showCarbsRequiredBadge
+        enableQuickBolus = settingsManager.settings.enableQuickBolus
         forecastDisplayType = settingsManager.settings.forecastDisplayType
         cgmAvailable = (fetchGlucoseManager.cgmGlucoseSourceType != CGMType.none)
         displayPumpStatusHighlightMessage()

+ 41 - 10
Trio/Sources/Modules/Home/View/HomeRootView.swift

@@ -32,6 +32,8 @@ extension Home {
         @State var isMenuPresented = false
         @State var showTreatments = false
         @State var selectedTab: Int = 0
+        @State var showQuickBolusPicker = false
+        @State var showQuickBolusNoHistory = false
         @State var showPumpSelection: Bool = false
         @State var showCGMSelection: Bool = false
         @State var showSnoozeSheet: Bool = false
@@ -1113,17 +1115,28 @@ extension Home {
                 }
                 .tint(Color.tabBar)
 
-                Button(
-                    action: {
-                        state.showModal(for: .treatmentView) },
-                    label: {
-                        Image(systemName: "plus.circle.fill")
-                            .font(.system(size: 40))
-                            .foregroundStyle(Color.tabBar)
-                            .padding(.vertical, 2)
-                            .padding(.horizontal, 24)
+                Image(systemName: "plus.circle.fill")
+                    .font(.system(size: 40))
+                    .foregroundStyle(Color.tabBar)
+                    .padding(.vertical, 2)
+                    .padding(.horizontal, 24)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        state.showModal(for: .treatmentView)
+                    }
+                    .onLongPressGesture(minimumDuration: 0.5) {
+                        guard state.enableQuickBolus else { return }
+                        let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
+                        impactHeavy.impactOccurred()
+                        Task {
+                            await state.loadQuickBolusSuggestions()
+                            if state.quickBolusHistory.isEmpty {
+                                showQuickBolusNoHistory = true
+                            } else {
+                                showQuickBolusPicker = true
+                            }
+                        }
                     }
-                )
             }.ignoresSafeArea(.keyboard, edges: .bottom).blur(radius: state.waitForSuggestion ? 8 : 0)
                 .onChange(of: selectedTab) {
                     if !settingsPath.isEmpty {
@@ -1140,6 +1153,24 @@ extension Home {
                     CustomProgressView(text: String(localized: "Updating IOB...", comment: "Progress text when updating IOB"))
                 }
             }
+            .sheet(isPresented: $showQuickBolusPicker) {
+                QuickPickBolusesView(
+                    suggestions: state.quickBolusHistory,
+                    onEnact: { amount in await state.enactQuickBolus(amount: amount) },
+                    isPresented: $showQuickBolusPicker
+                )
+            }
+            .alert(
+                String(localized: "No bolus history yet", comment: "Alert title when no quick-pick boluses history exists"),
+                isPresented: $showQuickBolusNoHistory
+            ) {
+                Button(String(localized: "OK"), role: .cancel) {}
+            } message: {
+                Text(String(
+                    localized: "Quick-Pick Boluses learns from your manual boluses over time. Once you've delivered a few boluses, it will suggest amounts based on what you typically enact at this time of day.",
+                    comment: "Alert body explaining that quick-pick boluses history is empty"
+                ))
+            }
         }
     }
 }

+ 35 - 0
Trio/Sources/Modules/Home/View/QuickPickBoluses/QuickPickBolusesInfoView.swift

@@ -0,0 +1,35 @@
+import SwiftUI
+
+struct QuickPickBolusesInfoView: View {
+    @Binding var isPresented: Bool
+
+    var body: some View {
+        NavigationStack {
+            ScrollView {
+                Text(String(
+                    localized: "Quick-Pick Boluses looks at your manual boluses from the past 90 days and suggests the amounts you most commonly enact at this time of day.\n\nIt gives more weight to boluses from similar times of day, and treats weekdays and weekends separately. Older entries gradually count less.\n\nTap a suggestion to select it, then slide to confirm. Your normal Face ID or Touch ID approval always applies.",
+                    comment: "Info sheet body explaining how quick-pick boluses scoring works"
+                ))
+                    .padding()
+            }
+            .navigationTitle(String(
+                localized: "About Quick-Pick Boluses",
+                comment: "Info sheet title for quick-pick boluses feature"
+            ))
+            .navigationBarTitleDisplayMode(.inline)
+
+            Button {
+                isPresented = false
+            } label: {
+                Text("Got it!", comment: "Dismiss button for Quick-Pick Boluses info sheet")
+                    .bold()
+                    .frame(maxWidth: .infinity, minHeight: 30, alignment: .center)
+            }
+            .buttonStyle(.bordered)
+            .padding([.horizontal, .bottom])
+            .padding(.top, 4)
+        }
+        .presentationDetents([.medium])
+        .presentationDragIndicator(.visible)
+    }
+}

+ 149 - 0
Trio/Sources/Modules/Home/View/QuickPickBoluses/QuickPickBolusesView.swift

@@ -0,0 +1,149 @@
+import SwiftUI
+
+struct QuickPickBolusesView: View {
+    let suggestions: [Decimal]
+    let onEnact: (Decimal) async -> Bool
+    @Binding var isPresented: Bool
+
+    @State private var selectedAmount: Decimal?
+    @State private var showInfo = false
+    @State private var isEnacting = false
+    @State private var showAuthFailedAlert = false
+
+    var body: some View {
+        let titleText = String(localized: "Quick-Pick Boluses", comment: "Title of the quick-pick boluses sheet")
+        NavigationStack {
+            HStack(alignment: .top) {
+                VStack(alignment: .leading) {
+                    pillRow
+                        .padding(.top)
+
+                    Text(
+                        "Your most-used bolus amounts at similar times on similar days. Tap one to pick it.",
+                        comment: "Subtitle of the quick-pick boluses pill row"
+                    )
+                    .font(.subheadline)
+                    .foregroundStyle(.secondary)
+                    .multilineTextAlignment(.leading)
+                    .padding()
+
+                    Spacer()
+                }
+            }.padding(.horizontal)
+                .frame(maxWidth: .infinity)
+                .safeAreaInset(edge: .bottom) {
+                    VStack {
+                        SlideToConfirmView(
+                            label: String(
+                                localized: "Slide to Enact Bolus",
+                                comment: "Slide to confirm label for quick-pick boluses"
+                            ),
+                            isEnabled: selectedAmount != nil && !isEnacting
+                        ) {
+                            guard let amount = selectedAmount, !isEnacting else { return }
+                            isEnacting = true
+                            Task {
+                                let success = await onEnact(amount)
+                                await MainActor.run {
+                                    if success {
+                                        isPresented = false
+                                    } else {
+                                        isEnacting = false
+                                        showAuthFailedAlert = true
+                                    }
+                                }
+                            }
+                        }.padding(.horizontal)
+                    }
+                    .padding(.horizontal)
+                }
+                .navigationTitle(titleText)
+                .navigationBarTitleDisplayMode(.inline)
+                .toolbar {
+                    ToolbarItem(placement: .principal) {
+                        Text(titleText)
+                            .font(.title3.bold())
+                    }
+                    ToolbarItem(placement: .topBarTrailing) {
+                        Button {
+                            showInfo = true
+                        } label: {
+                            Image(systemName: "questionmark.circle")
+                        }
+                    }
+                }
+                .sheet(isPresented: $showInfo) {
+                    QuickPickBolusesInfoView(isPresented: $showInfo)
+                }
+                .alert(
+                    String(
+                        localized: "Could not authenticate",
+                        comment: "Alert title when biometric auth fails for quick-pick boluses"
+                    ),
+                    isPresented: $showAuthFailedAlert
+                ) {
+                    Button(String(localized: "OK"), role: .cancel) {}
+                } message: {
+                    Text(String(
+                        localized: "Face ID or Touch ID did not succeed. The bolus was not enacted.",
+                        comment: "Alert body when biometric auth fails for quick-pick boluses"
+                    ))
+                }
+        }
+        .presentationDetents([.height(320)])
+        .presentationDragIndicator(.visible)
+        .interactiveDismissDisabled(isEnacting)
+    }
+
+    private var displayedSuggestions: [Decimal] {
+        var seen = Set<Decimal>()
+        return suggestions.prefix(3).filter { seen.insert($0).inserted }.sorted()
+    }
+
+    private var pillRow: some View {
+        let pills = displayedSuggestions
+        let isCompact = pills.count < 2
+        return HStack(spacing: 16) {
+            if isCompact { Spacer() }
+
+            ForEach(pills, id: \.self) { amount in
+                bolusAmountPill(amount)
+                    .frame(maxWidth: isCompact ? 160 : .infinity)
+            }
+
+            if isCompact { Spacer() }
+        }
+        .padding(.horizontal)
+    }
+
+    private func bolusAmountPill(_ amount: Decimal) -> some View {
+        let isSelected = selectedAmount == amount
+        let formatted = Formatter.bolusFormatter.string(from: amount as NSDecimalNumber) ?? amount.description
+
+        return Button {
+            selectedAmount = amount
+        } label: {
+            HStack(alignment: .firstTextBaseline, spacing: 2) {
+                Text(formatted)
+                    .font(.title2.bold())
+                Text("U")
+                    .font(.title2)
+                    .foregroundStyle(isSelected ? .white.opacity(0.85) : .secondary)
+            }
+            .padding(.vertical, 12)
+            .padding(.horizontal, 24)
+            .frame(maxWidth: .infinity, minHeight: 44)
+            .background(isSelected ? Color.accentColor : Color(.secondarySystemFill))
+            .foregroundStyle(isSelected ? .white : .primary)
+            .clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
+            .overlay {
+                if isSelected {
+                    RoundedRectangle(cornerRadius: 18, style: .continuous)
+                        .strokeBorder(Color.accentColor, lineWidth: 2.5)
+                }
+            }
+        }
+        .buttonStyle(.plain)
+        .animation(.easeInOut(duration: 0.15), value: isSelected)
+    }
+}

+ 58 - 0
Trio/Sources/Modules/Home/View/QuickPickBoluses/SlideToConfirmView.swift

@@ -0,0 +1,58 @@
+import SwiftUI
+
+struct SlideToConfirmView: View {
+    let label: String
+    let isEnabled: Bool
+    let action: () -> Void
+
+    @State private var dragOffset: CGFloat = 0
+
+    private let thumbSize: CGFloat = 52
+    private let trackHeight: CGFloat = 56
+    private let completionThreshold: CGFloat = 0.85
+
+    var body: some View {
+        GeometryReader { geo in
+            let maxDrag = geo.size.width - thumbSize - 8
+            let progress = maxDrag > 0 ? dragOffset / maxDrag : 0
+
+            ZStack(alignment: .leading) {
+                Capsule()
+                    .fill(isEnabled ? Color.accentColor.opacity(0.25) : Color.secondary.opacity(0.2))
+
+                Text(label)
+                    .font(.headline)
+                    .foregroundStyle(isEnabled ? .white.opacity(1 - progress) : .secondary)
+                    .frame(maxWidth: .infinity)
+
+                RoundedRectangle(cornerRadius: thumbSize / 2)
+                    .fill(isEnabled ? Color.accentColor : Color.secondary.opacity(0.4))
+                    .frame(width: thumbSize, height: thumbSize)
+                    .overlay {
+                        Image(systemName: "chevron.right.2")
+                            .foregroundStyle(.white)
+                            .font(.system(size: 18, weight: .semibold))
+                    }
+                    .offset(x: 4 + dragOffset)
+                    .gesture(
+                        isEnabled ? DragGesture()
+                            .onChanged { value in
+                                dragOffset = min(max(0, value.translation.width), maxDrag)
+                            }
+                            .onEnded { _ in
+                                guard maxDrag > 0 else { return }
+                                if dragOffset >= maxDrag * completionThreshold {
+                                    UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
+                                    withAnimation(.spring()) { dragOffset = 0 }
+                                    action()
+                                } else {
+                                    withAnimation(.spring()) { dragOffset = 0 }
+                                }
+                            } : nil
+                    )
+            }
+            .frame(height: trackHeight)
+        }
+        .frame(height: trackHeight)
+    }
+}

+ 5 - 0
Trio/Sources/Modules/QuickPickBolusesConfig/QuickPickBolusesConfigDataFlow.swift

@@ -0,0 +1,5 @@
+enum QuickPickBolusesConfig {
+    enum Config {}
+}
+
+protocol QuickPickBolusesConfigProvider {}

+ 3 - 0
Trio/Sources/Modules/QuickPickBolusesConfig/QuickPickBolusesConfigProvider.swift

@@ -0,0 +1,3 @@
+extension QuickPickBolusesConfig {
+    final class Provider: BaseProvider, QuickPickBolusesConfigProvider {}
+}

+ 19 - 0
Trio/Sources/Modules/QuickPickBolusesConfig/QuickPickBolusesConfigStateModel.swift

@@ -0,0 +1,19 @@
+import SwiftUI
+
+extension QuickPickBolusesConfig {
+    final class StateModel: BaseStateModel<Provider> {
+        @Published var enableQuickBolus: Bool = false
+        @Published var units: GlucoseUnits = .mgdL
+
+        override func subscribe() {
+            subscribeSetting(\.enableQuickBolus, on: $enableQuickBolus) { enableQuickBolus = $0 }
+            units = settingsManager.settings.units
+        }
+    }
+}
+
+extension QuickPickBolusesConfig.StateModel: SettingsObserver {
+    func settingsDidChange(_ settings: TrioSettings) {
+        units = settings.units
+    }
+}

+ 64 - 0
Trio/Sources/Modules/QuickPickBolusesConfig/View/QuickPickBolusesConfigRootView.swift

@@ -0,0 +1,64 @@
+import SwiftUI
+import Swinject
+
+extension QuickPickBolusesConfig {
+    struct RootView: BaseView {
+        let resolver: Resolver
+
+        @StateObject var state = StateModel()
+
+        @State private var shouldDisplayHint: Bool = false
+        @State var hintDetent = PresentationDetent.large
+        @State var selectedVerboseHint: AnyView?
+        @State var hintLabel: String?
+        @State private var decimalPlaceholder: Decimal = 0.0
+
+        @Environment(\.colorScheme) var colorScheme
+        @Environment(AppState.self) var appState
+
+        var body: some View {
+            List {
+                SettingInputSection(
+                    decimalValue: $decimalPlaceholder,
+                    booleanValue: $state.enableQuickBolus,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    selectedVerboseHint: Binding(
+                        get: { selectedVerboseHint },
+                        set: {
+                            selectedVerboseHint = $0.map { AnyView($0) }
+                            hintLabel = String(localized: "Enable Quick-Pick Boluses")
+                        }
+                    ),
+                    units: state.units,
+                    type: .boolean,
+                    label: String(localized: "Enable Quick-Pick Boluses"),
+                    miniHint: String(localized: "Long-press the + button on the home screen to enact a quick-pick bolus."),
+                    verboseHint: VStack(alignment: .leading, spacing: 10) {
+                        Text("Default: OFF").bold()
+                        Text(
+                            "When enabled, long-pressing the + button on the home screen opens a Quick-Pick Boluses sheet. It suggests up to three bolus amounts based on your bolus history at similar times of day, weighted by recency and day type (weekday vs. weekend)."
+                        )
+                        Text(
+                            "Slide to confirm your selected amount. Face ID or Touch ID is always required before the bolus is enacted."
+                        )
+                    }
+                )
+            }
+            .listSectionSpacing(sectionSpacing)
+            .sheet(isPresented: $shouldDisplayHint) {
+                SettingInputHintView(
+                    hintDetent: $hintDetent,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    hintLabel: hintLabel ?? "",
+                    hintText: selectedVerboseHint ?? AnyView(EmptyView()),
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
+                )
+            }
+            .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
+            .onAppear(perform: configureView)
+            .navigationBarTitle("Quick-Pick Boluses")
+            .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
+        }
+    }
+}

+ 8 - 2
Trio/Sources/Modules/Settings/SettingItems.swift

@@ -223,7 +223,13 @@ enum SettingItems {
                 "Enable Super Bolus": "Enable Super Bolus Option",
                 "Super Bolus Factor": "Enable Super Bolus Option"
             ],
-            path: ["Features", "Bolus Calculator"]
+            path: ["Features", "Treatments", "Bolus Calculator"]
+        ),
+        SettingItem(
+            title: "Quick-Pick Boluses",
+            view: .quickBolusConfig,
+            searchContents: ["Enable Quick-Pick Boluses"],
+            path: ["Features", "Treatments", "Quick-Pick Boluses"]
         ),
         SettingItem(
             title: "Meal Settings",
@@ -247,7 +253,7 @@ enum SettingItems {
                 "Fat and Protein Percentage": "Enable Fat and Protein Entries",
                 "FPU": "Enable Fat and Protein Entries"
             ],
-            path: ["Features", "Meal Settings"]
+            path: ["Features", "Treatments", "Meal Settings"]
         ),
         SettingItem(
             title: "Shortcuts",

+ 1 - 2
Trio/Sources/Modules/Settings/View/Subviews/FeatureSettingsView.swift

@@ -21,8 +21,7 @@ struct FeatureSettingsView: BaseView {
             Section(
                 header: Text("Trio Features"),
                 content: {
-                    Text("Bolus Calculator").navigationLink(to: .bolusCalculatorConfig, from: self)
-                    Text("Meal Settings").navigationLink(to: .mealSettings, from: self)
+                    Text("Treatments").navigationLink(to: .treatmentsSettings, from: self)
                     Text("Shortcuts").navigationLink(to: .shortcutsConfig, from: self)
                     Text("Remote Control").navigationLink(to: .remoteControlConfig, from: self)
                 }

+ 27 - 0
Trio/Sources/Modules/Settings/View/Subviews/TreatmentsSettingsView.swift

@@ -0,0 +1,27 @@
+import Foundation
+import SwiftUI
+import Swinject
+
+struct TreatmentsSettingsView: BaseView {
+    let resolver: Resolver
+
+    @ObservedObject var state: Settings.StateModel
+
+    @Environment(\.colorScheme) var colorScheme
+    @Environment(AppState.self) var appState
+
+    var body: some View {
+        Form {
+            Section {
+                Text("Bolus Calculator").navigationLink(to: .bolusCalculatorConfig, from: self)
+                Text("Quick-Pick Boluses").navigationLink(to: .quickBolusConfig, from: self)
+                Text("Meal Settings").navigationLink(to: .mealSettings, from: self)
+            }
+            .listRowBackground(Color.chart)
+        }
+        .scrollContentBackground(.hidden)
+        .background(appState.trioBackgroundColor(for: colorScheme))
+        .navigationTitle("Treatments")
+        .navigationBarTitleDisplayMode(.automatic)
+    }
+}

+ 23 - 20
Trio/Sources/Modules/SettingsExport/SettingsExportStateModel.swift

@@ -606,31 +606,31 @@ extension SettingsExport {
             if categoriesToExport.contains(.features) {
                 let featuresCategory = String(localized: "Features", comment: "Features menu item in the Settings main view.")
 
-                // Trio Features subcategory - Bolus Calculator
-                let bolusCalculatorSubcategory = String(localized: "Bolus Calculator")
+                // Trio Features subcategory - Treatments (Bolus Calculator, Quick-Pick Boluses, Meal Settings)
+                let treatmentsSubcategory = String(localized: "Treatments")
                 addSetting(
                     category: featuresCategory,
-                    subcategory: bolusCalculatorSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Display Meal Presets"),
                     value: trioSettings.displayPresets ? String(localized: "Enabled") : String(localized: "Disabled")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: bolusCalculatorSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Recommended Bolus Percentage"),
                     value: String(format: "%.0f", (trioSettings.overrideFactor as NSDecimalNumber).doubleValue * 100),
                     unit: "%"
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: bolusCalculatorSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Enable Reduced Bolus Option"),
                     value: trioSettings.fattyMeals ? String(localized: "Enabled") : String(localized: "Disabled")
                 )
                 if trioSettings.fattyMeals {
                     addSetting(
                         category: featuresCategory,
-                        subcategory: bolusCalculatorSubcategory,
+                        subcategory: treatmentsSubcategory,
                         name: String(localized: "Reduced Bolus Percentage"),
                         value: String(format: "%.0f", (trioSettings.fattyMealFactor as NSDecimalNumber).doubleValue * 100),
                         unit: "%"
@@ -638,14 +638,14 @@ extension SettingsExport {
                 }
                 addSetting(
                     category: featuresCategory,
-                    subcategory: bolusCalculatorSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Enable Super Bolus Option"),
                     value: trioSettings.sweetMeals ? String(localized: "Enabled") : String(localized: "Disabled")
                 )
                 if trioSettings.sweetMeals {
                     addSetting(
                         category: featuresCategory,
-                        subcategory: bolusCalculatorSubcategory,
+                        subcategory: treatmentsSubcategory,
                         name: String(localized: "Super Bolus Percentage"),
                         value: String(format: "%.0f", (trioSettings.sweetMealFactor as NSDecimalNumber).doubleValue * 100),
                         unit: "%"
@@ -653,64 +653,67 @@ extension SettingsExport {
                 }
                 addSetting(
                     category: featuresCategory,
-                    subcategory: bolusCalculatorSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Very Low Glucose Warning"),
                     value: trioSettings.confirmBolus ? String(localized: "Enabled") : String(localized: "Disabled")
                 )
-
-                // Trio Features subcategory - Meal Settings
-                let mealSettingsSubcategory = String(localized: "Meal Settings")
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
+                    name: String(localized: "Enable Quick-Pick Boluses"),
+                    value: trioSettings.enableQuickBolus ? String(localized: "Enabled") : String(localized: "Disabled")
+                )
+                addSetting(
+                    category: featuresCategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Max Carbs"),
                     value: String(describing: trioSettings.maxCarbs),
                     unit: String(localized: "g", comment: "Units for carbs")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Max Fat"),
                     value: String(describing: trioSettings.maxFat),
                     unit: String(localized: "g", comment: "Units for carbs")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Max Protein"),
                     value: String(describing: trioSettings.maxProtein),
                     unit: String(localized: "g", comment: "Units for carbs")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Max Meal Absorption Time"),
                     value: String(describing: preferences.maxMealAbsorptionTime),
                     unit: String(localized: "hours")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Enable Fat and Protein Entries"),
                     value: trioSettings.useFPUconversion ? String(localized: "Enabled") : String(localized: "Disabled")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Fat and Protein Delay"),
                     value: String(describing: trioSettings.delay),
                     unit: String(localized: "minutes")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Spread Interval"),
                     value: String(describing: trioSettings.minuteInterval),
                     unit: String(localized: "minutes")
                 )
                 addSetting(
                     category: featuresCategory,
-                    subcategory: mealSettingsSubcategory,
+                    subcategory: treatmentsSubcategory,
                     name: String(localized: "Fat and Protein Percentage"),
                     value: String(format: "%.0f", (trioSettings.individualAdjustmentFactor as NSDecimalNumber).doubleValue * 100),
                     unit: "%"

+ 6 - 0
Trio/Sources/Router/Screen.swift

@@ -30,6 +30,7 @@ enum Screen: Identifiable, Hashable {
     case watch
     case userInterfaceSettings
     case bolusCalculatorConfig
+    case quickBolusConfig
     case dynamicISF
     case calibrations
     case shortcutsConfig
@@ -51,6 +52,7 @@ enum Screen: Identifiable, Hashable {
     case unitsAndLimits
     case appDiagnostics
     case settingsExport
+    case treatmentsSettings
 
     var id: Int { String(reflecting: self).hashValue }
 }
@@ -126,6 +128,8 @@ extension Screen {
             UserInterfaceSettings.RootView(resolver: resolver)
         case .bolusCalculatorConfig:
             BolusCalculatorConfig.RootView(resolver: resolver)
+        case .quickBolusConfig:
+            QuickPickBolusesConfig.RootView(resolver: resolver)
         case .dynamicISF:
             DynamicSettings.RootView(resolver: resolver)
         case .calibrations:
@@ -168,6 +172,8 @@ extension Screen {
             AppDiagnostics.RootView(resolver: resolver)
         case .settingsExport:
             SettingsExport.RootView(resolver: resolver)
+        case .treatmentsSettings:
+            TreatmentsSettingsView(resolver: resolver, state: Settings.StateModel())
         }
     }