Procházet zdrojové kódy

Add non-pump insulin dialog
* New entry dialog to add non-pump insulin in history view
* Removes `Add bolus without actually bolusing` functionality from the bolus entry dialog
* New dialog adds ability to back-date insulin entries
* Backdating is limited to past dates up to current date

dnzxy před 2 roky
rodič
revize
824be7a212

+ 5 - 46
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -59,7 +59,6 @@ extension Bolus {
                     }
                 }
                 header: { Text("Recommendation") }
-
                 if !state.waitForSuggestion {
                     Section {
                         HStack {
@@ -79,58 +78,18 @@ extension Bolus {
                     Section {
                         Button { state.add() }
                         label: { Text(!(state.amount > state.maxBolus) ? "Enact bolus" : "Max Bolus exceeded!") }
+                            .frame(maxWidth: .infinity, alignment: .center)
                             .disabled(
                                 state.amount <= 0 || state.amount > state.maxBolus
                             )
                     }
-                    Section {
-                        if waitForSuggestion {
+
+                    if waitForSuggestion {
+                        Section {
                             Button { state.showModal(for: nil) }
-                            label: { Text("Continue without bolus") }
-                        } else {
-                            Button { isAddInsulinAlertPresented = true }
-                            label: { Text("Add insulin without actually bolusing") }
-                                .disabled(state.amount <= 0 || state.amount > state.maxBolus * 3)
+                            label: { Text("Continue without bolus") }.frame(maxWidth: .infinity, alignment: .center)
                         }
                     }
-                    .alert(isPresented: $isAddInsulinAlertPresented) {
-                        let isOverMax = state.amount > state.maxBolus ? true : false
-                        let secondParagrap1 = "Add"
-                        let secondParagraph2 = " U"
-                        let secondParagraph3 = " without bolusing"
-                        let insulinAmount = formatter.string(from: state.amount as NSNumber)!
-
-                        // Actual alert
-                        return Alert(
-                            title: Text(
-                                isOverMax ? "Warning" : "Are you sure?"
-                            ),
-                            message:
-                            Text(
-                                isOverMax ? (
-                                    NSLocalizedString(
-                                        "\nAmount is more than your Max Bolus setting! \nAre you sure you want to add ",
-                                        comment: "Alert"
-                                    ) + insulinAmount +
-                                        NSLocalizedString(secondParagraph2, comment: "Insulin unit") +
-                                        NSLocalizedString(secondParagraph3, comment: "Add insulin without bolusing alert") + "?"
-                                ) :
-                                    NSLocalizedString(secondParagrap1, comment: "Add insulin without bolusing alert") +
-                                    " " +
-                                    insulinAmount +
-                                    NSLocalizedString(secondParagraph2, comment: "Insulin unit") +
-                                    NSLocalizedString(secondParagraph3, comment: "Add insulin without bolusing alert")
-                            ),
-                            primaryButton: .destructive(
-                                Text("Add"),
-                                action: {
-                                    state.addWithoutBolus()
-                                    isAddInsulinAlertPresented = false
-                                }
-                            ),
-                            secondaryButton: .cancel()
-                        )
-                    }
                 }
             }
             .alert(isPresented: $displayError) {

+ 6 - 0
FreeAPS/Sources/Modules/DataTable/DataTableProvider.swift

@@ -13,6 +13,12 @@ extension DataTable {
             pumpHistoryStorage.recent()
         }
 
+        func pumpSettings() -> PumpSettings {
+            storage.retrieve(OpenAPS.Settings.settings, as: PumpSettings.self)
+                ?? PumpSettings(from: OpenAPS.defaults(for: OpenAPS.Settings.settings))
+                ?? PumpSettings(insulinActionCurve: 6, maxBolus: 10, maxBasal: 2)
+        }
+
         func tempTargets() -> [TempTarget] {
             tempTargetsStorage.recent()
         }

+ 39 - 0
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -6,6 +6,7 @@ extension DataTable {
         @Injected() var broadcaster: Broadcaster!
         @Injected() var unlockmanager: UnlockManager!
         @Injected() private var storage: FileStorage!
+        @Injected() var pumpHistoryStorage: PumpHistoryStorage!
         @Injected() var healthKitManager: HealthKitManager!
 
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
@@ -14,11 +15,15 @@ extension DataTable {
         @Published var treatments: [Treatment] = []
         @Published var glucose: [Glucose] = []
         @Published var manualGlcuose: Decimal = 0
+        @Published var maxBolus: Decimal = 0
+        @Published var nonPumpInsulinAmount: Decimal = 0
+        @Published var nonPumpInsulinDate = Date()
 
         var units: GlucoseUnits = .mmolL
 
         override func subscribe() {
             units = settingsManager.settings.units
+            maxBolus = provider.pumpSettings().maxBolus
             setupTreatments()
             setupGlucose()
             broadcaster.register(SettingsObserver.self, observer: self)
@@ -195,6 +200,40 @@ extension DataTable {
             saveToHealth.append(saveToJSON)
             healthKitManager.saveIfNeeded(bloodGlucose: saveToHealth)
         }
+
+        func addNonPumpInsulin() {
+            guard nonPumpInsulinAmount > 0 else {
+                showModal(for: nil)
+                return
+            }
+
+            nonPumpInsulinAmount = min(nonPumpInsulinAmount, maxBolus * 3) // Allow for 3 * Max Bolus for non-pump insulin
+            unlockmanager.unlock()
+                .sink { _ in } receiveValue: { [weak self] _ in
+                    guard let self = self else { return }
+                    pumpHistoryStorage.storeEvents(
+                        [
+                            PumpHistoryEvent(
+                                id: UUID().uuidString,
+                                type: .bolus,
+                                timestamp: nonPumpInsulinDate,
+                                amount: nonPumpInsulinAmount,
+                                duration: nil,
+                                durationMin: nil,
+                                rate: nil,
+                                temp: nil,
+                                carbInput: nil,
+                                isNonPumpInsulin: true
+                            )
+                        ]
+                    )
+                    debug(.default, "Non-pump insulin saved to pumphistory.json")
+
+                    // Reset amount to 0 for next entry.
+                    nonPumpInsulinAmount = 0
+                }
+                .store(in: &lifetime)
+        }
     }
 }
 

+ 119 - 3
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -11,12 +11,21 @@ extension DataTable {
         @State private var removeCarbsAlert: Alert?
         @State private var isRemoveInsulinAlertPresented = false
         @State private var removeInsulinAlert: Alert?
+        @State private var showNonPumpInsulin: Bool = false
+        @State private var isAmountUnconfirmed: Bool = true
         @State private var newGlucose = false
         @State private var isLayered = false
         @FocusState private var isFocused: Bool
 
         @Environment(\.colorScheme) var colorScheme
 
+        private var insulinFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 2
+            return formatter
+        }
+
         private var glucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
@@ -59,12 +68,49 @@ extension DataTable {
             .popup(isPresented: newGlucose, alignment: .center, direction: .top) {
                 addGlucose
             }
+            .sheet(isPresented: $showNonPumpInsulin, onDismiss: { if isAmountUnconfirmed { state.nonPumpInsulinAmount = 0
+                state.nonPumpInsulinDate = Date() } }) {
+                addNonPumpInsulinView
+            }
         }
 
         private var treatmentsList: some View {
-            List {
-                ForEach(state.treatments) { item in
-                    treatmentView(item)
+            Section(
+                header: VStack {
+                    Spacer()
+                    Button(action: { showNonPumpInsulin = true
+                        state.nonPumpInsulinDate = Date() }, label: {
+                        HStack {
+                            Text(
+                                NSLocalizedString("Non-Pump Insulin", comment: "Non-Pump Insulin button text")
+                            )
+                            .foregroundColor(Color.gray)
+                            .font(.body).textCase(.none)
+
+                            Image(systemName: "plus.circle.fill")
+                                .resizable()
+                                .frame(width: 24, height: 24)
+                                .foregroundColor(Color.gray)
+                        }.frame(maxWidth: .infinity, alignment: .trailing)
+
+                    }).buttonStyle(.borderless)
+
+                    Spacer()
+                }
+            ) {
+                List {
+                    if !state.treatments.isEmpty {
+                        ForEach(state.treatments) { item in
+                            treatmentView(item)
+                        }
+                    } else {
+                        HStack {
+                            Text(NSLocalizedString("No data.", comment: "No data text when no entries in history list"))
+                        }
+                    }
+                }
+                .alert(isPresented: $isRemoveInsulinAlertPresented) {
+                    removeInsulinAlert!
                 }
             }
         }
@@ -212,6 +258,76 @@ extension DataTable {
             }
         }
 
+        var addNonPumpInsulinView: some View {
+            NavigationView {
+                VStack {
+                    Form {
+                        Section {
+                            HStack {
+                                Text(NSLocalizedString("Amount", comment: ""))
+                                Spacer()
+                                DecimalTextField(
+                                    "0",
+                                    value: $state.nonPumpInsulinAmount,
+                                    formatter: insulinFormatter,
+                                    autofocus: true,
+                                    cleanInput: true
+                                )
+                                Text("U").foregroundColor(.secondary)
+                            }
+                        }
+
+                        Section {
+                            DatePicker("Date", selection: $state.nonPumpInsulinDate, in: ...Date())
+                        }
+
+                        let amountWarningCondition = (state.nonPumpInsulinAmount > state.maxBolus) &&
+                            (state.nonPumpInsulinAmount <= state.maxBolus * 3)
+
+                        Section {
+                            HStack {
+                                Button {
+                                    state.addNonPumpInsulin()
+                                    isAmountUnconfirmed = false
+                                    showNonPumpInsulin = false
+                                }
+                                label: {
+                                    Text(NSLocalizedString(
+                                        "Log non-pump insulin",
+                                        comment: "Log non-pump insulin button text"
+                                    ))
+                                }
+                                .foregroundColor(amountWarningCondition ? Color.white : Color.accentColor)
+                                .frame(maxWidth: .infinity, alignment: .center)
+                                .disabled(
+                                    state.nonPumpInsulinAmount <= 0 || state.nonPumpInsulinAmount > state
+                                        .maxBolus * 3
+                                )
+                            }
+                        }
+                        header: {
+                            if amountWarningCondition
+                            {
+                                Text(NSLocalizedString(
+                                    "⚠️ Warning! The entered insulin amount is greater than your Max Bolus setting!",
+                                    comment: "Non-pump insulin maxBolus * 3 alert text"
+                                ))
+                            }
+                        }
+                        .listRowBackground(
+                            amountWarningCondition ? Color
+                                .red : colorScheme == .dark ? Color(UIColor.secondarySystemBackground) : Color.white
+                        )
+                    }
+                }
+                .onAppear(perform: configureView)
+                .navigationTitle("Non-Pump Insulin")
+                .navigationBarTitleDisplayMode(.inline)
+                .navigationBarItems(leading: Button("Close", action: { showNonPumpInsulin = false
+                    state.nonPumpInsulinAmount = 0 }))
+            }
+        }
+
         @ViewBuilder private func glucoseView(_ item: Glucose, isManual: BloodGlucose) -> some View {
             VStack(alignment: .leading, spacing: 4) {
                 HStack {