Ver código fonte

Externalinsulin...again (#4)

* perform determine basal sync after external insulin entry

* logic for progress view

* add custom progress view to add external insulin view

* change appearance of progress view, blur background when progress view is toggled

* add gradient effect to loading bar

* make progress bar grow out of loading area, make animation a tiny bit slower
polscm32 2 anos atrás
pai
commit
2030a959d8

+ 4 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -315,6 +315,7 @@
 		B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */ = {isa = PBXBuildFile; fileRef = B9CAAEFB2AE70836000F68BC /* branch.txt */; };
 		BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C018D1680307A31C9ED7120 /* CGMStateModel.swift */; };
 		BA90041DC8991147E5C8C3AA /* CalibrationsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500371C09F54F89A97D65FDB /* CalibrationsRootView.swift */; };
+		BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1661302B82ADAB00256551 /* CustomProgressView.swift */; };
 		BD188BEC2B1B805B00B183BF /* WidgetBobble.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD188BEB2B1B805A00B183BF /* WidgetBobble.swift */; };
 		BD188BED2B1B805B00B183BF /* WidgetBobble.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD188BEB2B1B805A00B183BF /* WidgetBobble.swift */; };
 		BD2B464E0745FBE7B79913F4 /* NightscoutConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF768BD6264FF7D71D66767 /* NightscoutConfigProvider.swift */; };
@@ -881,6 +882,7 @@
 		B9CAAEFB2AE70836000F68BC /* branch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = branch.txt; sourceTree = SOURCE_ROOT; };
 		BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorDataFlow.swift; sourceTree = "<group>"; };
 		BC210C0F3CB6D3C86E5DED4E /* LibreConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LibreConfigRootView.swift; sourceTree = "<group>"; };
+		BD1661302B82ADAB00256551 /* CustomProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomProgressView.swift; sourceTree = "<group>"; };
 		BD188BEB2B1B805A00B183BF /* WidgetBobble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBobble.swift; sourceTree = "<group>"; };
 		BD2FF19F2AE29D43005D1C5D /* CheckboxToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxToggleStyle.swift; sourceTree = "<group>"; };
 		BD3CC0712B0B89D50013189E /* MainChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainChartView.swift; sourceTree = "<group>"; };
@@ -1762,6 +1764,7 @@
 				CEA4F62229BE10F70011ADF7 /* SavitzkyGolayFilter.swift */,
 				587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */,
 				BD2FF19F2AE29D43005D1C5D /* CheckboxToggleStyle.swift */,
+				BD1661302B82ADAB00256551 /* CustomProgressView.swift */,
 			);
 			path = Helpers;
 			sourceTree = "<group>";
@@ -2990,6 +2993,7 @@
 				711C0CB42CAABE788916BC9D /* ManualTempBasalDataFlow.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
 				F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */,
+				BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */,
 				C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */,
 				FE41E4D429463C660047FD55 /* NightscoutStatistics.swift in Sources */,
 				38E4453B274E411700EC9A94 /* Disk+VolumeInformation.swift in Sources */,

+ 44 - 0
FreeAPS/Sources/Helpers/CustomProgressView.swift

@@ -0,0 +1,44 @@
+import SwiftUI
+
+struct CustomProgressView: View {
+    @State var animate = false
+
+    let text: String
+
+    @Environment(\.colorScheme) var colorScheme
+
+    var body: some View {
+        ZStack {
+            Text(text)
+                .font(.system(.body, design: .rounded))
+                .bold()
+                .offset(x: 0, y: -25)
+
+            RoundedRectangle(cornerRadius: 3)
+                .stroke(Color(.systemGray5), lineWidth: 3)
+                .frame(width: 250, height: 3)
+
+            RoundedRectangle(cornerRadius: 3)
+                .stroke(LinearGradient(colors: [
+                    Color(red: 0.7215686275, green: 0.3411764706, blue: 1),
+                    Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569),
+                    Color(red: 0.4862745098, green: 0.5450980392, blue: 0.9529411765),
+                    Color(red: 0.3411764706, green: 0.6666666667, blue: 0.9254901961),
+                    Color(red: 0.262745098, green: 0.7333333333, blue: 0.9137254902)
+                ], startPoint: .leading, endPoint: .trailing), lineWidth: 3)
+                .frame(width: 250, height: 3)
+                .mask(
+                    RoundedRectangle(cornerRadius: 3)
+                        .frame(width: 80, height: 3)
+                        .offset(x: self.animate ? 180 : -180, y: 0)
+                        .animation(
+                            Animation.linear(duration: 2)
+                                .repeatForever(autoreverses: false), value: UUID()
+                        )
+                )
+        }
+        .onAppear {
+            self.animate.toggle()
+        }
+    }
+}

+ 2 - 2
FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift

@@ -581,7 +581,7 @@ extension Bolus {
                     + " / " +
                     state.isf.formatted()
                     + " ≈ " +
-                self.insulinRounder(state.targetDifferenceInsulin).formatted()
+                    self.insulinRounder(state.targetDifferenceInsulin).formatted()
 
                 Text(secondRow).foregroundColor(.secondary).gridColumnAlignment(.leading)
 
@@ -677,7 +677,7 @@ extension Bolus {
                         + " / " +
                         state.isf.formatted()
                         + " ≈ " +
-                    self.insulinRounder(state.fifteenMinInsulin).formatted()
+                        self.insulinRounder(state.fifteenMinInsulin).formatted()
                 )
                 .foregroundColor(.secondary)
                 .gridColumnAlignment(.leading)

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

@@ -4,6 +4,7 @@ import SwiftUI
 extension DataTable {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
+        @Injected() var apsManager: APSManager!
         @Injected() var unlockmanager: UnlockManager!
         @Injected() private var storage: FileStorage!
         @Injected() var pumpHistoryStorage: PumpHistoryStorage!
@@ -19,6 +20,9 @@ extension DataTable {
         @Published var maxBolus: Decimal = 0
         @Published var externalInsulinAmount: Decimal = 0
         @Published var externalInsulinDate = Date()
+        @Published var waitForSuggestion: Bool = false
+        @Published var showExternalInsulin: Bool = false
+        @Published var addButtonPressed: Bool = false
 
         var units: GlucoseUnits = .mmolL
         var historyLayout: HistoryLayout = .twoTabs
@@ -34,6 +38,7 @@ extension DataTable {
             broadcaster.register(TempTargetsObserver.self, observer: self)
             broadcaster.register(CarbsObserver.self, observer: self)
             broadcaster.register(GlucoseObserver.self, observer: self)
+            broadcaster.register(SuggestionObserver.self, observer: self)
         }
 
         private func setupTreatments() {
@@ -264,6 +269,9 @@ extension DataTable {
 
             // Reset amount to 0 for next entry.
             externalInsulinAmount = 0
+
+            // perform determine basal sync
+            apsManager.determineBasalSync()
         }
     }
 }
@@ -296,3 +304,14 @@ extension DataTable.StateModel:
         setupGlucose()
     }
 }
+
+extension DataTable.StateModel: SuggestionObserver {
+    func suggestionDidUpdate(_: Suggestion) {
+        DispatchQueue.main.async {
+            self.waitForSuggestion = false
+            if self.addButtonPressed {
+                self.showExternalInsulin = false
+            }
+        }
+    }
+}

+ 76 - 64
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -12,7 +12,6 @@ extension DataTable {
         @State private var alertTreatmentToDelete: Treatment?
         @State private var alertGlucoseToDelete: Glucose?
 
-        @State private var showExternalInsulin: Bool = false
         @State private var showFutureEntries: Bool = false // default to hide future entries
         @State private var showManualGlucose: Bool = false
         @State private var isAmountUnconfirmed: Bool = true
@@ -109,7 +108,7 @@ extension DataTable {
                     ToolbarItem(placement: .topBarTrailing) {
                         switch state.mode {
                         case .treatments: addButton({
-                                showExternalInsulin = true
+                                state.showExternalInsulin = true
                                 state.externalInsulinDate = Date()
                             })
                         case .meals: EmptyView()
@@ -123,8 +122,11 @@ extension DataTable {
                 .sheet(isPresented: $showManualGlucose) {
                     addGlucoseView()
                 }
-                .sheet(isPresented: $showExternalInsulin, onDismiss: { if isAmountUnconfirmed { state.externalInsulinAmount = 0
-                    state.externalInsulinDate = Date() } }) {
+                .sheet(
+                    isPresented: $state.showExternalInsulin,
+                    onDismiss: { if isAmountUnconfirmed { state.externalInsulinAmount = 0
+                        state.externalInsulinDate = Date() } }
+                ) {
                     addExternalInsulinView()
                 }
         }
@@ -392,85 +394,95 @@ extension DataTable {
 
         @ViewBuilder func addExternalInsulinView() -> some View {
             NavigationView {
-                VStack {
-                    Form {
-                        Section {
-                            HStack {
-                                Text("Amount")
-                                Spacer()
-                                DecimalTextField(
-                                    "0",
-                                    value: $state.externalInsulinAmount,
-                                    formatter: insulinFormatter,
-                                    autofocus: true,
-                                    cleanInput: true
-                                )
-                                Text("U").foregroundColor(.secondary)
-                            }
-                        }.listRowBackground(Color.chart)
+                ZStack(alignment: .center) {
+                    VStack {
+                        Form {
+                            Section {
+                                HStack {
+                                    Text("Amount")
+                                    Spacer()
+                                    DecimalTextField(
+                                        "0",
+                                        value: $state.externalInsulinAmount,
+                                        formatter: insulinFormatter,
+                                        autofocus: true,
+                                        cleanInput: true
+                                    )
+                                    Text("U").foregroundColor(.secondary)
+                                }
+                            }.listRowBackground(Color.chart)
 
-                        Section {
-                            DatePicker("Date", selection: $state.externalInsulinDate, in: ...Date())
-                        }.listRowBackground(Color.chart)
+                            Section {
+                                DatePicker("Date", selection: $state.externalInsulinDate, in: ...Date())
+                            }.listRowBackground(Color.chart)
 
-                        let amountWarningCondition = (state.externalInsulinAmount > state.maxBolus)
+                            let amountWarningCondition = (state.externalInsulinAmount > state.maxBolus)
 
-                        var listBackgroundColor: Color {
-                            if amountWarningCondition {
-                                return Color.red
-                            } else if state.externalInsulinAmount <= 0 || state.externalInsulinAmount > state.maxBolus * 3 {
-                                return Color(.systemGray4)
-                            } else {
-                                return Color(.systemBlue)
+                            var listBackgroundColor: Color {
+                                if amountWarningCondition {
+                                    return Color.red
+                                } else if state.externalInsulinAmount <= 0 || state.externalInsulinAmount > state.maxBolus * 3 {
+                                    return Color(.systemGray4)
+                                } else {
+                                    return Color(.systemBlue)
+                                }
                             }
-                        }
 
-                        var foregroundColor: Color {
-                            if amountWarningCondition {
-                                return Color.white
-                            } else if state.externalInsulinAmount <= 0 || state.externalInsulinAmount > state.maxBolus * 3 {
-                                return Color.secondary
-                            } else {
-                                return Color.white
+                            var foregroundColor: Color {
+                                if amountWarningCondition {
+                                    return Color.white
+                                } else if state.externalInsulinAmount <= 0 || state.externalInsulinAmount > state.maxBolus * 3 {
+                                    return Color.secondary
+                                } else {
+                                    return Color.white
+                                }
                             }
-                        }
 
-                        Section {
-                            HStack {
-                                Button {
-                                    Task {
-                                        do {
-                                            await state.addExternalInsulin()
-                                            isAmountUnconfirmed = false
-                                            showExternalInsulin = false
+                            Section {
+                                HStack {
+                                    Button {
+                                        Task {
+                                            do {
+                                                await state.addExternalInsulin()
+                                                state.waitForSuggestion = true
+                                                state.addButtonPressed = true
+                                                isAmountUnconfirmed = false
+                                            }
                                         }
+                                    } label: {
+                                        Text("Log external insulin")
                                     }
-                                } label: {
-                                    Text("Log external insulin")
+                                    .foregroundStyle(foregroundColor)
+                                    .frame(maxWidth: .infinity, alignment: .center)
+                                    .disabled(
+                                        state.externalInsulinAmount <= 0 || state.externalInsulinAmount > state.maxBolus * 3
+                                    )
                                 }
-                                .foregroundStyle(foregroundColor)
-                                .frame(maxWidth: .infinity, alignment: .center)
-                                .disabled(
-                                    state.externalInsulinAmount <= 0 || state.externalInsulinAmount > state.maxBolus * 3
-                                )
                             }
-                        }
-                        header: {
-                            if amountWarningCondition
-                            {
-                                Text("⚠️ Warning! The entered insulin amount is greater than your Max Bolus setting!")
+                            header: {
+                                if amountWarningCondition
+                                {
+                                    Text("⚠️ Warning! The entered insulin amount is greater than your Max Bolus setting!")
+                                }
                             }
-                        }
-                        .listRowBackground(listBackgroundColor).tint(.white)
-                    }.scrollContentBackground(.hidden).background(color)
+                            .listRowBackground(listBackgroundColor).tint(.white)
+                        }.scrollContentBackground(.hidden).background(color)
+                    }.blur(radius: state.waitForSuggestion ? 5 : 0)
+
+                    if state.waitForSuggestion {
+                        CustomProgressView(text: "Updating IOB")
+                    }
                 }
                 .onAppear(perform: configureView)
+                .onDisappear {
+                    state.addButtonPressed = false
+                }
                 .navigationTitle("External Insulin")
                 .navigationBarTitleDisplayMode(.inline)
                 .toolbar {
                     ToolbarItem(placement: .topBarLeading) {
                         Button("Close") {
-                            showExternalInsulin = false
+                            state.showExternalInsulin = false
                             state.externalInsulinAmount = 0
                         }
                     }

+ 39 - 31
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -436,37 +436,45 @@ extension MainChartView {
                 ).foregroundStyle(Color.clear)
                 /// temp basal rects
                 ForEach(TempBasals) { temp in
-                                 /// calculate end time of temp basal adding duration to start time
-                                 let end = temp.timestamp + (temp.durationMin ?? 0).minutes.timeInterval
-                                 let now = Date()
-
-                                 /// ensure that temp basals that are set cannot exceed current date -> i.e. scheduled temp basals are not shown
-                                 /// we could display scheduled temp basals with opacity etc... in the future
-                                 let maxEndTime = min(end, now)
-
-                                 /// find next basal entry and if available set end of current entry to start of next entry
-                                 if let nextTemp = TempBasals.first(where: { $0.timestamp > temp.timestamp }) {
-                                     let nextTempStart = nextTemp.timestamp
-
-                                     RectangleMark(
-                                         xStart: .value("start", temp.timestamp),
-                                         xEnd: .value("end", nextTempStart),
-                                         yStart: .value("rate-start", 0),
-                                         yEnd: .value("rate-end", temp.rate ?? 0)
-                                     ).foregroundStyle(Color.insulin.opacity(0.2))
-                                     LineMark(x: .value("Start Date", temp.timestamp), y: .value("Amount", temp.rate ?? 0)).lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
-                                     LineMark(x: .value("End Date", nextTempStart), y: .value("Amount", temp.rate ?? 0)).lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
-                                 } else {
-                                     RectangleMark(
-                                         xStart: .value("start", temp.timestamp),
-                                         xEnd: .value("end", maxEndTime),
-                                         yStart: .value("rate-start", 0),
-                                         yEnd: .value("rate-end", temp.rate ?? 0)
-                                     ).foregroundStyle(Color.insulin.opacity(0.2))
-                                     LineMark(x: .value("Start Date", temp.timestamp), y: .value("Amount", temp.rate ?? 0)).lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
-                                     LineMark(x: .value("End Date", maxEndTime), y: .value("Amount", temp.rate ?? 0)).lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
-                                 }
-                             }
+                    /// calculate end time of temp basal adding duration to start time
+                    let end = temp.timestamp + (temp.durationMin ?? 0).minutes.timeInterval
+                    let now = Date()
+
+                    /// ensure that temp basals that are set cannot exceed current date -> i.e. scheduled temp basals are not shown
+                    /// we could display scheduled temp basals with opacity etc... in the future
+                    let maxEndTime = min(end, now)
+
+                    /// find next basal entry and if available set end of current entry to start of next entry
+                    if let nextTemp = TempBasals.first(where: { $0.timestamp > temp.timestamp }) {
+                        let nextTempStart = nextTemp.timestamp
+
+                        RectangleMark(
+                            xStart: .value("start", temp.timestamp),
+                            xEnd: .value("end", nextTempStart),
+                            yStart: .value("rate-start", 0),
+                            yEnd: .value("rate-end", temp.rate ?? 0)
+                        ).foregroundStyle(Color.insulin.opacity(0.2))
+                        
+                        LineMark(x: .value("Start Date", temp.timestamp), y: .value("Amount", temp.rate ?? 0))
+                            .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+                        
+                        LineMark(x: .value("End Date", nextTempStart), y: .value("Amount", temp.rate ?? 0))
+                            .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+                    } else {
+                        RectangleMark(
+                            xStart: .value("start", temp.timestamp),
+                            xEnd: .value("end", maxEndTime),
+                            yStart: .value("rate-start", 0),
+                            yEnd: .value("rate-end", temp.rate ?? 0)
+                        ).foregroundStyle(Color.insulin.opacity(0.2))
+                        
+                        LineMark(x: .value("Start Date", temp.timestamp), y: .value("Amount", temp.rate ?? 0))
+                            .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+                        
+                        LineMark(x: .value("End Date", maxEndTime), y: .value("Amount", temp.rate ?? 0))
+                            .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+                    }
+                }
                 /// dashed profile line
                 ForEach(BasalProfiles, id: \.self) { profile in
                     LineMark(