소스 검색

New stat View (#33)

New Statistics View
Configure the BG limits in one place for entire app (in notifications) for charts, colour of  BG in header and for statistics.
Clean up.
Optional upload of statistics (and preferences) to NS once every 22 hours. 
Remove redundant settings.
Jon B Mårtensson 3 년 전
부모
커밋
a7e46d2b82
29개의 변경된 파일1507개의 추가작업 그리고 1049개의 파일을 삭제
  1. 4 1
      Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents
  2. 40 8
      FreeAPS.xcodeproj/project.pbxproj
  3. 1 1
      FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved
  4. 1 1
      FreeAPS/Resources/javascript/bundle/determine-basal.js
  5. 1 1
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  6. 561 572
      FreeAPS/Sources/APS/APSManager.swift
  7. 1 4
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  8. 3 2
      FreeAPS/Sources/Models/FreeAPSSettings.swift
  9. 0 6
      FreeAPS/Sources/Models/Preferences.swift
  10. 8 0
      FreeAPS/Sources/Models/TIRforChart.swift
  11. 4 4
      FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
  12. 2 4
      FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift
  13. 28 26
      FreeAPS/Sources/Modules/ConfigEditor/View/ConfigEditorRootView.swift
  14. 0 41
      FreeAPS/Sources/Modules/Home/DurationButton.swift
  15. 0 1
      FreeAPS/Sources/Modules/Home/HomeDataFlow.swift
  16. 0 8
      FreeAPS/Sources/Modules/Home/HomeProvider.swift
  17. 9 22
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  18. 20 21
      FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift
  19. 6 5
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  20. 16 264
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  21. 1 0
      FreeAPS/Sources/Modules/Main/MainStateModel.swift
  22. 0 51
      FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift
  23. 1 1
      FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift
  24. 5 0
      FreeAPS/Sources/Modules/Stat/StatDataFlow.swift
  25. 3 0
      FreeAPS/Sources/Modules/Stat/StatProvider.swift
  26. 21 0
      FreeAPS/Sources/Modules/Stat/StatStateModel.swift
  27. 767 0
      FreeAPS/Sources/Modules/Stat/View/StatRootView.swift
  28. 4 4
      FreeAPS/Sources/Router/Screen.swift
  29. 0 1
      FreeAPS/Sources/Services/UserNotifiactions/UserNotificationsManager.swift

+ 4 - 1
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -1,5 +1,5 @@
 <?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="21513" systemVersion="22D68" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22E261" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
     <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"/>
@@ -63,6 +63,9 @@
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
         <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
     </entity>
     </entity>
+    <entity name="StatsData" representedClassName="StatsData" syncable="YES" codeGenerationType="class">
+        <attribute name="lastrun" optional="YES" attributeType="Date" defaultDateTimeInterval="704497620" usesScalarValueType="NO"/>
+    </entity>
     <entity name="Target" representedClassName="Target" syncable="YES" codeGenerationType="class">
     <entity name="Target" representedClassName="Target" syncable="YES" codeGenerationType="class">
         <attribute name="current" optional="YES" attributeType="Decimal" defaultValueString="100"/>
         <attribute name="current" optional="YES" attributeType="Decimal" defaultValueString="100"/>
     </entity>
     </entity>

+ 40 - 8
FreeAPS.xcodeproj/project.pbxproj

@@ -21,13 +21,13 @@
 		1967DFC229D053D300759F30 /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFC129D053D300759F30 /* IconImage.swift */; };
 		1967DFC229D053D300759F30 /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFC129D053D300759F30 /* IconImage.swift */; };
 		19795118275953E50044850D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		19795118275953E50044850D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
-		19854F492961C3E500941627 /* DurationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19854F482961C3E500941627 /* DurationButton.swift */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
 		19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; };
 		19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; };
 		19D466A329AA2B80004D5F33 /* FPUConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A229AA2B80004D5F33 /* FPUConfigDataFlow.swift */; };
 		19D466A329AA2B80004D5F33 /* FPUConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19D466A229AA2B80004D5F33 /* FPUConfigDataFlow.swift */; };
 		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 */; };
 		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 */; };
@@ -39,6 +39,10 @@
 		19E1F7EA29D082ED005C8D20 /* IconConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E1F7E929D082ED005C8D20 /* IconConfigProvider.swift */; };
 		19E1F7EA29D082ED005C8D20 /* IconConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E1F7E929D082ED005C8D20 /* IconConfigProvider.swift */; };
 		19E1F7EC29D082FE005C8D20 /* IconConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E1F7EB29D082FE005C8D20 /* IconConfigStateModel.swift */; };
 		19E1F7EC29D082FE005C8D20 /* IconConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E1F7EB29D082FE005C8D20 /* IconConfigStateModel.swift */; };
 		19E1F7EF29D08EBA005C8D20 /* IconConfigRootWiew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E1F7EE29D08EBA005C8D20 /* IconConfigRootWiew.swift */; };
 		19E1F7EF29D08EBA005C8D20 /* IconConfigRootWiew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E1F7EE29D08EBA005C8D20 /* IconConfigRootWiew.swift */; };
+		19F95FF329F10FBC00314DDC /* StatDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F95FF229F10FBC00314DDC /* StatDataFlow.swift */; };
+		19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F95FF429F10FCF00314DDC /* StatProvider.swift */; };
+		19F95FF729F10FEE00314DDC /* StatStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F95FF629F10FEE00314DDC /* StatStateModel.swift */; };
+		19F95FFA29F1102A00314DDC /* StatRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F95FF929F1102A00314DDC /* StatRootView.swift */; };
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
 		23888883D4EA091C88480FF2 /* BolusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19984D62EFC0035A9E9644D /* BolusProvider.swift */; };
 		23888883D4EA091C88480FF2 /* BolusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19984D62EFC0035A9E9644D /* BolusProvider.swift */; };
@@ -507,7 +511,6 @@
 		198377E2266C0AC8004DE65E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E2266C0AC8004DE65E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E3266C0ADC004DE65E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E3266C0ADC004DE65E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E4266C13D2004DE65E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377E4266C13D2004DE65E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
-		19854F482961C3E500941627 /* DurationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationButton.swift; sourceTree = "<group>"; };
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
 		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -518,6 +521,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>"; };
 		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>"; };
@@ -527,6 +531,10 @@
 		19E1F7E929D082ED005C8D20 /* IconConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconConfigProvider.swift; sourceTree = "<group>"; };
 		19E1F7E929D082ED005C8D20 /* IconConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconConfigProvider.swift; sourceTree = "<group>"; };
 		19E1F7EB29D082FE005C8D20 /* IconConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconConfigStateModel.swift; sourceTree = "<group>"; };
 		19E1F7EB29D082FE005C8D20 /* IconConfigStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconConfigStateModel.swift; sourceTree = "<group>"; };
 		19E1F7EE29D08EBA005C8D20 /* IconConfigRootWiew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconConfigRootWiew.swift; sourceTree = "<group>"; };
 		19E1F7EE29D08EBA005C8D20 /* IconConfigRootWiew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconConfigRootWiew.swift; sourceTree = "<group>"; };
+		19F95FF229F10FBC00314DDC /* StatDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatDataFlow.swift; sourceTree = "<group>"; };
+		19F95FF429F10FCF00314DDC /* StatProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatProvider.swift; sourceTree = "<group>"; };
+		19F95FF629F10FEE00314DDC /* StatStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatStateModel.swift; sourceTree = "<group>"; };
+		19F95FF929F1102A00314DDC /* StatRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatRootView.swift; sourceTree = "<group>"; };
 		1CAE81192B118804DCD23034 /* SnoozeProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeProvider.swift; sourceTree = "<group>"; };
 		1CAE81192B118804DCD23034 /* SnoozeProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeProvider.swift; sourceTree = "<group>"; };
 		212E8BFE6D66EE65AA26A114 /* CalibrationsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CalibrationsProvider.swift; sourceTree = "<group>"; };
 		212E8BFE6D66EE65AA26A114 /* CalibrationsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CalibrationsProvider.swift; sourceTree = "<group>"; };
 		223EC0494F55A91E3EA69EF4 /* BolusStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusStateModel.swift; sourceTree = "<group>"; };
 		223EC0494F55A91E3EA69EF4 /* BolusStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusStateModel.swift; sourceTree = "<group>"; };
@@ -1037,6 +1045,25 @@
 			path = View;
 			path = View;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		19F95FF129F10F9C00314DDC /* Stat */ = {
+			isa = PBXGroup;
+			children = (
+				19F95FF229F10FBC00314DDC /* StatDataFlow.swift */,
+				19F95FF429F10FCF00314DDC /* StatProvider.swift */,
+				19F95FF629F10FEE00314DDC /* StatStateModel.swift */,
+				19F95FF829F10FF600314DDC /* View */,
+			);
+			path = Stat;
+			sourceTree = "<group>";
+		};
+		19F95FF829F10FF600314DDC /* View */ = {
+			isa = PBXGroup;
+			children = (
+				19F95FF929F1102A00314DDC /* StatRootView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		29B478DF61BF8D270F7D8954 /* Snooze */ = {
 		29B478DF61BF8D270F7D8954 /* Snooze */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -1060,6 +1087,7 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				CE94597C29E9E1CD0047C9C6 /* WatchConfig */,
 				CE94597C29E9E1CD0047C9C6 /* WatchConfig */,
+				19F95FF129F10F9C00314DDC /* Stat */,
 				19E1F7E629D0828B005C8D20 /* IconConfig */,
 				19E1F7E629D0828B005C8D20 /* IconConfig */,
 				19D466A129AA2B0A004D5F33 /* FPUConfig */,
 				19D466A129AA2B0A004D5F33 /* FPUConfig */,
 				F90692CD274B99850037068D /* HealthKit */,
 				F90692CD274B99850037068D /* HealthKit */,
@@ -1156,7 +1184,6 @@
 				3811DE2A25C9D49500A708ED /* HomeDataFlow.swift */,
 				3811DE2A25C9D49500A708ED /* HomeDataFlow.swift */,
 				3811DE2925C9D49500A708ED /* HomeProvider.swift */,
 				3811DE2925C9D49500A708ED /* HomeProvider.swift */,
 				3811DE2825C9D49500A708ED /* HomeStateModel.swift */,
 				3811DE2825C9D49500A708ED /* HomeStateModel.swift */,
-				19854F482961C3E500941627 /* DurationButton.swift */,
 				3811DE2C25C9D49500A708ED /* View */,
 				3811DE2C25C9D49500A708ED /* View */,
 			);
 			);
 			path = Home;
 			path = Home;
@@ -1512,6 +1539,7 @@
 				FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */,
 				FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */,
 				FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */,
 				FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */,
 				1967DFBD29D052C200759F30 /* Icons.swift */,
 				1967DFBD29D052C200759F30 /* Icons.swift */,
+				19D4E4EA29FC6A9F00351451 /* TIRforChart.swift */,
 			);
 			);
 			path = Models;
 			path = Models;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2379,7 +2407,6 @@
 				385CEAC425F2F154002D6D5B /* AnnouncementsStorage.swift in Sources */,
 				385CEAC425F2F154002D6D5B /* AnnouncementsStorage.swift in Sources */,
 				38AEE73D25F0200C0013F05B /* FreeAPSSettings.swift in Sources */,
 				38AEE73D25F0200C0013F05B /* FreeAPSSettings.swift in Sources */,
 				38FCF3FD25E997A80078B0D1 /* PumpHistoryStorage.swift in Sources */,
 				38FCF3FD25E997A80078B0D1 /* PumpHistoryStorage.swift in Sources */,
-				19854F492961C3E500941627 /* DurationButton.swift in Sources */,
 				38D0B3B625EBE24900CB6E88 /* Battery.swift in Sources */,
 				38D0B3B625EBE24900CB6E88 /* Battery.swift in Sources */,
 				38C4D33725E9A1A300D30B77 /* DispatchQueue+Extensions.swift in Sources */,
 				38C4D33725E9A1A300D30B77 /* DispatchQueue+Extensions.swift in Sources */,
 				F90692CF274B999A0037068D /* HealthKitDataFlow.swift in Sources */,
 				F90692CF274B999A0037068D /* HealthKitDataFlow.swift in Sources */,
@@ -2403,6 +2430,7 @@
 				383420D925FFEB3F002D46C1 /* Popup.swift in Sources */,
 				383420D925FFEB3F002D46C1 /* Popup.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
+				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
@@ -2469,6 +2497,7 @@
 				CE48C86628CA6B48007C0598 /* OmniPodManagerExtensions.swift in Sources */,
 				CE48C86628CA6B48007C0598 /* OmniPodManagerExtensions.swift in Sources */,
 				CEB434E728B9053300B70274 /* LoopUIColorPalette+Default.swift in Sources */,
 				CEB434E728B9053300B70274 /* LoopUIColorPalette+Default.swift in Sources */,
 				CECA4775298DA8310095139F /* DexcomSourceG5.swift in Sources */,
 				CECA4775298DA8310095139F /* DexcomSourceG5.swift in Sources */,
+				19F95FF329F10FBC00314DDC /* StatDataFlow.swift in Sources */,
 				3811DE2225C9D48300A708ED /* MainProvider.swift in Sources */,
 				3811DE2225C9D48300A708ED /* MainProvider.swift in Sources */,
 				3811DE0C25C9D32F00A708ED /* BaseProvider.swift in Sources */,
 				3811DE0C25C9D32F00A708ED /* BaseProvider.swift in Sources */,
 				3811DE5C25C9D4D500A708ED /* Formatters.swift in Sources */,
 				3811DE5C25C9D4D500A708ED /* Formatters.swift in Sources */,
@@ -2542,6 +2571,7 @@
 				38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */,
 				38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */,
 				38E98A3725F5509500C0CED0 /* String+Extensions.swift in Sources */,
 				38E98A3725F5509500C0CED0 /* String+Extensions.swift in Sources */,
 				F90692D1274B99B60037068D /* HealthKitProvider.swift in Sources */,
 				F90692D1274B99B60037068D /* HealthKitProvider.swift in Sources */,
+				19F95FF729F10FEE00314DDC /* StatStateModel.swift in Sources */,
 				385CEAC125F2EA52002D6D5B /* Announcement.swift in Sources */,
 				385CEAC125F2EA52002D6D5B /* Announcement.swift in Sources */,
 				8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */,
 				8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */,
 				389442CB25F65F7100FA1F27 /* NightscoutTreatment.swift in Sources */,
 				389442CB25F65F7100FA1F27 /* NightscoutTreatment.swift in Sources */,
@@ -2609,6 +2639,7 @@
 				69A31254F2451C20361D172F /* BolusStateModel.swift in Sources */,
 				69A31254F2451C20361D172F /* BolusStateModel.swift in Sources */,
 				0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */,
 				0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */,
 				1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */,
 				1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */,
+				19D4E4EB29FC6A9F00351451 /* TIRforChart.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 */,
@@ -2628,6 +2659,7 @@
 				A05235B9112E677ED03B6E8E /* AutotuneConfigRootView.swift in Sources */,
 				A05235B9112E677ED03B6E8E /* AutotuneConfigRootView.swift in Sources */,
 				7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */,
 				7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */,
 				1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */,
 				1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */,
+				19F95FFA29F1102A00314DDC /* StatRootView.swift in Sources */,
 				0D9A5E34A899219C5C4CDFAF /* DataTableStateModel.swift in Sources */,
 				0D9A5E34A899219C5C4CDFAF /* DataTableStateModel.swift in Sources */,
 				D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */,
 				D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */,
 				38569349270B5DFB0002C50D /* AppGroupSource.swift in Sources */,
 				38569349270B5DFB0002C50D /* AppGroupSource.swift in Sources */,
@@ -2819,7 +2851,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				MTL_FAST_MATH = YES;
@@ -2878,7 +2910,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				MTL_FAST_MATH = YES;
@@ -2909,7 +2941,7 @@
 					"$(PROJECT_DIR)/Dependencies/ios-armv7_arm64",
 					"$(PROJECT_DIR)/Dependencies/ios-armv7_arm64",
 				);
 				);
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",
@@ -2951,7 +2983,7 @@
 					"$(PROJECT_DIR)/Dependencies/ios-armv7_arm64",
 					"$(PROJECT_DIR)/Dependencies/ios-armv7_arm64",
 				);
 				);
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",

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

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


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

@@ -17,7 +17,7 @@
     "useAlarmSound": false,
     "useAlarmSound": false,
     "addSourceInfoToGlucoseNotifications": false,
     "addSourceInfoToGlucoseNotifications": false,
     "lowGlucose": 72,
     "lowGlucose": 72,
-    "highGlucose": 270,
+    "highGlucose": 145,
     "carbsRequiredThreshold": 10,
     "carbsRequiredThreshold": 10,
     "useAppleHealth": false,
     "useAppleHealth": false,
     "animatedBackground": false,
     "animatedBackground": false,

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 561 - 572
FreeAPS/Sources/APS/APSManager.swift


+ 1 - 4
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings

@@ -227,7 +227,7 @@ Enact a temp Basal or a temp target */
 /* */
 /* */
 "Manual Temp Basal" = "Ручная ВБС";
 "Manual Temp Basal" = "Ручная ВБС";
 
 
-/* Allow uploads tp NS */
+/* Allow uploads data to NS */
 "Allow uploads" = "Разрешить выгрузку";
 "Allow uploads" = "Разрешить выгрузку";
 
 
 /* API secret in NS */
 /* API secret in NS */
@@ -236,9 +236,6 @@ Enact a temp Basal or a temp target */
 /* Connect to NS */
 /* Connect to NS */
 "Connect" = "Подключить";
 "Connect" = "Подключить";
 
 
-/* Connected to NS */
-"Connected!" = "Подключено!";
-
 /* Connecting to NS */
 /* Connecting to NS */
 "Connecting..." = "Подключение...";
 "Connecting..." = "Подключение...";
 
 

+ 3 - 2
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -16,12 +16,11 @@ struct FreeAPSSettings: JSON, Equatable {
     var cgm: CGMType = .nightscout
     var cgm: CGMType = .nightscout
     var uploadGlucose: Bool = false
     var uploadGlucose: Bool = false
     var useCalendar: Bool = false
     var useCalendar: Bool = false
-    var useAppleHealth: Bool = false
     var glucoseBadge: Bool = false
     var glucoseBadge: Bool = false
     var glucoseNotificationsAlways: Bool = false
     var glucoseNotificationsAlways: Bool = false
     var useAlarmSound: Bool = false
     var useAlarmSound: Bool = false
     var addSourceInfoToGlucoseNotifications: Bool = false
     var addSourceInfoToGlucoseNotifications: Bool = false
-    var lowGlucose: Decimal = 72
+    var lowGlucose: Decimal = 70
     var highGlucose: Decimal = 270
     var highGlucose: Decimal = 270
     var carbsRequiredThreshold: Decimal = 10
     var carbsRequiredThreshold: Decimal = 10
     var animatedBackground: Bool = false
     var animatedBackground: Bool = false
@@ -31,7 +30,9 @@ struct FreeAPSSettings: JSON, Equatable {
     var timeCap: Int = 8
     var timeCap: Int = 8
     var minuteInterval: Int = 30
     var minuteInterval: Int = 30
     var delay: Int = 60
     var delay: Int = 60
+    var useAppleHealth: Bool = false
     var smoothGlucose: Bool = false
     var smoothGlucose: Bool = false
+    var overrideHbA1cUnit: Bool = false
 }
 }
 
 
 extension FreeAPSSettings: Decodable {
 extension FreeAPSSettings: Decodable {

+ 0 - 6
FreeAPS/Sources/Models/Preferences.swift

@@ -52,11 +52,8 @@ struct Preferences: JSON {
     var enableSMB_high_bg: Bool = false
     var enableSMB_high_bg: Bool = false
     var enableSMB_high_bg_target: Decimal = 110
     var enableSMB_high_bg_target: Decimal = 110
     var threshold_setting: Decimal = 65
     var threshold_setting: Decimal = 65
-    var high: Decimal = 10
-    var low: Decimal = 4
     var updateInterval: Decimal = 20
     var updateInterval: Decimal = 20
     var overrideHbA1cUnit: Bool = false
     var overrideHbA1cUnit: Bool = false
-    var displayLoops: Bool = false
 }
 }
 
 
 extension Preferences {
 extension Preferences {
@@ -111,11 +108,8 @@ extension Preferences {
         case enableSMB_high_bg
         case enableSMB_high_bg
         case enableSMB_high_bg_target
         case enableSMB_high_bg_target
         case threshold_setting
         case threshold_setting
-        case high
-        case low
         case updateInterval
         case updateInterval
         case overrideHbA1cUnit
         case overrideHbA1cUnit
-        case displayLoops
     }
     }
 }
 }
 
 

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

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

+ 4 - 4
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -118,7 +118,7 @@ extension AddCarbs {
                             preset.protein = state.protein as NSDecimalNumber
                             preset.protein = state.protein as NSDecimalNumber
                             preset.carbs = state.carbs as NSDecimalNumber
                             preset.carbs = state.carbs as NSDecimalNumber
                             try? moc.save()
                             try? moc.save()
-                            state.selection = preset
+                            state.addNewPresetToWaitersNotepad(dish)
                             saved = false
                             saved = false
                             isPromtPresented = false
                             isPromtPresented = false
                         }
                         }
@@ -175,19 +175,19 @@ extension AddCarbs {
                     )
                     )
                     Button {
                     Button {
                         if state.carbs != 0,
                         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) as Decimal) >= 0
                         {
                         {
                             state.carbs -= (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal)
                             state.carbs -= (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal)
                         } else { state.carbs = 0 }
                         } else { state.carbs = 0 }
 
 
                         if state.fat != 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) as Decimal) >= 0
                         {
                         {
                             state.fat -= (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal)
                             state.fat -= (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal)
                         } else { state.fat = 0 }
                         } else { state.fat = 0 }
 
 
                         if state.protein != 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) as Decimal) >= 0
                         {
                         {
                             state.protein -= (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal)
                             state.protein -= (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal)
                         } else { state.protein = 0 }
                         } else { state.protein = 0 }

+ 2 - 4
FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift

@@ -37,7 +37,7 @@ extension AddTempTarget {
             var lowTarget = low
             var lowTarget = low
 
 
             if viewPercantage {
             if viewPercantage {
-                lowTarget = computeTarget()
+                lowTarget = Decimal(round(Double(computeTarget())))
                 coredataContext.performAndWait {
                 coredataContext.performAndWait {
                     let saveToCoreData = TempTargets(context: self.coredataContext)
                     let saveToCoreData = TempTargets(context: self.coredataContext)
                     saveToCoreData.id = UUID().uuidString
                     saveToCoreData.id = UUID().uuidString
@@ -90,7 +90,6 @@ extension AddTempTarget {
                 let setHBT = TempTargetsSlider(context: self.coredataContext)
                 let setHBT = TempTargetsSlider(context: self.coredataContext)
                 setHBT.enabled = false
                 setHBT.enabled = false
                 setHBT.date = Date()
                 setHBT.date = Date()
-
                 try? self.coredataContext.save()
                 try? self.coredataContext.save()
             }
             }
         }
         }
@@ -102,10 +101,9 @@ extension AddTempTarget {
             var lowTarget = low
             var lowTarget = low
 
 
             if viewPercantage {
             if viewPercantage {
-                lowTarget = computeTarget()
+                lowTarget = Decimal(round(Double(computeTarget())))
                 saveSettings = true
                 saveSettings = true
             }
             }
-
             var highTarget = lowTarget
             var highTarget = lowTarget
 
 
             if units == .mmolL, !viewPercantage {
             if units == .mmolL, !viewPercantage {

+ 28 - 26
FreeAPS/Sources/Modules/ConfigEditor/View/ConfigEditorRootView.swift

@@ -9,35 +9,37 @@ extension ConfigEditor {
         @State private var showShareSheet = false
         @State private var showShareSheet = false
 
 
         var body: some View {
         var body: some View {
-            TextEditor(text: $state.configText)
-                .keyboardType(.asciiCapable)
-                .font(.system(.subheadline, design: .monospaced))
-                .allowsTightening(true)
-                .autocapitalization(.none)
-                .disableAutocorrection(true)
-                .toolbar {
-                    ToolbarItemGroup(placement: .bottomBar) {
-                        Spacer()
-                        Button { showShareSheet = true }
-                        label: {
-                            Image(systemName: "square.and.arrow.up")
+            ZStack {
+                TextEditor(text: $state.configText)
+                    .keyboardType(.asciiCapable)
+                    .font(.system(.subheadline, design: .monospaced))
+                    .allowsTightening(true)
+                    .autocapitalization(.none)
+                    .disableAutocorrection(true)
+                    .toolbar {
+                        ToolbarItemGroup(placement: .bottomBar) {
+                            Spacer()
+                            Button { showShareSheet = true }
+                            label: {
+                                Image(systemName: "square.and.arrow.up")
+                            }
                         }
                         }
                     }
                     }
-                }
-                .navigationBarItems(
-                    trailing: Button("Save", action: state.save)
-                )
-                .sheet(isPresented: $showShareSheet) {
-                    ShareSheet(activityItems: [state.provider.urlFor(file: state.file)!])
-                }
-                .onAppear {
-                    configureView {
-                        state.file = file
+                    .navigationBarItems(
+                        trailing: Button("Save", action: state.save)
+                    )
+                    .sheet(isPresented: $showShareSheet) {
+                        ShareSheet(activityItems: [state.provider.urlFor(file: state.file)!])
                     }
                     }
-                }
-                .navigationTitle(file)
-                .navigationBarTitleDisplayMode(.inline)
-                .padding()
+                    .onAppear {
+                        configureView {
+                            state.file = file
+                        }
+                    }
+                    .navigationTitle(file)
+                    .navigationBarTitleDisplayMode(.inline)
+                    .padding()
+            }
         }
         }
     }
     }
 }
 }

+ 0 - 41
FreeAPS/Sources/Modules/Home/DurationButton.swift

@@ -1,41 +0,0 @@
-import SwiftUI
-
-protocol DurationButton: CaseIterable {
-    var title: String { get }
-}
-
-extension DurationButton where Self: RawRepresentable, RawValue == String {
-    var title: String {
-        rawValue
-    }
-}
-
-enum durationState: String, DurationButton {
-    case day = "Past 24 Hours "
-    case week = "Past Week "
-    case month = "Past Month "
-    case total = "All Past Days of Data "
-}
-
-struct durationButton<T: DurationButton>: View {
-    let states: [T]
-    @State var currentIndex = 0
-    @Binding var selectedState: T
-
-    var body: some View {
-        Button {
-            currentIndex = currentIndex < states.count - 1 ? currentIndex + 1 : 0
-            selectedState = states[currentIndex]
-        } label: {
-            Text(NSLocalizedString(states[currentIndex].title, comment: "Duration displayed in statPanel"))
-                .font(.caption2)
-                .foregroundColor(.secondary)
-        }
-
-        .buttonBorderShape(.automatic)
-        .controlSize(.mini)
-        .buttonStyle(.bordered)
-        // .padding([.trailing], 15)
-        // .frame(maxWidth: .infinity, alignment: .trailing)
-    }
-}

+ 0 - 1
FreeAPS/Sources/Modules/Home/HomeDataFlow.swift

@@ -7,7 +7,6 @@ enum Home {
 
 
 protocol HomeProvider: Provider {
 protocol HomeProvider: Provider {
     var suggestion: Suggestion? { get }
     var suggestion: Suggestion? { get }
-    var statistics: Statistics? { get }
     var enactedSuggestion: Suggestion? { get }
     var enactedSuggestion: Suggestion? { get }
     func heartbeatNow()
     func heartbeatNow()
     func filteredGlucose(hours: Int) -> [BloodGlucose]
     func filteredGlucose(hours: Int) -> [BloodGlucose]

+ 0 - 8
FreeAPS/Sources/Modules/Home/HomeProvider.swift

@@ -14,14 +14,6 @@ extension Home {
             storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
             storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
         }
         }
 
 
-        var statistics: Statistics? {
-            let stat = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self)
-            if stat?.count ?? 0 != 0 {
-                return stat![0]
-            }
-            return nil
-        }
-
         var enactedSuggestion: Suggestion? {
         var enactedSuggestion: Suggestion? {
             storage.retrieve(OpenAPS.Enact.enacted, as: Suggestion.self)
             storage.retrieve(OpenAPS.Enact.enacted, as: Suggestion.self)
         }
         }

+ 9 - 22
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -13,7 +13,6 @@ extension Home {
 
 
         @Published var glucose: [BloodGlucose] = []
         @Published var glucose: [BloodGlucose] = []
         @Published var suggestion: Suggestion?
         @Published var suggestion: Suggestion?
-        @Published var statistics: Statistics?
         @Published var displayStatistics = false
         @Published var displayStatistics = false
         @Published var enactedSuggestion: Suggestion?
         @Published var enactedSuggestion: Suggestion?
         @Published var recentGlucose: BloodGlucose?
         @Published var recentGlucose: BloodGlucose?
@@ -46,15 +45,15 @@ extension Home {
         @Published var carbsRequired: Decimal?
         @Published var carbsRequired: Decimal?
         @Published var allowManualTemp = false
         @Published var allowManualTemp = false
         @Published var units: GlucoseUnits = .mmolL
         @Published var units: GlucoseUnits = .mmolL
-        @Published var low: Decimal = 4
-        @Published var high: Decimal = 10
-        @Published var displayLoops = false
         @Published var pumpDisplayState: PumpDisplayState?
         @Published var pumpDisplayState: PumpDisplayState?
         @Published var alarm: GlucoseAlarm?
         @Published var alarm: GlucoseAlarm?
         @Published var animatedBackground = false
         @Published var animatedBackground = false
         @Published var manualTempBasal = false
         @Published var manualTempBasal = false
         @Published var smooth = false
         @Published var smooth = false
         @Published var maxValue: Decimal = 1.2
         @Published var maxValue: Decimal = 1.2
+        @Published var lowGlucoseLine: Decimal = 70
+        @Published var highGlucoseLine: Decimal = 145
+        @Published var overrideUnit = false
 
 
         override func subscribe() {
         override func subscribe() {
             setupGlucose()
             setupGlucose()
@@ -67,14 +66,9 @@ extension Home {
             setupCarbs()
             setupCarbs()
             setupBattery()
             setupBattery()
             setupReservoir()
             setupReservoir()
-            setupStatistics()
 
 
             suggestion = provider.suggestion
             suggestion = provider.suggestion
-            statistics = provider.statistics
             displayStatistics = settingsManager.settings.displayStatistics
             displayStatistics = settingsManager.settings.displayStatistics
-            low = settingsManager.preferences.low
-            high = settingsManager.preferences.high
-            displayLoops = settingsManager.preferences.displayLoops
             enactedSuggestion = provider.enactedSuggestion
             enactedSuggestion = provider.enactedSuggestion
             units = settingsManager.settings.units
             units = settingsManager.settings.units
             allowManualTemp = !settingsManager.settings.closedLoop
             allowManualTemp = !settingsManager.settings.closedLoop
@@ -87,6 +81,9 @@ extension Home {
             setupCurrentTempTarget()
             setupCurrentTempTarget()
             smooth = settingsManager.settings.smoothGlucose
             smooth = settingsManager.settings.smoothGlucose
             maxValue = settingsManager.preferences.autosensMax
             maxValue = settingsManager.preferences.autosensMax
+            lowGlucoseLine = settingsManager.settings.lowGlucose
+            highGlucoseLine = settingsManager.settings.highGlucose
+            overrideUnit = settingsManager.preferences.overrideHbA1cUnit
 
 
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(SuggestionObserver.self, observer: self)
             broadcaster.register(SuggestionObserver.self, observer: self)
@@ -321,13 +318,6 @@ extension Home {
             }
             }
         }
         }
 
 
-        private func setupStatistics() {
-            DispatchQueue.main.async { [weak self] in
-                guard let self = self else { return }
-                self.statistics = self.provider.statistics
-            }
-        }
-
         private func setupBattery() {
         private func setupBattery() {
             DispatchQueue.main.async { [weak self] in
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
                 guard let self = self else { return }
@@ -380,7 +370,6 @@ extension Home.StateModel:
 {
 {
     func glucoseDidUpdate(_: [BloodGlucose]) {
     func glucoseDidUpdate(_: [BloodGlucose]) {
         setupGlucose()
         setupGlucose()
-        setupStatistics()
     }
     }
 
 
     func suggestionDidUpdate(_ suggestion: Suggestion) {
     func suggestionDidUpdate(_ suggestion: Suggestion) {
@@ -393,15 +382,14 @@ extension Home.StateModel:
         allowManualTemp = !settings.closedLoop
         allowManualTemp = !settings.closedLoop
         displayStatistics = settingsManager.settings.displayStatistics
         displayStatistics = settingsManager.settings.displayStatistics
         closedLoop = settingsManager.settings.closedLoop
         closedLoop = settingsManager.settings.closedLoop
-        low = settingsManager.preferences.low
-        high = settingsManager.preferences.high
-        displayLoops = settingsManager.preferences.displayLoops
         units = settingsManager.settings.units
         units = settingsManager.settings.units
         animatedBackground = settingsManager.settings.animatedBackground
         animatedBackground = settingsManager.settings.animatedBackground
         manualTempBasal = apsManager.isManualTempBasal
         manualTempBasal = apsManager.isManualTempBasal
         smooth = settingsManager.settings.smoothGlucose
         smooth = settingsManager.settings.smoothGlucose
+        lowGlucoseLine = settingsManager.settings.lowGlucose
+        highGlucoseLine = settingsManager.settings.highGlucose
+        overrideUnit = settingsManager.preferences.overrideHbA1cUnit
         setupGlucose()
         setupGlucose()
-        setupStatistics()
     }
     }
 
 
     func pumpHistoryDidUpdate(_: [PumpHistoryEvent]) {
     func pumpHistoryDidUpdate(_: [PumpHistoryEvent]) {
@@ -429,7 +417,6 @@ extension Home.StateModel:
     func enactedSuggestionDidUpdate(_ suggestion: Suggestion) {
     func enactedSuggestionDidUpdate(_ suggestion: Suggestion) {
         enactedSuggestion = suggestion
         enactedSuggestion = suggestion
         setStatusTitle()
         setStatusTitle()
-        setupStatistics()
     }
     }
 
 
     func pumpBatteryDidChange(_: Battery) {
     func pumpBatteryDidChange(_: Battery) {

+ 20 - 21
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -19,26 +19,23 @@ typealias GlucoseYRange = (minValue: Int, minY: CGFloat, maxValue: Int, maxY: CG
 struct MainChartView: View {
 struct MainChartView: View {
     private enum Config {
     private enum Config {
         static let endID = "End"
         static let endID = "End"
-        static let screenHours = 6
+        static let screenHours = 8
         static let basalHeight: CGFloat = 80
         static let basalHeight: CGFloat = 80
         static let topYPadding: CGFloat = 20
         static let topYPadding: CGFloat = 20
-        static let bottomYPadding: CGFloat = 50
+        static let bottomYPadding: CGFloat = 80
         static let minAdditionalWidth: CGFloat = 150
         static let minAdditionalWidth: CGFloat = 150
-        static let maxGlucose = 250
-        static let minGlucose = 50
+        static let maxGlucose = 270
+        static let minGlucose = 45
         static let yLinesCount = 5
         static let yLinesCount = 5
         static let glucoseScale: CGFloat = 2 // default 2
         static let glucoseScale: CGFloat = 2 // default 2
         static let bolusSize: CGFloat = 8
         static let bolusSize: CGFloat = 8
         static let bolusScale: CGFloat = 2.5
         static let bolusScale: CGFloat = 2.5
         static let carbsSize: CGFloat = 10
         static let carbsSize: CGFloat = 10
         static let carbsScale: CGFloat = 0.3
         static let carbsScale: CGFloat = 0.3
-        static let upperTarget: CGFloat = 180
-        static let lowerTarget: CGFloat = 70
     }
     }
 
 
     @Binding var glucose: [BloodGlucose]
     @Binding var glucose: [BloodGlucose]
     @Binding var suggestion: Suggestion?
     @Binding var suggestion: Suggestion?
-    @Binding var statistcs: Statistics?
     @Binding var tempBasals: [PumpHistoryEvent]
     @Binding var tempBasals: [PumpHistoryEvent]
     @Binding var boluses: [PumpHistoryEvent]
     @Binding var boluses: [PumpHistoryEvent]
     @Binding var suspensions: [PumpHistoryEvent]
     @Binding var suspensions: [PumpHistoryEvent]
@@ -51,6 +48,8 @@ struct MainChartView: View {
     @Binding var timerDate: Date
     @Binding var timerDate: Date
     @Binding var units: GlucoseUnits
     @Binding var units: GlucoseUnits
     @Binding var smooth: Bool
     @Binding var smooth: Bool
+    @Binding var highGlucoseLine: Decimal
+    @Binding var lowGlucoseLine: Decimal
 
 
     @State var didAppearTrigger = false
     @State var didAppearTrigger = false
     @State private var glucoseDots: [CGRect] = []
     @State private var glucoseDots: [CGRect] = []
@@ -66,7 +65,7 @@ struct MainChartView: View {
     @State private var carbsPath = Path()
     @State private var carbsPath = Path()
     @State private var fpuDots: [DotInfo] = []
     @State private var fpuDots: [DotInfo] = []
     @State private var fpuPath = Path()
     @State private var fpuPath = Path()
-    @State private var glucoseYGange: GlucoseYRange = (0, 0, 0, 0)
+    @State private var glucoseYRange: GlucoseYRange = (0, 0, 0, 0)
     @State private var offset: CGFloat = 0
     @State private var offset: CGFloat = 0
     @State private var cachedMaxBasalRate: Decimal?
     @State private var cachedMaxBasalRate: Decimal?
 
 
@@ -170,27 +169,27 @@ struct MainChartView: View {
     private func yGridView(fullSize: CGSize) -> some View {
     private func yGridView(fullSize: CGSize) -> some View {
         ZStack {
         ZStack {
             Path { path in
             Path { path in
-                let range = glucoseYGange
+                let range = glucoseYRange
                 let step = (range.maxY - range.minY) / CGFloat(Config.yLinesCount)
                 let step = (range.maxY - range.minY) / CGFloat(Config.yLinesCount)
                 for line in 0 ... Config.yLinesCount {
                 for line in 0 ... Config.yLinesCount {
                     path.move(to: CGPoint(x: 0, y: range.minY + CGFloat(line) * step))
                     path.move(to: CGPoint(x: 0, y: range.minY + CGFloat(line) * step))
                     path.addLine(to: CGPoint(x: fullSize.width, y: range.minY + CGFloat(line) * step))
                     path.addLine(to: CGPoint(x: fullSize.width, y: range.minY + CGFloat(line) * step))
                 }
                 }
-            }.stroke(Color.secondary, lineWidth: 0.2)
+            }.stroke(Color.secondary, lineWidth: 0.15)
             // horizontal limits
             // horizontal limits
-            let range = glucoseYGange
+            let range = glucoseYRange
             let topstep = (range.maxY - range.minY) / CGFloat(range.maxValue - range.minValue) *
             let topstep = (range.maxY - range.minY) / CGFloat(range.maxValue - range.minValue) *
-                (CGFloat(range.maxValue) - Config.upperTarget)
-            if CGFloat(range.maxValue) > Config.upperTarget {
+                (CGFloat(range.maxValue) - CGFloat(highGlucoseLine))
+            if CGFloat(range.maxValue) > CGFloat(highGlucoseLine) {
                 Path { path in
                 Path { path in
                     path.move(to: CGPoint(x: 0, y: range.minY + topstep))
                     path.move(to: CGPoint(x: 0, y: range.minY + topstep))
                     path.addLine(to: CGPoint(x: fullSize.width, y: range.minY + topstep))
                     path.addLine(to: CGPoint(x: fullSize.width, y: range.minY + topstep))
-                }.stroke(Color.loopYellow, lineWidth: 0.5)
+                }.stroke(Color.loopYellow, lineWidth: 0.5) // .StrokeStyle(lineWidth: 0.5, dash: [5])
             }
             }
-            let yrange = glucoseYGange
+            let yrange = glucoseYRange
             let bottomstep = (yrange.maxY - yrange.minY) / CGFloat(yrange.maxValue - yrange.minValue) *
             let bottomstep = (yrange.maxY - yrange.minY) / CGFloat(yrange.maxValue - yrange.minValue) *
-                (CGFloat(yrange.maxValue) - Config.lowerTarget)
-            if CGFloat(yrange.minValue) < Config.lowerTarget {
+                (CGFloat(yrange.maxValue) - CGFloat(lowGlucoseLine))
+            if CGFloat(yrange.minValue) < CGFloat(lowGlucoseLine) {
                 Path { path in
                 Path { path in
                     path.move(to: CGPoint(x: 0, y: yrange.minY + bottomstep))
                     path.move(to: CGPoint(x: 0, y: yrange.minY + bottomstep))
                     path.addLine(to: CGPoint(x: fullSize.width, y: yrange.minY + bottomstep))
                     path.addLine(to: CGPoint(x: fullSize.width, y: yrange.minY + bottomstep))
@@ -201,7 +200,7 @@ struct MainChartView: View {
 
 
     private func glucoseLabelsView(fullSize: CGSize) -> some View {
     private func glucoseLabelsView(fullSize: CGSize) -> some View {
         ForEach(0 ..< Config.yLinesCount + 1, id: \.self) { line -> AnyView in
         ForEach(0 ..< Config.yLinesCount + 1, id: \.self) { line -> AnyView in
-            let range = glucoseYGange
+            let range = glucoseYRange
             let yStep = (range.maxY - range.minY) / CGFloat(Config.yLinesCount)
             let yStep = (range.maxY - range.minY) / CGFloat(Config.yLinesCount)
             let valueStep = Double(range.maxValue - range.minValue) / Double(Config.yLinesCount)
             let valueStep = Double(range.maxValue - range.minValue) / Double(Config.yLinesCount)
             let value = round(Double(range.maxValue) - Double(line) * valueStep) *
             let value = round(Double(range.maxValue) - Double(line) * valueStep) *
@@ -274,7 +273,7 @@ struct MainChartView: View {
                     path.addLine(to: CGPoint(x: x, y: fullSize.height - 20))
                     path.addLine(to: CGPoint(x: x, y: fullSize.height - 20))
                 }
                 }
             }
             }
-            .stroke(Color.secondary, lineWidth: 0.2)
+            .stroke(Color.clear, lineWidth: 0.2)
 
 
             Path { path in // vertical timeline
             Path { path in // vertical timeline
                 let x = timeToXCoordinate(timerDate.timeIntervalSince1970, fullSize: fullSize)
                 let x = timeToXCoordinate(timerDate.timeIntervalSince1970, fullSize: fullSize)
@@ -488,7 +487,7 @@ extension MainChartView {
             let range = self.getGlucoseYRange(fullSize: fullSize)
             let range = self.getGlucoseYRange(fullSize: fullSize)
 
 
             DispatchQueue.main.async {
             DispatchQueue.main.async {
-                glucoseYGange = range
+                glucoseYRange = range
                 glucoseDots = dots
                 glucoseDots = dots
             }
             }
         }
         }
@@ -504,7 +503,7 @@ extension MainChartView {
             let range = self.getGlucoseYRange(fullSize: fullSize)
             let range = self.getGlucoseYRange(fullSize: fullSize)
 
 
             DispatchQueue.main.async {
             DispatchQueue.main.async {
-                glucoseYGange = range
+                glucoseYRange = range
                 unSmoothedGlucoseDots = dots
                 unSmoothedGlucoseDots = dots
             }
             }
         }
         }

+ 6 - 5
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -5,6 +5,8 @@ struct CurrentGlucoseView: View {
     @Binding var delta: Int?
     @Binding var delta: Int?
     @Binding var units: GlucoseUnits
     @Binding var units: GlucoseUnits
     @Binding var alarm: GlucoseAlarm?
     @Binding var alarm: GlucoseAlarm?
+    @Binding var lowGlucoseLine: Decimal
+    @Binding var highGlucoseLine: Decimal
 
 
     private var glucoseFormatter: NumberFormatter {
     private var glucoseFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         let formatter = NumberFormatter()
@@ -110,13 +112,12 @@ struct CurrentGlucoseView: View {
         let whichGlucose = recentGlucose?.glucose ?? 0
         let whichGlucose = recentGlucose?.glucose ?? 0
 
 
         switch whichGlucose {
         switch whichGlucose {
-        case 71 ... 145:
+        case Int(lowGlucoseLine ?? 70) + 1 ... Int(highGlucoseLine ?? 145) - 1:
             return .loopGreen
             return .loopGreen
-        case 1 ... 55,
-             217...:
+        case 0 ... Int(lowGlucoseLine ?? 70),
+             201...:
             return .loopRed
             return .loopRed
-        case 56 ... 70,
-             146 ... 216:
+        case Int(highGlucoseLine ?? 145) ... 200:
             return .loopYellow
             return .loopYellow
         default:
         default:
             return .primary
             return .primary

+ 16 - 264
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -10,7 +10,6 @@ extension Home {
 
 
         @StateObject var state = StateModel()
         @StateObject var state = StateModel()
         @State var isStatusPopupPresented = false
         @State var isStatusPopupPresented = false
-        @State var selectedState: durationState
 
 
         // Average/Median/Readings and CV/SD titles and values switches when you tap them
         // Average/Median/Readings and CV/SD titles and values switches when you tap them
         @State var averageOrMedianTitle = NSLocalizedString("Average", comment: "")
         @State var averageOrMedianTitle = NSLocalizedString("Average", comment: "")
@@ -119,7 +118,9 @@ extension Home {
                 recentGlucose: $state.recentGlucose,
                 recentGlucose: $state.recentGlucose,
                 delta: $state.glucoseDelta,
                 delta: $state.glucoseDelta,
                 units: $state.units,
                 units: $state.units,
-                alarm: $state.alarm
+                alarm: $state.alarm,
+                lowGlucoseLine: $state.lowGlucoseLine,
+                highGlucoseLine: $state.highGlucoseLine
             )
             )
             .onTapGesture {
             .onTapGesture {
                 if state.alarm == nil {
                 if state.alarm == nil {
@@ -218,7 +219,6 @@ extension Home {
             let percentString = "\((fetchedPercent.first?.percentage ?? 100).formatted(.number)) %"
             let percentString = "\((fetchedPercent.first?.percentage ?? 100).formatted(.number)) %"
             let durationString = (fetchedPercent.first?.indefinite ?? false) ?
             let durationString = (fetchedPercent.first?.indefinite ?? false) ?
                 "" : ", " + (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min"
                 "" : ", " + (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min"
-
             return percentString + durationString
             return percentString + durationString
         }
         }
 
 
@@ -264,264 +264,6 @@ extension Home {
             .frame(maxWidth: .infinity, maxHeight: 30)
             .frame(maxWidth: .infinity, maxHeight: 30)
         }
         }
 
 
-        @ViewBuilder private func statPanel() -> some View {
-            if state.displayStatistics {
-                VStack(spacing: 8) {
-                    durationButton(states: durationState.allCases, selectedState: $selectedState)
-
-                    switch selectedState {
-                    case .day:
-
-                        let hba1c_all = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
-                        let average_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Average.day ?? 0) as NSNumber) ?? ""
-                        let median_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Median.day ?? 0) as NSNumber) ?? ""
-                        let tir_low = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.day ?? 0) as NSNumber) ?? ""
-                        let tir_high = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.day ?? 0) as NSNumber) ?? ""
-                        let tir_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.TIR.day ?? 0) as NSNumber) ?? ""
-                        let hba1c_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.day ?? 0) as NSNumber) ?? ""
-                        let sd_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.SD.day ?? 0) as NSNumber) ?? ""
-                        let cv_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.CV.day ?? 0) as NSNumber) ?? ""
-
-                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
-
-                    case .week:
-                        let hba1c_all = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
-                        let average_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Average.week ?? 0) as NSNumber) ?? ""
-                        let median_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Median.week ?? 0) as NSNumber) ?? ""
-                        let tir_low = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.week ?? 0) as NSNumber) ?? ""
-                        let tir_high = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.week ?? 0) as NSNumber) ?? ""
-                        let tir_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.TIR.week ?? 0) as NSNumber) ?? ""
-                        let hba1c_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.week ?? 0) as NSNumber) ?? ""
-                        let sd_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.SD.week ?? 0) as NSNumber) ?? ""
-                        let cv_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.CV.week ?? 0) as NSNumber) ?? ""
-
-                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
-
-                    case .month:
-                        let hba1c_all = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
-                        let average_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Average.month ?? 0) as NSNumber) ?? ""
-                        let median_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Median.month ?? 0) as NSNumber) ?? ""
-                        let tir_low = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.month ?? 0) as NSNumber) ?? ""
-                        let tir_high = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.month ?? 0) as NSNumber) ?? ""
-                        let tir_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.TIR.month ?? 0) as NSNumber) ?? ""
-                        let hba1c_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.month ?? 0) as NSNumber) ?? ""
-                        let sd_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.SD.month ?? 0) as NSNumber) ?? ""
-                        let cv_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.CV.month ?? 0) as NSNumber) ?? ""
-
-                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
-
-                    case .total:
-                        let hba1c_all = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
-                        let average_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Average.total ?? 0) as NSNumber) ?? ""
-                        let median_ = targetFormatter
-                            .string(from: (state.statistics?.Statistics.Glucose.Median.total ?? 0) as NSNumber) ?? ""
-                        let tir_low = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypos.total ?? 0) as NSNumber) ?? ""
-                        let tir_high = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.Hypers.total ?? 0) as NSNumber) ??
-                            ""
-                        let tir_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Distribution.TIR.total ?? 0) as NSNumber) ?? ""
-                        let hba1c_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.HbA1c.total ?? 0) as NSNumber) ?? ""
-                        let sd_ = numberFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.SD.total ?? 0) as NSNumber) ?? ""
-                        let cv_ = tirFormatter
-                            .string(from: (state.statistics?.Statistics.Variance.CV.total ?? 0) as NSNumber) ?? ""
-
-                        averageTIRhca1c(hba1c_all, average_, median_, tir_low, tir_high, tir_, hba1c_, sd_, cv_)
-                    }
-                }
-                .frame(maxWidth: .infinity)
-                .padding([.bottom], 20)
-            }
-        }
-
-        @ViewBuilder private func averageTIRhca1c(
-            _ hba1c_all: String,
-            _ average_: String,
-            _ median_: String,
-            _ tir_low: String,
-            _ tir_high: String,
-            _ tir_: String,
-            _ hba1c_: String,
-            _ sd_: String,
-            _ cv_: String
-        ) -> some View {
-            HStack {
-                Group {
-                    if selectedState != .total {
-                        HStack {
-                            Text("HbA1c").font(.footnote).foregroundColor(.secondary)
-                            Text(hba1c_).font(.footnote)
-                        }
-                    } else {
-                        HStack {
-                            Text(
-                                "\(NSLocalizedString("HbA1c", comment: "")) (\(targetFormatter.string(from: (state.statistics?.GlucoseStorage_Days ?? 0) as NSNumber) ?? "") \(NSLocalizedString("days", comment: "")))"
-                            )
-                            .font(.footnote).foregroundColor(.secondary)
-                            Text(hba1c_all).font(.footnote)
-                        }
-                    }
-                    // Average as default. Changes to Median when clicking.
-                    let textAverageTitle = NSLocalizedString("Average", comment: "")
-                    let textMedianTitle = NSLocalizedString("Median", comment: "")
-                    let cgmReadingsTitle = NSLocalizedString("Readings", comment: "CGM readings in statPanel")
-
-                    HStack {
-                        Text(averageOrMedianTitle).font(.footnote).foregroundColor(.secondary)
-                        if averageOrMedianTitle == textAverageTitle {
-                            Text(averageOrmedian == "" ? average_ : average_).font(.footnote)
-                        } else if averageOrMedianTitle == textMedianTitle {
-                            Text(averageOrmedian == "" ? median_ : median_).font(.footnote)
-                        } else if averageOrMedianTitle == cgmReadingsTitle {
-                            Text(
-                                averageOrmedian != "0" ? tirFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.readings ?? 0) as NSNumber) ?? "" : ""
-                            )
-                            .font(.footnote)
-                        }
-                    }.onTapGesture {
-                        if averageOrMedianTitle == textAverageTitle {
-                            averageOrMedianTitle = textMedianTitle
-                            averageOrmedian = median_
-                        } else if averageOrMedianTitle == textMedianTitle {
-                            averageOrMedianTitle = cgmReadingsTitle
-                            averageOrmedian = tirFormatter
-                                .string(from: (state.statistics?.Statistics.LoopCycles.readings ?? 0) as NSNumber) ?? ""
-                        } else if averageOrMedianTitle == cgmReadingsTitle {
-                            averageOrMedianTitle = textAverageTitle
-                            averageOrmedian = average_
-                        }
-                    }
-                    .frame(minWidth: 110)
-                    // CV as default. Changes to SD when clicking
-                    let text_CV_Title = NSLocalizedString("CV", comment: "")
-                    let text_SD_Title = NSLocalizedString("SD", comment: "")
-
-                    HStack {
-                        Text(CV_or_SD_Title).font(.footnote).foregroundColor(.secondary)
-                        if CV_or_SD_Title == text_CV_Title {
-                            Text(CVorSD == "" ? cv_ : cv_).font(.footnote)
-                        } else {
-                            Text(CVorSD == "" ? sd_ : sd_).font(.footnote)
-                        }
-                    }.onTapGesture {
-                        if CV_or_SD_Title == text_CV_Title {
-                            CV_or_SD_Title = text_SD_Title
-                            CVorSD = sd_
-                        } else {
-                            CV_or_SD_Title = text_CV_Title
-                            CVorSD = cv_
-                        }
-                    }
-                }
-            }
-            HStack {
-                Group {
-                    HStack {
-                        Text(
-                            NSLocalizedString("Low", comment: " ")
-                        )
-                        .font(.footnote)
-                        .foregroundColor(.secondary)
-
-                        Text(tir_low + " %").font(.footnote).foregroundColor(.loopRed)
-                    }
-
-                    HStack {
-                        Text("Normal").font(.footnote).foregroundColor(.secondary)
-                        Text(tir_ + " %").font(.footnote).foregroundColor(.loopGreen)
-                    }
-
-                    HStack {
-                        Text(
-                            NSLocalizedString("High", comment: " ")
-                        )
-                        .font(.footnote).foregroundColor(.secondary)
-
-                        Text(tir_high + " %").font(.footnote).foregroundColor(.loopYellow)
-                    }
-                }
-            }
-
-            if state.settingsManager.preferences.displayLoops {
-                HStack {
-                    Group {
-                        let loopTitle = NSLocalizedString("Loops", comment: "Nr of Loops in statPanel")
-                        let errorTitle = NSLocalizedString("Errors", comment: "Loop Errors in statPanel")
-
-                        HStack {
-                            Text(loopStatTitle).font(.footnote).foregroundColor(.secondary)
-                            Text(
-                                loopStatTitle == loopTitle ? tirFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.loops ?? 0) as NSNumber) ?? "" :
-                                    tirFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.errors ?? 0) as NSNumber) ?? ""
-                            ).font(.footnote)
-                        }.onTapGesture {
-                            if loopStatTitle == loopTitle {
-                                loopStatTitle = errorTitle
-                            } else if loopStatTitle == errorTitle {
-                                loopStatTitle = loopTitle
-                            }
-                        }
-
-                        HStack {
-                            Text("Interval").font(.footnote)
-                                .foregroundColor(.secondary)
-                            Text(
-                                targetFormatter
-                                    .string(from: (state.statistics?.Statistics.LoopCycles.avg_interval ?? 0) as NSNumber) ??
-                                    ""
-                            ).font(.footnote)
-                        }
-
-                        HStack {
-                            Text("Duration").font(.footnote)
-                                .foregroundColor(.secondary)
-                            Text(
-                                numberFormatter
-                                    .string(
-                                        from: (state.statistics?.Statistics.LoopCycles.median_duration ?? 0) as NSNumber
-                                    ) ?? ""
-                            ).font(.footnote)
-                        }
-                    }
-                }
-            }
-        }
-
         var legendPanel: some View {
         var legendPanel: some View {
             ZStack {
             ZStack {
                 HStack(alignment: .center) {
                 HStack(alignment: .center) {
@@ -580,7 +322,6 @@ extension Home {
                 MainChartView(
                 MainChartView(
                     glucose: $state.glucose,
                     glucose: $state.glucose,
                     suggestion: $state.suggestion,
                     suggestion: $state.suggestion,
-                    statistcs: $state.statistics,
                     tempBasals: $state.tempBasals,
                     tempBasals: $state.tempBasals,
                     boluses: $state.boluses,
                     boluses: $state.boluses,
                     suspensions: $state.suspensions,
                     suspensions: $state.suspensions,
@@ -592,7 +333,9 @@ extension Home {
                     carbs: $state.carbs,
                     carbs: $state.carbs,
                     timerDate: $state.timerDate,
                     timerDate: $state.timerDate,
                     units: $state.units,
                     units: $state.units,
-                    smooth: $state.smooth
+                    smooth: $state.smooth,
+                    highGlucoseLine: $state.highGlucoseLine,
+                    lowGlucoseLine: $state.lowGlucoseLine
                 )
                 )
             }
             }
             .padding(.bottom)
             .padding(.bottom)
@@ -652,6 +395,16 @@ extension Home {
                         }.foregroundColor(.insulin)
                         }.foregroundColor(.insulin)
                         Spacer()
                         Spacer()
                     }
                     }
+                    Button { state.showModal(for: .statistics)
+                    }
+                    label: {
+                        Image(systemName: "chart.xyaxis.line")
+                            .renderingMode(.template)
+                            .resizable()
+                            .frame(width: 24, height: 24)
+                            .padding(8)
+                    }.foregroundColor(.purple)
+                    Spacer()
                     Button { state.showModal(for: .settings) }
                     Button { state.showModal(for: .settings) }
                     label: {
                     label: {
                         Image("settings1")
                         Image("settings1")
@@ -673,7 +426,6 @@ extension Home {
                     infoPanel
                     infoPanel
                     mainChart
                     mainChart
                     legendPanel
                     legendPanel
-                    statPanel()
                     bottomPanel(geo)
                     bottomPanel(geo)
                 }
                 }
                 .edgesIgnoringSafeArea(.vertical)
                 .edgesIgnoringSafeArea(.vertical)

+ 1 - 0
FreeAPS/Sources/Modules/Main/MainStateModel.swift

@@ -107,6 +107,7 @@ extension Main {
     }
     }
 }
 }
 
 
+@available(iOS 16.0, *)
 extension Main.StateModel: CompletionDelegate {
 extension Main.StateModel: CompletionDelegate {
     func completionNotifyingDidComplete(_: CompletionNotifying) {
     func completionNotifyingDidComplete(_: CompletionNotifying) {
         // close the window
         // close the window

+ 0 - 51
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift

@@ -26,57 +26,6 @@ extension PreferencesEditor {
             let statFields = [
             let statFields = [
                 Field(
                 Field(
                     displayName: NSLocalizedString(
                     displayName: NSLocalizedString(
-                        "Low Glucose Limit",
-                        comment: "Display As Low Glucose Percantage Under This Value"
-                    ) + " (\(settingsManager.settings.units.rawValue))",
-
-                    type: .decimal(keypath: \.low),
-                    infoText: NSLocalizedString(
-                        "Blood Glucoses Under This Value Will Added To And Displayed as Low Glucose Percantage",
-                        comment: "Description for Low Glucose Limit"
-                    ),
-                    settable: self
-                ),
-                Field(
-                    displayName: NSLocalizedString(
-                        "High Glucose Limit",
-                        comment: "Limit For High Glucose in Statistics View"
-                    ) + " (\(settingsManager.settings.units.rawValue))",
-
-                    type: .decimal(keypath: \.high),
-                    infoText: NSLocalizedString(
-                        "Blood Glucoses Over This Value Will Added To And Displaved as High Glucose Percantage",
-                        comment: "High Glucose Limit"
-                    ),
-                    settable: self
-                ),
-                Field(
-                    displayName: NSLocalizedString(
-                        "Update every number of minutes:",
-                        comment: "How often to update the statistics"
-                    ),
-
-                    type: .decimal(keypath: \.updateInterval),
-                    infoText: NSLocalizedString(
-                        "Default is 20 minutes. How often to update and save the statistics.json and to upload last array, when enabled, to Nightscout.",
-                        comment: "Description for update interval for statistics"
-                    ),
-                    settable: self
-                ),
-                Field(
-                    displayName: NSLocalizedString(
-                        "Display Loop Cycle statistics",
-                        comment: "Display Display Loop Cycle statistics in statPanel"
-                    ),
-                    type: .boolean(keypath: \.displayLoops),
-                    infoText: NSLocalizedString(
-                        "Displays Loop statistics in the statPanel in Home View",
-                        comment: "Description for Display Loop statistics"
-                    ),
-                    settable: self
-                ),
-                Field(
-                    displayName: NSLocalizedString(
                         "Override HbA1c unit",
                         "Override HbA1c unit",
                         comment: "Display %"
                         comment: "Display %"
                     ),
                     ),

+ 1 - 1
FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift

@@ -37,7 +37,7 @@ extension PreferencesEditor {
 
 
                     Toggle("Skip Bolus screen after carbs", isOn: $state.skipBolusScreenAfterCarbs)
                     Toggle("Skip Bolus screen after carbs", isOn: $state.skipBolusScreenAfterCarbs)
 
 
-                    Toggle("Display Statistics", isOn: $state.displayStatistics)
+                    Toggle("Allow Upload of Statistics to NS", isOn: $state.displayStatistics)
                 }
                 }
 
 
                 ForEach(state.sections.indexed(), id: \.1.id) { sectionIndex, section in
                 ForEach(state.sections.indexed(), id: \.1.id) { sectionIndex, section in

+ 5 - 0
FreeAPS/Sources/Modules/Stat/StatDataFlow.swift

@@ -0,0 +1,5 @@
+enum Stat {
+    enum Config {}
+}
+
+protocol StatProvider: Provider {}

+ 3 - 0
FreeAPS/Sources/Modules/Stat/StatProvider.swift

@@ -0,0 +1,3 @@
+extension Stat {
+    final class Provider: BaseProvider, StatProvider {}
+}

+ 21 - 0
FreeAPS/Sources/Modules/Stat/StatStateModel.swift

@@ -0,0 +1,21 @@
+import Foundation
+import SwiftUI
+import Swinject
+
+extension Stat {
+    final class StateModel: BaseStateModel<Provider> {
+        @Injected() var settings: SettingsManager!
+        @Published var highLimit: Decimal?
+        @Published var lowLimit: Decimal?
+        @Published var overrideUnit: Bool?
+
+        private(set) var units: GlucoseUnits = .mmolL
+
+        override func subscribe() {
+            highLimit = settingsManager.settings.highGlucose
+            lowLimit = settingsManager.settings.lowGlucose
+            units = settingsManager.settings.units
+            overrideUnit = settingsManager.preferences.overrideHbA1cUnit
+        }
+    }
+}

+ 767 - 0
FreeAPS/Sources/Modules/Stat/View/StatRootView.swift

@@ -0,0 +1,767 @@
+import Charts
+import CoreData
+import SwiftDate
+import SwiftUI
+import Swinject
+
+extension Stat {
+    struct RootView: BaseView {
+        let resolver: Resolver
+        @StateObject var state = StateModel()
+
+        // @Environment(\.managedObjectContext) var moc
+        @FetchRequest(
+            entity: Readings.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
+                format: "date >= %@", Calendar.current.startOfDay(for: Date()) as NSDate
+            )
+        ) var fetchedGlucoseDay: FetchedResults<Readings>
+
+        @FetchRequest(
+            entity: Readings.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)],
+            predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-24.hours.timeInterval) as NSDate)
+        ) var fetchedGlucoseTwentyFourHours: FetchedResults<Readings>
+
+        @FetchRequest(
+            entity: Readings.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)],
+            predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-7.days.timeInterval) as NSDate)
+        ) var fetchedGlucoseWeek: FetchedResults<Readings>
+
+        @FetchRequest(
+            entity: Readings.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
+                format: "date > %@",
+                Date().addingTimeInterval(-30.days.timeInterval) as NSDate
+            )
+        ) var fetchedGlucoseMonth: FetchedResults<Readings>
+
+        @FetchRequest(
+            entity: Readings.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
+                format: "date > %@",
+                Date().addingTimeInterval(-90.days.timeInterval) as NSDate
+            )
+        ) var fetchedGlucose: FetchedResults<Readings>
+
+        @FetchRequest(
+            entity: TDD.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "timestamp", ascending: false)]
+        ) var fetchedTDD: FetchedResults<TDD>
+
+        @FetchRequest(
+            entity: LoopStatRecord.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "start", ascending: false)], predicate: NSPredicate(
+                format: "start > %@",
+                Date().addingTimeInterval(-24.hours.timeInterval) as NSDate
+            )
+        ) var fetchedLoopStats: FetchedResults<LoopStatRecord>
+
+        @FetchRequest(
+            entity: InsulinDistribution.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
+        ) var fetchedInsulin: FetchedResults<InsulinDistribution>
+
+        enum Duration: String, CaseIterable, Identifiable {
+            case Today
+            case Day
+            case Week
+            case Month
+            case Total
+            var id: Self { self }
+        }
+
+        @State private var selectedDuration: Duration = .Today
+        @State var paddingAmount: CGFloat? = 10
+        @State var headline: Color = .secondary
+        @State var days: Double = 0
+        @State var pointSize: CGFloat = 3
+        @State var conversionFactor = 0.0555
+
+        @ViewBuilder func stats() -> some View {
+            bloodGlucose
+            Divider()
+            tirChart
+            Divider()
+            hba1c
+            Divider()
+            loops
+        }
+
+        @ViewBuilder func chart() -> some View {
+            Text("Statistics").font(.largeTitle).bold().padding(.top, 25)
+            switch selectedDuration {
+            case .Today:
+                glucoseChart
+            case .Day:
+                glucoseChartTwentyFourHours
+            case .Week:
+                glucoseChartWeek
+            case .Month:
+                glucoseChartMonth
+            case .Total:
+                glucoseChart90
+            }
+        }
+
+        var body: some View {
+            ZStack {
+                VStack(alignment: .center, spacing: 8) {
+                    chart().padding(.horizontal, 10)
+                    Divider()
+                    stats()
+                    Spacer()
+                    Picker("Duration", selection: $selectedDuration) {
+                        ForEach(Duration.allCases) { duration in
+                            Text(duration.rawValue).tag(Optional(duration))
+                        }
+                    }.pickerStyle(.segmented)
+                }
+            }.onAppear(perform: configureView)
+        }
+
+        var loops: some View {
+            VStack {
+                let loops_ = loopStats(fetchedLoopStats)
+                HStack {
+                    ForEach(0 ..< loops_.count, id: \.self) { index in
+                        VStack {
+                            Text(loops_[index].string).font(.subheadline).foregroundColor(.secondary)
+                            Text(
+                                index == 0 ? loops_[index].double.formatted() : (
+                                    index == 2 ? loops_[index].double
+                                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(2))) :
+                                        loops_[index]
+                                        .double
+                                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))
+                                )
+                            )
+                        }.padding(.horizontal, 6)
+                    }
+                }
+            }
+        }
+
+        var hba1c: some View {
+            let useUnit: GlucoseUnits = (state.units == .mmolL && (state.overrideUnit ?? false)) ? .mgdL :
+                (state.units == .mgdL && (state.overrideUnit ?? false) || state.units == .mmolL) ? .mmolL : .mgdL
+            return HStack {
+                let hba1cs = glucoseStats(fetchedGlucose)
+                let hba1cString = (
+                    useUnit == .mmolL ? hba1cs.ifcc
+                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) : hba1cs.ngsp
+                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))
+                ) + " %"
+
+                VStack {
+                    Text("HbA1C").font(.subheadline).foregroundColor(headline)
+                    HStack {
+                        VStack {
+                            Text(hba1cString)
+                        }
+                    }
+                }.padding([.horizontal], 15)
+                VStack {
+                    Text("SD").font(.subheadline).foregroundColor(.secondary)
+                    HStack {
+                        VStack {
+                            Text(
+                                hba1cs.sd
+                                    .formatted(
+                                        .number.grouping(.never).rounded()
+                                            .precision(.fractionLength(state.units == .mmolL ? 1 : 0))
+                                    )
+                            )
+                        }
+                    }
+                }.padding([.horizontal], 15)
+                VStack {
+                    Text("CV").font(.subheadline).foregroundColor(.secondary)
+                    HStack {
+                        VStack {
+                            Text(
+                                hba1cs.cv.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0)))
+                            )
+                        }
+                    }
+                }.padding([.horizontal], 15)
+                if selectedDuration == .Total {
+                    VStack {
+                        Text("Days").font(.subheadline).foregroundColor(.secondary)
+                        HStack {
+                            VStack {
+                                Text(numberOfDays.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))))
+                            }
+                        }
+                    }.padding([.horizontal], 15)
+                }
+            }
+        }
+
+        var bloodGlucose: some View {
+            VStack {
+                HStack {
+                    let bgs = glucoseStats(fetchedGlucose)
+                    VStack {
+                        HStack {
+                            Text("Average").font(.subheadline).foregroundColor(headline)
+                        }
+                        HStack {
+                            VStack {
+                                Text(
+                                    bgs.average
+                                        .formatted(
+                                            .number.grouping(.never).rounded()
+                                                .precision(.fractionLength(state.units == .mmolL ? 1 : 0))
+                                        )
+                                )
+                            }
+                        }
+                    }
+                    VStack {
+                        HStack {
+                            Text("Median").font(.subheadline).foregroundColor(.secondary)
+                        }
+                        HStack {
+                            VStack {
+                                Text(
+                                    bgs.median
+                                        .formatted(
+                                            .number.grouping(.never).rounded()
+                                                .precision(.fractionLength(state.units == .mmolL ? 1 : 0))
+                                        )
+                                )
+                            }
+                        }
+                    }
+                    VStack {
+                        HStack {
+                            Text(selectedDuration == .Today ? "Readings today" : "Readings / 24h").font(.subheadline)
+                                .foregroundColor(.secondary)
+                        }
+                        HStack {
+                            VStack {
+                                Text(
+                                    bgs.readings.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0)))
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        var numberOfDays: Double {
+            let endIndex = fetchedGlucose.count - 1
+            var days = 0.0
+
+            if endIndex > 0 {
+                let firstElementTime = fetchedGlucose.first?.date ?? Date()
+                let lastElementTime = fetchedGlucose[endIndex].date ?? Date()
+                days = (firstElementTime - lastElementTime).timeInterval / 8.64E4
+            }
+            return days
+        }
+
+        var tirChart: some View {
+            let array = selectedDuration == .Today ? fetchedGlucoseDay : selectedDuration == .Day ?
+                fetchedGlucoseTwentyFourHours :
+                selectedDuration == .Week ? fetchedGlucoseWeek : selectedDuration == .Month ? fetchedGlucoseMonth :
+                selectedDuration ==
+                .Total ? fetchedGlucose : fetchedGlucoseDay
+            let fetched = tir(array)
+            let data: [ShapeModel] = [
+                .init(type: "Low", percent: fetched[0].decimal),
+                .init(type: "In Range", percent: fetched[1].decimal),
+                .init(type: "High", percent: fetched[2].decimal)
+            ]
+
+            return VStack(alignment: .center) {
+                Chart(data) { shape in
+                    BarMark(
+                        x: .value("Shape", shape.type),
+                        y: .value("Percentage", shape.percent)
+                    )
+                    .foregroundStyle(by: .value("Group", shape.type))
+                    .annotation(position: shape.percent < 5 ? .top : .overlay, alignment: .center) {
+                        Text(shape.percent == 0 ? "" : "\(shape.percent, format: .number.precision(.fractionLength(0))) %")
+                        // .foregroundColor(.white)
+                    }
+                }
+                .chartYAxis(.hidden)
+                .chartLegend(.hidden)
+                .chartForegroundStyleScale(["Low": .red, "In Range": .green, "High": .orange])
+            }
+        }
+
+        var glucoseChart: some View {
+            let count = fetchedGlucoseDay.count
+            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            return Chart {
+                ForEach(fetchedGlucoseDay.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.orange)
+                    .symbolSize(count < 20 ? 30 : 12)
+                }
+                ForEach(
+                    fetchedGlucoseDay
+                        .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }),
+                    id: \.date
+                ) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.green)
+                    .symbolSize(count < 20 ? 30 : 12)
+                }
+                ForEach(fetchedGlucoseDay.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.red)
+                    .symbolSize(count < 20 ? 30 : 12)
+                }
+            }
+            .chartYScale(domain: [0, 17])
+            .chartYAxis {
+                AxisMarks(
+                    values: [
+                        0,
+                        lowLimit,
+                        highLimit,
+                        15
+                    ]
+                )
+            }
+        }
+
+        var glucoseChartTwentyFourHours: some View {
+            let count = fetchedGlucoseTwentyFourHours.count
+            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            return Chart {
+                ForEach(fetchedGlucoseTwentyFourHours.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.orange)
+                    .symbolSize(count < 20 ? 20 : 10)
+                }
+                ForEach(
+                    fetchedGlucoseTwentyFourHours
+                        .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }),
+                    id: \.date
+                ) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.green)
+                    .symbolSize(count < 20 ? 20 : 10)
+                }
+                ForEach(fetchedGlucoseTwentyFourHours.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.red)
+                    .symbolSize(count < 20 ? 20 : 10)
+                }
+                RuleMark(
+                    y: .value("Target", 100 * (state.units == .mmolL ? conversionFactor : 1))
+                )
+                .lineStyle(StrokeStyle(lineWidth: 0.5, dash: [10]))
+            }
+            .chartYScale(domain: [0, 17])
+            .chartYAxis {
+                AxisMarks(
+                    values: [
+                        0,
+                        lowLimit,
+                        highLimit,
+                        15
+                    ]
+                )
+            }
+        }
+
+        var glucoseChartWeek: some View {
+            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            return Chart {
+                ForEach(fetchedGlucoseWeek.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.orange)
+                    .symbolSize(5)
+                }
+                ForEach(
+                    fetchedGlucoseWeek
+                        .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }),
+                    id: \.date
+                ) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.green)
+                    .symbolSize(5)
+                }
+                ForEach(fetchedGlucoseWeek.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.red)
+                    .symbolSize(5)
+                }
+                RuleMark(
+                    y: .value("Target", 100 * (state.units == .mmolL ? conversionFactor : 1))
+                )
+                .lineStyle(StrokeStyle(lineWidth: 0.5, dash: [10]))
+            }
+            .chartYScale(domain: [0, 17])
+            .chartYAxis {
+                AxisMarks(
+                    values: [
+                        0,
+                        lowLimit,
+                        highLimit,
+                        15
+                    ]
+                )
+            }
+        }
+
+        var glucoseChartMonth: some View {
+            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            return Chart {
+                ForEach(fetchedGlucoseMonth.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.orange)
+                    .symbolSize(2)
+                }
+                ForEach(
+                    fetchedGlucoseMonth
+                        .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }),
+                    id: \.date
+                ) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.green)
+                    .symbolSize(2)
+                }
+                ForEach(fetchedGlucoseMonth.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.red)
+                    .symbolSize(2)
+                }
+                RuleMark(
+                    y: .value("Target", 100 * (state.units == .mmolL ? conversionFactor : 1))
+                )
+                .lineStyle(StrokeStyle(lineWidth: 0.5, dash: [10]))
+            }
+            .chartYScale(domain: [0, 17])
+            .chartYAxis {
+                AxisMarks(
+                    values: [
+                        0,
+                        lowLimit,
+                        highLimit,
+                        15
+                    ]
+                )
+            }
+        }
+
+        var glucoseChart90: some View {
+            let lowLimit = (state.lowLimit ?? 70) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            let highLimit = (state.highLimit ?? 145) * (state.units == .mmolL ? Decimal(conversionFactor) : 1)
+            return Chart {
+                ForEach(fetchedGlucose.filter({ $0.glucose > Int(state.highLimit ?? 145) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("Low", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.orange)
+                    .symbolSize(2)
+                }
+                ForEach(
+                    fetchedGlucose
+                        .filter({ $0.glucose >= Int(state.lowLimit ?? 70) && $0.glucose <= Int(state.highLimit ?? 145) }),
+                    id: \.date
+                ) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("In Range", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.green)
+                    .symbolSize(2)
+                }
+                ForEach(fetchedGlucose.filter({ $0.glucose < Int(state.lowLimit ?? 70) }), id: \.date) { item in
+                    PointMark(
+                        x: .value("Date", item.date ?? Date()),
+                        y: .value("High", Double(item.glucose) * (state.units == .mmolL ? conversionFactor : 1))
+                    )
+                    .foregroundStyle(.red)
+                    .symbolSize(2)
+                }
+                RuleMark(
+                    y: .value("Target", 100 * (state.units == .mmolL ? conversionFactor : 1))
+                )
+                .lineStyle(StrokeStyle(lineWidth: 0.5, dash: [10]))
+            }
+            .chartYScale(domain: [0, 17])
+            .chartYAxis {
+                AxisMarks(
+                    values: [
+                        0,
+                        lowLimit,
+                        highLimit,
+                        15
+                    ]
+                )
+            }
+        }
+
+        private func loopStats(_ loops: FetchedResults<LoopStatRecord>) -> [(double: Double, string: String)] {
+            guard (loops.first?.start) != nil else { return [] }
+
+            var i = 0.0
+            var minimumInt = 999.0
+            var maximumInt = 0.0
+            var timeIntervalLoops = 0.0
+            var previousTimeLoop = loops.first?.end ?? Date()
+            var timeIntervalLoopArray: [Double] = []
+
+            let durationArray = loops.compactMap({ each in each.duration })
+            let durationArrayCount = durationArray.count
+            var durationAverage = durationArray.reduce(0, +) / Double(durationArrayCount)
+
+            let medianDuration = medianCalculationDouble(array: durationArray)
+            let successsNR = loops.compactMap({ each in each.loopStatus }).filter({ each in each!.contains("Success") }).count
+            let errorNR = durationArrayCount - successsNR
+            let successRate: Double? = (Double(successsNR) / Double(successsNR + errorNR)) * 100
+
+            for each in loops {
+                if let loopEnd = each.end {
+                    i += 1
+                    timeIntervalLoops = (previousTimeLoop - (each.start ?? previousTimeLoop)).timeInterval / 60
+
+                    if timeIntervalLoops > 0.0, i != 1 {
+                        timeIntervalLoopArray.append(timeIntervalLoops)
+                    }
+                    if timeIntervalLoops > maximumInt {
+                        maximumInt = timeIntervalLoops
+                    }
+                    if timeIntervalLoops < minimumInt, i != 1 {
+                        minimumInt = timeIntervalLoops
+                    }
+                    previousTimeLoop = loopEnd
+                }
+            }
+
+            // Average Loop Interval in minutes
+            let timeOfFirstIndex = loops.first?.start ?? Date()
+            let lastIndexWithTimestamp = loops.count - 1
+            let timeOfLastIndex = loops[lastIndexWithTimestamp].end ?? Date()
+            let averageInterval = (timeOfFirstIndex - timeOfLastIndex).timeInterval / 60 / Double(errorNR + successsNR)
+
+            if minimumInt == 999.0 {
+                minimumInt = 0.0
+            }
+
+            var array: [(double: Double, string: String)] = []
+
+            array.append((double: Double(successsNR + errorNR), string: "Loops"))
+            array.append((double: averageInterval, string: "Interval"))
+            array.append((double: medianDuration, string: "Duration"))
+            array.append((double: successRate ?? 100, string: "%"))
+
+            return array
+        }
+
+        private func medianCalculation(array: [Int]) -> Double {
+            guard !array.isEmpty else {
+                return 0
+            }
+            let sorted = array.sorted()
+            let length = array.count
+
+            if length % 2 == 0 {
+                return Double((sorted[length / 2 - 1] + sorted[length / 2]) / 2)
+            }
+            return Double(sorted[length / 2])
+        }
+
+        private func medianCalculationDouble(array: [Double]) -> Double {
+            guard !array.isEmpty else {
+                return 0
+            }
+            let sorted = array.sorted()
+            let length = array.count
+
+            if length % 2 == 0 {
+                return (sorted[length / 2 - 1] + sorted[length / 2]) / 2
+            }
+            return sorted[length / 2]
+        }
+
+        private func glucoseStats(_ glucose_90: FetchedResults<Readings>)
+            -> (ifcc: Double, ngsp: Double, average: Double, median: Double, sd: Double, cv: Double, readings: Double)
+        {
+            var numberOfDays: Double = 0
+            let endIndex = glucose_90.count - 1
+
+            if endIndex > 0 {
+                let firstElementTime = glucose_90[0].date ?? Date()
+                let lastElementTime = glucose_90[endIndex].date ?? Date()
+                numberOfDays = (firstElementTime - lastElementTime).timeInterval / 8.64E4
+            }
+            var duration = 1
+            var denominator: Double = 1
+
+            switch selectedDuration {
+            case .Today:
+                let minutesSinceMidnight = Calendar.current.component(.hour, from: Date()) * 60 + Calendar.current
+                    .component(.minute, from: Date())
+                duration = minutesSinceMidnight
+                denominator = 1
+            case .Day:
+                duration = 1 * 1440
+                denominator = 1
+            case .Week:
+                duration = 7 * 1440
+                if numberOfDays > 7 { denominator = 7 } else { denominator = numberOfDays }
+            case .Month:
+                duration = 30 * 1440
+                if numberOfDays > 30 { denominator = 30 } else { denominator = numberOfDays }
+            case .Total:
+                duration = 90 * 1440
+                if numberOfDays >= 90 { denominator = 90 } else { denominator = numberOfDays }
+            }
+
+            let timeAgo = Date().addingTimeInterval(-duration.minutes.timeInterval)
+            let glucose = glucose_90.filter({ ($0.date ?? Date()) >= timeAgo })
+
+            let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) })
+            let sumReadings = justGlucoseArray.reduce(0, +)
+            let countReadings = justGlucoseArray.count
+
+            let glucoseAverage = Double(sumReadings) / Double(countReadings)
+            let medianGlucose = medianCalculation(array: justGlucoseArray)
+
+            var NGSPa1CStatisticValue = 0.0
+            var IFCCa1CStatisticValue = 0.0
+
+            if numberOfDays > 0 {
+                NGSPa1CStatisticValue = (glucoseAverage + 46.7) / 28.7 // NGSP (%)
+                IFCCa1CStatisticValue = 10.929 *
+                    (NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
+            }
+            var sumOfSquares = 0.0
+
+            for array in justGlucoseArray {
+                sumOfSquares += pow(Double(array) - Double(glucoseAverage), 2)
+            }
+            var sd = 0.0
+            var cv = 0.0
+
+            // Avoid division by zero
+            if glucoseAverage > 0 {
+                sd = sqrt(sumOfSquares / Double(countReadings))
+                cv = sd / Double(glucoseAverage) * 100
+            }
+
+            var output: (ifcc: Double, ngsp: Double, average: Double, median: Double, sd: Double, cv: Double, readings: Double)
+            output = (
+                ifcc: IFCCa1CStatisticValue,
+                ngsp: NGSPa1CStatisticValue,
+                average: glucoseAverage * (state.units == .mmolL ? conversionFactor : 1),
+                median: medianGlucose * (state.units == .mmolL ? conversionFactor : 1),
+                sd: sd * (state.units == .mmolL ? conversionFactor : 1), cv: cv,
+                readings: Double(countReadings) / denominator
+            )
+            return output
+        }
+
+        private func tir(_ glucose_90: FetchedResults<Readings>) -> [(decimal: Decimal, string: String)] {
+            var duration = 1
+
+            switch selectedDuration {
+            case .Today:
+                let minutesSinceMidnight = Calendar.current.component(.hour, from: Date()) * 60 + Calendar.current
+                    .component(.minute, from: Date())
+                duration = minutesSinceMidnight
+            case .Day:
+                duration = 1 * 1440
+            case .Week:
+                duration = 7 * 1440
+            case .Month:
+                duration = 30 * 1440
+            case .Total:
+                duration = 90 * 1440
+            }
+
+            let hypoLimit = Int(state.lowLimit ?? 70)
+            let hyperLimit = Int(state.highLimit ?? 145)
+
+            let timeAgo = Date().addingTimeInterval(-duration.minutes.timeInterval)
+            let glucose = glucose_90.filter({ ($0.date ?? Date()) >= timeAgo })
+
+            let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) })
+            let totalReadings = justGlucoseArray.count
+
+            let hyperArray = glucose.filter({ $0.glucose >= hyperLimit })
+            let hyperReadings = hyperArray.compactMap({ each in each.glucose as Int16 }).count
+            let hyperPercentage = Double(hyperReadings) / Double(totalReadings) * 100
+
+            let hypoArray = glucose.filter({ $0.glucose <= hypoLimit })
+            let hypoReadings = hypoArray.compactMap({ each in each.glucose as Int16 }).count
+            let hypoPercentage = Double(hypoReadings) / Double(totalReadings) * 100
+
+            let tir = 100 - (hypoPercentage + hyperPercentage)
+
+            var array: [(decimal: Decimal, string: String)] = []
+            array.append((decimal: Decimal(hypoPercentage), string: "Low"))
+            array.append((decimal: Decimal(tir), string: "NormaL"))
+            array.append((decimal: Decimal(hyperPercentage), string: "High"))
+
+            return array
+        }
+
+        private func colorOfGlucose(_ index: Int) -> Color {
+            let whichIndex = index
+
+            switch whichIndex {
+            case 0:
+                return .red
+            case 1:
+                return .green
+            case 2:
+                return .orange
+            default:
+                return .primary
+            }
+        }
+    }
+}

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

@@ -29,6 +29,7 @@ enum Screen: Identifiable, Hashable {
     case iconConfig
     case iconConfig
     case overrideProfilesConfig
     case overrideProfilesConfig
     case snooze
     case snooze
+    case statistics
     case watch
     case watch
 
 
     var id: Int { String(reflecting: self).hashValue }
     var id: Int { String(reflecting: self).hashValue }
@@ -40,10 +41,7 @@ extension Screen {
         case .loading:
         case .loading:
             ProgressView()
             ProgressView()
         case .home:
         case .home:
-            Home.RootView(
-                resolver: resolver,
-                selectedState: .day
-            )
+            Home.RootView(resolver: resolver)
         case .settings:
         case .settings:
             Settings.RootView(resolver: resolver)
             Settings.RootView(resolver: resolver)
         case let .configEditor(file):
         case let .configEditor(file):
@@ -96,6 +94,8 @@ extension Screen {
             Snooze.RootView(resolver: resolver)
             Snooze.RootView(resolver: resolver)
         case .watch:
         case .watch:
             WatchConfig.RootView(resolver: resolver)
             WatchConfig.RootView(resolver: resolver)
+        case .statistics:
+            Stat.RootView(resolver: resolver)
         }
         }
     }
     }
 
 

+ 0 - 1
FreeAPS/Sources/Services/UserNotifiactions/UserNotificationsManager.swift

@@ -186,7 +186,6 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
         addAppBadge(glucose: nil)
         addAppBadge(glucose: nil)
 
 
         let glucose = glucoseStorage.recent()
         let glucose = glucoseStorage.recent()
-
         guard let lastGlucose = glucose.last, let glucoseValue = lastGlucose.glucose else { return }
         guard let lastGlucose = glucose.last, let glucoseValue = lastGlucose.glucose else { return }
 
 
         addAppBadge(glucose: lastGlucose.glucose)
         addAppBadge(glucose: lastGlucose.glucose)