CGMStateModel.swift 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import CGMBLEKit
  2. import Combine
  3. import G7SensorKit
  4. import LoopKitUI
  5. import SwiftUI
  6. struct cgmName: Identifiable, Hashable {
  7. var id: String
  8. var type: CGMType
  9. var displayName: String
  10. var subtitle: String
  11. }
  12. let cgmDefaultName = cgmName(
  13. id: CGMType.none.id,
  14. type: .none,
  15. displayName: CGMType.none.displayName,
  16. subtitle: CGMType.none.subtitle
  17. )
  18. extension CGM {
  19. final class StateModel: BaseStateModel<Provider> {
  20. @Injected() var cgmManager: FetchGlucoseManager!
  21. @Injected() var calendarManager: CalendarManager!
  22. @Injected() var pluginCGMManager: PluginManager!
  23. @Published var setupCGM: Bool = false
  24. @Published var cgmCurrent = cgmDefaultName
  25. @Published var smoothGlucose = false
  26. @Published var createCalendarEvents = false
  27. @Published var calendarIDs: [String] = []
  28. @Published var currentCalendarID: String = ""
  29. @Persisted(key: "CalendarManager.currentCalendarID") var storedCalendarID: String? = nil
  30. @Published var cgmTransmitterDeviceAddress: String? = nil
  31. @Published var listOfCGM: [cgmName] = []
  32. override func subscribe() {
  33. // collect the list of CGM available with plugins and CGMType defined manually
  34. listOfCGM = CGMType.allCases.filter { $0 != CGMType.plugin }.map {
  35. cgmName(id: $0.id, type: $0, displayName: $0.displayName, subtitle: $0.subtitle)
  36. } +
  37. pluginCGMManager.availableCGMManagers.map {
  38. cgmName(id: $0.identifier, type: CGMType.plugin, displayName: $0.localizedTitle, subtitle: $0.localizedTitle)
  39. }
  40. switch settingsManager.settings.cgm {
  41. case .plugin:
  42. if let cgmPluginInfo = listOfCGM.first(where: { $0.id == settingsManager.settings.cgmPluginIdentifier }) {
  43. cgmCurrent = cgmName(
  44. id: settingsManager.settings.cgmPluginIdentifier,
  45. type: .plugin,
  46. displayName: cgmPluginInfo.displayName,
  47. subtitle: cgmPluginInfo.subtitle
  48. )
  49. } else {
  50. // no more type of plugin available - restart to defaut
  51. cgmCurrent = cgmDefaultName
  52. }
  53. default:
  54. cgmCurrent = cgmName(
  55. id: settingsManager.settings.cgm.id,
  56. type: settingsManager.settings.cgm,
  57. displayName: settingsManager.settings.cgm.displayName,
  58. subtitle: settingsManager.settings.cgm.subtitle
  59. )
  60. }
  61. currentCalendarID = storedCalendarID ?? ""
  62. calendarIDs = calendarManager.calendarIDs()
  63. cgmTransmitterDeviceAddress = UserDefaults.standard.cgmTransmitterDeviceAddress
  64. subscribeSetting(\.useCalendar, on: $createCalendarEvents) { createCalendarEvents = $0 }
  65. subscribeSetting(\.smoothGlucose, on: $smoothGlucose, initial: { smoothGlucose = $0 })
  66. $cgmCurrent
  67. .removeDuplicates()
  68. .sink { [weak self] value in
  69. guard let self = self else { return }
  70. guard self.cgmManager.cgmGlucoseSourceType != nil else {
  71. self.settingsManager.settings.cgm = .nightscout
  72. return
  73. }
  74. if value.type != self.settingsManager.settings.cgm ||
  75. value.id != self.settingsManager.settings.cgmPluginIdentifier
  76. {
  77. self.settingsManager.settings.cgm = value.type
  78. self.settingsManager.settings.cgmPluginIdentifier = value.id
  79. self.cgmManager.updateGlucoseSource(
  80. cgmGlucoseSourceType: value.type,
  81. cgmGlucosePluginId: value.id
  82. )
  83. }
  84. }
  85. .store(in: &lifetime)
  86. $createCalendarEvents
  87. .removeDuplicates()
  88. .flatMap { [weak self] ok -> AnyPublisher<Bool, Never> in
  89. guard ok, let self = self else { return Just(false).eraseToAnyPublisher() }
  90. return self.calendarManager.requestAccessIfNeeded()
  91. }
  92. .map { [weak self] ok -> [String] in
  93. guard ok, let self = self else { return [] }
  94. return self.calendarManager.calendarIDs()
  95. }
  96. .receive(on: DispatchQueue.main)
  97. .weakAssign(to: \.calendarIDs, on: self)
  98. .store(in: &lifetime)
  99. $currentCalendarID
  100. .removeDuplicates()
  101. .sink { [weak self] id in
  102. guard id.isNotEmpty else {
  103. self?.calendarManager.currentCalendarID = nil
  104. return
  105. }
  106. self?.calendarManager.currentCalendarID = id
  107. }
  108. .store(in: &lifetime)
  109. }
  110. }
  111. }
  112. extension CGM.StateModel: CompletionDelegate {
  113. func completionNotifyingDidComplete(_: CompletionNotifying) {
  114. setupCGM = false
  115. // if CGM was deleted
  116. if cgmManager.cgmGlucoseSourceType == nil {
  117. cgmCurrent = cgmDefaultName
  118. settingsManager.settings.cgm = cgmDefaultName.type
  119. settingsManager.settings.cgmPluginIdentifier = cgmDefaultName.id
  120. cgmManager.deleteGlucoseSource()
  121. } else {
  122. cgmManager.updateGlucoseSource(cgmGlucoseSourceType: cgmCurrent.type, cgmGlucosePluginId: cgmCurrent.id)
  123. }
  124. // refresh the upload options
  125. settingsManager.settings.uploadGlucose = cgmManager.shouldSyncToRemoteService
  126. // update if required the Glucose source
  127. }
  128. }
  129. extension CGM.StateModel: CGMManagerOnboardingDelegate {
  130. func cgmManagerOnboarding(didCreateCGMManager manager: LoopKitUI.CGMManagerUI) {
  131. // update the setting of upload Glucose in services
  132. settingsManager.settings.uploadGlucose = cgmManager.shouldSyncToRemoteService
  133. // update the glucose source
  134. cgmManager.updateGlucoseSource(
  135. cgmGlucoseSourceType: cgmCurrent.type,
  136. cgmGlucosePluginId: cgmCurrent.id,
  137. newManager: manager
  138. )
  139. }
  140. func cgmManagerOnboarding(didOnboardCGMManager _: LoopKitUI.CGMManagerUI) {
  141. // nothing to do ?
  142. }
  143. }