Преглед изворни кода

Reworked Nightscout Config View with subviews
- Imroved structure and UX workflow when configuring Nightscout settings
- Added Connect, Upload, Fetch views with relevant settings per view
- Import settings and backfill glucose buttons kept in rootview since they are more frequently used
- Added footers for many sections to explain what the toggles actually do
- Loacalizaation needed for new textstrings. Just updated that with small changes in already existing strings

Daniel Snällfot пре 2 година
родитељ
комит
6e187c162b
28 измењених фајлова са 210 додато и 137 уклоњено
  1. 12 0
      FreeAPS.xcodeproj/project.pbxproj
  2. 2 2
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  3. 1 1
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  4. 2 2
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  5. 2 2
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  6. 2 2
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  7. 2 2
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  8. 2 2
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  9. 2 2
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  10. 2 2
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  11. 2 2
      FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings
  12. 2 2
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  13. 2 2
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  14. 2 2
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  15. 2 2
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  16. 2 2
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  17. 2 2
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  18. 2 2
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  19. 2 2
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  20. 2 2
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  21. 2 2
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  22. 2 2
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  23. 2 2
      FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings
  24. 3 3
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  25. 35 91
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift
  26. 58 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift
  27. 35 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift
  28. 24 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift

+ 12 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -240,6 +240,9 @@
 		45717281F743594AA9D87191 /* ConfigEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */; };
 		5075C1608E6249A51495C422 /* TargetsEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */; };
 		53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8630D58BDAD6D9C650B9B39 /* PumpConfigProvider.swift */; };
+		5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */; };
+		5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */; };
+		5A2325582BFCC168003518CA /* NightscoutConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325572BFCC168003518CA /* NightscoutConnectView.swift */; };
 		5BFA1C2208114643B77F8CEB /* AddTempTargetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE53A13D26F101B332EFFC8 /* AddTempTargetProvider.swift */; };
 		5D16287A969E64D18CE40E44 /* PumpConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F60E97100041040446F44E7 /* PumpConfigStateModel.swift */; };
 		63E890B4D951EAA91C071D5C /* BasalProfileEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFF91130F2FCCC7EBBA11AD /* BasalProfileEditorStateModel.swift */; };
@@ -760,6 +763,9 @@
 		44080E4709E3AE4B73054563 /* ConfigEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorProvider.swift; sourceTree = "<group>"; };
 		4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorRootView.swift; sourceTree = "<group>"; };
 		505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorStateModel.swift; sourceTree = "<group>"; };
+		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
+		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
+		5A2325572BFCC168003518CA /* NightscoutConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutConnectView.swift; sourceTree = "<group>"; };
 		5B8A42073A2D03A278914448 /* AddTempTargetDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddTempTargetDataFlow.swift; sourceTree = "<group>"; };
 		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>"; };
@@ -1865,6 +1871,9 @@
 			isa = PBXGroup;
 			children = (
 				8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */,
+				5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */,
+				5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */,
+				5A2325572BFCC168003518CA /* NightscoutConnectView.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -2598,6 +2607,7 @@
 				CEE9A6552BBB418300EB5194 /* CalibrationsProvider.swift in Sources */,
 				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
+				5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */,
@@ -2716,6 +2726,7 @@
 				38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */,
 				38E98A2525F52C9300C0CED0 /* IssueReporter.swift in Sources */,
 				190EBCC429FF136900BA767D /* StatConfigDataFlow.swift in Sources */,
+				5A2325582BFCC168003518CA /* NightscoutConnectView.swift in Sources */,
 				3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */,
 				3811DE4325C9D4A100A708ED /* SettingsProvider.swift in Sources */,
 				45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */,
@@ -2860,6 +2871,7 @@
 				BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */,
 				CE94598729E9E4110047C9C6 /* WatchConfigRootView.swift in Sources */,
 				19E1F7E829D082D0005C8D20 /* IconConfigDataFlow.swift in Sources */,
+				5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */,
 				E3A08AAE59538BC8A8ABE477 /* NotificationsConfigDataFlow.swift in Sources */,
 				1956FB212AFF79E200C7B4FF /* CoreDataStorage.swift in Sources */,
 				0F7A65FBD2CD8D6477ED4539 /* NotificationsConfigProvider.swift in Sources */,

Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


+ 1 - 1
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings

@@ -312,7 +312,7 @@ Enact a temp Basal or a temp target */
 "Skip Bolus screen after carbs" = "Skip Bolus screen after carbs";
 
 /* Allow remote control from NS */
-"Remote control" = "Remote control";
+"Remote Control" = "Remote Control";
 
 /* Add Medtronic pump */
 "Add Medtronic" = "Add Medtronic";

Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings


Разлика између датотеке није приказан због своје велике величине
+ 3 - 3
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 35 - 91
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -7,72 +7,28 @@ extension NightscoutConfig {
         let resolver: Resolver
         let displayClose: Bool
         @StateObject var state = StateModel()
-        @State var importAlert: Alert?
-        @State var isImportAlertPresented = false
-        @State var importedHasRun = false
+        @State private var importAlert: Alert?
+        @State private var isImportAlertPresented = false
+        @State private var importedHasRun = false
 
         @FetchRequest(
             entity: ImportError.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
-                format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate
-            )
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)],
+            predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate)
         ) var fetchedErrors: FetchedResults<ImportError>
 
-        private var portFormater: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.allowsFloats = false
-            return formatter
-        }
-
         var body: some View {
             Form {
-                Section {
-                    TextField("URL", text: $state.url)
-                        .disableAutocorrection(true)
-                        .textContentType(.URL)
-                        .autocapitalization(.none)
-                        .keyboardType(.URL)
-                    SecureField("API secret", text: $state.secret)
-                        .disableAutocorrection(true)
-                        .autocapitalization(.none)
-                        .textContentType(.password)
-                        .keyboardType(.asciiCapable)
-                    if !state.message.isEmpty {
-                        Text(state.message)
-                    }
-                    if state.connecting {
-                        HStack {
-                            Text("Connecting...")
-                            Spacer()
-                            ProgressView()
-                        }
-                    }
-                }
+                NavigationLink("Connect", destination: NightscoutConnectView(state: state))
+                NavigationLink("Upload", destination: NightscoutUploadView(state: state))
+                NavigationLink("Fetch and Remote Control", destination: NightscoutFetchView(state: state))
 
-                Section {
-                    Button("Connect") { state.connect() }
-                        .disabled(state.url.isEmpty || state.connecting)
-                    Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
-                }
-
-                Section {
-                    Button("Open Nighstcout") {
-                        UIApplication.shared.open(URL(string: state.url)!, options: [:], completionHandler: nil)
-                    }
-                    .disabled(state.url.isEmpty || state.connecting)
-                }
-
-                Section {
-                    Toggle("Upload", isOn: $state.isUploadEnabled)
-                    if state.isUploadEnabled {
-                        Toggle("Glucose", isOn: $state.uploadGlucose).disabled(!state.changeUploadGlucose)
-                    }
-                    Toggle("Download", isOn: $state.isDownloadEnabled)
-                } header: {
-                    Text("Allow Up- and downloads")
-                }
-
-                Section {
+                Section(
+                    header: Text("Import Settings"),
+                    footer: Text(
+                        "Importing settings from Nightscout will overwrite these settings in Trio Settings -> Configuration:\n • DIA (Pump settings)\n • Basal Profile\n • Insulin Sensitivities\n • Carb Ratios\n • Target Glucose"
+                    )
+                ) {
                     Button("Import settings from Nightscout") {
                         importAlert = Alert(
                             title: Text("Import settings?"),
@@ -95,50 +51,38 @@ extension NightscoutConfig {
                         )
                         isImportAlertPresented.toggle()
                     }.disabled(state.url.isEmpty || state.connecting)
-
-                } header: { Text("Import from Nightscout") }
-
-                    .alert(isPresented: $importedHasRun) {
-                        Alert(
-                            title: Text((fetchedErrors.first?.error ?? "").count < 4 ? "Settings imported" : "Import Error"),
-                            message: Text(
-                                (fetchedErrors.first?.error ?? "").count < 4 ?
-                                    NSLocalizedString(
-                                        "\nNow please verify all of your new settings thoroughly:\n\n* Basal Settings\n * Carb Ratios\n * Glucose Targets\n * Insulin Sensitivities\n * DIA\n\n in Trio Settings > Configuration.\n\nBad or invalid profile settings could have disatrous effects.",
-                                        comment: "Imported Profiles Alert"
-                                    ) :
-                                    NSLocalizedString(fetchedErrors.first?.error ?? "", comment: "Import Error")
-                            ),
-                            primaryButton: .destructive(
-                                Text("OK")
-                            ),
-                            secondaryButton: .cancel()
-                        )
-                    }
-
-                Section {
-                    Toggle("Use local glucose server", isOn: $state.useLocalSource)
-                    HStack {
-                        Text("Port")
-                        DecimalTextField("", value: $state.localPort, formatter: portFormater)
-                    }
-                } header: { Text("Local glucose source") }
+                        .alert(isPresented: $importedHasRun) {
+                            Alert(
+                                title: Text((fetchedErrors.first?.error ?? "").count < 4 ? "Settings imported" : "Import Error"),
+                                message: Text(
+                                    (fetchedErrors.first?.error ?? "").count < 4 ?
+                                        NSLocalizedString(
+                                            "\nNow please verify all of your new settings thoroughly: \n\n • DIA (Pump settings)\n • Basal Profile\n • Insulin Sensitivities\n • Carb Ratios\n • Target Glucose\n\n in Trio Settings -> Configuration.\n\nBad or invalid profile settings could have disastrous effects.",
+                                            comment: "Imported Profiles Alert"
+                                        ) :
+                                        NSLocalizedString(fetchedErrors.first?.error ?? "", comment: "Import Error")
+                                ),
+                                primaryButton: .destructive(
+                                    Text("OK")
+                                ),
+                                secondaryButton: .cancel()
+                            )
+                        }
+                }
                 Section {
                     Button("Backfill glucose") { state.backfillGlucose() }
                         .disabled(state.url.isEmpty || state.connecting || state.backfilling)
+                } header: { Text("Backfill glucose from Nightscout")
                 }
-
-                Section {
-                    Toggle("Remote control", isOn: $state.allowAnnouncements)
-                } header: { Text("Allow Remote control of Trio") }
             }
-            .onAppear(perform: configureView)
             .navigationBarTitle("Nightscout Config")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
             .alert(isPresented: $isImportAlertPresented) {
                 importAlert!
             }
+
+            .onAppear(perform: configureView)
         }
     }
 }

+ 58 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift

@@ -0,0 +1,58 @@
+import SwiftUI
+
+struct NightscoutConnectView: View {
+    @ObservedObject var state: NightscoutConfig.StateModel
+    @State private var portFormater: NumberFormatter
+
+    init(state: NightscoutConfig.StateModel) {
+        self.state = state
+        portFormater = NumberFormatter()
+        portFormater.allowsFloats = false
+    }
+
+    var body: some View {
+        Form {
+            Section {
+                TextField("URL", text: $state.url)
+                    .disableAutocorrection(true)
+                    .textContentType(.URL)
+                    .autocapitalization(.none)
+                    .keyboardType(.URL)
+                SecureField("API secret", text: $state.secret)
+                    .disableAutocorrection(true)
+                    .autocapitalization(.none)
+                    .textContentType(.password)
+                    .keyboardType(.asciiCapable)
+                if !state.message.isEmpty {
+                    Text(state.message)
+                }
+                if state.connecting {
+                    HStack {
+                        Text("Connecting...")
+                        Spacer()
+                        ProgressView()
+                    }
+                }
+            }
+            Section {
+                Button("Connect") { state.connect() }
+                    .disabled(state.url.isEmpty || state.connecting)
+                Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
+            }
+            Section {
+                Button("Open Nightscout") {
+                    UIApplication.shared.open(URL(string: state.url)!, options: [:], completionHandler: nil)
+                }
+                .disabled(state.url.isEmpty || state.connecting)
+            }
+            Section {
+                Toggle("Use local glucose server", isOn: $state.useLocalSource)
+                HStack {
+                    Text("Port")
+                    DecimalTextField("", value: $state.localPort, formatter: portFormater)
+                }
+            } header: { Text("Local glucose source") }
+        }
+        .navigationTitle("Connect")
+    }
+}

+ 35 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift

@@ -0,0 +1,35 @@
+
+import SwiftUI
+
+struct NightscoutFetchView: View {
+    @ObservedObject var state: NightscoutConfig.StateModel
+
+    var body: some View {
+        Form {
+            Section {
+                Toggle("Fetch Treatments", isOn: $state.isDownloadEnabled)
+                    .onChange(of: state.isDownloadEnabled) { newValue in
+                        if !newValue {
+                            state.allowAnnouncements = false
+                        }
+                    }
+            } header: {
+                Text("Allow Fetching from Nightscout")
+            } footer: {
+                Text(
+                    "The Fetch Treatments toggle enables fetching of carbs and temp targets entered in Careportal or by another uploading device than Trio."
+                )
+            }
+            Section {
+                Toggle("Remote Control", isOn: $state.allowAnnouncements)
+                    .disabled(!state.isDownloadEnabled)
+            } header: { Text("Allow Remote control of Trio")
+            } footer: {
+                Text(
+                    "Fetch Treatments needs to be allowed to be able to toggle on Remote Control.\n\nWhen enabled you allow these remote functions through announcements from Nightscout:\n • Suspend/Resume Pump\n • Opening/Closing Loop\n • Set Temp Basal\n • Enact Bolus."
+                )
+            }
+        }
+        .navigationTitle("Fetch and Remote")
+    }
+}

+ 24 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift

@@ -0,0 +1,24 @@
+
+import SwiftUI
+
+struct NightscoutUploadView: View {
+    @ObservedObject var state: NightscoutConfig.StateModel
+
+    var body: some View {
+        Form {
+            Section {
+                Toggle("Upload Treatments and Settings", isOn: $state.isUploadEnabled)
+
+                Toggle("Upload Glucose", isOn: $state.uploadGlucose).disabled(!state.changeUploadGlucose)
+
+            } header: {
+                Text("Allow Uploading to Nightscout")
+            } footer: {
+                Text(
+                    "The Upload Treatments toggle enables uploading of carbs, temp targets, device status, preferences and settings.\n\nThe Upload Glucose enables uploading of CGM readings."
+                )
+            }
+        }
+        .navigationTitle("Upload")
+    }
+}