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

Delete Insulin from data table with required authorization

Jon Mårtensson 3 лет назад
Родитель
Сommit
948a67e33d

+ 15 - 0
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -14,6 +14,7 @@ protocol PumpHistoryStorage {
     func recent() -> [PumpHistoryEvent]
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment]
     func saveCancelTempEvents()
+    func deleteInsulin(at date: Date)
 }
 
 final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
@@ -194,6 +195,20 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
         storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self)?.reversed() ?? []
     }
 
+    func deleteInsulin(at date: Date) {
+        processQueue.sync {
+            var allValues = storage.retrieve(OpenAPS.Monitor.pumpHistory, as: [PumpHistoryEvent].self) ?? []
+            guard let entryIndex = allValues.firstIndex(where: { $0.timestamp == date }) else {
+                return
+            }
+            allValues.remove(at: entryIndex)
+            storage.save(allValues, as: OpenAPS.Monitor.pumpHistory)
+            broadcaster.notify(PumpHistoryObserver.self, on: processQueue) {
+                $0.pumpHistoryDidUpdate(allValues)
+            }
+        }
+    }
+
     func nightscoutTretmentsNotUploaded() -> [NigtscoutTreatment] {
         let events = recent()
         guard !events.isEmpty else { return [] }

+ 1 - 0
FreeAPS/Sources/Modules/DataTable/DataTableDataFlow.swift

@@ -173,5 +173,6 @@ protocol DataTableProvider: Provider {
     func carbs() -> [CarbsEntry]
     func glucose() -> [BloodGlucose]
     func deleteCarbs(at date: Date)
+    func deleteInsulin(at date: Date)
     func deleteGlucose(id: String)
 }

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

@@ -25,6 +25,10 @@ extension DataTable {
             nightscoutManager.deleteCarbs(at: date)
         }
 
+        func deleteInsulin(at date: Date) {
+            nightscoutManager.deleteInsulin(at: date)
+        }
+
         func glucose() -> [BloodGlucose] {
             glucoseStorage.recent().sorted { $0.date > $1.date }
         }

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

@@ -3,6 +3,8 @@ import SwiftUI
 extension DataTable {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
+        @Injected() var unlockmanager: UnlockManager!
+
         @Published var mode: Mode = .treatments
         @Published var treatments: [Treatment] = []
         @Published var glucose: [Glucose] = []
@@ -92,6 +94,15 @@ extension DataTable {
             provider.deleteCarbs(at: date)
         }
 
+        func deleteInsulin(at date: Date) {
+            unlockmanager.unlock()
+                .sink { _ in } receiveValue: { [weak self] _ in
+                    guard let self = self else { return }
+                    self.provider.deleteInsulin(at: date)
+                }
+                .store(in: &lifetime)
+        }
+
         func deleteGlucose(at index: Int) {
             let id = glucose[index].id
             provider.deleteGlucose(id: id)

+ 26 - 0
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -5,9 +5,13 @@ extension DataTable {
     struct RootView: BaseView {
         let resolver: Resolver
         @StateObject var state = StateModel()
+
         @State private var isRemoveCarbsAlertPresented = false
         @State private var removeCarbsAlert: Alert?
 
+        @State private var isRemoveInsulinAlertPresented = false
+        @State private var removeInsulinAlert: Alert?
+
         private var glucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
@@ -100,6 +104,28 @@ extension DataTable {
                             removeCarbsAlert!
                         }
                 }
+
+                if item.type == .bolus {
+                    Spacer()
+                    Image(systemName: "xmark.circle").foregroundColor(.secondary)
+                        .contentShape(Rectangle())
+                        .padding(.vertical)
+                        .onTapGesture {
+                            removeInsulinAlert = Alert(
+                                title: Text("Delete insulin?"),
+                                message: Text(item.amountText),
+                                primaryButton: .destructive(
+                                    Text("Delete"),
+                                    action: { state.deleteInsulin(at: item.date) }
+                                ),
+                                secondaryButton: .cancel()
+                            )
+                            isRemoveInsulinAlertPresented = true
+                        }
+                        .alert(isPresented: $isRemoveInsulinAlertPresented) {
+                            removeInsulinAlert!
+                        }
+                }
             }
         }
 

+ 1 - 1
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -23,7 +23,7 @@ extension Home {
         // Switch between Loops and Errors when tapping in ststPanel
         @State var loopStatTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
         @State var loopsOrerrors = ""
-        
+
         private var numberFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal

+ 29 - 0
FreeAPS/Sources/Services/Network/NightscoutAPI.swift

@@ -166,6 +166,35 @@ extension NightscoutAPI {
             .eraseToAnyPublisher()
     }
 
+    func deleteInsulin(at date: Date) -> AnyPublisher<Void, Swift.Error> {
+        var components = URLComponents()
+        components.scheme = url.scheme
+        components.host = url.host
+        components.port = url.port
+        components.path = Config.treatmentsPath
+        components.queryItems = [
+            URLQueryItem(name: "find[bolus][$exists]", value: "true"),
+            URLQueryItem(
+                name: "find[created_at][$eq]",
+                value: Formatter.iso8601withFractionalSeconds.string(from: date)
+            )
+        ]
+
+        var request = URLRequest(url: components.url!)
+        request.allowsConstrainedNetworkAccess = false
+        request.timeoutInterval = Config.timeout
+        request.httpMethod = "DELETE"
+
+        if let secret = secret {
+            request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
+        }
+
+        return service.run(request)
+            .retry(Config.retryCount)
+            .map { _ in () }
+            .eraseToAnyPublisher()
+    }
+
     func fetchTempTargets(sinceDate: Date? = nil) -> AnyPublisher<[TempTarget], Swift.Error> {
         var components = URLComponents()
         components.scheme = url.scheme

+ 20 - 0
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -9,6 +9,7 @@ protocol NightscoutManager: GlucoseSource {
     func fetchTempTargets() -> AnyPublisher<[TempTarget], Never>
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never>
     func deleteCarbs(at date: Date)
+    func deleteInsulin(at date: Date)
     func uploadStatus()
     func uploadStatistics(dailystat: Statistics)
     func uploadPreferences()
@@ -180,6 +181,25 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             .store(in: &lifetime)
     }
 
+    func deleteInsulin(at date: Date) {
+        guard let nightscout = nightscoutAPI, isUploadEnabled else {
+            pumpHistoryStorage.deleteInsulin(at: date)
+            return
+        }
+
+        nightscout.deleteInsulin(at: date)
+            .sink { completion in
+                switch completion {
+                case .finished:
+                    self.pumpHistoryStorage.deleteInsulin(at: date)
+                    debug(.nightscout, "Carbs deleted")
+                case let .failure(error):
+                    debug(.nightscout, error.localizedDescription)
+                }
+            } receiveValue: {}
+            .store(in: &lifetime)
+    }
+
     func uploadStatistics(dailystat: Statistics) {
         let stats = NightscoutStatistics(
             dailystats: dailystat