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

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = iAPS
-APP_VERSION = 1.6.5
+APP_VERSION = 2.0.0
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 DEVELOPER_TEAM = ##TEAM_ID##

+ 30 - 3
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -5,7 +5,6 @@
         <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="average_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
     </entity>
     <entity name="BGmedian" representedClassName="BGmedian" syncable="YES" codeGenerationType="class">
@@ -14,7 +13,6 @@
         <attribute name="median_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="median_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
     </entity>
     <entity name="Carbohydrates" representedClassName="Carbohydrates" syncable="YES" codeGenerationType="class">
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0"/>
@@ -27,7 +25,6 @@
         <attribute name="hba1c_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="hba1c_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="hba1c_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
     </entity>
     <entity name="InsulinDistribution" representedClassName="InsulinDistribution" syncable="YES" codeGenerationType="class">
         <attribute name="bolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -47,13 +44,43 @@
         <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
     </entity>
     <entity name="Override" representedClassName="Override" syncable="YES" codeGenerationType="class">
+        <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="end" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="id" optional="YES" attributeType="String"/>
         <attribute name="indefinite" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="100" usesScalarValueType="YES"/>
+        <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbIsOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+        <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="100"/>
+        <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+    </entity>
+    <entity name="OverridePresets" representedClassName="OverridePresets" syncable="YES" codeGenerationType="class">
+        <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="end" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="id" optional="YES" attributeType="String"/>
+        <attribute name="indefinite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <attribute name="name" optional="YES" attributeType="String"/>
+        <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="smbIsOff" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+        <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
     </entity>
     <entity name="Presets" representedClassName="Presets" syncable="YES" codeGenerationType="class">
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


+ 0 - 5
FreeAPS/Sources/APS/APSManager.swift

@@ -757,13 +757,10 @@ final class BaseAPSManager: APSManager, Injectable {
 
                 let sortCarbs = NSSortDescriptor(key: "date", ascending: true)
                 requestCarbs.sortDescriptors = [sortCarbs]
-
                 try? carbs = coredataContext.fetch(requestCarbs)
 
                 carbTotal = carbs.map({ carbs in carbs.carbs as? Decimal ?? 0 }).reduce(0, +)
 
-                // MARK: Fetch TDD from CoreData
-
                 var tdds = [TDD]()
                 var currentTDD: Decimal = 0
                 var tddTotalAverage: Decimal = 0
@@ -1073,8 +1070,6 @@ final class BaseAPSManager: APSManager, Injectable {
                     total: roundDecimal(Decimal(medianBG), 1)
                 )
 
-                // MARK: Save to Median to CoreData
-
                 let saveMedianToCoreData = BGmedian(context: self.coredataContext)
                 saveMedianToCoreData.date = Date()
                 saveMedianToCoreData.median = median.total as NSDecimalNumber

+ 26 - 33
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -124,6 +124,8 @@ final class OpenAPS {
             let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
             var hbt_ = preferences?.halfBasalExerciseTarget ?? 160
             let wp = preferences?.weightPercentage ?? 1
+            let smbMinutes = (preferences?.maxSMBBasalMinutes ?? 30) as NSDecimalNumber
+            let uamMinutes = (preferences?.maxUAMSMBBasalMinutes ?? 30) as NSDecimalNumber
 
             let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
             let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
@@ -146,7 +148,7 @@ final class OpenAPS {
             let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
             let sortOverride = NSSortDescriptor(key: "date", ascending: false)
             requestOverrides.sortDescriptors = [sortOverride]
-            requestOverrides.fetchLimit = 1
+            // requestOverrides.fetchLimit = 1
             try? overrideArray = coredataContext.fetch(requestOverrides)
 
             var tempTargetsArray = [TempTargets]()
@@ -193,6 +195,7 @@ final class OpenAPS {
             if useOverride {
                 duration = (overrideArray.first?.duration ?? 0) as Decimal
                 overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
+                let advancedSettings = overrideArray.first?.advancedSettings ?? false
                 let addedMinutes = Int(duration)
                 let date = overrideArray.first?.date ?? Date()
                 if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
@@ -204,38 +207,11 @@ final class OpenAPS {
                     saveToCoreData.date = Date()
                     saveToCoreData.duration = 0
                     saveToCoreData.indefinite = false
-                    saveToCoreData.percentage = Double(overridePercentage)
-                    try? self.coredataContext.save()
-                } else if overrideArray.first?.indefinite ?? false {
-                    let saveToCoreData = Override(context: self.coredataContext)
-                    saveToCoreData.enabled = true
-                    saveToCoreData.date = Date()
-                    saveToCoreData.duration = 0
-                    saveToCoreData.indefinite = true
-                    saveToCoreData.percentage = Double(overridePercentage)
-                    saveToCoreData.target = overrideTarget as NSDecimalNumber
-                    saveToCoreData.smbIsOff = disableSMBs
-                    try? self.coredataContext.save()
-                } else {
-                    newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
-                    let saveToCoreData = Override(context: self.coredataContext)
-                    saveToCoreData.enabled = true
-                    saveToCoreData.date = Date()
-                    saveToCoreData.duration = newDuration as NSDecimalNumber
-                    saveToCoreData.indefinite = false
-                    saveToCoreData.percentage = Double(overridePercentage)
-                    saveToCoreData.target = overrideTarget as NSDecimalNumber
-                    saveToCoreData.smbIsOff = disableSMBs
+                    saveToCoreData.percentage = 100
                     try? self.coredataContext.save()
                 }
             }
 
-            if newDuration < 0 {
-                newDuration = 0
-            } else {
-                duration = newDuration
-            }
-
             if !useOverride {
                 unlimited = true
                 overridePercentage = 100
@@ -244,7 +220,7 @@ final class OpenAPS {
                 disableSMBs = false
             }
 
-            if temptargetActive /* || isPercentageEnabled */ {
+            if temptargetActive {
                 var duration_ = 0
                 var hbt = Double(hbt_)
                 var dd = 0.0
@@ -258,7 +234,6 @@ final class OpenAPS {
 
                     if dd > 0.1 {
                         hbt_ = Decimal(hbt)
-                        // isPercentageEnabled = false
                         temptargetActive = true
                     } else {
                         temptargetActive = false
@@ -280,7 +255,16 @@ final class OpenAPS {
                     unlimited: unlimited,
                     hbt: hbt_,
                     overrideTarget: overrideTarget,
-                    smbIsOff: disableSMBs
+                    smbIsOff: disableSMBs,
+                    advancedSettings: overrideArray.first?.advancedSettings ?? false,
+                    isfAndCr: overrideArray.first?.isfAndCr ?? false,
+                    isf: overrideArray.first?.isf ?? false,
+                    cr: overrideArray.first?.cr ?? false,
+                    smbIsAlwaysOff: overrideArray.first?.smbIsAlwaysOff ?? false,
+                    start: (overrideArray.first?.start ?? 0) as Decimal,
+                    end: (overrideArray.first?.end ?? 0) as Decimal,
+                    smbMinutes: (overrideArray.first?.smbMinutes ?? smbMinutes) as Decimal,
+                    uamMinutes: (overrideArray.first?.uamMinutes ?? uamMinutes) as Decimal
                 )
                 storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
                 print("Test time for oref2_variables: \(-now.timeIntervalSinceNow) seconds")
@@ -300,7 +284,16 @@ final class OpenAPS {
                     unlimited: unlimited,
                     hbt: hbt_,
                     overrideTarget: overrideTarget,
-                    smbIsOff: disableSMBs
+                    smbIsOff: disableSMBs,
+                    advancedSettings: overrideArray.first?.advancedSettings ?? false,
+                    isfAndCr: overrideArray.first?.isfAndCr ?? false,
+                    isf: overrideArray.first?.isf ?? false,
+                    cr: overrideArray.first?.cr ?? false,
+                    smbIsAlwaysOff: overrideArray.first?.smbIsAlwaysOff ?? false,
+                    start: (overrideArray.first?.start ?? 0) as Decimal,
+                    end: (overrideArray.first?.end ?? 0) as Decimal,
+                    smbMinutes: (overrideArray.first?.smbMinutes ?? smbMinutes) as Decimal,
+                    uamMinutes: (overrideArray.first?.uamMinutes ?? uamMinutes) as Decimal
                 )
                 storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
                 return self.loadFileFromStorage(name: Monitor.oref2_variables)

+ 6 - 0
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings

@@ -1163,6 +1163,12 @@ Enact a temp Basal or a temp target */
 "Override Profiles" = "Override Profiles";
 
 /* */
+"Normal " = "Normal ";
+
+/* */
+"Add / Delete" = "Add / Delete";
+
+/* */
 "Currently no Override active" = "Currently no Override active";
 
 /* */

+ 37 - 1
FreeAPS/Sources/Models/Oref2_variables.swift

@@ -14,6 +14,15 @@ struct Oref2_variables: JSON, Equatable {
     var hbt: Decimal
     var overrideTarget: Decimal
     var smbIsOff: Bool
+    var advancedSettings: Bool
+    var isfAndCr: Bool
+    var isf: Bool
+    var cr: Bool
+    var smbIsAlwaysOff: Bool
+    var start: Decimal
+    var end: Decimal
+    var smbMinutes: Decimal
+    var uamMinutes: Decimal
 
     init(
         average_total_data: Decimal,
@@ -28,7 +37,16 @@ struct Oref2_variables: JSON, Equatable {
         unlimited: Bool,
         hbt: Decimal,
         overrideTarget: Decimal,
-        smbIsOff: Bool
+        smbIsOff: Bool,
+        advancedSettings: Bool,
+        isfAndCr: Bool,
+        isf: Bool,
+        cr: Bool,
+        smbIsAlwaysOff: Bool,
+        start: Decimal,
+        end: Decimal,
+        smbMinutes: Decimal,
+        uamMinutes: Decimal
     ) {
         self.average_total_data = average_total_data
         self.weightedAverage = weightedAverage
@@ -43,6 +61,15 @@ struct Oref2_variables: JSON, Equatable {
         self.hbt = hbt
         self.overrideTarget = overrideTarget
         self.smbIsOff = smbIsOff
+        self.advancedSettings = advancedSettings
+        self.isfAndCr = isfAndCr
+        self.isf = isf
+        self.cr = cr
+        self.smbIsAlwaysOff = smbIsAlwaysOff
+        self.start = start
+        self.end = end
+        self.smbMinutes = smbMinutes
+        self.uamMinutes = uamMinutes
     }
 }
 
@@ -61,5 +88,14 @@ extension Oref2_variables {
         case hbt
         case overrideTarget
         case smbIsOff
+        case advancedSettings
+        case isfAndCr
+        case isf
+        case cr
+        case smbIsAlwaysOff
+        case start
+        case end
+        case smbMinutes
+        case uamMinutes
     }
 }

+ 0 - 1
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -18,7 +18,6 @@ extension AddCarbs {
         @Published var summation: [String] = []
 
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
-        // @Environment(\.managedObjectContext) var moc
 
         override func subscribe() {
             subscribeSetting(\.useFPUconversion, on: $useFPUconversion) { useFPUconversion = $0 }

+ 12 - 1
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -1,4 +1,5 @@
 import Combine
+import CoreData
 import LoopKitUI
 import SwiftDate
 import SwiftUI
@@ -10,7 +11,6 @@ extension Home {
         @Injected() var nightscoutManager: NightscoutManager!
         private let timer = DispatchTimer(timeInterval: 5)
         private(set) var filteredHours = 24
-
         @Published var glucose: [BloodGlucose] = []
         @Published var suggestion: Suggestion?
         @Published var uploadStats = false
@@ -59,6 +59,8 @@ extension Home {
         @Published var displayYgridLines: Bool = false
         @Published var thresholdLines: Bool = false
 
+        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
+
         override func subscribe() {
             setupGlucose()
             setupBasals()
@@ -201,6 +203,15 @@ extension Home {
             apsManager.cancelBolus()
         }
 
+        func cancelProfile() {
+            coredataContext.perform { [self] in
+                let profiles = Override(context: self.coredataContext)
+                profiles.enabled = false
+                profiles.date = Date()
+                try? self.coredataContext.save()
+            }
+        }
+
         private func setupGlucose() {
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }

+ 104 - 23
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -10,20 +10,10 @@ extension Home {
 
         @StateObject var state = StateModel()
         @State var isStatusPopupPresented = false
+        @State var showCancelAlert = false
 
-        // Average/Median/Readings and CV/SD titles and values switches when you tap them
-        @State var averageOrMedianTitle = NSLocalizedString("Average", comment: "")
-        @State var median_ = ""
-        @State var average_ = ""
-        @State var readings = ""
-
-        @State var averageOrmedian = ""
-        @State var CV_or_SD_Title = NSLocalizedString("CV", comment: "CV")
-        @State var cv_ = ""
-        @State var sd_ = ""
-        @State var CVorSD = ""
-        // Switch between Loops and Errors when tapping in statPanel
-        @State var loopStatTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
+        @Environment(\.managedObjectContext) var moc
+        @Environment(\.colorScheme) var colorScheme
 
         @FetchRequest(
             entity: Override.entity(),
@@ -31,6 +21,13 @@ extension Home {
         ) var fetchedPercent: FetchedResults<Override>
 
         @FetchRequest(
+            entity: OverridePresets.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(
+                format: "name != %@", "" as String
+            )
+        ) var fetchedProfiles: FetchedResults<OverridePresets>
+
+        @FetchRequest(
             entity: TempTargets.entity(),
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
         ) var sliderTTpresets: FetchedResults<TempTargets>
@@ -96,8 +93,8 @@ extension Home {
                 Spacer()
             }
             .frame(maxWidth: .infinity)
-            .padding(.top, geo.safeAreaInsets.top)
-            .padding(.bottom, 6)
+            .padding(.top, 10 + geo.safeAreaInsets.top)
+            .padding(.bottom, 10)
             .background(Color.gray.opacity(0.2))
         }
 
@@ -211,10 +208,7 @@ extension Home {
             if sliderTTpresets.first?.active ?? false {
                 let hbt = sliderTTpresets.first?.hbt ?? 0
                 string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
-            } /* else if enactedSliderTT.first?.enabled ?? false {
-                 let hbt = enactedSliderTT.first?.hbt ?? 0
-                 string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
-             } */
+            }
 
             let percentString = state
                 .units == .mmolL ? (unitString + " mmol/L" + string) : (rawString + (string == "0" ? "" : string))
@@ -235,10 +229,27 @@ extension Home {
             var targetString = (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " + unit
             if tempTargetString != nil || target == 0 { targetString = "" }
             percentString = percentString == "100 %" ? "" : percentString
+
+            let duration = (fetchedPercent.first?.duration ?? 0) as Decimal
+            let addedMinutes = Int(duration)
+            let date = fetchedPercent.first?.date ?? Date()
+            var newDuration: Decimal = 0
+
+            if date.addingTimeInterval(addedMinutes.minutes.timeInterval) > Date() {
+                newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
+            }
+
             var durationString = indefinite ?
-                "" : ((tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min")
-            let smbToggleString = (fetchedPercent.first?.smbIsOff ?? false) ? " \u{20e0}" : ""
+                "" : newDuration >= 1 ?
+                (newDuration.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) + " min") :
+                (
+                    newDuration > 0 ? (
+                        newDuration.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) + "min"
+                    ) :
+                        ""
+                )
 
+            let smbToggleString = (fetchedPercent.first?.smbIsOff ?? false) ? " \u{20e0}" : ""
             var comma1 = ", "
             var comma2 = comma1
             var comma3 = comma1
@@ -262,6 +273,10 @@ extension Home {
             if smbToggleString == "" {
                 comma3 = ""
             }
+
+            if durationString == "", !indefinite {
+                return nil
+            }
             return percentString + comma1 + targetString + comma2 + durationString + comma3 + smbToggleString
         }
 
@@ -287,9 +302,9 @@ extension Home {
                 Spacer()
 
                 if let overrideString = overrideString {
-                    Text(overrideString)
+                    Text("👤 " + overrideString)
                         .font(.system(size: 12))
-                        .foregroundColor(.orange)
+                        .foregroundColor(.secondary)
                         .padding(.trailing, 8)
                 }
 
@@ -389,6 +404,71 @@ extension Home {
             .modal(for: .dataTable, from: self)
         }
 
+        @ViewBuilder private func profiles(_: GeometryProxy) -> some View {
+            let colour: Color = colorScheme == .dark ? .black : .white
+            // Rectangle().fill(colour).frame(maxHeight: 1)
+            ZStack {
+                Rectangle().fill(Color.gray.opacity(0.2)).frame(maxHeight: 40)
+                let cancel = fetchedPercent.first?.enabled ?? false
+                HStack(spacing: cancel ? 25 : 15) {
+                    Text(selectedProfile().name).foregroundColor(.secondary)
+                    if cancel, selectedProfile().isOn {
+                        Button { showCancelAlert.toggle() }
+                        label: {
+                            Image(systemName: "xmark")
+                                .foregroundStyle(.secondary)
+                        }
+                    }
+                    Button { state.showModal(for: .overrideProfilesConfig) }
+                    label: {
+                        Image(systemName: "person.3.sequence.fill")
+                            .symbolRenderingMode(.palette)
+                            .foregroundStyle(
+                                !(fetchedPercent.first?.enabled ?? false) ? .green : .cyan,
+                                !(fetchedPercent.first?.enabled ?? false) ? .cyan : .green,
+                                .purple
+                            )
+                    }
+                }
+            }
+            .alert(
+                "Return to Normal?", isPresented: $showCancelAlert,
+                actions: {
+                    Button("No", role: .cancel) {}
+                    Button("Yes", role: .destructive) {
+                        state.cancelProfile()
+                    }
+                }, message: { Text("This will change settings back to your normal profile.") }
+            )
+            Rectangle().fill(colour).frame(maxHeight: 1)
+        }
+
+        private func selectedProfile() -> (name: String, isOn: Bool) {
+            var profileString = ""
+            var display: Bool = false
+
+            let duration = (fetchedPercent.first?.duration ?? 0) as Decimal
+            let indefinite = fetchedPercent.first?.indefinite ?? false
+            let addedMinutes = Int(duration)
+            let date = fetchedPercent.first?.date ?? Date()
+            if date.addingTimeInterval(addedMinutes.minutes.timeInterval) > Date() || indefinite {
+                display.toggle()
+            }
+
+            if fetchedPercent.first?.enabled ?? false, !(fetchedPercent.first?.isPreset ?? false) {
+                profileString = NSLocalizedString("Custom Profile", comment: "Custom but unsaved Profile")
+            } else if !(fetchedPercent.first?.enabled ?? false) || !display {
+                profileString = NSLocalizedString("Normal Profile", comment: "Your normal Profile. Use a short string")
+            } else {
+                let id_ = fetchedPercent.first?.id ?? ""
+                let profile = fetchedProfiles.filter({ $0.id == id_ }).first
+                if profile != nil {
+                    profileString = profile?.name?.description ?? ""
+                }
+            }
+            return (name: profileString, isOn: display)
+        }
+
         @ViewBuilder private func bottomPanel(_ geo: GeometryProxy) -> some View {
             ZStack {
                 Rectangle().fill(Color.gray.opacity(0.2)).frame(height: 50 + geo.safeAreaInsets.bottom)
@@ -473,6 +553,7 @@ extension Home {
                     infoPanel
                     mainChart
                     legendPanel
+                    profiles(geo)
                     bottomPanel(geo)
                 }
                 .edgesIgnoringSafeArea(.vertical)

+ 176 - 2
FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift

@@ -10,11 +10,29 @@ extension OverrideProfilesConfig {
         @Published var target: Decimal = 0
         @Published var override_target: Bool = false
         @Published var smbIsOff: Bool = false
+        @Published var id: String = ""
+        @Published var profileName: String = ""
+        @Published var isPreset: Bool = false
+        @Published var presets: [OverridePresets] = []
+        @Published var selection: OverridePresets?
+        @Published var isPromtPresented: Bool = false
+        @Published var advancedSettings: Bool = false
+        @Published var isfAndCr: Bool = true
+        @Published var isf: Bool = true
+        @Published var cr: Bool = true
+        @Published var smbIsAlwaysOff: Bool = false
+        @Published var start: Decimal = 0
+        @Published var end: Decimal = 23
+        @Published var smbMinutes: Decimal = 0
+        @Published var uamMinutes: Decimal = 0
 
         var units: GlucoseUnits = .mmolL
 
         override func subscribe() {
             units = settingsManager.settings.units
+            smbMinutes = settingsManager.preferences.maxSMBBasalMinutes
+            uamMinutes = settingsManager.preferences.maxUAMSMBBasalMinutes
+            presets = [OverridePresets(context: coredataContext)]
         }
 
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
@@ -25,8 +43,12 @@ extension OverrideProfilesConfig {
                 saveOverride.duration = self.duration as NSDecimalNumber
                 saveOverride.indefinite = self._indefinite
                 saveOverride.percentage = self.percentage
-                saveOverride.enabled = self.isEnabled
+                saveOverride.enabled = true
                 saveOverride.smbIsOff = self.smbIsOff
+                if self.isPreset {
+                    saveOverride.isPreset = true
+                    saveOverride.id = id
+                } else { saveOverride.isPreset = false }
                 saveOverride.date = Date()
                 if override_target {
                     if units == .mmolL {
@@ -34,6 +56,116 @@ extension OverrideProfilesConfig {
                     }
                     saveOverride.target = target as NSDecimalNumber
                 } else { saveOverride.target = 0 }
+
+                if advancedSettings {
+                    saveOverride.advancedSettings = true
+
+                    if !isfAndCr {
+                        saveOverride.isfAndCr = false
+                        saveOverride.isf = isf
+                        saveOverride.cr = cr
+                    } else { saveOverride.isfAndCr = true }
+                    if smbIsAlwaysOff {
+                        saveOverride.smbIsAlwaysOff = true
+                        saveOverride.start = start as NSDecimalNumber
+                        saveOverride.end = end as NSDecimalNumber
+                    } else { saveOverride.smbIsAlwaysOff = false }
+                    if smbMinutes != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        saveOverride.smbMinutes = smbMinutes as NSDecimalNumber
+                    }
+                    if uamMinutes != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        saveOverride.uamMinutes = uamMinutes as NSDecimalNumber
+                    }
+                }
+                try? self.coredataContext.save()
+            }
+        }
+
+        func savePreset() {
+            coredataContext.perform { [self] in
+                let saveOverride = OverridePresets(context: self.coredataContext)
+                saveOverride.duration = self.duration as NSDecimalNumber
+                saveOverride.indefinite = self._indefinite
+                saveOverride.percentage = self.percentage
+                saveOverride.smbIsOff = self.smbIsOff
+                saveOverride.name = self.profileName
+                id = UUID().uuidString
+                self.isPreset.toggle()
+                saveOverride.id = id
+                saveOverride.date = Date()
+                if override_target {
+                    if units == .mmolL {
+                        target = target.asMgdL
+                    }
+                    saveOverride.target = target as NSDecimalNumber
+                } else { saveOverride.target = 0 }
+
+                if advancedSettings {
+                    saveOverride.advancedSettings = true
+
+                    if !isfAndCr {
+                        saveOverride.isfAndCr = false
+                        saveOverride.isf = isf
+                        saveOverride.cr = cr
+                    } else { saveOverride.isfAndCr = true }
+                    if smbIsAlwaysOff {
+                        saveOverride.smbIsAlwaysOff = true
+                        saveOverride.start = start as NSDecimalNumber
+                        saveOverride.end = end as NSDecimalNumber
+                    } else { smbIsAlwaysOff = false }
+                    if smbMinutes != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        saveOverride.smbMinutes = smbMinutes as NSDecimalNumber
+                    }
+                    if uamMinutes != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        saveOverride.uamMinutes = uamMinutes as NSDecimalNumber
+                    }
+                }
+                try? self.coredataContext.save()
+                isPromtPresented = false
+            }
+        }
+
+        func selectProfile(id_: String) {
+            guard id_ != "" else { return }
+            coredataContext.performAndWait {
+                var profileArray = [OverridePresets]()
+                let requestProfiles = OverridePresets.fetchRequest() as NSFetchRequest<OverridePresets>
+                try? profileArray = coredataContext.fetch(requestProfiles)
+
+                guard let profile = profileArray.filter({ $0.id == id_ }).first else { return }
+
+                let saveOverride = Override(context: self.coredataContext)
+                saveOverride.duration = (profile.duration ?? 0) as NSDecimalNumber
+                saveOverride.indefinite = profile.indefinite
+                saveOverride.percentage = profile.percentage
+                saveOverride.enabled = true
+                saveOverride.smbIsOff = profile.smbIsOff
+                saveOverride.isPreset = true
+                saveOverride.date = Date()
+                saveOverride.target = profile.target
+                saveOverride.id = id_
+
+                if profile.advancedSettings {
+                    if !isfAndCr {
+                        saveOverride.isfAndCr = false
+                        saveOverride.isf = profile.isf
+                        saveOverride.cr = profile.cr
+                    } else { saveOverride.isfAndCr = true }
+                    if profile.smbIsAlwaysOff {
+                        saveOverride.smbIsAlwaysOff = true
+                        saveOverride.start = profile.start
+                        saveOverride.end = profile.end
+                    } else { saveOverride.smbIsAlwaysOff = false }
+
+                    let smb = (profile.smbMinutes ?? 30) as Decimal
+                    if smb != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        saveOverride.smbMinutes = profile.smbMinutes
+                    }
+                    let uam = (profile.uamMinutes ?? 30) as Decimal
+                    if uam != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        saveOverride.uamMinutes = profile.uamMinutes
+                    }
+                }
                 try? self.coredataContext.save()
             }
         }
@@ -44,13 +176,37 @@ extension OverrideProfilesConfig {
                 let requestEnabled = Override.fetchRequest() as NSFetchRequest<Override>
                 let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
                 requestEnabled.sortDescriptors = [sortIsEnabled]
-                requestEnabled.fetchLimit = 1
+                // requestEnabled.fetchLimit = 1
                 try? overrideArray = coredataContext.fetch(requestEnabled)
                 isEnabled = overrideArray.first?.enabled ?? false
                 percentage = overrideArray.first?.percentage ?? 100
                 _indefinite = overrideArray.first?.indefinite ?? true
                 duration = (overrideArray.first?.duration ?? 0) as Decimal
                 smbIsOff = overrideArray.first?.smbIsOff ?? false
+                advancedSettings = overrideArray.first?.advancedSettings ?? false
+                isfAndCr = overrideArray.first?.isfAndCr ?? true
+                smbIsAlwaysOff = overrideArray.first?.smbIsAlwaysOff ?? false
+
+                if advancedSettings {
+                    if !isfAndCr {
+                        isf = overrideArray.first?.isf ?? false
+                        cr = overrideArray.first?.cr ?? false
+                    }
+                    if smbIsAlwaysOff {
+                        start = (overrideArray.first?.start ?? 0) as Decimal
+                        end = (overrideArray.first?.end ?? 0) as Decimal
+                    }
+
+                    let smb = (overrideArray.first?.smbMinutes ?? 30) as Decimal
+                    if smb != self.settingsManager.preferences.maxSMBBasalMinutes {
+                        smbMinutes = (overrideArray.first?.smbMinutes ?? 30) as Decimal
+                    }
+                    let uam = (overrideArray.first?.uamMinutes ?? 30) as Decimal
+                    if uam != self.settingsManager.preferences.maxUAMSMBBasalMinutes {
+                        uamMinutes = (overrideArray.first?.uamMinutes ?? 30) as Decimal
+                    }
+                }
+
                 let overrideTarget = (overrideArray.first?.target ?? 0) as Decimal
 
                 var newDuration = Double(duration)
@@ -77,8 +233,26 @@ extension OverrideProfilesConfig {
                     target = 0
                     override_target = false
                     smbIsOff = false
+                    advancedSettings = false
                 }
             }
         }
+
+        func cancelProfile() {
+            _indefinite = true
+            isEnabled = false
+            percentage = 100
+            duration = 0
+            target = 0
+            override_target = false
+            smbIsOff = false
+            advancedSettings = false
+            coredataContext.perform { [self] in
+                let profiles = Override(context: self.coredataContext)
+                profiles.enabled = false
+                profiles.date = Date()
+                try? self.coredataContext.save()
+            }
+        }
     }
 }

+ 238 - 70
FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift

@@ -10,9 +10,17 @@ extension OverrideProfilesConfig {
         @State private var isEditing = false
         @State private var showAlert = false
         @State private var showingDetail = false
-        @State private var isPresented = true
         @State private var alertSring = ""
+
         @Environment(\.dismiss) var dismiss
+        @Environment(\.managedObjectContext) var moc
+
+        @FetchRequest(
+            entity: OverridePresets.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(
+                format: "name != %@", "" as String
+            )
+        ) var fetchedProfiles: FetchedResults<OverridePresets>
 
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -32,79 +40,148 @@ extension OverrideProfilesConfig {
             return formatter
         }
 
+        var presetPopover: some View {
+            Form {
+                Section(header: Text("Enter Profile Name")) {
+                    TextField("Name Of Profile", text: $state.profileName)
+                    Button {
+                        state.savePreset()
+                    }
+
+                    label: { Text("Save") }
+                        .disabled(
+                            state.profileName == "" ||
+                                fetchedProfiles.filter({ $0.name == state.profileName }).isNotEmpty
+                        )
+                    Button {
+                        state.isPromtPresented = false }
+                    label: { Text("Cancel") }
+                }
+            }
+        }
+
         var body: some View {
             Form {
-                Section(
-                    header: Text("Override your Basal, ISF, CR and Target profiles"),
-                    footer: Text("" + (!state.isEnabled ? NSLocalizedString("Currently no Override active", comment: "") : ""))
-                ) {
-                    Toggle(isOn: $state.isEnabled) {
-                        Text("Override Profiles")
-                    }._onBindingChange($state.isEnabled, perform: { _ in
-                        if !state.isEnabled {
-                            state.duration = 0
-                            state.percentage = 100
-                            state._indefinite = true
-                            state.override_target = false
-                            state.saveSettings()
-                        }
-                    })
+                if state.presets.isNotEmpty {
+                    Section {
+                        ForEach(fetchedProfiles) { preset in
+                            profilesView(for: preset)
+                        }.onDelete(perform: removeProfile)
+                    }
                 }
-                if state.isEnabled {
-                    Section(
-                        header: Text("Total Insulin Adjustment"),
-                        footer: Text(
-                            "Your profile basal insulin will be adjusted with the override percentage and your profile ISF and CR will be inversly adjusted with the percentage.\n\nIf you toggle off the override every profile setting will return to normal."
-                        )
-                    ) {
-                        VStack {
-                            Slider(
-                                value: $state.percentage,
-                                in: 10 ... 200,
-                                step: 1,
-                                onEditingChanged: { editing in
-                                    isEditing = editing
-                                }
-                            ).accentColor(state.percentage >= 130 ? .red : .blue)
-                            Text("\(state.percentage.formatted(.number)) %")
-                                .foregroundColor(
-                                    state
-                                        .percentage >= 130 ? .red :
-                                        (isEditing ? .orange : .blue)
-                                )
-                                .font(.largeTitle)
-                            Spacer()
-                            Toggle(isOn: $state._indefinite) {
-                                Text("Enable indefinitely")
+                Section {
+                    VStack {
+                        Slider(
+                            value: $state.percentage,
+                            in: 10 ... 200,
+                            step: 1,
+                            onEditingChanged: { editing in
+                                isEditing = editing
                             }
+                        ).accentColor(state.percentage >= 130 ? .red : .blue)
+                        Text("\(state.percentage.formatted(.number)) %")
+                            .foregroundColor(
+                                state
+                                    .percentage >= 130 ? .red :
+                                    (isEditing ? .orange : .blue)
+                            )
+                            .font(.largeTitle)
+                        Spacer()
+                        Toggle(isOn: $state._indefinite) {
+                            Text("Enable indefinitely")
                         }
-                        if !state._indefinite {
-                            HStack {
-                                Text("Duration")
-                                DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: false)
-                                Text("minutes").foregroundColor(.secondary)
-                            }
+                    }
+                    if !state._indefinite {
+                        HStack {
+                            Text("Duration")
+                            DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: false)
+                            Text("minutes").foregroundColor(.secondary)
                         }
+                    }
 
+                    HStack {
+                        Toggle(isOn: $state.override_target) {
+                            Text("Override Profile Target")
+                        }
+                    }
+                    if state.override_target {
                         HStack {
-                            Toggle(isOn: $state.override_target) {
-                                Text("Override Profile Target")
+                            Text("Target Glucose")
+                            DecimalTextField("0", value: $state.target, formatter: glucoseFormatter, cleanInput: false)
+                            Text(state.units.rawValue).foregroundColor(.secondary)
+                        }
+                    }
+                    HStack {
+                        Toggle(isOn: $state.advancedSettings) {
+                            Text("More options")
+                        }
+                    }
+                    if state.advancedSettings {
+                        HStack {
+                            Toggle(isOn: $state.smbIsOff) {
+                                Text("Disable SMBs")
                             }
                         }
-                        if state.override_target {
+                        HStack {
+                            Toggle(isOn: $state.smbIsAlwaysOff) {
+                                Text("Schedule when SMBs are Off")
+                            }.disabled(!state.smbIsOff)
+                        }
+                        if state.smbIsAlwaysOff {
+                            HStack {
+                                Text("First Hour SMBs are Off (24 hours)")
+                                DecimalTextField("0", value: $state.start, formatter: formatter, cleanInput: false)
+                                Text("hour").foregroundColor(.secondary)
+                            }
                             HStack {
-                                Text("Target Glucose")
-                                DecimalTextField("0", value: $state.target, formatter: glucoseFormatter, cleanInput: false)
-                                Text(state.units.rawValue).foregroundColor(.secondary)
+                                Text("Last Hour SMBs are Off (24 hours)")
+                                DecimalTextField("0", value: $state.end, formatter: formatter, cleanInput: false)
+                                Text("hour").foregroundColor(.secondary)
                             }
                         }
                         HStack {
-                            Toggle(isOn: $state.smbIsOff) {
-                                Text("Disable SMBs")
+                            Toggle(isOn: $state.isfAndCr) {
+                                Text("Change ISF and CR")
+                            }
+                        }
+                        if !state.isfAndCr {
+                            HStack {
+                                Toggle(isOn: $state.isf) {
+                                    Text("Change ISF")
+                                }
+                            }
+                            HStack {
+                                Toggle(isOn: $state.cr) {
+                                    Text("Change CR")
+                                }
                             }
                         }
+                        HStack {
+                            Text("SMB Minutes")
+                            let minutes = state.settingsManager.preferences.maxSMBBasalMinutes
+                            DecimalTextField(
+                                minutes.formatted(),
+                                value: $state.smbMinutes,
+                                formatter: formatter,
+                                cleanInput: false
+                            )
+                            Text("minutes").foregroundColor(.secondary)
+                        }
+                        HStack {
+                            Text("UAM SMB Minutes")
+                            let uam_minutes = state.settingsManager.preferences.maxUAMSMBBasalMinutes
+                            DecimalTextField(
+                                uam_minutes.formatted(),
+                                value: $state.uamMinutes,
+                                formatter: formatter,
+                                cleanInput: false
+                            )
+                            Text("minutes").foregroundColor(.secondary)
+                        }
+                    }
 
-                        Button("Save") {
+                    HStack {
+                        Button("Start new Profile") {
                             showAlert.toggle()
                             alertSring = "\(state.percentage.formatted(.number)) %, " +
                                 (
@@ -126,29 +203,24 @@ extension OverrideProfilesConfig {
                                 +
                                 "\n\n"
                                 +
-                                "Saving this override will change your Profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping save will start your new overide or edit your current active override."
+                                "Starting this override will change your Profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping ”Start” will start your new overide or edit your current active override."
                         }
                         .disabled(
-                            !state
-                                .isEnabled || (state.percentage == 100 && !state.override_target && !state.smbIsOff) ||
-                                (!state._indefinite && state.duration == 0 || (state.override_target && state.target == 0))
+                            (state.percentage == 100 && !state.override_target && !state.smbIsOff) ||
+                                (!state._indefinite && state.duration == 0) || (state.override_target && state.target == 0)
                         )
-                        .accentColor(.orange)
+                        // .tint(.blue)
                         .buttonStyle(BorderlessButtonStyle())
                         .font(.callout)
-                        .frame(maxWidth: .infinity, alignment: .center)
                         .controlSize(.mini)
                         .alert(
-                            "Save Override",
+                            "Start Profile",
                             isPresented: $showAlert,
                             actions: {
-                                Button("Cancel", role: .cancel) {}
+                                Button("Cancel", role: .cancel) { state.isEnabled = false }
                                 Button("Start Override", role: .destructive) {
-                                    if state._indefinite {
-                                        state.duration = 0
-                                    } else if state.duration == 0 {
-                                        state.isEnabled = false
-                                    }
+                                    if state._indefinite { state.duration = 0 }
+                                    state.isEnabled.toggle()
                                     state.saveSettings()
                                     dismiss()
                                 }
@@ -157,11 +229,107 @@ extension OverrideProfilesConfig {
                                 Text(alertSring)
                             }
                         )
+                        Button {
+                            state.isPromtPresented.toggle()
+                        }
+                        label: { Text("Save as Profile") }
+                            .tint(.orange)
+                            .frame(maxWidth: .infinity, alignment: .trailing)
+                            .buttonStyle(BorderlessButtonStyle())
+                            .controlSize(.mini)
+                            .disabled(
+                                (state.percentage == 100 && !state.override_target && !state.smbIsOff) ||
+                                    (!state._indefinite && state.duration == 0) || (state.override_target && state.target == 0)
+                            )
+                    }
+                    .popover(isPresented: $state.isPromtPresented) {
+                        presetPopover
                     }
                 }
+
+                header: { Text("Insulin") }
+                footer: {
+                    Text(
+                        "Your profile basal insulin will be adjusted with the override percentage and your profile ISF and CR will be inversly adjusted with the percentage."
+                    )
+                }
+
+                Button("Return to Normal") {
+                    state.cancelProfile()
+                    dismiss()
+                }
+                .frame(maxWidth: .infinity, alignment: .center)
+                .buttonStyle(BorderlessButtonStyle())
+                .disabled(!state.isEnabled)
+                .tint(.red)
             }
             .onAppear(perform: configureView)
             .onAppear { state.savedSettings() }
+            .navigationBarTitle("Profiles")
+            .navigationBarTitleDisplayMode(.automatic)
+            .navigationBarItems(leading: Button("Close", action: state.hideModal))
+        }
+
+        @ViewBuilder private func profilesView(for preset: OverridePresets) -> some View {
+            let target = state.units == .mmolL ? (((preset.target ?? 0) as NSDecimalNumber) as Decimal)
+                .asMmolL : (preset.target ?? 0) as Decimal
+            let duration = (preset.duration ?? 0) as Decimal
+            let name = ((preset.name ?? "") == "") || (preset.name?.isEmpty ?? true) ? "" : preset.name!
+            let percent = preset.percentage / 100
+            let perpetual = preset.indefinite
+            let durationString = perpetual ? "" : "\(formatter.string(from: duration as NSNumber)!)"
+            let scheduledSMBstring = (preset.smbIsOff && preset.smbIsAlwaysOff) ? "Scheduled SMBs" : ""
+            let smbString = (preset.smbIsOff && scheduledSMBstring == "") ? "SMBs are off" : ""
+            let targetString = target != 0 ? "\(formatter.string(from: target as NSNumber)!)" : ""
+
+            if name != "" {
+                HStack {
+                    VStack {
+                        HStack {
+                            Text(name)
+                            Spacer()
+                        }
+                        HStack(spacing: 5) {
+                            Text(percent.formatted(.percent.grouping(.never).rounded().precision(.fractionLength(0))))
+                                .foregroundColor(.secondary)
+                                .font(.caption)
+                            if targetString != "" {
+                                Text(targetString)
+                                    .foregroundColor(.secondary)
+                                    .font(.caption)
+                                Text(targetString != "" ? state.units.rawValue : "")
+                                    .foregroundColor(.secondary)
+                                    .font(.caption) }
+                            if durationString != "" {
+                                Text(durationString + (perpetual ? "" : "min"))
+                                    .foregroundColor(.secondary)
+                                    .font(.caption) }
+                            if smbString != "" { Text(smbString).foregroundColor(.secondary).font(.caption) }
+                            Text(scheduledSMBstring)
+                                .foregroundColor(.secondary)
+                                .font(.caption)
+                            Spacer()
+                        }.padding(.top, 2)
+                    }
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        state.selectProfile(id_: preset.id ?? "")
+                        state.hideModal()
+                    }
+                }
+            }
+        }
+
+        private func removeProfile(at offsets: IndexSet) {
+            for index in offsets {
+                let language = fetchedProfiles[index]
+                moc.delete(language)
+            }
+            do {
+                try moc.save()
+            } catch {
+                // To do: add error
+            }
         }
     }
 }

+ 0 - 1
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -31,7 +31,6 @@ extension Settings {
                     }
                     Text("Notifications").navigationLink(to: .notificationsConfig, from: self)
                     Text("Fat And Protein Conversion").navigationLink(to: .fpuConfig, from: self)
-                    Text("Profile Override").navigationLink(to: .overrideProfilesConfig, from: self)
                     Text("App Icons").navigationLink(to: .iconConfig, from: self)
                     Text("Statistics and Home View").navigationLink(to: .statisticsConfig, from: self)
                 }