NightscoutConfigRootView.swift 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import CoreData
  2. import SwiftUI
  3. import Swinject
  4. extension NightscoutConfig {
  5. struct RootView: BaseView {
  6. let resolver: Resolver
  7. @StateObject var state = StateModel()
  8. @State var importAlert: Alert?
  9. @State var isImportAlertPresented = false
  10. @State var importedHasRun = false
  11. @Environment(\.colorScheme) var colorScheme
  12. var color: LinearGradient {
  13. colorScheme == .dark ? LinearGradient(
  14. gradient: Gradient(colors: [
  15. Color.bgDarkBlue,
  16. Color.bgDarkerDarkBlue
  17. ]),
  18. startPoint: .top,
  19. endPoint: .bottom
  20. )
  21. :
  22. LinearGradient(
  23. gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
  24. startPoint: .top,
  25. endPoint: .bottom
  26. )
  27. }
  28. @FetchRequest(
  29. entity: ImportError.entity(),
  30. sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
  31. format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate
  32. )
  33. ) var fetchedErrors: FetchedResults<ImportError>
  34. private var portFormater: NumberFormatter {
  35. let formatter = NumberFormatter()
  36. formatter.allowsFloats = false
  37. return formatter
  38. }
  39. var body: some View {
  40. Form {
  41. Section {
  42. TextField("URL", text: $state.url)
  43. .disableAutocorrection(true)
  44. .textContentType(.URL)
  45. .autocapitalization(.none)
  46. .keyboardType(.URL)
  47. SecureField("API secret", text: $state.secret)
  48. .disableAutocorrection(true)
  49. .autocapitalization(.none)
  50. .textContentType(.password)
  51. .keyboardType(.asciiCapable)
  52. if !state.message.isEmpty {
  53. Text(state.message)
  54. }
  55. if state.connecting {
  56. HStack {
  57. Text("Connecting...")
  58. Spacer()
  59. ProgressView()
  60. }
  61. }
  62. }
  63. Section {
  64. Button("Connect") { state.connect() }
  65. .disabled(state.url.isEmpty || state.connecting)
  66. Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
  67. }
  68. Section {
  69. Toggle("Upload", isOn: $state.isUploadEnabled)
  70. if state.isUploadEnabled {
  71. Toggle("Statistics", isOn: $state.uploadStats)
  72. HStack(alignment: .top) {
  73. Image(systemName: "pencil.circle.fill")
  74. VStack {
  75. Text(
  76. "This enables uploading of statistics.json to Nightscout, which can be used by the Community Statistics and Demographics Project.\n\nParticipation in Community Statistics is opt-in, and requires separate registration at:\n"
  77. )
  78. .font(.caption)
  79. Text(
  80. "https://iaps-stats.hub.org"
  81. )
  82. .font(.caption)
  83. .multilineTextAlignment(.center)
  84. }
  85. }
  86. .foregroundColor(Color.secondary)
  87. Toggle("Glucose", isOn: $state.uploadGlucose)
  88. }
  89. } header: {
  90. Text("Allow Uploads")
  91. }
  92. Section {
  93. Button("Import settings from Nightscout") {
  94. importAlert = Alert(
  95. title: Text("Import settings?"),
  96. message: Text(
  97. "\n" +
  98. NSLocalizedString(
  99. "This will replace some or all of your current pump settings. Are you sure you want to import profile settings from Nightscout?",
  100. comment: "Profile Import Alert"
  101. ) +
  102. "\n"
  103. ),
  104. primaryButton: .destructive(
  105. Text("Yes, Import"),
  106. action: {
  107. state.importSettings()
  108. importedHasRun = true
  109. }
  110. ),
  111. secondaryButton: .cancel()
  112. )
  113. isImportAlertPresented.toggle()
  114. }.disabled(state.url.isEmpty || state.connecting)
  115. } header: { Text("Import from Nightscout") }
  116. .alert(isPresented: $importedHasRun) {
  117. Alert(
  118. title: Text((fetchedErrors.first?.error ?? "").count < 4 ? "Settings imported" : "Import Error"),
  119. message: Text(
  120. (fetchedErrors.first?.error ?? "").count < 4 ?
  121. NSLocalizedString(
  122. "\nNow please verify all of your new settings thoroughly:\n\n* Basal Settings\n * Carb Ratios\n * Glucose Targets\n * Insulin Sensitivities\n * DIA\n\n in iAPS Settings > Configuration.\n\nBad or invalid profile settings could have disatrous effects.",
  123. comment: "Imported Profiles Alert"
  124. ) :
  125. NSLocalizedString(fetchedErrors.first?.error ?? "", comment: "Import Error")
  126. ),
  127. primaryButton: .destructive(
  128. Text("OK")
  129. ),
  130. secondaryButton: .cancel()
  131. )
  132. }
  133. Section {
  134. Toggle("Use local glucose server", isOn: $state.useLocalSource)
  135. HStack {
  136. Text("Port")
  137. DecimalTextField("", value: $state.localPort, formatter: portFormater)
  138. }
  139. } header: { Text("Local glucose source") }
  140. Section {
  141. Button("Backfill glucose") { state.backfillGlucose() }
  142. .disabled(state.url.isEmpty || state.connecting || state.backfilling)
  143. }
  144. Section {
  145. Toggle("Remote control", isOn: $state.allowAnnouncements)
  146. } header: { Text("Allow Remote control of iAPS") }
  147. }
  148. .scrollContentBackground(.hidden).background(color)
  149. .onAppear(perform: configureView)
  150. .navigationBarTitle("Nightscout Config")
  151. .navigationBarTitleDisplayMode(.automatic)
  152. .alert(isPresented: $isImportAlertPresented) {
  153. importAlert!
  154. }
  155. }
  156. }
  157. }