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

Pull in and solve merge conflicts for 3-tab-history

dnzxy 2 лет назад
Родитель
Сommit
d23c171346

+ 14 - 10
FreeAPS.xcodeproj/project.pbxproj

@@ -328,6 +328,7 @@
 		BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 680C4420C9A345D46D90D06C /* ManualTempBasalProvider.swift */; };
 		C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */; };
 		CA370FC152BC98B3D1832968 /* BasalProfileEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8BCB0C37DEB5EC377B9612 /* BasalProfileEditorRootView.swift */; };
+		CC41E29A2B1E1F460070974F /* HistoryLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC41E2992B1E1F460070974F /* HistoryLayout.swift */; };
 		CC6C406E2ACDD69E009B8058 /* RawFetchedProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */; };
 		CD78BB94E43B249D60CC1A1B /* NotificationsConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22963BD06A9C83959D4914E4 /* NotificationsConfigRootView.swift */; };
 		CE1856F52ADC4858007E39C7 /* AddCarbPresetIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1856F42ADC4858007E39C7 /* AddCarbPresetIntent.swift */; };
@@ -889,6 +890,7 @@
 		C19984D62EFC0035A9E9644D /* BolusProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusProvider.swift; sourceTree = "<group>"; };
 		C377490C77661D75E8C50649 /* ManualTempBasalRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalRootView.swift; sourceTree = "<group>"; };
 		C8D1A7CA8C10C4403D4BBFA7 /* BolusDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusDataFlow.swift; sourceTree = "<group>"; };
+		CC41E2992B1E1F460070974F /* HistoryLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryLayout.swift; sourceTree = "<group>"; };
 		CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawFetchedProfile.swift; sourceTree = "<group>"; };
 		CE1856F42ADC4858007E39C7 /* AddCarbPresetIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCarbPresetIntent.swift; sourceTree = "<group>"; };
 		CE1856F62ADC4869007E39C7 /* CarbPresetIntentRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbPresetIntentRequest.swift; sourceTree = "<group>"; };
@@ -1718,6 +1720,7 @@
 				19A910352A24D6D700C8951B /* DateFilter.swift */,
 				193F6CDC2A512C8F001240FD /* Loops.swift */,
 				CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */,
+				CC41E2992B1E1F460070974F /* HistoryLayout.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
@@ -2814,6 +2817,7 @@
 				38E98A2925F52C9300C0CED0 /* Error+Extensions.swift in Sources */,
 				38EA05DA261F6E7C0064E39B /* SimpleLogReporter.swift in Sources */,
 				3811DE6125C9D4D500A708ED /* ViewModifiers.swift in Sources */,
+				CC41E29A2B1E1F460070974F /* HistoryLayout.swift in Sources */,
 				3811DEAC25C9D88300A708ED /* NightscoutManager.swift in Sources */,
 				19A910302A24BF6300C8951B /* StatsView.swift in Sources */,
 				BD7DA9A92AE06E9200601B20 /* BolusCalculatorStateModel.swift in Sources */,
@@ -3290,7 +3294,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
 				DEVELOPMENT_ASSET_PATHS = "";
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				ENABLE_PREVIEWS = YES;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
@@ -3332,7 +3336,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
 				DEVELOPMENT_ASSET_PATHS = "";
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				ENABLE_PREVIEWS = YES;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
@@ -3377,7 +3381,7 @@
 				CODE_SIGN_ENTITLEMENTS = FreeAPSWatch/FreeAPSWatch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				GENERATE_INFOPLIST_FILE = YES;
 				IBSC_MODULE = FreeAPSWatch_WatchKit_Extension;
 				INFOPLIST_FILE = FreeAPSWatch/Info.plist;
@@ -3412,7 +3416,7 @@
 				CODE_SIGN_ENTITLEMENTS = FreeAPSWatch/FreeAPSWatch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				GENERATE_INFOPLIST_FILE = YES;
 				IBSC_MODULE = FreeAPSWatch_WatchKit_Extension;
 				INFOPLIST_FILE = FreeAPSWatch/Info.plist;
@@ -3442,7 +3446,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
 				DEVELOPMENT_ASSET_PATHS = "\"FreeAPSWatch WatchKit Extension/Preview Content\"";
-				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
+				DEVELOPMENT_TEAM = "";
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = "FreeAPSWatch WatchKit Extension/Info.plist";
@@ -3482,7 +3486,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = $APP_BUILD_NUMBER;
 				DEVELOPMENT_ASSET_PATHS = "\"FreeAPSWatch WatchKit Extension/Preview Content\"";
-				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
+				DEVELOPMENT_TEAM = "";
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = "FreeAPSWatch WatchKit Extension/Info.plist";
@@ -3516,7 +3520,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -3537,7 +3541,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -3562,7 +3566,7 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -3596,7 +3600,7 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
+				DEVELOPMENT_TEAM = "";
 				ENABLE_USER_SCRIPT_SANDBOXING = YES;
 				GCC_C_LANGUAGE_STANDARD = gnu17;
 				GENERATE_INFOPLIST_FILE = YES;

+ 1 - 1
FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -30,7 +30,7 @@
       },
       {
         "package": "SwiftCharts",
-        "repositoryURL": "https://github.com/ivanschuetz/SwiftCharts",
+        "repositoryURL": "https://github.com/ivanschuetz/SwiftCharts.git",
         "state": {
           "branch": "master",
           "revision": "c354c1945bb35a1f01b665b22474f6db28cba4a2",

+ 1 - 0
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -50,4 +50,5 @@
   "fattyMealFactor": 0.7,
   "sweetMeals": false,
   "sweetMealFactor": 2
+  "historyLayout": "twoTabs"
 }

+ 5 - 0
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -54,6 +54,7 @@ struct FreeAPSSettings: JSON, Equatable {
     var sweetMealFactor: Decimal = 2
     var displayPredictions: Bool = true
     var useLiveActivity: Bool = false
+    var historyLayout: HistoryLayout = .twoTabs
 }
 
 extension FreeAPSSettings: Decodable {
@@ -279,6 +280,10 @@ extension FreeAPSSettings: Decodable {
             settings.useLiveActivity = useLiveActivity
         }
 
+        if let historyLayout = try? container.decode(HistoryLayout.self, forKey: .historyLayout) {
+            settings.historyLayout = historyLayout
+        }
+
         self = settings
     }
 }

+ 16 - 0
FreeAPS/Sources/Models/HistoryLayout.swift

@@ -0,0 +1,16 @@
+import Foundation
+
+enum HistoryLayout: String, JSON, CaseIterable, Identifiable, Codable {
+    var id: String { rawValue }
+    case twoTabs
+    case threeTabs
+
+    var displayName: String {
+        switch self {
+        case .twoTabs:
+            return NSLocalizedString("2 Tabs", comment: "")
+        case .threeTabs:
+            return NSLocalizedString("3 Tabs", comment: "")
+        }
+    }
+}

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

@@ -6,6 +6,7 @@ enum DataTable {
 
     enum Mode: String, Hashable, Identifiable, CaseIterable {
         case treatments
+        case meals
         case glucose
 
         var id: String { rawValue }
@@ -15,9 +16,12 @@ enum DataTable {
             switch self {
             case .treatments:
                 name = "Treatments"
+            case .meals:
+                name = "Meals"
             case .glucose:
                 name = "Glucose"
             }
+
             return NSLocalizedString(name, comment: "History Mode")
         }
     }

+ 15 - 4
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -14,16 +14,19 @@ extension DataTable {
         @Published var mode: Mode = .treatments
         @Published var treatments: [Treatment] = []
         @Published var glucose: [Glucose] = []
+        @Published var meals: [Treatment] = []
         @Published var manualGlucose: Decimal = 0
         @Published var maxBolus: Decimal = 0
         @Published var externalInsulinAmount: Decimal = 0
         @Published var externalInsulinDate = Date()
 
         var units: GlucoseUnits = .mmolL
+        var historyLayout: HistoryLayout = .twoTabs
 
         override func subscribe() {
             units = settingsManager.settings.units
             maxBolus = provider.pumpSettings().maxBolus
+            historyLayout = settingsManager.settings.historyLayout
             setupTreatments()
             setupGlucose()
             broadcaster.register(SettingsObserver.self, observer: self)
@@ -36,7 +39,6 @@ extension DataTable {
         private func setupTreatments() {
             DispatchQueue.global().async {
                 let units = self.settingsManager.settings.units
-                var date = Date.now
                 let carbs = self.provider.carbs()
                     .filter { !($0.isFPU ?? false) }
                     .map {
@@ -132,9 +134,18 @@ extension DataTable {
                     }
 
                 DispatchQueue.main.async {
-                    self.treatments = [carbs, boluses, tempBasals, tempTargets, suspend, resume, fpus]
-                        .flatMap { $0 }
-                        .sorted { $0.date > $1.date }
+                    if self.historyLayout == .threeTabs {
+                        self.treatments = [boluses, tempBasals, tempTargets, suspend, resume]
+                            .flatMap { $0 }
+                            .sorted { $0.date > $1.date }
+                        self.meals = [carbs, fpus]
+                            .flatMap { $0 }
+                            .sorted { $0.date > $1.date }
+                    } else {
+                        self.treatments = [carbs, fpus, boluses, tempBasals, tempTargets, suspend, resume]
+                            .flatMap { $0 }
+                            .sorted { $0.date > $1.date }
+                    }
                 }
             }
         }

+ 106 - 28
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -60,8 +60,17 @@ extension DataTable {
         var body: some View {
             VStack {
                 Picker("Mode", selection: $state.mode) {
-                    ForEach(Mode.allCases.indexed(), id: \.1) { index, item in
-                        Text(item.name).tag(index)
+                    ForEach(
+                        Mode.allCases.filter({ state.historyLayout == .twoTabs ? $0 != .meals : true }).indexed(),
+                        id: \.1
+                    ) { index, item in
+                        if state.historyLayout == .threeTabs && item == .treatments {
+                            Text("Insulin")
+                                .tag(index)
+                        } else {
+                            Text(item.name)
+                                .tag(index)
+                        }
                     }
                 }
                 .pickerStyle(SegmentedPickerStyle())
@@ -71,6 +80,7 @@ extension DataTable {
                     switch state.mode {
                     case .treatments: treatmentsList
                     case .glucose: glucoseList
+                    case .meals: state.historyLayout == .threeTabs ? AnyView(mealsList) : AnyView(EmptyView())
                     }
                 }.scrollContentBackground(.hidden)
                     .background(color)
@@ -101,28 +111,33 @@ extension DataTable {
                         }.frame(maxWidth: .infinity, alignment: .leading)
                     }).buttonStyle(.borderless)
 
-                    Spacer()
-
-                    Button(action: { showFutureEntries.toggle() }, label: {
-                        HStack {
-                            Text(showFutureEntries ? "Hide Future" : "Show Future")
-                                .foregroundColor(Color.secondary)
-                                .font(.caption)
-                            Image(systemName: showFutureEntries ? "calendar.badge.minus" : "calendar.badge.plus")
-                        }.frame(maxWidth: .infinity, alignment: .trailing)
-                    }).buttonStyle(.borderless)
+                    if state.historyLayout == .twoTabs {
+                        Spacer()
+                        filterEntriesButton
+                    }
                 }
                 if !state.treatments.isEmpty {
-                    if !showFutureEntries {
-                        ForEach(state.treatments.filter { item in
-                            item.date <= Date()
-                        }) { item in
-                            treatmentView(item)
-                        }
-                    } else {
-                        ForEach(state.treatments) { item in
-                            treatmentView(item)
-                        }
+                    ForEach(state.treatments.filter({ !showFutureEntries ? $0.date <= Date() : true })) { item in
+                        treatmentView(item)
+                    }
+                } else {
+                    HStack {
+                        Text("No data.")
+                    }
+                }
+            }
+        }
+
+        private var mealsList: some View {
+            List {
+                HStack {
+                    Text("Type").foregroundStyle(.secondary)
+                    Spacer()
+                    filterEntriesButton
+                }
+                if !state.meals.isEmpty {
+                    ForEach(state.meals.filter({ !showFutureEntries ? $0.date <= Date() : true })) { item in
+                        mealView(item)
                     }
                 } else {
                     HStack {
@@ -211,13 +226,20 @@ extension DataTable {
                 LinearGradient(gradient: Gradient(colors: [Color.gray.opacity(0.1)]), startPoint: .top, endPoint: .bottom)
         }
 
+        private var filterEntriesButton: some View {
+            Button(action: { showFutureEntries.toggle() }, label: {
+                HStack {
+                    Text(showFutureEntries ? "Hide Future" : "Show Future")
+                        .foregroundColor(Color.secondary)
+                        .font(.caption)
+                    Image(systemName: showFutureEntries ? "calendar.badge.minus" : "calendar.badge.plus")
+                }.frame(maxWidth: .infinity, alignment: .trailing)
+            }).buttonStyle(.borderless)
+        }
+
         @ViewBuilder private func treatmentView(_ item: Treatment) -> some View {
             HStack {
-                if item.type == .bolus || item.type == .carbs {
-                    Image(systemName: "circle.fill").foregroundColor(item.color).padding(.vertical)
-                } else {
-                    Image(systemName: "circle.fill").foregroundColor(item.color)
-                }
+                Image(systemName: "circle.fill").foregroundColor(item.color)
                 Text((item.isSMB ?? false) ? "SMB" : item.type.name)
                 Text(item.amountText).foregroundColor(.secondary)
 
@@ -269,7 +291,7 @@ extension DataTable {
                         return
                     }
 
-                    if treatmentToDelete.type == .carbs || treatmentToDelete.type == .fpus {
+                    if state.historyLayout == .twoTabs, treatmentToDelete.type == .carbs || treatmentToDelete.type == .fpus {
                         state.deleteCarbs(treatmentToDelete)
                     } else {
                         state.deleteInsulin(treatmentToDelete)
@@ -280,6 +302,62 @@ extension DataTable {
             }
         }
 
+        @ViewBuilder private func mealView(_ meal: Treatment) -> some View {
+            HStack {
+                Image(systemName: "circle.fill").foregroundColor(meal.color)
+                Text(meal.type.name)
+                Text(meal.amountText).foregroundColor(.secondary)
+
+                if let duration = meal.durationText {
+                    Text(duration).foregroundColor(.secondary)
+                }
+
+                Spacer()
+
+                Text(dateFormatter.string(from: meal.date))
+                    .moveDisabled(true)
+            }.swipeActions {
+                Button(
+                    "Delete",
+                    systemImage: "trash.fill",
+                    role: .none,
+                    action: {
+                        alertTreatmentToDelete = meal
+
+                        if meal.type == .carbs {
+                            alertTitle = "Delete Carbs?"
+                            alertMessage = dateFormatter.string(from: meal.date) + ", " + meal.amountText
+                        } else if meal.type == .fpus {
+                            alertTitle = "Delete Carb Equivalents?"
+                            alertMessage = "All FPUs of the meal will be deleted."
+                        } else {
+                            // item is insulin treatment; item.type == .bolus
+                            alertTitle = "Delete Insulin?"
+                            alertMessage = dateFormatter.string(from: meal.date) + ", " + meal.amountText
+                        }
+
+                        isRemoveHistoryItemAlertPresented = true
+                    }
+                ).tint(.red)
+            }
+            .alert(
+                Text(NSLocalizedString(alertTitle, comment: "")),
+                isPresented: $isRemoveHistoryItemAlertPresented
+            ) {
+                Button("Cancel", role: .cancel) {}
+                Button("Delete", role: .destructive) {
+                    guard let treatmentToDelete = alertTreatmentToDelete else {
+                        debug(.default, "Cannot gracefully unwrap alertTreatmentToDelete!")
+                        return
+                    }
+
+                    state.deleteCarbs(treatmentToDelete)
+                }
+            } message: {
+                Text("\n" + NSLocalizedString(alertMessage, comment: ""))
+            }
+        }
+
         var addExternalInsulinView: some View {
             NavigationView {
                 VStack {

+ 8 - 8
FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift

@@ -42,8 +42,8 @@ struct PumpView: View {
         VStack(alignment: .leading, spacing: 20) {
             if let reservoir = reservoir {
                 HStack {
-                    Image(systemName: "drop.fill")
-                        .font(.system(size: 15))
+                    Image(systemName: "cross.vial.fill")
+                        .font(.system(size: 16))
                         .foregroundColor(reservoirColor)
                     if reservoir == 0xDEAD_BEEF {
                         Text("50+ " + NSLocalizedString("U", comment: "Insulin unit")).font(.system(size: 15))
@@ -52,13 +52,13 @@ struct PumpView: View {
                             reservoirFormatter
                                 .string(from: reservoir as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
                         )
-                        .font(.system(size: 15))
+                        .font(.system(size: 16))
                     }
                 }
 
                 if let timeZone = timeZone, timeZone.secondsFromGMT() != TimeZone.current.secondsFromGMT() {
                     Image(systemName: "clock.badge.exclamationmark.fill")
-                        .font(.system(size: 15))
+                        .font(.system(size: 16))
                         .symbolRenderingMode(.palette)
                         .foregroundStyle(.red, Color(.warning))
                 }
@@ -67,19 +67,19 @@ struct PumpView: View {
             if let battery = battery, battery.display ?? false, expiresAtDate == nil {
                 HStack {
                     Image(systemName: "battery.100")
-                        .font(.system(size: 15))
+                        .font(.system(size: 16))
                         .foregroundColor(batteryColor)
-                    Text("\(Int(battery.percent ?? 100)) %").font(.system(size: 15))
+                    Text("\(Int(battery.percent ?? 100)) %").font(.system(size: 16))
                 }
             }
 
             if let date = expiresAtDate {
                 HStack {
                     Image(systemName: "stopwatch.fill")
-                        .font(.system(size: 15))
+                        .font(.system(size: 16))
                         .foregroundColor(timerColor)
 
-                    Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.system(size: 15))
+                    Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.system(size: 16))
                 }
             }
         }

+ 55 - 44
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -455,25 +455,6 @@ extension Home {
                     .padding([.leading, .trailing], 10)
 
                 HStack {
-                    Button {
-                        state.showModal(for: .dataTable)
-                    }
-                    label: {
-                        if #available(iOS 17.0, *) {
-                            Image(systemName: "book.pages")
-                                .font(.system(size: 24))
-                                .foregroundColor(colorIcon)
-                                .padding(8)
-                        } else {
-                            Image(systemName: "book")
-                                .font(.system(size: 24))
-                                .foregroundColor(colorIcon)
-                                .padding(8)
-                        }
-                    }
-                    .foregroundColor(colorIcon)
-                    .buttonStyle(.borderless)
-                    Spacer()
                     Button { state.showModal(for: .addCarbs(editMode: false, override: false)) }
                     label: {
                         ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
@@ -535,17 +516,36 @@ extension Home {
                     Button {
                         state.showModal(for: .overrideProfilesConfig)
                     } label: {
-                        Image(systemName: "person")
+                        Image(systemName: state.isTempTargetActive || overrideString != nil ? "person.fill" : "person")
                             .font(.system(size: 26))
                             .padding(8)
                     }
                     .foregroundColor((state.isTempTargetActive || (overrideString != nil)) ? Color.purple : colorIcon)
                     .buttonStyle(.borderless)
                     Spacer()
+                    Button {
+                        state.showModal(for: .dataTable)
+                    }
+                    label: {
+                        if #available(iOS 17.0, *) {
+                            Image(systemName: "book.pages")
+                                .font(.system(size: 24))
+                                .foregroundColor(colorIcon)
+                                .padding(8)
+                        } else {
+                            Image(systemName: "book")
+                                .font(.system(size: 24))
+                                .foregroundColor(colorIcon)
+                                .padding(8)
+                        }
+                    }
+                    .foregroundColor(colorIcon)
+                    .buttonStyle(.borderless)
+                    Spacer()
                     Button { state.showModal(for: .statistics)
                     }
                     label: {
-                        Image(systemName: "chart.pie")
+                        Image(systemName: "chart.bar")
                             .font(.system(size: 24))
                             .foregroundColor(colorIcon)
                             .padding(8)
@@ -665,7 +665,7 @@ extension Home {
                 if let eventualBG = state.eventualBG {
                     HStack {
                         Image(systemName: "arrow.right.circle")
-                            .font(.system(size: 14))
+                            .font(.system(size: 16))
                         Text(
                             numberFormatter.string(
                                 from: (
@@ -674,61 +674,70 @@ extension Home {
                                 ) as NSNumber
                             )!
                         )
-                        .font(.system(size: 14))
+                        .font(.system(size: 16))
                     }
                 }
             }
         }
 
         @ViewBuilder func mealPanel(_: GeometryProxy) -> some View {
-            HStack(spacing: 40) {
+            HStack {
                 HStack {
                     Image(systemName: "syringe.fill")
-                        .font(.system(size: 15))
+                        .font(.system(size: 16))
                         .foregroundColor(Color.insulin)
                     Text(
                         (numberFormatter.string(from: (state.suggestion?.iob ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" U", comment: "Insulin unit")
                     )
-                    .font(.system(size: 15))
+                    .font(.system(size: 16, weight: .bold))
                 }
+
+                Spacer()
+
                 HStack {
                     Image(systemName: "fork.knife")
-                        .font(.system(size: 15))
+                        .font(.system(size: 16))
                         .foregroundColor(.loopYellow)
                     Text(
                         (numberFormatter.string(from: (state.suggestion?.cob ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" g", comment: "gram of carbs")
                     )
-                    .font(.system(size: 15))
+                    .font(.system(size: 16, weight: .bold))
                 }
+
+                Spacer()
+
                 HStack {
                     if state.pumpSuspended {
                         Text("Pump suspended")
-                            .font(.system(size: 12)).foregroundColor(.loopGray)
+                            .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
                     } else if let tempBasalString = tempBasalString {
+                        Image(systemName: "drop.circle")
+                            .font(.system(size: 16))
+                            .foregroundColor(.insulinTintColor)
                         Text(tempBasalString)
-                            .font(.system(size: 15))
-                            .foregroundColor(.insulin)
+                            .font(.system(size: 16, weight: .bold))
                     }
                 }
                 if !state.tins {
+                    Spacer()
+                    Text(
+                        "TDD: " + (numberFormatter.string(from: (state.suggestion?.tdd ?? 0) as NSNumber) ?? "0") +
+                            NSLocalizedString(" U", comment: "Insulin unit")
+                    )
+                    .font(.system(size: 16, weight: .bold))
+                } else {
+                    Spacer()
                     HStack {
                         Text(
-                            "TDD: " +
-                                (numberFormatter.string(from: (state.suggestion?.tdd ?? 0) as NSNumber) ?? "--") +
-                                NSLocalizedString(" U", comment: "Insulin unit")
-                        ).font(.system(size: 15))
+                            "TINS: \(state.calculateTINS())" +
+                                NSLocalizedString(" U", comment: "Unit in number of units delivered (keep the space character!)")
+                        )
+                        .font(.system(size: 16, weight: .bold))
                     }
-                } else {
-                    Text(
-                        "TINS: \(state.calculateTINS())" +
-                            NSLocalizedString(" U", comment: "Unit in number of units delivered (keep the space character!)")
-                    )
-                    .font(.system(size: 15))
-                    .foregroundColor(.insulin)
                 }
-            }
+            }.padding(.horizontal, 10)
         }
 
         @ViewBuilder func profileView(_: GeometryProxy) -> some View {
@@ -844,7 +853,9 @@ extension Home {
 
                     }.padding(.top, 40)
 
-                    mealPanel(geo).padding(.top, 20)
+                    Spacer()
+
+                    mealPanel(geo)
 
                     Spacer()
 

+ 3 - 0
FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift

@@ -12,6 +12,8 @@ extension StatConfig {
         @Published var skipBolusScreenAfterCarbs: Bool = false
         @Published var useFPUconversion: Bool = true
         @Published var tins: Bool = false
+        @Published var historyLayout: HistoryLayout = .twoTabs
+
         var units: GlucoseUnits = .mmolL
 
         override func subscribe() {
@@ -26,6 +28,7 @@ extension StatConfig {
             subscribeSetting(\.tins, on: $tins) { tins = $0 }
             subscribeSetting(\.skipBolusScreenAfterCarbs, on: $skipBolusScreenAfterCarbs) { skipBolusScreenAfterCarbs = $0 }
             subscribeSetting(\.oneDimensionalGraph, on: $oneDimensionalGraph) { oneDimensionalGraph = $0 }
+            subscribeSetting(\.historyLayout, on: $historyLayout) { historyLayout = $0 }
 
             subscribeSetting(\.low, on: $low, initial: {
                 let value = max(min($0, 90), 40)

+ 11 - 0
FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift

@@ -55,6 +55,17 @@ extension StatConfig {
                     Toggle("Skip Bolus screen after carbs", isOn: $state.skipBolusScreenAfterCarbs)
                     Toggle("Display and allow Fat and Protein entries", isOn: $state.useFPUconversion)
                 } header: { Text("Add Meal View settings ") }
+
+                Section {
+                    Picker(
+                        selection: $state.historyLayout,
+                        label: Text("History Layout")
+                    ) {
+                        ForEach(HistoryLayout.allCases) { selection in
+                            Text(selection.displayName).tag(selection)
+                        }
+                    }
+                } header: { Text("History Settings") }
             }
             .onAppear(perform: configureView)
             .navigationBarTitle("UI/UX")