CGMRootView.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import LoopKitUI
  2. import SwiftUI
  3. import Swinject
  4. extension CGM {
  5. struct RootView: BaseView {
  6. let resolver: Resolver
  7. let displayClose: Bool
  8. @StateObject var state = StateModel()
  9. @State private var setupCGM = false
  10. @State private var shouldDisplayHint: Bool = false
  11. @State var hintDetent = PresentationDetent.large
  12. @State var selectedVerboseHint: AnyView?
  13. @State var hintLabel: String?
  14. @State private var decimalPlaceholder: Decimal = 0.0
  15. @State private var booleanPlaceholder: Bool = false
  16. @Environment(\.colorScheme) var colorScheme
  17. var color: LinearGradient {
  18. colorScheme == .dark ? LinearGradient(
  19. gradient: Gradient(colors: [
  20. Color.bgDarkBlue,
  21. Color.bgDarkerDarkBlue
  22. ]),
  23. startPoint: .top,
  24. endPoint: .bottom
  25. )
  26. :
  27. LinearGradient(
  28. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  29. startPoint: .top,
  30. endPoint: .bottom
  31. )
  32. }
  33. var body: some View {
  34. NavigationView {
  35. Form {
  36. Section(
  37. header: Text("CGM Integration to Trio"),
  38. content: {
  39. VStack {
  40. Picker("Type", selection: $state.cgmCurrent) {
  41. ForEach(state.listOfCGM) { type in
  42. VStack(alignment: .leading) {
  43. Text(type.displayName)
  44. Text(type.subtitle).font(.caption).foregroundColor(.secondary)
  45. }.tag(type)
  46. }
  47. }.padding(.top)
  48. HStack(alignment: .top) {
  49. Text(
  50. "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  51. )
  52. .font(.footnote)
  53. .foregroundColor(.secondary)
  54. .lineLimit(nil)
  55. Spacer()
  56. Button(
  57. action: {
  58. hintLabel = "Available CGM Types for Trio"
  59. selectedVerboseHint =
  60. AnyView(
  61. Text(
  62. "CGM Types… bla bla \n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr."
  63. )
  64. )
  65. shouldDisplayHint.toggle()
  66. },
  67. label: {
  68. HStack {
  69. Image(systemName: "questionmark.circle")
  70. }
  71. }
  72. ).buttonStyle(BorderlessButtonStyle())
  73. }.padding(.top)
  74. }.padding(.bottom)
  75. if let link = state.cgmCurrent.type.externalLink {
  76. Button {
  77. UIApplication.shared.open(link, options: [:], completionHandler: nil)
  78. } label: {
  79. HStack {
  80. Text("About this source")
  81. Spacer()
  82. Image(systemName: "chevron.right")
  83. }
  84. }
  85. .frame(maxWidth: .infinity, alignment: .leading)
  86. }
  87. if state.cgmCurrent.type == .plugin {
  88. Button {
  89. setupCGM.toggle()
  90. } label: {
  91. HStack {
  92. Text("CGM Configuration")
  93. Spacer()
  94. Image(systemName: "chevron.right")
  95. }
  96. }
  97. .frame(maxWidth: .infinity, alignment: .leading)
  98. }
  99. }
  100. ).listRowBackground(Color.chart)
  101. if let appURL = state.urlOfApp() {
  102. Section {
  103. Button {
  104. UIApplication.shared.open(appURL, options: [:]) { success in
  105. if !success {
  106. self.router.alertMessage
  107. .send(MessageContent(content: "Unable to open the app", type: .warning))
  108. }
  109. }
  110. }
  111. label: {
  112. Label(state.displayNameOfApp() ?? "-", systemImage: "waveform.path.ecg.rectangle").font(.title3)
  113. .padding() }
  114. .frame(maxWidth: .infinity, alignment: .center)
  115. .buttonStyle(.bordered)
  116. }
  117. .listRowBackground(Color.clear)
  118. } else if state.cgmCurrent.type == .nightscout {
  119. if let url = state.url {
  120. Section {
  121. Button {
  122. UIApplication.shared.open(url, options: [:]) { success in
  123. if !success {
  124. self.router.alertMessage
  125. .send(MessageContent(content: "No URL available", type: .warning))
  126. }
  127. }
  128. }
  129. label: { Label("Open URL", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding() }
  130. .frame(maxWidth: .infinity, alignment: .center)
  131. .buttonStyle(.bordered)
  132. }
  133. .listRowBackground(Color.clear)
  134. } else {
  135. Section {
  136. Button {
  137. state.showModal(for: .nighscoutConfigDirect)
  138. }
  139. label: {
  140. Label("Config Nightscout", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding()
  141. }
  142. .frame(maxWidth: .infinity, alignment: .center)
  143. .buttonStyle(.bordered)
  144. }
  145. .listRowBackground(Color.clear)
  146. }
  147. }
  148. if state.cgmCurrent.type == .xdrip {
  149. Section(header: Text("Heartbeat")) {
  150. VStack(alignment: .leading) {
  151. if let cgmTransmitterDeviceAddress = state.cgmTransmitterDeviceAddress {
  152. Text("CGM address :").padding(.top)
  153. Text(cgmTransmitterDeviceAddress)
  154. } else {
  155. Text("CGM is not used as heartbeat.").padding(.top)
  156. }
  157. HStack(alignment: .top) {
  158. Text(
  159. "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  160. )
  161. .font(.footnote)
  162. .foregroundColor(.secondary)
  163. .lineLimit(nil)
  164. Spacer()
  165. Button(
  166. action: {
  167. hintLabel = "CGM Heartbeat"
  168. selectedVerboseHint =
  169. AnyView(Text("Lorem ipsum dolor sit amet, consetetur sadipscing elitr."))
  170. shouldDisplayHint.toggle()
  171. },
  172. label: {
  173. HStack {
  174. Image(systemName: "questionmark.circle")
  175. }
  176. }
  177. ).buttonStyle(BorderlessButtonStyle())
  178. }.padding(.vertical)
  179. }
  180. }.listRowBackground(Color.chart)
  181. }
  182. if state.cgmCurrent.type == .plugin && state.cgmCurrent.id.contains("Libre") {
  183. Section {
  184. Text("Libre Calibrations").navigationLink(to: .calibrations, from: self)
  185. }.listRowBackground(Color.chart)
  186. }
  187. SettingInputSection(
  188. decimalValue: $decimalPlaceholder,
  189. booleanValue: $state.smoothGlucose,
  190. shouldDisplayHint: $shouldDisplayHint,
  191. selectedVerboseHint: Binding(
  192. get: { selectedVerboseHint },
  193. set: {
  194. selectedVerboseHint = $0.map { AnyView($0) }
  195. hintLabel = "Smooth Glucose Value"
  196. }
  197. ),
  198. units: state.units,
  199. type: .boolean,
  200. label: "Smooth Glucose Value",
  201. miniHint: "Smooth CGM readings using Savitzky–Golay filtering.",
  202. verboseHint: Text("Smooth Glucose Value… bla bla bla")
  203. )
  204. }
  205. .scrollContentBackground(.hidden).background(color)
  206. .onAppear(perform: configureView)
  207. .navigationTitle("CGM")
  208. .navigationBarTitleDisplayMode(.automatic)
  209. .sheet(isPresented: $shouldDisplayHint) {
  210. SettingInputHintView(
  211. hintDetent: $hintDetent,
  212. shouldDisplayHint: $shouldDisplayHint,
  213. hintLabel: hintLabel ?? "",
  214. hintText: selectedVerboseHint ?? AnyView(EmptyView()),
  215. sheetTitle: "Help"
  216. )
  217. }
  218. .sheet(isPresented: $setupCGM) {
  219. if let cgmFetchManager = state.cgmManager,
  220. let cgmManager = cgmFetchManager.cgmManager,
  221. state.cgmCurrent.type == cgmFetchManager.cgmGlucoseSourceType,
  222. state.cgmCurrent.id == cgmFetchManager.cgmGlucosePluginId
  223. {
  224. CGMSettingsView(
  225. cgmManager: cgmManager,
  226. bluetoothManager: state.provider.apsManager.bluetoothManager!,
  227. unit: state.settingsManager.settings.units,
  228. completionDelegate: state
  229. )
  230. } else {
  231. CGMSetupView(
  232. CGMType: state.cgmCurrent,
  233. bluetoothManager: state.provider.apsManager.bluetoothManager!,
  234. unit: state.settingsManager.settings.units,
  235. completionDelegate: state,
  236. setupDelegate: state,
  237. pluginCGMManager: self.state.pluginCGMManager
  238. )
  239. }
  240. }
  241. .onChange(of: setupCGM) { setupCGM in
  242. state.setupCGM = setupCGM
  243. }
  244. .onChange(of: state.setupCGM) { setupCGM in
  245. self.setupCGM = setupCGM
  246. }
  247. .screenNavigation(self)
  248. }
  249. }
  250. }
  251. }