瀏覽代碼

Merge pull request #488 from nightscout/onboarding-adjustments

Onboarding Part 5 — Adjustments (default settings, texts, hints)
Sam King 1 年之前
父節點
當前提交
908ceac21b
共有 30 個文件被更改,包括 428 次插入116 次删除
  1. 12 4
      Trio.xcodeproj/project.pbxproj
  2. 1 1
      Trio/Resources/json/defaults/settings/basal_profile.json
  3. 2 2
      Trio/Resources/json/defaults/settings/bg_targets.json
  4. 1 1
      Trio/Resources/json/defaults/settings/carb_ratios.json
  5. 1 1
      Trio/Resources/json/defaults/settings/insulin_sensitivities.json
  6. 11 5
      Trio/Sources/Application/TrioApp.swift
  7. 83 0
      Trio/Sources/Localizations/Main/Localizable.xcstrings
  8. 1 1
      Trio/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift
  9. 6 9
      Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift
  10. 11 8
      Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift
  11. 1 1
      Trio/Sources/Modules/Home/View/HomeRootView.swift
  12. 4 4
      Trio/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift
  13. 128 16
      Trio/Sources/Modules/Onboarding/OnboardingStateModel.swift
  14. 34 5
      Trio/Sources/Modules/Onboarding/View/OnboardingRootView.swift
  15. 17 17
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/AlgorithmSettings/AlgorithmSettingsSubstepView.swift
  16. 7 4
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/Nightscout/NightscoutImportStepView.swift
  17. 14 0
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/Nightscout/NightscoutSetupStepView.swift
  18. 29 1
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/StartupGuide/StartupReturningUserStepView.swift
  19. 0 0
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/TherapySettings/BasalProfileStepView.swift
  20. 0 0
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/TherapySettings/CarbRatioStepView.swift
  21. 0 0
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/TherapySettings/GlucoseTargetStepView.swift
  22. 8 10
      Trio/Sources/Modules/Onboarding/View/OnboardingSteps/InsulinSensitivityStepView.swift
  23. 13 10
      Trio/Sources/Modules/Onboarding/View/OnboardingView+AlgorithmUtil.swift
  24. 12 8
      Trio/Sources/Modules/Onboarding/View/OnboardingView+Util.swift
  25. 7 0
      Trio/Sources/Modules/Onboarding/View/TherapySettingEditorView.swift
  26. 2 2
      Trio/Sources/Modules/PumpConfig/View/PumpConfigRootView.swift
  27. 8 2
      Trio/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift
  28. 12 1
      Trio/Sources/Modules/Settings/SettingItems.swift
  29. 2 2
      Trio/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift
  30. 1 1
      Trio/Sources/Modules/TargetsEditor/TargetsEditorStateModel.swift

+ 12 - 4
Trio.xcodeproj/project.pbxproj

@@ -2835,6 +2835,7 @@
 		BD47FDD52D8B64AE0043966B /* OnboardingSteps */ = {
 		BD47FDD52D8B64AE0043966B /* OnboardingSteps */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				DD3C47E42DC6CD8C003DD20D /* TherapySettings */,
 				DD6A4E4E2DBEBC7B008C4B26 /* StartupGuide */,
 				DD6A4E4E2DBEBC7B008C4B26 /* StartupGuide */,
 				DDFF20302DB1D15500AB8A96 /* BluetoothPermissionStepView.swift */,
 				DDFF20302DB1D15500AB8A96 /* BluetoothPermissionStepView.swift */,
 				DDFF202E2DB1D14500AB8A96 /* NotificationPermissionStepView.swift */,
 				DDFF202E2DB1D14500AB8A96 /* NotificationPermissionStepView.swift */,
@@ -2846,10 +2847,6 @@
 				DD3F1F842D9DD83B00DCE7B3 /* DeliveryLimitsStepView.swift */,
 				DD3F1F842D9DD83B00DCE7B3 /* DeliveryLimitsStepView.swift */,
 				DD3F1F822D9DC78300DCE7B3 /* UnitSelectionStepView.swift */,
 				DD3F1F822D9DC78300DCE7B3 /* UnitSelectionStepView.swift */,
 				BD47FD162D88AAEF0043966B /* CompletedStepView.swift */,
 				BD47FD162D88AAEF0043966B /* CompletedStepView.swift */,
-				BD47FDDC2D8B65AD0043966B /* GlucoseTargetStepView.swift */,
-				BD47FDDA2D8B65960043966B /* BasalProfileStepView.swift */,
-				BD47FDD82D8B65730043966B /* InsulinSensitivityStepView.swift */,
-				BD47FDD62D8B64CC0043966B /* CarbRatioStepView.swift */,
 			);
 			);
 			path = OnboardingSteps;
 			path = OnboardingSteps;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -3300,6 +3297,17 @@
 			path = Helper;
 			path = Helper;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		DD3C47E42DC6CD8C003DD20D /* TherapySettings */ = {
+			isa = PBXGroup;
+			children = (
+				BD47FDDC2D8B65AD0043966B /* GlucoseTargetStepView.swift */,
+				BD47FDDA2D8B65960043966B /* BasalProfileStepView.swift */,
+				BD47FDD82D8B65730043966B /* InsulinSensitivityStepView.swift */,
+				BD47FDD62D8B64CC0043966B /* CarbRatioStepView.swift */,
+			);
+			path = TherapySettings;
+			sourceTree = "<group>";
+		};
 		DD3F1F8E2D9E151200DCE7B3 /* Nightscout */ = {
 		DD3F1F8E2D9E151200DCE7B3 /* Nightscout */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (

+ 1 - 1
Trio/Resources/json/defaults/settings/basal_profile.json

@@ -2,6 +2,6 @@
     {
     {
         "start": "00:00:00",
         "start": "00:00:00",
         "minutes": 0,
         "minutes": 0,
-        "rate": 1.0
+        "rate": 0.1
     }
     }
 ]
 ]

+ 2 - 2
Trio/Resources/json/defaults/settings/bg_targets.json

@@ -3,8 +3,8 @@
     "user_preferred_units": "mg/dL",
     "user_preferred_units": "mg/dL",
     "targets": [
     "targets": [
         {
         {
-            "low": 100,
-            "high": 100,
+            "low": 110,
+            "high": 110,
             "start": "00:00:00",
             "start": "00:00:00",
             "offset": 0
             "offset": 0
         }
         }

+ 1 - 1
Trio/Resources/json/defaults/settings/carb_ratios.json

@@ -4,7 +4,7 @@
         {
         {
             "start": "00:00:00",
             "start": "00:00:00",
             "offset": 0,
             "offset": 0,
-            "ratio": 10
+            "ratio": 30
         }
         }
     ]
     ]
 }
 }

+ 1 - 1
Trio/Resources/json/defaults/settings/insulin_sensitivities.json

@@ -3,7 +3,7 @@
     "user_preferred_units": "mg/dL",
     "user_preferred_units": "mg/dL",
     "sensitivities": [
     "sensitivities": [
         {
         {
-            "sensitivity": 54,
+            "sensitivity": 200,
             "offset": 0,
             "offset": 0,
             "start": "00:00:00"
             "start": "00:00:00"
         }
         }

+ 11 - 5
Trio/Sources/Application/TrioApp.swift

@@ -22,9 +22,10 @@ extension Notification.Name {
     let onboardingManager = OnboardingManager.shared
     let onboardingManager = OnboardingManager.shared
 
 
     class InitState {
     class InitState {
-        var complete = false
-        var error = false
+        var complete: Bool = false
+        var error: Bool = false
         var migrationErrors: [String] = []
         var migrationErrors: [String] = []
+        var migrationFailed: Bool = false
     }
     }
 
 
     // We use both InitState and @State variables to track coreDataStack
     // We use both InitState and @State variables to track coreDataStack
@@ -207,6 +208,7 @@ extension Notification.Name {
         }
         }
 
 
         initState.migrationErrors = importErrors
         initState.migrationErrors = importErrors
+        initState.migrationFailed = importErrors.isNotEmpty
     }
     }
 
 
     /// Clears any legacy (Trio 0.2.x) delivered and pending notifications related to non-looping alerts.
     /// Clears any legacy (Trio 0.2.x) delivered and pending notifications related to non-looping alerts.
@@ -299,9 +301,13 @@ extension Notification.Name {
                     }
                     }
                 } else if onboardingManager.shouldShowOnboarding {
                 } else if onboardingManager.shouldShowOnboarding {
                     // Show onboarding if needed
                     // Show onboarding if needed
-                    Onboarding.RootView(resolver: resolver, onboardingManager: onboardingManager)
-                        .preferredColorScheme(colorScheme(for: .dark) ?? nil)
-                        .transition(.opacity)
+                    Onboarding.RootView(
+                        resolver: resolver,
+                        onboardingManager: onboardingManager,
+                        wasMigrationSuccessful: !initState.migrationFailed
+                    )
+                    .preferredColorScheme(colorScheme(for: .dark) ?? nil)
+                    .transition(.opacity)
                 } else {
                 } else {
                     Main.RootView(resolver: resolver)
                     Main.RootView(resolver: resolver)
                         .preferredColorScheme(colorScheme(for: colorSchemePreference) ?? nil)
                         .preferredColorScheme(colorScheme(for: colorSchemePreference) ?? nil)

+ 83 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -7309,6 +7309,16 @@
         }
         }
       }
       }
     },
     },
+    "%@ %@ / %@ %@/%@ = %@ %@" : {
+      "localizations" : {
+        "en" : {
+          "stringUnit" : {
+            "state" : "new",
+            "value" : "%1$@ %2$@ / %3$@ %4$@/%5$@ = %6$@ %7$@"
+          }
+        }
+      }
+    },
     "%@ %%" : {
     "%@ %%" : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -10039,6 +10049,7 @@
       }
       }
     },
     },
     "• Basal Rates" : {
     "• Basal Rates" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -10340,6 +10351,7 @@
       }
       }
     },
     },
     "• Carb Ratios" : {
     "• Carb Ratios" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -13042,6 +13054,7 @@
       }
       }
     },
     },
     "• Glucose Targets" : {
     "• Glucose Targets" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -13543,6 +13556,7 @@
       }
       }
     },
     },
     "• Insulin Sensitivities" : {
     "• Insulin Sensitivities" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -14844,6 +14858,7 @@
       }
       }
     },
     },
     "• Omnipod Dash" : {
     "• Omnipod Dash" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -14943,6 +14958,9 @@
         }
         }
       }
       }
     },
     },
+    "• Omnipod DASH" : {
+
+    },
     "• Omnipod Eros" : {
     "• Omnipod Eros" : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -33759,6 +33777,7 @@
       }
       }
     },
     },
     "Allow SMB when glucose is above the High BG Target value." : {
     "Allow SMB when glucose is above the High BG Target value." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -42993,6 +43012,9 @@
     "Autosens Max sets the maximum Sensitivity Ratio used by Autosens, Dynamic ISF, and Sigmoid Formula." : {
     "Autosens Max sets the maximum Sensitivity Ratio used by Autosens, Dynamic ISF, and Sigmoid Formula." : {
 
 
     },
     },
+    "Autosens Maximum" : {
+      "comment" : "Autosens Max"
+    },
     "Autosens Min" : {
     "Autosens Min" : {
       "comment" : "Autosens Min",
       "comment" : "Autosens Min",
       "localizations" : {
       "localizations" : {
@@ -43197,6 +43219,9 @@
     "Autosens Min sets the minimum Sensitivity Ratio used by Autosens, Dynamic ISF, and Sigmoid Formula." : {
     "Autosens Min sets the minimum Sensitivity Ratio used by Autosens, Dynamic ISF, and Sigmoid Formula." : {
 
 
     },
     },
+    "Autosens Minimum" : {
+      "comment" : "Autosens Min"
+    },
     "Autosens modifies Insulin Sensitivity Factor (ISF), basal rates, and target blood sugar levels. It doesn’t account for carbs but adjusts for insulin effectiveness based on patterns in your glucose data." : {
     "Autosens modifies Insulin Sensitivity Factor (ISF), basal rates, and target blood sugar levels. It doesn’t account for carbs but adjusts for insulin effectiveness based on patterns in your glucose data." : {
       "extractionState" : "stale",
       "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
@@ -89434,6 +89459,7 @@
     },
     },
     "Enable SMB With High BG" : {
     "Enable SMB With High BG" : {
       "comment" : "Enable SMB With High BG",
       "comment" : "Enable SMB With High BG",
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -90246,6 +90272,9 @@
         }
         }
       }
       }
     },
     },
+    "Enable UAM (Unannounced Meals)" : {
+      "comment" : "Enable UAM"
+    },
     "Enable Unannounced Meals SMB." : {
     "Enable Unannounced Meals SMB." : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -91467,6 +91496,7 @@
       }
       }
     },
     },
     "Enabling this feature allows Trio to deliver insulin required using Super Micro Boluses (SMB) when glucose reading is above the value set as High BG Target." : {
     "Enabling this feature allows Trio to deliver insulin required using Super Micro Boluses (SMB) when glucose reading is above the value set as High BG Target." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -121615,6 +121645,9 @@
         }
         }
       }
       }
     },
     },
+    "Information" : {
+
+    },
     "Information We Collect" : {
     "Information We Collect" : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -134886,6 +134919,7 @@
 
 
     },
     },
     "Lower target glucose when Autosens Ratio is <1." : {
     "Lower target glucose when Autosens Ratio is <1." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -134986,6 +135020,7 @@
       }
       }
     },
     },
     "Lower target glucose when Autosens Ratio is >1." : {
     "Lower target glucose when Autosens Ratio is >1." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -135085,6 +135120,9 @@
         }
         }
       }
       }
     },
     },
+    "Lower target glucose when Autosens Ratio is greater than 1." : {
+
+    },
     "m" : {
     "m" : {
       "comment" : "Abbreviation for Minutes\nabbreviation for minutes",
       "comment" : "Abbreviation for Minutes\nabbreviation for minutes",
       "localizations" : {
       "localizations" : {
@@ -136919,6 +136957,7 @@
     },
     },
     "Max Bolus" : {
     "Max Bolus" : {
       "comment" : "Max setting",
       "comment" : "Max setting",
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -138047,6 +138086,7 @@
     },
     },
     "Max Delta-BG Threshold SMB" : {
     "Max Delta-BG Threshold SMB" : {
       "comment" : "Max Delta-BG Threshold",
       "comment" : "Max Delta-BG Threshold",
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -139877,6 +139917,9 @@
         }
         }
       }
       }
     },
     },
+    "Max. Allowed Glucose Rise for SMB" : {
+      "comment" : "Max. Allowed Glucose Rise for SMB, formerly Max Delta-BG Threshold"
+    },
     "Maximum allowed positive percent change in glucose level to permit SMBs. If the difference in glucose is greater than this, Trio will disable SMBs." : {
     "Maximum allowed positive percent change in glucose level to permit SMBs. If the difference in glucose is greater than this, Trio will disable SMBs." : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -139977,6 +140020,9 @@
         }
         }
       }
       }
     },
     },
+    "Maximum amount of active carbs considered by the algorithm." : {
+
+    },
     "Maximum amount of carbs still available if no absorption is detected." : {
     "Maximum amount of carbs still available if no absorption is detected." : {
       "comment" : "Mini hint for Remaining Carbs Cap",
       "comment" : "Mini hint for Remaining Carbs Cap",
       "localizations" : {
       "localizations" : {
@@ -140078,7 +140124,17 @@
         }
         }
       }
       }
     },
     },
+    "Maximum Basal Rate" : {
+
+    },
+    "Maximum Bolus" : {
+
+    },
+    "Maximum Carbs on Board (COB)" : {
+      "comment" : "Max COB"
+    },
     "Maximum Carbs On Board (COB) allowed." : {
     "Maximum Carbs On Board (COB) allowed." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -140487,6 +140543,9 @@
         }
         }
       }
       }
     },
     },
+    "Maximum Insulin on Board (IOB)" : {
+      "comment" : "Max IOB"
+    },
     "Maximum Meal Absorption Time" : {
     "Maximum Meal Absorption Time" : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -156215,6 +156274,7 @@
       }
       }
     },
     },
     "Omnipod Dash" : {
     "Omnipod Dash" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -156314,6 +156374,9 @@
         }
         }
       }
       }
     },
     },
+    "Omnipod DASH" : {
+
+    },
     "Omnipod Eros" : {
     "Omnipod Eros" : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -158765,6 +158828,9 @@
         }
         }
       }
       }
     },
     },
+    "Other third-party services, like Apple Health or Tidepool, can be added later through the settings menu." : {
+
+    },
     "Our strong recommendation is to " : {
     "Our strong recommendation is to " : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -169115,6 +169181,7 @@
       }
       }
     },
     },
     "Raise target glucose if when Autosens Ratio is >1." : {
     "Raise target glucose if when Autosens Ratio is >1." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -169215,6 +169282,7 @@
       }
       }
     },
     },
     "Raise target glucose when Autosens Ratio is <1." : {
     "Raise target glucose when Autosens Ratio is <1." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -169314,6 +169382,9 @@
         }
         }
       }
       }
     },
     },
+    "Raise target glucose when Autosens Ratio is less than 1." : {
+
+    },
     "Random variation added to each reading to simulate real-world sensor noise." : {
     "Random variation added to each reading to simulate real-world sensor noise." : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -211250,6 +211321,9 @@
         }
         }
       }
       }
     },
     },
+    "This setting helps prevent delivering too much insulin at once. It’s typically a value close to the amount you might need for a very high blood sugar and the biggest meal of your life combined." : {
+
+    },
     "This setting helps the system estimate how much glucose your body is absorbing, even when it's not immediately visible in your glucose data, ensuring more accurate insulin dosing during carb absorption." : {
     "This setting helps the system estimate how much glucose your body is absorbing, even when it's not immediately visible in your glucose data, ensuring more accurate insulin dosing during carb absorption." : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -236402,6 +236476,9 @@
         }
         }
       }
       }
     },
     },
+    "While onboarding, Trio continues to operate with your prior settings." : {
+
+    },
     "While upgrading Trio to the new version, we ran into an issue transferring some of your historical data." : {
     "While upgrading Trio to the new version, we ran into an issue transferring some of your historical data." : {
 
 
     },
     },
@@ -238842,6 +238919,9 @@
         }
         }
       }
       }
     },
     },
+    "You can use Nightscout to import your existing therapy settings, or if you prefer, you can only connect to Nightscout, and configure therapy settings from scratch." : {
+
+    },
     "You have certain rights regarding your information, including:" : {
     "You have certain rights regarding your information, including:" : {
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
@@ -239760,6 +239840,9 @@
     "Your last 24 hr of treatment data (pump events, carb entries, glucose trace, etc.) are migrated." : {
     "Your last 24 hr of treatment data (pump events, carb entries, glucose trace, etc.) are migrated." : {
 
 
     },
     },
+    "Your last 24 hr of treatment data (pump events, carb entries, glucose trace, etc.) are not migrated." : {
+
+    },
     "Your phone or app is not enabled for NFC communications, which is needed to pair to libre2 sensors" : {
     "Your phone or app is not enabled for NFC communications, which is needed to pair to libre2 sensors" : {
       "extractionState" : "manual",
       "extractionState" : "manual",
       "localizations" : {
       "localizations" : {

+ 1 - 1
Trio/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift

@@ -11,7 +11,7 @@ extension AutosensSettings {
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
 
 
         private(set) var autosensISF: Decimal?
         private(set) var autosensISF: Decimal?
-        private(set) var autosensRatio: Decimal = 0
+        private(set) var autosensRatio: Decimal = 1
         @Published var determinationsFromPersistence: [OrefDetermination] = []
         @Published var determinationsFromPersistence: [OrefDetermination] = []
 
 
         let viewContext = CoreDataStack.shared.persistentContainer.viewContext
         let viewContext = CoreDataStack.shared.persistentContainer.viewContext

+ 6 - 9
Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -65,18 +65,15 @@ extension AutosensSettings {
                     let dynamicRatio = state.determinationsFromPersistence.first?.sensitivityRatio
                     let dynamicRatio = state.determinationsFromPersistence.first?.sensitivityRatio
                     let dynamicISF = state.determinationsFromPersistence.first?.insulinSensitivity
                     let dynamicISF = state.determinationsFromPersistence.first?.insulinSensitivity
                     let newISF = state.autosensISF
                     let newISF = state.autosensISF
+                    let decimalValue = !state.settingsManager.preferences.useNewFormula ? state
+                        .autosensRatio as NSDecimalNumber : dynamicRatio ?? 1
+                    let decimalValueText = rateFormatter
+                        .string(from: ((decimalValue as Decimal) * Decimal(100)) as NSNumber) ?? "100"
+
                     HStack {
                     HStack {
                         Text("Sensitivity Ratio")
                         Text("Sensitivity Ratio")
                         Spacer()
                         Spacer()
-                        Text(
-                            rateFormatter
-                                .string(from: (
-                                    (
-                                        !state.settingsManager.preferences.useNewFormula ? state
-                                            .autosensRatio as NSDecimalNumber : dynamicRatio
-                                    ) ?? 1
-                                ) as NSNumber) ?? "1"
-                        )
+                        Text("\(decimalValueText) \(String(localized: "%", comment: "Percentage symbol"))")
                     }.padding(.vertical)
                     }.padding(.vertical)
                     HStack {
                     HStack {
                         Text("Calculated Sensitivity")
                         Text("Calculated Sensitivity")

+ 11 - 8
Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift

@@ -36,12 +36,12 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         get: { selectedVerboseHint },
                         set: {
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = String(localized: "Max IOB", comment: "Max IOB")
+                            hintLabel = String(localized: "Maximum Insulin on Board (IOB)", comment: "Max IOB")
                         }
                         }
                     ),
                     ),
                     units: state.units,
                     units: state.units,
                     type: .decimal("maxIOB"),
                     type: .decimal("maxIOB"),
-                    label: String(localized: "Max IOB", comment: "Max IOB"),
+                    label: String(localized: "Maximum Insulin on Board (IOB)", comment: "Max IOB"),
                     miniHint: String(localized: "Maximum units of insulin allowed to be active."),
                     miniHint: String(localized: "Maximum units of insulin allowed to be active."),
                     verboseHint:
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                     VStack(alignment: .leading, spacing: 10) {
@@ -50,6 +50,9 @@ extension UnitsLimitsSettings {
                             "Warning: This must be greater than 0 for any automatic temporary basal rates or SMBs to be given."
                             "Warning: This must be greater than 0 for any automatic temporary basal rates or SMBs to be given."
                         ).bold()
                         ).bold()
                         Text(
                         Text(
+                            "This setting helps prevent delivering too much insulin at once. It’s typically a value close to the amount you might need for a very high blood sugar and the biggest meal of your life combined."
+                        )
+                        Text(
                             "This is the maximum amount of Insulin On Board (IOB) above profile basal rates from all sources - positive temporary basal rates, manual or meal boluses, and SMBs - that Trio is allowed to accumulate to address an above target glucose."
                             "This is the maximum amount of Insulin On Board (IOB) above profile basal rates from all sources - positive temporary basal rates, manual or meal boluses, and SMBs - that Trio is allowed to accumulate to address an above target glucose."
                         )
                         )
                         Text(
                         Text(
@@ -69,12 +72,12 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         get: { selectedVerboseHint },
                         set: {
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = String(localized: "Max Bolus")
+                            hintLabel = String(localized: "Maximum Bolus")
                         }
                         }
                     ),
                     ),
                     units: state.units,
                     units: state.units,
                     type: .decimal("maxBolus"),
                     type: .decimal("maxBolus"),
-                    label: String(localized: "Max Bolus"),
+                    label: String(localized: "Maximum Bolus"),
                     miniHint: String(localized: "Largest bolus of insulin allowed."),
                     miniHint: String(localized: "Largest bolus of insulin allowed."),
                     verboseHint:
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                     VStack(alignment: .leading, spacing: 10) {
@@ -100,7 +103,7 @@ extension UnitsLimitsSettings {
                     ),
                     ),
                     units: state.units,
                     units: state.units,
                     type: .decimal("maxBasal"),
                     type: .decimal("maxBasal"),
-                    label: String(localized: "Max Basal Rate"),
+                    label: String(localized: "Maximum Basal Rate"),
                     miniHint: String(localized: "Largest basal rate allowed."),
                     miniHint: String(localized: "Largest basal rate allowed."),
                     verboseHint:
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                     VStack(alignment: .leading, spacing: 10) {
@@ -122,13 +125,13 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         get: { selectedVerboseHint },
                         set: {
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = String(localized: "Max COB", comment: "Max COB")
+                            hintLabel = String(localized: "Maximum Carbs on Board (COB)", comment: "Max COB")
                         }
                         }
                     ),
                     ),
                     units: state.units,
                     units: state.units,
                     type: .decimal("maxCOB"),
                     type: .decimal("maxCOB"),
-                    label: String(localized: "Max COB", comment: "Max COB"),
-                    miniHint: String(localized: "Maximum Carbs On Board (COB) allowed."),
+                    label: String(localized: "Maximum Carbs on Board (COB)", comment: "Max COB"),
+                    miniHint: String(localized: "Maximum amount of active carbs considered by the algorithm."),
                     verboseHint:
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 120 grams of carbs").bold()
                         Text("Default: 120 grams of carbs").bold()

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

@@ -951,7 +951,7 @@ extension Home {
             .confirmationDialog("Pump Model", isPresented: $showPumpSelection) {
             .confirmationDialog("Pump Model", isPresented: $showPumpSelection) {
                 Button("Medtronic") { state.addPump(.minimed) }
                 Button("Medtronic") { state.addPump(.minimed) }
                 Button("Omnipod Eros") { state.addPump(.omnipod) }
                 Button("Omnipod Eros") { state.addPump(.omnipod) }
-                Button("Omnipod Dash") { state.addPump(.omnipodBLE) }
+                Button("Omnipod DASH") { state.addPump(.omnipodBLE) }
                 Button("Dana(RS/-i)") { state.addPump(.dana) }
                 Button("Dana(RS/-i)") { state.addPump(.dana) }
                 Button("Pump Simulator") { state.addPump(.simulator) }
                 Button("Pump Simulator") { state.addPump(.simulator) }
             } message: { Text("Select Pump Model") }
             } message: { Text("Select Pump Model") }

+ 4 - 4
Trio/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -208,8 +208,8 @@ extension ISFEditor {
                     ).foregroundStyle(
                     ).foregroundStyle(
                         .linearGradient(
                         .linearGradient(
                             colors: [
                             colors: [
-                                Color.red.opacity(0.6),
-                                Color.red.opacity(0.1)
+                                Color.cyan.opacity(0.6),
+                                Color.cyan.opacity(0.1)
                             ],
                             ],
                             startPoint: .bottom,
                             startPoint: .bottom,
                             endPoint: .top
                             endPoint: .top
@@ -217,10 +217,10 @@ extension ISFEditor {
                     ).alignsMarkStylesWithPlotArea()
                     ).alignsMarkStylesWithPlotArea()
 
 
                     LineMark(x: .value("End Date", startDate), y: .value("ISF", displayValueFloat ?? 0))
                     LineMark(x: .value("End Date", startDate), y: .value("ISF", displayValueFloat ?? 0))
-                        .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.red)
+                        .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.cyan)
 
 
                     LineMark(x: .value("Start Date", endDate), y: .value("ISF", displayValueFloat ?? 0))
                     LineMark(x: .value("Start Date", endDate), y: .value("ISF", displayValueFloat ?? 0))
-                        .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.red)
+                        .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.cyan)
                 }
                 }
             }
             }
             .chartXAxis {
             .chartXAxis {

+ 128 - 16
Trio/Sources/Modules/Onboarding/OnboardingStateModel.swift

@@ -1,8 +1,12 @@
 import Combine
 import Combine
+import DanaKit
 import FirebaseCrashlytics
 import FirebaseCrashlytics
 import Foundation
 import Foundation
 import LoopKit
 import LoopKit
+import MinimedKit
 import Observation
 import Observation
+import OmniBLE
+import OmniKit
 import SwiftUI
 import SwiftUI
 
 
 /// Model that holds the data collected during onboarding.
 /// Model that holds the data collected during onboarding.
@@ -15,6 +19,7 @@ extension Onboarding {
         @ObservationIgnored @Injected() var nightscoutManager: NightscoutManager!
         @ObservationIgnored @Injected() var nightscoutManager: NightscoutManager!
         @ObservationIgnored @Injected() var notificationsManager: UserNotificationsManager!
         @ObservationIgnored @Injected() var notificationsManager: UserNotificationsManager!
         @ObservationIgnored @Injected() var bluetoothManager: BluetoothStateManager!
         @ObservationIgnored @Injected() var bluetoothManager: BluetoothStateManager!
+        @ObservationIgnored @Injected() var apsManager: APSManager!
 
 
         private let settingsProvider = PickerSettingsProvider.shared
         private let settingsProvider = PickerSettingsProvider.shared
 
 
@@ -23,6 +28,58 @@ extension Onboarding {
         var diagnosticsSharingOption: DiagnosticsSharingOption = .enabled
         var diagnosticsSharingOption: DiagnosticsSharingOption = .enabled
         var hasAcceptedPrivacyPolicy: Bool = false
         var hasAcceptedPrivacyPolicy: Bool = false
 
 
+        // MARK: - Determine Initial Build State
+
+        /// Determines whether the app is in a fresh install state for Trio v0.3.0.
+        ///
+        /// This check is based on the assumption that a truly clean install will only contain
+        /// the `logs/` directory and the `preferences.json` file in the app's Documents directory.
+        ///
+        /// If this condition is met, the onboarding flow skips the `.returningUser` step and treats
+        /// the user as new. If more files or directories are found, it is assumed the user is returning.
+        ///
+        /// Note: This check is not directly connected to a completed migration. However, if a migration
+        /// has been triggered (whether successful or not), additional files such as treatment JSONs
+        /// will exist, which naturally causes this check to return `false`.
+        var isFreshTrioInstall: Bool {
+            let fileManager = FileManager.default
+            guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
+                return false
+            }
+
+            debug(.default, "Checking for fresh install in \(documentsURL.path)...")
+
+            let expectedLogsFolder = "logs"
+            let expectedPreferencesFile = OpenAPS.Settings.preferences
+
+            do {
+                let contents = try fileManager.contentsOfDirectory(atPath: documentsURL.path)
+
+                debug(.default, "Found \(contents) in \(documentsURL.path)...")
+
+                // Expect exactly 2 entries: "logs" and the preferences file
+                guard contents.count == 2 else {
+                    debug(.default, "Trio install is not fresh; returning user.")
+                    return false
+                }
+
+                // Ensure they match exactly
+                let expectedSet = Set([expectedLogsFolder, expectedPreferencesFile])
+                let actualSet = Set(contents)
+
+                debug(.default, "Expected: \(expectedSet), Actual: \(actualSet)")
+
+                let isFreshInstall = expectedSet == actualSet
+                debug(.default, "Trio install is fresh; new user.")
+
+                return isFreshInstall
+
+            } catch {
+                debug(.default, "Cannot determine Initial Build State. Failed to read documents directory: \(error)")
+                return false
+            }
+        }
+
         // MARK: - Nightscout Setup
         // MARK: - Nightscout Setup
 
 
         var nightscoutSetupOption: NightscoutSetupOption = .noSelection
         var nightscoutSetupOption: NightscoutSetupOption = .noSelection
@@ -39,7 +96,40 @@ extension Onboarding {
         // MARK: - Units and Pump Omboarding Option
         // MARK: - Units and Pump Omboarding Option
 
 
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
-        var pumpOptionForOnboardingUnits: PumpOptionForOnboardingUnits = .omnipodDash
+        private var selectedPumpOption: PumpOptionForOnboardingUnits?
+        var pumpOptionForOnboardingUnits: PumpOptionForOnboardingUnits {
+            get {
+                // let user edit selection and return user-selection, if present
+                if let selected = selectedPumpOption {
+                    return selected
+                }
+
+                let defaultOption: PumpOptionForOnboardingUnits
+                if let pumpManager = apsManager?.pumpManager {
+                    if pumpManager is OmniBLEPumpManager {
+                        defaultOption = .omnipodDash
+                    } else if pumpManager is OmnipodPumpManager {
+                        defaultOption = .omnipodEros
+                    } else if pumpManager is DanaKitPumpManager {
+                        defaultOption = .dana
+                    } else if pumpManager is MinimedPumpManager {
+                        defaultOption = .minimed
+                    } else {
+                        defaultOption = .omnipodDash
+                    }
+                } else {
+                    defaultOption = .omnipodDash
+                }
+
+                // cache it so picker can stay in sync
+                selectedPumpOption = defaultOption
+
+                return defaultOption
+            }
+            set {
+                selectedPumpOption = newValue
+            }
+        }
 
 
         // MARK: - Time Values (shared)
         // MARK: - Time Values (shared)
 
 
@@ -47,7 +137,7 @@ extension Onboarding {
 
 
         // MARK: - Carb Ratio
         // MARK: - Carb Ratio
 
 
-        let carbRatioPickerSetting = PickerSetting(value: 10, step: 0.1, min: 1, max: 50, type: .gram)
+        let carbRatioPickerSetting = PickerSetting(value: 30, step: 0.1, min: 1, max: 50, type: .gram)
         var carbRatioItems: [CarbRatioEditor.Item] = []
         var carbRatioItems: [CarbRatioEditor.Item] = []
         var initialCarbRatioItems: [CarbRatioEditor.Item] = []
         var initialCarbRatioItems: [CarbRatioEditor.Item] = []
         var carbRatioTimeValues: [TimeInterval] { sharedTimeValues }
         var carbRatioTimeValues: [TimeInterval] { sharedTimeValues }
@@ -56,15 +146,18 @@ extension Onboarding {
         // MARK: - Basal Profile
         // MARK: - Basal Profile
 
 
         var basalRatePickerSetting: PickerSetting {
         var basalRatePickerSetting: PickerSetting {
-            switch pumpOptionForOnboardingUnits {
+            switch selectedPumpOption {
             case .dana:
             case .dana:
-                return PickerSetting(value: 0.05, step: 0.05, min: 0, max: 3, type: .insulinUnitPerHour)
+                return PickerSetting(value: 0.1, step: 0.05, min: 0, max: 3, type: .insulinUnitPerHour)
             case .minimed:
             case .minimed:
-                return PickerSetting(value: 0.05, step: 0.05, min: 0, max: 35, type: .insulinUnitPerHour)
+                return PickerSetting(value: 0.1, step: 0.05, min: 0, max: 35, type: .insulinUnitPerHour)
             case .omnipodDash:
             case .omnipodDash:
-                return PickerSetting(value: 0.05, step: 0.05, min: 0, max: 30, type: .insulinUnitPerHour)
+                return PickerSetting(value: 0.1, step: 0.05, min: 0, max: 30, type: .insulinUnitPerHour)
             case .omnipodEros:
             case .omnipodEros:
-                return PickerSetting(value: 0.05, step: 0.05, min: 0.05, max: 30, type: .insulinUnitPerHour)
+                return PickerSetting(value: 0.1, step: 0.05, min: 0.05, max: 30, type: .insulinUnitPerHour)
+            case .none:
+                // same as dash, as that is the fallback
+                return PickerSetting(value: 0.1, step: 0.05, min: 0, max: 30, type: .insulinUnitPerHour)
             }
             }
         }
         }
 
 
@@ -76,7 +169,7 @@ extension Onboarding {
 
 
         // MARK: - Insulin Sensitivity Factor (ISF)
         // MARK: - Insulin Sensitivity Factor (ISF)
 
 
-        var sensitivityPickerSetting = PickerSetting(value: 100, step: 1, min: 9, max: 540, type: .glucose)
+        var sensitivityPickerSetting = PickerSetting(value: 200, step: 1, min: 9, max: 540, type: .glucose)
         var isfItems: [ISFEditor.Item] = []
         var isfItems: [ISFEditor.Item] = []
         var initialISFItems: [ISFEditor.Item] = []
         var initialISFItems: [ISFEditor.Item] = []
         var isfTimeValues: [TimeInterval] { sharedTimeValues }
         var isfTimeValues: [TimeInterval] { sharedTimeValues }
@@ -84,7 +177,7 @@ extension Onboarding {
 
 
         // MARK: - Glucose Targets
         // MARK: - Glucose Targets
 
 
-        let letTargetPickerSetting = PickerSetting(value: 100, step: 1, min: 72, max: 180, type: .glucose)
+        let letTargetPickerSetting = PickerSetting(value: 110, step: 1, min: 72, max: 180, type: .glucose)
         var targetItems: [TargetsEditor.Item] = []
         var targetItems: [TargetsEditor.Item] = []
         var initialTargetItems: [TargetsEditor.Item] = []
         var initialTargetItems: [TargetsEditor.Item] = []
         var targetTimeValues: [TimeInterval] { sharedTimeValues }
         var targetTimeValues: [TimeInterval] { sharedTimeValues }
@@ -215,16 +308,35 @@ extension Onboarding {
 
 
         /// Remaps therapy items affected by a pump model change.
         /// Remaps therapy items affected by a pump model change.
         ///
         ///
-        /// This function updates basal profile items to use the closest valid index
-        /// from the updated basal rate and time arrays, preserving the user's settings.
+        /// Updates basal profile items to use the closest valid index from
+        /// the updated basal rate and time arrays, preserving the user's settings
+        /// as closely as possible when switching between pump models.
+        ///
+        /// If an imported item's `rateIndex` or `timeIndex` exceeds the bounds of the
+        /// current pump's allowed values, it is clamped to the last valid index to avoid
+        /// crashes and preserve data integrity. A debug message is logged if clamping occurs.
         ///
         ///
         /// Call this after the user selects a new pump model.
         /// Call this after the user selects a new pump model.
         ///
         ///
         /// See also: `UnitSelectionStepView` `.onChange()` handlers.
         /// See also: `UnitSelectionStepView` `.onChange()` handlers.
         func remapTherapyItemsForChangedPumpModel() {
         func remapTherapyItemsForChangedPumpModel() {
+            let maxValidRateIndex = max(basalProfileRateValues.count - 1, 0)
+            let maxValidTimeIndex = max(basalProfileTimeValues.count - 1, 0)
+
             basalProfileItems = basalProfileItems.map { item in
             basalProfileItems = basalProfileItems.map { item in
-                let newRateIndex = closestIndex(for: basalProfileRateValues[item.rateIndex], in: basalProfileRateValues)
-                let newTimeIndex = closestIndex(for: basalProfileTimeValues[item.timeIndex], in: basalProfileTimeValues)
+                let safeRateIndex = min(item.rateIndex, maxValidRateIndex)
+                let safeTimeIndex = min(item.timeIndex, maxValidTimeIndex)
+
+                let originalRate = basalProfileRateValues[safeRateIndex]
+                let originalTime = basalProfileTimeValues[safeTimeIndex]
+
+                let newRateIndex = closestIndex(for: originalRate, in: basalProfileRateValues)
+                let newTimeIndex = closestIndex(for: originalTime, in: basalProfileTimeValues)
+
+                if safeRateIndex != item.rateIndex {
+                    debug(.default, "⚠️ rateIndex \(item.rateIndex) out of bounds; clamped to \(safeRateIndex)")
+                }
+
                 return BasalProfileEditor.Item(rateIndex: newRateIndex, timeIndex: newTimeIndex)
                 return BasalProfileEditor.Item(rateIndex: newRateIndex, timeIndex: newTimeIndex)
             }
             }
         }
         }
@@ -412,7 +524,7 @@ extension Onboarding {
         /// Adds a default ISF editor item at 00:00 with a standard sensitivity value.
         /// Adds a default ISF editor item at 00:00 with a standard sensitivity value.
         func addInitialISF() {
         func addInitialISF() {
             addInitialItem(
             addInitialItem(
-                defaultValue: 50,
+                defaultValue: 200,
                 rateValues: isfRateValues,
                 rateValues: isfRateValues,
                 assign: { isfItems = $0 },
                 assign: { isfItems = $0 },
                 makeItem: ISFEditor.Item.init
                 makeItem: ISFEditor.Item.init
@@ -432,7 +544,7 @@ extension Onboarding {
         /// Adds a default carb ratio editor item at 00:00 with a standard ratio.
         /// Adds a default carb ratio editor item at 00:00 with a standard ratio.
         func addInitialCarbRatio() {
         func addInitialCarbRatio() {
             addInitialItem(
             addInitialItem(
-                defaultValue: 10,
+                defaultValue: 30,
                 rateValues: carbRatioRateValues,
                 rateValues: carbRatioRateValues,
                 assign: { carbRatioItems = $0 },
                 assign: { carbRatioItems = $0 },
                 makeItem: CarbRatioEditor.Item.init
                 makeItem: CarbRatioEditor.Item.init
@@ -442,7 +554,7 @@ extension Onboarding {
         /// Adds a default glucose target item at 00:00 with a typical target value.
         /// Adds a default glucose target item at 00:00 with a typical target value.
         func addInitialTarget() {
         func addInitialTarget() {
             let timeIndex = 0
             let timeIndex = 0
-            let rateIndex = closestIndex(for: 100, in: targetRateValues)
+            let rateIndex = closestIndex(for: 110, in: targetRateValues)
             targetItems = [TargetsEditor.Item(lowIndex: rateIndex, highIndex: rateIndex, timeIndex: timeIndex)]
             targetItems = [TargetsEditor.Item(lowIndex: rateIndex, highIndex: rateIndex, timeIndex: timeIndex)]
         }
         }
 
 

+ 34 - 5
Trio/Sources/Modules/Onboarding/View/OnboardingRootView.swift

@@ -1,3 +1,4 @@
+import Foundation
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
 
 
@@ -8,6 +9,7 @@ extension Onboarding {
         @State var state = StateModel()
         @State var state = StateModel()
         @State private var navigationDirection: OnboardingNavigationDirection = .forward
         @State private var navigationDirection: OnboardingNavigationDirection = .forward
         let onboardingManager: OnboardingManager
         let onboardingManager: OnboardingManager
+        let wasMigrationSuccessful: Bool
 
 
         // Step management
         // Step management
         @State private var currentChapter: OnboardingChapter = .prepareTrio
         @State private var currentChapter: OnboardingChapter = .prepareTrio
@@ -124,6 +126,7 @@ extension Onboarding {
                         }
                         }
 
 
                         OnboardingStepContent(
                         OnboardingStepContent(
+                            wasMigrationSuccessful: wasMigrationSuccessful,
                             currentStep: $currentStep,
                             currentStep: $currentStep,
                             showingChapterCompletion: $showingChapterCompletion,
                             showingChapterCompletion: $showingChapterCompletion,
                             currentStartupSubstep: $currentStartupSubstep,
                             currentStartupSubstep: $currentStartupSubstep,
@@ -150,6 +153,7 @@ extension Onboarding {
                             currentSMBSubstep: $currentSMBSubstep,
                             currentSMBSubstep: $currentSMBSubstep,
                             currentTargetBehaviorSubstep: $currentTargetBehaviorSubstep,
                             currentTargetBehaviorSubstep: $currentTargetBehaviorSubstep,
                             onboardingManager: onboardingManager,
                             onboardingManager: onboardingManager,
+                            isFreshTrioInstall: state.isFreshTrioInstall,
                             state: state,
                             state: state,
                             shouldDisableNextButton: shouldDisableNextButton,
                             shouldDisableNextButton: shouldDisableNextButton,
                             navigationDirectionChanged: { navigationDirection = $0 }
                             navigationDirectionChanged: { navigationDirection = $0 }
@@ -280,6 +284,7 @@ struct OnboardingProgressBar: View {
 }
 }
 
 
 struct OnboardingStepContent: View {
 struct OnboardingStepContent: View {
+    var wasMigrationSuccessful: Bool
     @Binding var currentStep: OnboardingStep
     @Binding var currentStep: OnboardingStep
     @Binding var showingChapterCompletion: OnboardingChapter?
     @Binding var showingChapterCompletion: OnboardingChapter?
     @Binding var currentStartupSubstep: StartupSubstep
     @Binding var currentStartupSubstep: StartupSubstep
@@ -314,7 +319,7 @@ struct OnboardingStepContent: View {
                                 case .startupGuide:
                                 case .startupGuide:
                                     StartupGuideStepView(state: state)
                                     StartupGuideStepView(state: state)
                                 case .returningUser:
                                 case .returningUser:
-                                    StartupReturningUserStepView(state: state)
+                                    StartupReturningUserStepView(state: state, wasMigrationSuccessful: wasMigrationSuccessful)
                                 case .forceCloseWarning:
                                 case .forceCloseWarning:
                                     StartupForceCloseWarningStepView(state: state)
                                     StartupForceCloseWarningStepView(state: state)
                                 }
                                 }
@@ -451,6 +456,7 @@ struct OnboardingNavigationButtons: View {
     @Binding var currentTargetBehaviorSubstep: TargetBehaviorSubstep
     @Binding var currentTargetBehaviorSubstep: TargetBehaviorSubstep
 
 
     let onboardingManager: OnboardingManager
     let onboardingManager: OnboardingManager
+    let isFreshTrioInstall: Bool
     @Bindable var state: Onboarding.StateModel
     @Bindable var state: Onboarding.StateModel
     var shouldDisableNextButton: Bool
     var shouldDisableNextButton: Bool
     var navigationDirectionChanged: (OnboardingNavigationDirection) -> Void
     var navigationDirectionChanged: (OnboardingNavigationDirection) -> Void
@@ -505,7 +511,14 @@ struct OnboardingNavigationButtons: View {
 
 
         switch currentStep {
         switch currentStep {
         case .startupInfo:
         case .startupInfo:
-            if let previousSub = StartupSubstep(rawValue: currentStartupSubstep.rawValue - 1) {
+            var previous = StartupSubstep(rawValue: currentStartupSubstep.rawValue - 1)
+
+            /// Skip `.returningUser` if this is a fresh install
+            if previous == .returningUser, isFreshTrioInstall == true {
+                previous = StartupSubstep(rawValue: previous!.rawValue - 1)
+            }
+
+            if let previousSub = previous {
                 currentStartupSubstep = previousSub
                 currentStartupSubstep = previousSub
             } else if let previous = currentStep.previous {
             } else if let previous = currentStep.previous {
                 currentStep = previous
                 currentStep = previous
@@ -628,7 +641,17 @@ struct OnboardingNavigationButtons: View {
 
 
         switch currentStep {
         switch currentStep {
         case .startupInfo:
         case .startupInfo:
-            if let next = StartupSubstep(rawValue: currentStartupSubstep.rawValue + 1) {
+            let nextSubstepRaw = currentStartupSubstep.rawValue + 1
+
+            if isFreshTrioInstall, StartupSubstep(rawValue: nextSubstepRaw) == .returningUser {
+                /// Skip `.returningUser` if it's a fresh install
+                if let nextAfterSkip = StartupSubstep(rawValue: nextSubstepRaw + 1) {
+                    currentStartupSubstep = nextAfterSkip
+                } else if let nextStep = currentStep.next {
+                    currentStep = nextStep
+                    currentStartupSubstep = .startupGuide
+                }
+            } else if let next = StartupSubstep(rawValue: nextSubstepRaw) {
                 currentStartupSubstep = next
                 currentStartupSubstep = next
             } else if let nextStep = currentStep.next {
             } else if let nextStep = currentStep.next {
                 currentStep = nextStep
                 currentStep = nextStep
@@ -734,7 +757,13 @@ struct OnboardingNavigationButtons: View {
             }
             }
 
 
         case .bluetooth:
         case .bluetooth:
-            state.shouldDisplayBluetoothRequestAlert = true
+            if let next = currentStep.next {
+                if state.bluetoothManager.bluetoothAuthorization != .authorized {
+                    state.shouldDisplayBluetoothRequestAlert = true
+                } else {
+                    currentStep = next
+                }
+            }
 
 
         case .completed:
         case .completed:
             state.saveOnboardingData()
             state.saveOnboardingData()
@@ -754,7 +783,7 @@ struct Onboarding_Preview: PreviewProvider {
         Group {
         Group {
             let resolver = TrioApp.resolver
             let resolver = TrioApp.resolver
             let onboardingManager = OnboardingManager()
             let onboardingManager = OnboardingManager()
-            Onboarding.RootView(resolver: resolver, onboardingManager: onboardingManager)
+            Onboarding.RootView(resolver: resolver, onboardingManager: onboardingManager, wasMigrationSuccessful: true)
                 .previewDisplayName("Onboarding Flow")
                 .previewDisplayName("Onboarding Flow")
         }
         }
     }
     }

+ 17 - 17
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/AlgorithmSettings/AlgorithmSettingsSubstepView.swift

@@ -52,7 +52,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .autosensMax:
                 case .autosensMax:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: settingsProvider.settings.autosensMax,
                         setting: settingsProvider.settings.autosensMax,
                         decimalValue: $state.autosensMax,
                         decimalValue: $state.autosensMax,
@@ -61,7 +61,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .rewindResetsAutosens:
                 case .rewindResetsAutosens:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -71,7 +71,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .enableSMBAlways:
                 case .enableSMBAlways:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -80,7 +80,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .enableSMBWithCOB:
                 case .enableSMBWithCOB:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -90,7 +90,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .enableSMBWithTempTarget:
                 case .enableSMBWithTempTarget:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -100,7 +100,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .enableSMBAfterCarbs:
                 case .enableSMBAfterCarbs:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -110,7 +110,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .enableSMBWithHighGlucoseTarget:
                 case .enableSMBWithHighGlucoseTarget:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -131,7 +131,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     }
                     }
                 case .allowSMBWithHighTempTarget:
                 case .allowSMBWithHighTempTarget:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -140,7 +140,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .enableUAM:
                 case .enableUAM:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -149,7 +149,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .maxSMBMinutes:
                 case .maxSMBMinutes:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: settingsProvider.settings.maxSMBBasalMinutes,
                         setting: settingsProvider.settings.maxSMBBasalMinutes,
                         decimalValue: $state.maxSMBMinutes,
                         decimalValue: $state.maxSMBMinutes,
@@ -158,7 +158,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .maxUAMMinutes:
                 case .maxUAMMinutes:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: settingsProvider.settings.maxUAMSMBBasalMinutes,
                         setting: settingsProvider.settings.maxUAMSMBBasalMinutes,
                         decimalValue: $state.maxUAMMinutes,
                         decimalValue: $state.maxUAMMinutes,
@@ -167,7 +167,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .maxDeltaGlucoseThreshold:
                 case .maxDeltaGlucoseThreshold:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: settingsProvider.settings.maxDeltaBGthreshold,
                         setting: settingsProvider.settings.maxDeltaBGthreshold,
                         decimalValue: $state.maxDeltaGlucoseThreshold,
                         decimalValue: $state.maxDeltaGlucoseThreshold,
@@ -176,7 +176,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .highTempTargetRaisesSensitivity:
                 case .highTempTargetRaisesSensitivity:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -185,7 +185,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .lowTempTargetLowersSensitivity:
                 case .lowTempTargetLowersSensitivity:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -194,7 +194,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .sensitivityRaisesTarget:
                 case .sensitivityRaisesTarget:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -203,7 +203,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .resistanceLowersTarget:
                 case .resistanceLowersTarget:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: nil,
                         setting: nil,
                         decimalValue: $decimalPlaceholder,
                         decimalValue: $decimalPlaceholder,
@@ -212,7 +212,7 @@ struct AlgorithmSettingsSubstepView<Substep: AlgorithmSubstepProtocol & RawRepre
                     )
                     )
                 case .halfBasalTarget:
                 case .halfBasalTarget:
                     algorithmSettingsInput(
                     algorithmSettingsInput(
-                        label: substep.title,
+                        label: step.title,
                         displayPicker: $shouldDisplayPicker,
                         displayPicker: $shouldDisplayPicker,
                         setting: settingsProvider.settings.halfBasalExerciseTarget,
                         setting: settingsProvider.settings.halfBasalExerciseTarget,
                         decimalValue: $state.halfBasalTarget,
                         decimalValue: $state.halfBasalTarget,

+ 7 - 4
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/Nightscout/NightscoutImportStepView.swift

@@ -53,11 +53,14 @@ struct NightscoutImportStepView: View {
                     VStack(alignment: .leading, spacing: 10) {
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Trio will import the following therapy settings from your Nightscout instance:")
                         Text("Trio will import the following therapy settings from your Nightscout instance:")
                             .multilineTextAlignment(.leading)
                             .multilineTextAlignment(.leading)
+                            .fixedSize(horizontal: false, vertical: true)
                         VStack(alignment: .leading) {
                         VStack(alignment: .leading) {
-                            Text("• Glucose Targets")
-                            Text("• Basal Rates")
-                            Text("• Carb Ratios")
-                            Text("• Insulin Sensitivities")
+                            BulletPoint(String(localized: "Glucose Targets"))
+                            BulletPoint(String(localized: "Basal Rates"))
+                            BulletPoint(String(localized: "Carb Ratios"))
+                            BulletPoint(
+                                String(localized: "Insulin Sensitivities")
+                            )
                         }
                         }
                     }
                     }
                     .padding(.horizontal)
                     .padding(.horizontal)

+ 14 - 0
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/Nightscout/NightscoutSetupStepView.swift

@@ -30,6 +30,20 @@ struct NightscoutSetupStepView: View {
                 }
                 }
                 .buttonStyle(.plain)
                 .buttonStyle(.plain)
             }
             }
+
+            Text(
+                "You can use Nightscout to import your existing therapy settings, or if you prefer, you can only connect to Nightscout, and configure therapy settings from scratch."
+            )
+            .padding(.horizontal)
+            .font(.footnote)
+            .foregroundStyle(Color.secondary)
+            .multilineTextAlignment(.leading)
+
+            Text("Other third-party services, like Apple Health or Tidepool, can be added later through the settings menu.")
+                .padding(.horizontal)
+                .font(.footnote)
+                .foregroundStyle(Color.secondary)
+                .multilineTextAlignment(.leading)
         }
         }
     }
     }
 }
 }

+ 29 - 1
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/StartupGuide/StartupReturningUserStepView.swift

@@ -8,6 +8,7 @@ import SwiftUI
 
 
 struct StartupReturningUserStepView: View {
 struct StartupReturningUserStepView: View {
     @Bindable var state: Onboarding.StateModel
     @Bindable var state: Onboarding.StateModel
+    let wasMigrationSuccessful: Bool
 
 
     @Environment(\.openURL) var openURL
     @Environment(\.openURL) var openURL
 
 
@@ -25,7 +26,9 @@ struct StartupReturningUserStepView: View {
                     Text("Important").foregroundStyle(Color.orange)
                     Text("Important").foregroundStyle(Color.orange)
                 }.bold()
                 }.bold()
 
 
-                Text("Your last 24 hr of treatment data (pump events, carb entries, glucose trace, etc.) are migrated.")
+                if !wasMigrationSuccessful {
+                    Text("Your last 24 hr of treatment data (pump events, carb entries, glucose trace, etc.) are not migrated.")
+                }
 
 
                 Divider().overlay(Color.orange)
                 Divider().overlay(Color.orange)
 
 
@@ -41,10 +44,35 @@ struct StartupReturningUserStepView: View {
             .cornerRadius(10)
             .cornerRadius(10)
 
 
             VStack(alignment: .leading, spacing: 10) {
             VStack(alignment: .leading, spacing: 10) {
+                HStack(alignment: .top, spacing: 10) {
+                    Image(systemName: "info.circle.fill").foregroundStyle(Color.bgDarkBlue, Color.blue)
+                        .symbolRenderingMode(.palette)
+                    Text("Information").foregroundStyle(Color.blue)
+                }.bold()
+
+                Text("While onboarding, Trio continues to operate with your prior settings.")
+            }
+            .frame(maxWidth: .infinity)
+            .padding()
+            .background(Color.chart.opacity(0.65))
+            .overlay(
+                RoundedRectangle(cornerRadius: 10)
+                    .stroke(Color.blue, lineWidth: 2)
+            )
+            .cornerRadius(10)
+
+            VStack(alignment: .leading, spacing: 10) {
                 Text("Here's what you can expect to be preserved:")
                 Text("Here's what you can expect to be preserved:")
                     .font(.headline)
                     .font(.headline)
                     .padding(.bottom, 4)
                     .padding(.bottom, 4)
 
 
+                if wasMigrationSuccessful {
+                    BulletPoint(
+                        String(
+                            localized: "Your last 24 hr of treatment data (pump events, carb entries, glucose trace, etc.) are migrated."
+                        )
+                    )
+                }
                 BulletPoint(String(localized: "Your pump and CGM configurations are retained and fully functional."))
                 BulletPoint(String(localized: "Your pump and CGM configurations are retained and fully functional."))
                 BulletPoint(
                 BulletPoint(
                     String(
                     String(

Trio/Sources/Modules/Onboarding/View/OnboardingSteps/BasalProfileStepView.swift → Trio/Sources/Modules/Onboarding/View/OnboardingSteps/TherapySettings/BasalProfileStepView.swift


Trio/Sources/Modules/Onboarding/View/OnboardingSteps/CarbRatioStepView.swift → Trio/Sources/Modules/Onboarding/View/OnboardingSteps/TherapySettings/CarbRatioStepView.swift


Trio/Sources/Modules/Onboarding/View/OnboardingSteps/GlucoseTargetStepView.swift → Trio/Sources/Modules/Onboarding/View/OnboardingSteps/TherapySettings/GlucoseTargetStepView.swift


+ 8 - 10
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/InsulinSensitivityStepView.swift

@@ -75,9 +75,8 @@ struct InsulinSensitivityStepView: View {
                         VStack(alignment: .leading, spacing: 8) {
                         VStack(alignment: .leading, spacing: 8) {
                             // Current glucose is 40 mg/dL or 2.2 mmol/L above target
                             // Current glucose is 40 mg/dL or 2.2 mmol/L above target
                             let aboveTarget = state.units == .mgdL ? Decimal(40) : 40.asMmolL
                             let aboveTarget = state.units == .mgdL ? Decimal(40) : 40.asMmolL
-
-                            let isfValue = state.units == .mgdL ? Decimal(50) : 50.asMmolL
-
+                            let firstIsfRate: Decimal = state.isfRateValues[state.isfItems.first?.rateIndex ?? 0]
+                            let isfValue = state.units == .mgdL ? firstIsfRate : firstIsfRate.asMmolL
                             let insulinNeeded = aboveTarget / isfValue
                             let insulinNeeded = aboveTarget / isfValue
 
 
                             Text(
                             Text(
@@ -87,11 +86,10 @@ struct InsulinSensitivityStepView: View {
                             .padding(.horizontal)
                             .padding(.horizontal)
 
 
                             Text(
                             Text(
-                                "\(numberFormatter.string(from: aboveTarget as NSNumber) ?? "--") / \(numberFormatter.string(from: isfValue as NSNumber) ?? "--") = \(String(format: "%.1f", Double(insulinNeeded)))" +
-                                    " " + String(localized: "U", comment: "Insulin unit abbreviation")
+                                "\(aboveTarget.description) \(state.units.rawValue) / \(isfValue.description) \(state.units.rawValue)/\(String(localized: "U", comment: "Insulin unit abbreviation")) = \(String(format: "%.1f", Double(insulinNeeded))) \(String(localized: "U", comment: "Insulin unit abbreviation"))"
                             )
                             )
                             .font(.system(.body, design: .monospaced))
                             .font(.system(.body, design: .monospaced))
-                            .foregroundColor(.red)
+                            .foregroundColor(.cyan)
                             .padding()
                             .padding()
                             .frame(maxWidth: .infinity, alignment: .center)
                             .frame(maxWidth: .infinity, alignment: .center)
                             .background(Color.chart.opacity(0.65))
                             .background(Color.chart.opacity(0.65))
@@ -163,8 +161,8 @@ struct InsulinSensitivityStepView: View {
                 ).foregroundStyle(
                 ).foregroundStyle(
                     .linearGradient(
                     .linearGradient(
                         colors: [
                         colors: [
-                            Color.red.opacity(0.6),
-                            Color.red.opacity(0.1)
+                            Color.cyan.opacity(0.6),
+                            Color.cyan.opacity(0.1)
                         ],
                         ],
                         startPoint: .bottom,
                         startPoint: .bottom,
                         endPoint: .top
                         endPoint: .top
@@ -172,10 +170,10 @@ struct InsulinSensitivityStepView: View {
                 ).alignsMarkStylesWithPlotArea()
                 ).alignsMarkStylesWithPlotArea()
 
 
                 LineMark(x: .value("End Date", startDate), y: .value("ISF", displayValue))
                 LineMark(x: .value("End Date", startDate), y: .value("ISF", displayValue))
-                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.red)
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.cyan)
 
 
                 LineMark(x: .value("Start Date", endDate), y: .value("ISF", displayValue))
                 LineMark(x: .value("Start Date", endDate), y: .value("ISF", displayValue))
-                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.red)
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.cyan)
             }
             }
         }
         }
         .id(refreshUI) // Force chart update
         .id(refreshUI) // Force chart update

+ 13 - 10
Trio/Sources/Modules/Onboarding/View/OnboardingView+AlgorithmUtil.swift

@@ -99,8 +99,8 @@ enum AlgorithmSettingsSubstep: Int, CaseIterable, Identifiable {
 
 
     var title: String {
     var title: String {
         switch self {
         switch self {
-        case .autosensMin: return String(localized: "Autosens Min", comment: "Autosens Min")
-        case .autosensMax: return String(localized: "Autosens Max", comment: "Autosens Max")
+        case .autosensMin: return String(localized: "Autosens Minimum", comment: "Autosens Min")
+        case .autosensMax: return String(localized: "Autosens Maximum", comment: "Autosens Max")
         case .rewindResetsAutosens: return String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens")
         case .rewindResetsAutosens: return String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens")
         case .enableSMBAlways: return String(localized: "Enable SMB Always", comment: "Enable SMB Always")
         case .enableSMBAlways: return String(localized: "Enable SMB Always", comment: "Enable SMB Always")
         case .enableSMBWithCOB: return String(localized: "Enable SMB With COB", comment: "Enable SMB With COB")
         case .enableSMBWithCOB: return String(localized: "Enable SMB With COB", comment: "Enable SMB With COB")
@@ -110,17 +110,20 @@ enum AlgorithmSettingsSubstep: Int, CaseIterable, Identifiable {
             )
             )
         case .enableSMBAfterCarbs: return String(localized: "Enable SMB After Carbs", comment: "Enable SMB After Carbs")
         case .enableSMBAfterCarbs: return String(localized: "Enable SMB After Carbs", comment: "Enable SMB After Carbs")
         case .enableSMBWithHighGlucoseTarget: return String(
         case .enableSMBWithHighGlucoseTarget: return String(
-                localized: "Enable SMB With High BG",
-                comment: "Enable SMB With High BG"
+                localized: "Enable SMB With High Glucose",
+                comment: "Enable SMB With High Glucose"
             )
             )
         case .allowSMBWithHighTempTarget: return String(
         case .allowSMBWithHighTempTarget: return String(
                 localized: "Allow SMB With High Temptarget",
                 localized: "Allow SMB With High Temptarget",
                 comment: "Allow SMB With High Temptarget"
                 comment: "Allow SMB With High Temptarget"
             )
             )
-        case .enableUAM: return String(localized: "Enable UAM", comment: "Enable UAM")
+        case .enableUAM: return String(localized: "Enable UAM (Unannounced Meals)", comment: "Enable UAM")
         case .maxSMBMinutes: return String(localized: "Max SMB Basal Minutes", comment: "Max SMB Basal Minutes")
         case .maxSMBMinutes: return String(localized: "Max SMB Basal Minutes", comment: "Max SMB Basal Minutes")
         case .maxUAMMinutes: return String(localized: "Max UAM Basal Minutes", comment: "Max UAM Basal Minutes")
         case .maxUAMMinutes: return String(localized: "Max UAM Basal Minutes", comment: "Max UAM Basal Minutes")
-        case .maxDeltaGlucoseThreshold: return String(localized: "Max Delta-BG Threshold SMB", comment: "Max Delta-BG Threshold")
+        case .maxDeltaGlucoseThreshold: return String(
+                localized: "Max. Allowed Glucose Rise for SMB",
+                comment: "Max. Allowed Glucose Rise for SMB, formerly Max Delta-BG Threshold"
+            )
         case .highTempTargetRaisesSensitivity: return String(
         case .highTempTargetRaisesSensitivity: return String(
                 localized: "High Temp Target Raises Sensitivity",
                 localized: "High Temp Target Raises Sensitivity",
                 comment: "High Temp Target Raises Sensitivity"
                 comment: "High Temp Target Raises Sensitivity"
@@ -147,7 +150,7 @@ enum AlgorithmSettingsSubstep: Int, CaseIterable, Identifiable {
                 localized: "Allow SMB when a manual Temporary Target is set under \(units == .mgdL ? "100" : 100.formattedAsMmolL) \(units.rawValue)."
                 localized: "Allow SMB when a manual Temporary Target is set under \(units == .mgdL ? "100" : 100.formattedAsMmolL) \(units.rawValue)."
             )
             )
         case .enableSMBAfterCarbs: return String(localized: "Allow SMB for 6 hrs after a carb entry.")
         case .enableSMBAfterCarbs: return String(localized: "Allow SMB for 6 hrs after a carb entry.")
-        case .enableSMBWithHighGlucoseTarget: return String(localized: "Allow SMB when glucose is above the High BG Target value.")
+        case .enableSMBWithHighGlucoseTarget: return String(localized: "Allow SMB when glucose is above the High Glucose Target value.")
         case .allowSMBWithHighTempTarget: return String(
         case .allowSMBWithHighTempTarget: return String(
                 localized: "Allow SMB when a manual Temporary Target is set greater than \(units == .mgdL ? "100" : 100.formattedAsMmolL) \(units.rawValue)."
                 localized: "Allow SMB when a manual Temporary Target is set greater than \(units == .mgdL ? "100" : 100.formattedAsMmolL) \(units.rawValue)."
             )
             )
@@ -161,8 +164,8 @@ enum AlgorithmSettingsSubstep: Int, CaseIterable, Identifiable {
         case .lowTempTargetLowersSensitivity: return String(
         case .lowTempTargetLowersSensitivity: return String(
                 localized: "Decrease sensitivity when glucose is below target if a manual Temp Target < \(units == .mgdL ? "100" : 100.formattedAsMmolL) \(units.rawValue) is set."
                 localized: "Decrease sensitivity when glucose is below target if a manual Temp Target < \(units == .mgdL ? "100" : 100.formattedAsMmolL) \(units.rawValue) is set."
             )
             )
-        case .sensitivityRaisesTarget: return String(localized: "Raise target glucose if when Autosens Ratio is >1.")
-        case .resistanceLowersTarget: return String(localized: "Lower target glucose when Autosens Ratio is <1.")
+        case .sensitivityRaisesTarget: return String(localized: "Raise target glucose when Autosens Ratio is less than 1.")
+        case .resistanceLowersTarget: return String(localized: "Lower target glucose when Autosens Ratio is greater than 1.")
         case .halfBasalTarget: return String(localized: "Scales down your basal rate to 50% at this value.")
         case .halfBasalTarget: return String(localized: "Scales down your basal rate to 50% at this value.")
         }
         }
     }
     }
@@ -261,7 +264,7 @@ enum AlgorithmSettingsSubstep: Int, CaseIterable, Identifiable {
             return VStack(alignment: .leading, spacing: 8) {
             return VStack(alignment: .leading, spacing: 8) {
                 Text("Default: OFF").bold().foregroundStyle(Color.primary)
                 Text("Default: OFF").bold().foregroundStyle(Color.primary)
                 Text(
                 Text(
-                    "Enabling this feature allows Trio to deliver insulin required using Super Micro Boluses (SMB) when glucose reading is above the value set as High BG Target."
+                    "Enabling this feature allows Trio to deliver insulin required using Super Micro Boluses (SMB) when glucose reading is above the value set as High Glucose Target."
                 )
                 )
                 Text(
                 Text(
                     "Note: If this is enabled and the criteria are met, SMBs could be utilized regardless of other SMB settings being enabled or not."
                     "Note: If this is enabled and the criteria are met, SMBs could be utilized regardless of other SMB settings being enabled or not."

+ 12 - 8
Trio/Sources/Modules/Onboarding/View/OnboardingView+Util.swift

@@ -318,7 +318,7 @@ enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
         case .carbRatio:
         case .carbRatio:
             return Color.orange
             return Color.orange
         case .insulinSensitivity:
         case .insulinSensitivity:
-            return Color.red
+            return Color.cyan
         }
         }
     }
     }
 
 
@@ -365,10 +365,10 @@ enum DeliveryLimitSubstep: Int, CaseIterable, Identifiable {
 
 
     var title: String {
     var title: String {
         switch self {
         switch self {
-        case .maxIOB: return String(localized: "Max IOB", comment: "Max IOB")
-        case .maxBolus: return String(localized: "Max Bolus")
-        case .maxBasal: return String(localized: "Max Basal Rate")
-        case .maxCOB: return String(localized: "Max COB", comment: "Max COB")
+        case .maxIOB: return String(localized: "Maximum Insulin on Board (IOB)", comment: "Max IOB")
+        case .maxBolus: return String(localized: "Maximum Bolus")
+        case .maxBasal: return String(localized: "Maximum Basal Rate")
+        case .maxCOB: return String(localized: "Maximum Carbs on Board (COB)", comment: "Max COB")
         case .minimumSafetyThreshold: return String(localized: "Minimum Safety Threshold")
         case .minimumSafetyThreshold: return String(localized: "Minimum Safety Threshold")
         }
         }
     }
     }
@@ -378,7 +378,7 @@ enum DeliveryLimitSubstep: Int, CaseIterable, Identifiable {
         case .maxIOB: return String(localized: "Maximum units of insulin allowed to be active.")
         case .maxIOB: return String(localized: "Maximum units of insulin allowed to be active.")
         case .maxBolus: return String(localized: "Largest bolus of insulin allowed.")
         case .maxBolus: return String(localized: "Largest bolus of insulin allowed.")
         case .maxBasal: return String(localized: "Largest basal rate allowed.")
         case .maxBasal: return String(localized: "Largest basal rate allowed.")
-        case .maxCOB: return String(localized: "Maximum Carbs On Board (COB) allowed.")
+        case .maxCOB: return String(localized: "Maximum amount of active carbs considered by the algorithm.")
         case .minimumSafetyThreshold: return String(localized: "Increase the safety threshold used to suspend insulin delivery.")
         case .minimumSafetyThreshold: return String(localized: "Increase the safety threshold used to suspend insulin delivery.")
         }
         }
     }
     }
@@ -389,7 +389,11 @@ enum DeliveryLimitSubstep: Int, CaseIterable, Identifiable {
             return VStack(alignment: .leading, spacing: 8) {
             return VStack(alignment: .leading, spacing: 8) {
                 Text(
                 Text(
                     "Note: This setting must be greater than 0 for any automatic insulin dosing by Trio."
                     "Note: This setting must be greater than 0 for any automatic insulin dosing by Trio."
-                ).bold().foregroundStyle(Color.primary)
+                ).bold().foregroundStyle(Color.orange)
+
+                Text(
+                    "This setting helps prevent delivering too much insulin at once. It’s typically a value close to the amount you might need for a very high blood sugar and the biggest meal of your life combined."
+                )
 
 
                 Text(
                 Text(
                     "This is the maximum amount of Insulin On Board (IOB) above profile basal rates from all sources - positive temporary basal rates, manual or meal boluses, and SMBs - that Trio is allowed to accumulate to address an above target glucose."
                     "This is the maximum amount of Insulin On Board (IOB) above profile basal rates from all sources - positive temporary basal rates, manual or meal boluses, and SMBs - that Trio is allowed to accumulate to address an above target glucose."
@@ -493,7 +497,7 @@ enum PumpOptionForOnboardingUnits: String, Equatable, CaseIterable, Identifiable
         case .omnipodEros:
         case .omnipodEros:
             return "Omnipod Eros"
             return "Omnipod Eros"
         case .omnipodDash:
         case .omnipodDash:
-            return "Omnipod Dash"
+            return "Omnipod DASH"
         case .dana:
         case .dana:
             return "Dana (RS/-i)"
             return "Dana (RS/-i)"
         }
         }

+ 7 - 0
Trio/Sources/Modules/Onboarding/View/TherapySettingEditorView.swift

@@ -114,6 +114,13 @@ struct TherapySettingEditorView: View {
         .onAppear {
         .onAppear {
             // ensure picker is closed when view appears
             // ensure picker is closed when view appears
             selectedItemID = nil
             selectedItemID = nil
+            // sorts items
+            validateTherapySettingItems()
+        }
+        .onDisappear {
+            // ensure picker is closed when view appears
+            selectedItemID = nil
+            // sorts items
             validateTherapySettingItems()
             validateTherapySettingItems()
         }
         }
         .onChange(of: items, { _, _ in
         .onChange(of: items, { _, _ in

+ 2 - 2
Trio/Sources/Modules/PumpConfig/View/PumpConfigRootView.swift

@@ -116,7 +116,7 @@ extension PumpConfig {
                                 VStack(alignment: .leading) {
                                 VStack(alignment: .leading) {
                                     Text("• Medtronic")
                                     Text("• Medtronic")
                                     Text("• Omnipod Eros")
                                     Text("• Omnipod Eros")
-                                    Text("• Omnipod Dash")
+                                    Text("• Omnipod DASH")
                                     Text("• Dana (RS/-i)")
                                     Text("• Dana (RS/-i)")
                                     Text("• Pump Simulator")
                                     Text("• Pump Simulator")
                                 }
                                 }
@@ -131,7 +131,7 @@ extension PumpConfig {
                 .confirmationDialog("Pump Model", isPresented: $showPumpSelection) {
                 .confirmationDialog("Pump Model", isPresented: $showPumpSelection) {
                     Button("Medtronic") { state.addPump(.minimed) }
                     Button("Medtronic") { state.addPump(.minimed) }
                     Button("Omnipod Eros") { state.addPump(.omnipod) }
                     Button("Omnipod Eros") { state.addPump(.omnipod) }
-                    Button("Omnipod Dash") { state.addPump(.omnipodBLE) }
+                    Button("Omnipod DASH") { state.addPump(.omnipodBLE) }
                     Button("Dana(RS/-i)") { state.addPump(.dana) }
                     Button("Dana(RS/-i)") { state.addPump(.dana) }
                     Button("Pump Simulator") { state.addPump(.simulator) }
                     Button("Pump Simulator") { state.addPump(.simulator) }
                 } message: { Text("Select Pump Model") }
                 } message: { Text("Select Pump Model") }

+ 8 - 2
Trio/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift

@@ -330,12 +330,18 @@ extension SMBSettings {
                         get: { selectedVerboseHint },
                         get: { selectedVerboseHint },
                         set: {
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = String(localized: "Max Delta-BG Threshold SMB", comment: "Max Delta-BG Threshold")
+                            hintLabel = String(
+                                localized: "Max. Allowed Glucose Rise for SMB",
+                                comment: "Max. Allowed Glucose Rise for SMB, formerly Max Delta-BG Threshold"
+                            )
                         }
                         }
                     ),
                     ),
                     units: state.units,
                     units: state.units,
                     type: .decimal("maxDeltaBGthreshold"),
                     type: .decimal("maxDeltaBGthreshold"),
-                    label: String(localized: "Max Delta-BG Threshold SMB", comment: "Max Delta-BG Threshold"),
+                    label: String(
+                        localized: "Max. Allowed Glucose Rise for SMB",
+                        comment: "Max. Allowed Glucose Rise for SMB, formerly Max Delta-BG Threshold"
+                    ),
                     miniHint: String(localized: "Disables SMBs if last two glucose values differ by more than this percent."),
                     miniHint: String(localized: "Disables SMBs if last two glucose values differ by more than this percent."),
                     verboseHint:
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                     VStack(alignment: .leading, spacing: 10) {

+ 12 - 1
Trio/Sources/Modules/Settings/SettingItems.swift

@@ -231,7 +231,18 @@ enum SettingItems {
             ],
             ],
             path: ["Features", "User Interface"]
             path: ["Features", "User Interface"]
         ),
         ),
-        SettingItem(title: "App Icons", view: .iconConfig)
+        SettingItem(
+            title: "App Icons",
+            view: .iconConfig,
+            searchContents: ["Trio Icon"],
+            path: ["Features", "App Icons"]
+        ),
+        SettingItem(
+            title: "App Diagnostics",
+            view: .appDiagnostics,
+            searchContents: ["Anonymized Data Sharing"],
+            path: ["Features", "App Diagnostics"]
+        )
     ]
     ]
 
 
     static let notificationItems = [
     static let notificationItems = [

+ 2 - 2
Trio/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift

@@ -110,7 +110,7 @@ extension TargetBehavoir {
                     units: state.units,
                     units: state.units,
                     type: .boolean,
                     type: .boolean,
                     label: String(localized: "Sensitivity Raises Target", comment: "Sensitivity Raises Target"),
                     label: String(localized: "Sensitivity Raises Target", comment: "Sensitivity Raises Target"),
-                    miniHint: String(localized: "Raise target glucose when Autosens Ratio is <1."),
+                    miniHint: String(localized: "Raise target glucose when Autosens Ratio is less than 1."),
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
                         Text("Default: OFF").bold()
                         Text(
                         Text(
@@ -133,7 +133,7 @@ extension TargetBehavoir {
                     units: state.units,
                     units: state.units,
                     type: .boolean,
                     type: .boolean,
                     label: String(localized: "Resistance Lowers Target", comment: "Resistance Lowers Target"),
                     label: String(localized: "Resistance Lowers Target", comment: "Resistance Lowers Target"),
-                    miniHint: String(localized: "Lower target glucose when Autosens Ratio is >1."),
+                    miniHint: String(localized: "Lower target glucose when Autosens Ratio is greater than 1."),
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
                         Text("Default: OFF").bold()
                         Text(
                         Text(

+ 1 - 1
Trio/Sources/Modules/TargetsEditor/TargetsEditorStateModel.swift

@@ -13,7 +13,7 @@ extension TargetsEditor {
 
 
         var rateValues: [Decimal] {
         var rateValues: [Decimal] {
             let settingsProvider = PickerSettingsProvider.shared
             let settingsProvider = PickerSettingsProvider.shared
-            let glucoseSetting = PickerSetting(value: 0, step: 1, min: 72, max: 180, type: .glucose)
+            let glucoseSetting = PickerSetting(value: 110, step: 1, min: 72, max: 180, type: .glucose)
             return settingsProvider.generatePickerValues(from: glucoseSetting, units: units)
             return settingsProvider.generatePickerValues(from: glucoseSetting, units: units)
         }
         }