Explorar o código

Refactor therapy settings

Deniz Cengiz hai 1 ano
pai
achega
83a9c646dd

+ 27 - 19
FreeAPS.xcodeproj/project.pbxproj

@@ -17,7 +17,7 @@
 		110AEDEC2C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE72C51A0AE00615CC9 /* ShortcutsConfigDataFlow.swift */; };
 		110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE82C51A0AE00615CC9 /* ShortcutsConfigProvider.swift */; };
 		110AEDEE2C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 110AEDE92C51A0AE00615CC9 /* ShortcutsConfigStateModel.swift */; };
-		17A9D0899046B45E87834820 /* CREditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8D5F457B5AFF763F8CF3DF /* CREditorProvider.swift */; };
+		17A9D0899046B45E87834820 /* CarbRatioEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8D5F457B5AFF763F8CF3DF /* CarbRatioEditorProvider.swift */; };
 		19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19012CDB291D2CB900FB8210 /* LoopStats.swift */; };
 		190EBCC429FF136900BA767D /* UserInterfaceSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC329FF136900BA767D /* UserInterfaceSettingsDataFlow.swift */; };
 		190EBCC629FF138000BA767D /* UserInterfaceSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */; };
@@ -263,7 +263,7 @@
 		65070A332BFDCB83006F213F /* TidepoolStartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65070A322BFDCB83006F213F /* TidepoolStartView.swift */; };
 		6632A0DC746872439A858B44 /* ISFEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BDA519C9B890FD9A5DFCF3 /* ISFEditorDataFlow.swift */; };
 		69A31254F2451C20361D172F /* BolusStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 223EC0494F55A91E3EA69EF4 /* BolusStateModel.swift */; };
-		69B9A368029F7EB39F525422 /* CREditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA5E04A2761F6EEA6568E1 /* CREditorStateModel.swift */; };
+		69B9A368029F7EB39F525422 /* CarbRatioEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA5E04A2761F6EEA6568E1 /* CarbRatioEditorStateModel.swift */; };
 		6B1A8D192B14D91600E76752 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B1A8D182B14D91600E76752 /* WidgetKit.framework */; };
 		6B1A8D1B2B14D91600E76752 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B1A8D1A2B14D91600E76752 /* SwiftUI.framework */; };
 		6B1A8D1E2B14D91600E76752 /* LiveActivityBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B1A8D1D2B14D91600E76752 /* LiveActivityBundle.swift */; };
@@ -287,11 +287,11 @@
 		8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */; };
 		9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */; };
 		9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */; };
-		98641AF4F92123DA668AB931 /* CREditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BDC6993C1087310EDFC428 /* CREditorRootView.swift */; };
+		98641AF4F92123DA668AB931 /* CarbRatioEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */; };
 		A05235B9112E677ED03B6E8E /* AutotuneConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CF5ACEE1F0859670E71B2C0 /* AutotuneConfigRootView.swift */; };
 		A0B8EC8CC5CD1DD237D1BCD2 /* PumpSettingsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C7F882606FF83A21BE00D8 /* PumpSettingsEditorRootView.swift */; };
 		A228DF96647338139F152B15 /* PreferencesEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12204445D7632AF09264A979 /* PreferencesEditorDataFlow.swift */; };
-		A33352ED40476125EBAC6EE0 /* CREditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E22146D3DF4853786C78132 /* CREditorDataFlow.swift */; };
+		A33352ED40476125EBAC6EE0 /* CarbRatioEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */; };
 		AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */; };
 		B7C465E9472624D8A2BE2A6A /* (null) in Sources */ = {isa = PBXBuildFile; };
 		B958F1B72BA0711600484851 /* MKRingProgressView in Frameworks */ = {isa = PBXBuildFile; productRef = B958F1B62BA0711600484851 /* MKRingProgressView */; };
@@ -466,6 +466,8 @@
 		DDF847DF2C5C28780049BB3B /* LiveActivitySettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */; };
 		DDF847E12C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */; };
 		DDF847E42C5C288F0049BB3B /* LiveActivitySettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E32C5C288F0049BB3B /* LiveActivitySettingsRootView.swift */; };
+		DDF847E82C5DABA30049BB3B /* WatchConfigAppleWatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E72C5DABA30049BB3B /* WatchConfigAppleWatchView.swift */; };
+		DDF847EA2C5DABAC0049BB3B /* WatchConfigGarminView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E92C5DABAC0049BB3B /* WatchConfigGarminView.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 */; };
@@ -893,7 +895,7 @@
 		5C018D1680307A31C9ED7120 /* CGMStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMStateModel.swift; sourceTree = "<group>"; };
 		5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorStateModel.swift; sourceTree = "<group>"; };
 		60744C3E9BB3652895C908CC /* DataTableProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableProvider.swift; sourceTree = "<group>"; };
-		64AA5E04A2761F6EEA6568E1 /* CREditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CREditorStateModel.swift; sourceTree = "<group>"; };
+		64AA5E04A2761F6EEA6568E1 /* CarbRatioEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorStateModel.swift; sourceTree = "<group>"; };
 		65070A322BFDCB83006F213F /* TidepoolStartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TidepoolStartView.swift; sourceTree = "<group>"; };
 		67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorDataFlow.swift; sourceTree = "<group>"; };
 		680C4420C9A345D46D90D06C /* ManualTempBasalProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalProvider.swift; sourceTree = "<group>"; };
@@ -910,7 +912,7 @@
 		6F8BA8533F56BC55748CA877 /* PreferencesEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PreferencesEditorProvider.swift; sourceTree = "<group>"; };
 		72778B68C3004F71F6E79BDC /* PumpSettingsEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpSettingsEditorStateModel.swift; sourceTree = "<group>"; };
 		79BDA519C9B890FD9A5DFCF3 /* ISFEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorDataFlow.swift; sourceTree = "<group>"; };
-		7E22146D3DF4853786C78132 /* CREditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CREditorDataFlow.swift; sourceTree = "<group>"; };
+		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 /* DataTableRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableRootView.swift; sourceTree = "<group>"; };
 		8A965332F237348B119FB858 /* PreferencesEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PreferencesEditorRootView.swift; sourceTree = "<group>"; };
@@ -919,7 +921,7 @@
 		920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorRootView.swift; sourceTree = "<group>"; };
 		9455FA2D92E77A6C4AFED8A3 /* DataTableStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableStateModel.swift; sourceTree = "<group>"; };
 		96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalDataFlow.swift; sourceTree = "<group>"; };
-		9C8D5F457B5AFF763F8CF3DF /* CREditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CREditorProvider.swift; sourceTree = "<group>"; };
+		9C8D5F457B5AFF763F8CF3DF /* CarbRatioEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorProvider.swift; sourceTree = "<group>"; };
 		9F9F137F126D9F8DEB799F26 /* ISFEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorProvider.swift; sourceTree = "<group>"; };
 		A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigStateModel.swift; sourceTree = "<group>"; };
 		A401509D21F7F35D4E109EDA /* DataTableDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableDataFlow.swift; sourceTree = "<group>"; };
@@ -1019,7 +1021,7 @@
 		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>"; };
 		CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalStateModel.swift; sourceTree = "<group>"; };
-		D0BDC6993C1087310EDFC428 /* CREditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CREditorRootView.swift; sourceTree = "<group>"; };
+		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
 		D295A3F870E826BE371C0BB5 /* AutotuneConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AutotuneConfigStateModel.swift; sourceTree = "<group>"; };
 		D97F14812C1AFED3621165A5 /* PumpSettingsEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpSettingsEditorProvider.swift; sourceTree = "<group>"; };
 		DC2C6489D29ECCCAD78E0721 /* GlucoseNotificationSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -1103,6 +1105,8 @@
 		DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsProvider.swift; sourceTree = "<group>"; };
 		DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsStateModel.swift; sourceTree = "<group>"; };
 		DDF847E32C5C288F0049BB3B /* LiveActivitySettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsRootView.swift; sourceTree = "<group>"; };
+		DDF847E72C5DABA30049BB3B /* WatchConfigAppleWatchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConfigAppleWatchView.swift; sourceTree = "<group>"; };
+		DDF847E92C5DABAC0049BB3B /* WatchConfigGarminView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConfigGarminView.swift; sourceTree = "<group>"; };
 		E00EEBFD27368630002FF094 /* ServiceAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceAssembly.swift; sourceTree = "<group>"; };
 		E00EEBFE27368630002FF094 /* SecurityAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurityAssembly.swift; sourceTree = "<group>"; };
 		E00EEBFF27368630002FF094 /* StorageAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageAssembly.swift; sourceTree = "<group>"; };
@@ -1410,7 +1414,7 @@
 				CEE9A64D2BBB411C00EB5194 /* Calibrations */,
 				F75CB57ED6971B46F8756083 /* CGM */,
 				0610F7D6D2EC00E3BA1569F0 /* ConfigEditor */,
-				E42231DBF0DBE2B4B92D1B15 /* CREditor */,
+				E42231DBF0DBE2B4B92D1B15 /* CarbRatioEditor */,
 				9E56E3626FAD933385101B76 /* DataTable */,
 				195D80B22AF696EE00D25097 /* DynamicSettings */,
 				DD17454C2C55CA0200211FAC /* GeneralSettings */,
@@ -2160,7 +2164,7 @@
 		54946647FDCFE43028F60511 /* View */ = {
 			isa = PBXGroup;
 			children = (
-				D0BDC6993C1087310EDFC428 /* CREditorRootView.swift */,
+				D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -2414,6 +2418,8 @@
 			isa = PBXGroup;
 			children = (
 				CE94598629E9E4110047C9C6 /* WatchConfigRootView.swift */,
+				DDF847E72C5DABA30049BB3B /* WatchConfigAppleWatchView.swift */,
+				DDF847E92C5DABAC0049BB3B /* WatchConfigGarminView.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -2697,15 +2703,15 @@
 			path = Assemblies;
 			sourceTree = "<group>";
 		};
-		E42231DBF0DBE2B4B92D1B15 /* CREditor */ = {
+		E42231DBF0DBE2B4B92D1B15 /* CarbRatioEditor */ = {
 			isa = PBXGroup;
 			children = (
-				7E22146D3DF4853786C78132 /* CREditorDataFlow.swift */,
-				9C8D5F457B5AFF763F8CF3DF /* CREditorProvider.swift */,
-				64AA5E04A2761F6EEA6568E1 /* CREditorStateModel.swift */,
+				7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */,
+				9C8D5F457B5AFF763F8CF3DF /* CarbRatioEditorProvider.swift */,
+				64AA5E04A2761F6EEA6568E1 /* CarbRatioEditorStateModel.swift */,
 				54946647FDCFE43028F60511 /* View */,
 			);
-			path = CREditor;
+			path = CarbRatioEditor;
 			sourceTree = "<group>";
 		};
 		E493126EA71765130F64CCE5 /* PumpSettingsEditor */ = {
@@ -3146,6 +3152,7 @@
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
 				5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
+				DDF847E82C5DABA30049BB3B /* WatchConfigAppleWatchView.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */,
 				38A0364225ED069400FCBB52 /* TempBasal.swift in Sources */,
@@ -3308,6 +3315,7 @@
 				38BF021B25E7D06400579895 /* PumpSettingsView.swift in Sources */,
 				3811DEEA25CA063400A708ED /* SyncAccess.swift in Sources */,
 				190EBCC829FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift in Sources */,
+				DDF847EA2C5DABAC0049BB3B /* WatchConfigGarminView.swift in Sources */,
 				38BF021F25E7F0DE00579895 /* DeviceDataManager.swift in Sources */,
 				BDB3C1192C03DD1000CEEAA1 /* UserDefaultsExtension.swift in Sources */,
 				38A504A425DD9C4000C5B9E8 /* UserDefaultsExtensions.swift in Sources */,
@@ -3420,11 +3428,11 @@
 				F816825E28DB441200054060 /* HeartBeatManager.swift in Sources */,
 				58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */,
 				38FEF413273B317A00574A46 /* HKUnit.swift in Sources */,
-				A33352ED40476125EBAC6EE0 /* CREditorDataFlow.swift in Sources */,
-				17A9D0899046B45E87834820 /* CREditorProvider.swift in Sources */,
-				69B9A368029F7EB39F525422 /* CREditorStateModel.swift in Sources */,
+				A33352ED40476125EBAC6EE0 /* CarbRatioEditorDataFlow.swift in Sources */,
+				17A9D0899046B45E87834820 /* CarbRatioEditorProvider.swift in Sources */,
+				69B9A368029F7EB39F525422 /* CarbRatioEditorStateModel.swift in Sources */,
 				38E44538274E411700EC9A94 /* Disk+[Data].swift in Sources */,
-				98641AF4F92123DA668AB931 /* CREditorRootView.swift in Sources */,
+				98641AF4F92123DA668AB931 /* CarbRatioEditorRootView.swift in Sources */,
 				BDF34F902C10CF8C00D51995 /* CoreDataStack.swift in Sources */,
 				CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */,
 				110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */,

+ 0 - 35
FreeAPS/Sources/APS/CGM/CGMType.swift

@@ -5,12 +5,7 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
     case none
     case nightscout
     case xdrip
-//    case dexcomG5
-//    case dexcomG6
-//    case dexcomG7
     case simulator
-//    case libreTransmitter
-    case glucoseDirect
     case enlite
     case plugin
 
@@ -22,18 +17,8 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
             return "Nightscout"
         case .xdrip:
             return "xDrip4iOS"
-        case .glucoseDirect:
-            return "Glucose Direct"
-//        case .dexcomG5:
-//            return "Dexcom G5"
-//        case .dexcomG6:
-//            return "Dexcom G6"
-//        case .dexcomG7:
-//            return "Dexcom G7"
         case .simulator:
             return NSLocalizedString("Glucose Simulator", comment: "Glucose Simulator CGM type")
-//        case .libreTransmitter:
-//            return NSLocalizedString("Libre Transmitter", comment: "Libre Transmitter type")
         case .enlite:
             return "Medtronic Enlite"
         case .plugin:
@@ -49,8 +34,6 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
             return nil
         case .xdrip:
             return URL(string: "xdripswift://")!
-        case .glucoseDirect:
-            return URL(string: "libredirect://")!
         case .simulator:
             return nil
         case .plugin:
@@ -62,8 +45,6 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
         switch self {
         case .xdrip:
             return URL(string: "https://github.com/JohanDegraeve/xdripswift")!
-        case .glucoseDirect:
-            return URL(string: "https://github.com/creepymonster/GlucoseDirectApp")!
         default: return nil
         }
     }
@@ -79,24 +60,8 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
                 "Using shared app group with external CGM app xDrip4iOS",
                 comment: "Shared app group xDrip4iOS"
             )
-//        case .dexcomG5:
-//            return NSLocalizedString("Native G5 app", comment: "Native G5 app")
-//        case .dexcomG6:
-//            return NSLocalizedString("Dexcom G6 app", comment: "Dexcom G6 app")
-//        case .dexcomG7:
-//            return NSLocalizedString("Dexcom G7 app", comment: "Dexcom G76 app")
         case .simulator:
             return NSLocalizedString("Simple simulator", comment: "Simple simulator")
-//        case .libreTransmitter:
-//            return NSLocalizedString(
-//                "Direct connection with Libre 1 transmitters or European Libre 2 sensors",
-//                comment: "Direct connection with Libre 1 transmitters or European Libre 2 sensors"
-//            )
-        case .glucoseDirect:
-            return NSLocalizedString(
-                "Using shared app group with external CGM app GlucoseDirect",
-                comment: "Shared app group GlucoseDirect"
-            )
         case .enlite:
             return NSLocalizedString("Minilink transmitter", comment: "Minilink transmitter")
         case .plugin:

+ 0 - 2
FreeAPS/Sources/APS/FetchGlucoseManager.swift

@@ -134,8 +134,6 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
             glucoseSource = nightscoutManager
         case .simulator:
             glucoseSource = simulatorSource
-        case .glucoseDirect:
-            glucoseSource = AppGroupSource(from: "GlucoseDirect", cgmType: .glucoseDirect)
         case .enlite:
             glucoseSource = deviceDataManager
         case .plugin:

+ 51 - 44
FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -42,8 +42,8 @@ extension BasalProfileEditor {
             Form {
                 Section(header: Text("Schedule")) {
                     list
-                    addButton
-                }
+                }.listRowBackground(Color.chart)
+
                 Section {
                     HStack {
                         Text("Total")
@@ -55,27 +55,38 @@ extension BasalProfileEditor {
                             Text(" U/day")
                             .foregroundColor(.secondary)
                     }
-                }
+                }.listRowBackground(Color.chart)
+
                 Section {
                     HStack {
                         if state.syncInProgress {
                             ProgressView().padding(.trailing, 10)
                         }
-                        Button { state.save() }
-                        label: {
-                            Text(state.syncInProgress ? "Saving..." : "Save on Pump")
+                        Button {
+                            let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
+                            impactHeavy.impactOccurred()
+                            state.save()
+                        } label: {
+                            Text(state.syncInProgress ? "Saving..." : "Save")
                         }
                         .disabled(state.syncInProgress || state.items.isEmpty)
+                        .frame(maxWidth: .infinity, alignment: .center)
+                        .tint(.white)
                     }
-                }
+                }.listRowBackground(state.syncInProgress || state.items.isEmpty ? Color(.systemGray4) : Color(.systemBlue))
             }
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
             .navigationTitle("Basal Profile")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(
-                trailing: EditButton()
-            )
+            .toolbar(content: {
+                ToolbarItem(placement: .topBarTrailing) {
+                    EditButton()
+                }
+                ToolbarItem(placement: .topBarTrailing) {
+                    addButton
+                }
+            })
             .environment(\.editMode, $editMode)
             .onAppear {
                 state.validate()
@@ -83,44 +94,40 @@ extension BasalProfileEditor {
         }
 
         private func pickers(for index: Int) -> some View {
-            GeometryReader { geometry in
-                VStack {
-                    HStack {
-                        Text("Rate").frame(width: geometry.size.width / 2)
-                        Text("Time").frame(width: geometry.size.width / 2)
-                    }
-                    HStack(spacing: 0) {
-                        Picker(selection: $state.items[index].rateIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
-                                Text(
-                                    (
-                                        self.rateFormatter
-                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
-                                    ) + " U/hr"
-                                ).tag(i)
-                            }
+            Form {
+                Section {
+                    Picker(selection: $state.items[index].rateIndex, label: Text("Rate")) {
+                        ForEach(0 ..< state.rateValues.count, id: \.self) { i in
+                            Text(
+                                (
+                                    self.rateFormatter
+                                        .string(from: state.rateValues[i] as NSNumber) ?? ""
+                                ) + " U/hr"
+                            ).tag(i)
                         }
-                        .onChange(of: state.items[index].rateIndex, perform: { _ in state.calcTotal() })
-                        .frame(maxWidth: geometry.size.width / 2)
-                        .clipped()
+                    }
+                    .onChange(of: state.items[index].rateIndex, perform: { _ in state.calcTotal() })
+                }.listRowBackground(Color.chart)
 
-                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
-                                Text(
-                                    self.dateFormatter
-                                        .string(from: Date(
-                                            timeIntervalSince1970: state
-                                                .timeValues[i]
-                                        ))
-                                ).tag(i)
-                            }
+                Section {
+                    Picker(selection: $state.items[index].timeIndex, label: Text("Text")) {
+                        ForEach(0 ..< state.timeValues.count, id: \.self) { i in
+                            Text(
+                                self.dateFormatter
+                                    .string(from: Date(
+                                        timeIntervalSince1970: state
+                                            .timeValues[i]
+                                    ))
+                            ).tag(i)
                         }
-                        .onChange(of: state.items[index].timeIndex, perform: { _ in state.calcTotal() })
-                        .frame(maxWidth: geometry.size.width / 2)
-                        .clipped()
                     }
-                }
+                    .onChange(of: state.items[index].timeIndex, perform: { _ in state.calcTotal() })
+                }.listRowBackground(Color.chart)
             }
+            .padding(.top)
+            .scrollContentBackground(.hidden).background(color)
+            .navigationTitle("Set Rate")
+            .navigationBarTitleDisplayMode(.automatic)
         }
 
         private var list: some View {
@@ -152,7 +159,7 @@ extension BasalProfileEditor {
 
             switch editMode {
             case .inactive:
-                return AnyView(Button(action: onAdd) { Text("Add") })
+                return AnyView(Button(action: onAdd) { Image(systemName: "plus") })
             default:
                 return AnyView(EmptyView())
             }

+ 20 - 5
FreeAPS/Sources/Modules/CGM/CGMStateModel.swift

@@ -35,12 +35,27 @@ extension CGM {
 
         override func subscribe() {
             // collect the list of CGM available with plugins and CGMType defined manually
-            listOfCGM = CGMType.allCases.filter { $0 != CGMType.plugin }.map {
-                cgmName(id: $0.id, type: $0, displayName: $0.displayName, subtitle: $0.subtitle)
-            } +
-                pluginCGMManager.availableCGMManagers.map {
-                    cgmName(id: $0.identifier, type: CGMType.plugin, displayName: $0.localizedTitle, subtitle: $0.localizedTitle)
+            listOfCGM = (
+                CGMType.allCases.filter { $0 != CGMType.plugin }.map {
+                    cgmName(id: $0.id, type: $0, displayName: $0.displayName, subtitle: $0.subtitle)
+                } +
+                    pluginCGMManager.availableCGMManagers.map {
+                        cgmName(
+                            id: $0.identifier,
+                            type: CGMType.plugin,
+                            displayName: $0.localizedTitle,
+                            subtitle: $0.localizedTitle
+                        )
+                    }
+            ).sorted(by: { lhs, rhs in
+                if lhs.displayName == "None" {
+                    return true
+                } else if rhs.displayName == "None" {
+                    return false
+                } else {
+                    return lhs.displayName < rhs.displayName
                 }
+            })
 
             switch settingsManager.settings.cgm {
             case .plugin:

+ 128 - 37
FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift

@@ -9,6 +9,13 @@ extension CGM {
         @StateObject var state = StateModel()
         @State private var setupCGM = false
 
+        @State private var shouldDisplayHint: Bool = false
+        @State var hintDetent = PresentationDetent.large
+        @State var selectedVerboseHint: String?
+        @State var hintLabel: String?
+        @State private var decimalPlaceholder: Decimal = 0.0
+        @State private var booleanPlaceholder: Bool = false
+
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
             colorScheme == .dark ? LinearGradient(
@@ -27,29 +34,75 @@ extension CGM {
                 )
         }
 
-        // @AppStorage(UserDefaults.BTKey.cgmTransmitterDeviceAddress.rawValue) private var cgmTransmitterDeviceAddress: String? = nil
-
         var body: some View {
             NavigationView {
                 Form {
-                    Section(header: Text("CGM")) {
-                        Picker("Type", selection: $state.cgmCurrent) {
-                            ForEach(state.listOfCGM) { type in
-                                VStack(alignment: .leading) {
-                                    Text(type.displayName)
-                                    Text(type.subtitle).font(.caption).foregroundColor(.secondary)
-                                }.tag(type)
+                    Section(
+                        header: Text("CGM Integration to Trio"),
+                        content: {
+                            VStack {
+                                Picker("Type", selection: $state.cgmCurrent) {
+                                    ForEach(state.listOfCGM) { type in
+                                        VStack(alignment: .leading) {
+                                            Text(type.displayName)
+                                            Text(type.subtitle).font(.caption).foregroundColor(.secondary)
+                                        }.tag(type)
+                                    }
+                                }.padding(.top)
+
+                                HStack(alignment: .top) {
+                                    Text(
+                                        "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                                    )
+                                    .font(.footnote)
+                                    .foregroundColor(.secondary)
+                                    .lineLimit(nil)
+                                    Spacer()
+                                    Button(
+                                        action: {
+                                            hintLabel = "Available CGM Types for Trio"
+                                            selectedVerboseHint =
+                                                "CGM Types… bla bla \n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                                            shouldDisplayHint.toggle()
+                                        },
+                                        label: {
+                                            HStack {
+                                                Image(systemName: "questionmark.circle")
+                                            }
+                                        }
+                                    ).buttonStyle(BorderlessButtonStyle())
+                                }.padding(.top)
+                            }.padding(.bottom)
+
+                            if let link = state.cgmCurrent.type.externalLink {
+                                Button {
+                                    UIApplication.shared.open(link, options: [:], completionHandler: nil)
+                                } label: {
+                                    HStack {
+                                        Text("About this source")
+                                        Spacer()
+                                        Image(systemName: "chevron.right")
+                                    }
+                                }
+                                .frame(maxWidth: .infinity, alignment: .leading)
                             }
-                        }
-                        if let link = state.cgmCurrent.type.externalLink {
-                            Button("About this source") {
-                                UIApplication.shared.open(link, options: [:], completionHandler: nil)
+
+                            if state.cgmCurrent.type == .plugin {
+                                Button {
+                                    setupCGM.toggle()
+                                } label: {
+                                    HStack {
+                                        Text("CGM Configuration")
+                                        Spacer()
+                                        Image(systemName: "chevron.right")
+                                    }
+                                }
+                                .frame(maxWidth: .infinity, alignment: .leading)
                             }
                         }
-                    }
+                    ).listRowBackground(Color.chart)
 
-                    if let appURL = state.urlOfApp()
-                    {
+                    if let appURL = state.urlOfApp() {
                         Section {
                             Button {
                                 UIApplication.shared.open(appURL, options: [:]) { success in
@@ -61,7 +114,8 @@ extension CGM {
                             }
 
                             label: {
-                                Label(state.displayNameOfApp() ?? "-", systemImage: "waveform.path.ecg.rectangle").font(.title3) }
+                                Label(state.displayNameOfApp() ?? "-", systemImage: "waveform.path.ecg.rectangle").font(.title3)
+                                    .padding() }
                                 .frame(maxWidth: .infinity, alignment: .center)
                                 .buttonStyle(.bordered)
                         }
@@ -77,7 +131,7 @@ extension CGM {
                                         }
                                     }
                                 }
-                                label: { Label("Open URL", systemImage: "waveform.path.ecg.rectangle").font(.title3) }
+                                label: { Label("Open URL", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding() }
                                     .frame(maxWidth: .infinity, alignment: .center)
                                     .buttonStyle(.bordered)
                             }
@@ -86,9 +140,9 @@ extension CGM {
                             Section {
                                 Button {
                                     state.showModal(for: .nighscoutConfigDirect)
-                                    // router.mainSecondaryModalView.send(router.view(for: .nighscoutConfigDirect))
                                 }
-                                label: { Label("Config Nightscout", systemImage: "waveform.path.ecg.rectangle").font(.title3)
+                                label: {
+                                    Label("Config Nightscout", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding()
                                 }
                                 .frame(maxWidth: .infinity, alignment: .center)
                                 .buttonStyle(.bordered)
@@ -97,40 +151,77 @@ extension CGM {
                         }
                     }
 
-                    if state.cgmCurrent.type == .plugin {
-                        Section {
-                            Button("CGM Configuration") {
-                                setupCGM.toggle()
-                            }
-                        }
-                    }
                     if state.cgmCurrent.type == .xdrip {
                         Section(header: Text("Heartbeat")) {
                             VStack(alignment: .leading) {
                                 if let cgmTransmitterDeviceAddress = state.cgmTransmitterDeviceAddress {
-                                    Text("CGM address :")
+                                    Text("CGM address :").padding(.top)
                                     Text(cgmTransmitterDeviceAddress)
                                 } else {
-                                    Text("CGM is not used as heartbeat.")
+                                    Text("CGM is not used as heartbeat.").padding(.top)
                                 }
+
+                                HStack(alignment: .top) {
+                                    Text(
+                                        "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                                    )
+                                    .font(.footnote)
+                                    .foregroundColor(.secondary)
+                                    .lineLimit(nil)
+                                    Spacer()
+                                    Button(
+                                        action: {
+                                            hintLabel = "CGM Heartbeat"
+                                            selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                                            shouldDisplayHint.toggle()
+                                        },
+                                        label: {
+                                            HStack {
+                                                Image(systemName: "questionmark.circle")
+                                            }
+                                        }
+                                    ).buttonStyle(BorderlessButtonStyle())
+                                }.padding(.vertical)
                             }
-                        }
+                        }.listRowBackground(Color.chart)
                     }
+
                     if state.cgmCurrent.type == .plugin && state.cgmCurrent.id.contains("Libre") {
-                        Section(header: Text("Calibrations")) {
-                            Text("Calibrations").navigationLink(to: .calibrations, from: self)
-                        }
+                        Section {
+                            Text("Libre Calibrations").navigationLink(to: .calibrations, from: self)
+                        }.listRowBackground(Color.chart)
                     }
 
-                    Section(header: Text("Experimental")) {
-                        Toggle("Smooth Glucose Value", isOn: $state.smoothGlucose)
-                    }
+                    SettingInputSection(
+                        decimalValue: $decimalPlaceholder,
+                        booleanValue: $state.smoothGlucose,
+                        shouldDisplayHint: $shouldDisplayHint,
+                        selectedVerboseHint: Binding(
+                            get: { selectedVerboseHint },
+                            set: {
+                                selectedVerboseHint = $0
+                                hintLabel = "Smooth Glucose Value"
+                            }
+                        ),
+                        type: .boolean,
+                        label: "Smooth Glucose Value",
+                        miniHint: "Smooth CGM readings using Savitzky–Golay filtering.",
+                        verboseHint: "Smooth Glucose Value… bla bla bla"
+                    )
                 }
                 .scrollContentBackground(.hidden).background(color)
                 .onAppear(perform: configureView)
                 .navigationTitle("CGM")
                 .navigationBarTitleDisplayMode(.automatic)
-                .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
+                .sheet(isPresented: $shouldDisplayHint) {
+                    SettingInputHintView(
+                        hintDetent: $hintDetent,
+                        shouldDisplayHint: $shouldDisplayHint,
+                        hintLabel: hintLabel ?? "",
+                        hintText: selectedVerboseHint ?? "",
+                        sheetTitle: "Help"
+                    )
+                }
                 .sheet(isPresented: $setupCGM) {
                     if let cgmFetchManager = state.cgmManager,
                        let cgmManager = cgmFetchManager.cgmManager,

+ 4 - 4
FreeAPS/Sources/Modules/Calibrations/View/CalibrationsRootView.swift

@@ -53,7 +53,7 @@ extension Calibrations {
                         }
                         label: { Text("Add") }
                             .disabled(state.newCalibration <= 0)
-                    }
+                    }.listRowBackground(Color.chart)
 
                     Section(header: Text("Info")) {
                         HStack {
@@ -66,7 +66,7 @@ extension Calibrations {
                             Spacer()
                             Text(formatter.string(from: state.intercept as NSNumber)!)
                         }
-                    }
+                    }.listRowBackground(Color.chart)
 
                     Section(header: Text("Remove")) {
                         Button {
@@ -97,13 +97,13 @@ extension Calibrations {
 
                             }.onDelete(perform: delete)
                         }
-                    }
+                    }.listRowBackground(Color.chart)
 
                     if state.calibrations.isNotEmpty {
                         Section(header: Text("Chart")) {
                             CalibrationsChart().environmentObject(state)
                                 .frame(minHeight: geo.size.width)
-                        }
+                        }.listRowBackground(Color.chart)
                     }
                 }
             }

+ 47 - 42
FreeAPS/Sources/Modules/CarbRatioEditor/View/CarbRatioEditorRootView.swift

@@ -48,31 +48,39 @@ extension CarbRatioEditor {
                             Text(rateFormatter.string(from: autotune.carbRatio as NSNumber) ?? "0")
                             Text("g/U").foregroundColor(.secondary)
                         }
-                    }
+                    }.listRowBackground(Color.chart)
                 }
+
                 Section(header: Text("Schedule")) {
                     list
-                    addButton
-                }
+                }.listRowBackground(Color.chart)
+
                 Section {
                     Button {
                         let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
                         impactHeavy.impactOccurred()
                         state.save()
-                    }
-                    label: {
+                    } label: {
                         Text("Save")
                     }
                     .disabled(state.items.isEmpty)
-                }
+                    .frame(maxWidth: .infinity, alignment: .center)
+                    .tint(.white)
+
+                }.listRowBackground(state.items.isEmpty ? Color(.systemGray4) : Color(.systemBlue))
             }
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
             .navigationTitle("Carb Ratios")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(
-                trailing: EditButton()
-            )
+            .toolbar(content: {
+                ToolbarItem(placement: .topBarTrailing) {
+                    EditButton()
+                }
+                ToolbarItem(placement: .topBarTrailing) {
+                    addButton
+                }
+            })
             .environment(\.editMode, $editMode)
             .onAppear {
                 state.validate()
@@ -80,42 +88,39 @@ extension CarbRatioEditor {
         }
 
         private func pickers(for index: Int) -> some View {
-            GeometryReader { geometry in
-                VStack {
-                    HStack {
-                        Text("Ratio").frame(width: geometry.size.width / 2)
-                        Text("Time").frame(width: geometry.size.width / 2)
-                    }
-                    HStack(spacing: 0) {
-                        Picker(selection: $state.items[index].rateIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
-                                Text(
-                                    (
-                                        self.rateFormatter
-                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
-                                    ) + " g/U"
-                                ).tag(i)
-                            }
+            Form {
+                Section {
+                    Picker(selection: $state.items[index].rateIndex, label: Text("Ratio")) {
+                        ForEach(0 ..< state.rateValues.count, id: \.self) { i in
+                            Text(
+                                (
+                                    self.rateFormatter
+                                        .string(from: state.rateValues[i] as NSNumber) ?? ""
+                                ) + " g/U"
+                            ).tag(i)
                         }
-                        .frame(maxWidth: geometry.size.width / 2)
-                        .clipped()
-
-                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
-                                Text(
-                                    self.dateFormatter
-                                        .string(from: Date(
-                                            timeIntervalSince1970: state
-                                                .timeValues[i]
-                                        ))
-                                ).tag(i)
-                            }
+                    }
+                }.listRowBackground(Color.chart)
+
+                Section {
+                    Picker(selection: $state.items[index].timeIndex, label: Text("Time")) {
+                        ForEach(0 ..< state.timeValues.count, id: \.self) { i in
+                            Text(
+                                self.dateFormatter
+                                    .string(from: Date(
+                                        timeIntervalSince1970: state
+                                            .timeValues[i]
+                                    ))
+                            ).tag(i)
                         }
-                        .frame(maxWidth: geometry.size.width / 2)
-                        .clipped()
                     }
-                }
+
+                }.listRowBackground(Color.chart)
             }
+            .padding(.top)
+            .scrollContentBackground(.hidden).background(color)
+            .navigationTitle("Set Ratio")
+            .navigationBarTitleDisplayMode(.automatic)
         }
 
         private var list: some View {
@@ -147,7 +152,7 @@ extension CarbRatioEditor {
 
             switch editMode {
             case .inactive:
-                return AnyView(Button(action: onAdd) { Text("Add") })
+                return AnyView(Button(action: onAdd) { Image(systemName: "plus") })
             default:
                 return AnyView(EmptyView())
             }

+ 46 - 42
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -53,7 +53,7 @@ extension ISFEditor {
                             }
                             Text(state.units.rawValue + "/U").foregroundColor(.secondary)
                         }
-                    }
+                    }.listRowBackground(Color.chart)
                 }
                 if let newISF = state.autosensISF {
                     Section(
@@ -89,31 +89,39 @@ extension ISFEditor {
                             )
                             Text(state.units.rawValue + "/U").foregroundColor(.secondary)
                         }
-                    }
+                    }.listRowBackground(Color.chart)
                 }
+
                 Section(header: Text("Schedule")) {
                     list
-                    addButton
-                }
+                }.listRowBackground(Color.chart)
+
                 Section {
                     Button {
                         let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
                         impactHeavy.impactOccurred()
                         state.save()
-                    }
-                    label: {
+                    } label: {
                         Text("Save")
                     }
                     .disabled(state.items.isEmpty)
-                }
+                    .frame(maxWidth: .infinity, alignment: .center)
+                    .tint(.white)
+
+                }.listRowBackground(state.items.isEmpty ? Color(.systemGray4) : Color(.systemBlue))
             }
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
             .navigationTitle("Insulin Sensitivities")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(
-                trailing: EditButton()
-            )
+            .toolbar(content: {
+                ToolbarItem(placement: .topBarTrailing) {
+                    EditButton()
+                }
+                ToolbarItem(placement: .topBarTrailing) {
+                    addButton
+                }
+            })
             .environment(\.editMode, $editMode)
             .onAppear {
                 state.validate()
@@ -121,42 +129,38 @@ extension ISFEditor {
         }
 
         private func pickers(for index: Int) -> some View {
-            GeometryReader { geometry in
-                VStack {
-                    HStack {
-                        Text("Rate").frame(width: geometry.size.width / 2)
-                        Text("Time").frame(width: geometry.size.width / 2)
-                    }
-                    HStack(spacing: 0) {
-                        Picker(selection: $state.items[index].rateIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
-                                Text(
-                                    (
-                                        self.rateFormatter
-                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
-                                    ) + " \(state.units.rawValue)/U"
-                                ).tag(i)
-                            }
+            Form {
+                Section {
+                    Picker(selection: $state.items[index].rateIndex, label: Text("Rate")) {
+                        ForEach(0 ..< state.rateValues.count, id: \.self) { i in
+                            Text(
+                                (
+                                    self.rateFormatter
+                                        .string(from: state.rateValues[i] as NSNumber) ?? ""
+                                ) + " \(state.units.rawValue)/U"
+                            ).tag(i)
                         }
-                        .frame(maxWidth: geometry.size.width / 2)
-                        .clipped()
+                    }
+                }.listRowBackground(Color.chart)
 
-                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
-                                Text(
-                                    self.dateFormatter
-                                        .string(from: Date(
-                                            timeIntervalSince1970: state
-                                                .timeValues[i]
-                                        ))
-                                ).tag(i)
-                            }
+                Section {
+                    Picker(selection: $state.items[index].timeIndex, label: Text("Time")) {
+                        ForEach(0 ..< state.timeValues.count, id: \.self) { i in
+                            Text(
+                                self.dateFormatter
+                                    .string(from: Date(
+                                        timeIntervalSince1970: state
+                                            .timeValues[i]
+                                    ))
+                            ).tag(i)
                         }
-                        .frame(maxWidth: geometry.size.width / 2)
-                        .clipped()
                     }
-                }
+                }.listRowBackground(Color.chart)
             }
+            .padding(.top)
+            .scrollContentBackground(.hidden).background(color)
+            .navigationTitle("Set Rate")
+            .navigationBarTitleDisplayMode(.automatic)
         }
 
         private var list: some View {
@@ -188,7 +192,7 @@ extension ISFEditor {
 
             switch editMode {
             case .inactive:
-                return AnyView(Button(action: onAdd) { Text("Add") })
+                return AnyView(Button(action: onAdd) { Image(systemName: "plus") })
             default:
                 return AnyView(EmptyView())
             }

+ 81 - 18
FreeAPS/Sources/Modules/PumpConfig/View/PumpConfigRootView.swift

@@ -7,6 +7,14 @@ extension PumpConfig {
         let displayClose: Bool
         @StateObject var state = StateModel()
 
+        @State private var shouldDisplayHint: Bool = false
+        @State var hintDetent = PresentationDetent.large
+        @State var selectedVerboseHint: String?
+        @State var hintLabel: String?
+        @State private var decimalPlaceholder: Decimal = 0.0
+        @State private var booleanPlaceholder: Bool = false
+        @State var showPumpSelection: Bool = false
+
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
             colorScheme == .dark ? LinearGradient(
@@ -28,31 +36,71 @@ extension PumpConfig {
         var body: some View {
             NavigationView {
                 Form {
-                    Section(header: Text("Model")) {
-                        if let pumpState = state.pumpState {
-                            Button {
-                                state.setupPump = true
-                            } label: {
-                                HStack {
-                                    Image(uiImage: pumpState.image ?? UIImage()).padding()
-                                    Text(pumpState.name)
+                    Section(
+                        header: Text("Pump Integration to Trio"),
+                        content: {
+                            if let pumpState = state.pumpState {
+                                Button {
+                                    state.setupPump = true
+                                } label: {
+                                    HStack {
+                                        Image(uiImage: pumpState.image ?? UIImage()).padding()
+                                        Text(pumpState.name)
+                                    }
                                 }
+                                if state.alertNotAck {
+                                    Spacer()
+                                    Button("Acknowledge all alerts") { state.ack() }
+                                }
+                            } else {
+                                VStack {
+                                    Button {
+                                        showPumpSelection.toggle()
+                                    } label: {
+                                        Text("Add Pump")
+                                            .font(.title3) }
+                                        .frame(maxWidth: .infinity, alignment: .center)
+                                        .buttonStyle(.bordered)
+
+                                    HStack(alignment: .top) {
+                                        Text(
+                                            "Pair a compatible pump with Trio. See details for available devices."
+                                        )
+                                        .font(.footnote)
+                                        .foregroundColor(.secondary)
+                                        .lineLimit(nil)
+                                        Spacer()
+                                        Button(
+                                            action: {
+                                                hintLabel = "Pump Pairing to Trio"
+                                                selectedVerboseHint =
+                                                    "Explanation… limitation… etc."
+                                                shouldDisplayHint.toggle()
+                                            },
+                                            label: {
+                                                HStack {
+                                                    Image(systemName: "questionmark.circle")
+                                                }
+                                            }
+                                        ).buttonStyle(BorderlessButtonStyle())
+                                    }.padding(.top)
+                                }.padding(.vertical)
                             }
-                            if state.alertNotAck {
-                                Spacer()
-                                Button("Acknowledge all alerts") { state.ack() }
-                            }
-                        } else {
-                            Button("Add Medtronic") { state.addPump(.minimed) }
-                            Button("Add Omnipod") { state.addPump(.omnipod) }
-                            Button("Add Omnipod Dash") { state.addPump(.omnipodBLE) }
-                            Button("Add Simulator") { state.addPump(.simulator) }
                         }
+                    )
+                    .padding(.top)
+                    .listRowBackground(Color.chart)
+
+                    if state.pumpState != nil {
+                        Section {
+                            Text("Delivery Limits & DIA")
+                                .navigationLink(to: .pumpSettingsEditor, from: self)
+                        }.listRowBackground(Color.chart)
                     }
                 }
                 .scrollContentBackground(.hidden).background(color)
                 .onAppear(perform: configureView)
-                .navigationTitle("Pump config")
+                .navigationTitle("Insulin Pump")
                 .navigationBarTitleDisplayMode(.automatic)
                 .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
                 .sheet(isPresented: $state.setupPump) {
@@ -73,6 +121,21 @@ extension PumpConfig {
                         )
                     }
                 }
+                .sheet(isPresented: $shouldDisplayHint) {
+                    SettingInputHintView(
+                        hintDetent: $hintDetent,
+                        shouldDisplayHint: $shouldDisplayHint,
+                        hintLabel: hintLabel ?? "",
+                        hintText: selectedVerboseHint ?? "",
+                        sheetTitle: "Help"
+                    )
+                }
+                .confirmationDialog("Pump Model", isPresented: $showPumpSelection) {
+                    Button("Medtronic") { state.addPump(.minimed) }
+                    Button("Omnipod Eros") { state.addPump(.omnipod) }
+                    Button("Omnipod Dash") { state.addPump(.omnipodBLE) }
+                    Button("Pump Simulator") { state.addPump(.simulator) }
+                } message: { Text("Select Pump Model") }
             }
         }
     }

+ 45 - 3
FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift

@@ -1,18 +1,54 @@
+import Combine
 import SwiftUI
 
 extension PumpSettingsEditor {
     final class StateModel: BaseStateModel<Provider> {
-        @Published var maxBasal: Decimal = 0.0
-        @Published var maxBolus: Decimal = 0.0
-        @Published var dia: Decimal = 0.0
+        @Published var maxBasal: Decimal = 0.0 {
+            didSet {
+                checkForChanges()
+            }
+        }
+
+        @Published var maxBolus: Decimal = 0.0 {
+            didSet {
+                checkForChanges()
+            }
+        }
+
+        @Published var dia: Decimal = 0.0 {
+            didSet {
+                checkForChanges()
+            }
+        }
 
         @Published var syncInProgress = false
+        @Published var hasChanged: Bool = false
+
+        private var initialMaxBasal: Decimal = 0.0
+        private var initialMaxBolus: Decimal = 0.0
+        private var initialDia: Decimal = 0.0
 
         override func subscribe() {
             let settings = provider.settings()
             maxBasal = settings.maxBasal
             maxBolus = settings.maxBolus
             dia = settings.insulinActionCurve
+
+            initialMaxBasal = settings.maxBasal
+            initialMaxBolus = settings.maxBolus
+            initialDia = settings.insulinActionCurve
+
+            checkForChanges()
+        }
+
+        var unchanged: Bool {
+            initialMaxBasal == maxBasal &&
+                initialMaxBolus == maxBolus &&
+                initialDia == dia
+        }
+
+        private func checkForChanges() {
+            hasChanged = !unchanged
         }
 
         func save() {
@@ -29,7 +65,13 @@ extension PumpSettingsEditor {
                     self.syncInProgress = false
                     self.maxBasal = settings.maxBasal
                     self.maxBolus = settings.maxBolus
+                    self.dia = settings.insulinActionCurve
+
+                    self.initialMaxBasal = settings.maxBasal
+                    self.initialMaxBolus = settings.maxBolus
+                    self.initialDia = settings.insulinActionCurve
 
+                    self.checkForChanges()
                 } receiveValue: {}
                 .store(in: &lifetime)
         }

+ 81 - 25
FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift

@@ -6,6 +6,13 @@ extension PumpSettingsEditor {
         let resolver: Resolver
         @StateObject var state = StateModel()
 
+        @State private var shouldDisplayHint: Bool = false
+        @State var hintDetent = PresentationDetent.large
+        @State var selectedVerboseHint: String?
+        @State var hintLabel: String?
+        @State private var decimalPlaceholder: Decimal = 0.0
+        @State private var booleanPlaceholder: Bool = false
+
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
             colorScheme == .dark ? LinearGradient(
@@ -32,45 +39,94 @@ extension PumpSettingsEditor {
 
         var body: some View {
             Form {
-                Section(header: Text("Delivery limits")) {
-                    HStack {
-                        Text("Max Basal")
-                        TextFieldWithToolBar(text: $state.maxBasal, placeholder: "U/hr", numberFormatter: formatter)
-                    }
-                    HStack {
-                        Text("Max Bolus")
-                        TextFieldWithToolBar(text: $state.maxBolus, placeholder: "U", numberFormatter: formatter)
-                    }
-                    // TODO: is max carbs now somewhere else?
-//                    HStack {
-//                        Text("Max Carbs")
-//                        TextFieldWithToolBar(text: $state.maxCarbs, placeholder: "g", numberFormatter: formatter)
-//                    }
-                }
+                Section(
+                    header: Text("Insulin Pump Configuration"),
+                    content: {
+                        VStack {
+                            HStack {
+                                Text("Max Basal")
+                                Spacer()
+                                TextFieldWithToolBar(text: $state.maxBasal, placeholder: "0", numberFormatter: formatter)
+                            }.padding(.top)
+                            HStack {
+                                Text("Max Bolus")
+                                Spacer()
+                                TextFieldWithToolBar(text: $state.maxBolus, placeholder: "0", numberFormatter: formatter)
+                            }
 
-                Section(header: Text("Duration of Insulin Action")) {
-                    HStack {
-                        Text("DIA")
-                        TextFieldWithToolBar(text: $state.dia, placeholder: "hours", numberFormatter: formatter)
+                            HStack(alignment: .top) {
+                                Text(
+                                    "Sets delivery limits for basal and bolus insulin on pump."
+                                )
+                                .lineLimit(nil)
+                                .font(.footnote)
+                                .foregroundColor(.secondary)
+
+                                Spacer()
+                                Button(
+                                    action: {
+                                        hintLabel = "Insulin Delivery limits"
+                                        selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                                        shouldDisplayHint.toggle()
+                                    },
+                                    label: {
+                                        HStack {
+                                            Image(systemName: "questionmark.circle")
+                                        }
+                                    }
+                                ).buttonStyle(BorderlessButtonStyle())
+                            }
+                        }.padding(.bottom)
                     }
-                }
+                ).listRowBackground(Color.chart)
+
+                SettingInputSection(
+                    decimalValue: $state.dia,
+                    booleanValue: $booleanPlaceholder,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    selectedVerboseHint: Binding(
+                        get: { selectedVerboseHint },
+                        set: {
+                            selectedVerboseHint = $0
+                            hintLabel = "Duration of Insulin Action"
+                        }
+                    ),
+                    type: .decimal,
+                    label: "Duration of Insulin Action",
+                    miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
+                    verboseHint: "Duration of Insulin Action… bla bla bla"
+                )
 
                 Section {
                     HStack {
                         if state.syncInProgress {
                             ProgressView().padding(.trailing, 10)
                         }
-                        Button { state.save() }
-                        label: {
+                        Button {
+                            let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
+                            impactHeavy.impactOccurred()
+                            state.save()
+                        } label: {
                             Text(state.syncInProgress ? "Saving..." : "Save on Pump")
                         }
-                        .disabled(state.syncInProgress)
+                        .disabled(state.syncInProgress || !state.hasChanged)
+                        .frame(maxWidth: .infinity, alignment: .center)
+                        .tint(.white)
                     }
-                }
+                }.listRowBackground(state.syncInProgress || !state.hasChanged ? Color(.systemGray4) : Color(.systemBlue))
+            }
+            .sheet(isPresented: $shouldDisplayHint) {
+                SettingInputHintView(
+                    hintDetent: $hintDetent,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    hintLabel: hintLabel ?? "",
+                    hintText: selectedVerboseHint ?? "",
+                    sheetTitle: "Help"
+                )
             }
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
-            .navigationTitle("Pump Settings")
+            .navigationTitle("Delivery Limits & DIA")
             .navigationBarTitleDisplayMode(.automatic)
         }
     }

+ 3 - 5
FreeAPS/Sources/Modules/Settings/View/Subviews/DevicesView.swift

@@ -36,11 +36,9 @@ struct DevicesView: BaseView {
             Section(
                 header: Text("Setup & Configuraton"),
                 content: {
-                    Text("Pump").navigationLink(to: .pumpConfig, from: self)
-                    Text("Pump Settings").navigationLink(to: .pumpSettingsEditor, from: self)
-                    Text("TODO: Migrate Settings into Pump 👆")
-                    Text("CGM").navigationLink(to: .cgm, from: self)
-                    Text("Watch").navigationLink(to: .watch, from: self)
+                    Text("Insulin Pump").navigationLink(to: .pumpConfig, from: self)
+                    Text("Continuous Glucose Monitor").navigationLink(to: .cgm, from: self)
+                    Text("Smart Watch").navigationLink(to: .watch, from: self)
                 }
             )
             .listRowBackground(Color.chart)

+ 47 - 36
FreeAPS/Sources/Modules/TargetsEditor/View/TargetsEditorRootView.swift

@@ -42,27 +42,34 @@ extension TargetsEditor {
             Form {
                 Section(header: Text("Schedule")) {
                     list
-                    addButton
-                }
+                }.listRowBackground(Color.chart)
+
                 Section {
                     Button {
                         let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
                         impactHeavy.impactOccurred()
                         state.save()
-                    }
-                    label: {
+                    } label: {
                         Text("Save")
                     }
                     .disabled(state.items.isEmpty)
-                }
+                    .frame(maxWidth: .infinity, alignment: .center)
+                    .tint(.white)
+
+                }.listRowBackground(state.items.isEmpty ? Color(.systemGray4) : Color(.systemBlue))
             }
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
             .navigationTitle("Target Glucose")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(
-                trailing: EditButton()
-            )
+            .toolbar(content: {
+                ToolbarItem(placement: .topBarTrailing) {
+                    EditButton()
+                }
+                ToolbarItem(placement: .topBarTrailing) {
+                    addButton
+                }
+            })
             .environment(\.editMode, $editMode)
             .onAppear {
                 state.validate()
@@ -70,39 +77,43 @@ extension TargetsEditor {
         }
 
         private func pickers(for index: Int) -> some View {
-            GeometryReader { geometry in
-                VStack {
-                    HStack {
-                        Text("Target").frame(width: geometry.size.width / 3)
-                        Text("Time").frame(width: geometry.size.width / 3)
-                    }
-                    HStack(spacing: 0) {
-                        Picker(selection: $state.items[index].lowIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
-                                Text(
+            Form {
+                Section {
+                    Picker(
+                        selection: $state.items[index].lowIndex,
+                        label: Text("Target ")
+                    ) {
+                        ForEach(0 ..< state.rateValues.count, id: \.self) { i in
+                            Text(
+                                (
                                     self.rateFormatter
                                         .string(from: state.rateValues[i] as NSNumber) ?? ""
-                                ).tag(i)
-                            }
+                                )
+                                    + " \(state.units.rawValue)"
+
+                            ).tag(i)
                         }
-                        .frame(maxWidth: geometry.size.width / 3)
-                        .clipped()
-                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
-                                Text(
-                                    self.dateFormatter
-                                        .string(from: Date(
-                                            timeIntervalSince1970: state
-                                                .timeValues[i]
-                                        ))
-                                ).tag(i)
-                            }
+                    }
+                }.listRowBackground(Color.chart)
+
+                Section {
+                    Picker(selection: $state.items[index].timeIndex, label: Text("Time")) {
+                        ForEach(0 ..< state.timeValues.count, id: \.self) { i in
+                            Text(
+                                self.dateFormatter
+                                    .string(from: Date(
+                                        timeIntervalSince1970: state
+                                            .timeValues[i]
+                                    ))
+                            ).tag(i)
                         }
-                        .frame(maxWidth: geometry.size.width / 3)
-                        .clipped()
                     }
-                }
+                }.listRowBackground(Color.chart)
             }
+            .padding(.top)
+            .scrollContentBackground(.hidden).background(color)
+            .navigationTitle("Set Target")
+            .navigationBarTitleDisplayMode(.automatic)
         }
 
         private var list: some View {
@@ -134,7 +145,7 @@ extension TargetsEditor {
 
             switch editMode {
             case .inactive:
-                return AnyView(Button(action: onAdd) { Text("Add") })
+                return AnyView(Button(action: onAdd) { Image(systemName: "plus") })
             default:
                 return AnyView(EmptyView())
             }

+ 123 - 0
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift

@@ -0,0 +1,123 @@
+import SwiftUI
+
+struct WatchConfigAppleWatchView: View {
+    @ObservedObject var state: WatchConfig.StateModel
+
+    @State private var shouldDisplayHint: Bool = false
+    @State var hintDetent = PresentationDetent.large
+    @State var selectedVerboseHint: String?
+    @State var hintLabel: String?
+    @State private var decimalPlaceholder: Decimal = 0.0
+    @State private var booleanPlaceholder: Bool = false
+
+    @Environment(\.colorScheme) var colorScheme
+    var color: LinearGradient {
+        colorScheme == .dark ? LinearGradient(
+            gradient: Gradient(colors: [
+                Color.bgDarkBlue,
+                Color.bgDarkerDarkBlue
+            ]),
+            startPoint: .top,
+            endPoint: .bottom
+        )
+            :
+            LinearGradient(
+                gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
+                startPoint: .top,
+                endPoint: .bottom
+            )
+    }
+
+    private func onDelete(offsets: IndexSet) {
+        state.devices.remove(atOffsets: offsets)
+        state.deleteGarminDevice()
+    }
+
+    var body: some View {
+        Form {
+            Section(
+                header: Text("Apple Watch Configuration"),
+                content: {
+                    VStack {
+                        Picker(
+                            selection: $state.selectedAwConfig,
+                            label: Text("Display on Watch")
+                        ) {
+                            ForEach(AwConfig.allCases) { selection in
+                                Text(selection.displayName).tag(selection)
+                            }
+                        }.padding(.top)
+
+                        HStack(alignment: .top) {
+                            Text(
+                                "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                            )
+                            .font(.footnote)
+                            .foregroundColor(.secondary)
+                            .lineLimit(nil)
+                            Spacer()
+                            Button(
+                                action: {
+                                    hintLabel = "Display on Watch"
+                                    selectedVerboseHint = "Display on Watch… bla bla bla"
+                                    shouldDisplayHint.toggle()
+                                },
+                                label: {
+                                    HStack {
+                                        Image(systemName: "questionmark.circle")
+                                    }
+                                }
+                            ).buttonStyle(BorderlessButtonStyle())
+                        }.padding(.top)
+                    }.padding(.bottom)
+                }
+            ).listRowBackground(Color.chart)
+
+            SettingInputSection(
+                decimalValue: $decimalPlaceholder,
+                booleanValue: $state.displayFatAndProteinOnWatch,
+                shouldDisplayHint: $shouldDisplayHint,
+                selectedVerboseHint: Binding(
+                    get: { selectedVerboseHint },
+                    set: {
+                        selectedVerboseHint = $0
+                        hintLabel = "Show Protein and Fat"
+                    }
+                ),
+                type: .boolean,
+                label: "Show Protein and Fat",
+                miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
+                verboseHint: "Show Protein and Fat… bla bla bla"
+            )
+
+            SettingInputSection(
+                decimalValue: $decimalPlaceholder,
+                booleanValue: $state.confirmBolusFaster,
+                shouldDisplayHint: $shouldDisplayHint,
+                selectedVerboseHint: Binding(
+                    get: { selectedVerboseHint },
+                    set: {
+                        selectedVerboseHint = $0
+                        hintLabel = "Confirm Bolus Faster"
+                    }
+                ),
+                type: .boolean,
+                label: "Confirm Bolus Faster",
+                miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
+                verboseHint: "Confirm Bolus Faster… bla bla bla"
+            )
+        }
+        .sheet(isPresented: $shouldDisplayHint) {
+            SettingInputHintView(
+                hintDetent: $hintDetent,
+                shouldDisplayHint: $shouldDisplayHint,
+                hintLabel: hintLabel ?? "",
+                hintText: selectedVerboseHint ?? "",
+                sheetTitle: "Help"
+            )
+        }
+        .navigationTitle("Apple Watch")
+        .navigationBarTitleDisplayMode(.automatic)
+        .scrollContentBackground(.hidden).background(color)
+    }
+}

+ 100 - 0
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigGarminView.swift

@@ -0,0 +1,100 @@
+import SwiftUI
+
+struct WatchConfigGarminView: View {
+    @ObservedObject var state: WatchConfig.StateModel
+
+    @State private var shouldDisplayHint: Bool = false
+    @State var hintDetent = PresentationDetent.large
+    @State var selectedVerboseHint: String?
+    @State var hintLabel: String?
+    @State private var decimalPlaceholder: Decimal = 0.0
+    @State private var booleanPlaceholder: Bool = false
+
+    @Environment(\.colorScheme) var colorScheme
+    var color: LinearGradient {
+        colorScheme == .dark ? LinearGradient(
+            gradient: Gradient(colors: [
+                Color.bgDarkBlue,
+                Color.bgDarkerDarkBlue
+            ]),
+            startPoint: .top,
+            endPoint: .bottom
+        )
+            :
+            LinearGradient(
+                gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
+                startPoint: .top,
+                endPoint: .bottom
+            )
+    }
+
+    private func onDelete(offsets: IndexSet) {
+        state.devices.remove(atOffsets: offsets)
+        state.deleteGarminDevice()
+    }
+
+    var body: some View {
+        Form {
+            Section(
+                header: Text("Garmin Configuration"),
+                content:
+                {
+                    VStack {
+                        Button {
+                            state.selectGarminDevices()
+                        } label: {
+                            Text("Add Device")
+                                .font(.title3) }
+                            .frame(maxWidth: .infinity, alignment: .center)
+                            .buttonStyle(.bordered)
+
+                        HStack(alignment: .top) {
+                            Text(
+                                "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+                            )
+                            .font(.footnote)
+                            .foregroundColor(.secondary)
+                            .lineLimit(nil)
+                            Spacer()
+                            Button(
+                                action: {
+                                    hintLabel = "Add Device"
+                                    selectedVerboseHint = "Add Garmin Device… bla bla bla"
+                                    shouldDisplayHint.toggle()
+                                },
+                                label: {
+                                    HStack {
+                                        Image(systemName: "questionmark.circle")
+                                    }
+                                }
+                            ).buttonStyle(BorderlessButtonStyle())
+                        }.padding(.top)
+                    }.padding(.vertical)
+                }
+            ).listRowBackground(Color.chart)
+
+            if !state.devices.isEmpty {
+                Section(header: Text("Garmin Watch")) {
+                    List {
+                        ForEach(state.devices, id: \.uuid) { device in
+                            Text(device.friendlyName)
+                        }
+                        .onDelete(perform: onDelete)
+                    }
+                }.listRowBackground(Color.chart)
+            }
+        }
+        .sheet(isPresented: $shouldDisplayHint) {
+            SettingInputHintView(
+                hintDetent: $hintDetent,
+                shouldDisplayHint: $shouldDisplayHint,
+                hintLabel: hintLabel ?? "",
+                hintText: selectedVerboseHint ?? "",
+                sheetTitle: "Help"
+            )
+        }
+        .navigationTitle("Garmin")
+        .navigationBarTitleDisplayMode(.automatic)
+        .scrollContentBackground(.hidden).background(color)
+    }
+}

+ 7 - 29
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigRootView.swift

@@ -26,40 +26,18 @@ extension WatchConfig {
 
         var body: some View {
             Form {
-                Section(header: Text("Apple Watch")) {
-                    Picker(
-                        selection: $state.selectedAwConfig,
-                        label: Text("Display on Watch")
-                    ) {
-                        ForEach(AwConfig.allCases) { v in
-                            Text(v.displayName).tag(v)
-                        }
+                Section(
+                    header: Text("Smartwatch Configuration"),
+                    content: {
+                        NavigationLink("Apple Watch", destination: WatchConfigAppleWatchView(state: state))
+                        NavigationLink("Garmin", destination: WatchConfigGarminView(state: state))
                     }
-                    Toggle("Display Protein & Fat", isOn: $state.displayFatAndProteinOnWatch)
-                    Toggle("Confirm Bolus Faster", isOn: $state.confirmBolusFaster)
-                }
-
-                Section(header: Text("Garmin Watch")) {
-                    List {
-                        ForEach(state.devices, id: \.uuid) { device in
-                            Text(device.friendlyName)
-                        }
-                        .onDelete(perform: onDelete)
-                    }
-                    Button("Add devices") {
-                        state.selectGarminDevices()
-                    }
-                }
+                ).listRowBackground(Color.chart)
             }
             .scrollContentBackground(.hidden).background(color)
             .onAppear(perform: configureView)
-            .navigationTitle("Watch Configuration")
+            .navigationTitle("Watch")
             .navigationBarTitleDisplayMode(.automatic)
         }
-
-        private func onDelete(offsets: IndexSet) {
-            state.devices.remove(atOffsets: offsets)
-            state.deleteGarminDevice()
-        }
     }
 }

+ 1 - 1
FreeAPS/Sources/Router/Screen.swift

@@ -78,7 +78,7 @@ extension Screen {
         case .isfEditor:
             ISFEditor.RootView(resolver: resolver)
         case .crEditor:
-            CREditor.RootView(resolver: resolver)
+            CarbRatioEditor.RootView(resolver: resolver)
         case .targetsEditor:
             TargetsEditor.RootView(resolver: resolver)
         case .preferencesEditor: