add user-defined TIR back but in a better way

to avoid adding extra menu lines, use an enum to track the TIR type chosen and allow the user to just chose directly, or cycle through by double-tapping the statistics view
This commit is contained in:
Paul Plant 2023-12-24 15:00:26 +01:00
parent d96f2e2e66
commit bd6ce1c184
18 changed files with 265 additions and 85 deletions

View File

@ -37,6 +37,7 @@
47ADD2E127FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47ADD2E027FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift */; }; 47ADD2E127FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47ADD2E027FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift */; };
47B60F3726F389E2003198D3 /* LandscapeChartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B60F3626F389E2003198D3 /* LandscapeChartViewController.swift */; }; 47B60F3726F389E2003198D3 /* LandscapeChartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B60F3626F389E2003198D3 /* LandscapeChartViewController.swift */; };
47B7FC722B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B7FC712B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift */; }; 47B7FC722B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B7FC712B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift */; };
47CF18B22B37689A00FA6160 /* TimeInRangeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CF18B12B37689A00FA6160 /* TimeInRangeType.swift */; };
47D2DB3B2B14F6D000C8EE6B /* ScreenLockDimmingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D2DB3A2B14F6D000C8EE6B /* ScreenLockDimmingType.swift */; }; 47D2DB3B2B14F6D000C8EE6B /* ScreenLockDimmingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D2DB3A2B14F6D000C8EE6B /* ScreenLockDimmingType.swift */; };
47D9BC952A78498500AB85B2 /* BgReadingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D9BC942A78498500AB85B2 /* BgReadingsDetailView.swift */; }; 47D9BC952A78498500AB85B2 /* BgReadingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D9BC942A78498500AB85B2 /* BgReadingsDetailView.swift */; };
47DB06C22A6FC02200267BE3 /* SettingsViewDataSourceSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47DB06C12A6FC02200267BE3 /* SettingsViewDataSourceSettingsViewModel.swift */; }; 47DB06C22A6FC02200267BE3 /* SettingsViewDataSourceSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47DB06C12A6FC02200267BE3 /* SettingsViewDataSourceSettingsViewModel.swift */; };
@ -742,6 +743,7 @@
47ADD2E027FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift; sourceTree = "<group>"; }; 47ADD2E027FB05EB0025E2F4 /* ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPointsScatterDownTrianglesWithDropdownLineLayer.swift; sourceTree = "<group>"; };
47B60F3626F389E2003198D3 /* LandscapeChartViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandscapeChartViewController.swift; sourceTree = "<group>"; }; 47B60F3626F389E2003198D3 /* LandscapeChartViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandscapeChartViewController.swift; sourceTree = "<group>"; };
47B7FC712B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowerBackgroundKeepAliveType.swift; sourceTree = "<group>"; }; 47B7FC712B00CF4B004C872B /* FollowerBackgroundKeepAliveType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowerBackgroundKeepAliveType.swift; sourceTree = "<group>"; };
47CF18B12B37689A00FA6160 /* TimeInRangeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInRangeType.swift; sourceTree = "<group>"; };
47D2DB3A2B14F6D000C8EE6B /* ScreenLockDimmingType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenLockDimmingType.swift; sourceTree = "<group>"; }; 47D2DB3A2B14F6D000C8EE6B /* ScreenLockDimmingType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenLockDimmingType.swift; sourceTree = "<group>"; };
47D9BC942A78498500AB85B2 /* BgReadingsDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BgReadingsDetailView.swift; sourceTree = "<group>"; }; 47D9BC942A78498500AB85B2 /* BgReadingsDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BgReadingsDetailView.swift; sourceTree = "<group>"; };
47DB06C02A6FB3CC00267BE3 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/BgReadings.strings; sourceTree = "<group>"; }; 47DB06C02A6FB3CC00267BE3 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/BgReadings.strings; sourceTree = "<group>"; };
@ -1670,6 +1672,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
47FB28072636B04200042FFB /* StatisticsManager.swift */, 47FB28072636B04200042FFB /* StatisticsManager.swift */,
47CF18B12B37689A00FA6160 /* TimeInRangeType.swift */,
); );
path = Statistics; path = Statistics;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3862,6 +3865,7 @@
F8F9722D23A5915900C3F17D /* M5StackBluetoothTransmitter.swift in Sources */, F8F9722D23A5915900C3F17D /* M5StackBluetoothTransmitter.swift in Sources */,
F808D2C8240323CA0084B5DB /* BubbleBluetoothPeripheralViewModel.swift in Sources */, F808D2C8240323CA0084B5DB /* BubbleBluetoothPeripheralViewModel.swift in Sources */,
F8E6C79024CEC22A007C1199 /* TextsSnooze.swift in Sources */, F8E6C79024CEC22A007C1199 /* TextsSnooze.swift in Sources */,
47CF18B22B37689A00FA6160 /* TimeInRangeType.swift in Sources */,
F8F9722C23A5915900C3F17D /* GlucoseData.swift in Sources */, F8F9722C23A5915900C3F17D /* GlucoseData.swift in Sources */,
F8A389E7232ECE7E0010F405 /* SettingsViewUtilities.swift in Sources */, F8A389E7232ECE7E0010F405 /* SettingsViewUtilities.swift in Sources */,
F8A2BC3125DB0D6D001D1E78 /* BluetoothPeripheralManager+M5StackBluetoothTransmitterDelegate.swift in Sources */, F8A2BC3125DB0D6D001D1E78 /* BluetoothPeripheralManager+M5StackBluetoothTransmitterDelegate.swift in Sources */,

View File

@ -125,6 +125,8 @@ extension UserDefaults {
case daysToUseStatistics = "daysToUseStatistics" case daysToUseStatistics = "daysToUseStatistics"
/// use IFCC way to show A1C? /// use IFCC way to show A1C?
case useIFCCA1C = "useIFCCA1C" case useIFCCA1C = "useIFCCA1C"
/// which type of TIR calculation is selected?
case timeInRangeType = "timeInRangeType"
/// no longer used, but will leave it here to prevent compiler coredata warnings /// no longer used, but will leave it here to prevent compiler coredata warnings
case useStandardStatisticsRange = "useStandardStatisticsRange" case useStandardStatisticsRange = "useStandardStatisticsRange"
/// use the newer TITR of 70-140mg/dL to calculate the statistics? If false, we will use the conventional TIR of 70-180mg/dL /// use the newer TITR of 70-140mg/dL to calculate the statistics? If false, we will use the conventional TIR of 70-180mg/dL
@ -1171,25 +1173,15 @@ extension UserDefaults {
} }
} }
/// no longer used, but will leave it here to prevent compiler coredata warnings /// holds the enum integer of the time in range calculation type
@objc dynamic var useStandardStatisticsRange: Bool { /// it will default to 0 which is standard
// default value for bool in userdefaults is false, by default we want the statistics view to calculate using the older-style, standardised TIR values (false) var timeInRangeType: TimeInRangeType {
get { get {
return bool(forKey: Key.useTITRStatisticsRange.rawValue) let timeInRangeTypeAsInt = integer(forKey: Key.timeInRangeType.rawValue)
return TimeInRangeType(rawValue: timeInRangeTypeAsInt) ?? .standardRange
} }
set { set {
set(newValue, forKey: Key.useTITRStatisticsRange.rawValue) set(newValue.rawValue, forKey: Key.timeInRangeType.rawValue)
}
}
/// use the newer TITR range of 70-140mg/dL to calculate the statistics? If false, we will use the conventional TIR range of 70-180mg/dL
@objc dynamic var useTITRStatisticsRange: Bool {
// default value for bool in userdefaults is false, by default we want the statistics view to calculate using the older-style, standardised TIR values (false)
get {
return bool(forKey: Key.useTITRStatisticsRange.rawValue)
}
set {
set(newValue, forKey: Key.useTITRStatisticsRange.rawValue)
} }
} }

View File

@ -121,7 +121,7 @@ public final class StatisticsManager {
} }
} }
/*
// let's set up the which values will be used to calculate TIR. It can be either the standardised "Time in Range" values or the newer "Time in Tight Range" values. // let's set up the which values will be used to calculate TIR. It can be either the standardised "Time in Range" values or the newer "Time in Tight Range" values.
let useTITR: Bool = UserDefaults.standard.useTITRStatisticsRange let useTITR: Bool = UserDefaults.standard.useTITRStatisticsRange
@ -132,6 +132,9 @@ public final class StatisticsManager {
lowLimitForTIR = useTITR ? ConstantsStatistics.standardisedLowValueForTITRInMmol : ConstantsStatistics.standardisedLowValueForTIRInMmol lowLimitForTIR = useTITR ? ConstantsStatistics.standardisedLowValueForTITRInMmol : ConstantsStatistics.standardisedLowValueForTIRInMmol
highLimitForTIR = useTITR ? ConstantsStatistics.standardisedHighValueForTITRInMmol : ConstantsStatistics.standardisedHighValueForTIRInMmol highLimitForTIR = useTITR ? ConstantsStatistics.standardisedHighValueForTITRInMmol : ConstantsStatistics.standardisedHighValueForTIRInMmol
} }
*/
lowLimitForTIR = UserDefaults.standard.timeInRangeType.lowerLimit
highLimitForTIR = UserDefaults.standard.timeInRangeType.higherLimit
// make sure that there exist elements in the glucoseValue array before trying to process statistics calculations or we could get a fatal divide by zero error/crash // make sure that there exist elements in the glucoseValue array before trying to process statistics calculations or we could get a fatal divide by zero error/crash
if glucoseValues.count > 0 { if glucoseValues.count > 0 {

View File

@ -0,0 +1,81 @@
//
// TimeInRangeType.swift
// xdrip
//
// Created by Paul Plant on 23/12/23.
// Copyright © 2023 Johan Degraeve. All rights reserved.
//
import Foundation
/// types of background keep-alive
public enum TimeInRangeType: Int, CaseIterable {
// when adding to TimeInRangeType, add new cases at the end (ie 3, ...)
// if this is done in the middle then a database migration would be required, because the rawvalue is stored as Int16 in the coredata
// the order of the returned enum can be defined in allCases below
case standardRange = 0
case tightRange = 1
case userDefinedRange = 2
var description: String {
switch self {
case .standardRange:
return Texts_SettingsView.timeInRangeTypeStandardRange
case .tightRange:
return Texts_SettingsView.timeInRangeTypeTightRange
case .userDefinedRange:
return Texts_SettingsView.timeInRangeTypeUserDefinedRange
}
}
var title: String {
switch self {
case .standardRange:
return Texts_Common.inRangeStatistics
case .tightRange:
return Texts_Common.inTightRangeStatistics
case .userDefinedRange:
return Texts_Common.userRangeStatistics
}
}
var lowerLimit: Double {
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl
switch self {
case .standardRange:
return isMgDl ? ConstantsStatistics.standardisedLowValueForTIRInMgDl : ConstantsStatistics.standardisedLowValueForTIRInMmol
case .tightRange:
return isMgDl ? ConstantsStatistics.standardisedLowValueForTITRInMgDl : ConstantsStatistics.standardisedLowValueForTITRInMmol
case .userDefinedRange:
return UserDefaults.standard.lowMarkValueInUserChosenUnit
}
}
var higherLimit: Double {
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl
switch self {
case .standardRange:
return isMgDl ? ConstantsStatistics.standardisedHighValueForTIRInMgDl : ConstantsStatistics.standardisedHighValueForTIRInMmol
case .tightRange:
return isMgDl ? ConstantsStatistics.standardisedHighValueForTITRInMgDl : ConstantsStatistics.standardisedHighValueForTITRInMmol
case .userDefinedRange:
return UserDefaults.standard.highMarkValueInUserChosenUnit
}
}
func rangeString() -> String {
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl
return " (" + self.lowerLimit.bgValuetoString(mgdl: isMgDl) + "-" + self.higherLimit.bgValuetoString(mgdl: isMgDl) + ")"
}
}

View File

@ -30,7 +30,6 @@
"settingsviews_showStatistics" = "Show Statistics"; "settingsviews_showStatistics" = "Show Statistics";
"settingsviews_daysToUseStatisticsTitle" = "Days to Calculate"; "settingsviews_daysToUseStatisticsTitle" = "Days to Calculate";
"settingsviews_daysToUseStatisticsMessage" = "Maximum days we should try to use to calculate the statistics?\n\n(Enter 0 to calculate today since midnight)"; "settingsviews_daysToUseStatisticsMessage" = "Maximum days we should try to use to calculate the statistics?\n\n(Enter 0 to calculate today since midnight)";
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol"; "settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";
"settingsviews_sectiontitletransmitter" = "Transmitter"; "settingsviews_sectiontitletransmitter" = "Transmitter";
"settingsviews_transmittertype" = "Transmitter Type"; "settingsviews_transmittertype" = "Transmitter Type";

View File

@ -42,6 +42,7 @@
"common_statistics_low" = "Low"; "common_statistics_low" = "Low";
"common_statistics_inRange" = "In Range"; "common_statistics_inRange" = "In Range";
"common_statistics_inTightRange" = "Tight Range"; "common_statistics_inTightRange" = "Tight Range";
"common_statistics_userRange" = "User Range";
"common_statistics_high" = "High"; "common_statistics_high" = "High";
"common_statistics_average" = "Average"; "common_statistics_average" = "Average";
"common_statistics_a1c" = "HbA1c"; "common_statistics_a1c" = "HbA1c";

View File

@ -60,7 +60,10 @@
"settingsviews_showStatistics" = "Show Statistics"; "settingsviews_showStatistics" = "Show Statistics";
"settingsviews_daysToUseStatisticsTitle" = "Days to Calculate"; "settingsviews_daysToUseStatisticsTitle" = "Days to Calculate";
"settingsviews_daysToUseStatisticsMessage" = "Maximum days we should try to use to calculate the statistics?\n\n(Enter 0 to calculate today since midnight)"; "settingsviews_daysToUseStatisticsMessage" = "Maximum days we should try to use to calculate the statistics?\n\n(Enter 0 to calculate today since midnight)";
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range"; "settingsviews_labelTimeInRangeType" = "Time In Range Type";
"settingsviews_timeInRangeTypeStandardRange" = "Standard Range";
"settingsviews_timeInRangeTypeTightRange" = "Tight Range";
"settingsviews_timeInRangeTypeUserDefinedRange" = "User Range";
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol"; "settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";
"settingsviews_sectiontitletransmitter" = "Transmitter"; "settingsviews_sectiontitletransmitter" = "Transmitter";
"settingsviews_transmittertype" = "Transmitter Type"; "settingsviews_transmittertype" = "Transmitter Type";

View File

@ -42,6 +42,7 @@
"common_statistics_low" = "Baja"; "common_statistics_low" = "Baja";
"common_statistics_inRange" = "En Rango"; "common_statistics_inRange" = "En Rango";
"common_statistics_inTightRange" = "Estrecho"; "common_statistics_inTightRange" = "Estrecho";
"common_statistics_userRange" = "Usuario";
"common_statistics_high" = "Alta"; "common_statistics_high" = "Alta";
"common_statistics_average" = "Promedio"; "common_statistics_average" = "Promedio";
"common_statistics_a1c" = "HbA1c"; "common_statistics_a1c" = "HbA1c";

View File

@ -55,7 +55,10 @@
"settingsviews_showStatistics" = "Mostrar Estadísticas"; "settingsviews_showStatistics" = "Mostrar Estadísticas";
"settingsviews_daysToUseStatisticsTitle" = "Días a Calcular"; "settingsviews_daysToUseStatisticsTitle" = "Días a Calcular";
"settingsviews_daysToUseStatisticsMessage" = "Máximos días a intentar usar para calcular las estadísticas?\n\n(Entrar 0 para calcular el día de hoy desde las 00:00hrs)"; "settingsviews_daysToUseStatisticsMessage" = "Máximos días a intentar usar para calcular las estadísticas?\n\n(Entrar 0 para calcular el día de hoy desde las 00:00hrs)";
"settingsviews_useTITRStatisticsRange" = "Usar Tiempo en Rango Estrecho"; "settingsviews_labelTimeInRangeType" = "Tipo de Tiempo en Rango";
"settingsviews_timeInRangeTypeStandardRange" = "Rango Estándar";
"settingsviews_timeInRangeTypeTightRange" = "Rango Estrecho";
"settingsviews_timeInRangeTypeUserDefinedRange" = "Rango Usuario";
"settingsviews_useIFCCA1C" = "Ver HbA1c en mmols/mol"; "settingsviews_useIFCCA1C" = "Ver HbA1c en mmols/mol";
"settingsviews_sectiontitletransmitter" = "Transmisor"; "settingsviews_sectiontitletransmitter" = "Transmisor";
"settingsviews_transmittertype" = "Tipo de Transmisor"; "settingsviews_transmittertype" = "Tipo de Transmisor";

View File

@ -307,9 +307,6 @@
/// statistics settings, section title /// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics"; "settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c? /// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol"; "settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -307,9 +307,6 @@
/// statistics settings, section title /// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics"; "settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c? /// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol"; "settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -307,9 +307,6 @@
/// statistics settings, section title /// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics"; "settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c? /// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol"; "settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -270,9 +270,6 @@
/// transmitter settings, this is for the button, when clicked then user will be requested to give transmitter id. The only difference with settingsviews_transmitterid is that ':' is not added /// transmitter settings, this is for the button, when clicked then user will be requested to give transmitter id. The only difference with settingsviews_transmitterid is that ':' is not added
"settingsviews_transmitterid_text_for_button" = "Transmitter ID"; "settingsviews_transmitterid_text_for_button" = "Transmitter ID";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// dexcom share settings, where user can select if readings should be uploaded to dexcom share yes or no /// dexcom share settings, where user can select if readings should be uploaded to dexcom share yes or no
"settingsviews_uploadReadingstoDexcomShare" = "Upload to Dexcom Share"; "settingsviews_uploadReadingstoDexcomShare" = "Upload to Dexcom Share";

View File

@ -231,9 +231,6 @@
/// statistics settings, section title /// statistics settings, section title
"settingsviews_sectiontitlestatistics" = "Statistics"; "settingsviews_sectiontitlestatistics" = "Statistics";
/// statistics settings, use standard range?
"settingsviews_useTITRStatisticsRange" = "Use Time in Tight Range";
/// statistics settings, use IFCC method for HbA1c? /// statistics settings, use IFCC method for HbA1c?
"settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol"; "settingsviews_useIFCCA1C" = "Show HbA1c in mmols/mol";

View File

@ -190,6 +190,10 @@ class Texts_Common {
return NSLocalizedString("common_statistics_inTightRange", tableName: filename, bundle: Bundle.main, value: "Tight Range", comment: "the words in tight range") return NSLocalizedString("common_statistics_inTightRange", tableName: filename, bundle: Bundle.main, value: "Tight Range", comment: "the words in tight range")
}() }()
static let userRangeStatistics = {
return NSLocalizedString("common_statistics_userRange", tableName: filename, bundle: Bundle.main, value: "User Range", comment: "the words in user range")
}()
static let highStatistics = { static let highStatistics = {
return NSLocalizedString("common_statistics_high", tableName: filename, bundle: Bundle.main, value: "High", comment: "the word high") return NSLocalizedString("common_statistics_high", tableName: filename, bundle: Bundle.main, value: "High", comment: "the word high")
}() }()

View File

@ -272,8 +272,20 @@ class Texts_SettingsView {
return NSLocalizedString("settingsviews_daysToUseStatisticsMessage", tableName: filename, bundle: Bundle.main, value: "How many days should we use to calculate the statistics? (Enter 0 to calculate today since midnight)", comment: "statistics settings, how many days to use for calculations") return NSLocalizedString("settingsviews_daysToUseStatisticsMessage", tableName: filename, bundle: Bundle.main, value: "How many days should we use to calculate the statistics? (Enter 0 to calculate today since midnight)", comment: "statistics settings, how many days to use for calculations")
}() }()
static let labelUseTITRStatisticsRange: String = { static let labelTimeInRangeType: String = {
return NSLocalizedString("settingsviews_useTITRStatisticsRange", tableName: filename, bundle: Bundle.main, value: "Use Time in Tight Range", comment: "statistics settings, prefer time in tight range") return NSLocalizedString("settingsviews_labelTimeInRangeType", tableName: filename, bundle: Bundle.main, value: "Time In Range Type", comment: "statistics settings, the type of time in range selected")
}()
static let timeInRangeTypeStandardRange: String = {
return NSLocalizedString("settingsviews_timeInRangeTypeStandardRange", tableName: filename, bundle: Bundle.main, value: "Standard Range", comment: "statistics settings, prefer standard time in range")
}()
static let timeInRangeTypeTightRange: String = {
return NSLocalizedString("settingsviews_timeInRangeTypeTightRange", tableName: filename, bundle: Bundle.main, value: "Tight Range", comment: "statistics settings, prefer time in tight range")
}()
static let timeInRangeTypeUserDefinedRange: String = {
return NSLocalizedString("settingsviews_timeInRangeTypeUserDefinedRange", tableName: filename, bundle: Bundle.main, value: "User Range", comment: "statistics settings, prefer user-defined range")
}() }()
static let labelUseIFFCA1C: String = { static let labelUseIFFCA1C: String = {

View File

@ -375,47 +375,61 @@ final class RootViewController: UIViewController, ObservableObject {
@IBOutlet var miniChartDoubleTapGestureRecognizer: UITapGestureRecognizer! @IBOutlet var miniChartDoubleTapGestureRecognizer: UITapGestureRecognizer!
/// can be used as a shortcut to switch between TIR and TITR calculation methods. The user will be notified of the change via UI transitions to show what has changed in the calculation limits /// can be used as a shortcut to switch between different TIR calculation methods. The user will be notified of the change via UI transitions to show what has changed in the calculation limits
@IBAction func statisticsViewDoubleTapGestureRecognizer(_ sender: UITapGestureRecognizer) { @IBAction func statisticsViewDoubleTapGestureRecognizer(_ sender: UITapGestureRecognizer) {
// the userdefault will be changed due to the double tap let previousTimeInRangeType = UserDefaults.standard.timeInRangeType
let useTITR = !UserDefaults.standard.useTITRStatisticsRange
UserDefaults.standard.useTITRStatisticsRange = useTITR var newTimeInRangeType: TimeInRangeType = previousTimeInRangeType
// if we're at the last index in the enum, then go to the first one
// otherwise, just set to the next index
if previousTimeInRangeType == TimeInRangeType.allCases.last {
newTimeInRangeType = .standardRange
} else {
newTimeInRangeType = TimeInRangeType(rawValue: previousTimeInRangeType.rawValue + 1) ?? .tightRange
}
// write the new index back to userdefaults (this will also trigger the observers to update the UI)
UserDefaults.standard.timeInRangeType = newTimeInRangeType
updateStatistics(animate: false) updateStatistics(animate: false)
let normalTitleColor: UIColor = lowTitleLabelOutlet.textColor let normalTitleColor: UIColor = lowTitleLabelOutlet.textColor
let normalLimitValueColor: UIColor = lowLabelOutlet.textColor
inRangeTitleLabelOutlet.textColor = ConstantsStatistics.highlightColorTitles inRangeTitleLabelOutlet.textColor = ConstantsStatistics.highlightColorTitles
if ConstantsStatistics.standardisedLowValueForTIRInMgDl != ConstantsStatistics.standardisedLowValueForTITRInMgDl { if previousTimeInRangeType.lowerLimit != newTimeInRangeType.lowerLimit {
self.lowLabelOutlet.textColor = ConstantsStatistics.labelLowColor self.lowLabelOutlet.textColor = ConstantsStatistics.labelLowColor
} }
if ConstantsStatistics.standardisedHighValueForTIRInMgDl != ConstantsStatistics.standardisedHighValueForTITRInMgDl { if previousTimeInRangeType.higherLimit != newTimeInRangeType.higherLimit {
self.highLabelOutlet.textColor = ConstantsStatistics.labelHighColor self.highLabelOutlet.textColor = ConstantsStatistics.labelHighColor
} }
// wait a short while and then fade the labels back out // wait a short while and then fade the labels back out
// even if the label colours weren't changed, it's easier to just fade them all every time. // even if some of the label colours weren't changed, it's easier to just fade them all every time.
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
UIView.transition(with: self.inRangeTitleLabelOutlet, duration: 2, options: .transitionCrossDissolve, animations: { UIView.transition(with: self.inRangeTitleLabelOutlet, duration: 1, options: .transitionCrossDissolve, animations: {
self.inRangeTitleLabelOutlet.textColor = normalTitleColor self.inRangeTitleLabelOutlet.textColor = normalTitleColor
}) })
UIView.transition(with: self.lowLabelOutlet, duration: 2, options: .transitionCrossDissolve, animations: { UIView.transition(with: self.lowLabelOutlet, duration: 1, options: .transitionCrossDissolve, animations: {
self.lowLabelOutlet.textColor = normalLimitValueColor self.lowLabelOutlet.textColor = .lightGray
}) })
UIView.transition(with: self.highLabelOutlet, duration: 2, options: .transitionCrossDissolve, animations: { UIView.transition(with: self.highLabelOutlet, duration: 1, options: .transitionCrossDissolve, animations: {
self.highLabelOutlet.textColor = normalLimitValueColor self.highLabelOutlet.textColor = .lightGray
}) })
} }
@ -2609,7 +2623,7 @@ final class RootViewController: UIViewController, ObservableObject {
// set the title labels to their correct localization // set the title labels to their correct localization
self.lowTitleLabelOutlet.text = Texts_Common.lowStatistics self.lowTitleLabelOutlet.text = Texts_Common.lowStatistics
self.inRangeTitleLabelOutlet.text = UserDefaults.standard.useTITRStatisticsRange ? Texts_Common.inTightRangeStatistics : Texts_Common.inRangeStatistics self.inRangeTitleLabelOutlet.text = UserDefaults.standard.timeInRangeType.title
self.highTitleLabelOutlet.text = Texts_Common.highStatistics self.highTitleLabelOutlet.text = Texts_Common.highStatistics
self.averageTitleLabelOutlet.text = Texts_Common.averageStatistics self.averageTitleLabelOutlet.text = Texts_Common.averageStatistics
self.a1cTitleLabelOutlet.text = Texts_Common.a1cStatistics self.a1cTitleLabelOutlet.text = Texts_Common.a1cStatistics

View File

@ -15,8 +15,8 @@ fileprivate enum Setting:Int, CaseIterable {
//show the statistics on the home screen? //show the statistics on the home screen?
case showStatistics = 0 case showStatistics = 0
//should we use the standard range for TIR, or the newer Time in Tighter Range values? //which TIR type should be used?
case useTITRStatisticsRange = 1 case timeInRangeType = 1
//urgent low value //urgent low value
case useIFCCA1C = 2 case useIFCCA1C = 2
@ -24,7 +24,15 @@ fileprivate enum Setting:Int, CaseIterable {
} }
/// conforms to SettingsViewModelProtocol for all general settings in the first sections screen /// conforms to SettingsViewModelProtocol for all general settings in the first sections screen
struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol { class SettingsViewStatisticsSettingsViewModel: NSObject, SettingsViewModelProtocol {
override init() {
super.init()
addObservers()
}
func uiView(index: Int) -> UIView? { func uiView(index: Int) -> UIView? {
@ -35,8 +43,8 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
case .showStatistics: case .showStatistics:
return UISwitch(isOn: UserDefaults.standard.showStatistics, action: {(isOn:Bool) in UserDefaults.standard.showStatistics = isOn}) return UISwitch(isOn: UserDefaults.standard.showStatistics, action: {(isOn:Bool) in UserDefaults.standard.showStatistics = isOn})
case .useTITRStatisticsRange : case .timeInRangeType:
return UISwitch(isOn: UserDefaults.standard.useTITRStatisticsRange, action: {(isOn:Bool) in UserDefaults.standard.useTITRStatisticsRange = isOn}) return nil
case .useIFCCA1C : case .useIFCCA1C :
return UISwitch(isOn: UserDefaults.standard.useIFCCA1C, action: {(isOn:Bool) in UserDefaults.standard.useIFCCA1C = isOn}) return UISwitch(isOn: UserDefaults.standard.useIFCCA1C, action: {(isOn:Bool) in UserDefaults.standard.useIFCCA1C = isOn})
@ -45,12 +53,21 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
} }
func completeSettingsViewRefreshNeeded(index: Int) -> Bool { func completeSettingsViewRefreshNeeded(index: Int) -> Bool {
// changing follower to master or master to follower requires changing ui for nightscout settings and transmitter type settings
if (index == Setting.timeInRangeType.rawValue) {return true}
return false return false
} }
var sectionReloadClosure: (() -> Void)?
func storeRowReloadClosure(rowReloadClosure: ((Int) -> Void)) {} func storeRowReloadClosure(rowReloadClosure: ((Int) -> Void)) {}
func storeSectionReloadClosure(sectionReloadClosure: @escaping (() -> Void)) {
self.sectionReloadClosure = sectionReloadClosure
}
func storeUIViewController(uIViewController: UIViewController) {} func storeUIViewController(uIViewController: UIViewController) {}
func storeMessageHandler(messageHandler: ((String, String) -> Void)) { func storeMessageHandler(messageHandler: ((String, String) -> Void)) {
@ -65,34 +82,59 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") } guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
switch setting { switch setting {
case .showStatistics:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.showStatistics {
UserDefaults.standard.showStatistics = false
} else {
UserDefaults.standard.showStatistics = true
}
})
case .timeInRangeType:
// data to be displayed in list from which user needs to pick a screen dimming type
var data = [String]()
var selectedRow: Int?
var index = 0
let currentTimeInRangeType = UserDefaults.standard.timeInRangeType
// get all data source types and add the description to data. Search for the type that matches the ScreenLockDimmingType that is currently stored in userdefaults.
for timeInRangeType in TimeInRangeType.allCases {
case .showStatistics: data.append(timeInRangeType.description + timeInRangeType.rangeString())
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.showStatistics { if timeInRangeType == currentTimeInRangeType {
UserDefaults.standard.showStatistics = false selectedRow = index
} else { }
UserDefaults.standard.showStatistics = true
} index += 1
})
}
return SettingsSelectedRowAction.selectFromList(title: Texts_SettingsView.labelTimeInRangeType, data: data, selectedRow: selectedRow, actionTitle: nil, cancelTitle: nil, actionHandler: {(index:Int) in
if index != selectedRow {
case .useTITRStatisticsRange: UserDefaults.standard.timeInRangeType = TimeInRangeType(rawValue: index) ?? .standardRange
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.useTITRStatisticsRange { }
UserDefaults.standard.useTITRStatisticsRange = false
} else {
UserDefaults.standard.useTITRStatisticsRange = true
}
})
case .useIFCCA1C:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.useIFCCA1C {
UserDefaults.standard.useIFCCA1C = false
} else {
UserDefaults.standard.useIFCCA1C = true
}
})
}, cancelHandler: nil, didSelectRowHandler: nil)
case .useIFCCA1C:
return SettingsSelectedRowAction.callFunction(function: {
if UserDefaults.standard.useIFCCA1C {
UserDefaults.standard.useIFCCA1C = false
} else {
UserDefaults.standard.useIFCCA1C = true
}
})
} }
} }
@ -118,8 +160,8 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
case .showStatistics: case .showStatistics:
return Texts_SettingsView.labelShowStatistics return Texts_SettingsView.labelShowStatistics
case .useTITRStatisticsRange: case .timeInRangeType:
return Texts_SettingsView.labelUseTITRStatisticsRange return Texts_SettingsView.labelTimeInRangeType
case .useIFCCA1C: case .useIFCCA1C:
return Texts_SettingsView.labelUseIFFCA1C return Texts_SettingsView.labelUseIFFCA1C
@ -132,9 +174,12 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
switch setting { switch setting {
case .showStatistics, .useTITRStatisticsRange, .useIFCCA1C: case .showStatistics, .useIFCCA1C:
return UITableViewCell.AccessoryType.none return UITableViewCell.AccessoryType.none
case .timeInRangeType:
return UITableViewCell.AccessoryType.disclosureIndicator
} }
} }
@ -143,9 +188,42 @@ struct SettingsViewStatisticsSettingsViewModel:SettingsViewModelProtocol {
switch setting { switch setting {
case .showStatistics, .useTITRStatisticsRange, .useIFCCA1C: case .showStatistics, .useIFCCA1C:
return nil return nil
case .timeInRangeType:
return UserDefaults.standard.timeInRangeType.description
}
}
// MARK: - observe functions
private func addObservers() {
// Listen for changes in the timeInRangeType to trigger the UI to be updated
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.timeInRangeType.rawValue, options: .new, context: nil)
}
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath,
let keyPathEnum = UserDefaults.Key(rawValue: keyPath)
else { return }
switch keyPathEnum {
case UserDefaults.Key.timeInRangeType:
// we have to run this in the main thread to avoid access errors
DispatchQueue.main.async {
self.sectionReloadClosure?()
}
default:
break
} }
} }