CGMRootView.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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: String?
  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. "CGM Types… bla bla \n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr."
  61. shouldDisplayHint.toggle()
  62. },
  63. label: {
  64. HStack {
  65. Image(systemName: "questionmark.circle")
  66. }
  67. }
  68. ).buttonStyle(BorderlessButtonStyle())
  69. }.padding(.top)
  70. }.padding(.bottom)
  71. if let link = state.cgmCurrent.type.externalLink {
  72. Button {
  73. UIApplication.shared.open(link, options: [:], completionHandler: nil)
  74. } label: {
  75. HStack {
  76. Text("About this source")
  77. Spacer()
  78. Image(systemName: "chevron.right")
  79. }
  80. }
  81. .frame(maxWidth: .infinity, alignment: .leading)
  82. }
  83. if state.cgmCurrent.type == .plugin {
  84. Button {
  85. setupCGM.toggle()
  86. } label: {
  87. HStack {
  88. Text("CGM Configuration")
  89. Spacer()
  90. Image(systemName: "chevron.right")
  91. }
  92. }
  93. .frame(maxWidth: .infinity, alignment: .leading)
  94. }
  95. }
  96. ).listRowBackground(Color.chart)
  97. if let appURL = state.urlOfApp() {
  98. Section {
  99. Button {
  100. UIApplication.shared.open(appURL, options: [:]) { success in
  101. if !success {
  102. self.router.alertMessage
  103. .send(MessageContent(content: "Unable to open the app", type: .warning))
  104. }
  105. }
  106. }
  107. label: {
  108. Label(state.displayNameOfApp() ?? "-", systemImage: "waveform.path.ecg.rectangle").font(.title3)
  109. .padding() }
  110. .frame(maxWidth: .infinity, alignment: .center)
  111. .buttonStyle(.bordered)
  112. }
  113. .listRowBackground(Color.clear)
  114. } else if state.cgmCurrent.type == .nightscout {
  115. if let url = state.url {
  116. Section {
  117. Button {
  118. UIApplication.shared.open(url, options: [:]) { success in
  119. if !success {
  120. self.router.alertMessage
  121. .send(MessageContent(content: "No URL available", type: .warning))
  122. }
  123. }
  124. }
  125. label: { Label("Open URL", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding() }
  126. .frame(maxWidth: .infinity, alignment: .center)
  127. .buttonStyle(.bordered)
  128. }
  129. .listRowBackground(Color.clear)
  130. } else {
  131. Section {
  132. Button {
  133. state.showModal(for: .nighscoutConfigDirect)
  134. }
  135. label: {
  136. Label("Config Nightscout", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding()
  137. }
  138. .frame(maxWidth: .infinity, alignment: .center)
  139. .buttonStyle(.bordered)
  140. }
  141. .listRowBackground(Color.clear)
  142. }
  143. }
  144. if state.cgmCurrent.type == .xdrip {
  145. Section(header: Text("Heartbeat")) {
  146. VStack(alignment: .leading) {
  147. if let cgmTransmitterDeviceAddress = state.cgmTransmitterDeviceAddress {
  148. Text("CGM address :").padding(.top)
  149. Text(cgmTransmitterDeviceAddress)
  150. } else {
  151. Text("CGM is not used as heartbeat.").padding(.top)
  152. }
  153. HStack(alignment: .top) {
  154. Text(
  155. "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  156. )
  157. .font(.footnote)
  158. .foregroundColor(.secondary)
  159. .lineLimit(nil)
  160. Spacer()
  161. Button(
  162. action: {
  163. hintLabel = "CGM Heartbeat"
  164. selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
  165. shouldDisplayHint.toggle()
  166. },
  167. label: {
  168. HStack {
  169. Image(systemName: "questionmark.circle")
  170. }
  171. }
  172. ).buttonStyle(BorderlessButtonStyle())
  173. }.padding(.vertical)
  174. }
  175. }.listRowBackground(Color.chart)
  176. }
  177. if state.cgmCurrent.type == .plugin && state.cgmCurrent.id.contains("Libre") {
  178. Section {
  179. Text("Libre Calibrations").navigationLink(to: .calibrations, from: self)
  180. }.listRowBackground(Color.chart)
  181. }
  182. SettingInputSection(
  183. decimalValue: $decimalPlaceholder,
  184. booleanValue: $state.smoothGlucose,
  185. shouldDisplayHint: $shouldDisplayHint,
  186. selectedVerboseHint: Binding(
  187. get: { selectedVerboseHint },
  188. set: {
  189. selectedVerboseHint = $0
  190. hintLabel = "Smooth Glucose Value"
  191. }
  192. ),
  193. units: state.units,
  194. type: .boolean,
  195. label: "Smooth Glucose Value",
  196. miniHint: "Smooth CGM readings using Savitzky–Golay filtering.",
  197. verboseHint: "Smooth Glucose Value… bla bla bla"
  198. )
  199. }
  200. .scrollContentBackground(.hidden).background(color)
  201. .onAppear(perform: configureView)
  202. .navigationTitle("CGM")
  203. .navigationBarTitleDisplayMode(.automatic)
  204. .sheet(isPresented: $shouldDisplayHint) {
  205. SettingInputHintView(
  206. hintDetent: $hintDetent,
  207. shouldDisplayHint: $shouldDisplayHint,
  208. hintLabel: hintLabel ?? "",
  209. hintText: selectedVerboseHint ?? "",
  210. sheetTitle: "Help"
  211. )
  212. }
  213. .sheet(isPresented: $setupCGM) {
  214. if let cgmFetchManager = state.cgmManager,
  215. let cgmManager = cgmFetchManager.cgmManager,
  216. state.cgmCurrent.type == cgmFetchManager.cgmGlucoseSourceType,
  217. state.cgmCurrent.id == cgmFetchManager.cgmGlucosePluginId
  218. {
  219. CGMSettingsView(
  220. cgmManager: cgmManager,
  221. bluetoothManager: state.provider.apsManager.bluetoothManager!,
  222. unit: state.settingsManager.settings.units,
  223. completionDelegate: state
  224. )
  225. } else {
  226. CGMSetupView(
  227. CGMType: state.cgmCurrent,
  228. bluetoothManager: state.provider.apsManager.bluetoothManager!,
  229. unit: state.settingsManager.settings.units,
  230. completionDelegate: state,
  231. setupDelegate: state,
  232. pluginCGMManager: self.state.pluginCGMManager
  233. )
  234. }
  235. }
  236. .onChange(of: setupCGM) { setupCGM in
  237. state.setupCGM = setupCGM
  238. }
  239. .onChange(of: state.setupCGM) { setupCGM in
  240. self.setupCGM = setupCGM
  241. }
  242. .screenNavigation(self)
  243. }
  244. }
  245. }
  246. }