NightscoutConfigRootView.swift 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import CoreData
  2. import SwiftUI
  3. import Swinject
  4. extension NightscoutConfig {
  5. struct RootView: BaseView {
  6. let resolver: Resolver
  7. let displayClose: Bool
  8. @StateObject var state = StateModel()
  9. @State var importAlert: Alert?
  10. @State var isImportAlertPresented = false
  11. @State var importedHasRun = false
  12. @State private var shouldDisplayHint: Bool = false
  13. @State var hintDetent = PresentationDetent.large
  14. @State var selectedVerboseHint: String?
  15. @State var hintLabel: String?
  16. @State private var decimalPlaceholder: Decimal = 0.0
  17. @State private var booleanPlaceholder: Bool = false
  18. @Environment(\.colorScheme) var colorScheme
  19. @Environment(AppState.self) var appState
  20. var body: some View {
  21. ZStack {
  22. Form {
  23. Section(
  24. header: Text("Nightscout Integration"),
  25. content: {
  26. NavigationLink(destination: NightscoutConnectView(state: state), label: {
  27. HStack {
  28. Text("Connect")
  29. ZStack {
  30. if state.isConnectedToNS {
  31. Image(systemName: "network")
  32. Image(systemName: "checkmark.circle.fill").foregroundColor(.green).font(.caption2)
  33. .offset(x: 9, y: 6)
  34. } else {
  35. Image(systemName: "network.slash")
  36. }
  37. }
  38. }
  39. })
  40. NavigationLink("Upload", destination: NightscoutUploadView(state: state))
  41. NavigationLink("Fetch & Remote Control", destination: NightscoutFetchView(state: state))
  42. }
  43. ).listRowBackground(Color.chart)
  44. Section {
  45. VStack {
  46. Button {
  47. importAlert = Alert(
  48. title: Text("Import Therapy Settings?"),
  49. message: Text(
  50. "Are you sure you want to import profile settings from Nightscout?\n\nThis will overwrite the following Trio therapy settings: Basal Rates, Insulin Sensitivities, Carb Ratios, Target Glucose, and Duration of Insulin Action."
  51. ),
  52. primaryButton: .default(
  53. Text("Yes, Import!"),
  54. action: {
  55. Task {
  56. await state.importSettings()
  57. // Check the import status and errors after the import process finishes
  58. if state.importStatus == .failed, state.importErrors.isNotEmpty,
  59. let errorMessage = state.importErrors.first
  60. {
  61. DispatchQueue.main.async {
  62. importAlert = Alert(
  63. title: Text("Import Failed"),
  64. message: Text(errorMessage.description),
  65. dismissButton: .default(Text("OK"))
  66. )
  67. isImportAlertPresented = true
  68. }
  69. }
  70. }
  71. }
  72. ),
  73. secondaryButton: .cancel()
  74. )
  75. isImportAlertPresented = true
  76. } label: {
  77. Text("Import Settings")
  78. .font(.title3) }
  79. .frame(maxWidth: .infinity, alignment: .center)
  80. .buttonStyle(.bordered)
  81. .disabled(state.url.isEmpty || state.connecting)
  82. HStack(alignment: .top) {
  83. Text(
  84. "You can import therapy settings from Nightscout. See hint for more information which settings will be overwritten."
  85. )
  86. .font(.footnote)
  87. .foregroundColor(.secondary)
  88. .lineLimit(nil)
  89. Spacer()
  90. Button(
  91. action: {
  92. hintLabel = "Import Settings from Nightscout"
  93. selectedVerboseHint =
  94. "This will overwrite the following Trio therapy settings: \n • Basal Rates \n • Insulin Sensitivities \n • Carb Ratios \n • Target Glucose \n • Duration of Insulin Action"
  95. shouldDisplayHint.toggle()
  96. },
  97. label: {
  98. HStack {
  99. Image(systemName: "questionmark.circle")
  100. }
  101. }
  102. ).buttonStyle(BorderlessButtonStyle())
  103. }.padding(.top)
  104. }.padding(.vertical)
  105. }.listRowBackground(Color.chart)
  106. Section(
  107. content:
  108. {
  109. VStack {
  110. Button {
  111. Task {
  112. await state.backfillGlucose()
  113. }
  114. } label: {
  115. Text("Backfill Glucose")
  116. .font(.title3) }
  117. .frame(maxWidth: .infinity, alignment: .center)
  118. .buttonStyle(.bordered)
  119. .disabled(state.url.isEmpty || state.connecting || state.backfilling)
  120. HStack(alignment: .top) {
  121. Text(
  122. "You can backfill missing glucose data from Nightscout."
  123. )
  124. .font(.footnote)
  125. .foregroundColor(.secondary)
  126. .lineLimit(nil)
  127. Spacer()
  128. Button(
  129. action: {
  130. hintLabel = "Backfill Glucose from Nightscout"
  131. selectedVerboseHint =
  132. "Explanation… limitation… etc."
  133. shouldDisplayHint.toggle()
  134. },
  135. label: {
  136. HStack {
  137. Image(systemName: "questionmark.circle")
  138. }
  139. }
  140. ).buttonStyle(BorderlessButtonStyle())
  141. }.padding(.top)
  142. }.padding(.vertical)
  143. }
  144. ).listRowBackground(Color.chart)
  145. }.blur(radius: state.importStatus == .running ? 5 : 0)
  146. if state.importStatus == .running {
  147. CustomProgressView(text: "Importing Profile...")
  148. }
  149. }
  150. .fullScreenCover(isPresented: $state.isImportResultReviewPresented, content: {
  151. NightscoutImportResultView(resolver: resolver, state: state)
  152. })
  153. .sheet(isPresented: $shouldDisplayHint) {
  154. SettingInputHintView(
  155. hintDetent: $hintDetent,
  156. shouldDisplayHint: $shouldDisplayHint,
  157. hintLabel: hintLabel ?? "",
  158. hintText: selectedVerboseHint ?? "",
  159. sheetTitle: "Help"
  160. )
  161. }
  162. .navigationBarTitle("Nightscout")
  163. .navigationBarTitleDisplayMode(.automatic)
  164. .alert(isPresented: $isImportAlertPresented) {
  165. importAlert ?? Alert(title: Text("Unknown Error"))
  166. }
  167. .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
  168. .onAppear(perform: configureView)
  169. }
  170. }
  171. }