From 0d3dde37307b5cc7396fd2ee7d3da9dc1d9bed01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20M=C3=A5rtensson?= Date: Wed, 8 Nov 2023 14:53:27 +0100 Subject: [PATCH 1/2] Save the standing or lying Chart setting. --- FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift b/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift index 269fe8db6..398f611e6 100644 --- a/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift +++ b/FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift @@ -24,6 +24,7 @@ extension StatConfig { subscribeSetting(\.rulerMarks, on: $rulerMarks) { rulerMarks = $0 } subscribeSetting(\.useFPUconversion, on: $useFPUconversion) { useFPUconversion = $0 } subscribeSetting(\.skipBolusScreenAfterCarbs, on: $skipBolusScreenAfterCarbs) { skipBolusScreenAfterCarbs = $0 } + subscribeSetting(\.oneDimensionalGraph, on: $oneDimensionalGraph) { oneDimensionalGraph = $0 } subscribeSetting(\.low, on: $low, initial: { let value = max(min($0, 90), 40) From d6f817543e73ab5f52a0408edb88b1822424bc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20B=20M=C3=A5rtensson?= <53905247+Jon-b-m@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:18:04 +0100 Subject: [PATCH 2/2] 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. --- Config.xcconfig | 2 +- .../Core_Data.xcdatamodel/contents | 11 + .../CGMBLEKitUI/de.lproj/Localizable.strings | 2 +- .../de.lproj/Localizable.strings | 2 +- .../G7SensorKit/de.lproj/Localizable.strings | 6 +- FreeAPS.xcodeproj/project.pbxproj | 12 +- .../Main/ar.lproj/Localizable.strings | 11 + .../Main/da.lproj/Localizable.strings | 11 + .../Main/de.lproj/Localizable.strings | 11 + .../Main/en.lproj/Localizable.strings | 9 + .../Main/es.lproj/Localizable.strings | 11 + .../Main/fi.lproj/Localizable.strings | 11 + .../Main/fr.lproj/Localizable.strings | 11 + .../Main/he.lproj/Localizable.strings | 11 + .../Main/it.lproj/Localizable.strings | 17 +- .../Main/nb.lproj/Localizable.strings | 35 ++- .../Main/nl.lproj/Localizable.strings | 11 + .../Main/pl.lproj/Localizable.strings | 11 + .../Main/pt-BR.lproj/Localizable.strings | 11 + .../Main/pt-PT.lproj/Localizable.strings | 11 + .../Main/ru.lproj/Localizable.strings | 37 ++- .../Main/sk.lproj/Localizable.strings | 11 + .../Main/sv.lproj/Localizable.strings | 12 + .../Main/tr.lproj/Localizable.strings | 11 + .../Main/uk.lproj/Localizable.strings | 11 + .../Main/zh-Hans.lproj/Localizable.strings | 11 + FreeAPS/Sources/Models/Charts.swift | 17 ++ FreeAPS/Sources/Models/TIRforChart.swift | 8 - .../Modules/AddCarbs/AddCarbsStateModel.swift | 36 +-- .../AddCarbs/View/AddCarbsRootView.swift | 268 +++++++++++------- .../Modules/Bolus/BolusStateModel.swift | 16 +- .../View/AlternativeBolusCalcRootView.swift | 74 +++-- .../Bolus/View/DefaultBolusCalcRootView.swift | 78 +++-- .../Modules/Bolus/View/Predictions.swift | 110 +++++++ .../Sources/Modules/Home/HomeStateModel.swift | 2 +- .../Modules/Home/View/HomeRootView.swift | 2 +- FreeAPS/Sources/Router/Screen.swift | 6 +- 37 files changed, 696 insertions(+), 231 deletions(-) create mode 100644 FreeAPS/Sources/Models/Charts.swift delete mode 100644 FreeAPS/Sources/Models/TIRforChart.swift create mode 100644 FreeAPS/Sources/Modules/Bolus/View/Predictions.swift diff --git a/Config.xcconfig b/Config.xcconfig index 2d57d6bd6..53495d2b4 100644 --- a/Config.xcconfig +++ b/Config.xcconfig @@ -1,5 +1,5 @@ APP_DISPLAY_NAME = iAPS -APP_VERSION = 2.2.7 +APP_VERSION = 2.2.8 APP_BUILD_NUMBER = 1 COPYRIGHT_NOTICE = DEVELOPER_TEAM = ##TEAM_ID## diff --git a/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents b/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents index fefaa04c7..3a617a7a3 100644 --- a/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents +++ b/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents @@ -1,5 +1,16 @@ + + + + + + + + + + + diff --git a/Dependencies/CGMBLEKit/CGMBLEKitUI/de.lproj/Localizable.strings b/Dependencies/CGMBLEKit/CGMBLEKitUI/de.lproj/Localizable.strings index d3217ad23..99cf84b4d 100644 --- a/Dependencies/CGMBLEKit/CGMBLEKitUI/de.lproj/Localizable.strings +++ b/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"; /* Title describing sensor expiration */ -"Sensor Expires" = "Sensor-Ablaufzeitpunkt"; +"Sensor Expires" = "Sensor läuft ab"; /* Title describing past sensor expiration */ "Sensor Expired" = "Sensor abgelaufen"; diff --git a/Dependencies/G7SensorKit/G7SensorKitUI/de.lproj/Localizable.strings b/Dependencies/G7SensorKit/G7SensorKitUI/de.lproj/Localizable.strings index 5ca3fcc5e..e9ae6ef1b 100644 --- a/Dependencies/G7SensorKit/G7SensorKitUI/de.lproj/Localizable.strings +++ b/Dependencies/G7SensorKit/G7SensorKitUI/de.lproj/Localizable.strings @@ -54,7 +54,7 @@ "Last Reading" = "Letzte Messung"; /* 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 */ "LOW" = "NIEDRIG"; diff --git a/Dependencies/G7SensorKit/de.lproj/Localizable.strings b/Dependencies/G7SensorKit/de.lproj/Localizable.strings index 16733d4cb..ac6fad5f7 100644 --- a/Dependencies/G7SensorKit/de.lproj/Localizable.strings +++ b/Dependencies/G7SensorKit/de.lproj/Localizable.strings @@ -2,7 +2,7 @@ "Dexcom G7" = "Dexcom G7"; /* 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 */ "Continue" = "Fortsetzen"; @@ -11,7 +11,7 @@ "Cancel" = "Abbrechen"; /* 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. */ "Sensor is OK" = "Sensor ist OK"; @@ -70,7 +70,7 @@ "Configuration" = "Konfiguration"; /* title for g7 config settings to upload readings */ -"Upload Readings" = "Upload von Messwerten"; +"Upload Readings" = "Werte hochladen"; /* Button */ "Scan for new sensor" = "Nach neuem Sensor suchen"; diff --git a/FreeAPS.xcodeproj/project.pbxproj b/FreeAPS.xcodeproj/project.pbxproj index ac5c0d98d..ebd4e222d 100644 --- a/FreeAPS.xcodeproj/project.pbxproj +++ b/FreeAPS.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 190EBCC829FF13AA00BA767D /* StatConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* StatConfigStateModel.swift */; }; 190EBCCB29FF13CB00BA767D /* StatConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* StatConfigRootView.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 */; }; 1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.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 */; }; 19D466A729AA2C22004D5F33 /* FPUConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A629AA2C22004D5F33 /* FPUConfigStateModel.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 */; }; 19DA48E929CD339C00EEA1E7 /* 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 = ""; }; 1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = ""; }; + 19229B952AFBB84800CD91CA /* Predictions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Predictions.swift; sourceTree = ""; }; 1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = ""; }; 1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = ""; }; 1927C8EB2744611900347C69 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; @@ -568,7 +570,7 @@ 19D466A429AA2BD4004D5F33 /* FPUConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigProvider.swift; sourceTree = ""; }; 19D466A629AA2C22004D5F33 /* FPUConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigStateModel.swift; sourceTree = ""; }; 19D466A929AA3099004D5F33 /* FPUConfigRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUConfigRootView.swift; sourceTree = ""; }; - 19D4E4EA29FC6A9F00351451 /* TIRforChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TIRforChart.swift; sourceTree = ""; }; + 19D4E4EA29FC6A9F00351451 /* Charts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Charts.swift; sourceTree = ""; }; 19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 19DC677E29CA675700FD9EC4 /* OverrideProfilesDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProfilesDataFlow.swift; sourceTree = ""; }; 19DC678029CA676A00FD9EC4 /* OverrideProfilesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProfilesProvider.swift; sourceTree = ""; }; @@ -1655,7 +1657,7 @@ FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */, 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */, 1967DFBD29D052C200759F30 /* Icons.swift */, - 19D4E4EA29FC6A9F00351451 /* TIRforChart.swift */, + 19D4E4EA29FC6A9F00351451 /* Charts.swift */, 19A910352A24D6D700C8951B /* DateFilter.swift */, 193F6CDC2A512C8F001240FD /* Loops.swift */, CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */, @@ -2084,6 +2086,7 @@ 10A0C32B0DAB52726EF9B6D9 /* BolusRootView.swift */, BDFD165B2AE40688007F0DDA /* DefaultBolusCalcRootView.swift */, BDFD16592AE40438007F0DDA /* AlternativeBolusCalcRootView.swift */, + 19229B952AFBB84800CD91CA /* Predictions.swift */, ); path = View; sourceTree = ""; @@ -2830,6 +2833,7 @@ 1967DFBE29D052C200759F30 /* Icons.swift in Sources */, 38E8754F275556FA00975559 /* WatchManager.swift in Sources */, A228DF96647338139F152B15 /* PreferencesEditorDataFlow.swift in Sources */, + 19229B962AFBB84800CD91CA /* Predictions.swift in Sources */, 389ECE052601144100D86C4F /* ConcurrentMap.swift in Sources */, CE7CA3562A064973004BE681 /* StateIntentRequest.swift in Sources */, E4984C5262A90469788754BB /* PreferencesEditorProvider.swift in Sources */, @@ -2862,7 +2866,7 @@ 0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */, 1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */, BDFD165C2AE40688007F0DDA /* DefaultBolusCalcRootView.swift in Sources */, - 19D4E4EB29FC6A9F00351451 /* TIRforChart.swift in Sources */, + 19D4E4EB29FC6A9F00351451 /* Charts.swift in Sources */, FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */, F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */, 3862CC1F273FDC9200BF832C /* CalibrationsChart.swift in Sources */, diff --git a/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings index 2a918bab3..a4c97a702 100644 --- a/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continue"; + /* 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?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings index 21cff8d8b..b576f2386 100644 --- a/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Fortsæt"; + /* Home title */ "Home" = "Hjem"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Krævede kulhydrater"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Er du sikker?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventuelt Glukoseniveau"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings index 79e1f22ee..7a0d7e035 100644 --- a/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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"; +/* Bolus View Continue Button */ +"Continue" = "Fortsetzen"; + /* Home title */ "Home" = "Hauptseite"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Kohlenhydrate erforderlich"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Sind Sie sicher?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Prognostizierte Glukose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Ziel Glukose"; diff --git a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings index aa9173ed6..ab02a1432 100644 --- a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings @@ -121,6 +121,9 @@ /* */ "Carbs required" = "Carbs required"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Are you sure?"; @@ -1400,6 +1403,12 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +/* */ +"Glucose, " = "Glucose, "; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings index 369c7e260..d292a9ee5 100644 --- a/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continuar"; + /* Home title */ "Home" = "Inicio"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Se requieren carbohidratos"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "¿Estás seguro?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings index 19596ed75..36459d38b 100644 --- a/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Jatka"; + /* 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?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings index edfd6baa1..a01828158 100644 --- a/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continuer"; + /* Home title */ "Home" = "Page d'accueil"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Glucides requis"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Êtes-vous sûr ?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings index 2a918bab3..a4c97a702 100644 --- a/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continue"; + /* 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?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings index d71b6eba0..11070d45e 100644 --- a/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continua"; + /* Home title */ "Home" = "Home"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Carboidrati necessari"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "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"; /* 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"; @@ -1134,7 +1140,7 @@ Enact a temp Basal or a temp target */ "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"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "previsione glicemica"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glicemia"; @@ -1556,7 +1567,7 @@ Enact a temp Basal or a temp target */ "Total Delivery" = "Totale Consegnata"; /* */ -"Add Omnipod Dash" = "Agg Omnipod Dash"; +"Add Omnipod Dash" = "Aggiungi Omnipod Dash"; /* */ "Insert Cannula" = "Inserisci cannula"; diff --git a/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings index e1e2c6e8b..a37a1a1bb 100644 --- a/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings @@ -56,37 +56,40 @@ "Error at" = "Feil kl."; /* Bolus View Meal Summary Header */ -"Meal Summary" = "Meal Summary"; +"Meal Summary" = "Oppsummering av måltid"; /* Bolus View Meal Edit Meal Button */ -"Edit Meal" = "Edit Meal"; +"Edit Meal" = "Endre måltid"; /* Bolus View Meal Add Meal Button */ -"Add Meal" = "Add Meal"; +"Add Meal" = "Legg til måltid"; /* Bolus View Bolus Summary Header */ -"Bolus Summary" = "Bolus Summary"; +"Bolus Summary" = "Bolus-sammendrag"; /* For the Bolus View pop-up */ -"Calculations" = "Calculations"; +"Calculations" = "Beregninger"; /* For the Bolus View pop-up */ -"Fatty Meal" = "Fatty Meal"; +"Fatty Meal" = "Fett måltid"; /* For the Bolus View pop-up */ "Full Bolus" = "Full Bolus"; /* For the Bolus View pop-up */ -"Fraction" = "Fraction"; +"Fraction" = "Brøkdel"; /* 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 */ -"Result" = "Result"; +"Result" = "Resultat"; /* 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" = "Hjem"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Karbo nødvendig"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Er du sikker?"; @@ -603,7 +609,7 @@ Enact a temp Basal or a temp target */ "Automatic" = "Automatisk"; /* External insulin treatments */ -"External" = "External"; +"External" = "Ekstern"; /* */ "Other" = "Annet"; @@ -1208,7 +1214,7 @@ Enact a temp Basal or a temp target */ "SMB" = "SMB"; /* A manually entered dose of external insulin */ -"External Insulin" = "External Insulin"; +"External Insulin" = "Eksternt insulin"; /* Status highlight when manual temp basal is running. */ "Manual Basal" = "Manuell basal"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Beregnet blodsukker"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Blodsukkermål"; diff --git a/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings index 202d39c65..e343bd77a 100644 --- a/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Vervolg"; + /* Home title */ "Home" = "Hoofdmenu"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Koolhydraten benodigd"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Weet je het zeker?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Voorspelde glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Doel glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings index 576a542c3..7199dadbb 100644 --- a/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Kontynuuj"; + /* Home title */ "Home" = "Home"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Carbs required"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Czy jesteś pewien?"; @@ -1398,6 +1404,11 @@ Połączono z Nightscout!"; /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings index d91200004..95ee3eb09 100644 --- a/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continuar"; + /* Home title */ "Home" = "Tela inicial"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Carbos necessários"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Tem certeza?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings index 78e5d7811..10a944286 100644 --- a/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Continue"; + /* Home title */ "Home" = "Tela inicial"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Carbos necessários"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Tem certeza?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings index 73483ee46..e73c85b37 100644 --- a/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings @@ -56,37 +56,40 @@ "Error at" = "Ошибка в"; /* Bolus View Meal Summary Header */ -"Meal Summary" = "Meal Summary"; +"Meal Summary" = "Итог питания"; /* Bolus View Meal Edit Meal Button */ -"Edit Meal" = "Edit Meal"; +"Edit Meal" = "Изменить еду"; /* Bolus View Meal Add Meal Button */ -"Add Meal" = "Add Meal"; +"Add Meal" = "Добавить еду"; /* Bolus View Bolus Summary Header */ -"Bolus Summary" = "Bolus Summary"; +"Bolus Summary" = "Болюс суммарно"; /* For the Bolus View pop-up */ -"Calculations" = "Calculations"; +"Calculations" = "Вычисления"; /* For the Bolus View pop-up */ -"Fatty Meal" = "Fatty Meal"; +"Fatty Meal" = "Жирная пища"; /* For the Bolus View pop-up */ -"Full Bolus" = "Full Bolus"; +"Full Bolus" = "Полный болюс"; /* For the Bolus View pop-up */ -"Fraction" = "Fraction"; +"Fraction" = "Доля"; /* For the Bolus View pop-up */ -"Fatty Meal Factor" = "Fatty Meal Factor"; +"Fatty Meal Factor" = "Фактор жирной пищи"; /* For the Bolus View pop-up */ -"Result" = "Result"; +"Result" = "Итого"; /* 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" = "Главная"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Требуются углеводы"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Вы уверены?"; @@ -603,7 +609,7 @@ Enact a temp Basal or a temp target */ "Automatic" = "Автоматически"; /* External insulin treatments */ -"External" = "External"; +"External" = "Внешний"; /* */ "Other" = "Прочее"; @@ -1208,7 +1214,7 @@ Enact a temp Basal or a temp target */ "SMB" = "SMB"; /* A manually entered dose of external insulin */ -"External Insulin" = "External Insulin"; +"External Insulin" = "Внешний инсулин"; /* Status highlight when manual temp basal is running. */ "Manual Basal" = "Ручная ВБС"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Возможная глюкоза"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Целевая глюкоза"; diff --git a/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings index edc3c93cd..85094b83c 100644 --- a/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Pokračovať"; + /* 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?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings index ded401097..8646f0066 100644 --- a/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Fortsätt"; + /* Home title */ "Home" = "Hem"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Kolhydrater krävs"; +/* Saved Food Presets */ +"Saved Food" = "Mat"; + /* */ "Are you sure?" = "Är du säker?"; @@ -1396,6 +1402,12 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Blodsockerprognos"; +/* */ +"Please wait" = "Vänta..."; + +/* */ +"Glucose, " = "Blodsocker, "; + /* */ "Target Glucose" = "Målvärde"; diff --git a/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings index ce64317a6..82e8d804a 100644 --- a/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "Devam et"; + /* Home title */ "Home" = "Ana sayfa"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Gerekli karbonhidrat"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Emin misiniz?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings index 65ddbb630..b4d2d58ff 100644 --- a/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* For the Bolus View pop-up */ "Your entered amount was limited by your max Bolus setting of %d%@" = "Введена вами сума була обмежена налаштуванням максимального болюсу %d%@"; +/* Bolus View Continue Button */ +"Continue" = "Продовжити"; + /* Home title */ "Home" = "Головна"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "Введіть кількість вуглеводів"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "Ви впевнені?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Можлива глюкоза"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Цільова Глюкоза"; diff --git a/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings index 3324c2533..b2cc93627 100644 --- a/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings @@ -88,6 +88,9 @@ /* 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%@"; +/* Bolus View Continue Button */ +"Continue" = "继续"; + /* Home title */ "Home" = "主页"; @@ -118,6 +121,9 @@ /* */ "Carbs required" = "所需碳水化合物"; +/* Saved Food Presets */ +"Saved Food" = "Saved Food"; + /* */ "Are you sure?" = "确定?"; @@ -1396,6 +1402,11 @@ Enact a temp Basal or a temp target */ /* */ "Eventual Glucose" = "Eventual Glucose"; +/* */ +"Please wait" = "Please wait"; + +"Glucose, %@" = "Glucose, %@"; + /* */ "Target Glucose" = "Target Glucose"; diff --git a/FreeAPS/Sources/Models/Charts.swift b/FreeAPS/Sources/Models/Charts.swift new file mode 100644 index 000000000..82896af45 --- /dev/null +++ b/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() +} diff --git a/FreeAPS/Sources/Models/TIRforChart.swift b/FreeAPS/Sources/Models/TIRforChart.swift deleted file mode 100644 index bdbc13e38..000000000 --- a/FreeAPS/Sources/Models/TIRforChart.swift +++ /dev/null @@ -1,8 +0,0 @@ - -import Foundation - -struct ShapeModel: Identifiable { - var type: String - var percent: Decimal - var id = UUID() -} diff --git a/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift b/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift index f309a95b7..c10114e6f 100644 --- a/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift +++ b/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift @@ -31,7 +31,7 @@ extension AddCarbs { useFPUconversion = settingsManager.settings.useFPUconversion } - func add() { + func add(_ continue_: Bool, fetch: Bool) { guard carbs > 0 || fat > 0 || protein > 0 else { showModal(for: nil) return @@ -51,7 +51,7 @@ extension AddCarbs { )] carbsStorage.storeCarbs(carbsToStore) - if skipBolus { + if skipBolus, !continue_, !fetch { apsManager.determineBasalSync() showModal(for: nil) } else if carbs > 0 { @@ -140,16 +140,16 @@ extension AddCarbs { var addedString = "" if extracarbs > 0, filteredArray.isNotEmpty { - addedString += "Additional carbs: \(extracarbs) " + addedString += "Additional carbs: \(extracarbs) ," } else if extracarbs < 0 { addedString += "Removed carbs: \(extracarbs) " } 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 { - 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 != "" { waitersNotepad.append(addedString) @@ -170,7 +170,7 @@ extension AddCarbs { func loadEntries(_ editMode: Bool) { if editMode { - coredataContext.perform { + coredataContext.performAndWait { var mealToEdit = [Meals]() let requestMeal = Meals.fetchRequest() as NSFetchRequest let sortMeal = NSSortDescriptor(key: "createdAt", ascending: false) @@ -188,15 +188,17 @@ extension AddCarbs { } 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() + } } } } diff --git a/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift b/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift index b05230b36..c0b9a028b 100644 --- a/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift +++ b/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift @@ -6,10 +6,12 @@ extension AddCarbs { struct RootView: BaseView { let resolver: Resolver let editMode: Bool + let override: Bool @StateObject var state = StateModel() @State var dish: String = "" @State var isPromptPresented = false @State var saved = false + @State var pushed = false @State private var showAlert = false @FocusState private var isFocused: Bool @@ -29,12 +31,12 @@ extension AddCarbs { var body: some View { Form { - if let carbsReq = state.carbsRequired { + if let carbsReq = state.carbsRequired, state.carbs < carbsReq { Section { HStack { Text("Carbs required") 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 ) Text("grams").foregroundColor(.secondary) - }.padding(.vertical) + } if state.useFPUconversion { 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 { Text("Note").foregroundColor(.secondary) TextField("", text: $state.note).multilineTextAlignment(.trailing) @@ -62,25 +108,23 @@ extension AddCarbs { Button { isFocused = false } label: { Image(systemName: "keyboard.chevron.compact.down") } .controlSize(.mini) } - }.focused($isFocused) - .popover(isPresented: $isPromptPresented) { - presetPopover - } - } - - Section { - mealPresets + } + .focused($isFocused) + .popover(isPresented: $isPromptPresented) { + presetPopover + } } 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) - } footer: { Text(state.waitersNotepad().description) } + }.listRowBackground(!empty ? Color(.systemBlue) : Color(.systemGray4)) + .tint(.white) Section { - DatePicker("Date", selection: $state.date) + mealPresets } } .onAppear { @@ -93,7 +137,7 @@ extension AddCarbs { .navigationBarItems(leading: Button("Close", action: state.hideModal)) } - var presetPopover: some View { + private var presetPopover: some View { Form { Section { 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 { 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 Text(preset.dish ?? "").tag(preset as Presets?) } } .labelsHidden() - .frame(maxWidth: .infinity, alignment: .trailing) + .frame(maxWidth: .infinity, alignment: .center) ._onBindingChange($state.selection) { _ in 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.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 + ) + } +} diff --git a/FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift b/FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift index 26f7e4203..cc64987ee 100644 --- a/FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift +++ b/FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift @@ -1,5 +1,3 @@ - -import CoreData import LoopKit import SwiftUI import Swinject @@ -14,9 +12,8 @@ extension Bolus { @Injected() var settings: SettingsManager! @Injected() var nsManager: NightscoutManager! - let coredataContext = CoreDataStack.shared.persistentContainer.viewContext - @Published var suggestion: Suggestion? + @Published var predictions: Predictions? @Published var amount: Decimal = 0 @Published var insulinRecommended: Decimal = 0 @Published var insulinRequired: Decimal = 0 @@ -60,7 +57,6 @@ extension Bolus { @Published var fattyMeals: Bool = false @Published var fattyMealFactor: Decimal = 0 @Published var useFattyMealCorrectionFactor: Bool = false - @Published var eventualBG: Int = 0 @Published var meal: [CarbsEntry]? @Published var carbs: Decimal = 0 @@ -93,6 +89,12 @@ extension Bolus { } }.store(in: &lifetime) } + if let notNilSugguestion = provider.suggestion { + suggestion = notNilSugguestion + if let notNilPredictions = suggestion?.predictions { + predictions = notNilPredictions + } + } } 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) - showModal(for: .addCarbs(editMode: complexEntry)) + showModal(for: .addCarbs(editMode: complexEntry, override: override)) } func delete(deleteTwice: Bool, id: String) { diff --git a/FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift b/FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift index f5999be92..7427593dc 100644 --- a/FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift +++ b/FreeAPS/Sources/Modules/Bolus/View/AlternativeBolusCalcRootView.swift @@ -1,3 +1,4 @@ +import Charts import CoreData import SwiftUI import Swinject @@ -56,6 +57,15 @@ extension Bolus { var body: some View { Form { + Section { + if state.waitForSuggestion { + Text("Please wait") + } else { + predictionChart + } + } header: { Text("Predictions") } + + Section {} if fetch { Section { mealEntries @@ -108,27 +118,26 @@ extension Bolus { .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") } if state.amount > 0 { @@ -139,10 +148,9 @@ extension Bolus { } label: { Text(exceededMaxBolus ? "Max Bolus exceeded!" : "Enact bolus") } .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 { @@ -160,7 +168,7 @@ extension Bolus { .navigationBarTitleDisplayMode(.inline) .navigationBarItems( leading: Button { - carbssView() + carbsView() } 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 var bolusInfoAlternativeCalculator: some View { VStack { @@ -249,6 +265,10 @@ extension Bolus { ) } + private var disabled: Bool { + state.amount <= 0 || state.amount > state.maxBolus + } + var changed: Bool { ((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) } - func carbssView() { + func carbsView() { let id_ = meal.first?.id ?? "" if fetch { keepForNextWiew = true - state.backToCarbsView(complexEntry: fetch, id_) + state.backToCarbsView(complexEntry: fetch, id_, override: false) } else { - state.showModal(for: .addCarbs(editMode: false)) + state.backToCarbsView(complexEntry: false, id_, override: true) } } diff --git a/FreeAPS/Sources/Modules/Bolus/View/DefaultBolusCalcRootView.swift b/FreeAPS/Sources/Modules/Bolus/View/DefaultBolusCalcRootView.swift index 8d66170cb..3ad2b7b23 100644 --- a/FreeAPS/Sources/Modules/Bolus/View/DefaultBolusCalcRootView.swift +++ b/FreeAPS/Sources/Modules/Bolus/View/DefaultBolusCalcRootView.swift @@ -1,3 +1,5 @@ +import Charts +import CoreData import SwiftUI import Swinject @@ -35,6 +37,14 @@ extension Bolus { var body: some View { Form { + Section { + if state.waitForSuggestion { + Text("Please wait") + } else { + predictionChart + } + } header: { Text("Predictions") } + if fetch { Section { mealEntries @@ -70,37 +80,37 @@ extension Bolus { else { state.amount = state.insulinRecommended } } }.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") } - 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 { Section { Button { @@ -143,7 +153,7 @@ extension Bolus { .navigationBarTitleDisplayMode(.inline) .navigationBarItems( leading: Button { - carbssView() + carbsView() } 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 { ((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) } - func carbssView() { + func carbsView() { let id_ = meal.first?.id ?? "" if fetch { keepForNextWiew = true - state.backToCarbsView(complexEntry: fetch, id_) + state.backToCarbsView(complexEntry: fetch, id_, override: false) } else { - state.showModal(for: .addCarbs(editMode: false)) + state.backToCarbsView(complexEntry: false, id_, override: true) } } diff --git a/FreeAPS/Sources/Modules/Bolus/View/Predictions.swift b/FreeAPS/Sources/Modules/Bolus/View/Predictions.swift new file mode 100644 index 000000000..1556bb3ea --- /dev/null +++ b/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) + } +} diff --git a/FreeAPS/Sources/Modules/Home/HomeStateModel.swift b/FreeAPS/Sources/Modules/Home/HomeStateModel.swift index 2075d544b..50d737ead 100644 --- a/FreeAPS/Sources/Modules/Home/HomeStateModel.swift +++ b/FreeAPS/Sources/Modules/Home/HomeStateModel.swift @@ -196,7 +196,7 @@ extension Home { } func addCarbs() { - showModal(for: .addCarbs(editMode: false)) + showModal(for: .addCarbs(editMode: false, override: false)) } func runLoop() { diff --git a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift index acc79ac51..ed4c3645f 100644 --- a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift +++ b/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) HStack { - Button { state.showModal(for: .addCarbs(editMode: false)) } + Button { state.showModal(for: .addCarbs(editMode: false, override: false)) } label: { ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) { Image("carbs") diff --git a/FreeAPS/Sources/Router/Screen.swift b/FreeAPS/Sources/Router/Screen.swift index 23f83f4a9..55a634de8 100644 --- a/FreeAPS/Sources/Router/Screen.swift +++ b/FreeAPS/Sources/Router/Screen.swift @@ -14,7 +14,7 @@ enum Screen: Identifiable, Hashable { case crEditor case targetsEditor case preferencesEditor - case addCarbs(editMode: Bool) + case addCarbs(editMode: Bool, override: Bool) case addTempTarget case bolus(waitForSuggestion: Bool, fetch: Bool) case manualTempBasal @@ -65,8 +65,8 @@ extension Screen { TargetsEditor.RootView(resolver: resolver) case .preferencesEditor: 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: AddTempTarget.RootView(resolver: resolver) case let .bolus(waitForSuggestion, fetch):