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

Update Bolus View and Meal View

Meal View
* Make presets UX/UI a bit cleaner (still far from perfect now)
* Make a summary for all presets and other entries used
* Make the important buttons blue and white (similar to Loop). 
* Prevent inadvertently changing the date of entry. Now you need to tap twice to change. Also allow for + - of 10 minute increments (also similar to Loop) with small button. 

Bolus View
* Display all of the glucose predictions in bolus view
* Display the future Eventual Glucose (end time depends on your DIA)
* Whilst waiting for the recommendation (including predictions) allow a manual bolus entry.
* Make the "Enact bolus" button blue, like in Loop.
* Allow to add end edit a meal also when you have Skip Bolus setting on. Bolus->Carbs->Bolus sequence is now allowed.
Jon B Mårtensson 2 лет назад
Родитель
Сommit
d6f817543e
37 измененных файлов с 696 добавлено и 231 удалено
  1. 1 1
      Config.xcconfig
  2. 11 0
      Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents
  3. 1 1
      Dependencies/CGMBLEKit/CGMBLEKitUI/de.lproj/Localizable.strings
  4. 1 1
      Dependencies/G7SensorKit/G7SensorKitUI/de.lproj/Localizable.strings
  5. 3 3
      Dependencies/G7SensorKit/de.lproj/Localizable.strings
  6. 8 4
      FreeAPS.xcodeproj/project.pbxproj
  7. 11 0
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  8. 11 0
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  9. 11 0
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  10. 9 0
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  11. 11 0
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  12. 11 0
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  13. 11 0
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  14. 11 0
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  15. 14 3
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  16. 23 12
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  17. 11 0
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  18. 11 0
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  19. 11 0
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  20. 11 0
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  21. 24 13
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  22. 11 0
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  23. 12 0
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  24. 11 0
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  25. 11 0
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  26. 11 0
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  27. 17 0
      FreeAPS/Sources/Models/Charts.swift
  28. 0 8
      FreeAPS/Sources/Models/TIRforChart.swift
  29. 19 17
      FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift
  30. 167 101
      FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
  31. 9 7
      FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift
  32. 47 27
      FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift
  33. 50 28
      FreeAPS/Sources/Modules/Bolus/View/DefaultBolusCalcRootView.swift
  34. 110 0
      FreeAPS/Sources/Modules/Bolus/View/Predictions.swift
  35. 1 1
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  36. 1 1
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  37. 3 3
      FreeAPS/Sources/Router/Screen.swift

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = iAPS
 APP_DISPLAY_NAME = iAPS
-APP_VERSION = 2.2.7
+APP_VERSION = 2.2.8
 APP_BUILD_NUMBER = 1
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 COPYRIGHT_NOTICE =
 DEVELOPER_TEAM = ##TEAM_ID##
 DEVELOPER_TEAM = ##TEAM_ID##

+ 11 - 0
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -1,5 +1,16 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22225" systemVersion="22G120" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
 <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22225" systemVersion="22G120" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+    <entity name="Autosens_" representedClassName="Autosens_" syncable="YES" codeGenerationType="class">
+        <attribute name="newisf" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="ratio" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <entity name="Autotune_" representedClassName="Autotune_" syncable="YES" codeGenerationType="class">
+        <attribute name="basalProfile" optional="YES" attributeType="Transformable"/>
+        <attribute name="carbRatio" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="sensitivity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
     <entity name="BGaverages" representedClassName="BGaverages" syncable="YES" codeGenerationType="class">
     <entity name="BGaverages" representedClassName="BGaverages" syncable="YES" codeGenerationType="class">
         <attribute name="average" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>

+ 1 - 1
Dependencies/CGMBLEKit/CGMBLEKitUI/de.lproj/Localizable.strings

@@ -39,7 +39,7 @@ Title text for the button to remove a CGM from Loop */
 "Remote Data Synchronization" = "Remote Daten Synchronisation";
 "Remote Data Synchronization" = "Remote Daten Synchronisation";
 
 
 /* Title describing sensor expiration */
 /* Title describing sensor expiration */
-"Sensor Expires" = "Sensor-Ablaufzeitpunkt";
+"Sensor Expires" = "Sensor läuft ab";
 
 
 /* Title describing past sensor expiration */
 /* Title describing past sensor expiration */
 "Sensor Expired" = "Sensor abgelaufen";
 "Sensor Expired" = "Sensor abgelaufen";

+ 1 - 1
Dependencies/G7SensorKit/G7SensorKitUI/de.lproj/Localizable.strings

@@ -54,7 +54,7 @@
 "Last Reading" = "Letzte Messung";
 "Last Reading" = "Letzte Messung";
 
 
 /* Descriptive text on G7StartupView */
 /* Descriptive text on G7StartupView */
-"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "iAPS kann CGM Daten vom G7 direkt lesen. Zum Verbinden, Kalibrieren und weiteres Sensor Management braucht man die G7 App.";
+"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "iAPS kann CGM Daten direkt vom G7 lesen. Zum Verbinden, Kalibrieren und erweitertem Sensor Management benötigt man die G7 App.";
 
 
 /* String displayed instead of a glucose value below the CGM range */
 /* String displayed instead of a glucose value below the CGM range */
 "LOW" = "NIEDRIG";
 "LOW" = "NIEDRIG";

+ 3 - 3
Dependencies/G7SensorKit/de.lproj/Localizable.strings

@@ -2,7 +2,7 @@
 "Dexcom G7" = "Dexcom G7";
 "Dexcom G7" = "Dexcom G7";
 
 
 /* Descriptive text on G7StartupView */
 /* Descriptive text on G7StartupView */
-"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "iAPS kann CGM Daten vom G7 direkt lesen. Zum Verbinden, Kalibrieren und weiteres Sensor Management braucht man die G7 App.";
+"iAPS can read G7 CGM data, but you must still use the Dexcom G7 App for pairing, calibration, and other sensor management." = "iAPS kann CGM Daten direkt vom G7 lesen. Zum Verbinden, Kalibrieren und erweitertes Sensor Management benötigt man die G7 App.";
 
 
 /* Button title for starting setup */
 /* Button title for starting setup */
 "Continue" = "Fortsetzen";
 "Continue" = "Fortsetzen";
@@ -11,7 +11,7 @@
 "Cancel" = "Abbrechen";
 "Cancel" = "Abbrechen";
 
 
 /* Error description for unreliable state */
 /* Error description for unreliable state */
-"Glucose data is unavailable" = "Blutzuckerwerte sind nicht verfügbar";
+"Glucose data is unavailable" = "Glukosewerte sind nicht verfügbar";
 
 
 /* The description of sensor algorithm state when sensor is ok. */
 /* The description of sensor algorithm state when sensor is ok. */
 "Sensor is OK" = "Sensor ist OK";
 "Sensor is OK" = "Sensor ist OK";
@@ -70,7 +70,7 @@
 "Configuration" = "Konfiguration";
 "Configuration" = "Konfiguration";
 
 
 /* title for g7 config settings to upload readings */
 /* title for g7 config settings to upload readings */
-"Upload Readings" = "Upload von Messwerten";
+"Upload Readings" = "Werte hochladen";
 
 
 /* Button */
 /* Button */
 "Scan for new sensor" = "Nach neuem Sensor suchen";
 "Scan for new sensor" = "Nach neuem Sensor suchen";

+ 8 - 4
FreeAPS.xcodeproj/project.pbxproj

@@ -19,6 +19,7 @@
 		190EBCC829FF13AA00BA767D /* StatConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* StatConfigStateModel.swift */; };
 		190EBCC829FF13AA00BA767D /* StatConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* StatConfigStateModel.swift */; };
 		190EBCCB29FF13CB00BA767D /* StatConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */; };
 		190EBCCB29FF13CB00BA767D /* StatConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */; };
 		191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */; };
 		191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */; };
+		19229B962AFBB84800CD91CA /* Predictions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19229B952AFBB84800CD91CA /* Predictions.swift */; };
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
@@ -40,7 +41,7 @@
 		19D466A529AA2BD4004D5F33 /* FPUConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */; };
 		19D466A529AA2BD4004D5F33 /* FPUConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */; };
 		19D466A729AA2C22004D5F33 /* FPUConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */; };
 		19D466A729AA2C22004D5F33 /* FPUConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */; };
 		19D466AA29AA3099004D5F33 /* FPUConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */; };
 		19D466AA29AA3099004D5F33 /* FPUConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */; };
-		19D4E4EB29FC6A9F00351451 /* TIRforChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D4E4EA29FC6A9F00351451 /* TIRforChart.swift */; };
+		19D4E4EB29FC6A9F00351451 /* Charts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D4E4EA29FC6A9F00351451 /* Charts.swift */; };
 		19DA48E829CD339B00EEA1E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; };
 		19DA48E829CD339B00EEA1E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; };
 		19DA48E929CD339C00EEA1E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; };
 		19DA48E929CD339C00EEA1E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; };
 		19DA48EA29CD339C00EEA1E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; };
 		19DA48EA29CD339C00EEA1E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */; };
@@ -508,6 +509,7 @@
 		190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatConfigRootView.swift; sourceTree = "<group>"; };
 		190EBCCA29FF13CB00BA767D /* StatConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatConfigRootView.swift; sourceTree = "<group>"; };
 		1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = "<group>"; };
 		191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = "<group>"; };
+		19229B952AFBB84800CD91CA /* Predictions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Predictions.swift; sourceTree = "<group>"; };
 		1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8EB2744611900347C69 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		1927C8EB2744611900347C69 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
@@ -568,7 +570,7 @@
 		19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigProvider.swift; sourceTree = "<group>"; };
 		19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigProvider.swift; sourceTree = "<group>"; };
 		19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigStateModel.swift; sourceTree = "<group>"; };
 		19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigStateModel.swift; sourceTree = "<group>"; };
 		19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigRootView.swift; sourceTree = "<group>"; };
 		19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigRootView.swift; sourceTree = "<group>"; };
-		19D4E4EA29FC6A9F00351451 /* TIRforChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TIRforChart.swift; sourceTree = "<group>"; };
+		19D4E4EA29FC6A9F00351451 /* Charts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Charts.swift; sourceTree = "<group>"; };
 		19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		19DC677E29CA675700FD9EC4 /* OverrideProfilesDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProfilesDataFlow.swift; sourceTree = "<group>"; };
 		19DC677E29CA675700FD9EC4 /* OverrideProfilesDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProfilesDataFlow.swift; sourceTree = "<group>"; };
 		19DC678029CA676A00FD9EC4 /* OverrideProfilesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProfilesProvider.swift; sourceTree = "<group>"; };
 		19DC678029CA676A00FD9EC4 /* OverrideProfilesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProfilesProvider.swift; sourceTree = "<group>"; };
@@ -1655,7 +1657,7 @@
 				FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */,
 				FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */,
 				191F62672AD6B05A004D7911 /* NightscoutSettings.swift */,
 				191F62672AD6B05A004D7911 /* NightscoutSettings.swift */,
 				1967DFBD29D052C200759F30 /* Icons.swift */,
 				1967DFBD29D052C200759F30 /* Icons.swift */,
-				19D4E4EA29FC6A9F00351451 /* TIRforChart.swift */,
+				19D4E4EA29FC6A9F00351451 /* Charts.swift */,
 				19A910352A24D6D700C8951B /* DateFilter.swift */,
 				19A910352A24D6D700C8951B /* DateFilter.swift */,
 				193F6CDC2A512C8F001240FD /* Loops.swift */,
 				193F6CDC2A512C8F001240FD /* Loops.swift */,
 				CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */,
 				CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */,
@@ -2084,6 +2086,7 @@
 				10A0C32B0DAB52726EF9B6D9 /* BolusRootView.swift */,
 				10A0C32B0DAB52726EF9B6D9 /* BolusRootView.swift */,
 				BDFD165B2AE40688007F0DDA /* DefaultBolusCalcRootView.swift */,
 				BDFD165B2AE40688007F0DDA /* DefaultBolusCalcRootView.swift */,
 				BDFD16592AE40438007F0DDA /* AlternativeBolusCalcRootView.swift */,
 				BDFD16592AE40438007F0DDA /* AlternativeBolusCalcRootView.swift */,
+				19229B952AFBB84800CD91CA /* Predictions.swift */,
 			);
 			);
 			path = View;
 			path = View;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2830,6 +2833,7 @@
 				1967DFBE29D052C200759F30 /* Icons.swift in Sources */,
 				1967DFBE29D052C200759F30 /* Icons.swift in Sources */,
 				38E8754F275556FA00975559 /* WatchManager.swift in Sources */,
 				38E8754F275556FA00975559 /* WatchManager.swift in Sources */,
 				A228DF96647338139F152B15 /* PreferencesEditorDataFlow.swift in Sources */,
 				A228DF96647338139F152B15 /* PreferencesEditorDataFlow.swift in Sources */,
+				19229B962AFBB84800CD91CA /* Predictions.swift in Sources */,
 				389ECE052601144100D86C4F /* ConcurrentMap.swift in Sources */,
 				389ECE052601144100D86C4F /* ConcurrentMap.swift in Sources */,
 				CE7CA3562A064973004BE681 /* StateIntentRequest.swift in Sources */,
 				CE7CA3562A064973004BE681 /* StateIntentRequest.swift in Sources */,
 				E4984C5262A90469788754BB /* PreferencesEditorProvider.swift in Sources */,
 				E4984C5262A90469788754BB /* PreferencesEditorProvider.swift in Sources */,
@@ -2862,7 +2866,7 @@
 				0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */,
 				0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */,
 				1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */,
 				1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */,
 				BDFD165C2AE40688007F0DDA /* DefaultBolusCalcRootView.swift in Sources */,
 				BDFD165C2AE40688007F0DDA /* DefaultBolusCalcRootView.swift in Sources */,
-				19D4E4EB29FC6A9F00351451 /* TIRforChart.swift in Sources */,
+				19D4E4EB29FC6A9F00351451 /* Charts.swift in Sources */,
 				FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */,
 				FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */,
 				F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */,
 				F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */,
 				3862CC1F273FDC9200BF832C /* CalibrationsChart.swift in Sources */,
 				3862CC1F273FDC9200BF832C /* CalibrationsChart.swift in Sources */,

+ 11 - 0
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continue";
+
 /* Home title */
 /* Home title */
 "Home" = "Home";
 "Home" = "Home";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbs required";
 "Carbs required" = "Carbs required";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Are you sure?";
 "Are you sure?" = "Are you sure?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Fortsæt";
+
 /* Home title */
 /* Home title */
 "Home" = "Hjem";
 "Home" = "Hjem";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Krævede kulhydrater";
 "Carbs required" = "Krævede kulhydrater";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Er du sikker?";
 "Are you sure?" = "Er du sikker?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventuelt Glukoseniveau";
 "Eventual Glucose" = "Eventuelt Glukoseniveau";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Dein eingegebener Wert wurde durch deine maximale Bolus-Einstellung von %d%@ begrenzt";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Dein eingegebener Wert wurde durch deine maximale Bolus-Einstellung von %d%@ begrenzt";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Fortsetzen";
+
 /* Home title */
 /* Home title */
 "Home" = "Hauptseite";
 "Home" = "Hauptseite";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Kohlenhydrate erforderlich";
 "Carbs required" = "Kohlenhydrate erforderlich";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Sind Sie sicher?";
 "Are you sure?" = "Sind Sie sicher?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Prognostizierte Glukose";
 "Eventual Glucose" = "Prognostizierte Glukose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Ziel Glukose";
 "Target Glucose" = "Ziel Glukose";
 
 
 /* */
 /* */

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

@@ -121,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbs required";
 "Carbs required" = "Carbs required";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Are you sure?";
 "Are you sure?" = "Are you sure?";
 
 
@@ -1401,6 +1404,12 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+/* */
+"Glucose, " = "Glucose, ";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continuar";
+
 /* Home title */
 /* Home title */
 "Home" = "Inicio";
 "Home" = "Inicio";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Se requieren carbohidratos";
 "Carbs required" = "Se requieren carbohidratos";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "¿Estás seguro?";
 "Are you sure?" = "¿Estás seguro?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Jatka";
+
 /* Home title */
 /* Home title */
 "Home" = "Home";
 "Home" = "Home";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbs required";
 "Carbs required" = "Carbs required";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Are you sure?";
 "Are you sure?" = "Are you sure?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continuer";
+
 /* Home title */
 /* Home title */
 "Home" = "Page d'accueil";
 "Home" = "Page d'accueil";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Glucides requis";
 "Carbs required" = "Glucides requis";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Êtes-vous sûr ?";
 "Are you sure?" = "Êtes-vous sûr ?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continue";
+
 /* Home title */
 /* Home title */
 "Home" = "Home";
 "Home" = "Home";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbs required";
 "Carbs required" = "Carbs required";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Are you sure?";
 "Are you sure?" = "Are you sure?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 14 - 3
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Il tuo importo inserito è stato limitato dalla tua impostazione del Bolo massimo di %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Il tuo importo inserito è stato limitato dalla tua impostazione del Bolo massimo di %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continua";
+
 /* Home title */
 /* Home title */
 "Home" = "Home";
 "Home" = "Home";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carboidrati necessari";
 "Carbs required" = "Carboidrati necessari";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Sei sicuro?";
 "Are you sure?" = "Sei sicuro?";
 
 
@@ -1029,7 +1035,7 @@ Enact a temp Basal or a temp target */
 "Last loop was more than %d min ago" = "L'ultimo ciclo è stato più di %d min fa";
 "Last loop was more than %d min ago" = "L'ultimo ciclo è stato più di %d min fa";
 
 
 /* Glucose badge */
 /* Glucose badge */
-"Show glucose on the app badge" = "Mostra il glicemie sul badge dell'app";
+"Show glucose on the app badge" = "Mostra le glicemie sul badge dell'app";
 
 
 /* */
 /* */
 "Backfill glucose" = "Riempi glicemia";
 "Backfill glucose" = "Riempi glicemia";
@@ -1134,7 +1140,7 @@ Enact a temp Basal or a temp target */
 "Only Autotune Basal Insulin" = "Solo Insulina Basale Autotune";
 "Only Autotune Basal Insulin" = "Solo Insulina Basale Autotune";
 
 
 /* */
 /* */
-"Save as your Normal Basal Rates" = "Salva come Tassi Basali Normali";
+"Save as your Normal Basal Rates" = "Salva come Profilo Basale Normale";
 
 
 /* */
 /* */
 "Save on Pump" = "Salva sul microinfusore";
 "Save on Pump" = "Salva sul microinfusore";
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "previsione glicemica";
 "Eventual Glucose" = "previsione glicemica";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glicemia";
 "Target Glucose" = "Target Glicemia";
 
 
 /* */
 /* */
@@ -1556,7 +1567,7 @@ Enact a temp Basal or a temp target */
 "Total Delivery" = "Totale Consegnata";
 "Total Delivery" = "Totale Consegnata";
 
 
 /* */
 /* */
-"Add Omnipod Dash" = "Agg Omnipod Dash";
+"Add Omnipod Dash" = "Aggiungi Omnipod Dash";
 
 
 /* */
 /* */
 "Insert Cannula" = "Inserisci cannula";
 "Insert Cannula" = "Inserisci cannula";

+ 23 - 12
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings

@@ -56,37 +56,40 @@
 "Error at" = "Feil kl.";
 "Error at" = "Feil kl.";
 
 
 /* Bolus View Meal Summary Header */
 /* Bolus View Meal Summary Header */
-"Meal Summary" = "Meal Summary";
+"Meal Summary" = "Oppsummering av måltid";
 
 
 /* Bolus View Meal Edit Meal Button */
 /* Bolus View Meal Edit Meal Button */
-"Edit Meal" = "Edit Meal";
+"Edit Meal" = "Endre måltid";
 
 
 /* Bolus View Meal Add Meal Button */
 /* Bolus View Meal Add Meal Button */
-"Add Meal" = "Add Meal";
+"Add Meal" = "Legg til måltid";
 
 
 /* Bolus View Bolus Summary Header */
 /* Bolus View Bolus Summary Header */
-"Bolus Summary" = "Bolus Summary";
+"Bolus Summary" = "Bolus-sammendrag";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Calculations" = "Calculations";
+"Calculations" = "Beregninger";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Fatty Meal" = "Fatty Meal";
+"Fatty Meal" = "Fett måltid";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Full Bolus" = "Full Bolus";
 "Full Bolus" = "Full Bolus";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Fraction" = "Fraction";
+"Fraction" = "Brøkdel";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Fatty Meal Factor" = "Fatty Meal Factor";
+"Fatty Meal Factor" = "Brøkdel for fett måltid";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Result" = "Result";
+"Result" = "Resultat";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
+"Your entered amount was limited by your max Bolus setting of %d%@" = "Mengden du la inn ble begrenset av innstillingen for Maks Bolus på %d%@";
+
+/* Bolus View Continue Button */
+"Continue" = "Fortsett";
 
 
 /* Home title */
 /* Home title */
 "Home" = "Hjem";
 "Home" = "Hjem";
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Karbo nødvendig";
 "Carbs required" = "Karbo nødvendig";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Er du sikker?";
 "Are you sure?" = "Er du sikker?";
 
 
@@ -603,7 +609,7 @@ Enact a temp Basal or a temp target */
 "Automatic" = "Automatisk";
 "Automatic" = "Automatisk";
 
 
 /* External insulin treatments */
 /* External insulin treatments */
-"External" = "External";
+"External" = "Ekstern";
 
 
 /* */
 /* */
 "Other" = "Annet";
 "Other" = "Annet";
@@ -1208,7 +1214,7 @@ Enact a temp Basal or a temp target */
 "SMB" = "SMB";
 "SMB" = "SMB";
 
 
 /* A manually entered dose of external insulin */
 /* A manually entered dose of external insulin */
-"External Insulin" = "External Insulin";
+"External Insulin" = "Eksternt insulin";
 
 
 /* Status highlight when manual temp basal is running. */
 /* Status highlight when manual temp basal is running. */
 "Manual Basal" = "Manuell basal";
 "Manual Basal" = "Manuell basal";
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Beregnet blodsukker";
 "Eventual Glucose" = "Beregnet blodsukker";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Blodsukkermål";
 "Target Glucose" = "Blodsukkermål";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Je ingevoerde hoeveelheid is beperkt door je maximale bolusinstelling van %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Je ingevoerde hoeveelheid is beperkt door je maximale bolusinstelling van %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Vervolg";
+
 /* Home title */
 /* Home title */
 "Home" = "Hoofdmenu";
 "Home" = "Hoofdmenu";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Koolhydraten benodigd";
 "Carbs required" = "Koolhydraten benodigd";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Weet je het zeker?";
 "Are you sure?" = "Weet je het zeker?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Voorspelde glucose";
 "Eventual Glucose" = "Voorspelde glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Doel glucose";
 "Target Glucose" = "Doel glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Kontynuuj";
+
 /* Home title */
 /* Home title */
 "Home" = "Home";
 "Home" = "Home";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbs required";
 "Carbs required" = "Carbs required";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Czy jesteś pewien?";
 "Are you sure?" = "Czy jesteś pewien?";
 
 
@@ -1399,6 +1405,11 @@ Połączono z Nightscout!";
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continuar";
+
 /* Home title */
 /* Home title */
 "Home" = "Tela inicial";
 "Home" = "Tela inicial";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbos necessários";
 "Carbs required" = "Carbos necessários";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Tem certeza?";
 "Are you sure?" = "Tem certeza?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Continue";
+
 /* Home title */
 /* Home title */
 "Home" = "Tela inicial";
 "Home" = "Tela inicial";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbos necessários";
 "Carbs required" = "Carbos necessários";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Tem certeza?";
 "Are you sure?" = "Tem certeza?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 24 - 13
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings

@@ -56,37 +56,40 @@
 "Error at" = "Ошибка в";
 "Error at" = "Ошибка в";
 
 
 /* Bolus View Meal Summary Header */
 /* Bolus View Meal Summary Header */
-"Meal Summary" = "Meal Summary";
+"Meal Summary" = "Итог питания";
 
 
 /* Bolus View Meal Edit Meal Button */
 /* Bolus View Meal Edit Meal Button */
-"Edit Meal" = "Edit Meal";
+"Edit Meal" = "Изменить еду";
 
 
 /* Bolus View Meal Add Meal Button */
 /* Bolus View Meal Add Meal Button */
-"Add Meal" = "Add Meal";
+"Add Meal" = "Добавить еду";
 
 
 /* Bolus View Bolus Summary Header */
 /* Bolus View Bolus Summary Header */
-"Bolus Summary" = "Bolus Summary";
+"Bolus Summary" = "Болюс суммарно";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Calculations" = "Calculations";
+"Calculations" = "Вычисления";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Fatty Meal" = "Fatty Meal";
+"Fatty Meal" = "Жирная пища";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Full Bolus" = "Full Bolus";
+"Full Bolus" = "Полный болюс";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Fraction" = "Fraction";
+"Fraction" = "Доля";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Fatty Meal Factor" = "Fatty Meal Factor";
+"Fatty Meal Factor" = "Фактор жирной пищи";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Result" = "Result";
+"Result" = "Итого";
 
 
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
-"Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
+"Your entered amount was limited by your max Bolus setting of %d%@" = "Доза была ограничена Вашей настройкой максимального болюса в размере %d%@";
+
+/* Bolus View Continue Button */
+"Continue" = "Продолжить";
 
 
 /* Home title */
 /* Home title */
 "Home" = "Главная";
 "Home" = "Главная";
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Требуются углеводы";
 "Carbs required" = "Требуются углеводы";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Вы уверены?";
 "Are you sure?" = "Вы уверены?";
 
 
@@ -603,7 +609,7 @@ Enact a temp Basal or a temp target */
 "Automatic" = "Автоматически";
 "Automatic" = "Автоматически";
 
 
 /* External insulin treatments */
 /* External insulin treatments */
-"External" = "External";
+"External" = "Внешний";
 
 
 /* */
 /* */
 "Other" = "Прочее";
 "Other" = "Прочее";
@@ -1208,7 +1214,7 @@ Enact a temp Basal or a temp target */
 "SMB" = "SMB";
 "SMB" = "SMB";
 
 
 /* A manually entered dose of external insulin */
 /* A manually entered dose of external insulin */
-"External Insulin" = "External Insulin";
+"External Insulin" = "Внешний инсулин";
 
 
 /* Status highlight when manual temp basal is running. */
 /* Status highlight when manual temp basal is running. */
 "Manual Basal" = "Ручная ВБС";
 "Manual Basal" = "Ручная ВБС";
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Возможная глюкоза";
 "Eventual Glucose" = "Возможная глюкоза";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Целевая глюкоза";
 "Target Glucose" = "Целевая глюкоза";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Pokračovať";
+
 /* Home title */
 /* Home title */
 "Home" = "Home";
 "Home" = "Home";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Carbs required";
 "Carbs required" = "Carbs required";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Are you sure?";
 "Are you sure?" = "Are you sure?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 12 - 0
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Du har angivit en insulinmängd som är över din maxinställning på %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Du har angivit en insulinmängd som är över din maxinställning på %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Fortsätt";
+
 /* Home title */
 /* Home title */
 "Home" = "Hem";
 "Home" = "Hem";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Kolhydrater krävs";
 "Carbs required" = "Kolhydrater krävs";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Mat";
+
 /* */
 /* */
 "Are you sure?" = "Är du säker?";
 "Are you sure?" = "Är du säker?";
 
 
@@ -1397,6 +1403,12 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Blodsockerprognos";
 "Eventual Glucose" = "Blodsockerprognos";
 
 
 /* */
 /* */
+"Please wait" = "Vänta...";
+
+/* */
+"Glucose, " = "Blodsocker, ";
+
+/* */
 "Target Glucose" = "Målvärde";
 "Target Glucose" = "Målvärde";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Devam et";
+
 /* Home title */
 /* Home title */
 "Home" = "Ana sayfa";
 "Home" = "Ana sayfa";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Gerekli karbonhidrat";
 "Carbs required" = "Gerekli karbonhidrat";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Emin misiniz?";
 "Are you sure?" = "Emin misiniz?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Введена вами сума була обмежена налаштуванням максимального болюсу %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Введена вами сума була обмежена налаштуванням максимального болюсу %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "Продовжити";
+
 /* Home title */
 /* Home title */
 "Home" = "Головна";
 "Home" = "Головна";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "Введіть кількість вуглеводів";
 "Carbs required" = "Введіть кількість вуглеводів";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "Ви впевнені?";
 "Are you sure?" = "Ви впевнені?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Можлива глюкоза";
 "Eventual Glucose" = "Можлива глюкоза";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Цільова Глюкоза";
 "Target Glucose" = "Цільова Глюкоза";
 
 
 /* */
 /* */

+ 11 - 0
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings

@@ -88,6 +88,9 @@
 /* For the  Bolus View pop-up */
 /* For the  Bolus View pop-up */
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 "Your entered amount was limited by your max Bolus setting of %d%@" = "Your entered amount was limited by your max Bolus setting of %d%@";
 
 
+/* Bolus View Continue Button */
+"Continue" = "继续";
+
 /* Home title */
 /* Home title */
 "Home" = "主页";
 "Home" = "主页";
 
 
@@ -118,6 +121,9 @@
 /*  */
 /*  */
 "Carbs required" = "所需碳水化合物";
 "Carbs required" = "所需碳水化合物";
 
 
+/* Saved Food Presets */
+"Saved Food" = "Saved Food";
+
 /* */
 /* */
 "Are you sure?" = "确定?";
 "Are you sure?" = "确定?";
 
 
@@ -1397,6 +1403,11 @@ Enact a temp Basal or a temp target */
 "Eventual Glucose" = "Eventual Glucose";
 "Eventual Glucose" = "Eventual Glucose";
 
 
 /* */
 /* */
+"Please wait" = "Please wait";
+
+"Glucose, %@" = "Glucose, %@";
+
+/* */
 "Target Glucose" = "Target Glucose";
 "Target Glucose" = "Target Glucose";
 
 
 /* */
 /* */

+ 17 - 0
FreeAPS/Sources/Models/Charts.swift

@@ -0,0 +1,17 @@
+
+import Foundation
+
+struct ShapeModel: Identifiable {
+    var type: String
+    var percent: Decimal
+    var id = UUID()
+}
+
+struct ChartData: Identifiable {
+    var date: Date
+    var iob: Double
+    var zt: Double
+    var cob: Double
+    var uam: Double
+    var id = UUID()
+}

+ 0 - 8
FreeAPS/Sources/Models/TIRforChart.swift

@@ -1,8 +0,0 @@
-
-import Foundation
-
-struct ShapeModel: Identifiable {
-    var type: String
-    var percent: Decimal
-    var id = UUID()
-}

+ 19 - 17
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -31,7 +31,7 @@ extension AddCarbs {
             useFPUconversion = settingsManager.settings.useFPUconversion
             useFPUconversion = settingsManager.settings.useFPUconversion
         }
         }
 
 
-        func add() {
+        func add(_ continue_: Bool, fetch: Bool) {
             guard carbs > 0 || fat > 0 || protein > 0 else {
             guard carbs > 0 || fat > 0 || protein > 0 else {
                 showModal(for: nil)
                 showModal(for: nil)
                 return
                 return
@@ -51,7 +51,7 @@ extension AddCarbs {
             )]
             )]
             carbsStorage.storeCarbs(carbsToStore)
             carbsStorage.storeCarbs(carbsToStore)
 
 
-            if skipBolus {
+            if skipBolus, !continue_, !fetch {
                 apsManager.determineBasalSync()
                 apsManager.determineBasalSync()
                 showModal(for: nil)
                 showModal(for: nil)
             } else if carbs > 0 {
             } else if carbs > 0 {
@@ -140,16 +140,16 @@ extension AddCarbs {
             var addedString = ""
             var addedString = ""
 
 
             if extracarbs > 0, filteredArray.isNotEmpty {
             if extracarbs > 0, filteredArray.isNotEmpty {
-                addedString += "Additional carbs: \(extracarbs) "
+                addedString += "Additional carbs: \(extracarbs) ,"
             } else if extracarbs < 0 { addedString += "Removed carbs: \(extracarbs) " }
             } else if extracarbs < 0 { addedString += "Removed carbs: \(extracarbs) " }
 
 
             if extraFat > 0, filteredArray.isNotEmpty {
             if extraFat > 0, filteredArray.isNotEmpty {
-                addedString += "Additional fat: \(extraFat) "
-            } else if extraFat < 0 { addedString += "Removed fat: \(extraFat) " }
+                addedString += "Additional fat: \(extraFat) ,"
+            } else if extraFat < 0 { addedString += "Removed fat: \(extraFat) ," }
 
 
             if extraProtein > 0, filteredArray.isNotEmpty {
             if extraProtein > 0, filteredArray.isNotEmpty {
-                addedString += "Additional protein: \(extraProtein) "
-            } else if extraProtein < 0 { addedString += "Removed protein: \(extraProtein) " }
+                addedString += "Additional protein: \(extraProtein) ,"
+            } else if extraProtein < 0 { addedString += "Removed protein: \(extraProtein) ," }
 
 
             if addedString != "" {
             if addedString != "" {
                 waitersNotepad.append(addedString)
                 waitersNotepad.append(addedString)
@@ -170,7 +170,7 @@ extension AddCarbs {
 
 
         func loadEntries(_ editMode: Bool) {
         func loadEntries(_ editMode: Bool) {
             if editMode {
             if editMode {
-                coredataContext.perform {
+                coredataContext.performAndWait {
                     var mealToEdit = [Meals]()
                     var mealToEdit = [Meals]()
                     let requestMeal = Meals.fetchRequest() as NSFetchRequest<Meals>
                     let requestMeal = Meals.fetchRequest() as NSFetchRequest<Meals>
                     let sortMeal = NSSortDescriptor(key: "createdAt", ascending: false)
                     let sortMeal = NSSortDescriptor(key: "createdAt", ascending: false)
@@ -188,15 +188,17 @@ extension AddCarbs {
         }
         }
 
 
         func saveToCoreData(_ stored: [CarbsEntry]) {
         func saveToCoreData(_ stored: [CarbsEntry]) {
-            let save = Meals(context: coredataContext)
-            save.createdAt = Date.now
-            save.id = stored.first?.collectionID ?? ""
-            save.carbs = Double(stored.first?.carbs ?? 0)
-            save.fat = Double(stored.first?.fat ?? 0)
-            save.protein = Double(stored.first?.protein ?? 0)
-            save.note = stored.first?.note ?? ""
-            if coredataContext.hasChanges {
-                try? coredataContext.save()
+            coredataContext.performAndWait {
+                let save = Meals(context: coredataContext)
+                if let entry = stored.first {
+                    save.createdAt = Date.now
+                    save.id = entry.collectionID ?? ""
+                    save.carbs = Double(entry.carbs)
+                    save.fat = Double(entry.fat ?? 0)
+                    save.protein = Double(entry.protein ?? 0)
+                    save.note = entry.note
+                    try? coredataContext.save()
+                }
             }
             }
         }
         }
     }
     }

+ 167 - 101
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -6,10 +6,12 @@ extension AddCarbs {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
         let editMode: Bool
         let editMode: Bool
+        let override: Bool
         @StateObject var state = StateModel()
         @StateObject var state = StateModel()
         @State var dish: String = ""
         @State var dish: String = ""
         @State var isPromptPresented = false
         @State var isPromptPresented = false
         @State var saved = false
         @State var saved = false
+        @State var pushed = false
         @State private var showAlert = false
         @State private var showAlert = false
         @FocusState private var isFocused: Bool
         @FocusState private var isFocused: Bool
 
 
@@ -29,12 +31,12 @@ extension AddCarbs {
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
-                if let carbsReq = state.carbsRequired {
+                if let carbsReq = state.carbsRequired, state.carbs < carbsReq {
                     Section {
                     Section {
                         HStack {
                         HStack {
                             Text("Carbs required")
                             Text("Carbs required")
                             Spacer()
                             Spacer()
-                            Text(formatter.string(from: carbsReq as NSNumber)! + " g")
+                            Text((formatter.string(from: carbsReq as NSNumber) ?? "") + " g")
                         }
                         }
                     }
                     }
                 }
                 }
@@ -50,11 +52,55 @@ extension AddCarbs {
                             cleanInput: true
                             cleanInput: true
                         )
                         )
                         Text("grams").foregroundColor(.secondary)
                         Text("grams").foregroundColor(.secondary)
-                    }.padding(.vertical)
+                    }
 
 
                     if state.useFPUconversion {
                     if state.useFPUconversion {
                         proteinAndFat()
                         proteinAndFat()
                     }
                     }
+
+                    // Summary when combining presets
+                    if state.waitersNotepad() != "" {
+                        HStack {
+                            Text("Total")
+                            let test = state.waitersNotepad().components(separatedBy: ", ").removeDublicates()
+                            HStack(spacing: 0) {
+                                ForEach(test, id: \.self) {
+                                    Text($0).foregroundStyle(Color.randomGreen()).font(.footnote)
+                                    Text($0 == test[test.count - 1] ? "" : ", ")
+                                }
+                            }.frame(maxWidth: .infinity, alignment: .trailing)
+                        }
+                    }
+
+                    // Time
+                    HStack {
+                        let now = Date.now
+                        Text("Time")
+                        Spacer()
+                        if !pushed {
+                            Button {
+                                pushed = true
+                            } label: { Text("Now") }.buttonStyle(.borderless).foregroundColor(.secondary).padding(.trailing, 5)
+                        } else {
+                            Button { state.date = state.date.addingTimeInterval(-10.minutes.timeInterval) }
+                            label: { Image(systemName: "minus.circle") }.tint(.blue).buttonStyle(.borderless)
+                            DatePicker(
+                                "Time",
+                                selection: $state.date,
+                                in: ...now,
+                                displayedComponents: [.hourAndMinute]
+                            ).controlSize(.mini)
+                                .labelsHidden()
+                            Button {
+                                if state.date.addingTimeInterval(5.minutes.timeInterval) < now {
+                                    state.date = state.date.addingTimeInterval(10.minutes.timeInterval)
+                                }
+                            }
+                            label: { Image(systemName: "plus.circle") }.tint(.blue).buttonStyle(.borderless)
+                        }
+                    }
+
+                    // Optional meal note
                     HStack {
                     HStack {
                         Text("Note").foregroundColor(.secondary)
                         Text("Note").foregroundColor(.secondary)
                         TextField("", text: $state.note).multilineTextAlignment(.trailing)
                         TextField("", text: $state.note).multilineTextAlignment(.trailing)
@@ -62,25 +108,23 @@ extension AddCarbs {
                             Button { isFocused = false } label: { Image(systemName: "keyboard.chevron.compact.down") }
                             Button { isFocused = false } label: { Image(systemName: "keyboard.chevron.compact.down") }
                                 .controlSize(.mini)
                                 .controlSize(.mini)
                         }
                         }
-                    }.focused($isFocused)
-                        .popover(isPresented: $isPromptPresented) {
-                            presetPopover
-                        }
-                }
-
-                Section {
-                    mealPresets
+                    }
+                    .focused($isFocused)
+                    .popover(isPresented: $isPromptPresented) {
+                        presetPopover
+                    }
                 }
                 }
 
 
                 Section {
                 Section {
-                    Button { state.add() }
-                    label: { Text(state.skipBolus ? "Save" : "Continue") }
-                        .disabled(state.carbs <= 0 && state.fat <= 0 && state.protein <= 0)
+                    Button { state.add(override, fetch: editMode) }
+                    label: { Text((state.skipBolus && !override && !editMode) ? "Save" : "Continue") }
+                        .disabled(empty)
                         .frame(maxWidth: .infinity, alignment: .center)
                         .frame(maxWidth: .infinity, alignment: .center)
-                } footer: { Text(state.waitersNotepad().description) }
+                }.listRowBackground(!empty ? Color(.systemBlue) : Color(.systemGray4))
+                    .tint(.white)
 
 
                 Section {
                 Section {
-                    DatePicker("Date", selection: $state.date)
+                    mealPresets
                 }
                 }
             }
             }
             .onAppear {
             .onAppear {
@@ -93,7 +137,7 @@ extension AddCarbs {
             .navigationBarItems(leading: Button("Close", action: state.hideModal))
             .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
         }
 
 
-        var presetPopover: some View {
+        private var presetPopover: some View {
             Form {
             Form {
                 Section {
                 Section {
                     TextField("Name Of Dish", text: $dish)
                     TextField("Name Of Dish", text: $dish)
@@ -121,114 +165,125 @@ extension AddCarbs {
             }
             }
         }
         }
 
 
-        var notEmpty: Bool {
-            state.carbs > 0 || state.protein > 0 || state.fat > 0
+        private var empty: Bool {
+            state.carbs <= 0 && state.fat <= 0 && state.protein <= 0
+        }
+
+        private var minusButton: some View {
+            Button {
+                if state.carbs != 0,
+                   (state.carbs - (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
+                {
+                    state.carbs -= (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal)
+                } else { state.carbs = 0 }
+
+                if state.fat != 0,
+                   (state.fat - (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
+                {
+                    state.fat -= (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal)
+                } else { state.fat = 0 }
+
+                if state.protein != 0,
+                   (state.protein - (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
+                {
+                    state.protein -= (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal)
+                } else { state.protein = 0 }
+
+                state.removePresetFromNewMeal()
+                if state.carbs == 0, state.fat == 0, state.protein == 0 { state.summation = [] }
+            }
+            label: { Image(systemName: "minus.circle") }
+                .disabled(
+                    state
+                        .selection == nil ||
+                        (
+                            !state.summation
+                                .contains(state.selection?.dish ?? "") && (state.selection?.dish ?? "") != ""
+                        )
+                )
+                .buttonStyle(.borderless)
+                .tint(.blue)
+        }
+
+        private var plusButton: some View {
+            Button {
+                state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
+                state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
+                state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
+
+                state.addPresetToNewMeal()
+            }
+            label: { Image(systemName: "plus.circle") }
+                .disabled(state.selection == nil)
+                .buttonStyle(.borderless)
+                .tint(.blue)
         }
         }
 
 
-        var mealPresets: some View {
+        private var mealPresets: some View {
             Section {
             Section {
                 HStack {
                 HStack {
-                    Button {
-                        isPromptPresented = true
+                    if state.selection != nil {
+                        minusButton
                     }
                     }
-                    label: { Text("Save as Preset") }
-                        .buttonStyle(BorderlessButtonStyle())
-                        .disabled(
-                            !notEmpty ||
-                                (
-                                    (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
-                                        .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
-                                        .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
-                                        .protein
-                                )
-                        )
-
-                    Picker("Select a Preset", selection: $state.selection) {
-                        Text("Presets").tag(nil as Presets?)
+                    Picker("Preset", selection: $state.selection) {
+                        Text("Saved Food").tag(nil as Presets?)
                         ForEach(carbPresets, id: \.self) { (preset: Presets) in
                         ForEach(carbPresets, id: \.self) { (preset: Presets) in
                             Text(preset.dish ?? "").tag(preset as Presets?)
                             Text(preset.dish ?? "").tag(preset as Presets?)
                         }
                         }
                     }
                     }
                     .labelsHidden()
                     .labelsHidden()
-                    .frame(maxWidth: .infinity, alignment: .trailing)
+                    .frame(maxWidth: .infinity, alignment: .center)
                     ._onBindingChange($state.selection) { _ in
                     ._onBindingChange($state.selection) { _ in
                         state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
                         state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
                         state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
                         state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
                         state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
                         state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
                         state.addToSummation()
                         state.addToSummation()
                     }
                     }
+                    if state.selection != nil {
+                        plusButton
+                    }
                 }
                 }
 
 
-                if state.selection != nil {
-                    HStack {
-                        Button("Delete Preset") {
-                            showAlert.toggle()
-                        }
-                        .disabled(state.selection == nil)
-                        .tint(.orange)
-                        .buttonStyle(BorderlessButtonStyle())
-                        .alert(
-                            "Delete preset '\(state.selection?.dish ?? "")'?",
-                            isPresented: $showAlert,
-                            actions: {
-                                Button("No", role: .cancel) {}
-                                Button("Yes", role: .destructive) {
-                                    state.deletePreset()
+                HStack {
+                    Button("Delete Preset") {
+                        showAlert.toggle()
+                    }
+                    .disabled(state.selection == nil)
+                    .tint(.orange)
+                    .buttonStyle(.borderless)
+                    .alert(
+                        "Delete preset '\(state.selection?.dish ?? "")'?",
+                        isPresented: $showAlert,
+                        actions: {
+                            Button("No", role: .cancel) {}
+                            Button("Yes", role: .destructive) {
+                                state.deletePreset()
 
 
-                                    state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
-                                    state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
-                                    state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
+                                state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
+                                state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
+                                state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
 
 
-                                    state.addPresetToNewMeal()
-                                }
+                                state.addPresetToNewMeal()
                             }
                             }
-                        )
-                        Button {
-                            if state.carbs != 0,
-                               (state.carbs - (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
-                            {
-                                state.carbs -= (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal)
-                            } else { state.carbs = 0 }
-
-                            if state.fat != 0,
-                               (state.fat - (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
-                            {
-                                state.fat -= (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal)
-                            } else { state.fat = 0 }
-
-                            if state.protein != 0,
-                               (state.protein - (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
-                            {
-                                state.protein -= (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal)
-                            } else { state.protein = 0 }
-
-                            state.removePresetFromNewMeal()
-                            if state.carbs == 0, state.fat == 0, state.protein == 0 { state.summation = [] }
                         }
                         }
-                        label: { Text("[ -1 ]") }
-                            .disabled(
-                                state
-                                    .selection == nil ||
-                                    (
-                                        !state.summation
-                                            .contains(state.selection?.dish ?? "") && (state.selection?.dish ?? "") != ""
-                                    )
-                            )
-                            .buttonStyle(BorderlessButtonStyle())
-                            .frame(maxWidth: .infinity, alignment: .trailing)
-                            .tint(.minus)
-                        Button {
-                            state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
-                            state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
-                            state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
+                    )
 
 
-                            state.addPresetToNewMeal()
-                        }
-                        label: { Text("[ +1 ]") }
-                            .disabled(state.selection == nil)
-                            .buttonStyle(BorderlessButtonStyle())
-                            .tint(.blue)
+                    Spacer()
+
+                    Button {
+                        isPromptPresented = true
                     }
                     }
+                    label: { Text("Save as Preset") }
+                        .buttonStyle(.borderless)
+                        .disabled(
+                            empty ||
+                                (
+                                    (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
+                                        .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
+                                        .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
+                                        .protein
+                                )
+                        )
                 }
                 }
             }
             }
         }
         }
@@ -262,3 +317,14 @@ extension AddCarbs {
         }
         }
     }
     }
 }
 }
+
+public extension Color {
+    static func randomGreen(randomOpacity: Bool = false) -> Color {
+        Color(
+            red: .random(in: 0 ... 1),
+            green: .random(in: 0.4 ... 0.7),
+            blue: .random(in: 0.2 ... 1),
+            opacity: randomOpacity ? .random(in: 0.8 ... 1) : 1
+        )
+    }
+}

+ 9 - 7
FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift

@@ -1,5 +1,3 @@
-
-import CoreData
 import LoopKit
 import LoopKit
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
@@ -14,9 +12,8 @@ extension Bolus {
         @Injected() var settings: SettingsManager!
         @Injected() var settings: SettingsManager!
         @Injected() var nsManager: NightscoutManager!
         @Injected() var nsManager: NightscoutManager!
 
 
-        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
-
         @Published var suggestion: Suggestion?
         @Published var suggestion: Suggestion?
+        @Published var predictions: Predictions?
         @Published var amount: Decimal = 0
         @Published var amount: Decimal = 0
         @Published var insulinRecommended: Decimal = 0
         @Published var insulinRecommended: Decimal = 0
         @Published var insulinRequired: Decimal = 0
         @Published var insulinRequired: Decimal = 0
@@ -60,7 +57,6 @@ extension Bolus {
         @Published var fattyMeals: Bool = false
         @Published var fattyMeals: Bool = false
         @Published var fattyMealFactor: Decimal = 0
         @Published var fattyMealFactor: Decimal = 0
         @Published var useFattyMealCorrectionFactor: Bool = false
         @Published var useFattyMealCorrectionFactor: Bool = false
-        @Published var eventualBG: Int = 0
 
 
         @Published var meal: [CarbsEntry]?
         @Published var meal: [CarbsEntry]?
         @Published var carbs: Decimal = 0
         @Published var carbs: Decimal = 0
@@ -93,6 +89,12 @@ extension Bolus {
                         }
                         }
                     }.store(in: &lifetime)
                     }.store(in: &lifetime)
             }
             }
+            if let notNilSugguestion = provider.suggestion {
+                suggestion = notNilSugguestion
+                if let notNilPredictions = suggestion?.predictions {
+                    predictions = notNilPredictions
+                }
+            }
         }
         }
 
 
         func getDeltaBG() {
         func getDeltaBG() {
@@ -220,9 +222,9 @@ extension Bolus {
             }
             }
         }
         }
 
 
-        func backToCarbsView(complexEntry: Bool, _ id: String) {
+        func backToCarbsView(complexEntry: Bool, _ id: String, override: Bool) {
             delete(deleteTwice: complexEntry, id: id)
             delete(deleteTwice: complexEntry, id: id)
-            showModal(for: .addCarbs(editMode: complexEntry))
+            showModal(for: .addCarbs(editMode: complexEntry, override: override))
         }
         }
 
 
         func delete(deleteTwice: Bool, id: String) {
         func delete(deleteTwice: Bool, id: String) {

+ 47 - 27
FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift

@@ -1,3 +1,4 @@
+import Charts
 import CoreData
 import CoreData
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
@@ -56,6 +57,15 @@ extension Bolus {
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
+                Section {
+                    if state.waitForSuggestion {
+                        Text("Please wait")
+                    } else {
+                        predictionChart
+                    }
+                } header: { Text("Predictions") }
+
+                Section {}
                 if fetch {
                 if fetch {
                     Section {
                     Section {
                         mealEntries
                         mealEntries
@@ -108,27 +118,26 @@ extension Bolus {
                             .onTapGesture { state.amount = state.insulinCalculated }
                             .onTapGesture { state.amount = state.insulinCalculated }
                     }
                     }
 
 
-                    if !state.waitForSuggestion {
-                        HStack {
-                            Text("Bolus")
-                            Spacer()
-                            DecimalTextField(
-                                "0",
-                                value: $state.amount,
-                                formatter: formatter,
-                                autofocus: false,
-                                cleanInput: true
-                            )
-                            Text(exceededMaxBolus ? "😵" : " U").foregroundColor(.secondary)
-                        }
-                        .onChange(of: state.amount) { newValue in
-                            if newValue > state.maxBolus {
-                                exceededMaxBolus = true
-                            } else {
-                                exceededMaxBolus = false
-                            }
+                    HStack {
+                        Text("Bolus")
+                        Spacer()
+                        DecimalTextField(
+                            "0",
+                            value: $state.amount,
+                            formatter: formatter,
+                            autofocus: false,
+                            cleanInput: true
+                        )
+                        Text(exceededMaxBolus ? "😵" : " U").foregroundColor(.secondary)
+                    }
+                    .onChange(of: state.amount) { newValue in
+                        if newValue > state.maxBolus {
+                            exceededMaxBolus = true
+                        } else {
+                            exceededMaxBolus = false
                         }
                         }
                     }
                     }
+
                 } header: { Text("Bolus") }
                 } header: { Text("Bolus") }
 
 
                 if state.amount > 0 {
                 if state.amount > 0 {
@@ -139,10 +148,9 @@ extension Bolus {
                         }
                         }
                         label: { Text(exceededMaxBolus ? "Max Bolus exceeded!" : "Enact bolus") }
                         label: { Text(exceededMaxBolus ? "Max Bolus exceeded!" : "Enact bolus") }
                             .frame(maxWidth: .infinity, alignment: .center)
                             .frame(maxWidth: .infinity, alignment: .center)
-                            .foregroundColor(exceededMaxBolus ? .loopRed : .accentColor)
-                            .disabled(
-                                state.amount <= 0 || state.amount > state.maxBolus
-                            )
+                            .disabled(disabled)
+                            .listRowBackground(!disabled ? Color(.systemBlue) : Color(.systemGray4))
+                            .tint(.white)
                     }
                     }
                 }
                 }
                 if state.amount <= 0 {
                 if state.amount <= 0 {
@@ -160,7 +168,7 @@ extension Bolus {
             .navigationBarTitleDisplayMode(.inline)
             .navigationBarTitleDisplayMode(.inline)
             .navigationBarItems(
             .navigationBarItems(
                 leading: Button {
                 leading: Button {
-                    carbssView()
+                    carbsView()
                 }
                 }
                 label: { Text(fetch ? "Back" : "Meal") },
                 label: { Text(fetch ? "Back" : "Meal") },
 
 
@@ -186,6 +194,14 @@ extension Bolus {
             }
             }
         }
         }
 
 
+        var predictionChart: some View {
+            ZStack {
+                PredictionView(
+                    predictions: $state.predictions, units: $state.units, eventualBG: $state.evBG, target: $state.target
+                )
+            }
+        }
+
         // Pop-up
         // Pop-up
         var bolusInfoAlternativeCalculator: some View {
         var bolusInfoAlternativeCalculator: some View {
             VStack {
             VStack {
@@ -249,6 +265,10 @@ extension Bolus {
             )
             )
         }
         }
 
 
+        private var disabled: Bool {
+            state.amount <= 0 || state.amount > state.maxBolus
+        }
+
         var changed: Bool {
         var changed: Bool {
             ((meal.first?.carbs ?? 0) > 0) || ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
             ((meal.first?.carbs ?? 0) > 0) || ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
         }
         }
@@ -257,13 +277,13 @@ extension Bolus {
             ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
             ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
         }
         }
 
 
-        func carbssView() {
+        func carbsView() {
             let id_ = meal.first?.id ?? ""
             let id_ = meal.first?.id ?? ""
             if fetch {
             if fetch {
                 keepForNextWiew = true
                 keepForNextWiew = true
-                state.backToCarbsView(complexEntry: fetch, id_)
+                state.backToCarbsView(complexEntry: fetch, id_, override: false)
             } else {
             } else {
-                state.showModal(for: .addCarbs(editMode: false))
+                state.backToCarbsView(complexEntry: false, id_, override: true)
             }
             }
         }
         }
 
 

+ 50 - 28
FreeAPS/Sources/Modules/Bolus/View/DefaultBolusCalcRootView.swift

@@ -1,3 +1,5 @@
+import Charts
+import CoreData
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
 
 
@@ -35,6 +37,14 @@ extension Bolus {
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
+                Section {
+                    if state.waitForSuggestion {
+                        Text("Please wait")
+                    } else {
+                        predictionChart
+                    }
+                } header: { Text("Predictions") }
+
                 if fetch {
                 if fetch {
                     Section {
                     Section {
                         mealEntries
                         mealEntries
@@ -70,37 +80,37 @@ extension Bolus {
                                     else { state.amount = state.insulinRecommended }
                                     else { state.amount = state.insulinRecommended }
                                 }
                                 }
                         }.contentShape(Rectangle())
                         }.contentShape(Rectangle())
+                    }
 
 
-                        HStack {
-                            Text("Amount")
-                            Spacer()
-                            DecimalTextField(
-                                "0",
-                                value: $state.amount,
-                                formatter: formatter,
-                                autofocus: true,
-                                cleanInput: true
-                            )
-                            Text(!(state.amount > state.maxBolus) ? "U" : "😵").foregroundColor(.secondary)
-                        }
+                    HStack {
+                        Text("Amount")
+                        Spacer()
+                        DecimalTextField(
+                            "0",
+                            value: $state.amount,
+                            formatter: formatter,
+                            autofocus: true,
+                            cleanInput: true
+                        )
+                        Text(!(state.amount > state.maxBolus) ? "U" : "😵").foregroundColor(.secondary)
                     }
                     }
+
                 } header: { Text("Bolus") }
                 } header: { Text("Bolus") }
 
 
-                if !state.waitForSuggestion {
-                    if state.amount > 0 {
-                        Section {
-                            Button {
-                                keepForNextWiew = true
-                                state.add()
-                            }
-                            label: { Text(!(state.amount > state.maxBolus) ? "Enact bolus" : "Max Bolus exceeded!") }
-                                .frame(maxWidth: .infinity, alignment: .center)
-                                .disabled(
-                                    state.amount <= 0 || state.amount > state.maxBolus
-                                )
+                if state.amount > 0 {
+                    Section {
+                        Button {
+                            keepForNextWiew = true
+                            state.add()
                         }
                         }
+                        label: { Text(!(state.amount > state.maxBolus) ? "Enact bolus" : "Max Bolus exceeded!") }
+                            .frame(maxWidth: .infinity, alignment: .center)
+                            .disabled(disabled)
+                            .listRowBackground(!disabled ? Color(.systemBlue) : Color(.systemGray4))
+                            .tint(.white)
                     }
                     }
                 }
                 }
+
                 if state.amount <= 0 {
                 if state.amount <= 0 {
                     Section {
                     Section {
                         Button {
                         Button {
@@ -143,7 +153,7 @@ extension Bolus {
             .navigationBarTitleDisplayMode(.inline)
             .navigationBarTitleDisplayMode(.inline)
             .navigationBarItems(
             .navigationBarItems(
                 leading: Button {
                 leading: Button {
-                    carbssView()
+                    carbsView()
                 }
                 }
                 label: { Text(fetch ? "Back" : "Meal") },
                 label: { Text(fetch ? "Back" : "Meal") },
 
 
@@ -155,6 +165,18 @@ extension Bolus {
             }
             }
         }
         }
 
 
+        var disabled: Bool {
+            state.amount <= 0 || state.amount > state.maxBolus
+        }
+
+        var predictionChart: some View {
+            ZStack {
+                PredictionView(
+                    predictions: $state.predictions, units: $state.units, eventualBG: $state.evBG, target: $state.target
+                )
+            }
+        }
+
         var changed: Bool {
         var changed: Bool {
             ((meal.first?.carbs ?? 0) > 0) || ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
             ((meal.first?.carbs ?? 0) > 0) || ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
         }
         }
@@ -163,13 +185,13 @@ extension Bolus {
             ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
             ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
         }
         }
 
 
-        func carbssView() {
+        func carbsView() {
             let id_ = meal.first?.id ?? ""
             let id_ = meal.first?.id ?? ""
             if fetch {
             if fetch {
                 keepForNextWiew = true
                 keepForNextWiew = true
-                state.backToCarbsView(complexEntry: fetch, id_)
+                state.backToCarbsView(complexEntry: fetch, id_, override: false)
             } else {
             } else {
-                state.showModal(for: .addCarbs(editMode: false))
+                state.backToCarbsView(complexEntry: false, id_, override: true)
             }
             }
         }
         }
 
 

+ 110 - 0
FreeAPS/Sources/Modules/Bolus/View/Predictions.swift

@@ -0,0 +1,110 @@
+import Charts
+import CoreData
+import SwiftUI
+import Swinject
+
+struct PredictionView: View {
+    @Binding var predictions: Predictions?
+    @Binding var units: GlucoseUnits
+    @Binding var eventualBG: Int
+    @Binding var target: Decimal
+
+    private enum Config {
+        static let height: CGFloat = 160
+        static let lineWidth: CGFloat = 2
+    }
+
+    var body: some View {
+        VStack {
+            chart()
+            HStack {
+                let conversion = units == .mmolL ? 0.0555 : 1
+                Text("Eventual Glucose")
+                Spacer()
+                Text(
+                    (Double(eventualBG) * conversion)
+                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(units == .mmolL ? 1 : 0)))
+                )
+                Text(units.rawValue).foregroundStyle(.secondary)
+                Divider()
+            }.font(.callout)
+        }
+    }
+
+    func chart() -> some View {
+        // Data Source
+        let iob = predictions?.iob ?? [Int]()
+        let cob = predictions?.cob ?? [Int]()
+        let uam = predictions?.uam ?? [Int]()
+        let zt = predictions?.zt ?? [Int]()
+        let count = max(iob.count, cob.count, uam.count, zt.count)
+        var now = Date.now
+        var startIndex = 0
+        let conversion = units == .mmolL ? 0.0555 : 1
+        // Organize the data needed for prediction chart.
+        var data = [ChartData]()
+        repeat {
+            now = now.addingTimeInterval(5.minutes.timeInterval)
+            if startIndex < count {
+                let addedData = ChartData(
+                    date: now,
+                    iob: startIndex < iob.count ? Double(iob[startIndex]) * conversion : 0,
+                    zt: startIndex < zt.count ? Double(zt[startIndex]) * conversion : 0,
+                    cob: startIndex < cob.count ? Double(cob[startIndex]) * conversion : 0,
+                    uam: startIndex < uam.count ? Double(uam[startIndex]) * conversion : 0,
+                    id: UUID()
+                )
+                data.append(addedData)
+            }
+            startIndex += 1
+        } while startIndex < count
+        // Chart
+        return Chart(data) {
+            // Remove 0 (empty) values
+            if $0.iob != 0 {
+                LineMark(
+                    x: .value("Time", $0.date),
+                    y: .value("IOB", $0.iob),
+                    series: .value("IOB", "A")
+                )
+                .foregroundStyle(Color(.insulin))
+                .lineStyle(StrokeStyle(lineWidth: Config.lineWidth))
+            }
+            if $0.uam != 0 {
+                LineMark(
+                    x: .value("Time", $0.date),
+                    y: .value("UAM", $0.uam),
+                    series: .value("UAM", "B")
+                )
+                .foregroundStyle(Color(.UAM))
+                .lineStyle(StrokeStyle(lineWidth: Config.lineWidth))
+            }
+            if $0.cob != 0 {
+                LineMark(
+                    x: .value("Time", $0.date),
+                    y: .value("COB", $0.cob),
+                    series: .value("COB", "C")
+                )
+                .foregroundStyle(Color(.loopYellow))
+                .lineStyle(StrokeStyle(lineWidth: Config.lineWidth))
+            }
+            if $0.zt != 0 {
+                LineMark(
+                    x: .value("Time", $0.date),
+                    y: .value("ZT", $0.zt),
+                    series: .value("ZT", "D")
+                )
+                .foregroundStyle(Color(.ZT))
+                .lineStyle(StrokeStyle(lineWidth: Config.lineWidth))
+            }
+        }
+        .frame(minHeight: Config.height)
+        .chartForegroundStyleScale([
+            "IOB": Color(.insulin),
+            "UAM": Color(.UAM),
+            "COB": Color(.loopYellow),
+            "ZT": Color(.ZT)
+        ])
+        .chartYAxisLabel(NSLocalizedString("Glucose, ", comment: "") + units.rawValue, alignment: .center)
+    }
+}

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

@@ -196,7 +196,7 @@ extension Home {
         }
         }
 
 
         func addCarbs() {
         func addCarbs() {
-            showModal(for: .addCarbs(editMode: false))
+            showModal(for: .addCarbs(editMode: false, override: false))
         }
         }
 
 
         func runLoop() {
         func runLoop() {

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

@@ -481,7 +481,7 @@ extension Home {
                 Rectangle().fill(Color.gray.opacity(0.3)).frame(height: 50 + geo.safeAreaInsets.bottom)
                 Rectangle().fill(Color.gray.opacity(0.3)).frame(height: 50 + geo.safeAreaInsets.bottom)
 
 
                 HStack {
                 HStack {
-                    Button { state.showModal(for: .addCarbs(editMode: false)) }
+                    Button { state.showModal(for: .addCarbs(editMode: false, override: false)) }
                     label: {
                     label: {
                         ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
                         ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
                             Image("carbs")
                             Image("carbs")

+ 3 - 3
FreeAPS/Sources/Router/Screen.swift

@@ -14,7 +14,7 @@ enum Screen: Identifiable, Hashable {
     case crEditor
     case crEditor
     case targetsEditor
     case targetsEditor
     case preferencesEditor
     case preferencesEditor
-    case addCarbs(editMode: Bool)
+    case addCarbs(editMode: Bool, override: Bool)
     case addTempTarget
     case addTempTarget
     case bolus(waitForSuggestion: Bool, fetch: Bool)
     case bolus(waitForSuggestion: Bool, fetch: Bool)
     case manualTempBasal
     case manualTempBasal
@@ -65,8 +65,8 @@ extension Screen {
             TargetsEditor.RootView(resolver: resolver)
             TargetsEditor.RootView(resolver: resolver)
         case .preferencesEditor:
         case .preferencesEditor:
             PreferencesEditor.RootView(resolver: resolver)
             PreferencesEditor.RootView(resolver: resolver)
-        case let .addCarbs(editMode):
-            AddCarbs.RootView(resolver: resolver, editMode: editMode)
+        case let .addCarbs(editMode, override):
+            AddCarbs.RootView(resolver: resolver, editMode: editMode, override: override)
         case .addTempTarget:
         case .addTempTarget:
             AddTempTarget.RootView(resolver: resolver)
             AddTempTarget.RootView(resolver: resolver)
         case let .bolus(waitForSuggestion, fetch):
         case let .bolus(waitForSuggestion, fetch):