Просмотр исходного кода

Refactor/-style Nightscout setings

Deniz Cengiz 1 год назад
Родитель
Сommit
5d5addc72e

+ 7 - 1
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -33,6 +33,7 @@ extension NightscoutConfig {
         @Published var maxBasal: Decimal = 2
         @Published var maxBolus: Decimal = 10
         @Published var allowAnnouncements: Bool = false
+        @Published var isConnectedToNS: Bool = false
 
         override func subscribe() {
             url = keychain.getValue(String.self, forKey: Config.urlKey) ?? ""
@@ -49,6 +50,8 @@ extension NightscoutConfig {
             subscribeSetting(\.useLocalGlucoseSource, on: $useLocalSource) { useLocalSource = $0 }
             subscribeSetting(\.localGlucosePort, on: $localPort.map(Int.init)) { localPort = Decimal($0) }
             subscribeSetting(\.uploadGlucose, on: $uploadGlucose, initial: { uploadGlucose = $0 })
+
+            isConnectedToNS = nightscoutAPI != nil
         }
 
         func connect() {
@@ -75,6 +78,8 @@ extension NightscoutConfig {
                     self.message = "Connected!"
                     self.keychain.setValue(self.url, forKey: Config.urlKey)
                     self.keychain.setValue(self.secret, forKey: Config.secretKey)
+                    self.connecting = true
+                    self.isConnectedToNS = self.nightscoutAPI != nil
                 }
                 .store(in: &lifetime)
         }
@@ -232,7 +237,7 @@ extension NightscoutConfig {
                         }
                         if sensitivities.filter({ $0.sensitivity <= 0 }).isNotEmpty {
                             error =
-                                "\nInvalid Nightcsout Sensitivities Settings. \n\nImport aborted. Please check your Nightscout Profile Sensitivities Settings!"
+                                "\nInvalid Nightscout Sensitivities Settings. \n\nImport aborted. Please check your Nightscout Profile Sensitivities Settings!"
                             group.leave()
                             return
                         }
@@ -364,6 +369,7 @@ extension NightscoutConfig {
             keychain.removeObject(forKey: Config.secretKey)
             url = ""
             secret = ""
+            isConnectedToNS = false
         }
     }
 }

+ 150 - 68
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -10,6 +10,12 @@ extension NightscoutConfig {
         @State var importAlert: Alert?
         @State var isImportAlertPresented = false
         @State var importedHasRun = 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 {
@@ -45,82 +51,158 @@ extension NightscoutConfig {
 
         var body: some View {
             Form {
-                Section {
-                    NavigationLink("Connect", destination: NightscoutConnectView(state: state))
-                    NavigationLink("Upload", destination: NightscoutUploadView(state: state))
-                    NavigationLink("Fetch and Remote Control", destination: NightscoutFetchView(state: state))
-                }.listRowBackground(Color.chart)
-
                 Section(
-                    header: Text("Import Settings from Nightscout"),
-                    footer: VStack(alignment: .leading, spacing: 2) {
-                        Text(
-                            "Importing settings from Nightscout will overwrite these settings in Trio Settings -> Configuration:"
-                        )
-                        Text(" • ") + Text("DIA (Pump settings)")
-                        Text(" • ") + Text("Basal Profile")
-                        Text(" • ") + Text("Insulin Sensitivities")
-                        Text(" • ") + Text("Carb Ratios")
-                        Text(" • ") + Text("Target Glucose")
+                    header: Text("Nightscout Integration"),
+                    content: {
+                        NavigationLink("Connect", destination: NightscoutConnectView(state: state))
+                        NavigationLink("Upload", destination: NightscoutUploadView(state: state))
+                        NavigationLink("Fetch and Remote Control", destination: NightscoutFetchView(state: state))
                     }
-                ) {
-                    Button("Import settings") {
-                        importAlert = Alert(
-                            title: Text("Import settings?"),
-                            message: Text(
-                                "\n" +
-                                    NSLocalizedString(
-                                        "This will replace some or all of your current pump settings. Are you sure you want to import profile settings from Nightscout?",
-                                        comment: "Profile Import Alert"
-                                    ) +
-                                    "\n"
-                            ),
-                            primaryButton: .destructive(
-                                Text("Yes, Import"),
-                                action: {
-                                    state.importSettings()
-                                    importedHasRun = true
+                ).listRowBackground(Color.chart)
+
+                Section(
+                    header: Text("Tidepool Integration"),
+                    content:
+                    {
+                        VStack {
+                            Button {
+                                importAlert = Alert(
+                                    title: Text("Import settings?"),
+                                    message: Text(
+                                        "\n" +
+                                            NSLocalizedString(
+                                                "This will replace some or all of your current pump settings. Are you sure you want to import profile settings from Nightscout?",
+                                                comment: "Profile Import Alert"
+                                            ) +
+                                            "\n"
+                                    ),
+                                    primaryButton: .destructive(
+                                        Text("Yes, Import"),
+                                        action: {
+                                            state.importSettings()
+                                            importedHasRun = true
+                                        }
+                                    ),
+                                    secondaryButton: .cancel()
+                                )
+                                isImportAlertPresented.toggle()
+                            } label: {
+                                Text("Import Settings")
+                                    .font(.title3) }
+                                .frame(maxWidth: .infinity, alignment: .center)
+                                .buttonStyle(.bordered)
+                                .disabled(state.url.isEmpty || state.connecting)
+                                .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 Rates\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()
+                                    )
                                 }
-                            ),
-                            secondaryButton: .cancel()
-                        )
-                        isImportAlertPresented.toggle()
-                    }
-                    .listRowBackground(Color.chart)
-                    .disabled(state.url.isEmpty || state.connecting)
-                    .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()
-                        )
+
+                            HStack(alignment: .top) {
+                                Text(
+                                    "You can import therapy settings from Nightscout. See hint for more information which settings will be overwritten."
+                                )
+                                .font(.footnote)
+                                .foregroundColor(.secondary)
+                                .lineLimit(nil)
+                                Spacer()
+                                Button(
+                                    action: {
+                                        hintLabel = "Import Settings from Nightscout"
+                                        selectedVerboseHint =
+                                            "Importing settings from Nightscout will overwrite the following Trio therapy settings: \n • DIA (Pump settings) \n • Basal Profile \n • Insulin Sensitivities \n • Carb Ratios \n • Target Glucose"
+                                        shouldDisplayHint.toggle()
+                                    },
+                                    label: {
+                                        HStack {
+                                            Image(systemName: "questionmark.circle")
+                                        }
+                                    }
+                                ).buttonStyle(BorderlessButtonStyle())
+                            }.padding(.top)
+                        }.padding(.vertical)
                     }
-                }
+                ).listRowBackground(Color.chart)
+
+                Section(
+                    content:
+                    {
+                        VStack {
+                            Button {
+                                Task {
+                                    await state.backfillGlucose()
+                                }
+                            } label: {
+                                Text("Backfill Glucose")
+                                    .font(.title3) }
+                                .frame(maxWidth: .infinity, alignment: .center)
+                                .buttonStyle(.bordered)
+                                .disabled(state.url.isEmpty || state.connecting || state.backfilling)
 
-                Section {
-                    Button("Backfill glucose") {
-                        Task {
-                            await state.backfillGlucose()
-                        }
+                            HStack(alignment: .top) {
+                                Text(
+                                    "You can backfill missing glucose data from Nightscout."
+                                )
+                                .font(.footnote)
+                                .foregroundColor(.secondary)
+                                .lineLimit(nil)
+                                Spacer()
+                                Button(
+                                    action: {
+                                        hintLabel = "Backfill Glucose from Nightscout"
+                                        selectedVerboseHint =
+                                            "Explanation… limitation… etc."
+                                        shouldDisplayHint.toggle()
+                                    },
+                                    label: {
+                                        HStack {
+                                            Image(systemName: "questionmark.circle")
+                                        }
+                                    }
+                                ).buttonStyle(BorderlessButtonStyle())
+                            }.padding(.top)
+                        }.padding(.vertical)
                     }
-                    .disabled(state.url.isEmpty || state.connecting || state.backfilling)
-                }
-                header: { Text("Backfill glucose from Nightscout")
-                }.listRowBackground(Color.chart)
+                ).listRowBackground(Color.chart)
+            }
+            .sheet(isPresented: $shouldDisplayHint) {
+                SettingInputHintView(
+                    hintDetent: $hintDetent,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    hintLabel: hintLabel ?? "",
+                    hintText: selectedVerboseHint ?? "",
+                    sheetTitle: "Help"
+                )
+            }
+            .sheet(isPresented: $shouldDisplayHint) {
+                SettingInputHintView(
+                    hintDetent: $hintDetent,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    hintLabel: hintLabel ?? "",
+                    hintText: selectedVerboseHint ?? "",
+                    sheetTitle: "Help"
+                )
             }
-            .navigationBarTitle("Nightscout Config")
+            .navigationBarTitle("Nightscout")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
             .alert(isPresented: $isImportAlertPresented) {
                 importAlert!
             }

+ 69 - 44
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift

@@ -31,57 +31,82 @@ struct NightscoutConnectView: View {
 
     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(
+                header: Text("Connect to Nightscout"),
+                content: {
+                    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()
+                        }
                     }
-                }
-            }.listRowBackground(Color.chart)
-
-            Section {
-                Button("Connect to Nightscout") { state.connect() }
-                    .disabled(state.url.isEmpty || state.connecting)
-                Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
-            }.listRowBackground(Color.chart)
 
-            Section {
-                Button("Open Nightscout") {
-                    UIApplication.shared.open(URL(string: state.url)!, options: [:], completionHandler: nil)
+                    if !state.isConnectedToNS {
+                        Button {
+                            state.connect()
+                        } label: {
+                            Text("Connect to Nightscout")
+                                .font(.title3) }
+                            .frame(maxWidth: .infinity, alignment: .center)
+                            .buttonStyle(.bordered)
+                            .disabled(state.url.isEmpty && state.connecting)
+                    } else {
+                        Button(role: .destructive) {
+                            state.delete()
+                        } label: {
+                            Text("Disconnect and Remove")
+                                .font(.title3)
+                        }
+                        .frame(maxWidth: .infinity, alignment: .center)
+                        .buttonStyle(.bordered)
+                        .tint(Color.loopRed)
+                    }
                 }
-                .disabled(state.url.isEmpty || state.connecting)
-            }.listRowBackground(Color.chart)
+            ).listRowBackground(Color.chart)
 
-            Section {
-                Toggle("Use local glucose server", isOn: $state.useLocalSource)
-                HStack {
-                    Text("Port")
-                    TextFieldWithToolBar(
-                        text: $state.localPort,
-                        placeholder: "",
-                        keyboardType: .numberPad,
-                        numberFormatter: portFormatter,
-                        allowDecimalSeparator: false
-                    )
+            if state.isConnectedToNS {
+                Section {
+                    Button {
+                        UIApplication.shared.open(URL(string: state.url)!, options: [:], completionHandler: nil)
+                    }
+                    label: { Label("Open Nightscout", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding() }
+                        .frame(maxWidth: .infinity, alignment: .center)
+                        .buttonStyle(.bordered)
                 }
-            } header: { Text("Local glucose source") }.listRowBackground(Color.chart)
+                .listRowBackground(Color.clear)
+            }
+
+            // TODO: Find out if this is still required or needed ?!
+//            Section {
+//                Toggle("Use local glucose server", isOn: $state.useLocalSource)
+//                HStack {
+//                    Text("Port")
+//                    TextFieldWithToolBar(
+//                        text: $state.localPort,
+//                        placeholder: "",
+//                        keyboardType: .numberPad,
+//                        numberFormatter: portFormatter,
+//                        allowDecimalSeparator: false
+//                    )
+//                }
+//            } header: { Text("Local glucose source") }.listRowBackground(Color.chart)
         }
         .navigationTitle("Connect")
+        .navigationBarTitleDisplayMode(.automatic)
         .scrollContentBackground(.hidden).background(color)
     }
 }

+ 54 - 27
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift

@@ -4,6 +4,13 @@ import SwiftUI
 struct NightscoutFetchView: View {
     @ObservedObject var state: NightscoutConfig.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(
@@ -24,38 +31,58 @@ struct NightscoutFetchView: View {
 
     var body: some View {
         Form {
-            Section {
-                Toggle("Fetch Treatments", isOn: $state.isDownloadEnabled)
-                    .onChange(of: state.isDownloadEnabled) { newValue in
-                        if !newValue {
-                            state.allowAnnouncements = false
-                        }
+            SettingInputSection(
+                decimalValue: $decimalPlaceholder,
+                booleanValue: $state.isDownloadEnabled,
+                shouldDisplayHint: $shouldDisplayHint,
+                selectedVerboseHint: Binding(
+                    get: { selectedVerboseHint },
+                    set: {
+                        selectedVerboseHint = $0
+                        hintLabel = "Allow Fetching from Nightscout"
                     }
-            } 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."
-                )
-            }.listRowBackground(Color.chart)
+                ),
+                type: .boolean,
+                label: "Allow Fetching from Nightscout",
+                miniHint: "Enable fetching of selected data sets from Nightscout. See hint for more details.",
+                verboseHint: "The Fetch Treatments toggle enables fetching of carbs and temp targets entered in Careportal or by another uploading device than Trio from Nightscout.",
+                headerText: "Remote & Fetch Capabilities"
+            )
 
-            Section(
-                header: Text("Allow Remote control of Trio"),
-                footer: VStack(alignment: .leading, spacing: 2) {
-                    Text("Fetch Treatments needs to be allowed to be able to toggle on Remote Control.")
-                    Text("\nWhen enabled you allow these remote functions through announcements from Nightscout:")
-                    Text(" • ") + Text("Suspend/Resume Pump")
-                    Text(" • ") + Text("Opening/Closing Loop")
-                    Text(" • ") + Text("Set Temp Basal")
-                    Text(" • ") + Text("Enact Bolus")
-                }
+            if state.isDownloadEnabled {
+                SettingInputSection(
+                    decimalValue: $decimalPlaceholder,
+                    booleanValue: $state.allowAnnouncements,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    selectedVerboseHint: Binding(
+                        get: { selectedVerboseHint },
+                        set: {
+                            selectedVerboseHint = $0
+                            hintLabel = "Allow Remote Control of Trio"
+                        }
+                    ),
+                    type: .boolean,
+                    label: "Allow Remote Control of Trio",
+                    miniHint: "Enables selected remote control capabilities via Nightscout. See hint for more details.",
+                    verboseHint: "When 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"
+                )
+            } else {
+                Section {
+                    Text("'Allow Fetching from Nightscout' must be enabled to allow for Trio Remote Control.")
+                }.listRowBackground(Color.tabBar)
+            }
+        }
+        .sheet(isPresented: $shouldDisplayHint) {
+            SettingInputHintView(
+                hintDetent: $hintDetent,
+                shouldDisplayHint: $shouldDisplayHint,
+                hintLabel: hintLabel ?? "",
+                hintText: selectedVerboseHint ?? "",
+                sheetTitle: "Help"
             )
-                {
-                    Toggle("Remote Control", isOn: $state.allowAnnouncements)
-                        .disabled(!state.isDownloadEnabled)
-                }.listRowBackground(Color.chart)
         }
         .navigationTitle("Fetch and Remote")
+        .navigationBarTitleDisplayMode(.automatic)
         .scrollContentBackground(.hidden).background(color)
     }
 }

+ 51 - 16
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift

@@ -1,9 +1,15 @@
-
 import SwiftUI
 
 struct NightscoutUploadView: View {
     @ObservedObject var state: NightscoutConfig.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(
@@ -24,26 +30,55 @@ struct NightscoutUploadView: View {
 
     var body: some View {
         Form {
-            Section(
-                header: Text("Allow Uploading to Nightscout"),
-                footer: VStack(alignment: .leading, spacing: 2) {
-                    Text(
-                        "The Upload Treatments toggle enables uploading of carbs, temp targets, device status, preferences and settings."
-                    )
-                    Text("\nThe Upload Glucose toggle enables uploading of CGM readings.")
-
-                    if !state.changeUploadGlucose {
-                        Text("\nTo flip the Upload Glucose toggle, go to ⚙️ > CGM > CGM Configuration")
+            SettingInputSection(
+                decimalValue: $decimalPlaceholder,
+                booleanValue: $state.isUploadEnabled,
+                shouldDisplayHint: $shouldDisplayHint,
+                selectedVerboseHint: Binding(
+                    get: { selectedVerboseHint },
+                    set: {
+                        selectedVerboseHint = $0
+                        hintLabel = "Allow Uploading to Nightscout"
+                        shouldDisplayHint = true
                     }
-                }
+                ),
+                type: .boolean,
+                label: "Allow Uploading to Nightscout",
+                miniHint: "Enables upload of selected data sets to Nightscout. See hint for more details.",
+                verboseHint: "The Upload Treatments toggle enables uploading of carbs, temp targets, device status, preferences and settings."
             )
-                {
-                    Toggle("Upload Treatments and Settings", isOn: $state.isUploadEnabled)
 
-                    Toggle("Upload Glucose", isOn: $state.uploadGlucose).disabled(!state.changeUploadGlucose)
-                }.listRowBackground(Color.chart)
+            if state.changeUploadGlucose {
+                SettingInputSection(
+                    decimalValue: $decimalPlaceholder,
+                    booleanValue: $state.uploadGlucose,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    selectedVerboseHint: Binding(
+                        get: { selectedVerboseHint },
+                        set: {
+                            selectedVerboseHint = $0
+                            hintLabel = "Upload Glucose"
+                            shouldDisplayHint = true
+                        }
+                    ),
+                    type: .boolean,
+                    label: "Upload Glucose",
+                    miniHint: "Enables uploading of CGM readings to Nightscout.",
+                    verboseHint: "Write stuff here."
+                )
+            }
+        }
+        .sheet(isPresented: $shouldDisplayHint) {
+            SettingInputHintView(
+                hintDetent: $hintDetent,
+                shouldDisplayHint: $shouldDisplayHint,
+                hintLabel: hintLabel ?? "",
+                hintText: selectedVerboseHint ?? "",
+                sheetTitle: "Help"
+            )
         }
         .navigationTitle("Upload")
+        .navigationBarTitleDisplayMode(.automatic)
         .scrollContentBackground(.hidden).background(color)
     }
 }